homebridge-smartsystem 6.3.4 → 6.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -34
- package/config.json +48 -56
- package/duotecno/master.js +85 -3
- package/duotecno/master.ts +88 -6
- package/duotecno/protocol.js +97 -25
- package/duotecno/protocol.ts +103 -27
- package/duotecno/types.js +8 -4
- package/duotecno/types.ts +12 -6
- package/index.spec.js +1 -0
- package/index.spec.ts +1 -0
- package/package.json +1 -1
- package/server/powerbase.js +15 -16
- package/server/powerbase.ts +19 -18
- package/server/smartapp.js +11 -6
- package/server/smartapp.ts +15 -8
- package/server/somfy.ts +4 -2
- package/server/views/master-detail.ejs +16 -0
- package/server/views/master-list.ejs +2 -0
- package/server/views/power-binding.ejs +3 -3
- package/server/views/power.ejs +3 -3
package/duotecno/protocol.js
CHANGED
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.Protocol = exports.Unit = exports.Node = exports.recName = exports.Rec = exports.TempPreset = exports.cmdName = void 0;
|
|
12
|
+
exports.Protocol = exports.Unit = exports.Node = exports.recName = exports.Rec = exports.TempPreset = exports.cmdName = exports.reqNodeAttributes = void 0;
|
|
13
13
|
const types_1 = require("./types");
|
|
14
14
|
const logger_1 = require("./logger");
|
|
15
15
|
// Duotecno master IP protocol implementation
|
|
@@ -19,21 +19,29 @@ const logger_1 = require("./logger");
|
|
|
19
19
|
// Mar 2019 - v2 - rewrite to support multiple masters
|
|
20
20
|
//
|
|
21
21
|
// Dec 2019 - v3 - for app side only
|
|
22
|
+
// May-Nov 2021 - v4 - changes for pro-app
|
|
23
|
+
// Nov 2022 - Register and merge with gateway
|
|
22
24
|
///////////////////////////
|
|
23
25
|
// Commands + attributes //
|
|
24
26
|
///////////////////////////
|
|
25
27
|
var Cmd;
|
|
26
28
|
(function (Cmd) {
|
|
27
29
|
Cmd[Cmd["SetSensorValue"] = 7] = "SetSensorValue";
|
|
30
|
+
Cmd[Cmd["Internal"] = 9] = "Internal";
|
|
31
|
+
Cmd[Cmd["SetBasicAudio"] = 159] = "SetBasicAudio";
|
|
32
|
+
Cmd[Cmd["SetExtendedAudio"] = 208] = "SetExtendedAudio";
|
|
33
|
+
Cmd[Cmd["SetAVMatrix"] = 202] = "SetAVMatrix";
|
|
28
34
|
Cmd[Cmd["SetSwitch"] = 163] = "SetSwitch";
|
|
29
35
|
Cmd[Cmd["SetDimmer"] = 162] = "SetDimmer";
|
|
30
36
|
Cmd[Cmd["SetControl"] = 168] = "SetControl";
|
|
31
37
|
Cmd[Cmd["SetMotor"] = 182] = "SetMotor";
|
|
32
38
|
Cmd[Cmd["SetSensor"] = 136] = "SetSensor";
|
|
39
|
+
Cmd[Cmd["SetDateTime"] = 170] = "SetDateTime";
|
|
33
40
|
Cmd[Cmd["Login"] = 214] = "Login";
|
|
34
41
|
Cmd[Cmd["Heartbeat"] = 215] = "Heartbeat";
|
|
35
42
|
Cmd[Cmd["DatabaseInfo"] = 209] = "DatabaseInfo";
|
|
36
43
|
Cmd[Cmd["SetSchedule"] = 217] = "SetSchedule";
|
|
44
|
+
Cmd[Cmd["Register"] = 224] = "Register";
|
|
37
45
|
})(Cmd || (Cmd = {}));
|
|
38
46
|
// for Set Switch/Dimmer/Control
|
|
39
47
|
const reqDim = 3;
|
|
@@ -49,6 +57,16 @@ const reqUnitInfo = 2;
|
|
|
49
57
|
const reqUnitStatus = 3;
|
|
50
58
|
// for Schedule
|
|
51
59
|
const reqSchedule = 218;
|
|
60
|
+
// for versions et al
|
|
61
|
+
const reqNodeManagement = 220;
|
|
62
|
+
var reqNodeAttributes;
|
|
63
|
+
(function (reqNodeAttributes) {
|
|
64
|
+
reqNodeAttributes[reqNodeAttributes["masterSupported"] = 0] = "masterSupported";
|
|
65
|
+
reqNodeAttributes[reqNodeAttributes["isMaster"] = 4] = "isMaster";
|
|
66
|
+
reqNodeAttributes[reqNodeAttributes["nodeInfo"] = 5] = "nodeInfo";
|
|
67
|
+
reqNodeAttributes[reqNodeAttributes["nodeVersion"] = 6] = "nodeVersion";
|
|
68
|
+
reqNodeAttributes[reqNodeAttributes["nodeProtocol"] = 7] = "nodeProtocol";
|
|
69
|
+
})(reqNodeAttributes = exports.reqNodeAttributes || (exports.reqNodeAttributes = {}));
|
|
52
70
|
function cmdName(cmd) {
|
|
53
71
|
return Cmd[cmd] || "cmd" + cmd;
|
|
54
72
|
}
|
|
@@ -80,6 +98,8 @@ var Rec;
|
|
|
80
98
|
Rec[Rec["TimeDateStatus"] = 71] = "TimeDateStatus";
|
|
81
99
|
Rec[Rec["HeartbeatStatus"] = 72] = "HeartbeatStatus";
|
|
82
100
|
Rec[Rec["ScheduleStatus"] = 73] = "ScheduleStatus";
|
|
101
|
+
Rec[Rec["NodeMgtInfo"] = 74] = "NodeMgtInfo";
|
|
102
|
+
Rec[Rec["Register"] = 77] = "Register";
|
|
83
103
|
// return info from recDBInfo
|
|
84
104
|
Rec[Rec["Info"] = 64] = "Info";
|
|
85
105
|
// return info from reqUnitStatus
|
|
@@ -151,8 +171,8 @@ class Unit {
|
|
|
151
171
|
this.extendedType = this.extendedType || this.calcExtendedType();
|
|
152
172
|
this.name = this.name || this.getSerialNr();
|
|
153
173
|
// delete the "<" and ">" for power meters
|
|
154
|
-
if (this.extendedType ===
|
|
155
|
-
|
|
174
|
+
// if (this.extendedType === UnitExtendedType.kPower)
|
|
175
|
+
// this.name = this.name.substring(1);
|
|
156
176
|
// make a name for homekit, without the | but add § is 'specials' to add "sfeer", etc...
|
|
157
177
|
// if the display name is empty make a N[nodeAdr]-U[unitAdr] name.
|
|
158
178
|
// delete all type modifiers ( $, * and ! )
|
|
@@ -229,11 +249,12 @@ class Unit {
|
|
|
229
249
|
case types_1.UnitType.kDimmer: return "03|" + name;
|
|
230
250
|
case types_1.UnitType.kSwitch: return "04|" + name;
|
|
231
251
|
case types_1.UnitType.kMood: return "09|" + name;
|
|
232
|
-
case types_1.UnitType.kInput: return "
|
|
252
|
+
case types_1.UnitType.kInput: return "10|" + name;
|
|
253
|
+
// case UnitType.kPower: return "11|" + name;
|
|
233
254
|
case types_1.UnitType.kExtendedAudio: "12|" + name;
|
|
234
|
-
case types_1.UnitType.kAudio: return "
|
|
235
|
-
case types_1.UnitType.kAV: return "
|
|
236
|
-
case types_1.UnitType.kVideo: return "
|
|
255
|
+
case types_1.UnitType.kAudio: return "13|" + name;
|
|
256
|
+
case types_1.UnitType.kAV: return "14|" + name;
|
|
257
|
+
case types_1.UnitType.kVideo: return "15|" + name;
|
|
237
258
|
case types_1.UnitType.kIRTX: return "19|" + name;
|
|
238
259
|
default: return "99|" + name;
|
|
239
260
|
}
|
|
@@ -280,9 +301,9 @@ class Unit {
|
|
|
280
301
|
//////////////////
|
|
281
302
|
// Temp / Power //
|
|
282
303
|
//////////////////
|
|
283
|
-
if ((this.type ===
|
|
284
|
-
|
|
285
|
-
|
|
304
|
+
//if ((this.type === UnitType.kTemperature) &&
|
|
305
|
+
// ((this.name[0] === "<") || (this.name[0] === ">")))
|
|
306
|
+
// return UnitExtendedType.kPower;
|
|
286
307
|
/////////////
|
|
287
308
|
// Up/Down //
|
|
288
309
|
/////////////
|
|
@@ -472,12 +493,55 @@ exports.Protocol = {
|
|
|
472
493
|
.map(val => String.fromCharCode(val))
|
|
473
494
|
.join("");
|
|
474
495
|
},
|
|
496
|
+
makeWord: function (arr, at) {
|
|
497
|
+
return arr[at + 0] * 256 + arr[at + 1];
|
|
498
|
+
},
|
|
499
|
+
makeSignedWord: function (arr, at) {
|
|
500
|
+
if (arr[at + 0] > 127)
|
|
501
|
+
return (arr[at + 0] - 255) * 256 +
|
|
502
|
+
(arr[at + 1] - 255) - 1;
|
|
503
|
+
else
|
|
504
|
+
return arr[at + 0] * 256 + arr[at + 1];
|
|
505
|
+
},
|
|
475
506
|
makeLong: function (arr, at) {
|
|
476
507
|
return arr[at + 0] * 256 * 256 * 256 +
|
|
477
508
|
arr[at + 1] * 256 * 256 +
|
|
478
509
|
arr[at + 2] * 256 +
|
|
479
510
|
arr[at + 3];
|
|
480
511
|
},
|
|
512
|
+
makeSigned: function (arr, at) {
|
|
513
|
+
if (arr[at + 0] > 127)
|
|
514
|
+
return (arr[at + 0] - 255) * 256 * 256 * 256 +
|
|
515
|
+
(arr[at + 1] - 255) * 256 * 256 +
|
|
516
|
+
(arr[at + 2] - 255) * 256 +
|
|
517
|
+
(arr[at + 3] - 255) - 1;
|
|
518
|
+
else
|
|
519
|
+
return arr[at + 0] * 256 * 256 * 256 +
|
|
520
|
+
arr[at + 1] * 256 * 256 +
|
|
521
|
+
arr[at + 2] * 256 +
|
|
522
|
+
arr[at + 3];
|
|
523
|
+
},
|
|
524
|
+
signedToArray: function (value) {
|
|
525
|
+
if (value < 0)
|
|
526
|
+
return [256 + Math.floor(value / 256 / 256 / 256),
|
|
527
|
+
256 + Math.floor(value / 256 / 256) % 256,
|
|
528
|
+
256 + Math.floor(value / 256) % 256,
|
|
529
|
+
256 + Math.floor(value) % 256];
|
|
530
|
+
else
|
|
531
|
+
return [Math.floor(value / 256 / 256 / 256),
|
|
532
|
+
Math.floor(value / 256 / 256) % 256,
|
|
533
|
+
Math.floor(value / 256) % 256,
|
|
534
|
+
Math.floor(value) % 256];
|
|
535
|
+
},
|
|
536
|
+
signedWordToArray: function (value) {
|
|
537
|
+
if (value < 0)
|
|
538
|
+
return [256 + Math.floor(value / 256) % 256, 256 + Math.floor(value) % 256];
|
|
539
|
+
else
|
|
540
|
+
return [Math.floor(value / 256) % 256, Math.floor(value) % 256];
|
|
541
|
+
},
|
|
542
|
+
wordToArray: function (value) {
|
|
543
|
+
return [Math.floor(value / 256) % 256, Math.floor(value) % 256];
|
|
544
|
+
},
|
|
481
545
|
isStatus: function (cmd) {
|
|
482
546
|
return (cmd === Rec.Mood) ||
|
|
483
547
|
(cmd === Rec.Dimmer) ||
|
|
@@ -529,8 +593,8 @@ exports.Protocol = {
|
|
|
529
593
|
socket.write(data + String.fromCharCode(10));
|
|
530
594
|
return types_1.WriteError.writeOK;
|
|
531
595
|
}
|
|
532
|
-
catch (
|
|
533
|
-
(0, logger_1.
|
|
596
|
+
catch (e) {
|
|
597
|
+
(0, logger_1.err)("protocol", "error sending through socket " + e.message);
|
|
534
598
|
return types_1.WriteError.writeFatal;
|
|
535
599
|
}
|
|
536
600
|
}
|
|
@@ -609,6 +673,9 @@ exports.Protocol = {
|
|
|
609
673
|
buildRequestSchedule() {
|
|
610
674
|
return [reqSchedule, 0];
|
|
611
675
|
},
|
|
676
|
+
buildRequestNodeMgt(method = reqNodeAttributes.nodeProtocol) {
|
|
677
|
+
return [reqNodeManagement, method];
|
|
678
|
+
},
|
|
612
679
|
getCmdAndMethod: function (unit, value) {
|
|
613
680
|
switch (unit.type) {
|
|
614
681
|
case types_1.UnitType.kDimmer:
|
|
@@ -656,10 +723,10 @@ exports.Protocol = {
|
|
|
656
723
|
return [Cmd.SetSensor, 13, node.logicalAddress, unit.logicalAddress, preset];
|
|
657
724
|
},
|
|
658
725
|
buildSetSensor(node, unit, value) {
|
|
659
|
-
return [Cmd.SetSensorValue, 10, node.logicalAddress, unit.logicalAddress, types_1.UnitType.kTemperature,
|
|
726
|
+
return [Cmd.SetSensorValue, 10, node.logicalAddress, unit.logicalAddress, types_1.UnitType.kTemperature, ...this.wordToArray(value)];
|
|
660
727
|
},
|
|
661
728
|
buildSetPreset(node, unit, preset, value) {
|
|
662
|
-
return [Cmd.SetSensor, 1, node.logicalAddress, unit.logicalAddress, preset,
|
|
729
|
+
return [Cmd.SetSensor, 1, node.logicalAddress, unit.logicalAddress, preset, ...this.wordToArray(value)];
|
|
663
730
|
},
|
|
664
731
|
buildIncDecPreset(node, unit, inc) {
|
|
665
732
|
return [Cmd.SetSensor, (inc) ? 5 : 6, node.logicalAddress, unit.logicalAddress];
|
|
@@ -671,6 +738,16 @@ exports.Protocol = {
|
|
|
671
738
|
buildSendSchedule(schedule) {
|
|
672
739
|
return [Cmd.SetSchedule, 0, Math.max(0, Math.min(3, schedule))];
|
|
673
740
|
},
|
|
741
|
+
buildRegister(register, value) {
|
|
742
|
+
if (typeof value != "undefined") {
|
|
743
|
+
// set register
|
|
744
|
+
return [Cmd.Register, 1, ...this.wordToArray(register), ...this.signedToArray(value)];
|
|
745
|
+
}
|
|
746
|
+
else {
|
|
747
|
+
// get register
|
|
748
|
+
return [Cmd.Register, 2, ...this.wordToArray(register)];
|
|
749
|
+
}
|
|
750
|
+
},
|
|
674
751
|
///////////////////
|
|
675
752
|
// Received info //
|
|
676
753
|
///////////////////
|
|
@@ -678,13 +755,13 @@ exports.Protocol = {
|
|
|
678
755
|
let kind = "-";
|
|
679
756
|
if (next.cmd === Rec.Sensor) {
|
|
680
757
|
// sensor -> value
|
|
681
|
-
unit.value = next.message
|
|
758
|
+
unit.value = this.makeWord(next.message, 9); // 10x current temperature
|
|
682
759
|
unit.status = next.message[7]; // 0=idle, 1=heating, 2=cooling
|
|
683
760
|
unit.preset = (next.message[6]) ? next.message[8] : -1; // 0=sun, 1=half sun, 2=moon, 3=half moon, -1 = off
|
|
684
|
-
unit.sun = next.message
|
|
685
|
-
unit.hsun = next.message
|
|
686
|
-
unit.moon = next.message
|
|
687
|
-
unit.hmoon = next.message
|
|
761
|
+
unit.sun = this.makeWord(next.message, 11); // 10x temperature
|
|
762
|
+
unit.hsun = this.makeWord(next.message, 13); // 10x temperature
|
|
763
|
+
unit.moon = this.makeWord(next.message, 15); // 10x temperature
|
|
764
|
+
unit.hmoon = this.makeWord(next.message, 17); // 10x temperature
|
|
688
765
|
(0, logger_1.debug)("protocol", "received status - " + unit.getDisplayName() + ", temperature = " + unit.value / 10.0 + ", sun = " + unit.sun);
|
|
689
766
|
kind = "S";
|
|
690
767
|
// Dimmers, switches and moods have
|
|
@@ -737,16 +814,11 @@ exports.Protocol = {
|
|
|
737
814
|
// only change dim value when state = 1 (ON, PIR ON, DIM STOP) for Dimmers and IRTX + for any setpoint event
|
|
738
815
|
if ((((next.message[4] === 1) || (next.message[4] === 6) || (next.message[4] === 7)) && (next.message[5] === 1)) ||
|
|
739
816
|
(next.message[4] === 11)) {
|
|
740
|
-
unit.value = next.message
|
|
817
|
+
unit.value = this.makeWord(next.message, 6);
|
|
741
818
|
}
|
|
742
819
|
(0, logger_1.debug)("protocol", "received status - macro -> value=" + unit.value + " / status=" + unit.status);
|
|
743
820
|
kind = "M";
|
|
744
821
|
}
|
|
745
|
-
// clear the timer to turn the mood off again.
|
|
746
|
-
//if (unit.resetTimer) {
|
|
747
|
-
// clearInterval(unit.resetTimer);
|
|
748
|
-
// unit.resetTimer = null;
|
|
749
|
-
//}
|
|
750
822
|
this.alertSubscriber(unit);
|
|
751
823
|
this.emitter.emit('update', unit, kind);
|
|
752
824
|
},
|
package/duotecno/protocol.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CommRecord, Message, Sanitizers, NodeInfo, UnitInfo, DBInfo, WriteError, hex, NodeType, UnitType, UnitExtendedType, UnitState, two } from "./types";
|
|
2
|
-
import { debug, log } from "./logger";
|
|
2
|
+
import { debug, log, err } from "./logger";
|
|
3
3
|
import { Master } from "./master";
|
|
4
4
|
import { SmartSocket } from "./smartsocket";
|
|
5
5
|
import { EventEmitter } from "events";
|
|
@@ -11,6 +11,8 @@ import { EventEmitter } from "events";
|
|
|
11
11
|
// Mar 2019 - v2 - rewrite to support multiple masters
|
|
12
12
|
//
|
|
13
13
|
// Dec 2019 - v3 - for app side only
|
|
14
|
+
// May-Nov 2021 - v4 - changes for pro-app
|
|
15
|
+
// Nov 2022 - Register and merge with gateway
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
///////////////////////////
|
|
@@ -18,15 +20,25 @@ import { EventEmitter } from "events";
|
|
|
18
20
|
///////////////////////////
|
|
19
21
|
enum Cmd {
|
|
20
22
|
SetSensorValue = 7,
|
|
23
|
+
Internal = 9, // smartsystem defined (not Duotecno)
|
|
24
|
+
|
|
25
|
+
SetBasicAudio = 159,
|
|
26
|
+
SetExtendedAudio = 208,
|
|
27
|
+
SetAVMatrix = 202,
|
|
28
|
+
|
|
21
29
|
SetSwitch = 163,
|
|
22
30
|
SetDimmer = 162,
|
|
23
31
|
SetControl = 168,
|
|
24
32
|
SetMotor = 182,
|
|
25
33
|
SetSensor = 136,
|
|
34
|
+
|
|
35
|
+
SetDateTime = 170,
|
|
36
|
+
|
|
26
37
|
Login = 214,
|
|
27
38
|
Heartbeat = 215,
|
|
28
39
|
DatabaseInfo = 209,
|
|
29
|
-
SetSchedule = 217
|
|
40
|
+
SetSchedule = 217,
|
|
41
|
+
Register = 224
|
|
30
42
|
}
|
|
31
43
|
|
|
32
44
|
// for Set Switch/Dimmer/Control
|
|
@@ -47,6 +59,16 @@ const reqUnitStatus = 3;
|
|
|
47
59
|
// for Schedule
|
|
48
60
|
const reqSchedule = 218;
|
|
49
61
|
|
|
62
|
+
// for versions et al
|
|
63
|
+
const reqNodeManagement = 220;
|
|
64
|
+
export enum reqNodeAttributes {
|
|
65
|
+
masterSupported = 0,
|
|
66
|
+
isMaster = 4,
|
|
67
|
+
nodeInfo = 5,
|
|
68
|
+
nodeVersion = 6,
|
|
69
|
+
nodeProtocol = 7
|
|
70
|
+
}
|
|
71
|
+
|
|
50
72
|
|
|
51
73
|
export function cmdName(cmd: number | string) {
|
|
52
74
|
return Cmd[cmd] || "cmd"+cmd;
|
|
@@ -88,6 +110,8 @@ export enum Rec {
|
|
|
88
110
|
TimeDateStatus = 71,
|
|
89
111
|
HeartbeatStatus = 72,
|
|
90
112
|
ScheduleStatus = 73,
|
|
113
|
+
NodeMgtInfo = 74,
|
|
114
|
+
Register = 77,
|
|
91
115
|
|
|
92
116
|
// return info from recDBInfo
|
|
93
117
|
Info = 64,
|
|
@@ -207,8 +231,8 @@ export class Unit {
|
|
|
207
231
|
this.name = this.name || this.getSerialNr();
|
|
208
232
|
|
|
209
233
|
// delete the "<" and ">" for power meters
|
|
210
|
-
if (this.extendedType === UnitExtendedType.kPower)
|
|
211
|
-
|
|
234
|
+
// if (this.extendedType === UnitExtendedType.kPower)
|
|
235
|
+
// this.name = this.name.substring(1);
|
|
212
236
|
|
|
213
237
|
// make a name for homekit, without the | but add § is 'specials' to add "sfeer", etc...
|
|
214
238
|
// if the display name is empty make a N[nodeAdr]-U[unitAdr] name.
|
|
@@ -290,12 +314,14 @@ export class Unit {
|
|
|
290
314
|
case UnitType.kDimmer: return "03|" + name;
|
|
291
315
|
case UnitType.kSwitch: return "04|" + name;
|
|
292
316
|
case UnitType.kMood: return "09|" + name;
|
|
293
|
-
case UnitType.kInput: return "
|
|
317
|
+
case UnitType.kInput: return "10|" + name;
|
|
318
|
+
// case UnitType.kPower: return "11|" + name;
|
|
294
319
|
case UnitType.kExtendedAudio: "12|" + name;
|
|
295
|
-
case UnitType.kAudio: return "
|
|
296
|
-
case UnitType.kAV: return "
|
|
297
|
-
case UnitType.kVideo: return "
|
|
320
|
+
case UnitType.kAudio: return "13|" + name;
|
|
321
|
+
case UnitType.kAV: return "14|" + name;
|
|
322
|
+
case UnitType.kVideo: return "15|" + name;
|
|
298
323
|
case UnitType.kIRTX: return "19|" + name;
|
|
324
|
+
|
|
299
325
|
default: return "99|" + name;
|
|
300
326
|
}
|
|
301
327
|
}
|
|
@@ -347,9 +373,9 @@ export class Unit {
|
|
|
347
373
|
//////////////////
|
|
348
374
|
// Temp / Power //
|
|
349
375
|
//////////////////
|
|
350
|
-
if ((this.type === UnitType.kTemperature) &&
|
|
351
|
-
|
|
352
|
-
|
|
376
|
+
//if ((this.type === UnitType.kTemperature) &&
|
|
377
|
+
// ((this.name[0] === "<") || (this.name[0] === ">")))
|
|
378
|
+
// return UnitExtendedType.kPower;
|
|
353
379
|
|
|
354
380
|
|
|
355
381
|
/////////////
|
|
@@ -552,12 +578,55 @@ export const Protocol = {
|
|
|
552
578
|
.join("");
|
|
553
579
|
},
|
|
554
580
|
|
|
581
|
+
makeWord: function(arr: Array<number>, at: number) {
|
|
582
|
+
return arr[at+0] * 256 + arr[at+1];
|
|
583
|
+
},
|
|
584
|
+
makeSignedWord: function(arr: Array<number>, at: number) {
|
|
585
|
+
if (arr[at+0] > 127)
|
|
586
|
+
return (arr[at+0]-255) * 256 +
|
|
587
|
+
(arr[at+1]-255) - 1;
|
|
588
|
+
else
|
|
589
|
+
return arr[at+0] * 256 + arr[at+1];
|
|
590
|
+
},
|
|
555
591
|
makeLong: function(arr: Array<number>, at: number) {
|
|
556
592
|
return arr[at+0] * 256 * 256 * 256 +
|
|
557
593
|
arr[at+1] * 256 * 256 +
|
|
558
594
|
arr[at+2] * 256 +
|
|
559
595
|
arr[at+3];
|
|
560
596
|
},
|
|
597
|
+
makeSigned: function(arr: Array<number>, at: number) {
|
|
598
|
+
if (arr[at+0] > 127)
|
|
599
|
+
return (arr[at+0]-255) * 256 * 256 * 256 +
|
|
600
|
+
(arr[at+1]-255) * 256 * 256 +
|
|
601
|
+
(arr[at+2]-255) * 256 +
|
|
602
|
+
(arr[at+3]-255) - 1;
|
|
603
|
+
else
|
|
604
|
+
return arr[at+0] * 256 * 256 * 256 +
|
|
605
|
+
arr[at+1] * 256 * 256 +
|
|
606
|
+
arr[at+2] * 256 +
|
|
607
|
+
arr[at+3];
|
|
608
|
+
},
|
|
609
|
+
signedToArray: function(value: number) {
|
|
610
|
+
if (value < 0)
|
|
611
|
+
return [256 + Math.floor(value / 256 / 256 / 256),
|
|
612
|
+
256 + Math.floor(value / 256 / 256) % 256,
|
|
613
|
+
256 + Math.floor(value / 256) % 256,
|
|
614
|
+
256 + Math.floor(value) % 256]
|
|
615
|
+
else
|
|
616
|
+
return [Math.floor(value / 256 / 256 / 256),
|
|
617
|
+
Math.floor(value / 256 / 256) % 256,
|
|
618
|
+
Math.floor(value / 256) % 256,
|
|
619
|
+
Math.floor(value) % 256];
|
|
620
|
+
},
|
|
621
|
+
signedWordToArray: function(value: number) {
|
|
622
|
+
if (value < 0)
|
|
623
|
+
return [256 + Math.floor(value / 256) % 256, 256 + Math.floor(value) % 256];
|
|
624
|
+
else
|
|
625
|
+
return [Math.floor(value / 256) % 256, Math.floor(value) % 256];
|
|
626
|
+
},
|
|
627
|
+
wordToArray: function(value: number) {
|
|
628
|
+
return [Math.floor(value / 256) % 256, Math.floor(value) % 256];
|
|
629
|
+
},
|
|
561
630
|
|
|
562
631
|
isStatus: function(cmd: number): boolean {
|
|
563
632
|
return (cmd === Rec.Mood) ||
|
|
@@ -617,8 +686,8 @@ export const Protocol = {
|
|
|
617
686
|
socket.write(data+String.fromCharCode(10));
|
|
618
687
|
return WriteError.writeOK;
|
|
619
688
|
|
|
620
|
-
} catch(
|
|
621
|
-
|
|
689
|
+
} catch(e) {
|
|
690
|
+
err("protocol", "error sending through socket " + e.message);
|
|
622
691
|
return WriteError.writeFatal;
|
|
623
692
|
}
|
|
624
693
|
} else {
|
|
@@ -715,6 +784,9 @@ export const Protocol = {
|
|
|
715
784
|
buildRequestSchedule(): Message {
|
|
716
785
|
return [reqSchedule, 0];
|
|
717
786
|
},
|
|
787
|
+
buildRequestNodeMgt(method = reqNodeAttributes.nodeProtocol): Message {
|
|
788
|
+
return [reqNodeManagement, method];
|
|
789
|
+
},
|
|
718
790
|
|
|
719
791
|
getCmdAndMethod: function(unit: Unit, value: number | boolean): CmdSet {
|
|
720
792
|
switch (unit.type) {
|
|
@@ -771,10 +843,10 @@ export const Protocol = {
|
|
|
771
843
|
return [Cmd.SetSensor, 13, node.logicalAddress, unit.logicalAddress, preset];
|
|
772
844
|
},
|
|
773
845
|
buildSetSensor(node: Node, unit: Unit, value: number): Message {
|
|
774
|
-
return [Cmd.SetSensorValue, 10, node.logicalAddress, unit.logicalAddress, UnitType.kTemperature,
|
|
846
|
+
return [Cmd.SetSensorValue, 10, node.logicalAddress, unit.logicalAddress, UnitType.kTemperature, ...this.wordToArray(value)];
|
|
775
847
|
},
|
|
776
848
|
buildSetPreset(node: Node, unit: Unit, preset: number, value: number): Message {
|
|
777
|
-
return [Cmd.SetSensor, 1, node.logicalAddress, unit.logicalAddress, preset,
|
|
849
|
+
return [Cmd.SetSensor, 1, node.logicalAddress, unit.logicalAddress, preset, ...this.wordToArray(value)];
|
|
778
850
|
},
|
|
779
851
|
buildIncDecPreset(node: Node, unit: Unit, inc: boolean): Message {
|
|
780
852
|
return [Cmd.SetSensor, (inc) ? 5 : 6, node.logicalAddress, unit.logicalAddress];
|
|
@@ -788,6 +860,16 @@ export const Protocol = {
|
|
|
788
860
|
return [Cmd.SetSchedule, 0, Math.max(0, Math.min(3, schedule))];
|
|
789
861
|
},
|
|
790
862
|
|
|
863
|
+
buildRegister(register: number, value?: number) {
|
|
864
|
+
if (typeof value != "undefined") {
|
|
865
|
+
// set register
|
|
866
|
+
return [Cmd.Register, 1, ...this.wordToArray(register), ...this.signedToArray(value)];
|
|
867
|
+
} else {
|
|
868
|
+
// get register
|
|
869
|
+
return [Cmd.Register, 2, ...this.wordToArray(register)];
|
|
870
|
+
}
|
|
871
|
+
},
|
|
872
|
+
|
|
791
873
|
///////////////////
|
|
792
874
|
// Received info //
|
|
793
875
|
///////////////////
|
|
@@ -796,13 +878,13 @@ export const Protocol = {
|
|
|
796
878
|
|
|
797
879
|
if (next.cmd === Rec.Sensor) {
|
|
798
880
|
// sensor -> value
|
|
799
|
-
unit.value = next.message
|
|
881
|
+
unit.value = this.makeWord(next.message, 9); // 10x current temperature
|
|
800
882
|
unit.status = next.message[7]; // 0=idle, 1=heating, 2=cooling
|
|
801
883
|
unit.preset = (next.message[6]) ? next.message[8] : -1; // 0=sun, 1=half sun, 2=moon, 3=half moon, -1 = off
|
|
802
|
-
unit.sun = next.message
|
|
803
|
-
unit.hsun = next.message
|
|
804
|
-
unit.moon = next.message
|
|
805
|
-
unit.hmoon = next.message
|
|
884
|
+
unit.sun = this.makeWord(next.message, 11); // 10x temperature
|
|
885
|
+
unit.hsun = this.makeWord(next.message, 13); // 10x temperature
|
|
886
|
+
unit.moon = this.makeWord(next.message, 15); // 10x temperature
|
|
887
|
+
unit.hmoon = this.makeWord(next.message, 17); // 10x temperature
|
|
806
888
|
debug("protocol", "received status - " + unit.getDisplayName() + ", temperature = " + <number>unit.value / 10.0 + ", sun = " + unit.sun);
|
|
807
889
|
kind = "S";
|
|
808
890
|
|
|
@@ -858,19 +940,13 @@ export const Protocol = {
|
|
|
858
940
|
// only change dim value when state = 1 (ON, PIR ON, DIM STOP) for Dimmers and IRTX + for any setpoint event
|
|
859
941
|
if ((((next.message[4] === 1) || (next.message[4] === 6) || (next.message[4] === 7)) && (next.message[5] === 1)) ||
|
|
860
942
|
(next.message[4] === 11)) {
|
|
861
|
-
unit.value = next.message
|
|
943
|
+
unit.value = this.makeWord(next.message, 6);
|
|
862
944
|
}
|
|
863
945
|
|
|
864
946
|
debug("protocol", "received status - macro -> value=" + unit.value + " / status=" + unit.status);
|
|
865
947
|
kind = "M";
|
|
866
948
|
}
|
|
867
949
|
|
|
868
|
-
// clear the timer to turn the mood off again.
|
|
869
|
-
//if (unit.resetTimer) {
|
|
870
|
-
// clearInterval(unit.resetTimer);
|
|
871
|
-
// unit.resetTimer = null;
|
|
872
|
-
//}
|
|
873
|
-
|
|
874
950
|
this.alertSubscriber(unit);
|
|
875
951
|
this.emitter.emit('update', unit, kind);
|
|
876
952
|
},
|
package/duotecno/types.js
CHANGED
|
@@ -140,7 +140,9 @@ var SwitchType;
|
|
|
140
140
|
SwitchType["kSomfy"] = "somfy";
|
|
141
141
|
})(SwitchType = exports.SwitchType || (exports.SwitchType = {}));
|
|
142
142
|
;
|
|
143
|
-
exports.kEmptyBinding =
|
|
143
|
+
exports.kEmptyBinding = {
|
|
144
|
+
channel: "0", masterAddress: "0.0.0.0", masterPort: 5001, register: 0
|
|
145
|
+
};
|
|
144
146
|
exports.kEmptyAction = Object.assign(Object.assign({}, exports.kEmptyUnit), { value: false });
|
|
145
147
|
;
|
|
146
148
|
exports.kEmptyRule = {
|
|
@@ -325,9 +327,11 @@ exports.Sanitizers = {
|
|
|
325
327
|
return rule;
|
|
326
328
|
},
|
|
327
329
|
bindingConfig(binding) {
|
|
328
|
-
const b =
|
|
329
|
-
b.
|
|
330
|
-
|
|
330
|
+
const b = Object.assign({}, exports.kEmptyBinding);
|
|
331
|
+
b.masterAddress = binding.masterAddress || '',
|
|
332
|
+
b.register = binding.register || 0,
|
|
333
|
+
b.masterPort = binding.masterPort || 5001,
|
|
334
|
+
b.channel = binding.channel || "0";
|
|
331
335
|
return b;
|
|
332
336
|
},
|
|
333
337
|
//////////////
|
package/duotecno/types.ts
CHANGED
|
@@ -207,19 +207,24 @@ export enum SwitchType {
|
|
|
207
207
|
};
|
|
208
208
|
|
|
209
209
|
|
|
210
|
-
export interface Binding
|
|
210
|
+
export interface Binding {
|
|
211
|
+
masterAddress: string;
|
|
212
|
+
masterPort: number;
|
|
213
|
+
register: number;
|
|
211
214
|
channel: string;
|
|
212
|
-
|
|
215
|
+
value?: number;
|
|
213
216
|
}
|
|
214
217
|
export const kEmptyBinding: Binding = {
|
|
215
|
-
channel: "0",
|
|
218
|
+
channel: "0", masterAddress: "0.0.0.0", masterPort: 5001, register: 0
|
|
216
219
|
} as const;
|
|
217
220
|
|
|
221
|
+
|
|
218
222
|
export interface Action extends UnitDef {
|
|
219
223
|
value: number | boolean;
|
|
220
224
|
}
|
|
221
225
|
export const kEmptyAction: Action = { ...kEmptyUnit, value: false } as const;
|
|
222
226
|
|
|
227
|
+
|
|
223
228
|
export interface Rule {
|
|
224
229
|
type: string;
|
|
225
230
|
channel: string;
|
|
@@ -512,10 +517,11 @@ export const Sanitizers = {
|
|
|
512
517
|
},
|
|
513
518
|
|
|
514
519
|
bindingConfig(binding): Binding {
|
|
515
|
-
const b = <Binding>
|
|
516
|
-
|
|
520
|
+
const b = <Binding>{... kEmptyBinding};
|
|
521
|
+
b.masterAddress = binding.masterAddress || '',
|
|
522
|
+
b.register = binding.register || 0,
|
|
523
|
+
b.masterPort = binding.masterPort || 5001,
|
|
517
524
|
b.channel = binding.channel || "0";
|
|
518
|
-
b.power = binding.power || NaN;
|
|
519
525
|
return b;
|
|
520
526
|
},
|
|
521
527
|
|
package/index.spec.js
CHANGED
|
@@ -23,6 +23,7 @@ try {
|
|
|
23
23
|
const configStr = configBuf.toString();
|
|
24
24
|
const config = JSON.parse(configStr);
|
|
25
25
|
(0, logger_1.debug)("spec", config);
|
|
26
|
+
config.debug = true;
|
|
26
27
|
const platform = new platform_1.Platform(console, config, { registerPlatformAccessories: adder });
|
|
27
28
|
}
|
|
28
29
|
catch (err) {
|
package/index.spec.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-smartsystem",
|
|
3
3
|
"displayname": "Duotecno Bridge",
|
|
4
|
-
"version": "6.
|
|
4
|
+
"version": "6.4.2",
|
|
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
|
@@ -114,19 +114,19 @@ class PowerBase extends base_1.Base {
|
|
|
114
114
|
// for all bindings
|
|
115
115
|
this.bindings.forEach(b => {
|
|
116
116
|
const power = this.evalPower(b.channel);
|
|
117
|
-
if (this.significant(power, b.
|
|
118
|
-
(0, logger_1.debug)("power", "APPLY to " + b.channel + " = " + b.
|
|
119
|
-
"
|
|
120
|
-
" > threshold: 5% =" + Math.abs(b.
|
|
117
|
+
if (this.significant(power, b.value)) {
|
|
118
|
+
(0, logger_1.debug)("power", "APPLY to " + b.channel + " = " + b.value + " -> " + power + " for " + b.register +
|
|
119
|
+
"): difference: " + Math.abs(b.value - power) +
|
|
120
|
+
" > threshold: 5% =" + Math.abs(b.value * 0.05) + " > threshold: 2% =" + Math.abs(b.value * 0.02));
|
|
121
121
|
this.applyBinding(b, power)
|
|
122
|
-
.then(
|
|
123
|
-
b.
|
|
124
|
-
|
|
122
|
+
.then(power => {
|
|
123
|
+
b.value = power;
|
|
124
|
+
(0, logger_1.debug)("power", "set register " + b.register + " to " + power);
|
|
125
125
|
})
|
|
126
126
|
.catch((e) => (0, logger_1.err)(this.type, e.message || e));
|
|
127
127
|
// } else {
|
|
128
|
-
// debug("power", "DONT to " + b.channel + " = " + b.power + " -> " + power + " for " + b.
|
|
129
|
-
// "
|
|
128
|
+
// debug("power", "DONT to " + b.channel + " = " + b.power + " -> " + power + " for " + b.register +
|
|
129
|
+
// " : difference: " + Math.abs(b.power - power) +
|
|
130
130
|
// " > threshold: 5% =" + Math.abs(b.power * 0.05) + " > threshold: 2% =" + Math.abs(b.power * 0.02));
|
|
131
131
|
}
|
|
132
132
|
});
|
|
@@ -134,15 +134,14 @@ class PowerBase extends base_1.Base {
|
|
|
134
134
|
}
|
|
135
135
|
applyBinding(binding, value) {
|
|
136
136
|
return __awaiter(this, void 0, void 0, function* () {
|
|
137
|
-
const
|
|
138
|
-
if (
|
|
139
|
-
|
|
140
|
-
yield unit.setSensorValue(Math.abs(value));
|
|
137
|
+
const master = this.system.findMaster(binding.masterAddress, binding.masterPort);
|
|
138
|
+
if (master) {
|
|
139
|
+
yield master.setRegisterValue(binding.register, value);
|
|
141
140
|
return value;
|
|
142
141
|
}
|
|
143
|
-
else if ((binding.
|
|
144
|
-
throw new Error("*****
|
|
145
|
-
}
|
|
142
|
+
else if ((!binding.masterAddress) || (binding.register)) {
|
|
143
|
+
throw new Error("***** MASTER NOT FOUND for binding for value " + value + " to " + binding.masterAddress + ":" + binding.masterPort + " - register = " + binding.register + " *****");
|
|
144
|
+
} // else empty binding -> no error
|
|
146
145
|
});
|
|
147
146
|
}
|
|
148
147
|
/////////////////////
|