homebridge-smartsystem 6.8.13 → 7.0.1

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 (75) hide show
  1. package/.vscode/launch.json +15 -4
  2. package/MIXINS-GUIDE.md +210 -0
  3. package/README.md +32 -0
  4. package/SETTINGS-UPDATE.md +195 -0
  5. package/TESTING-GUIDE.md +220 -0
  6. package/WEBSOCKET-API.md +303 -0
  7. package/accessories/accessory.js +20 -4
  8. package/accessories/accessory.ts +19 -4
  9. package/accessories/bulb.js +0 -5
  10. package/accessories/bulb.ts +1 -5
  11. package/accessories/dimmer.js +11 -3
  12. package/accessories/dimmer.ts +11 -4
  13. package/accessories/garagedoor.js +19 -9
  14. package/accessories/garagedoor.ts +21 -9
  15. package/accessories/lock.js +11 -3
  16. package/accessories/lock.ts +10 -3
  17. package/accessories/temperature.js +10 -2
  18. package/accessories/temperature.ts +9 -2
  19. package/accessories/windowcovering.js +30 -14
  20. package/accessories/windowcovering.ts +29 -15
  21. package/config.json +57 -19
  22. package/duotecno/types.js +48 -2
  23. package/duotecno/types.ts +75 -1
  24. package/package.json +12 -4
  25. package/raspberry-pi-setup.sh +80 -0
  26. package/server/HA-API.js +4 -1
  27. package/server/HA-API.ts +4 -1
  28. package/server/HB.js +6 -1
  29. package/server/HB.ts +5 -1
  30. package/server/mDNS.js +168 -0
  31. package/server/mDNS.ts +198 -0
  32. package/server/platform.js +7 -0
  33. package/server/platform.ts +8 -0
  34. package/server/proxy.js +58 -19
  35. package/server/proxy.ts +31 -3
  36. package/server/smartapp.js +397 -18
  37. package/server/smartapp.ts +432 -20
  38. package/server/socapp.js +2 -1
  39. package/server/socapp.ts +2 -1
  40. package/server/views/assets/Duotecno_logo_black.svg +10 -0
  41. package/server/views/assets/Duotecno_logo_white.svg +10 -0
  42. package/server/views/device-details.ejs +139 -0
  43. package/server/views/device-list.ejs +44 -0
  44. package/server/views/footer.ejs +0 -1
  45. package/server/views/head.ejs +9 -0
  46. package/server/views/homekit.ejs +2 -9
  47. package/server/views/link-details.ejs +2 -15
  48. package/server/views/link-list.ejs +2 -9
  49. package/server/views/login.ejs +8 -9
  50. package/server/views/master-detail.ejs +3 -9
  51. package/server/views/master-list.ejs +3 -10
  52. package/server/views/nav.ejs +44 -11
  53. package/server/views/node-details.ejs +1 -8
  54. package/server/views/power-binding.ejs +3 -10
  55. package/server/views/power-rule.ejs +3 -10
  56. package/server/views/power.ejs +8 -15
  57. package/server/views/proxy.ejs +1 -8
  58. package/server/views/scripts.ejs +10 -0
  59. package/server/views/service-list.ejs +2 -9
  60. package/server/views/settings.ejs +61 -22
  61. package/server/views/style.ejs +49 -5
  62. package/server/views/switch-details.ejs +2 -9
  63. package/server/views/switch-list.ejs +2 -9
  64. package/server/webapp.js +150 -11
  65. package/server/webapp.ts +166 -14
  66. package/{index.spec.js → testHB.js} +1 -1
  67. package/{run.js → testProxy.js} +2 -2
  68. package/{run.ts → testProxy.ts} +1 -1
  69. package/testWS.js +137 -0
  70. package/testWS.ts +174 -0
  71. package/run9998.js +0 -7
  72. package/run9998.ts +0 -6
  73. package/run9999.js +0 -7
  74. package/run9999.ts +0 -6
  75. /package/{index.spec.ts → testHB.ts} +0 -0
