node-red-contrib-alarm-ultimate 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.MD +41 -20
- package/README.md +21 -16
- package/nodes/AlarmSystemUltimate.html +51 -27
- package/nodes/AlarmSystemUltimate.js +57 -2
- package/nodes/AlarmUltimateSiren.html +25 -3
- package/nodes/AlarmUltimateState.html +26 -4
- package/nodes/AlarmUltimateZone.html +26 -4
- package/package.json +7 -4
- package/tools/alarm-json-mapper.html +284 -748
- package/tools/alarm-panel.html +252 -517
- package/tools/alarm-settings.html +386 -0
- package/tools/assets/alarm-tools-pages.css +619 -0
- package/tools/assets/alarm-vue-shell.css +425 -0
- package/tools/assets/alarm-vue-shell.js +94 -0
- package/tools/assets/vue.global.prod.js +13 -0
package/CHANGELOG.MD
CHANGED
|
@@ -1,13 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
> Stable release track from `0.3.0`.
|
|
4
|
-
> Previous
|
|
4
|
+
> Previous pre-release history is preserved below.
|
|
5
|
+
|
|
6
|
+
## [1.0.2] - 2026-04-08
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
|
|
10
|
+
- **Tools + adminAuth (OAuth):** fixed `401/Unauthorized` when opening tools pages from the editor with protected Node-RED admin access.
|
|
11
|
+
- **OAuth token handling:** tools now support `access_token` propagation from editor links and keep the token while navigating between tool pages.
|
|
12
|
+
- **Bearer parsing conflict:** fixed `400 Bad Request` when both query `access_token` and `Authorization` header were present by normalizing auth before permission checks.
|
|
13
|
+
- **Control Panel auth fallback:** improved token lookup to support Node-RED token storage keys with `httpAdminRoot` suffix.
|
|
14
|
+
|
|
15
|
+
## [1.0.1] - 2026-04-07
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **Tools UI (Vue shell):** aligned all pages in `tools/` to the KNX AI visual style (layout, colors, typography, shared CSS shell).
|
|
20
|
+
- **Navigation:** renamed sections to **Control Panel**, **Zones**, and **Settings** with SVG icons and improved selected-menu highlighting.
|
|
21
|
+
- **Zones page:** reorganized import flow by moving the full **Import Wizard** into its own dedicated box (now positioned above the zones list).
|
|
22
|
+
- **Settings page:** moved JSON import/export actions from Zones to Settings and renamed actions to **Export Zones** / **Import Zones**.
|
|
23
|
+
- **Control Panel page:** removed redundant helper texts in the Node box and improved node-selection button readability.
|
|
24
|
+
- **Zones supervision timeout:** default timeout is now `120` seconds and empty timeout fields no longer trigger validation errors.
|
|
25
|
+
- **Packaging:** added `prepublishOnly` hook to automatically run `build:vue-tools` before publishing.
|
|
5
26
|
|
|
6
27
|
## [0.3.0] - 2026-02-18
|
|
7
28
|
|
|
8
29
|
### Release
|
|
9
30
|
|
|
10
|
-
- First stable release of `node-red-contrib-alarm-ultimate` (from `0.3.0-
|
|
31
|
+
- First stable release of `node-red-contrib-alarm-ultimate` (from `0.3.0-pre-release.7`).
|
|
11
32
|
|
|
12
33
|
### Fixed
|
|
13
34
|
|
|
@@ -18,9 +39,9 @@
|
|
|
18
39
|
|
|
19
40
|
- **Packaging:** added explicit npm publish allowlist (`package.json` `files`) to avoid accidental inclusion of unwanted files.
|
|
20
41
|
|
|
21
|
-
## [0.3.0-
|
|
42
|
+
## [0.3.0-pre-release.7] - 2026-02-08
|
|
22
43
|
|
|
23
|
-
### Breaking (
|
|
44
|
+
### Breaking (pre-release)
|
|
24
45
|
|
|
25
46
|
- **Alarm outputs (Open zones):** removed **Open zones (on request)** output + `list_open_zones` / request-topic behavior. Open zones listing while arming and cyclic listing are now always-on outputs (configurable intervals). Total outputs: **9**.
|
|
26
47
|
- **Alarm Zone / Alarm State (outputs):** `msg.payload` is now always a boolean (`true|false`). All extra fields are emitted as root properties on `msg`.
|
|
@@ -34,9 +55,9 @@
|
|
|
34
55
|
- **Alarm System:** output label renamed to **Any zone open (bool)** and help now documents each output pin.
|
|
35
56
|
- **Examples/Docs/Tests:** updated for new pins and helper node payload shape.
|
|
36
57
|
|
|
37
|
-
## [0.3.0-
|
|
58
|
+
## [0.3.0-pre-release.6] - 2026-02-07
|
|
38
59
|
|
|
39
|
-
### Breaking (
|
|
60
|
+
### Breaking (pre-release)
|
|
40
61
|
|
|
41
62
|
- **Alarm Zone:** `AlarmUltimateZone` is now output-only (removed Input mode and all adapters).
|
|
42
63
|
- **Alarm System:** moved zone input adapters into the Alarm node (**Zones → Zone input adapter** + **AX Pro match**).
|
|
@@ -49,9 +70,9 @@
|
|
|
49
70
|
|
|
50
71
|
- **Tests:** expanded unit test coverage for core Alarm behaviors (arming violations, topic prefix matching, code denial, persistence fallback).
|
|
51
72
|
|
|
52
|
-
## [0.3.0-
|
|
73
|
+
## [0.3.0-pre-release.5] - 2026-02-07
|
|
53
74
|
|
|
54
|
-
### Breaking (
|
|
75
|
+
### Breaking (pre-release)
|
|
55
76
|
|
|
56
77
|
- **Alarm System:** removed `blockArmOnViolations`. Arming is now always blocked by violations (open zones, or supervised zones that are **MISSING** when `supervision.blockArm: true`).
|
|
57
78
|
|
|
@@ -65,7 +86,7 @@
|
|
|
65
86
|
- **Tools:** moved **Export JSON** / **Import JSON** from the node edit dialog to `/alarm-ultimate/alarm-json-mapper`.
|
|
66
87
|
- **Tools / Runtime:** `supervision.blockArm` now treats false-like values (e.g. `"false"`, `0`) as `false`.
|
|
67
88
|
|
|
68
|
-
## [0.3.0-
|
|
89
|
+
## [0.3.0-pre-release.4] - 2026-02-06
|
|
69
90
|
|
|
70
91
|
### Changed
|
|
71
92
|
|
|
@@ -73,7 +94,7 @@
|
|
|
73
94
|
- **UI:** `AlarmUltimateZone` now allows selecting the **Ax Pro** adapter also in **Output** mode.
|
|
74
95
|
- **Docs:** refreshed node help and README for coherence (HomeKit input notes, Ax Pro mapping notes, "click Done" reminder).
|
|
75
96
|
|
|
76
|
-
## [0.3.0-
|
|
97
|
+
## [0.3.0-pre-release.3] - 2026-02-06
|
|
77
98
|
|
|
78
99
|
### Changed
|
|
79
100
|
|
|
@@ -85,7 +106,7 @@
|
|
|
85
106
|
- **Tools (Zones JSON Mapper):** improved visibility of the "click Done in Node-RED editor to apply" reminder.
|
|
86
107
|
- **UI:** clarified AX Pro matching help text in `AlarmUltimateZone`.
|
|
87
108
|
|
|
88
|
-
## [0.3.0-
|
|
109
|
+
## [0.3.0-pre-release.2] - 2026-02-04
|
|
89
110
|
|
|
90
111
|
### Changed
|
|
91
112
|
|
|
@@ -94,9 +115,9 @@
|
|
|
94
115
|
- **Tools:** Zones JSON Mapper now accepts **JSON array only** (aligned with `AlarmSystemUltimate.zones`).
|
|
95
116
|
- **UI:** refined AX Pro “match” option labels in `AlarmUltimateZone`.
|
|
96
117
|
|
|
97
|
-
## [0.3.0-
|
|
118
|
+
## [0.3.0-pre-release.1] - 2026-02-04
|
|
98
119
|
|
|
99
|
-
### Breaking (
|
|
120
|
+
### Breaking (pre-release)
|
|
100
121
|
|
|
101
122
|
- **Zones identifier:** removed the `id`/`zoneId` field from zones. Zones are identified only by `topic`.
|
|
102
123
|
- **Zones format:** `AlarmSystemUltimate.zones` now accepts **JSON array only** (no single-object, no JSON-per-line).
|
|
@@ -113,9 +134,9 @@
|
|
|
113
134
|
- **Alarm State (Input + AX Pro):** added support for AX Pro `CIDEvent.code` to inject arm/disarm (`3401` Away, `3441` Stay, `1401` Disarmed).
|
|
114
135
|
- **Tools:** Alarm JSON Mapper and Alarm Panel updated to reflect topic-only zones and the simplified zones format.
|
|
115
136
|
|
|
116
|
-
## [0.3.0-
|
|
137
|
+
## [0.3.0-pre-release.0] - 2026-02-04
|
|
117
138
|
|
|
118
|
-
### Breaking (
|
|
139
|
+
### Breaking (pre-release)
|
|
119
140
|
|
|
120
141
|
- **Removed adapter nodes:** `AlarmUltimateInputAdapter` and `AlarmUltimateOutputAdapter` have been removed.
|
|
121
142
|
- **Alarm State / Alarm Zone I/O mode:** `AlarmUltimateState` and `AlarmUltimateZone` can now be configured as **Input** (flow → selected alarm) or **Output** (selected alarm → flow).
|
|
@@ -131,9 +152,9 @@
|
|
|
131
152
|
- Replace any `AlarmUltimateOutputAdapter` nodes with:
|
|
132
153
|
- `AlarmUltimateState` / `AlarmUltimateZone` → **Mode: Output**, pick an **Adapter**, wire their output to your integrations.
|
|
133
154
|
|
|
134
|
-
## [0.2.0-
|
|
155
|
+
## [0.2.0-pre-release.0] - 2026-02-03
|
|
135
156
|
|
|
136
|
-
### Breaking (
|
|
157
|
+
### Breaking (pre-release)
|
|
137
158
|
|
|
138
159
|
- **Canonical envelope:** all nodes now emit `msg.alarmUltimate` (versioned) as the stable, canonical representation of alarm messages.
|
|
139
160
|
- **Output-only helper nodes:** `Alarm State`, `Alarm Zone`, `Alarm Siren` now emit the same “default telegrams” as `AlarmSystemUltimate`:
|
|
@@ -155,7 +176,7 @@
|
|
|
155
176
|
|
|
156
177
|
## [0.1.17] - 2026-02-02
|
|
157
178
|
|
|
158
|
-
### Breaking (
|
|
179
|
+
### Breaking (pre-release)
|
|
159
180
|
|
|
160
181
|
- **Output Adapter (Homekit Alarm Status):** on `arming` the node keeps `SecuritySystemCurrentState` at the previous value (while updating `SecuritySystemTargetState`). If you relied on both being the same during arming, update your downstream flows.
|
|
161
182
|
|
|
@@ -197,7 +218,7 @@
|
|
|
197
218
|
|
|
198
219
|
## [Unreleased]
|
|
199
220
|
|
|
200
|
-
### Breaking (
|
|
221
|
+
### Breaking (pre-release)
|
|
201
222
|
|
|
202
223
|
- TBD.
|
|
203
224
|
|
|
@@ -215,7 +236,7 @@
|
|
|
215
236
|
|
|
216
237
|
## [0.1.9] - 2026-02-02
|
|
217
238
|
|
|
218
|
-
### Breaking (
|
|
239
|
+
### Breaking (pre-release)
|
|
219
240
|
|
|
220
241
|
- **Alarm node:** removed classic multi-output mode; `AlarmSystemUltimate` now has a single output only.
|
|
221
242
|
|
package/README.md
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
[![Node-RED Flows][flows-image]][flows-url]
|
|
8
8
|
[![License][license-image]][license-url]
|
|
9
9
|
[![GitHub issues][issues-image]][issues-url]
|
|
10
|
-
[![Status: beta][beta-image]][repo-url]
|
|
11
10
|
|
|
12
11
|
# node-red-contrib-alarm-ultimate
|
|
13
12
|
|
|
@@ -23,14 +22,14 @@ Alarm System Ultimate nodes + web panel for Node-RED.
|
|
|
23
22
|
|
|
24
23
|
Includes:
|
|
25
24
|
|
|
26
|
-
- `AlarmSystemUltimate
|
|
25
|
+
- `AlarmSystemUltimate`: main alarm node with panel-style behavior (zones, entry/exit delays, bypass, chime, 24h/fire/tamper, siren, event log, optional per-zone sensor supervision).
|
|
27
26
|
- Helper nodes: `AlarmUltimateState`, `AlarmUltimateZone`, `AlarmUltimateSiren`.
|
|
28
|
-
- `AlarmUltimateState`:
|
|
27
|
+
- `AlarmUltimateState`: input/output helper for arm/disarm and state integration (Default / Homekit / Ax Pro / KNX-Ultimate).
|
|
29
28
|
- `AlarmUltimateZone`: output-only zone events splitter (no adapters).
|
|
30
29
|
- `AlarmUltimateSiren`: output-only siren state splitter.
|
|
31
30
|
- Web tools: Zones JSON mapper + web Alarm Panel (embeddable in Node-RED Dashboard).
|
|
32
31
|
|
|
33
|
-
|
|
32
|
+
`AlarmSystemUltimate` is now stable.
|
|
34
33
|
|
|
35
34
|
## Table of contents
|
|
36
35
|
|
|
@@ -40,8 +39,8 @@ Note: `AlarmSystemUltimate` is currently **BETA**.
|
|
|
40
39
|
- [Nodes](#nodes)
|
|
41
40
|
- [Web tools](#web-tools)
|
|
42
41
|
- [Examples](#examples)
|
|
43
|
-
- [
|
|
44
|
-
- [
|
|
42
|
+
- [Advanced integration reference (optional)](#advanced-integration-reference-optional)
|
|
43
|
+
- [Contributing (optional)](#contributing-optional)
|
|
45
44
|
|
|
46
45
|
## Install
|
|
47
46
|
|
|
@@ -56,8 +55,8 @@ npm i node-red-contrib-alarm-ultimate
|
|
|
56
55
|
|
|
57
56
|
Beginner-friendly flow:
|
|
58
57
|
|
|
59
|
-
1. Add an **AlarmSystemUltimate
|
|
60
|
-
2. Click **Manage zones** and add at least one zone (example topic: `sensor/frontdoor`).
|
|
58
|
+
1. Add an **AlarmSystemUltimate** node.
|
|
59
|
+
2. Click **Manage zones** and add at least one zone (example topic: `sensor/frontdoor`). For backup/restore use **Export Zones** / **Import Zones** in the **Settings** page.
|
|
61
60
|
**Important:** after editing zones, click **Done** in the Node-RED editor to save (if you click **Cancel**, changes are lost).
|
|
62
61
|
3. Send sensor messages to the Alarm node:
|
|
63
62
|
- open: `msg.topic="sensor/frontdoor"`, `msg.payload=true`
|
|
@@ -91,7 +90,7 @@ Optional (recommended):
|
|
|
91
90
|
|
|
92
91
|
## Nodes
|
|
93
92
|
|
|
94
|
-
### Alarm System Ultimate
|
|
93
|
+
### Alarm System Ultimate
|
|
95
94
|
|
|
96
95
|
Main node that:
|
|
97
96
|
|
|
@@ -152,7 +151,9 @@ Example zone:
|
|
|
152
151
|
- `Alarm Zone` (`AlarmUltimateZone`): emits `zone_open` / `zone_close` as `.../event` telegrams
|
|
153
152
|
- `Alarm Siren` (`AlarmUltimateSiren`): emits siren telegrams (`msg.topic = <controlTopic>/siren`, `msg.event = siren_on|siren_off`, `msg.payload = true|false`)
|
|
154
153
|
|
|
155
|
-
###
|
|
154
|
+
### Advanced integration (optional): `msg.alarmUltimate`
|
|
155
|
+
|
|
156
|
+
If you are just wiring nodes in Node-RED, you can safely skip this section.
|
|
156
157
|
|
|
157
158
|
All nodes in this package add a stable, versioned object to every output message:
|
|
158
159
|
|
|
@@ -168,11 +169,11 @@ msg.alarmUltimate = {
|
|
|
168
169
|
};
|
|
169
170
|
```
|
|
170
171
|
|
|
171
|
-
|
|
172
|
+
Adapters and advanced integrations can use `msg.alarmUltimate` as a consistent source of alarm details.
|
|
172
173
|
|
|
173
174
|
## Web tools
|
|
174
175
|
|
|
175
|
-
These pages are
|
|
176
|
+
These web pages are available from your Node-RED editor at:
|
|
176
177
|
|
|
177
178
|
- Zones JSON Mapper: `/alarm-ultimate/alarm-json-mapper`
|
|
178
179
|
- Alarm Panel: `/alarm-ultimate/alarm-panel`
|
|
@@ -187,9 +188,12 @@ The Zones JSON Mapper supports:
|
|
|
187
188
|
|
|
188
189
|
- Sample message mapping (e.g. KNX Ultimate): map `topic`/`payload` fields and generate a zone template.
|
|
189
190
|
- ETS Group Addresses export (TSV): paste the exported table and generate zones in batch (boolean datapoints only).
|
|
190
|
-
- Backup/restore: **Export JSON** / **Import JSON** for zone definitions.
|
|
191
191
|
- Quality-of-life: bulk apply (Kind/Supervision), sorting, duplicate-topic skipping on import, persisted Step 1 input.
|
|
192
192
|
|
|
193
|
+
The Settings page supports:
|
|
194
|
+
|
|
195
|
+
- Backup/restore: **Export Zones** / **Import Zones** for zone definitions.
|
|
196
|
+
|
|
193
197
|
## Examples
|
|
194
198
|
|
|
195
199
|
- `examples/alarm-ultimate-basic.json`: ready-to-import flow with `AlarmSystemUltimate`, injects and debug nodes.
|
|
@@ -200,7 +204,7 @@ The Zones JSON Mapper supports:
|
|
|
200
204
|
|
|
201
205
|
See `examples/README.md`.
|
|
202
206
|
|
|
203
|
-
##
|
|
207
|
+
## Contributing (optional)
|
|
204
208
|
|
|
205
209
|
Run tests:
|
|
206
210
|
|
|
@@ -208,7 +212,9 @@ Run tests:
|
|
|
208
212
|
npm test
|
|
209
213
|
```
|
|
210
214
|
|
|
211
|
-
##
|
|
215
|
+
## Advanced integration reference (optional)
|
|
216
|
+
|
|
217
|
+
If you are using the package normally in flows, you can ignore this section.
|
|
212
218
|
|
|
213
219
|
When Node-RED authentication is enabled, the admin endpoints use these permissions (if available):
|
|
214
220
|
|
|
@@ -236,4 +242,3 @@ HTTP admin endpoints:
|
|
|
236
242
|
[flows-image]: https://img.shields.io/badge/Node--RED%20Flows-library-8f0000?logo=nodered&logoColor=white
|
|
237
243
|
[license-image]: https://img.shields.io/npm/l/node-red-contrib-alarm-ultimate.svg
|
|
238
244
|
[issues-image]: https://img.shields.io/github/issues/Supergiovane/node-red-contrib-alarm-ultimate.svg
|
|
239
|
-
[beta-image]: https://img.shields.io/badge/status-beta-orange.svg
|
|
@@ -267,13 +267,35 @@
|
|
|
267
267
|
zoneAxProMatchRow.toggle(adapter === "axpro");
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
+
function getEditorAccessToken() {
|
|
271
|
+
try {
|
|
272
|
+
const tokens =
|
|
273
|
+
RED && RED.settings && typeof RED.settings.get === "function"
|
|
274
|
+
? RED.settings.get("auth-tokens")
|
|
275
|
+
: null;
|
|
276
|
+
return tokens && typeof tokens.access_token === "string"
|
|
277
|
+
? tokens.access_token.trim()
|
|
278
|
+
: "";
|
|
279
|
+
} catch (_err) {
|
|
280
|
+
return "";
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function withAccessToken(url) {
|
|
285
|
+
const token = getEditorAccessToken();
|
|
286
|
+
if (!token) return url;
|
|
287
|
+
const sep = url.includes("?") ? "&" : "?";
|
|
288
|
+
return `${url}${sep}access_token=${encodeURIComponent(token)}`;
|
|
289
|
+
}
|
|
290
|
+
|
|
270
291
|
function openZonesManager() {
|
|
271
292
|
const httpAdminRoot = (RED.settings && RED.settings.httpAdminRoot) || "/";
|
|
272
293
|
const root = httpAdminRoot.endsWith("/") ? httpAdminRoot : `${httpAdminRoot}/`;
|
|
273
294
|
const currentName = String($("#node-input-name").val() || "").trim();
|
|
274
295
|
const namePart = currentName ? `&name=${encodeURIComponent(currentName)}` : "";
|
|
275
296
|
const idPart = nodeId ? `?id=${encodeURIComponent(nodeId)}${namePart}` : "";
|
|
276
|
-
|
|
297
|
+
const targetUrl = `${root}alarm-ultimate/alarm-json-mapper${idPart}`;
|
|
298
|
+
window.open(withAccessToken(targetUrl), "_blank");
|
|
277
299
|
}
|
|
278
300
|
|
|
279
301
|
$("#node-input-zones-panel").on("click", function (evt) {
|
|
@@ -281,8 +303,9 @@
|
|
|
281
303
|
const httpAdminRoot = (RED.settings && RED.settings.httpAdminRoot) || "/";
|
|
282
304
|
const root = httpAdminRoot.endsWith("/") ? httpAdminRoot : `${httpAdminRoot}/`;
|
|
283
305
|
const idPart = nodeId ? `?id=${encodeURIComponent(nodeId)}` : "";
|
|
306
|
+
const targetUrl = `${root}alarm-ultimate/alarm-panel${idPart}`;
|
|
284
307
|
window.open(
|
|
285
|
-
|
|
308
|
+
withAccessToken(targetUrl),
|
|
286
309
|
"_blank",
|
|
287
310
|
"noopener,noreferrer",
|
|
288
311
|
);
|
|
@@ -748,7 +771,7 @@
|
|
|
748
771
|
## Quick start (5 minutes)
|
|
749
772
|
|
|
750
773
|
1. Add an **Alarm System** node to your flow.
|
|
751
|
-
2. Click **Manage zones** and create at least one zone (example topic: `sensor/frontdoor`).
|
|
774
|
+
2. Click **Manage zones** and create at least one zone (example topic: `sensor/frontdoor`). For backup/restore use **Export Zones** / **Import Zones** in the **Settings** web page.
|
|
752
775
|
**Important:** after editing zones, click **Done** in the Node-RED editor to save (if you click **Cancel**, changes are lost).
|
|
753
776
|
3. Send sensor messages to the Alarm node:
|
|
754
777
|
- open: `msg.topic="sensor/frontdoor"` + `msg.payload=true` (also works: `"open"`, `"on"`, `1`)
|
|
@@ -769,10 +792,11 @@
|
|
|
769
792
|
|
|
770
793
|
<br/>
|
|
771
794
|
|
|
772
|
-
###
|
|
795
|
+
### Editor and web pages
|
|
773
796
|
|
|
774
797
|
- **Panel**: `/alarm-ultimate/alarm-panel` (supports `?id=<alarmNodeId>` and `?embed=1&id=<alarmNodeId>`).
|
|
775
|
-
- **Zones**: use **Manage zones** to open `/alarm-ultimate/alarm-json-mapper` and sync
|
|
798
|
+
- **Zones**: use **Manage zones** to open `/alarm-ultimate/alarm-json-mapper` and sync zones back into the editor. Click **Done** in the Node-RED node editor to apply/save.
|
|
799
|
+
- **Settings**: open `/alarm-ultimate/alarm-settings` to **Export Zones** / **Import Zones**.
|
|
776
800
|
- **Name**: optional label shown in the Alarm Panel.
|
|
777
801
|
- **Control topic**: topic that receives commands (and is used as base for some outputs).
|
|
778
802
|
- **With Input**: message property evaluated as sensor value (default `payload`). It must be convertible to boolean (`true/false`, `open/closed`, `on/off`, `1/0`, ...).
|
|
@@ -800,11 +824,11 @@
|
|
|
800
824
|
| Emit restore events | Emits `zone_restore` when a zone returns to false. |
|
|
801
825
|
| Event log size | Max stored log entries in node context (0 disables log). |
|
|
802
826
|
| Sync other alarms | Optional: when this node is armed/disarmed, it can arm/disarm other Alarm nodes. The original `msg.code` is forwarded. |
|
|
803
|
-
| Zones | Zone definitions. Use **Manage zones**
|
|
827
|
+
| Zones | Zone definitions. Use **Manage zones** to edit them in the web page (including optional supervision). For backup/restore use **Settings** → **Export Zones / Import Zones**. |
|
|
804
828
|
|
|
805
829
|
<br/>
|
|
806
830
|
|
|
807
|
-
## Zones
|
|
831
|
+
## Zones (advanced, optional)
|
|
808
832
|
|
|
809
833
|
Each zone can optionally enable **sensor supervision** (per-zone): if no valid sensor updates are received within `timeoutSeconds`, the node emits `supervision_lost` (supervision starts immediately when the node runs, and `supervision_restored` is emitted on the next valid update).
|
|
810
834
|
|
|
@@ -827,11 +851,11 @@
|
|
|
827
851
|
|
|
828
852
|
<br/>
|
|
829
853
|
|
|
830
|
-
## Output
|
|
854
|
+
## Output reference
|
|
831
855
|
|
|
832
856
|
The node has **9 outputs** (pins).
|
|
833
857
|
|
|
834
|
-
Important: **Output #1 (All messages)**
|
|
858
|
+
Important: **Output #1 (All messages)** includes everything from the other outputs. It can emit:
|
|
835
859
|
|
|
836
860
|
- a single message, or
|
|
837
861
|
- an array of messages (for example, when the node emits an event **and** a siren update at the same time).
|
|
@@ -889,7 +913,7 @@
|
|
|
889
913
|
|
|
890
914
|
<br/>
|
|
891
915
|
|
|
892
|
-
##
|
|
916
|
+
## Event reference (`msg.event`, optional)
|
|
893
917
|
|
|
894
918
|
Arming / state:
|
|
895
919
|
|
|
@@ -921,7 +945,7 @@
|
|
|
921
945
|
|
|
922
946
|
<br/>
|
|
923
947
|
|
|
924
|
-
## Control
|
|
948
|
+
## Control message reference (`msg.topic === controlTopic`)
|
|
925
949
|
|
|
926
950
|
Arm:
|
|
927
951
|
|
|
@@ -937,11 +961,11 @@
|
|
|
937
961
|
|
|
938
962
|
- `msg.command = "status"` or `msg.status = true`
|
|
939
963
|
|
|
940
|
-
|
|
964
|
+
Bypass / unbypass (zone topic):
|
|
941
965
|
|
|
942
966
|
- `msg.command = "bypass"` / `msg.command = "unbypass"`
|
|
943
967
|
- or `msg.bypass = true` / `msg.unbypass = true`
|
|
944
|
-
|
|
968
|
+
- pass the zone **topic** in `msg.zoneTopic`
|
|
945
969
|
|
|
946
970
|
Siren:
|
|
947
971
|
|
|
@@ -963,18 +987,18 @@
|
|
|
963
987
|
|
|
964
988
|
<br/>
|
|
965
989
|
|
|
966
|
-
##
|
|
990
|
+
## Zone fields
|
|
967
991
|
|
|
968
|
-
|
|
992
|
+
Zones must be configured as a JSON array of objects.
|
|
969
993
|
|
|
970
|
-
|
|
994
|
+
Minimal fields:
|
|
971
995
|
|
|
972
|
-
|
|
996
|
+
- `topic` (exact topic, or a prefix ending with `*`)
|
|
973
997
|
|
|
974
|
-
|
|
998
|
+
Common fields:
|
|
975
999
|
|
|
976
|
-
|
|
977
|
-
|
|
1000
|
+
- `topic`: **unique identifier** of the zone (used for bypass via `msg.zoneTopic`)
|
|
1001
|
+
- `name`: label used in events (optional; defaults to `topic`)
|
|
978
1002
|
- `type`: `perimeter` (default), `motion`, `tamper`, `fire`, `24h`
|
|
979
1003
|
- `entry`: `true` to use entry delay, otherwise triggers instantly
|
|
980
1004
|
- `entryDelaySeconds`: overrides the global entry delay
|
|
@@ -990,13 +1014,13 @@
|
|
|
990
1014
|
|
|
991
1015
|
Example (formatted JSON array):
|
|
992
1016
|
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1017
|
+
```json
|
|
1018
|
+
[
|
|
1019
|
+
{
|
|
1020
|
+
"name": "Front door",
|
|
1021
|
+
"topic": "house/door/front",
|
|
1022
|
+
"type": "perimeter",
|
|
1023
|
+
"entry": true,
|
|
1000
1024
|
"entryDelaySeconds": 30,
|
|
1001
1025
|
"bypassable": true,
|
|
1002
1026
|
"chime": true
|
|
@@ -86,6 +86,37 @@ module.exports = function (RED) {
|
|
|
86
86
|
? RED.auth.needsPermission('AlarmSystemUltimate.write')
|
|
87
87
|
: (req, res, next) => next();
|
|
88
88
|
|
|
89
|
+
function applyAccessTokenFromQuery(req) {
|
|
90
|
+
const token =
|
|
91
|
+
req &&
|
|
92
|
+
req.query &&
|
|
93
|
+
typeof req.query.access_token === 'string' &&
|
|
94
|
+
req.query.access_token.trim().length > 0
|
|
95
|
+
? req.query.access_token.trim()
|
|
96
|
+
: '';
|
|
97
|
+
if (!token) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (!req.headers || typeof req.headers !== 'object') {
|
|
101
|
+
req.headers = {};
|
|
102
|
+
}
|
|
103
|
+
const authHeader =
|
|
104
|
+
typeof req.headers.authorization === 'string' ? req.headers.authorization.trim() : '';
|
|
105
|
+
if (!authHeader && token) {
|
|
106
|
+
req.headers.authorization = `Bearer ${token}`;
|
|
107
|
+
}
|
|
108
|
+
// passport-http-bearer returns 400 if token is present in both header and query.
|
|
109
|
+
// Normalize to header-only for this request.
|
|
110
|
+
if (req.query && Object.prototype.hasOwnProperty.call(req.query, 'access_token')) {
|
|
111
|
+
delete req.query.access_token;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function needsReadWithQueryToken(req, res, next) {
|
|
116
|
+
applyAccessTokenFromQuery(req);
|
|
117
|
+
return needsRead(req, res, next);
|
|
118
|
+
}
|
|
119
|
+
|
|
89
120
|
function sendToolFile(res, filename) {
|
|
90
121
|
const filePath = path.join(__dirname, '..', 'tools', filename);
|
|
91
122
|
res.set('Cache-Control', 'no-store, max-age=0');
|
|
@@ -97,14 +128,38 @@ module.exports = function (RED) {
|
|
|
97
128
|
});
|
|
98
129
|
}
|
|
99
130
|
|
|
100
|
-
|
|
131
|
+
function sendToolAssetFile(res, filename) {
|
|
132
|
+
const assetsRoot = path.join(__dirname, '..', 'tools', 'assets');
|
|
133
|
+
const requested = String(filename || '').replace(/^\/+/, '');
|
|
134
|
+
const rootPath = path.resolve(assetsRoot);
|
|
135
|
+
const fullPath = path.resolve(rootPath, requested);
|
|
136
|
+
if (!fullPath.startsWith(rootPath + path.sep)) {
|
|
137
|
+
res.status(403).end();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
res.sendFile(fullPath, (err) => {
|
|
141
|
+
if (err) {
|
|
142
|
+
res.status(err.statusCode || 404).end();
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
RED.httpAdmin.get('/alarm-ultimate/alarm-json-mapper', needsReadWithQueryToken, (req, res) => {
|
|
101
148
|
sendToolFile(res, 'alarm-json-mapper.html');
|
|
102
149
|
});
|
|
103
150
|
|
|
104
|
-
RED.httpAdmin.get('/alarm-ultimate/alarm-panel',
|
|
151
|
+
RED.httpAdmin.get('/alarm-ultimate/alarm-panel', needsReadWithQueryToken, (req, res) => {
|
|
105
152
|
sendToolFile(res, 'alarm-panel.html');
|
|
106
153
|
});
|
|
107
154
|
|
|
155
|
+
RED.httpAdmin.get('/alarm-ultimate/alarm-settings', needsReadWithQueryToken, (req, res) => {
|
|
156
|
+
sendToolFile(res, 'alarm-settings.html');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
RED.httpAdmin.get('/alarm-ultimate/alarm-tools/assets/:file', (req, res) => {
|
|
160
|
+
sendToolAssetFile(res, req.params.file);
|
|
161
|
+
});
|
|
162
|
+
|
|
108
163
|
RED.httpAdmin.get('/alarm-ultimate/alarm/nodes', needsRead, (req, res) => {
|
|
109
164
|
const nodes = Array.from(alarmInstances.values()).map((api) => ({
|
|
110
165
|
id: api.id,
|
|
@@ -31,12 +31,34 @@
|
|
|
31
31
|
const url = `${root}alarm-ultimate/alarm/nodes`;
|
|
32
32
|
const panelButton = $("#node-input-alarm-panel");
|
|
33
33
|
|
|
34
|
+
function getEditorAccessToken() {
|
|
35
|
+
try {
|
|
36
|
+
const tokens =
|
|
37
|
+
RED && RED.settings && typeof RED.settings.get === "function"
|
|
38
|
+
? RED.settings.get("auth-tokens")
|
|
39
|
+
: null;
|
|
40
|
+
return tokens && typeof tokens.access_token === "string"
|
|
41
|
+
? tokens.access_token.trim()
|
|
42
|
+
: "";
|
|
43
|
+
} catch (_err) {
|
|
44
|
+
return "";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function withAccessToken(urlValue) {
|
|
49
|
+
const token = getEditorAccessToken();
|
|
50
|
+
if (!token) return urlValue;
|
|
51
|
+
const sep = urlValue.includes("?") ? "&" : "?";
|
|
52
|
+
return `${urlValue}${sep}access_token=${encodeURIComponent(token)}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
34
55
|
panelButton.off("click").on("click", (evt) => {
|
|
35
56
|
evt.preventDefault();
|
|
36
57
|
const alarmId = alarmSelect.val() || this.alarmId || "";
|
|
37
58
|
const idPart = alarmId ? `?id=${encodeURIComponent(alarmId)}` : "";
|
|
59
|
+
const targetUrl = `${root}alarm-ultimate/alarm-panel${idPart}`;
|
|
38
60
|
window.open(
|
|
39
|
-
|
|
61
|
+
withAccessToken(targetUrl),
|
|
40
62
|
"_blank",
|
|
41
63
|
"noopener,noreferrer",
|
|
42
64
|
);
|
|
@@ -112,10 +134,10 @@ Output message (same format as the Alarm node siren output):
|
|
|
112
134
|
- `msg.event`: `siren_on` or `siren_off`
|
|
113
135
|
- `msg.payload`: `true` when the siren is on, otherwise `false`
|
|
114
136
|
- `msg.reason`: `manual|timeout|arm|disarm|...`
|
|
115
|
-
- `msg.alarmUltimate`:
|
|
137
|
+
- `msg.alarmUltimate`: optional standard object with extra alarm details
|
|
116
138
|
|
|
117
139
|
Notes:
|
|
118
140
|
|
|
119
|
-
- No
|
|
141
|
+
- No wire from the Alarm node is required: just select the Alarm node in this editor.
|
|
120
142
|
- Use the **Panel** button to open the Alarm Panel preselected on the chosen Alarm node.
|
|
121
143
|
</script>
|
|
@@ -40,6 +40,27 @@
|
|
|
40
40
|
const topicRow = $("#au-row-topic");
|
|
41
41
|
const initRow = $("#au-row-outputInitialState");
|
|
42
42
|
|
|
43
|
+
function getEditorAccessToken() {
|
|
44
|
+
try {
|
|
45
|
+
const tokens =
|
|
46
|
+
RED && RED.settings && typeof RED.settings.get === "function"
|
|
47
|
+
? RED.settings.get("auth-tokens")
|
|
48
|
+
: null;
|
|
49
|
+
return tokens && typeof tokens.access_token === "string"
|
|
50
|
+
? tokens.access_token.trim()
|
|
51
|
+
: "";
|
|
52
|
+
} catch (_err) {
|
|
53
|
+
return "";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function withAccessToken(urlValue) {
|
|
58
|
+
const token = getEditorAccessToken();
|
|
59
|
+
if (!token) return urlValue;
|
|
60
|
+
const sep = urlValue.includes("?") ? "&" : "?";
|
|
61
|
+
return `${urlValue}${sep}access_token=${encodeURIComponent(token)}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
43
64
|
function refreshVisibility() {
|
|
44
65
|
const mode = String(ioSelect.val() || "out");
|
|
45
66
|
const isOut = mode === "out";
|
|
@@ -51,8 +72,9 @@
|
|
|
51
72
|
evt.preventDefault();
|
|
52
73
|
const alarmId = alarmSelect.val() || this.alarmId || "";
|
|
53
74
|
const idPart = alarmId ? `?id=${encodeURIComponent(alarmId)}` : "";
|
|
75
|
+
const targetUrl = `${root}alarm-ultimate/alarm-panel${idPart}`;
|
|
54
76
|
window.open(
|
|
55
|
-
|
|
77
|
+
withAccessToken(targetUrl),
|
|
56
78
|
"_blank",
|
|
57
79
|
"noopener,noreferrer",
|
|
58
80
|
);
|
|
@@ -166,11 +188,11 @@ Output message (same format as the Alarm node `.../event` output):
|
|
|
166
188
|
- `msg.payload`: `true|false` (armed/active boolean)
|
|
167
189
|
- `msg.mode`: `armed|disarmed`
|
|
168
190
|
- other event fields (reason, seconds, violations, ...) are emitted as **root properties** on `msg`
|
|
169
|
-
- `msg.alarmUltimate`:
|
|
191
|
+
- `msg.alarmUltimate`: optional standard object with extra alarm details
|
|
170
192
|
|
|
171
193
|
Notes:
|
|
172
194
|
|
|
173
|
-
- No
|
|
195
|
+
- No wire from the Alarm node is required: just select the Alarm node in this editor.
|
|
174
196
|
- Use the **Panel** button to open the Alarm Panel preselected on the chosen Alarm node.
|
|
175
197
|
|
|
176
198
|
## Input mode details
|
|
@@ -215,7 +237,7 @@ Only arming/disarming CID codes are handled (other CID events are ignored):
|
|
|
215
237
|
- `3441` → `arm` (Stay)
|
|
216
238
|
- `1401` → `disarm`
|
|
217
239
|
|
|
218
|
-
##
|
|
240
|
+
## Adapter output behavior
|
|
219
241
|
|
|
220
242
|
- **Default / Ax Pro**: keeps a boolean `msg.payload` and emits details as root properties.
|
|
221
243
|
- **Homekit**: emits `msg.SecuritySystemTargetState` and `msg.SecuritySystemCurrentState` as root properties (while keeping boolean `msg.payload`).
|