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,255 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType('Color',{
3
+ category: 'alice',
4
+ defaults:{
5
+ device: {value:"", type:"alice-device"},
6
+ name: {value:""},
7
+ color_support:{value:true},
8
+ scheme: {value:"rgb_normal"},
9
+ response:{value:true},
10
+ temperature_k: {value:false},
11
+ temperature_min: {value:2000, validate:function(v){
12
+ let value = parseInt(v);
13
+ return value>=2000;
14
+ }},
15
+ temperature_max: {value:9000, validate:function(v){
16
+ let value = parseInt(v);
17
+ return value<=9000;
18
+ }},
19
+ color_scene: {value:[]}
20
+ },
21
+ inputs:1,
22
+ outputs:3,
23
+ outputLabels: ["color","temperature_k","color_scene"],
24
+ icon: "alice.png",
25
+ color: "#D8BFD8",
26
+ label: function(){
27
+ return this.name + ":Color";
28
+ },
29
+ oneditprepare: function(){
30
+ if (this.response === undefined){
31
+ $( "#node-input-response").prop('checked', true);
32
+ };
33
+ if (this.color_support === undefined){
34
+ $( "#node-input-color_support").prop('checked', true);
35
+ };
36
+ if (this.temperature_min === undefined){
37
+ $('#node-input-temperature_min').val(2000);
38
+ };
39
+ if (this.temperature_max === undefined){
40
+ $('#node-input-temperature_max').val(9000);
41
+ };
42
+ $('#node-input-color_support').on('change',()=>{
43
+ if ($('#node-input-color_support').is(":checked")){
44
+ $('#node-input-scheme').prop('disabled', false);
45
+ }else{
46
+ $('#node-input-scheme').prop('disabled', true);
47
+ };
48
+ });
49
+ $('#node-input-temperature_k').on('change',()=>{
50
+ if ($('#node-input-temperature_k').is(":checked")){
51
+ $('#node-input-temperature_min').prop('disabled', false);
52
+ $('#node-input-temperature_max').prop('disabled', false);
53
+ }else{
54
+ $('#node-input-temperature_min').prop('disabled', true);
55
+ $('#node-input-temperature_max').prop('disabled', true);
56
+ };
57
+ });
58
+ updateAllScenes(this.color_scene);
59
+ },
60
+ oneditsave: function(){
61
+ deivcename = $('#node-input-device option:selected').text();
62
+ $('#node-input-name').val(deivcename);
63
+ this.color_scene = getCurrentScenes();
64
+ }
65
+ })
66
+ function updateAllScenes(scenes){
67
+ deleteAllScenes();
68
+ scenes.forEach((s,i)=>{
69
+ addScene2List(s,i);
70
+ });
71
+ };
72
+ function deleteAllScenes() {
73
+ $('#node-input-color_scene-container').empty();
74
+ };
75
+ function addScene2List(scene,index){
76
+ $('#node-input-color_scene-container').append('<li rel="'+scene+'" class="red-ui-editableList-item-sortable red-ui-editableList-item-removable"><div class="red-ui-editableList-item-content" style="overflow: hidden; white-space: nowrap;"><span>'+scene+'</span></div><a href="#" onclick="delScene('+index+')" class="red-ui-editableList-item-remove red-ui-button red-ui-button-small"><i class="fa fa-remove"></i></a></li>')
77
+ };
78
+ function getCurrentScenes() {
79
+ let scenes = [];
80
+ $('#node-input-color_scene-container li').each(function(){
81
+ scenes.push($(this).attr('rel'));
82
+ });
83
+ return scenes;
84
+ };
85
+ function addScene() {
86
+ let scene = $('#select-custom-scene').find(":selected").val();
87
+ let scenes = getCurrentScenes();
88
+ if (scenes.indexOf(scene)>-1){
89
+ return;
90
+ }else{
91
+ scenes.push(scene);
92
+ updateAllScenes(scenes);
93
+ }
94
+ };
95
+ function delScene(index) {
96
+ let scenes = getCurrentScenes();
97
+ scenes.splice(index, 1);
98
+ updateAllScenes(scenes);
99
+ };
100
+ </script>
101
+
102
+ <script type="text/x-red" data-template-name="Color">
103
+ <input type="hidden" id="node-input-name">
104
+ <div class="form-row" style="border-bottom: 1px solid #ddd; padding-bottom: 12px;">
105
+ <label for="node-input-device">Device</label>
106
+ <input id="node-input-device">
107
+ </div>
108
+ <div class="form-row" style="border-bottom: 1px solid #ddd; padding-bottom: 12px;">
109
+ <label for="node-input-color_support"><span >Color</span></label>
110
+ <label for="node-input-color_support" style="width:70%">
111
+ <input type="checkbox" id="node-input-color_support" style="display:inline-block; width:22px; vertical-align:baseline;" autocomplete="off"><span> supports color change</span>
112
+ </label>
113
+ <label for="node-input-scheme">Scheme</label>
114
+ <select id="node-input-scheme" style="width: 70%;">
115
+ <option value="rgb_normal">RGB</option>
116
+ <!-- <option value="rgb">RGB (24bit)</option> -->
117
+ <option value="hsv">HSV</option>
118
+ </select>
119
+ </div>
120
+ <div class="form-row" style="border-bottom: 1px solid #ddd; padding-bottom: 12px;">
121
+ <label for="node-input-temperature_k"><span>Wite Temp</span></label>
122
+ <label for="node-input-temperature_k" style="width:70%">
123
+ <input type="checkbox" id="node-input-temperature_k" style="display:inline-block; width:22px; vertical-align:baseline;" autocomplete="off">
124
+ <span> white temperature support</span>
125
+ </label>
126
+ <div>
127
+ <label for="node-input-temperature_x"><span>Temp Range</span></label>
128
+ <span style="padding-left: 4px;"> Min</span>
129
+ <input id="node-input-temperature_min" type="number" style="width: 70px;">
130
+ <span> K</span>
131
+ <span style="padding-left: 10px;"> Max</span>
132
+ <input id="node-input-temperature_max" type="number" style="width: 70px;">
133
+ <span> K</span>
134
+ </div>
135
+ </div>
136
+
137
+ <div class="form-row node-input-rule-container-row" style="border-bottom: 1px solid #ddd; padding-bottom: 12px;">
138
+ <label for="node-input-color_scene" style="width:auto">Supported color scenes</label>
139
+ <div class="red-ui-editableList">
140
+ <div class="red-ui-editableList-border red-ui-editableList-container" style="min-height: 150px; max-height: none; overflow-y: scroll; height: 308.4px;">
141
+ <ol id="node-input-color_scene-container" class="red-ui-editableList-list" style="min-height: 100px; min-width: 150px; ">
142
+ <li class="red-ui-editableList-item-sortable red-ui-editableList-item-removable">
143
+ <div class="red-ui-editableList-item-content" style="overflow: hidden; white-space: nowrap;">
144
+ <span>Tets</span>
145
+ </div>
146
+ <i class="red-ui-editableList-item-handle fa fa-bullhorn" style="cursor:auto"></i><a href="#" class="red-ui-editableList-item-remove red-ui-button red-ui-button-small"><i class="fa fa-remove"></i></a>
147
+ </li>
148
+ <li class="red-ui-editableList-item-sortable red-ui-editableList-item-removable">
149
+ <div class="red-ui-editableList-item-content" style="overflow: hidden; white-space: nowrap;">
150
+ <span>Tets2</span>
151
+ </div>
152
+ <i class="red-ui-editableList-item-handle fa fa-bullhorn" style="cursor:auto"></i><a href="#" class="red-ui-editableList-item-remove red-ui-button red-ui-button-small"><i class="fa fa-remove"></i></a>
153
+ </li>
154
+ </ol>
155
+ </div>
156
+ <div style="margin-top:4px">
157
+ <select id="select-custom-scene">
158
+ <option value="alarm">alarm</option>
159
+ <option value="alice">alice</option>
160
+ <option value="candle">candle</option>
161
+ <option value="dinner">dinner</option>
162
+ <option value="fantasy">fantasy</option>
163
+ <option value="garland">garland</option>
164
+ <option value="jungle">jungle</option>
165
+ <option value="movie">movie</option>
166
+ <option value="neon">neon</option>
167
+ <option value="night">night</option>
168
+ <option value="ocean">ocean</option>
169
+ <option value="party">party</option>
170
+ <option value="reading">reading</option>
171
+ <option value="rest">rest</option>
172
+ <option value="romance">romance</option>
173
+ <option value="siren">siren</option>
174
+ <option value="sunrise">sunrise</option>
175
+ <option value="sunset">sunset</option>
176
+ </select>
177
+ <a id="button-addScene" href="#" class="red-ui-button" onclick="addScene()" style="margin-top: 4px;"><i class="fa fa-plus"></i></a>
178
+ </div>
179
+ </div>
180
+ </div>
181
+
182
+ <div class="form-row">
183
+ <label for="node-input-response"><i class="fa fa-refresh"></i> <span >Response</span></label>
184
+ <label for="node-input-response" style="width:70%">
185
+ <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>
186
+ </label>
187
+ </div>
188
+ </script>
189
+
190
+ <script type="text/x-red" data-help-name="Color">
191
+ <p>Change the color of their luminous elements to any one from the HSV model or from the RGB model;</p>
192
+
193
+ <h3>Property</h3>
194
+ <dl class="message-properties">
195
+ <dt>Device
196
+ <span class="property-type">Select</span>
197
+ </dt>
198
+ <dd> The device to which this feature is connected </dd>
199
+ <dt>Color Scheme
200
+ <span class="property-type">Select</span>
201
+ </dt>
202
+ <dd> Color scheme selection (RGB or HSV) </dd>
203
+ <dt>White temperature support
204
+ <span class="property-type">Integer</span>
205
+ </dt>
206
+ <dd>White temperature support, min and max value in Kelvin</dd>
207
+ <dd>Min value must be above 2000 kelvin</dd>
208
+ <dd>Max value must be below 9000 kelvin</dd>
209
+ <dt>Supported color scenes
210
+ <span class="property-type">array of strings</span>
211
+ </dt>
212
+ <dd>List of supported color scenes</dd>
213
+ <dt>Response
214
+ <span class="property-type">checkbox</span>
215
+ </dt>
216
+ <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.
217
+ If your device takes longer or doesn’t return a confirmation at all, just check this box. </dd>
218
+ </dl>
219
+
220
+ <h3>Inputs</h3>
221
+ <dl class="message-properties">
222
+ <dt>payload
223
+ <span class="property-type">Object || Integer || String</span>
224
+ </dt>
225
+ <dd> If you want to set the color, msg.payload must be RGB or HSV Object </br><i>{r,g,b} or {h,s,v}</i></dd>
226
+ <dd> If you want white temperature, msg.payload must be Integer >=MIN and <=MAX</dd>
227
+ <dd> If you want to set the color scene, the msg.payload must be String set in the settings</dd>
228
+ </dl>
229
+
230
+ <h3>Outputs 1</h3>
231
+ <dl class="message-properties">
232
+ <dt>payload
233
+ <span class="property-type">Object</span>
234
+ </dt>
235
+ <dd> RGB or HSV Object </br><i>{r,g,b} or {h,s,v}</i></dd>
236
+ </dl>
237
+ <h3>Outputs 2</h3>
238
+ <dl class="message-properties">
239
+ <dt>payload
240
+ <span class="property-type">Integer</span>
241
+ </dt>
242
+ <dd> White temperature in Kelvin</dd>
243
+ </dl>
244
+ <h3>Outputs 3</h3>
245
+ <dl class="message-properties">
246
+ <dt>payload
247
+ <span class="property-type">String</span>
248
+ </dt>
249
+ <dd> Color scene text name</dd>
250
+ </dl>
251
+ <h3>References</h3>
252
+ <ul>
253
+ <li><a href="https://yandex.ru/dev/dialogs/alice/doc/smart-home/concepts/color_setting-docpage/"> Yandex documentation</a></li>
254
+ </ul>
255
+ </script>
@@ -0,0 +1,227 @@
1
+ import { NodeAPI, Node } from "node-red";
2
+ import { AliceColorConfig, AliceDeviceNode, CapabilityState, CapabilityRegistration } from "./types.js";
3
+
4
+ interface RGBColor {
5
+ r: number;
6
+ g: number;
7
+ b: number;
8
+ }
9
+
10
+ interface HSVColor {
11
+ h: number;
12
+ s: number;
13
+ v: number;
14
+ }
15
+
16
+ export = (RED: NodeAPI): void => {
17
+ function AliceColor(this: Node, config: AliceColorConfig): void {
18
+ RED.nodes.createNode(this, config);
19
+ const device = RED.nodes.getNode(config.device) as AliceDeviceNode;
20
+ device.setMaxListeners(device.getMaxListeners() + 1);
21
+
22
+ const ctype = 'devices.capabilities.color_setting';
23
+ let scheme = config.scheme;
24
+ const temperature_k = config.temperature_k;
25
+ const temperature_min = parseInt(config.temperature_min);
26
+ const temperature_max = parseInt(config.temperature_max);
27
+ const color_scene = config.color_scene || [];
28
+ let needConvert = false;
29
+ let response = config.response;
30
+ let color_support = config.color_support;
31
+ let lastValue: string | undefined;
32
+
33
+ if (scheme == "rgb_normal") {
34
+ scheme = "rgb";
35
+ needConvert = true;
36
+ }
37
+ if (config.response === undefined) {
38
+ response = true;
39
+ }
40
+ if (config.color_support === undefined) {
41
+ color_support = true;
42
+ }
43
+
44
+ const init = (): void => {
45
+ const parameters: Record<string, any> = {};
46
+ const capab: CapabilityRegistration = {
47
+ type: ctype,
48
+ retrievable: true,
49
+ reportable: true,
50
+ parameters: parameters
51
+ };
52
+
53
+ if (!color_support && !temperature_k && color_scene.length < 1) {
54
+ this.error("Error on create capability: At least one parameter must be enabled");
55
+ this.status({ fill: "red", shape: "dot", text: "error" });
56
+ return;
57
+ }
58
+
59
+ if (color_scene.length > 0) {
60
+ const scenes = color_scene.map(s => ({ id: s }));
61
+ parameters.color_scene = { scenes: scenes };
62
+ }
63
+
64
+ if (color_support) {
65
+ parameters.color_model = scheme;
66
+ }
67
+
68
+ if (temperature_k) {
69
+ parameters.temperature_k = {
70
+ min: temperature_min,
71
+ max: temperature_max
72
+ };
73
+ }
74
+
75
+ device.setCapability(this.id, capab)
76
+ .then(() => {
77
+ this.status({ fill: "green", shape: "dot", text: "online" });
78
+ })
79
+ .catch(err => {
80
+ this.error("Error on create capability: " + err.message);
81
+ this.status({ fill: "red", shape: "dot", text: "error" });
82
+ });
83
+ };
84
+
85
+ if (device.initState) init();
86
+
87
+ device.on("online", () => {
88
+ init();
89
+ });
90
+
91
+ device.on("offline", () => {
92
+ this.status({ fill: "red", shape: "dot", text: "offline" });
93
+ });
94
+
95
+ device.on(this.id, (val: any, newstate: { instance: string }) => {
96
+ // отправляем данные на выход
97
+ const outmsgs: Array<{ payload: any } | null> = [null, null, null];
98
+ switch (newstate.instance) {
99
+ case 'rgb': {
100
+ const value: RGBColor = {
101
+ r: val >> 16,
102
+ g: val >> 8 & 0xFF,
103
+ b: val & 0xFF
104
+ };
105
+ outmsgs[0] = { payload: value };
106
+ break;
107
+ }
108
+ case 'hsv':
109
+ outmsgs[0] = { payload: val };
110
+ break;
111
+ case 'temperature_k':
112
+ outmsgs[1] = { payload: val };
113
+ break;
114
+ case 'scene':
115
+ outmsgs[2] = { payload: val };
116
+ break;
117
+ }
118
+ this.send(outmsgs);
119
+
120
+ // возвращаем подтверждение в базу
121
+ const state: CapabilityState = {
122
+ type: ctype,
123
+ state: {
124
+ instance: newstate.instance,
125
+ value: val
126
+ }
127
+ };
128
+ if (response) {
129
+ device.updateCapabState(this.id, state)
130
+ .then(() => {
131
+ lastValue = JSON.stringify(val);
132
+ this.status({ fill: "green", shape: "dot", text: "online" });
133
+ })
134
+ .catch(err => {
135
+ this.error("Error on update capability state: " + err.message);
136
+ this.status({ fill: "red", shape: "dot", text: "Error" });
137
+ });
138
+ }
139
+ });
140
+
141
+ this.on('input', (msg, _send, done) => {
142
+ let value: any = msg.payload;
143
+ const state: { value?: any; instance?: string } = {};
144
+
145
+ switch (typeof value) {
146
+ case 'object': {
147
+ const obj = value as RGBColor | HSVColor;
148
+ if (('r' in obj && 'g' in obj && 'b' in obj) || ('h' in obj && 's' in obj && 'v' in obj)) {
149
+ if (scheme == 'rgb' && 'r' in obj) {
150
+ value = (obj as RGBColor).r << 16 | (obj as RGBColor).g << 8 | (obj as RGBColor).b;
151
+ }
152
+ state.value = value;
153
+ state.instance = scheme;
154
+ } else {
155
+ this.error("Wrong type! For Color, msg.payload must be RGB or HSV Object.");
156
+ if (done) { done(); }
157
+ return;
158
+ }
159
+ break;
160
+ }
161
+ case 'number': {
162
+ value = Math.round(value as number);
163
+ if (value >= temperature_min && value <= temperature_max) {
164
+ state.value = value;
165
+ state.instance = 'temperature_k';
166
+ } else {
167
+ this.error("Wrong type! For Temperature_k, msg.payload must be >=MIN and <=MAX.");
168
+ if (done) { done(); }
169
+ return;
170
+ }
171
+ break;
172
+ }
173
+ case 'string':
174
+ if (color_scene.includes(value as string)) {
175
+ state.value = value;
176
+ state.instance = 'scene';
177
+ } else {
178
+ this.error("Wrong type! For the Scene, the msg.payload must be set in the settings");
179
+ if (done) { done(); }
180
+ return;
181
+ }
182
+ break;
183
+ default:
184
+ this.error("Wrong type! Unsupported msg.payload type");
185
+ if (done) { done(); }
186
+ return;
187
+ }
188
+
189
+ if (JSON.stringify(value) === lastValue) {
190
+ this.debug("Value not changed. Cancel update");
191
+ if (done) { done(); }
192
+ return;
193
+ }
194
+
195
+ const upState: CapabilityState = {
196
+ type: ctype,
197
+ state: state as CapabilityState['state']
198
+ };
199
+ device.updateCapabState(this.id, upState)
200
+ .then(() => {
201
+ lastValue = JSON.stringify(value);
202
+ this.status({ fill: "green", shape: "dot", text: JSON.stringify(msg.payload) });
203
+ if (done) { done(); }
204
+ })
205
+ .catch(err => {
206
+ this.error("Error on update capability state: " + err.message);
207
+ this.status({ fill: "red", shape: "dot", text: "Error" });
208
+ if (done) { done(); }
209
+ });
210
+ });
211
+
212
+ this.on('close', (removed: boolean, done: () => void) => {
213
+ if (removed) {
214
+ device.delCapability(this.id)
215
+ .then(() => { done(); })
216
+ .catch(err => {
217
+ this.error("Error on delete capability: " + err.message);
218
+ done();
219
+ });
220
+ } else {
221
+ done();
222
+ }
223
+ });
224
+ }
225
+
226
+ RED.nodes.registerType("Color", AliceColor);
227
+ };
@@ -0,0 +1,94 @@
1
+ <script type="text/javascript">
2
+ RED.nodes.registerType('alice-device',{
3
+ category: 'config',
4
+ defaults:{
5
+ service: {value:"", type:"alice-service"},
6
+ name: {value:null, required: true},
7
+ description: {value:null},
8
+ room: {value:"Комната"},
9
+ dtype: {value:"devices.types.light", required: true}
10
+ },
11
+ label: function(){
12
+ return this.room+":"+this.name || "Alice-Device";
13
+ },
14
+ oneditprepare: ()=>{
15
+ $("#node-config-input-dtype").typedInput({
16
+ types: [
17
+ {
18
+ options: [
19
+ { value: "devices.types.light", label: "Light" },
20
+ { value: "devices.types.light.ceiling", label: "Light Сeiling" },
21
+ { value: "devices.types.light.strip", label: "Light Strip" },
22
+ { value: "devices.types.socket", label: "Socket" },
23
+ { value: "devices.types.switch", label: "Switch" },
24
+ { value: "devices.types.thermostat", label: "Thermostat" },
25
+ { value: "devices.types.thermostat.ac", label: "Air conditioning" },
26
+ { value: "devices.types.media_device", label: "Multimedia" },
27
+ { value: "devices.types.media_device.tv", label: "TV" },
28
+ { value: "devices.types.media_device.tv_box", label: "TV Box" },
29
+ { value: "devices.types.media_device.receiver", label: "AV Receiver" },
30
+ { value: "devices.types.camera", label: "Camera" },
31
+ { value: "devices.types.cooking", label: "Kitchen appliances" },
32
+ { value: "devices.types.cooking.coffee_maker", label: "Coffee machine" },
33
+ { value: "devices.types.cooking.kettle", label: "Smart kettle" },
34
+ { value: "devices.types.cooking.multicooker", label: "Multicooker" },
35
+ { value: "devices.types.openable", label: "Door, gate, window, shutters" },
36
+ { value: "devices.types.openable.curtain", label: "Curtains, blinds" },
37
+ { value: "devices.types.openable.valve", label: "Valve (ball valve)" },
38
+ { value: "devices.types.humidifier", label: "Humidifier" },
39
+ { value: "devices.types.purifier", label: "Air purifier" },
40
+ { value: "devices.types.ventilation", label: "Ventilation" },
41
+ { value: "devices.types.ventilation.fan", label: "Fan" },
42
+ { value: "devices.types.vacuum_cleaner", label: "Vacuum cleaner robot" },
43
+ { value: "devices.types.washing_machine", label: "Washing machine" },
44
+ { value: "devices.types.dishwasher", label: "Dishwasher" },
45
+ { value: "devices.types.iron", label: "Iron, steam generator" },
46
+ { value: "devices.types.sensor", label: "Sensor" },
47
+ { value: "devices.types.sensor.motion", label: "Sensor motion" },
48
+ { value: "devices.types.sensor.vibration", label: "Sensor vibration" },
49
+ { value: "devices.types.sensor.illumination", label: "Sensor illumination" },
50
+ { value: "devices.types.sensor.open", label: "Sensor open" },
51
+ { value: "devices.types.sensor.climate", label: "Sensor climate" },
52
+ { value: "devices.types.sensor.water_leak", label: "Sensor water_leak" },
53
+ { value: "devices.types.sensor.button", label: "Sensor button" },
54
+ { value: "devices.types.sensor.gas", label: "Sensor gas" },
55
+ { value: "devices.types.sensor.smoke", label: "Sensor smoke" },
56
+ { value: "devices.types.smart_meter", label: "Counter" },
57
+ { value: "devices.types.smart_meter.cold_water", label: "Cold Water counter" },
58
+ { value: "devices.types.smart_meter.hot_water", label: "Hot Water counter" },
59
+ { value: "devices.types.smart_meter.electricity", label: "Electricity counter" },
60
+ { value: "devices.types.smart_meter.gas", label: "Gas counter" },
61
+ { value: "devices.types.smart_meter.heat", label: "Heat Water counter" },
62
+ { value: "devices.types.pet_drinking_fountain", label: "Pet drinking fountain" },
63
+ { value: "devices.types.pet_feeder", label: "Pet feeder" },
64
+ { value: "devices.types.other", label: "Other" },
65
+ ]
66
+ }
67
+ ]
68
+ });
69
+ }
70
+ })
71
+ </script>
72
+
73
+ <script type="text/x-red" data-template-name="alice-device">
74
+ <div class="form-row">
75
+ <label for="node-config-input-service">Credentials</label>
76
+ <input id="node-config-input-service">
77
+ </div>
78
+ <div class="form-row">
79
+ <label for="node-config-input-name">Name</label>
80
+ <input type="text" id="node-config-input-name" placeholder="Name">
81
+ </div>
82
+ <div class="form-row">
83
+ <label for="node-config-input-description">Description</label>
84
+ <input type="text" id="node-config-input-description" placeholder="">
85
+ </div>
86
+ <div class="form-row">
87
+ <label for="node-config-input-room">Room</label>
88
+ <input type="text" id="node-config-input-room" placeholder="Room">
89
+ </div>
90
+ <div class="form-row">
91
+ <label for="node-config-input-dtype">Type</label>
92
+ <input type="text" id="node-config-input-dtype">
93
+ </div>
94
+ </script>