node-red-contrib-alarm-ultimate 0.1.2 → 0.1.3
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/README.md +3 -0
- package/examples/README.md +80 -3
- package/examples/alarm-ultimate-dashboard-controls.json +31 -2
- package/examples/alarm-ultimate-dashboard-v2.json +74 -2
- package/examples/alarm-ultimate-dashboard.json +31 -2
- package/examples/alarm-ultimate-home-assistant-alarm-panel.json +335 -0
- package/nodes/AlarmSystemUltimate.html +166 -28
- package/nodes/AlarmSystemUltimate.js +119 -4
- package/package.json +1 -1
- package/test/alarm-system.spec.js +61 -0
- package/tools/alarm-json-mapper.html +55 -36
- package/tools/alarm-panel.html +390 -33
package/README.md
CHANGED
|
@@ -92,6 +92,7 @@ The Alarm Panel supports:
|
|
|
92
92
|
|
|
93
93
|
- Preselect node: `/alarm-ultimate/alarm-panel?id=<alarmNodeId>`
|
|
94
94
|
- Embed mode (for Dashboard iframes): `/alarm-ultimate/alarm-panel?embed=1&id=<alarmNodeId>`
|
|
95
|
+
- Views: `view=keypad`, `view=zones`, `view=log` (e.g. `/alarm-ultimate/alarm-panel?embed=1&view=log&id=<alarmNodeId>`)
|
|
95
96
|
|
|
96
97
|
The Zones JSON Mapper supports:
|
|
97
98
|
|
|
@@ -104,6 +105,7 @@ The Zones JSON Mapper supports:
|
|
|
104
105
|
- `examples/alarm-ultimate-dashboard.json`: Node-RED Dashboard example embedding the Alarm Panel in a `ui_template` iframe.
|
|
105
106
|
- `examples/alarm-ultimate-dashboard-controls.json`: Node-RED Dashboard example with the embedded panel plus command buttons (and a small sensor simulator).
|
|
106
107
|
- `examples/alarm-ultimate-dashboard-v2.json`: Dashboard 2.0 example for `@flowfuse/node-red-dashboard` (Alarm Panel + basic controls + status).
|
|
108
|
+
- `examples/alarm-ultimate-home-assistant-alarm-panel.json`: Home Assistant Add-on example (no MQTT) using the HA Alarm Panel card + `AlarmUltimateInputAdapter`.
|
|
107
109
|
|
|
108
110
|
See `examples/README.md`.
|
|
109
111
|
|
|
@@ -127,6 +129,7 @@ HTTP admin endpoints:
|
|
|
127
129
|
|
|
128
130
|
- `GET /alarm-ultimate/alarm/nodes`
|
|
129
131
|
- `GET /alarm-ultimate/alarm/:id/state`
|
|
132
|
+
- `GET /alarm-ultimate/alarm/:id/log`
|
|
130
133
|
- `GET /alarm-ultimate/input-adapter/presets`
|
|
131
134
|
- `POST /alarm-ultimate/alarm/:id/command`
|
|
132
135
|
- `GET /alarm-ultimate/alarm-json-mapper`
|
package/examples/README.md
CHANGED
|
@@ -22,8 +22,8 @@ Notes:
|
|
|
22
22
|
Import `examples/alarm-ultimate-dashboard.json`.
|
|
23
23
|
|
|
24
24
|
- Requires `node-red-dashboard` installed (`ui_*` nodes).
|
|
25
|
-
- The widget uses an iframe pointing to
|
|
26
|
-
-
|
|
25
|
+
- The widget uses an iframe pointing to `../alarm-ultimate/alarm-panel?embed=1&id=<alarmNodeId>` (escapes the Dashboard path like `/ui`).
|
|
26
|
+
- The embedded panel supports `view=keypad`, `view=zones`, and `view=log`.
|
|
27
27
|
|
|
28
28
|
### Dashboard + commands (panel + controls)
|
|
29
29
|
|
|
@@ -31,7 +31,8 @@ Import `examples/alarm-ultimate-dashboard-controls.json`.
|
|
|
31
31
|
|
|
32
32
|
- Includes the panel (iframe) + Dashboard buttons for `arm`, `disarm`, `status`, `list_open_zones`, `siren_on/off`, `panic`.
|
|
33
33
|
- Also includes a small “sensor simulator” (buttons that send `true/false` on `sensor/frontdoor` and `sensor/living_pir`).
|
|
34
|
-
- The iframe uses a
|
|
34
|
+
- The iframe uses a _relative_ URL that escapes the Dashboard path (`../alarm-ultimate/...`) so it works even when Node-RED is served under a path prefix (e.g. Home Assistant Ingress).
|
|
35
|
+
- The embedded panel supports `view=keypad`, `view=zones`, and `view=log` (useful to show a user-facing event log).
|
|
35
36
|
|
|
36
37
|
## Dashboard V2 (FlowFuse / @flowfuse/node-red-dashboard)
|
|
37
38
|
|
|
@@ -40,3 +41,79 @@ Import `examples/alarm-ultimate-dashboard-v2.json`.
|
|
|
40
41
|
- Requires `@flowfuse/node-red-dashboard` installed (`ui-*` nodes).
|
|
41
42
|
- Embeds the Alarm Panel via a Dashboard 2.0 `ui-template` (Vue SFC) iframe.
|
|
42
43
|
- Includes basic command buttons (`arm`, `disarm`, `status`), a small sensor simulator, and two status widgets (`AlarmUltimateState`, `AlarmUltimateSiren` → `ui-text`).
|
|
44
|
+
|
|
45
|
+
## Home Assistant (Alarm Panel card, no MQTT)
|
|
46
|
+
|
|
47
|
+
If you run Node-RED as the Home Assistant Add-on and you want to use the standard Home Assistant Alarm Panel card, import:
|
|
48
|
+
|
|
49
|
+
- `examples/alarm-ultimate-home-assistant-alarm-panel.json`
|
|
50
|
+
|
|
51
|
+
This example:
|
|
52
|
+
|
|
53
|
+
- Maps `binary_sensor.*` state changes (`"on"/"off"`) into Alarm zones using `AlarmUltimateInputAdapter` (built-in preset: **Home Assistant on/off**).
|
|
54
|
+
- Receives arm/disarm commands from a Home Assistant Template Alarm Control Panel via the `alarm_ultimate_command` event.
|
|
55
|
+
- Mirrors Alarm events back into Home Assistant by updating `input_select.alarm_ultimate_state`.
|
|
56
|
+
|
|
57
|
+
### Setup steps
|
|
58
|
+
|
|
59
|
+
1. **Install prerequisites**
|
|
60
|
+
- In Home Assistant, ensure the Node-RED Add-on is installed and running.
|
|
61
|
+
- In Node-RED, ensure `node-red-contrib-home-assistant-websocket` nodes are available.
|
|
62
|
+
|
|
63
|
+
2. **Create the helper entity in Home Assistant**
|
|
64
|
+
|
|
65
|
+
Add this to your `configuration.yaml` (or a `template:`/`input_select:` include), then restart Home Assistant:
|
|
66
|
+
|
|
67
|
+
```yaml
|
|
68
|
+
input_select:
|
|
69
|
+
alarm_ultimate_state:
|
|
70
|
+
name: Alarm Ultimate state
|
|
71
|
+
options:
|
|
72
|
+
- disarmed
|
|
73
|
+
- arming
|
|
74
|
+
- armed_away
|
|
75
|
+
- armed_home
|
|
76
|
+
- pending
|
|
77
|
+
- triggered
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
3. **Create the Template Alarm Control Panel**
|
|
81
|
+
|
|
82
|
+
This exposes an `alarm_control_panel` entity that the standard Home Assistant “Alarm Panel” dashboard card can use.
|
|
83
|
+
|
|
84
|
+
```yaml
|
|
85
|
+
template:
|
|
86
|
+
- alarm_control_panel:
|
|
87
|
+
- name: "Alarm Ultimate"
|
|
88
|
+
unique_id: alarm_ultimate
|
|
89
|
+
state: "{{ states('input_select.alarm_ultimate_state') }}"
|
|
90
|
+
code_format: no_code
|
|
91
|
+
arm_away:
|
|
92
|
+
- event: alarm_ultimate_command
|
|
93
|
+
event_data:
|
|
94
|
+
action: arm_away
|
|
95
|
+
arm_home:
|
|
96
|
+
- event: alarm_ultimate_command
|
|
97
|
+
event_data:
|
|
98
|
+
action: arm_home
|
|
99
|
+
disarm:
|
|
100
|
+
- event: alarm_ultimate_command
|
|
101
|
+
event_data:
|
|
102
|
+
action: disarm
|
|
103
|
+
trigger:
|
|
104
|
+
- event: alarm_ultimate_command
|
|
105
|
+
event_data:
|
|
106
|
+
action: trigger
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
4. **Import and configure the Node-RED flow**
|
|
110
|
+
- Import `examples/alarm-ultimate-home-assistant-alarm-panel.json`.
|
|
111
|
+
- Open the Home Assistant server config node in the flow and ensure it’s set for the Add-on (default in the example).
|
|
112
|
+
- Edit the `server-state-changed` node to match your real sensors (replace the example `binary_sensor.*` entity ids).
|
|
113
|
+
- In the `AlarmSystemUltimate` node, set each zone `topic` to the matching Home Assistant entity id (same strings as above).
|
|
114
|
+
|
|
115
|
+
5. **Add the Home Assistant dashboard card**
|
|
116
|
+
- Add the built-in “Alarm panel” card and select the entity `alarm_control_panel.alarm_ultimate`.
|
|
117
|
+
|
|
118
|
+
Notes:
|
|
119
|
+
- This integration uses a single Alarm “armed” state internally. The example maps `arm_home`/`arm_away` to HA states (`armed_home`/`armed_away`) for the UI by remembering the last requested arm mode.
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"type": "comment",
|
|
12
12
|
"z": "b81f9a4b2f9c1a10",
|
|
13
13
|
"name": "Requires node-red-dashboard",
|
|
14
|
-
"info": "Install `node-red-dashboard`, then import this flow.\n\nThe iframe uses a *relative* URL so it works even if Node-RED is hosted under a path prefix (eg Home Assistant Ingress).\nIf you changed `httpAdminRoot` / `httpRoot`, adjust the iframe URL accordingly.\n\nTip: Open the panel directly at `alarm-ultimate/alarm-panel?id=<alarmNodeId>` to test outside Dashboard.",
|
|
14
|
+
"info": "Install `node-red-dashboard`, then import this flow.\n\nThe iframe uses a *relative* URL that escapes the `/ui` path (`../alarm-ultimate/...`), so it works even if Node-RED is hosted under a path prefix (eg Home Assistant Ingress).\nIf you changed `httpAdminRoot` / `httpRoot`, adjust the iframe URL accordingly.\n\nTip: Open the panel directly at `alarm-ultimate/alarm-panel?id=<alarmNodeId>` to test outside Dashboard.",
|
|
15
15
|
"x": 250,
|
|
16
16
|
"y": 60,
|
|
17
17
|
"wires": []
|
|
@@ -57,6 +57,17 @@
|
|
|
57
57
|
"collapse": false,
|
|
58
58
|
"className": ""
|
|
59
59
|
},
|
|
60
|
+
{
|
|
61
|
+
"id": "d4c3c4e5f6078893",
|
|
62
|
+
"type": "ui_group",
|
|
63
|
+
"name": "Log",
|
|
64
|
+
"tab": "d9078c5d2b1a4f30",
|
|
65
|
+
"order": 4,
|
|
66
|
+
"disp": true,
|
|
67
|
+
"width": "12",
|
|
68
|
+
"collapse": false,
|
|
69
|
+
"className": ""
|
|
70
|
+
},
|
|
60
71
|
{
|
|
61
72
|
"id": "d0c1b2a3f4e50617",
|
|
62
73
|
"type": "AlarmSystemUltimate",
|
|
@@ -115,7 +126,7 @@
|
|
|
115
126
|
"order": 1,
|
|
116
127
|
"width": "12",
|
|
117
128
|
"height": "18",
|
|
118
|
-
"format": "<iframe src=\"alarm-ultimate/alarm-panel?embed=1&id=d0c1b2a3f4e50617\" style=\"width:100%; height:720px; border:0; border-radius:10px;\"></iframe>",
|
|
129
|
+
"format": "<iframe src=\"../alarm-ultimate/alarm-panel?embed=1&id=d0c1b2a3f4e50617\" style=\"width:100%; height:720px; border:0; border-radius:10px;\"></iframe>",
|
|
119
130
|
"storeOutMessages": false,
|
|
120
131
|
"fwdInMessages": false,
|
|
121
132
|
"resendOnRefresh": true,
|
|
@@ -124,6 +135,24 @@
|
|
|
124
135
|
"x": 520,
|
|
125
136
|
"y": 180
|
|
126
137
|
},
|
|
138
|
+
{
|
|
139
|
+
"id": "e1f2a3b4c5d60719",
|
|
140
|
+
"type": "ui_template",
|
|
141
|
+
"z": "b81f9a4b2f9c1a10",
|
|
142
|
+
"group": "d4c3c4e5f6078893",
|
|
143
|
+
"name": "Alarm Log (embed)",
|
|
144
|
+
"order": 1,
|
|
145
|
+
"width": "12",
|
|
146
|
+
"height": "10",
|
|
147
|
+
"format": "<iframe src=\"../alarm-ultimate/alarm-panel?embed=1&view=log&id=d0c1b2a3f4e50617\" style=\"width:100%; height:420px; border:0; border-radius:10px;\"></iframe>",
|
|
148
|
+
"storeOutMessages": false,
|
|
149
|
+
"fwdInMessages": false,
|
|
150
|
+
"resendOnRefresh": true,
|
|
151
|
+
"templateScope": "local",
|
|
152
|
+
"className": "",
|
|
153
|
+
"x": 520,
|
|
154
|
+
"y": 240
|
|
155
|
+
},
|
|
127
156
|
{
|
|
128
157
|
"id": "c44ac2b6af1a1e20",
|
|
129
158
|
"type": "ui_button",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"type": "comment",
|
|
12
12
|
"z": "c0a4b6d3e5f70819",
|
|
13
13
|
"name": "Requires @flowfuse/node-red-dashboard",
|
|
14
|
-
"info": "Install `@flowfuse/node-red-dashboard` (Dashboard 2.0), then import this flow.\n\nDashboard V2 URLs (default):\n- Panel: http://<node-red-host>:1880/dashboard/panel\n- Controls: http://<node-red-host>:1880/dashboard/controls\n- Sensors: http://<node-red-host>:1880/dashboard/sensors\n- Zones: http://<node-red-host>:1880/dashboard/zones\n- Status: http://<node-red-host>:1880/dashboard/status\n\nNotes:\n- If you changed the Dashboard path in the `ui-base` config, adjust `/dashboard` accordingly.\n- The embedded Alarm Panel is served from Node-RED *admin* endpoints (`/alarm-ultimate/alarm-panel`).",
|
|
14
|
+
"info": "Install `@flowfuse/node-red-dashboard` (Dashboard 2.0), then import this flow.\n\nDashboard V2 URLs (default):\n- Panel: http://<node-red-host>:1880/dashboard/panel\n- Controls: http://<node-red-host>:1880/dashboard/controls\n- Sensors: http://<node-red-host>:1880/dashboard/sensors\n- Zones: http://<node-red-host>:1880/dashboard/zones\n- Log: http://<node-red-host>:1880/dashboard/log\n- Status: http://<node-red-host>:1880/dashboard/status\n\nNotes:\n- If you changed the Dashboard path in the `ui-base` config, adjust `/dashboard` accordingly.\n- The embedded Alarm Panel is served from Node-RED *admin* endpoints (`/alarm-ultimate/alarm-panel`).",
|
|
15
15
|
"x": 290,
|
|
16
16
|
"y": 60,
|
|
17
17
|
"wires": []
|
|
@@ -100,6 +100,28 @@
|
|
|
100
100
|
[]
|
|
101
101
|
]
|
|
102
102
|
},
|
|
103
|
+
{
|
|
104
|
+
"id": "2d3e4f5061728394",
|
|
105
|
+
"type": "ui-template",
|
|
106
|
+
"z": "c0a4b6d3e5f70819",
|
|
107
|
+
"group": "5c6d7e8f901a2b3c",
|
|
108
|
+
"name": "Log (embed)",
|
|
109
|
+
"order": 1,
|
|
110
|
+
"width": 12,
|
|
111
|
+
"height": 14,
|
|
112
|
+
"head": "",
|
|
113
|
+
"format": "<template>\n <iframe :src=\"panelUrl\" style=\"width:100%; height:560px; border:0; border-radius:10px;\"></iframe>\n</template>\n\n<script>\nexport default {\n data () {\n return {\n panelUrl: ''\n }\n },\n mounted () {\n const alarmNodeId = 'a2b3c4d5e6f70819'\n const setupUrl = new URL('../_setup', window.location.href)\n\n fetch(setupUrl)\n .then(r => r.json())\n .then(setup => {\n const httpAdminRoot = setup?.RED?.httpAdminRoot ?? '/'\n const root = httpAdminRoot.endsWith('/') ? httpAdminRoot : `${httpAdminRoot}/`\n this.panelUrl = `${root}alarm-ultimate/alarm-panel?embed=1&view=log&id=${encodeURIComponent(alarmNodeId)}`\n })\n .catch(() => {\n const relative = `../alarm-ultimate/alarm-panel?embed=1&view=log&id=${encodeURIComponent(alarmNodeId)}`\n this.panelUrl = new URL(relative, window.location.href).toString()\n })\n }\n}\n</script>\n",
|
|
114
|
+
"storeOutMessages": true,
|
|
115
|
+
"passthru": true,
|
|
116
|
+
"resendOnRefresh": true,
|
|
117
|
+
"templateScope": "local",
|
|
118
|
+
"className": "",
|
|
119
|
+
"x": 1090,
|
|
120
|
+
"y": 200,
|
|
121
|
+
"wires": [
|
|
122
|
+
[]
|
|
123
|
+
]
|
|
124
|
+
},
|
|
103
125
|
{
|
|
104
126
|
"id": "b4c5d6e7f8091a2b",
|
|
105
127
|
"type": "function",
|
|
@@ -653,6 +675,42 @@
|
|
|
653
675
|
"visible": true,
|
|
654
676
|
"disabled": false
|
|
655
677
|
},
|
|
678
|
+
{
|
|
679
|
+
"id": "f5060718293b4c5d",
|
|
680
|
+
"type": "ui-page",
|
|
681
|
+
"name": "Log",
|
|
682
|
+
"ui": "c4d5e6f708192a3b",
|
|
683
|
+
"path": "/log",
|
|
684
|
+
"icon": "format-list-text",
|
|
685
|
+
"layout": "grid",
|
|
686
|
+
"theme": "b3c4d5e6f708192a",
|
|
687
|
+
"breakpoints": [
|
|
688
|
+
{
|
|
689
|
+
"name": "Default",
|
|
690
|
+
"px": 0,
|
|
691
|
+
"cols": 12
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
"name": "Tablet",
|
|
695
|
+
"px": 576,
|
|
696
|
+
"cols": 12
|
|
697
|
+
},
|
|
698
|
+
{
|
|
699
|
+
"name": "Small Desktop",
|
|
700
|
+
"px": 768,
|
|
701
|
+
"cols": 12
|
|
702
|
+
},
|
|
703
|
+
{
|
|
704
|
+
"name": "Desktop",
|
|
705
|
+
"px": 1024,
|
|
706
|
+
"cols": 12
|
|
707
|
+
}
|
|
708
|
+
],
|
|
709
|
+
"order": 5,
|
|
710
|
+
"className": "",
|
|
711
|
+
"visible": true,
|
|
712
|
+
"disabled": false
|
|
713
|
+
},
|
|
656
714
|
{
|
|
657
715
|
"id": "d3e4f5060718293a",
|
|
658
716
|
"type": "ui-page",
|
|
@@ -684,7 +742,7 @@
|
|
|
684
742
|
"cols": 12
|
|
685
743
|
}
|
|
686
744
|
],
|
|
687
|
-
"order":
|
|
745
|
+
"order": 6,
|
|
688
746
|
"className": "",
|
|
689
747
|
"visible": true,
|
|
690
748
|
"disabled": false
|
|
@@ -758,5 +816,19 @@
|
|
|
758
816
|
"visible": true,
|
|
759
817
|
"disabled": false,
|
|
760
818
|
"groupType": "default"
|
|
819
|
+
},
|
|
820
|
+
{
|
|
821
|
+
"id": "5c6d7e8f901a2b3c",
|
|
822
|
+
"type": "ui-group",
|
|
823
|
+
"name": "Log",
|
|
824
|
+
"page": "f5060718293b4c5d",
|
|
825
|
+
"width": 12,
|
|
826
|
+
"height": 1,
|
|
827
|
+
"order": 1,
|
|
828
|
+
"showTitle": true,
|
|
829
|
+
"className": "",
|
|
830
|
+
"visible": true,
|
|
831
|
+
"disabled": false,
|
|
832
|
+
"groupType": "default"
|
|
761
833
|
}
|
|
762
834
|
]
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"type": "comment",
|
|
12
12
|
"z": "a9f3e19b2b7c4d01",
|
|
13
13
|
"name": "Requires node-red-dashboard",
|
|
14
|
-
"info": "Installa `node-red-dashboard`, poi importa questo flow.\n\
|
|
14
|
+
"info": "Installa `node-red-dashboard`, poi importa questo flow.\n\nL'iframe usa un URL *relativo* che esce da `/ui` (`../alarm-ultimate/...`) così funziona anche con un path prefix (es. Home Assistant Ingress).\nPuoi usare anche `view=log` per mostrare il registro eventi all'utente.",
|
|
15
15
|
"x": 200,
|
|
16
16
|
"y": 60,
|
|
17
17
|
"wires": []
|
|
@@ -35,6 +35,17 @@
|
|
|
35
35
|
"collapse": false,
|
|
36
36
|
"className": ""
|
|
37
37
|
},
|
|
38
|
+
{
|
|
39
|
+
"id": "1e2d3c4b5a697801",
|
|
40
|
+
"type": "ui_group",
|
|
41
|
+
"name": "Log",
|
|
42
|
+
"tab": "f1c9d0f3d9b8a001",
|
|
43
|
+
"order": 2,
|
|
44
|
+
"disp": true,
|
|
45
|
+
"width": "12",
|
|
46
|
+
"collapse": false,
|
|
47
|
+
"className": ""
|
|
48
|
+
},
|
|
38
49
|
{
|
|
39
50
|
"id": "a1b2c3d4e5f6a7b8",
|
|
40
51
|
"type": "AlarmSystemUltimate",
|
|
@@ -87,7 +98,7 @@
|
|
|
87
98
|
"order": 1,
|
|
88
99
|
"width": "12",
|
|
89
100
|
"height": "18",
|
|
90
|
-
"format": "<iframe src=\"
|
|
101
|
+
"format": "<iframe src=\"../alarm-ultimate/alarm-panel?embed=1&id=a1b2c3d4e5f6a7b8\" style=\"width:100%; height:720px; border:0; border-radius:10px;\"></iframe>",
|
|
91
102
|
"storeOutMessages": false,
|
|
92
103
|
"fwdInMessages": false,
|
|
93
104
|
"resendOnRefresh": true,
|
|
@@ -95,5 +106,23 @@
|
|
|
95
106
|
"className": "",
|
|
96
107
|
"x": 520,
|
|
97
108
|
"y": 160
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"id": "2f1e0d3c4b5a6978",
|
|
112
|
+
"type": "ui_template",
|
|
113
|
+
"z": "a9f3e19b2b7c4d01",
|
|
114
|
+
"group": "1e2d3c4b5a697801",
|
|
115
|
+
"name": "Alarm Log (embed)",
|
|
116
|
+
"order": 1,
|
|
117
|
+
"width": "12",
|
|
118
|
+
"height": "10",
|
|
119
|
+
"format": "<iframe src=\"../alarm-ultimate/alarm-panel?embed=1&view=log&id=a1b2c3d4e5f6a7b8\" style=\"width:100%; height:420px; border:0; border-radius:10px;\"></iframe>",
|
|
120
|
+
"storeOutMessages": false,
|
|
121
|
+
"fwdInMessages": false,
|
|
122
|
+
"resendOnRefresh": true,
|
|
123
|
+
"templateScope": "local",
|
|
124
|
+
"className": "",
|
|
125
|
+
"x": 520,
|
|
126
|
+
"y": 220
|
|
98
127
|
}
|
|
99
128
|
]
|