@@ -0,0 +1,303 @@
1
+ # WebSocket Device Control API
2
+
3
+ ## Overview
4
+
5
+ The SmartApp now includes a WebSocket-based device control system that allows real-time monitoring and control of Duotecno devices with integrated power measurement support.
6
+
7
+ ## Features
8
+
9
+ - **WebSocket Server**: Runs on same ports as HTTP server (80 and configured port)
10
+ - **Real-time Device Status**: Get current relay states and power measurements
11
+ - **Power Measurement Integration**: Supports P1, Shelly, Smappee, or estimated power
12
+ - **Remote Control**: Toggle device relays by ID
13
+ - **Keep-alive Polling**: Send empty array to get status updates
14
+
15
+ ## WebSocket Endpoint
16
+
17
+ **URL**: `ws://duotecno-gateway.local/devices`
18
+
19
+ Or using IP address: `ws://your-server-ip/devices`
20
+
21
+ ## Device Configuration
22
+
23
+ ### Web Interface
24
+
25
+ 1. Navigate to `http://duotecno-gateway.local/devices` (or `http://your-server-ip/devices`)
26
+ 2. Click "Add device"
27
+ 3. Configure device:
28
+ - **Name**: Friendly name for the device
29
+ - **Master**: Select Duotecno master controller
30
+ - **Unit**: Select or enter unit address (node/unit)
31
+ - **Power Source**: Choose measurement source:
32
+ - **Non-measured (Estimated)**: Use fixed power estimate
33
+ - **P1 Meter**: Read from P1 smart meter
34
+ - **Shelly PM**: Read from Shelly power meter
35
+ - **Smappee**: Read from Smappee energy monitor
36
+ - **Power Channel**: Channel/register for measured sources (e.g., "0", "0+1+2")
37
+ - **Estimated Power**: Power in watts when not measured (used when relay is ON)
38
+
39
+ ### Device ID Calculation
40
+
41
+ Device IDs are automatically calculated as:
42
+ ```
43
+ Device ID = (Node Number × 256) + Unit Number
44
+ ```
45
+
46
+ Examples:
47
+ - Node 2, Unit 1 → ID = 513
48
+ - Node 1, Unit 2 → ID = 258
49
+ - Node 3, Unit 10 → ID = 778
50
+
51
+ ## WebSocket Protocol
52
+
53
+ ### Message Format
54
+
55
+ All messages are JSON arrays.
56
+
57
+ ### Client → Server Messages
58
+
59
+ #### 1. Keep-alive / Status Poll
60
+
61
+ Send empty array to receive current device status:
62
+
63
+ ```json
64
+ []
65
+ ```
66
+
67
+ #### 2. Control Relays
68
+
69
+ Send array of control commands:
70
+
71
+ ```json
72
+ [
73
+ { "id": 513, "relay": true }, // Turn ON device 513 (node 2, unit 1)
74
+ { "id": 258, "relay": false } // Turn OFF device 258 (node 1, unit 2)
75
+ ]
76
+ ```
77
+
78
+ ### Server → Client Messages
79
+
80
+ Server always responds with complete device list:
81
+
82
+ ```json
83
+ [
84
+ {
85
+ "id": 513,
86
+ "name": "Airco",
87
+ "power": 1250.5, // Current power in watts
88
+ "energy": 10.5, // Energy consumption in kWh (future)
89
+ "voltage": 230.0, // Voltage
90
+ "current": 5.44, // Current in amps (calculated)
91
+ "relay": true, // Relay state (ON/OFF)
92
+ "measured": true // true = real measurements, false = estimated
93
+ },
94
+ {
95
+ "id": 258,
96
+ "name": "Zwembad pomp",
97
+ "power": 0,
98
+ "energy": 0,
99
+ "voltage": 230.0,
100
+ "current": 0,
101
+ "relay": false,
102
+ "measured": false
103
+ }
104
+ ]
105
+ ```
106
+
107
+ ## Power Measurement Sources
108
+
109
+ ### Smappee
110
+
111
+ - **Channel Format**: `"0"`, `"1"`, `"0+1+2"` (sum multiple channels)
112
+ - **Data Source**: `this.power.smappee.bindings[].value`
113
+
114
+ ### P1 Meter
115
+
116
+ - **Channel Format**: `"0"`, `"1"`, `"0+1+2"`
117
+ - **Data Source**: `this.power.p1.bindings[].value`
118
+
119
+ ### Shelly PM
120
+
121
+ - **Channel Format**: `"0"`, `"1"`, `"0+1+2"`
122
+ - **Data Source**: `this.power.shelly.bindings[].value`
123
+
124
+ ### Non-measured (Estimated)
125
+
126
+ - **Power Calculation**: Uses `estimatedPower` when relay is ON, 0 when OFF
127
+ - **Use Case**: Devices without power monitoring
128
+
129
+ ## Testing with testWS.ts
130
+
131
+ ### Running the Test Client
132
+
133
+ ```bash
134
+ # Default (localhost:80)
135
+ ts-node testWS.ts
136
+
137
+ # Custom server
138
+ ts-node testWS.ts ws://192.168.0.99:5002/devices
139
+
140
+ # Or with compiled JavaScript
141
+ node testWS.js ws://192.168.0.99:5002/devices
142
+ ```
143
+
144
+ ### Test Client Features
145
+
146
+ - **Auto-connect**: Connects to WebSocket server
147
+ - **Keep-alive**: Polls every 5 seconds with `[]`
148
+ - **Random Control**: Toggles 1-3 random relays every 10-15 seconds
149
+ - **Pretty Display**: Shows device status in formatted table
150
+ - **Graceful Shutdown**: Press Ctrl+C to disconnect cleanly
151
+
152
+ ### Example Output
153
+
154
+ ```
155
+ ╔════════════════════════════════════════════════════════════════════╗
156
+ ║ WebSocket Device Control Test Client ║
157
+ ║ Press Ctrl+C to exit ║
158
+ ╚════════════════════════════════════════════════════════════════════╝
159
+
160
+ 🔌 Connecting to WebSocket server: ws://192.168.0.99:5002/devices
161
+ ✅ Connected to WebSocket server
162
+
163
+ 📊 Received 2 device(s):
164
+ ════════════════════════════════════════════════════════════════════════════════════════════════════
165
+ ID | Name | Power(W) | Current(A) | Voltage(V) | Relay | Measured
166
+ ────────────────────────────────────────────────────────────────────────────────────────────────────
167
+ 513 | Airco | 1250.5 | 5.44 | 230.0 | 🟢 ON | 📊
168
+ 258 | Zwembad pomp | 0.0 | 0.00 | 230.0 | 🔴 OFF | 📝
169
+ ════════════════════════════════════════════════════════════════════════════════════════════════════
170
+
171
+ ⏱️ Sending keep-alive poll...
172
+
173
+ 🎲 Sending random control commands:
174
+ → Airco (node=2, unit=1, id=513): 🔴 OFF
175
+ → Zwembad pomp (node=1, unit=2, id=258): 🟢 ON
176
+ ```
177
+
178
+ ## Configuration Example
179
+
180
+ Add to your `config.json`:
181
+
182
+ ```json
183
+ {
184
+ "smartapp": {
185
+ "port": 5002,
186
+ "devices": [
187
+ {
188
+ "name": "Airco",
189
+ "masterAddress": "192.168.0.97",
190
+ "masterPort": 5001,
191
+ "logicalNodeAddress": 2,
192
+ "logicalAddress": 1,
193
+ "powerSource": "smappee",
194
+ "powerChannel": "0",
195
+ "measured": true
196
+ },
197
+ {
198
+ "name": "Zwembad pomp",
199
+ "masterAddress": "192.168.0.97",
200
+ "masterPort": 5001,
201
+ "logicalNodeAddress": 1,
202
+ "logicalAddress": 2,
203
+ "powerSource": "none",
204
+ "estimatedPower": 750,
205
+ "measured": false
206
+ }
207
+ ]
208
+ }
209
+ }
210
+ ```
211
+
212
+ ## Client Implementation Example
213
+
214
+ ### JavaScript/TypeScript
215
+
216
+ ```typescript
217
+ import * as WebSocket from 'ws';
218
+
219
+ const ws = new WebSocket('ws://192.168.0.99:5002/devices');
220
+
221
+ ws.on('open', () => {
222
+ console.log('Connected');
223
+
224
+ // Poll for status
225
+ ws.send('[]');
226
+ });
227
+
228
+ ws.on('message', (data: string) => {
229
+ const devices = JSON.parse(data.toString());
230
+ console.log('Devices:', devices);
231
+
232
+ // Control a device
233
+ ws.send(JSON.stringify([
234
+ { id: 513, relay: true }
235
+ ]));
236
+ });
237
+ ```
238
+
239
+ ### Python
240
+
241
+ ```python
242
+ import websocket
243
+ import json
244
+
245
+ def on_message(ws, message):
246
+ devices = json.loads(message)
247
+ print(f"Devices: {devices}")
248
+
249
+ # Control device 513
250
+ ws.send(json.dumps([{"id": 513, "relay": True}]))
251
+
252
+ def on_open(ws):
253
+ print("Connected")
254
+ # Poll for status
255
+ ws.send("[]")
256
+
257
+ ws = websocket.WebSocketApp(
258
+ "ws://192.168.0.99:5002/devices",
259
+ on_message=on_message,
260
+ on_open=on_open
261
+ )
262
+ ws.run_forever()
263
+ ```
264
+
265
+ ## Error Handling
266
+
267
+ - Invalid JSON: Server ignores message
268
+ - Non-array messages: Server logs error and ignores
269
+ - Invalid device IDs: Silently ignored (no such device)
270
+ - Connection closed: Automatic cleanup
271
+
272
+ ## Security Considerations
273
+
274
+ - WebSocket uses same authentication as HTTP (if enabled)
275
+ - No encryption by default (use reverse proxy with WSS for production)
276
+ - Consider rate limiting for control commands
277
+ - Validate device IDs match authorized units
278
+
279
+ ## Troubleshooting
280
+
281
+ ### Connection Refused
282
+
283
+ - Check server is running
284
+ - Verify port is correct (80 or configured port)
285
+ - Check firewall rules
286
+
287
+ ### Devices Not Updating
288
+
289
+ - Verify device configuration in web UI
290
+ - Check power source is configured correctly
291
+ - Verify channel numbers match power meter setup
292
+ - Check server logs for errors
293
+
294
+ ### Power Always Zero
295
+
296
+ - For measured devices: Check power source connection
297
+ - For estimated: Verify `estimatedPower` is set
298
+ - Check relay state is updating correctly
299
+
300
+ ## API Version
301
+
302
+ Current version: 1.0
303
+ Last updated: November 2025
@@ -49,8 +49,16 @@ class Accessory {
49
49
  getState(next) {
50
50
  if (this.unit) {
51
51
  this.unit.reqState(unit => {
52
- next(null, unit.status);
53
- (0, logger_1.log)("accessory", "getState was called for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.status);
52
+ try {
53
+ if (!unit) {
54
+ return next(new Error("Unit state not available"));
55
+ }
56
+ next(null, unit.status);
57
+ (0, logger_1.log)("accessory", "getState was called for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.status);
58
+ }
59
+ catch (err) {
60
+ next(err);
61
+ }
54
62
  }).catch(err => next(err));
55
63
  }
56
64
  else {
@@ -60,8 +68,16 @@ class Accessory {
60
68
  getValue(next) {
61
69
  if (this.unit) {
62
70
  this.unit.reqState(unit => {
63
- next(null, unit.value);
64
- (0, logger_1.log)("accessory", "getValue was called for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.value);
71
+ try {
72
+ if (!unit) {
73
+ return next(new Error("Unit state not available"));
74
+ }
75
+ next(null, unit.value);
76
+ (0, logger_1.log)("accessory", "getValue was called for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.value);
77
+ }
78
+ catch (err) {
79
+ next(err);
80
+ }
65
81
  }).catch(err => next(err));
66
82
  }
67
83
  else {
@@ -68,8 +68,15 @@ export class Accessory {
68
68
  getState(next) {
69
69
  if (this.unit) {
70
70
  this.unit.reqState(unit => {
71
- next(null, unit.status);
72
- log("accessory", "getState was called for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.status);
71
+ try {
72
+ if (!unit) {
73
+ return next(new Error("Unit state not available"));
74
+ }
75
+ next(null, unit.status);
76
+ log("accessory", "getState was called for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.status);
77
+ } catch(err) {
78
+ next(err);
79
+ }
73
80
  }).catch(err => next(err));
74
81
  } else {
75
82
  next( new Error("accessory - getState needs to be overridden if no unit is provided.") );
@@ -79,14 +86,22 @@ export class Accessory {
79
86
  getValue(next) {
80
87
  if (this.unit) {
81
88
  this.unit.reqState(unit => {
82
- next(null, unit.value);
83
- log("accessory", "getValue was called for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.value);
89
+ try {
90
+ if (!unit) {
91
+ return next(new Error("Unit state not available"));
92
+ }
93
+ next(null, unit.value);
94
+ log("accessory", "getValue was called for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.value);
95
+ } catch(err) {
96
+ next(err);
97
+ }
84
98
  }).catch(err => next(err));
85
99
  } else {
86
100
  next( new Error("accessory - getState needs to be overridden if no unit is provided.") );
87
101
  }
88
102
  }
89
103
 
104
+
90
105
  setState(value, next) {
91
106
  if (this.unit)
92
107
  this.unit.setState(value)
@@ -25,11 +25,6 @@ class Bulb extends accessory_1.Accessory {
25
25
  .then(() => next())
26
26
  .catch(err => next(err));
27
27
  }
28
- setBrightness(value, next) {
29
- this.unit.setState(value)
30
- .then(() => next())
31
- .catch(err => next(err));
32
- }
33
28
  }
34
29
  exports.Bulb = Bulb;
35
30
  //# sourceMappingURL=bulb.js.map
@@ -34,9 +34,5 @@ export class Bulb extends Accessory {
34
34
  .then(() => next())
35
35
  .catch(err => next(err))
36
36
  }
37
- setBrightness(value, next) {
38
- this.unit.setState(value)
39
- .then(() => next())
40
- .catch(err => next(err))
41
- }
37
+
42
38
  }
@@ -30,12 +30,20 @@ class Dimmer extends accessory_1.Accessory {
30
30
  getBrightness(next) {
31
31
  if (this.unit) {
32
32
  this.unit.reqState(unit => {
33
- next(null, unit.value);
34
- (0, logger_1.log)("accessory", "getBrightness was called for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.value);
33
+ try {
34
+ if (!unit) {
35
+ return next(new Error("Unit state not available"));
36
+ }
37
+ next(null, unit.value);
38
+ (0, logger_1.log)("accessory", "getBrightness was called for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.value);
39
+ }
40
+ catch (err) {
41
+ next(err);
42
+ }
35
43
  }).catch(err => next(err));
36
44
  }
37
45
  else {
38
- next(new Error("accessory - getState needs to be overridden if no unit is provided."));
46
+ next(new Error("accessory - getBrightness needs to be overridden if no unit is provided."));
39
47
  }
40
48
  }
41
49
  setPower(powerOn, next) {
@@ -41,14 +41,21 @@ export class Dimmer extends Accessory {
41
41
  getBrightness(next) {
42
42
  if (this.unit) {
43
43
  this.unit.reqState(unit => {
44
- next(null, unit.value);
45
- log("accessory", "getBrightness was called for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.value);
44
+ try {
45
+ if (!unit) {
46
+ return next(new Error("Unit state not available"));
47
+ }
48
+ next(null, unit.value);
49
+ log("accessory", "getBrightness was called for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.value);
50
+ } catch(err) {
51
+ next(err);
52
+ }
46
53
  }).catch(err => next(err));
47
54
  } else {
48
- next( new Error("accessory - getState needs to be overridden if no unit is provided.") );
55
+ next( new Error("accessory - getBrightness needs to be overridden if no unit is provided.") );
49
56
  }
50
57
  }
51
-
58
+
52
59
  setPower(powerOn, next) {
53
60
  this.waitingSetPower = true;
54
61
  log("accessory", "setPower: *** waiting *** " + powerOn);
@@ -13,7 +13,7 @@ const accessory_1 = require("./accessory");
13
13
  class GarageDoor extends accessory_1.Accessory {
14
14
  constructor(homebridge, unit) {
15
15
  super(homebridge, unit);
16
- this.targetState = this.homebridge.Characteristic.CurrentDoorState.STOPPED;
16
+ this.targetState = this.homebridge.Characteristic.TargetDoorState.CLOSED;
17
17
  }
18
18
  getAccessoryServices() {
19
19
  // GarageDoorOpener needs authentication
@@ -39,13 +39,15 @@ class GarageDoor extends accessory_1.Accessory {
39
39
  return this.homebridge.Characteristic.CurrentDoorState.OPENING;
40
40
  else if (status == types_1.UnitState.kStopped)
41
41
  return this.homebridge.Characteristic.CurrentDoorState.STOPPED;
42
- else
43
- return this.homebridge.Characteristic.CurrentDoorState.STOPPED; // ????
42
+ else {
43
+ (0, logger_1.log)("accessory", `Unknown Door/UnitState ${status}, defaulting to STOPPED`);
44
+ return this.homebridge.Characteristic.CurrentDoorState.STOPPED;
45
+ }
44
46
  }
45
47
  HB2DT(state) {
46
- if (state == this.homebridge.Characteristic.CurrentDoorState.OPEN)
48
+ if (state == this.homebridge.Characteristic.TargetDoorState.OPEN)
47
49
  return types_1.UnitMotorCmd.kOpen;
48
- else if (state == this.homebridge.Characteristic.CurrentDoorState.CLOSED)
50
+ else if (state == this.homebridge.Characteristic.TargetDoorState.CLOSED)
49
51
  return types_1.UnitMotorCmd.kClose;
50
52
  else if (state == this.homebridge.Characteristic.CurrentDoorState.STOPPED)
51
53
  return types_1.UnitMotorCmd.kStop;
@@ -56,7 +58,7 @@ class GarageDoor extends accessory_1.Accessory {
56
58
  try {
57
59
  this.unit.reqState(unit => {
58
60
  const hb = this.DT2HB(unit.status);
59
- (0, logger_1.log)("accessory", "Get TargetDoorState of " + this.name + " = " + unit.status + " -> " + hb);
61
+ (0, logger_1.log)("accessory", "Get CurrentDoorState of " + this.name + " = " + unit.status + " -> " + hb);
60
62
  next(null, hb);
61
63
  });
62
64
  }
@@ -86,9 +88,17 @@ class GarageDoor extends accessory_1.Accessory {
86
88
  }
87
89
  // in response to Duotecno status messages
88
90
  updateState() {
89
- const value = this.DT2HB(this.unit.status);
90
- (0, logger_1.log)("accessory", "Received updateState -> Homekit GarageDoor for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.status + " / " + this.unit.value + " -> passing to HB: " + value);
91
- this.me.getCharacteristic(this.homebridge.Characteristic.CurrentDoorState).updateValue(value);
91
+ const current = this.DT2HB(this.unit.status);
92
+ const target = current === this.homebridge.Characteristic.CurrentDoorState.OPEN
93
+ ? this.homebridge.Characteristic.TargetDoorState.OPEN
94
+ : this.homebridge.Characteristic.TargetDoorState.CLOSED;
95
+ (0, logger_1.log)("accessory", "Received updateState -> Homekit GarageDoor for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.status + " -> passing to HB (current/target): " + current + " / " + target);
96
+ this.me
97
+ .getCharacteristic(this.homebridge.Characteristic.CurrentDoorState)
98
+ .updateValue(current);
99
+ this.me
100
+ .getCharacteristic(this.homebridge.Characteristic.TargetDoorState)
101
+ .updateValue(target);
92
102
  }
93
103
  }
94
104
  exports.GarageDoor = GarageDoor;
@@ -14,7 +14,7 @@ import { Accessory } from "./accessory";
14
14
 
15
15
 
16
16
  export class GarageDoor extends Accessory {
17
- targetState = this.homebridge.Characteristic.CurrentDoorState.STOPPED;
17
+ targetState = this.homebridge.Characteristic.TargetDoorState.CLOSED;
18
18
 
19
19
  constructor(homebridge: API, unit: Unit) {
20
20
  super(homebridge, unit);
@@ -56,15 +56,17 @@ export class GarageDoor extends Accessory {
56
56
  else if (status == UnitState.kStopped)
57
57
  return this.homebridge.Characteristic.CurrentDoorState.STOPPED;
58
58
 
59
- else
60
- return this.homebridge.Characteristic.CurrentDoorState.STOPPED; // ????
59
+ else {
60
+ log("accessory", `Unknown Door/UnitState ${status}, defaulting to STOPPED`);
61
+ return this.homebridge.Characteristic.CurrentDoorState.STOPPED;
62
+ }
61
63
  }
62
64
 
63
65
  HB2DT(state): UnitMotorCmd | null {
64
- if (state == this.homebridge.Characteristic.CurrentDoorState.OPEN)
66
+ if (state == this.homebridge.Characteristic.TargetDoorState.OPEN)
65
67
  return UnitMotorCmd.kOpen;
66
68
 
67
- else if (state == this.homebridge.Characteristic.CurrentDoorState.CLOSED)
69
+ else if (state == this.homebridge.Characteristic.TargetDoorState.CLOSED)
68
70
  return UnitMotorCmd.kClose;
69
71
 
70
72
  else if (state == this.homebridge.Characteristic.CurrentDoorState.STOPPED)
@@ -78,7 +80,7 @@ export class GarageDoor extends Accessory {
78
80
  try {
79
81
  this.unit.reqState(unit => {
80
82
  const hb = this.DT2HB(unit.status);
81
- log("accessory", "Get TargetDoorState of " + this.name + " = " + unit.status + " -> " + hb);
83
+ log("accessory", "Get CurrentDoorState of " + this.name + " = " + unit.status + " -> " + hb);
82
84
  next(null, hb);
83
85
  });
84
86
 
@@ -114,9 +116,19 @@ export class GarageDoor extends Accessory {
114
116
 
115
117
  // in response to Duotecno status messages
116
118
  updateState() {
117
- const value = this.DT2HB(this.unit.status);
118
- log("accessory", "Received updateState -> Homekit GarageDoor for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.status + " / " + this.unit.value + " -> passing to HB: " + value);
119
- this.me.getCharacteristic(this.homebridge.Characteristic.CurrentDoorState).updateValue(value);
119
+ const current = this.DT2HB(this.unit.status);
120
+ const target =
121
+ current === this.homebridge.Characteristic.CurrentDoorState.OPEN
122
+ ? this.homebridge.Characteristic.TargetDoorState.OPEN
123
+ : this.homebridge.Characteristic.TargetDoorState.CLOSED;
124
+
125
+ log("accessory", "Received updateState -> Homekit GarageDoor for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.status + " -> passing to HB (current/target): " + current + " / " + target);
126
+ this.me
127
+ .getCharacteristic(this.homebridge.Characteristic.CurrentDoorState)
128
+ .updateValue(current);
129
+ this.me
130
+ .getCharacteristic(this.homebridge.Characteristic.TargetDoorState)
131
+ .updateValue(target);
120
132
  }
121
133
 
122
134
  }
@@ -63,9 +63,17 @@ class Lock extends accessory_1.Accessory {
63
63
  }
64
64
  getCurrent(next) {
65
65
  this.unit.reqState(unit => {
66
- (0, logger_1.log)("accessory", "HB getting current state of lock = " + this.unit.status + " of " + this.unit.getDescription());
67
- next(null, !!this.unit.status);
68
- });
66
+ try {
67
+ if (!unit) {
68
+ return next(new Error("Unit state not available"));
69
+ }
70
+ (0, logger_1.log)("accessory", "HB getting current state of lock = " + this.unit.status + " of " + this.unit.getDescription());
71
+ next(null, !!this.unit.status);
72
+ }
73
+ catch (err) {
74
+ next(err);
75
+ }
76
+ }).catch(err => next(err));
69
77
  }
70
78
  // in response to Duotecno status messages
71
79
  updateState() {
@@ -81,9 +81,16 @@ export class Lock extends Accessory {
81
81
 
82
82
  getCurrent(next) {
83
83
  this.unit.reqState(unit => {
84
- log("accessory", "HB getting current state of lock = " + this.unit.status + " of " + this.unit.getDescription());
85
- next(null, !!this.unit.status);
86
- });
84
+ try {
85
+ if (!unit) {
86
+ return next(new Error("Unit state not available"));
87
+ }
88
+ log("accessory", "HB getting current state of lock = " + this.unit.status + " of " + this.unit.getDescription());
89
+ next(null, !!this.unit.status);
90
+ } catch(err) {
91
+ next(err);
92
+ }
93
+ }).catch(err => next(err));
87
94
  }
88
95
 
89
96
 
@@ -23,8 +23,16 @@ class Temperature extends accessory_1.Accessory {
23
23
  getTemperature(next) {
24
24
  if (this.unit) {
25
25
  this.unit.reqState(unit => {
26
- (0, logger_1.log)("accessory", "reqState/getTemperature returned a value for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.value);
27
- next(null, unit.value / 10.0);
26
+ try {
27
+ if (!unit) {
28
+ return next(new Error("Unit state not available"));
29
+ }
30
+ (0, logger_1.log)("accessory", "reqState/getTemperature returned a value for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.value);
31
+ next(null, unit.value / 10.0);
32
+ }
33
+ catch (err) {
34
+ next(err);
35
+ }
28
36
  })
29
37
  .catch(err => next(err));
30
38
  }
@@ -31,8 +31,15 @@ export class Temperature extends Accessory {
31
31
  getTemperature(next) {
32
32
  if (this.unit) {
33
33
  this.unit.reqState(unit => {
34
- log("accessory", "reqState/getTemperature returned a value for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.value);
35
- next(null, <number>unit.value / 10.0);
34
+ try {
35
+ if (!unit) {
36
+ return next(new Error("Unit state not available"));
37
+ }
38
+ log("accessory", "reqState/getTemperature returned a value for " + this.unit.node.getName() + " - " + this.unit.getName() + " -> " + this.unit.value);
39
+ next(null, <number>unit.value / 10.0);
40
+ } catch(err) {
41
+ next(err);
42
+ }
36
43
  })
37
44
  .catch(err => next(err));
38
45
  } else {