node-red-contrib-alice 2.2.4 → 2.3.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.
Files changed (43) hide show
  1. package/.claude/settings.local.json +11 -0
  2. package/CLAUDE.md +54 -0
  3. package/nodes/alice-color.js +208 -231
  4. package/nodes/alice-device.html +6 -1
  5. package/nodes/alice-device.js +252 -286
  6. package/nodes/alice-event.js +110 -114
  7. package/nodes/alice-get.html +91 -0
  8. package/nodes/alice-get.js +9 -0
  9. package/nodes/alice-mode.js +136 -145
  10. package/nodes/alice-onoff.js +126 -130
  11. package/nodes/alice-range.js +144 -150
  12. package/nodes/alice-sensor.html +0 -2
  13. package/nodes/alice-sensor.js +101 -106
  14. package/nodes/alice-togle.js +118 -125
  15. package/nodes/alice-video.js +88 -132
  16. package/nodes/alice.js +127 -122
  17. package/nodes/types.js +3 -0
  18. package/package.json +22 -8
  19. package/src/alice-color.html +255 -0
  20. package/src/alice-color.ts +227 -0
  21. package/src/alice-device.html +94 -0
  22. package/src/alice-device.ts +301 -0
  23. package/src/alice-event.html +148 -0
  24. package/src/alice-event.ts +112 -0
  25. package/src/alice-get.html +67 -6
  26. package/src/alice-get.ts +12 -15
  27. package/src/alice-mode.html +296 -0
  28. package/src/alice-mode.ts +139 -0
  29. package/src/alice-onoff.html +93 -0
  30. package/src/alice-onoff.ts +132 -0
  31. package/src/alice-range.html +293 -0
  32. package/src/alice-range.ts +144 -0
  33. package/src/alice-sensor.html +307 -0
  34. package/src/alice-sensor.ts +103 -0
  35. package/src/alice-togle.html +96 -0
  36. package/src/alice-togle.ts +122 -0
  37. package/src/alice-video.html +90 -0
  38. package/src/alice-video.ts +99 -0
  39. package/src/alice.html +242 -0
  40. package/src/alice.ts +146 -0
  41. package/src/types.ts +157 -0
  42. package/tsconfig.json +13 -106
  43. package/.eslintrc.json +0 -20
