homebridge-smartsystem 6.1.6 → 6.1.7
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 +1 -0
- package/config.json +578 -0
- package/duotecno/api.js +73 -0
- package/duotecno/api.ts +61 -0
- package/duotecno/config.js +105 -0
- package/duotecno/config.ts +118 -0
- package/package.json +1 -1
- package/server/powerbase.js +1 -1
- package/server/powerbase.ts +1 -1
- package/server/views/login.ejs +57 -0
package/README.md
CHANGED
|
@@ -257,6 +257,7 @@ Homebridge UI version
|
|
|
257
257
|
- 4: errors when starting power mgrs from platform.ts
|
|
258
258
|
- 5: v6.1.3 changes were gone... restored them.
|
|
259
259
|
- 6: added login with pw in config + http.get with on-error
|
|
260
|
+
- 7: allow NaN in bindings compare for update
|
|
260
261
|
|
|
261
262
|
## Hardware
|
|
262
263
|
A Raspberry Pi that connects to a Duotecno IP Node
|
package/config.json
ADDED
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manufacturer": "Duotecno-Coppieterscc",
|
|
3
|
+
"platform": "DuotecnoPlatform",
|
|
4
|
+
"smappee": {
|
|
5
|
+
"rules": [
|
|
6
|
+
{
|
|
7
|
+
"type": "sun",
|
|
8
|
+
"channel": "0",
|
|
9
|
+
"low": 30,
|
|
10
|
+
"high": 900,
|
|
11
|
+
"actions": [
|
|
12
|
+
{
|
|
13
|
+
"name": "Mood A",
|
|
14
|
+
"value": false,
|
|
15
|
+
"masterAddress": "192.168.0.97",
|
|
16
|
+
"masterPort": 5001,
|
|
17
|
+
"logicalAddress": 10,
|
|
18
|
+
"logicalNodeAddress": 252
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "Mood A",
|
|
22
|
+
"value": 50,
|
|
23
|
+
"masterAddress": "192.168.0.97",
|
|
24
|
+
"masterPort": 5001,
|
|
25
|
+
"logicalAddress": 10,
|
|
26
|
+
"logicalNodeAddress": 252
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"name": "Mood A",
|
|
30
|
+
"value": true,
|
|
31
|
+
"masterAddress": "192.168.0.97",
|
|
32
|
+
"masterPort": 5001,
|
|
33
|
+
"logicalAddress": 10,
|
|
34
|
+
"logicalNodeAddress": 252
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"power": -3382,
|
|
38
|
+
"current": 0
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"type": "power",
|
|
42
|
+
"channel": "4",
|
|
43
|
+
"low": 10,
|
|
44
|
+
"high": 20,
|
|
45
|
+
"actions": [
|
|
46
|
+
{
|
|
47
|
+
"name": "Mood B",
|
|
48
|
+
"value": false,
|
|
49
|
+
"masterAddress": "192.168.0.97",
|
|
50
|
+
"masterPort": 5001,
|
|
51
|
+
"logicalAddress": 11,
|
|
52
|
+
"logicalNodeAddress": 252
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"name": "Mood B",
|
|
56
|
+
"value": 50,
|
|
57
|
+
"masterAddress": "192.168.0.97",
|
|
58
|
+
"masterPort": 5001,
|
|
59
|
+
"logicalAddress": 11,
|
|
60
|
+
"logicalNodeAddress": 252
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "Mood B",
|
|
64
|
+
"value": true,
|
|
65
|
+
"masterAddress": "192.168.0.97",
|
|
66
|
+
"masterPort": 5001,
|
|
67
|
+
"logicalAddress": 11,
|
|
68
|
+
"logicalNodeAddress": 252
|
|
69
|
+
}
|
|
70
|
+
],
|
|
71
|
+
"power": 0,
|
|
72
|
+
"current": 0
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"type": "power",
|
|
76
|
+
"channel": "7+8+9",
|
|
77
|
+
"low": 30,
|
|
78
|
+
"high": 900,
|
|
79
|
+
"actions": [
|
|
80
|
+
{
|
|
81
|
+
"name": "Mood C",
|
|
82
|
+
"value": false,
|
|
83
|
+
"masterAddress": "192.168.0.97",
|
|
84
|
+
"masterPort": 5001,
|
|
85
|
+
"logicalAddress": 12,
|
|
86
|
+
"logicalNodeAddress": 252
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"name": "Mood C",
|
|
90
|
+
"value": 50,
|
|
91
|
+
"masterAddress": "192.168.0.97",
|
|
92
|
+
"masterPort": 5001,
|
|
93
|
+
"logicalAddress": 12,
|
|
94
|
+
"logicalNodeAddress": 252
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"name": "Mood C",
|
|
98
|
+
"value": true,
|
|
99
|
+
"masterAddress": "192.168.0.97",
|
|
100
|
+
"masterPort": 5001,
|
|
101
|
+
"logicalAddress": 12,
|
|
102
|
+
"logicalNodeAddress": 252
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
"power": 2207,
|
|
106
|
+
"current": 2
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"type": "sun",
|
|
110
|
+
"channel": "7",
|
|
111
|
+
"low": 0,
|
|
112
|
+
"high": 0,
|
|
113
|
+
"actions": [
|
|
114
|
+
{
|
|
115
|
+
"name": "Mood C",
|
|
116
|
+
"value": false,
|
|
117
|
+
"masterAddress": "192.168.0.97",
|
|
118
|
+
"masterPort": 5001,
|
|
119
|
+
"logicalAddress": 12,
|
|
120
|
+
"logicalNodeAddress": 252
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"name": "Mood C",
|
|
124
|
+
"value": 50,
|
|
125
|
+
"masterAddress": "192.168.0.97",
|
|
126
|
+
"masterPort": 5001,
|
|
127
|
+
"logicalAddress": 12,
|
|
128
|
+
"logicalNodeAddress": 252
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"name": "Mood C",
|
|
132
|
+
"value": true,
|
|
133
|
+
"masterAddress": "192.168.0.97",
|
|
134
|
+
"masterPort": 5001,
|
|
135
|
+
"logicalAddress": 12,
|
|
136
|
+
"logicalNodeAddress": 252
|
|
137
|
+
}
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
],
|
|
141
|
+
"address": "192.168.0.186",
|
|
142
|
+
"uid": "57e3e0d8-bb05-4b04-8662-1a9871998f3f",
|
|
143
|
+
"debug": false,
|
|
144
|
+
"bindings": [
|
|
145
|
+
{
|
|
146
|
+
"name": "<Solar",
|
|
147
|
+
"value": 0,
|
|
148
|
+
"masterAddress": "192.168.0.97",
|
|
149
|
+
"masterPort": 5001,
|
|
150
|
+
"logicalAddress": 102,
|
|
151
|
+
"logicalNodeAddress": 252,
|
|
152
|
+
"channel": "0+1",
|
|
153
|
+
"power": 1706
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"name": ">Grid",
|
|
157
|
+
"value": 0,
|
|
158
|
+
"masterAddress": "192.168.0.97",
|
|
159
|
+
"masterPort": 5001,
|
|
160
|
+
"logicalAddress": 100,
|
|
161
|
+
"logicalNodeAddress": 252,
|
|
162
|
+
"channel": "2+3",
|
|
163
|
+
"power": 2494
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"name": ">Cars",
|
|
167
|
+
"value": 0,
|
|
168
|
+
"masterAddress": "192.168.0.97",
|
|
169
|
+
"masterPort": 5001,
|
|
170
|
+
"logicalAddress": 103,
|
|
171
|
+
"logicalNodeAddress": 252,
|
|
172
|
+
"channel": "4+5",
|
|
173
|
+
"power": 0
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"name": ">Swimmingpool",
|
|
177
|
+
"value": 0,
|
|
178
|
+
"masterAddress": "192.168.0.97",
|
|
179
|
+
"masterPort": 5001,
|
|
180
|
+
"logicalAddress": 101,
|
|
181
|
+
"logicalNodeAddress": 252,
|
|
182
|
+
"channel": "8",
|
|
183
|
+
"power": 0
|
|
184
|
+
}
|
|
185
|
+
]
|
|
186
|
+
},
|
|
187
|
+
"p1": {
|
|
188
|
+
"rules": [
|
|
189
|
+
{
|
|
190
|
+
"type": "power",
|
|
191
|
+
"channel": "3+4+5",
|
|
192
|
+
"low": 30,
|
|
193
|
+
"high": 1500,
|
|
194
|
+
"actions": [
|
|
195
|
+
{
|
|
196
|
+
"name": "--",
|
|
197
|
+
"value": false,
|
|
198
|
+
"masterAddress": "192.168.0.97",
|
|
199
|
+
"masterPort": 5001,
|
|
200
|
+
"logicalAddress": 0,
|
|
201
|
+
"logicalNodeAddress": 0
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"name": "--",
|
|
205
|
+
"value": 50,
|
|
206
|
+
"masterAddress": "192.168.0.97",
|
|
207
|
+
"masterPort": 5001,
|
|
208
|
+
"logicalAddress": 0,
|
|
209
|
+
"logicalNodeAddress": 0
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"name": "Alles aan",
|
|
213
|
+
"value": true,
|
|
214
|
+
"masterAddress": "192.168.0.97",
|
|
215
|
+
"masterPort": 5001,
|
|
216
|
+
"logicalAddress": 5,
|
|
217
|
+
"logicalNodeAddress": 3
|
|
218
|
+
}
|
|
219
|
+
]
|
|
220
|
+
}
|
|
221
|
+
],
|
|
222
|
+
"bindings": [
|
|
223
|
+
{
|
|
224
|
+
"name": "<Solar Panels",
|
|
225
|
+
"value": 0,
|
|
226
|
+
"masterAddress": "192.168.0.97",
|
|
227
|
+
"masterPort": 5001,
|
|
228
|
+
"logicalAddress": 102,
|
|
229
|
+
"logicalNodeAddress": 252,
|
|
230
|
+
"channel": "0+1+2",
|
|
231
|
+
"power": null
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"name": ">Grid",
|
|
235
|
+
"value": 0,
|
|
236
|
+
"masterAddress": "192.168.0.97",
|
|
237
|
+
"masterPort": 5001,
|
|
238
|
+
"logicalAddress": 100,
|
|
239
|
+
"logicalNodeAddress": 252,
|
|
240
|
+
"channel": "3+4+5",
|
|
241
|
+
"power": null
|
|
242
|
+
}
|
|
243
|
+
],
|
|
244
|
+
"address": "192.168.0.214"
|
|
245
|
+
},
|
|
246
|
+
"shelly": false,
|
|
247
|
+
"smartapp": {
|
|
248
|
+
"apiPort": 8581,
|
|
249
|
+
"apiHost": "192.168.0.99",
|
|
250
|
+
"socapp": false,
|
|
251
|
+
"port": 5002,
|
|
252
|
+
"switches": [
|
|
253
|
+
{
|
|
254
|
+
"name": "Shelly Valies",
|
|
255
|
+
"unitName": "Red",
|
|
256
|
+
"masterAddress": "192.168.0.97",
|
|
257
|
+
"masterPort": 5001,
|
|
258
|
+
"nomacro": "Y",
|
|
259
|
+
"nostop": "Y",
|
|
260
|
+
"logicalAddress": 107,
|
|
261
|
+
"logicalNodeAddress": 252,
|
|
262
|
+
"type": "httpdim",
|
|
263
|
+
"plug": "http://192.168.0.96/light/0?brightness=$&turn=#",
|
|
264
|
+
"data": "",
|
|
265
|
+
"method": "GET",
|
|
266
|
+
"value": 0
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
"name": "Screen Salon Top",
|
|
270
|
+
"unitName": "Salon Top",
|
|
271
|
+
"masterAddress": "192.168.0.97",
|
|
272
|
+
"masterPort": 5001,
|
|
273
|
+
"nomacro": "N",
|
|
274
|
+
"nostop": "N",
|
|
275
|
+
"logicalAddress": 105,
|
|
276
|
+
"logicalNodeAddress": 252,
|
|
277
|
+
"type": "somfy",
|
|
278
|
+
"plug": "1",
|
|
279
|
+
"data": "",
|
|
280
|
+
"method": "GET",
|
|
281
|
+
"value": 0
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
"name": "Screen Tim small",
|
|
285
|
+
"unitName": "Tim Small",
|
|
286
|
+
"masterAddress": "192.168.0.97",
|
|
287
|
+
"masterPort": 5001,
|
|
288
|
+
"nomacro": "N",
|
|
289
|
+
"nostop": "Y",
|
|
290
|
+
"logicalAddress": 104,
|
|
291
|
+
"logicalNodeAddress": 252,
|
|
292
|
+
"type": "httpupdown",
|
|
293
|
+
"plug": "2",
|
|
294
|
+
"data": "",
|
|
295
|
+
"method": "GET",
|
|
296
|
+
"value": 0
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
"name": "Test Plug",
|
|
300
|
+
"unitName": "Plug",
|
|
301
|
+
"masterAddress": "gm.coppieters.be",
|
|
302
|
+
"masterPort": 5005,
|
|
303
|
+
"nomacro": "N",
|
|
304
|
+
"nostop": "N",
|
|
305
|
+
"logicalAddress": 35,
|
|
306
|
+
"logicalNodeAddress": 16,
|
|
307
|
+
"type": "smappee",
|
|
308
|
+
"plug": "1",
|
|
309
|
+
"data": "",
|
|
310
|
+
"method": "GET",
|
|
311
|
+
"value": 0
|
|
312
|
+
}
|
|
313
|
+
],
|
|
314
|
+
"debug": false
|
|
315
|
+
},
|
|
316
|
+
"system": {
|
|
317
|
+
"language": "EN",
|
|
318
|
+
"stores": false,
|
|
319
|
+
"multiple": false,
|
|
320
|
+
"socketserver": "ws.duotecno.eu",
|
|
321
|
+
"socketport": 9999,
|
|
322
|
+
"mood": "sfeer",
|
|
323
|
+
"debug": false,
|
|
324
|
+
"cmasters": [
|
|
325
|
+
{
|
|
326
|
+
"address": "192.168.0.97",
|
|
327
|
+
"port": 5001,
|
|
328
|
+
"password": "GMTecno",
|
|
329
|
+
"name": "Valies",
|
|
330
|
+
"active": true,
|
|
331
|
+
"nodenames": {
|
|
332
|
+
"3": "SB 1",
|
|
333
|
+
"4": "oled",
|
|
334
|
+
"252": " 20211003"
|
|
335
|
+
},
|
|
336
|
+
"debug": false
|
|
337
|
+
}
|
|
338
|
+
],
|
|
339
|
+
"cunits": [
|
|
340
|
+
{
|
|
341
|
+
"active": "Y",
|
|
342
|
+
"used": "Y",
|
|
343
|
+
"group": 0,
|
|
344
|
+
"name": "<Solar Panels",
|
|
345
|
+
"displayName": "<Solar Panels",
|
|
346
|
+
"type": 4,
|
|
347
|
+
"extendedType": null,
|
|
348
|
+
"masterAddress": "192.168.0.97",
|
|
349
|
+
"masterPort": 5001,
|
|
350
|
+
"logicalNodeAddress": 252,
|
|
351
|
+
"logicalAddress": 102
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
"active": "Y",
|
|
355
|
+
"used": "Y",
|
|
356
|
+
"group": 0,
|
|
357
|
+
"name": ">Cars",
|
|
358
|
+
"displayName": ">Cars",
|
|
359
|
+
"type": 4,
|
|
360
|
+
"extendedType": null,
|
|
361
|
+
"masterAddress": "192.168.0.97",
|
|
362
|
+
"masterPort": 5001,
|
|
363
|
+
"logicalNodeAddress": 252,
|
|
364
|
+
"logicalAddress": 103
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
"active": "Y",
|
|
368
|
+
"used": "Y",
|
|
369
|
+
"group": 0,
|
|
370
|
+
"name": ">Grid",
|
|
371
|
+
"displayName": ">Grid",
|
|
372
|
+
"type": 4,
|
|
373
|
+
"extendedType": null,
|
|
374
|
+
"masterAddress": "192.168.0.97",
|
|
375
|
+
"masterPort": 5001,
|
|
376
|
+
"logicalNodeAddress": 252,
|
|
377
|
+
"logicalAddress": 100
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
"active": "Y",
|
|
381
|
+
"used": "Y",
|
|
382
|
+
"group": 0,
|
|
383
|
+
"name": ">Swimmingpool",
|
|
384
|
+
"displayName": ">Swimmingpool",
|
|
385
|
+
"type": 4,
|
|
386
|
+
"extendedType": null,
|
|
387
|
+
"masterAddress": "192.168.0.97",
|
|
388
|
+
"masterPort": 5001,
|
|
389
|
+
"logicalNodeAddress": 252,
|
|
390
|
+
"logicalAddress": 101
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
"active": "Y",
|
|
394
|
+
"used": "Y",
|
|
395
|
+
"group": 0,
|
|
396
|
+
"name": "Salon Top",
|
|
397
|
+
"displayName": "Salon Top",
|
|
398
|
+
"type": 8,
|
|
399
|
+
"extendedType": 8,
|
|
400
|
+
"masterAddress": "192.168.0.97",
|
|
401
|
+
"masterPort": 5001,
|
|
402
|
+
"logicalNodeAddress": 252,
|
|
403
|
+
"logicalAddress": 105
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
"active": "Y",
|
|
407
|
+
"used": "Y",
|
|
408
|
+
"group": 0,
|
|
409
|
+
"name": "Tim Small",
|
|
410
|
+
"displayName": "Tim Small",
|
|
411
|
+
"type": 8,
|
|
412
|
+
"extendedType": 8,
|
|
413
|
+
"masterAddress": "192.168.0.97",
|
|
414
|
+
"masterPort": 5001,
|
|
415
|
+
"logicalNodeAddress": 252,
|
|
416
|
+
"logicalAddress": 104
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
"active": "Y",
|
|
420
|
+
"used": "Y",
|
|
421
|
+
"group": 0,
|
|
422
|
+
"name": "Plug 1",
|
|
423
|
+
"displayName": "Plug 1",
|
|
424
|
+
"type": 2,
|
|
425
|
+
"extendedType": 2,
|
|
426
|
+
"masterAddress": "192.168.0.97",
|
|
427
|
+
"masterPort": 5001,
|
|
428
|
+
"logicalNodeAddress": 252,
|
|
429
|
+
"logicalAddress": 106
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
"active": "Y",
|
|
433
|
+
"used": "Y",
|
|
434
|
+
"group": 0,
|
|
435
|
+
"name": "Smartbox",
|
|
436
|
+
"displayName": "Smartbox",
|
|
437
|
+
"type": 4,
|
|
438
|
+
"extendedType": null,
|
|
439
|
+
"masterAddress": "192.168.0.97",
|
|
440
|
+
"masterPort": 5001,
|
|
441
|
+
"logicalNodeAddress": 3,
|
|
442
|
+
"logicalAddress": 1
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
"active": "Y",
|
|
446
|
+
"used": "Y",
|
|
447
|
+
"group": 0,
|
|
448
|
+
"name": "Valies",
|
|
449
|
+
"displayName": "Valies",
|
|
450
|
+
"type": 4,
|
|
451
|
+
"extendedType": null,
|
|
452
|
+
"masterAddress": "192.168.0.97",
|
|
453
|
+
"masterPort": 5001,
|
|
454
|
+
"logicalNodeAddress": 3,
|
|
455
|
+
"logicalAddress": 0
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
"active": "Y",
|
|
459
|
+
"used": "Y",
|
|
460
|
+
"group": 0,
|
|
461
|
+
"name": "Blue LED",
|
|
462
|
+
"displayName": "Blue LED",
|
|
463
|
+
"type": 1,
|
|
464
|
+
"extendedType": null,
|
|
465
|
+
"masterAddress": "192.168.0.97",
|
|
466
|
+
"masterPort": 5001,
|
|
467
|
+
"logicalNodeAddress": 3,
|
|
468
|
+
"logicalAddress": 30
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
"active": "Y",
|
|
472
|
+
"used": "Y",
|
|
473
|
+
"group": 0,
|
|
474
|
+
"name": "Red LED",
|
|
475
|
+
"displayName": "Red LED",
|
|
476
|
+
"type": 1,
|
|
477
|
+
"extendedType": null,
|
|
478
|
+
"masterAddress": "192.168.0.97",
|
|
479
|
+
"masterPort": 5001,
|
|
480
|
+
"logicalNodeAddress": 3,
|
|
481
|
+
"logicalAddress": 29
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
"active": "Y",
|
|
485
|
+
"used": "Y",
|
|
486
|
+
"group": 0,
|
|
487
|
+
"name": "Bulb",
|
|
488
|
+
"displayName": "Bulb",
|
|
489
|
+
"type": 2,
|
|
490
|
+
"extendedType": 2,
|
|
491
|
+
"masterAddress": "192.168.0.97",
|
|
492
|
+
"masterPort": 5001,
|
|
493
|
+
"logicalNodeAddress": 3,
|
|
494
|
+
"logicalAddress": 35
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
"active": "Y",
|
|
498
|
+
"used": "Y",
|
|
499
|
+
"group": 0,
|
|
500
|
+
"name": "Lamp-Bottom",
|
|
501
|
+
"displayName": "Lamp-Bottom",
|
|
502
|
+
"type": 2,
|
|
503
|
+
"extendedType": 2,
|
|
504
|
+
"masterAddress": "192.168.0.97",
|
|
505
|
+
"masterPort": 5001,
|
|
506
|
+
"logicalNodeAddress": 3,
|
|
507
|
+
"logicalAddress": 34
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
"active": "Y",
|
|
511
|
+
"used": "Y",
|
|
512
|
+
"group": 0,
|
|
513
|
+
"name": "Lamp-Top",
|
|
514
|
+
"displayName": "Lamp-Top",
|
|
515
|
+
"type": 2,
|
|
516
|
+
"extendedType": 2,
|
|
517
|
+
"masterAddress": "192.168.0.97",
|
|
518
|
+
"masterPort": 5001,
|
|
519
|
+
"logicalNodeAddress": 3,
|
|
520
|
+
"logicalAddress": 33
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
"active": "Y",
|
|
524
|
+
"used": "Y",
|
|
525
|
+
"group": 0,
|
|
526
|
+
"name": "Stopcontact stk",
|
|
527
|
+
"displayName": "Stopcontact stk",
|
|
528
|
+
"type": 2,
|
|
529
|
+
"extendedType": 2,
|
|
530
|
+
"masterAddress": "192.168.0.97",
|
|
531
|
+
"masterPort": 5001,
|
|
532
|
+
"logicalNodeAddress": 3,
|
|
533
|
+
"logicalAddress": 36
|
|
534
|
+
},
|
|
535
|
+
{
|
|
536
|
+
"active": "Y",
|
|
537
|
+
"used": "Y",
|
|
538
|
+
"group": 0,
|
|
539
|
+
"name": "Alles aan",
|
|
540
|
+
"displayName": "Alles aan",
|
|
541
|
+
"type": 7,
|
|
542
|
+
"extendedType": 7,
|
|
543
|
+
"masterAddress": "192.168.0.97",
|
|
544
|
+
"masterPort": 5001,
|
|
545
|
+
"logicalNodeAddress": 3,
|
|
546
|
+
"logicalAddress": 5
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
"active": "Y",
|
|
550
|
+
"used": "Y",
|
|
551
|
+
"group": 0,
|
|
552
|
+
"name": "Alles uit",
|
|
553
|
+
"displayName": "Alles uit",
|
|
554
|
+
"type": 7,
|
|
555
|
+
"extendedType": 7,
|
|
556
|
+
"masterAddress": "192.168.0.97",
|
|
557
|
+
"masterPort": 5001,
|
|
558
|
+
"logicalNodeAddress": 3,
|
|
559
|
+
"logicalAddress": 4
|
|
560
|
+
}
|
|
561
|
+
]
|
|
562
|
+
},
|
|
563
|
+
"scenes": [
|
|
564
|
+
{
|
|
565
|
+
"name": "Scene",
|
|
566
|
+
"trigger": {
|
|
567
|
+
"masterAddress": "0.0.0.0",
|
|
568
|
+
"masterPort": 5001,
|
|
569
|
+
"name": "unit",
|
|
570
|
+
"logicalAddress": 0,
|
|
571
|
+
"logicalNodeAddress": 0,
|
|
572
|
+
"value": true
|
|
573
|
+
},
|
|
574
|
+
"order": 0,
|
|
575
|
+
"units": []
|
|
576
|
+
}
|
|
577
|
+
]
|
|
578
|
+
}
|
package/duotecno/api.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.post = exports.get = void 0;
|
|
13
|
+
const http = require("http");
|
|
14
|
+
const types_1 = require("./types");
|
|
15
|
+
///////////////////
|
|
16
|
+
// http function //
|
|
17
|
+
///////////////////
|
|
18
|
+
function get(host, port, path, headers = {}) {
|
|
19
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
20
|
+
return request('GET', host, port, path, {}, headers);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
exports.get = get;
|
|
24
|
+
function post(host, port, path, body, headers = {}) {
|
|
25
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
26
|
+
return request('POST', host, port, path, body, headers);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
exports.post = post;
|
|
30
|
+
function request(method, host, port, path, body, headers = {}) {
|
|
31
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
32
|
+
let timer;
|
|
33
|
+
let httpReq;
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
try {
|
|
36
|
+
timer = setTimeout(() => {
|
|
37
|
+
if (httpReq) {
|
|
38
|
+
httpReq.destroy();
|
|
39
|
+
}
|
|
40
|
+
reject("Request to " + host + path + " timed out");
|
|
41
|
+
}, 4000);
|
|
42
|
+
httpReq = http.request({ host, port, path, headers, method }, (response) => {
|
|
43
|
+
let str = '';
|
|
44
|
+
// another chunk of data has been received, so append it to `str`
|
|
45
|
+
response.on('data', (chunk) => {
|
|
46
|
+
str += chunk;
|
|
47
|
+
});
|
|
48
|
+
// the whole response has been received,
|
|
49
|
+
// stop the timer and resolve the promise with ithe collected result
|
|
50
|
+
response.on('end', () => {
|
|
51
|
+
if (timer)
|
|
52
|
+
clearTimeout(timer);
|
|
53
|
+
resolve(str);
|
|
54
|
+
});
|
|
55
|
+
}).on('error', (e) => {
|
|
56
|
+
if (timer)
|
|
57
|
+
clearTimeout(timer);
|
|
58
|
+
(0, types_1.err)("http", "http.get failed with " + JSON.stringify(e));
|
|
59
|
+
reject(e);
|
|
60
|
+
});
|
|
61
|
+
if (method === "POST") {
|
|
62
|
+
httpReq.write(JSON.stringify(body));
|
|
63
|
+
}
|
|
64
|
+
httpReq.end();
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
(0, types_1.err)("http", "http.get failed with " + JSON.stringify(error));
|
|
68
|
+
reject(error);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=api.js.map
|
package/duotecno/api.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as http from "http";
|
|
2
|
+
import { err } from "./types";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
///////////////////
|
|
6
|
+
// http function //
|
|
7
|
+
///////////////////
|
|
8
|
+
export async function get(host: string, port: number, path: string, headers = {}): Promise<string> {
|
|
9
|
+
return request('GET', host, port, path, {}, headers);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function post(host: string, port: number, path: string, body: object, headers = {}): Promise<string> {
|
|
13
|
+
return request('POST', host, port, path, body, headers);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function request(method: string, host: string, port: number, path: string, body: object, headers = {}): Promise<string> {
|
|
17
|
+
let timer;
|
|
18
|
+
let httpReq: http.ClientRequest;
|
|
19
|
+
|
|
20
|
+
return new Promise<any>((resolve, reject) => {
|
|
21
|
+
try {
|
|
22
|
+
timer = setTimeout(() => {
|
|
23
|
+
if (httpReq) {
|
|
24
|
+
httpReq.destroy();
|
|
25
|
+
}
|
|
26
|
+
reject("Request to " + host + path + " timed out");
|
|
27
|
+
}, 4000);
|
|
28
|
+
|
|
29
|
+
httpReq = http.request({ host, port, path, headers, method }, (response) => {
|
|
30
|
+
let str = '';
|
|
31
|
+
|
|
32
|
+
// another chunk of data has been received, so append it to `str`
|
|
33
|
+
response.on('data', (chunk) => {
|
|
34
|
+
str += chunk;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// the whole response has been received,
|
|
38
|
+
// stop the timer and resolve the promise with ithe collected result
|
|
39
|
+
response.on('end', () => {
|
|
40
|
+
if (timer) clearTimeout(timer);
|
|
41
|
+
resolve(str);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
}).on('error', (e) => {
|
|
45
|
+
if (timer) clearTimeout(timer);
|
|
46
|
+
err("http", "http.get failed with " + JSON.stringify(e));
|
|
47
|
+
reject(e);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (method === "POST") {
|
|
51
|
+
httpReq.write(JSON.stringify(body));
|
|
52
|
+
}
|
|
53
|
+
httpReq.end();
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
} catch(error) {
|
|
57
|
+
err("http", "http.get failed with " + JSON.stringify(error));
|
|
58
|
+
reject(error);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
///////////////////
|
|
3
|
+
// Global Config //
|
|
4
|
+
///////////////////
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.setAPIConfig = exports.getAPIConfig = exports.setSubConfig = exports.getSubConfig = exports.getConfig = exports.setConfig = exports.setToken = void 0;
|
|
7
|
+
const api_1 = require("./api");
|
|
8
|
+
const types_1 = require("./types");
|
|
9
|
+
const fs = require("fs");
|
|
10
|
+
const os_1 = require("os");
|
|
11
|
+
let gToken = "";
|
|
12
|
+
function setToken(token) {
|
|
13
|
+
gToken = token;
|
|
14
|
+
}
|
|
15
|
+
exports.setToken = setToken;
|
|
16
|
+
const isMac = ((0, os_1.platform)() === "darwin");
|
|
17
|
+
const kConfigFiles = (isMac) ? (__dirname + "/..") : ((0, os_1.homedir)() + "/duotecno");
|
|
18
|
+
if (!fs.existsSync(kConfigFiles))
|
|
19
|
+
fs.mkdirSync(kConfigFiles);
|
|
20
|
+
// set up empty config
|
|
21
|
+
let gPlatformConfig = types_1.Sanitizers.platform(null);
|
|
22
|
+
add("smartapp"); // webserver interface
|
|
23
|
+
add("settings"); // Pi settings
|
|
24
|
+
add("system"); // Duotecno system
|
|
25
|
+
add("server"); // web socket
|
|
26
|
+
add("smappee"); // power-smappee
|
|
27
|
+
add("p1"); // power-p1
|
|
28
|
+
// add("shelly"); // power-shelly
|
|
29
|
+
function add(type, cfg = null) {
|
|
30
|
+
gPlatformConfig[type] = types_1.Sanitizers[type] ? types_1.Sanitizers[type](cfg) : cfg;
|
|
31
|
+
}
|
|
32
|
+
// getter and setter
|
|
33
|
+
function setConfig(cfg) {
|
|
34
|
+
gPlatformConfig = types_1.Sanitizers.platform(cfg);
|
|
35
|
+
}
|
|
36
|
+
exports.setConfig = setConfig;
|
|
37
|
+
function getConfig() {
|
|
38
|
+
return gPlatformConfig;
|
|
39
|
+
}
|
|
40
|
+
exports.getConfig = getConfig;
|
|
41
|
+
function getSubConfig(type) {
|
|
42
|
+
const cfg = gPlatformConfig[type];
|
|
43
|
+
return types_1.Sanitizers[type] ? types_1.Sanitizers[type](cfg) : cfg;
|
|
44
|
+
}
|
|
45
|
+
exports.getSubConfig = getSubConfig;
|
|
46
|
+
function setSubConfig(type, cfg) {
|
|
47
|
+
gPlatformConfig[type] = types_1.Sanitizers[type] ? types_1.Sanitizers[type](cfg) : cfg;
|
|
48
|
+
}
|
|
49
|
+
exports.setSubConfig = setSubConfig;
|
|
50
|
+
// we should get the config through the homebridge platform init call !!
|
|
51
|
+
function getAPIConfig(done) {
|
|
52
|
+
let config = null;
|
|
53
|
+
if (gPlatformConfig.apiHost) {
|
|
54
|
+
const headers = { "Content-Type": "application/json" };
|
|
55
|
+
if (gToken) {
|
|
56
|
+
headers["Authorization"] = "Bearer " + gToken;
|
|
57
|
+
}
|
|
58
|
+
(0, api_1.get)(gPlatformConfig.apiHost, gPlatformConfig.apiPort, "/api/config-editor", headers).then((resp) => {
|
|
59
|
+
(0, types_1.debug)("smartapp", "OK: " + resp);
|
|
60
|
+
const parsed = JSON.parse(resp);
|
|
61
|
+
setConfig(parsed);
|
|
62
|
+
if (done)
|
|
63
|
+
done(parsed);
|
|
64
|
+
}, (err) => {
|
|
65
|
+
(0, types_1.debug)("smartapp", "ERROR: " + JSON.stringify(err));
|
|
66
|
+
done(null);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
const fn = kConfigFiles + "/config.json";
|
|
71
|
+
try {
|
|
72
|
+
const configBuf = fs.readFileSync(fn);
|
|
73
|
+
const configStr = configBuf.toString();
|
|
74
|
+
config = JSON.parse(configStr);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
(0, types_1.log)("system", "Couldn't read my config file (" + fn + ") -- Creating empty config.");
|
|
78
|
+
}
|
|
79
|
+
// sanitize the config
|
|
80
|
+
const sanitized = types_1.Sanitizers.platform(config);
|
|
81
|
+
setConfig(sanitized);
|
|
82
|
+
done(sanitized);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
exports.getAPIConfig = getAPIConfig;
|
|
86
|
+
function setAPIConfig() {
|
|
87
|
+
if (gPlatformConfig.apiHost) {
|
|
88
|
+
const headers = { "Content-Type": "application/json" };
|
|
89
|
+
if (gToken) {
|
|
90
|
+
headers["Authorization"] = "Bearer " + gToken;
|
|
91
|
+
}
|
|
92
|
+
(0, api_1.post)(gPlatformConfig.apiHost, gPlatformConfig.apiPort, "/api/config-editor", gPlatformConfig, headers).then((resp) => (0, types_1.debug)("smartapp", "OK: " + resp), (err) => (0, types_1.debug)("smartapp", "ERROR: " + JSON.stringify(err)));
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
const fn = kConfigFiles + "/config.json";
|
|
96
|
+
try {
|
|
97
|
+
fs.writeFileSync(fn, JSON.stringify(gPlatformConfig, null, 2));
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
(0, types_1.err)(this.type, "Couldn't write config.json file (" + fn + ")");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
exports.setAPIConfig = setAPIConfig;
|
|
105
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
|
|
2
|
+
///////////////////
|
|
3
|
+
// Global Config //
|
|
4
|
+
///////////////////
|
|
5
|
+
|
|
6
|
+
import { get, post } from "./api";
|
|
7
|
+
import { debug, err, log, PlatformConfig, Sanitizers } from "./types";
|
|
8
|
+
import * as fs from "fs";
|
|
9
|
+
import { homedir, platform } from "os";
|
|
10
|
+
|
|
11
|
+
let gToken = "";
|
|
12
|
+
|
|
13
|
+
export function setToken(token: string) {
|
|
14
|
+
gToken = token;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const isMac = (platform() === "darwin");
|
|
18
|
+
const kConfigFiles = (isMac) ? (__dirname+"/..") : (homedir() + "/duotecno");
|
|
19
|
+
if (! fs.existsSync(kConfigFiles)) fs.mkdirSync(kConfigFiles);
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
// set up empty config
|
|
23
|
+
let gPlatformConfig: PlatformConfig = Sanitizers.platform(null);
|
|
24
|
+
|
|
25
|
+
add("smartapp"); // webserver interface
|
|
26
|
+
add("settings"); // Pi settings
|
|
27
|
+
add("system"); // Duotecno system
|
|
28
|
+
add("server"); // web socket
|
|
29
|
+
|
|
30
|
+
add("smappee"); // power-smappee
|
|
31
|
+
add("p1"); // power-p1
|
|
32
|
+
// add("shelly"); // power-shelly
|
|
33
|
+
|
|
34
|
+
function add(type: string, cfg = null) {
|
|
35
|
+
gPlatformConfig[type] = Sanitizers[type] ? Sanitizers[type](cfg) : cfg;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// getter and setter
|
|
39
|
+
export function setConfig(cfg: PlatformConfig) {
|
|
40
|
+
gPlatformConfig = Sanitizers.platform(cfg);
|
|
41
|
+
}
|
|
42
|
+
export function getConfig(): PlatformConfig {
|
|
43
|
+
return gPlatformConfig;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
export function getSubConfig(type: string): object {
|
|
50
|
+
const cfg = gPlatformConfig[type];
|
|
51
|
+
return Sanitizers[type] ? Sanitizers[type](cfg) : cfg;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function setSubConfig(type: string, cfg: object) {
|
|
55
|
+
gPlatformConfig[type] = Sanitizers[type] ? Sanitizers[type](cfg) : cfg;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
// we should get the config through the homebridge platform init call !!
|
|
60
|
+
export function getAPIConfig(done) {
|
|
61
|
+
let config = null;
|
|
62
|
+
if (gPlatformConfig.apiHost) {
|
|
63
|
+
const headers = {"Content-Type": "application/json"};
|
|
64
|
+
if (gToken) {
|
|
65
|
+
headers["Authorization"] = "Bearer "+ gToken;
|
|
66
|
+
}
|
|
67
|
+
get(gPlatformConfig.apiHost, gPlatformConfig.apiPort, "/api/config-editor", headers).then(
|
|
68
|
+
(resp) => {
|
|
69
|
+
debug("smartapp", "OK: " + resp);
|
|
70
|
+
const parsed = JSON.parse(resp);
|
|
71
|
+
setConfig(parsed);
|
|
72
|
+
if (done)
|
|
73
|
+
done(parsed);
|
|
74
|
+
},
|
|
75
|
+
(err) => {
|
|
76
|
+
debug("smartapp", "ERROR: " + JSON.stringify(err));
|
|
77
|
+
done(null);
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
} else {
|
|
81
|
+
const fn = kConfigFiles + "/config.json";
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const configBuf = fs.readFileSync(fn);
|
|
85
|
+
const configStr = configBuf.toString();
|
|
86
|
+
config = JSON.parse(configStr);
|
|
87
|
+
|
|
88
|
+
} catch(err) {
|
|
89
|
+
log("system", "Couldn't read my config file (" + fn + ") -- Creating empty config.")
|
|
90
|
+
}
|
|
91
|
+
// sanitize the config
|
|
92
|
+
const sanitized = Sanitizers.platform(config);
|
|
93
|
+
setConfig(sanitized);
|
|
94
|
+
|
|
95
|
+
done(sanitized);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function setAPIConfig() {
|
|
100
|
+
if (gPlatformConfig.apiHost) {
|
|
101
|
+
const headers = {"Content-Type": "application/json"};
|
|
102
|
+
if (gToken) {
|
|
103
|
+
headers["Authorization"] = "Bearer "+ gToken;
|
|
104
|
+
}
|
|
105
|
+
post(gPlatformConfig.apiHost, gPlatformConfig.apiPort, "/api/config-editor", gPlatformConfig, headers).then(
|
|
106
|
+
(resp) => debug("smartapp", "OK: " + resp),
|
|
107
|
+
(err) => debug("smartapp", "ERROR: " + JSON.stringify(err))
|
|
108
|
+
);
|
|
109
|
+
} else {
|
|
110
|
+
const fn = kConfigFiles + "/config.json";
|
|
111
|
+
try {
|
|
112
|
+
fs.writeFileSync(fn, JSON.stringify(gPlatformConfig, null, 2));
|
|
113
|
+
|
|
114
|
+
} catch(error) {
|
|
115
|
+
err(this.type, "Couldn't write config.json file (" + fn + ")");
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-smartsystem",
|
|
3
3
|
"displayname": "Duotecno Bridge",
|
|
4
|
-
"version": "6.1.
|
|
4
|
+
"version": "6.1.7",
|
|
5
5
|
"description": "SmartServer (Proxy Websockets to TCP sockets, Smappee MQTT, Duotecno IP Nodes, Homekit interface)",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"author": "Johan Coppieters",
|
package/server/powerbase.js
CHANGED
|
@@ -137,7 +137,7 @@ class PowerBase extends base_1.Base {
|
|
|
137
137
|
// for all bindings
|
|
138
138
|
this.bindings.forEach(b => {
|
|
139
139
|
const power = this.evalPower(b.channel);
|
|
140
|
-
if (!isNaN(
|
|
140
|
+
if ((!isNaN(power)) && (isNaN(b.power) || (Math.abs(b.power - power) > Math.abs(b.power * 0.1)))) {
|
|
141
141
|
(0, types_1.debug)("power", "APPLY to " + b.channel + " = " + b.power + " -> " + power + " for " + b.name + " (" + b.logicalNodeAddress + ";" + b.logicalAddress + "): difference: " + Math.abs(b.power - power) + " > threshold: " + Math.abs(b.power * 0.1));
|
|
142
142
|
this.applyBinding(b, power)
|
|
143
143
|
.then(value => {
|
package/server/powerbase.ts
CHANGED
|
@@ -158,7 +158,7 @@ export class PowerBase extends Base {
|
|
|
158
158
|
// for all bindings
|
|
159
159
|
this.bindings.forEach( b => {
|
|
160
160
|
const power = this.evalPower(b.channel);
|
|
161
|
-
if (!isNaN(
|
|
161
|
+
if ((!isNaN(power)) && (isNaN(b.power) || (Math.abs(b.power - power) > Math.abs(b.power * 0.1)))) {
|
|
162
162
|
debug("power", "APPLY to " + b.channel + " = " + b.power + " -> " + power + " for " + b.name + " (" + b.logicalNodeAddress + ";" + b.logicalAddress + "): difference: " + Math.abs(b.power - power) + " > threshold: " + Math.abs(b.power * 0.1))
|
|
163
163
|
this.applyBinding(b, power)
|
|
164
164
|
.then(value => {
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<link type="text/css" rel="stylesheet" href="/files/min.css" media="screen,projection"/>
|
|
5
|
+
|
|
6
|
+
<!--Let browser know website is optimized for mobile-->
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
8
|
+
<link rel="shortcut icon" href="/files/favicon.ico" type="image/x-icon" />
|
|
9
|
+
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
|
10
|
+
|
|
11
|
+
<%- include('/views/style'); -%>
|
|
12
|
+
</head>
|
|
13
|
+
|
|
14
|
+
<body>
|
|
15
|
+
<nav>
|
|
16
|
+
<div class="nav-wrapper">
|
|
17
|
+
<a href="/" target=_blank class="brand-logo hide-on-small-only">Duotecno</a>
|
|
18
|
+
</div>
|
|
19
|
+
</nav>
|
|
20
|
+
|
|
21
|
+
<% if (message) { %>
|
|
22
|
+
<p class="message"><b>Message: </b>
|
|
23
|
+
<%= message %>
|
|
24
|
+
</p>
|
|
25
|
+
<% } %>
|
|
26
|
+
|
|
27
|
+
<form method="post" action="/login">
|
|
28
|
+
<input type="hidden" name="daction" value="login" />
|
|
29
|
+
|
|
30
|
+
<h1>
|
|
31
|
+
<button class="right btn waves-effect waves-light" type="submit" name="action" value="login">Log in</button>
|
|
32
|
+
</h1>
|
|
33
|
+
|
|
34
|
+
<div class="row">
|
|
35
|
+
<div class="input-field col s12">
|
|
36
|
+
<input type="text" name="user" id="user" value="" />
|
|
37
|
+
<label for="user">Username</label>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<div class="row">
|
|
42
|
+
<div class="input-field col s12">
|
|
43
|
+
<input type="password" name="password" id="password" value="" />
|
|
44
|
+
<label for="password">Password</label>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</form>
|
|
48
|
+
|
|
49
|
+
<footer class="page-footer">
|
|
50
|
+
<p>
|
|
51
|
+
Johan Coppieters - Duotecno, 2019-<%= year %> - v<%= version %>
|
|
52
|
+
</p>
|
|
53
|
+
</footer>
|
|
54
|
+
|
|
55
|
+
</body>
|
|
56
|
+
<%- include('/views/scripts'); -%>
|
|
57
|
+
</html>
|