@@ -0,0 +1,307 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType('Sensor',{
3
+ category: 'alice',
4
+ defaults:{
5
+ device: {value:"", type:"alice-device"},
6
+ name: {value:"Sensor"},
7
+ stype: {value:'devices.properties.float'},
8
+ instance: {value:undefined, validate: (v)=>{
9
+ if (v){
10
+ return true;
11
+ }else{
12
+ return false;
13
+ }
14
+ }},
15
+ unit:{value:undefined}
16
+ },
17
+ inputs:1,
18
+ outputs:0,
19
+ icon: "alice.png",
20
+ color: "#D8BFD8",
21
+ label: function(){
22
+ return this.name;
23
+ },
24
+ oneditprepare: function(){
25
+ $('#node-input-instance').on('change',()=>{
26
+ var val = $('#node-input-instance').find(":selected").val();
27
+ switch (val) {
28
+ case 'amperage':
29
+ $('#node-input-unit')
30
+ .find('option')
31
+ .remove()
32
+ .end()
33
+ .append('<option value="unit.ampere">Ampere (A)</option>')
34
+ .val('unit.ampere')
35
+ .prop('disabled', 'disabled');
36
+ break;
37
+ case 'battery_level':
38
+ $('#node-input-unit')
39
+ .find('option')
40
+ .remove()
41
+ .end()
42
+ .append('<option value="unit.percent">Percent (%)</option>')
43
+ .val('unit.percent')
44
+ .prop('disabled', 'disabled');
45
+ break;
46
+ case 'co2_level':
47
+ $('#node-input-unit')
48
+ .find('option')
49
+ .remove()
50
+ .end()
51
+ .append('<option value="unit.ppm">Parts per million (ppm)</option>')
52
+ .val('unit.ppm')
53
+ .prop('disabled', 'disabled');
54
+ break;
55
+ case 'food_level':
56
+ $('#node-input-unit')
57
+ .find('option')
58
+ .remove()
59
+ .end()
60
+ .append('<option value="unit.percent">Percent (%)</option>')
61
+ .val('unit.percent')
62
+ .prop('disabled', 'disabled');
63
+ break;
64
+ case 'humidity':
65
+ $('#node-input-unit')
66
+ .find('option')
67
+ .remove()
68
+ .end()
69
+ .append('<option value="unit.percent">Percent (%)</option>')
70
+ .val('unit.percent')
71
+ .prop('disabled', 'disabled');
72
+ break;
73
+ case 'power':
74
+ $('#node-input-unit')
75
+ .find('option')
76
+ .remove()
77
+ .end()
78
+ .append('<option value="unit.watt">Watt (W)</option>')
79
+ .val('unit.watt')
80
+ .prop('disabled', 'disabled');
81
+ break;
82
+ case 'temperature':
83
+ $('#node-input-unit')
84
+ .find('option')
85
+ .remove()
86
+ .end()
87
+ .append('<option value="unit.temperature.celsius">Celsius</option>')
88
+ .append('<option value="unit.temperature.kelvin">Kelvin</option>')
89
+ .prop('disabled', false);
90
+ if (this.unit && this.unit.includes("unit.temperature")){
91
+ $('#node-input-unit').val(this.unit);
92
+ }else{
93
+ $('#node-input-unit').val('unit.temperature.celsius');
94
+ }
95
+ break;
96
+ case 'voltage':
97
+ $('#node-input-unit')
98
+ .find('option')
99
+ .remove()
100
+ .end()
101
+ .append('<option value="unit.volt">Volt (V)</option>')
102
+ .val('unit.volt')
103
+ .prop('disabled', 'disabled');
104
+ break;
105
+ case 'water_level':
106
+ $('#node-input-unit')
107
+ .find('option')
108
+ .remove()
109
+ .end()
110
+ .append('<option value="unit.percent">Percent (%)</option>')
111
+ .val('unit.percent')
112
+ .prop('disabled', 'disabled');
113
+ break;
114
+ case 'illumination':
115
+ $('#node-input-unit')
116
+ .find('option')
117
+ .remove()
118
+ .end()
119
+ .append('<option value="unit.illumination.lux">Lux (lx)</option>')
120
+ .val('unit.illumination.lux')
121
+ .prop('disabled', 'disabled');
122
+ break;
123
+ case 'pressure':
124
+ $('#node-input-unit')
125
+ .find('option')
126
+ .remove()
127
+ .end()
128
+ .append('<option value="unit.pressure.atm">Atmosphere (atm)</option>')
129
+ .append('<option value="unit.pressure.pascal">Pascal (pascal)</option>')
130
+ .append('<option value="unit.pressure.bar">Bar (bar)</option>')
131
+ .append('<option value="unit.pressure.mmhg">Millimeters of mercury (mmhg)</option>')
132
+ // .val('unit.pressure.bar')
133
+ .prop('disabled', false);
134
+ if (this.unit && this.unit.includes("unit.pressure")){
135
+ $('#node-input-unit').val(this.unit);
136
+ }else{
137
+ $('#node-input-unit').val('unit.pressure.bar');
138
+ }
139
+ break;
140
+ case 'pm1_density':
141
+ $('#node-input-unit')
142
+ .find('option')
143
+ .remove()
144
+ .end()
145
+ .append('<option value="unit.density.mcg_m3">μg/m3</option>')
146
+ .val('unit.density.mcg_m3')
147
+ .prop('disabled', 'disabled');
148
+ break;
149
+ case 'pm2.5_density':
150
+ $('#node-input-unit')
151
+ .find('option')
152
+ .remove()
153
+ .end()
154
+ .append('<option value="unit.density.mcg_m3">μg/m3</option>')
155
+ .val('unit.density.mcg_m3')
156
+ .prop('disabled', 'disabled');
157
+ break;
158
+ case 'pm10_density':
159
+ $('#node-input-unit')
160
+ .find('option')
161
+ .remove()
162
+ .end()
163
+ .append('<option value="unit.density.mcg_m3">μg/m3</option>')
164
+ .val('unit.density.mcg_m3')
165
+ .prop('disabled', 'disabled');
166
+ break;
167
+ case 'tvoc':
168
+ $('#node-input-unit')
169
+ .find('option')
170
+ .remove()
171
+ .end()
172
+ .append('<option value="unit.density.mcg_m3">μg/m3</option>')
173
+ .val('unit.density.mcg_m3')
174
+ .prop('disabled', 'disabled');
175
+ break;
176
+ case 'electricity_meter':
177
+ $('#node-input-unit')
178
+ .find('option')
179
+ .remove()
180
+ .end()
181
+ .append('<option value="unit.kilowatt_hour">kW⋅h</option>')
182
+ .val('unit.kilowatt_hour')
183
+ .prop('disabled', 'disabled');
184
+ break;
185
+ case 'gas_meter':
186
+ $('#node-input-unit')
187
+ .find('option')
188
+ .remove()
189
+ .end()
190
+ .append('<option value="unit.cubic_meter">m3</option>')
191
+ .val('unit.cubic_meter')
192
+ .prop('disabled', 'disabled');
193
+ break;
194
+ case 'heat_meter':
195
+ $('#node-input-unit')
196
+ .find('option')
197
+ .remove()
198
+ .end()
199
+ .append('<option value="unit.gigacalorie">Gcal</option>')
200
+ .val('unit.gigacalorie')
201
+ .prop('disabled', 'disabled');
202
+ break;
203
+ case 'water_meter':
204
+ $('#node-input-unit')
205
+ .find('option')
206
+ .remove()
207
+ .end()
208
+ .append('<option value="unit.cubic_meter">m3</option>')
209
+ .val('unit.cubic_meter')
210
+ .prop('disabled', 'disabled');
211
+ break;
212
+ case 'meter':
213
+ $('#node-input-unit')
214
+ .find('option')
215
+ .remove()
216
+ .end()
217
+ .prop('disabled', 'disabled');
218
+ break;
219
+ default:
220
+ $('#node-input-unit').prop('disabled', 'disabled');
221
+ break;
222
+ };
223
+ });
224
+ },
225
+ oneditsave: function(){
226
+ deivcename = $('#node-input-device option:selected').text();
227
+ instance = $('#node-input-instance option:selected').text();
228
+ $('#node-input-name').val(deivcename+":"+instance);
229
+ }
230
+ })
231
+ </script>
232
+
233
+ <script type="text/x-red" data-template-name="Sensor">
234
+ <input type="hidden" id="node-input-name">
235
+ <div class="form-row">
236
+ <label for="node-input-device">Device</label>
237
+ <input id="node-input-device">
238
+ </div>
239
+ <div class="form-row">
240
+ <label for="node-input-instance">Sensor Type</label>
241
+ <select id="node-input-instance" style="width: 70%;">
242
+ <option value="amperage">Amperage</option>
243
+ <option value="battery_level">Battery level</option>
244
+ <option value="co2_level">CO2 level</option>
245
+ <option value="food_level">Food level</option>
246
+ <option value="humidity">Humidity</option>
247
+ <option value="illumination">Illumination</option>
248
+ <option value="pm1_density">PM 1 density</option>
249
+ <option value="pm2.5_density">PM 2.5_density</option>
250
+ <option value="pm10_density">PM 10 density</option>
251
+ <option value="power">Power</option>
252
+ <option value="pressure">Pressure</option>
253
+ <option value="temperature">Temperature</option>
254
+ <option value="tvoc">TVOC</option>
255
+ <option value="voltage">Voltage</option>
256
+ <option value="water_level">Water level</option>
257
+ <option value="electricity_meter">Electricity counter</option>
258
+ <option value="gas_meter">Gas counter</option>
259
+ <option value="water_meter">Water counter</option>
260
+ <option value="meter">Universal counter</option>
261
+ </select>
262
+ </div>
263
+
264
+ <div class="form-row">
265
+ <label for="node-input-unit">Unit</label>
266
+ <select id="node-input-unit" style="width: 70%;">
267
+ <option value="unit.percent">Percent (%)</option>
268
+ <option value="unit.ppm">Parts per million (ppm)</option>
269
+ <option value="unit.temperature.celsius">Celsius</option>
270
+ <option value="unit.temperature.kelvin">Kelvin</option>
271
+ <option value="unit.ampere">Ampere (A)</option>
272
+ <option value="unit.volt">Volt (V)</option>
273
+ <option value="unit.watt">Watt (W)</option>
274
+ <option value="unit.illumination.lux">Lux (lx)</option>
275
+ <option value="unit.density.mcg_m3">μg/m3</option>
276
+ </select>
277
+ </div>
278
+ </script>
279
+
280
+ <script type="text/x-red" data-help-name="Sensor">
281
+ <p>Transmits data from sensors built into the device.</p>
282
+
283
+ <h3>Property</h3>
284
+ <dl class="message-properties">
285
+ <dt>Device
286
+ <span class="property-type">Select</span>
287
+ </dt>
288
+ <dd> The device to which this sensor is connected </dd>
289
+ <dt>Sensor Type
290
+ <span class="property-type">Select</span>
291
+ </dt>
292
+ <dd>a type of sensor that helps to better understand the area of its use and the data it collects</dd>
293
+ </dl>
294
+
295
+ <h3>Inputs</h3>
296
+ <dl class="message-properties">
297
+ <dt>payload
298
+ <span class="property-type">boolean/float</span>
299
+ </dt>
300
+ <dd>bollean or float</dd>
301
+ </dl>
302
+
303
+ <h3>References</h3>
304
+ <ul>
305
+ <li><a href="https://yandex.ru/dev/dialogs/alice/doc/smart-home/concepts/properties-types.html"> - Yandex documentation</a></li>
306
+ </ul>
307
+ </script>
@@ -0,0 +1,103 @@
1
+ import { NodeAPI, Node } from "node-red";
2
+ import { AliceSensorConfig, AliceDeviceNode, SensorState } from "./types.js";
3
+
4
+ export = (RED: NodeAPI): void => {
5
+ function AliceSensor(this: Node, config: AliceSensorConfig): void {
6
+ RED.nodes.createNode(this, config);
7
+ const device = RED.nodes.getNode(config.device) as AliceDeviceNode;
8
+ device.setMaxListeners(device.getMaxListeners() + 1);
9
+
10
+ const id = JSON.parse(JSON.stringify(this.id));
11
+ const stype = config.stype;
12
+ const unit = config.unit;
13
+ const instance = config.instance;
14
+
15
+ const curentState: SensorState = {
16
+ type: stype,
17
+ state: {
18
+ instance: instance,
19
+ value: 0
20
+ }
21
+ };
22
+
23
+ this.status({ fill: "red", shape: "dot", text: "offline" });
24
+
25
+ const init = (): void => {
26
+ this.debug("Starting sensor initilization ...");
27
+ const sensor = {
28
+ type: stype,
29
+ reportable: true,
30
+ retrievable: true,
31
+ parameters: {
32
+ instance: instance,
33
+ unit: unit
34
+ }
35
+ };
36
+
37
+ device.setSensor(id, sensor)
38
+ .then(() => {
39
+ this.debug("Sensor initilization - success!");
40
+ this.status({ fill: "green", shape: "dot", text: "online" });
41
+ })
42
+ .catch(err => {
43
+ this.error("Error on create sensor: " + err.message);
44
+ this.status({ fill: "red", shape: "dot", text: "error" });
45
+ });
46
+ };
47
+
48
+ if (device.initState) init();
49
+
50
+ device.on("online", () => {
51
+ init();
52
+ });
53
+
54
+ device.on("offline", () => {
55
+ this.status({ fill: "red", shape: "dot", text: "offline" });
56
+ });
57
+
58
+ this.on('input', (msg, _send, done) => {
59
+ if (typeof msg.payload != 'number') {
60
+ this.error("Wrong type! msg.payload must be number.");
61
+ if (done) { done(); }
62
+ return;
63
+ }
64
+ if (unit == 'unit.temperature.celsius' || unit == 'unit.ampere') {
65
+ msg.payload = +msg.payload.toFixed(1);
66
+ } else {
67
+ msg.payload = +msg.payload.toFixed(0);
68
+ }
69
+ if (curentState.state.value == msg.payload) {
70
+ this.debug("Value not changed. Cancel update");
71
+ if (done) { done(); }
72
+ return;
73
+ }
74
+ curentState.state.value = msg.payload;
75
+ device.updateSensorState(id, curentState)
76
+ .then(() => {
77
+ this.status({ fill: "green", shape: "dot", text: String(msg.payload) });
78
+ if (done) { done(); }
79
+ })
80
+ .catch(err => {
81
+ this.error("Error on update sensor state: " + err.message);
82
+ this.status({ fill: "red", shape: "dot", text: "Error" });
83
+ if (done) { done(); }
84
+ });
85
+ });
86
+
87
+ this.on('close', (removed: boolean, done: () => void) => {
88
+ if (removed) {
89
+ device.delSensor(id)
90
+ .then(() => { done(); })
91
+ .catch(err => {
92
+ this.error("Error on delete property: " + err.message);
93
+ done();
94
+ });
95
+ } else {
96
+ device.setMaxListeners(device.getMaxListeners() - 1);
97
+ done();
98
+ }
99
+ });
100
+ }
101
+
102
+ RED.nodes.registerType("Sensor", AliceSensor);
103
+ };
@@ -0,0 +1,96 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType('Toggle',{
3
+ category: 'alice',
4
+ defaults:{
5
+ device: {value:"", type:"alice-device"},
6
+ name: {value:""},
7
+ instance: {value:"backlight"},
8
+ response: {value:true}
9
+ },
10
+ inputs:1,
11
+ outputs:1,
12
+ icon: "alice.png",
13
+ color: "#D8BFD8",
14
+ label: function(){
15
+ return this.name + ":"+this.instance;
16
+ },
17
+ oneditprepare: function(){
18
+ if (this.response === undefined){
19
+ $( "#node-input-response").prop('checked', true);
20
+ }
21
+ },
22
+ oneditsave: function(){
23
+ deivcename = $('#node-input-device option:selected').text();
24
+ $('#node-input-name').val(deivcename);
25
+ }
26
+ })
27
+ </script>
28
+
29
+ <script type="text/x-red" data-template-name="Toggle">
30
+ <input type="hidden" id="node-input-name">
31
+ <div class="form-row">
32
+ <label for="node-input-device">Device</label>
33
+ <input id="node-input-device">
34
+ </div>
35
+ <div class="form-row">
36
+ <label for="node-input-instance">Instance</label>
37
+ <select id="node-input-instance" style="width: 70%;">
38
+ <option value="backlight">Backlight</option>
39
+ <option value="controls_locked">Locking</option>
40
+ <option value="ionization" >Ionization</option>
41
+ <option value="keep_warm">Keep warm</option>
42
+ <option value="mute">Mute</option>
43
+ <option value="oscillation">Oscillation</option>
44
+ <option value="pause">Pause</option>
45
+ </select>
46
+ </div>
47
+ <div class="form-row">
48
+ <label for="node-input-response"><i class="fa fa-refresh"></i> <span >Response</span></label>
49
+ <label for="node-input-response" style="width:70%">
50
+ <input type="checkbox" id="node-input-response" style="display:inline-block; width:22px; vertical-align:baseline;" autocomplete="off"><span>Always answer Alice with success</span>
51
+ </label>
52
+ </div>
53
+ </script>
54
+
55
+ <script type="text/x-red" data-help-name="Toggle">
56
+ <p>Manage device settings that can only be in one of two states.</p>
57
+ <p>The toggle skill has features. Functions are characterized by certain parameters and
58
+ voice scripts that allow you to describe the corresponding skills of the device.</p>
59
+
60
+ <h3>Property</h3>
61
+ <dl class="message-properties">
62
+ <dt>Device
63
+ <span class="property-type">Select</span>
64
+ </dt>
65
+ <dd> The device to which this feature is connected </dd>
66
+ <dt>Instance
67
+ <span class="property-type">Select</span>
68
+ </dt>
69
+ <dd> The name of the function for this skill. Affects which voice command is called </dd>
70
+ <dt>Response
71
+ <span class="property-type">checkbox</span>
72
+ </dt>
73
+ <dd> In order for the device to respond to Alice that the command was successful, the corresponding value should arrive at the input within 2.5 seconds.
74
+ If your device takes longer or doesn’t return a confirmation at all, just check this box. </dd>
75
+ </dl>
76
+
77
+ <h3>Inputs</h3>
78
+ <dl class="message-properties">
79
+ <dt>payload
80
+ <span class="property-type">boolean</span>
81
+ </dt>
82
+ <dd> true or false </dd>
83
+ </dl>
84
+
85
+ <h3>Outputs</h3>
86
+ <dl class="message-properties">
87
+ <dt>payload
88
+ <span class="property-type">boolean</span>
89
+ </dt>
90
+ <dd> true or false </dd>
91
+ </dl>
92
+ <h3>References</h3>
93
+ <ul>
94
+ <li><a href="https://yandex.ru/dev/dialogs/alice/doc/smart-home/concepts/toggle-instance-docpage/"> - Yandex documentation</a></li>
95
+ </ul>
96
+ </script>
@@ -0,0 +1,122 @@
1
+ import { NodeAPI, Node } from "node-red";
2
+ import { AliceCapabilityConfig, AliceDeviceNode, CapabilityState } from "./types.js";
3
+
4
+ export = (RED: NodeAPI): void => {
5
+ function AliceToggle(this: Node, config: AliceCapabilityConfig): void {
6
+ RED.nodes.createNode(this, config);
7
+ const device = RED.nodes.getNode(config.device) as AliceDeviceNode;
8
+ device.setMaxListeners(device.getMaxListeners() + 1);
9
+
10
+ const ctype = 'devices.capabilities.toggle';
11
+ const instance = config.instance || '';
12
+ let response = config.response;
13
+ let value = false;
14
+
15
+ if (config.response === undefined) {
16
+ response = true;
17
+ }
18
+
19
+ this.status({ fill: "red", shape: "dot", text: "offline" });
20
+
21
+ const init = (): void => {
22
+ this.debug("Starting capability initilization ...");
23
+ const capab = {
24
+ type: ctype,
25
+ retrievable: true,
26
+ reportable: true,
27
+ parameters: {
28
+ instance: instance,
29
+ }
30
+ };
31
+ device.setCapability(this.id, capab)
32
+ .then(() => {
33
+ this.debug("Capability initilization - success!");
34
+ this.status({ fill: "green", shape: "dot", text: "online" });
35
+ })
36
+ .catch(err => {
37
+ this.error("Error on create capability: " + err.message);
38
+ this.status({ fill: "red", shape: "dot", text: "error" });
39
+ });
40
+ };
41
+
42
+ if (device.initState) init();
43
+
44
+ device.on("online", () => {
45
+ init();
46
+ });
47
+
48
+ device.on("offline", () => {
49
+ this.status({ fill: "red", shape: "dot", text: "offline" });
50
+ });
51
+
52
+ device.on(this.id, (val: boolean) => {
53
+ this.debug("Received a new value from Yandex...");
54
+ this.send({ payload: val });
55
+ const state: CapabilityState = {
56
+ type: ctype,
57
+ state: {
58
+ instance: instance,
59
+ value: val
60
+ }
61
+ };
62
+ if (response) {
63
+ this.debug("Automatic confirmation is true, sending confirmation to Yandex ...");
64
+ device.updateCapabState(this.id, state)
65
+ .then(() => {
66
+ value = val;
67
+ this.status({ fill: "green", shape: "dot", text: String(val) });
68
+ })
69
+ .catch(err => {
70
+ this.error("Error on update capability state: " + err.message);
71
+ this.status({ fill: "red", shape: "dot", text: "Error" });
72
+ });
73
+ }
74
+ });
75
+
76
+ this.on('input', (msg, _send, done) => {
77
+ if (typeof msg.payload != 'boolean') {
78
+ this.error("Wrong type! msg.payload must be boolean.");
79
+ if (done) { done(); }
80
+ return;
81
+ }
82
+ if (msg.payload === value) {
83
+ this.debug("Value not changed. Cancel update");
84
+ if (done) { done(); }
85
+ return;
86
+ }
87
+ const state: CapabilityState = {
88
+ type: ctype,
89
+ state: {
90
+ instance: instance,
91
+ value: msg.payload
92
+ }
93
+ };
94
+ device.updateCapabState(this.id, state)
95
+ .then(() => {
96
+ value = msg.payload as boolean;
97
+ this.status({ fill: "green", shape: "dot", text: String(msg.payload) });
98
+ if (done) { done(); }
99
+ })
100
+ .catch(err => {
101
+ this.error("Error on update capability state: " + err.message);
102
+ this.status({ fill: "red", shape: "dot", text: "Error" });
103
+ if (done) { done(); }
104
+ });
105
+ });
106
+
107
+ this.on('close', (removed: boolean, done: () => void) => {
108
+ if (removed) {
109
+ device.delCapability(this.id)
110
+ .then(() => { done(); })
111
+ .catch(err => {
112
+ this.error("Error on delete capability: " + err.message);
113
+ done();
114
+ });
115
+ } else {
116
+ done();
117
+ }
118
+ });
119
+ }
120
+
121
+ RED.nodes.registerType("Toggle", AliceToggle);
122
+ };