sinricpro 4.0.0 → 6.0.0

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 (36) hide show
  1. package/README.md +43 -0
  2. package/dist/capabilities/EqualizerController.js +4 -3
  3. package/dist/capabilities/EqualizerController.js.map +1 -1
  4. package/dist/capabilities/ModeController.js +5 -3
  5. package/dist/capabilities/ModeController.js.map +1 -1
  6. package/dist/capabilities/RangeController.js +7 -4
  7. package/dist/capabilities/RangeController.js.map +1 -1
  8. package/dist/capabilities/SettingController.js +12 -3
  9. package/dist/capabilities/SettingController.js.map +1 -1
  10. package/dist/core/MessageQueue.js +6 -2
  11. package/dist/core/MessageQueue.js.map +1 -1
  12. package/dist/core/Signature.js +51 -18
  13. package/dist/core/Signature.js.map +1 -1
  14. package/dist/core/SinricPro.js +255 -42
  15. package/dist/core/SinricPro.js.map +1 -1
  16. package/dist/core/SinricProDevice.js +16 -10
  17. package/dist/core/SinricProDevice.js.map +1 -1
  18. package/dist/core/WebSocketClient.js +2 -1
  19. package/dist/core/WebSocketClient.js.map +1 -1
  20. package/dist/core/local/MdnsAnnouncer.js +107 -0
  21. package/dist/core/local/MdnsAnnouncer.js.map +1 -0
  22. package/dist/core/local/UdpListener.js +135 -0
  23. package/dist/core/local/UdpListener.js.map +1 -0
  24. package/dist/core/types.js +9 -1
  25. package/dist/core/types.js.map +1 -1
  26. package/dist/devices/SinricProCustomDevice.js +63 -0
  27. package/dist/devices/SinricProCustomDevice.js.map +1 -0
  28. package/dist/devices/SinricProSwitch.js +3 -1
  29. package/dist/devices/SinricProSwitch.js.map +1 -1
  30. package/dist/devices/SinricProTemperatureSensor.js +28 -0
  31. package/dist/devices/SinricProTemperatureSensor.js.map +1 -0
  32. package/dist/devices/index.js +3 -0
  33. package/dist/devices/index.js.map +1 -1
  34. package/dist/utils/network.js +58 -0
  35. package/dist/utils/network.js.map +1 -0
  36. package/package.json +14 -10
package/README.md CHANGED
@@ -10,6 +10,7 @@ Official SinricPro SDK for Node.js and TypeScript. Control your IoT devices with
10
10
  - ✅ Modern async/await API
11
11
  - ✅ WebSocket with automatic reconnection
12
12
  - ✅ HMAC-SHA256 authentication
13
+ - ✅ Local control over the LAN (works while SinricPro is unreachable)
13
14
  - ✅ Event rate limiting
14
15
  - ✅ Multiple device types (Switch, Light, Thermostat, etc.)
15
16
  - ✅ Comprehensive error handling
@@ -95,6 +96,48 @@ ts-node app.ts
95
96
 
96
97
  ---
97
98
 
99
+ ## 🏠 Local Control
100
+
101
+ Devices also answer signed commands over the local network, so they keep working
102
+ while SinricPro is unreachable. It is on by default and needs no application
103
+ changes: a LAN request runs the same callbacks as a cloud request. UDP listener on port `3333`, joined to multicast group `224.9.9.9` and answering unicast on the same port. Replies go back to the peer that sent the request, never to the cloud websocket.
104
+
105
+ The mDNS announcement uses `bonjour-service`, installed with the SDK:
106
+
107
+ ```bash
108
+ npm install sinricpro
109
+ ```
110
+
111
+ ---
112
+
113
+ ## Troubleshooting
114
+
115
+ ### Connection Issues
116
+
117
+ 1. **Check credentials** - Ensure APP_KEY and APP_SECRET are correct
118
+ 2. **Check device ID** - Verify the device ID is exactly 24 hexadecimal characters
119
+ 3. **Check network** - Ensure you have internet connectivity
120
+ 4. **Enable debug logging** -
121
+
122
+ ```js
123
+ import { SinricProSdkLogger, LogLevel } from 'sinricpro';
124
+ SinricProSdkLogger.setLevel(LogLevel.DEBUG); // DEBUG, INFO, WARN, ERROR, NONE
125
+ ```
126
+
127
+
128
+ ### Local Control Issues
129
+
130
+ 1. **No device found on the LAN** - check the log for
131
+ `Local control listening on UDP 3333`. A failed multicast join leaves nothing
132
+ listening, and the log line says so.
133
+ 2. **No mDNS record** - install the extra: `pip install sinricpro[mdns]`.
134
+ 3. **Discovery answers on the wrong network** - set `local_control_interface` to the
135
+ LAN address of the host.
136
+ 4. **Android clients need a `WifiManager.MulticastLock`**, and iOS clients need
137
+ `_sinricpro._udp` listed in `NSBonjourServices`, or discovery returns nothing.
138
+
139
+ ---
140
+
98
141
  ## 🤝 Contributing
99
142
 
100
143
  Contributions welcome! Please:
@@ -32,11 +32,11 @@ function EqualizerController(Base) {
32
32
  }
33
33
  return this.sendEvent('setBands', { bands }, cause);
34
34
  }
35
- async sendModeEvent(mode, cause = types_1.PHYSICAL_INTERACTION) {
35
+ async sendModeEvent(mode, instanceId = '', cause = types_1.PHYSICAL_INTERACTION) {
36
36
  if (this.equalizerEventLimiter.isLimited()) {
37
37
  return false;
38
38
  }
39
- return this.sendEvent('setMode', { mode }, cause);
39
+ return this.sendEvent('setMode', { mode }, cause, instanceId);
40
40
  }
41
41
  async handleEqualizerRequest(request) {
42
42
  if (request.action === 'setBands' && this.setBandsCallback) {
@@ -79,7 +79,8 @@ function EqualizerController(Base) {
79
79
  }
80
80
  if (request.action === 'setMode' && this.setModeCallback) {
81
81
  const mode = request.requestValue.mode;
82
- const result = await this.setModeCallback(this.getDeviceId(), mode);
82
+ const instanceId = request.instance || '';
83
+ const result = await this.setModeCallback(this.getDeviceId(), mode, instanceId);
83
84
  // Handle both boolean and object return types
84
85
  let success;
85
86
  if (typeof result === 'boolean') {
@@ -1 +1 @@
1
- {"version":3,"file":"EqualizerController.js","sourceRoot":"","sources":["../../src/capabilities/EqualizerController.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"EqualizerController.js","sourceRoot":"","sources":["../../src/capabilities/EqualizerController.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  /**
3
3
  * ModeController - Adds generic mode control capability
4
+ * Supports optional instanceId for multi-instance mode control
4
5
  */
5
6
  Object.defineProperty(exports, "__esModule", { value: true });
6
7
  exports.ModeController = ModeController;
@@ -18,18 +19,19 @@ function ModeController(Base) {
18
19
  onSetMode(callback) {
19
20
  this.setModeCallback = callback;
20
21
  }
21
- async sendModeEvent(mode, cause = types_1.PHYSICAL_INTERACTION) {
22
+ async sendModeEvent(mode, instanceId = '', cause = types_1.PHYSICAL_INTERACTION) {
22
23
  if (this.modeEventLimiter.isLimited()) {
23
24
  return false;
24
25
  }
25
- return this.sendEvent('setMode', { mode }, cause);
26
+ return this.sendEvent('setMode', { mode }, cause, instanceId);
26
27
  }
27
28
  async handleModeRequest(request) {
28
29
  if (request.action !== 'setMode' || !this.setModeCallback) {
29
30
  return false;
30
31
  }
31
32
  const mode = request.requestValue.mode;
32
- const result = await this.setModeCallback(this.getDeviceId(), mode);
33
+ const instanceId = request.instance || '';
34
+ const result = await this.setModeCallback(this.getDeviceId(), mode, instanceId);
33
35
  // Handle both boolean and object return types
34
36
  let success;
35
37
  if (typeof result === 'boolean') {
@@ -1 +1 @@
1
- {"version":3,"file":"ModeController.js","sourceRoot":"","sources":["../../src/capabilities/ModeController.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAoBH,wCAiDC;AAlED,uDAAoD;AAEpD,yCAAwE;AAexE,SAAgB,cAAc,CAAyC,IAAO;IAC5E,OAAO,KAAM,SAAQ,IAAI;QAIvB,8DAA8D;QAC9D,YAAY,GAAG,IAAW;YACxB,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;YALT,oBAAe,GAA2B,IAAI,CAAC;YAC/C,qBAAgB,GAAG,IAAI,2BAAY,CAAC,yBAAiB,CAAC,CAAC;YAK7D,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACjE,CAAC;QAED,SAAS,CAAC,QAAyB;YACjC,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC;QAClC,CAAC;QAED,KAAK,CAAC,aAAa,CAAC,IAAY,EAAE,QAAgB,4BAAoB;YACpE,IAAI,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,EAAE,CAAC;gBACtC,OAAO,KAAK,CAAC;YACf,CAAC;YAED,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC;QACpD,CAAC;QAEO,KAAK,CAAC,iBAAiB,CAAC,OAAyB;YACvD,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;gBAC1D,OAAO,KAAK,CAAC;YACf,CAAC;YAED,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC;YACvC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,CAAC;YAEpE,8CAA8C;YAC9C,IAAI,OAAgB,CAAC;YACrB,IAAI,OAAO,MAAM,KAAK,SAAS,EAAE,CAAC;gBAChC,OAAO,GAAG,MAAM,CAAC;YACnB,CAAC;iBAAM,CAAC;gBACN,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;gBACzB,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;oBACnB,OAAO,CAAC,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC;gBACxC,CAAC;YACH,CAAC;YAED,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,CAAC,aAAa,CAAC,IAAI,GAAG,IAAI,CAAC;YACpC,CAAC;YAED,OAAO,OAAO,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"ModeController.js","sourceRoot":"","sources":["../../src/capabilities/ModeController.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAqBH,wCAsDC;AAxED,uDAAoD;AAEpD,yCAAwE;AAgBxE,SAAgB,cAAc,CAAyC,IAAO;IAC5E,OAAO,KAAM,SAAQ,IAAI;QAIvB,8DAA8D;QAC9D,YAAY,GAAG,IAAW;YACxB,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;YALT,oBAAe,GAA2B,IAAI,CAAC;YAC/C,qBAAgB,GAAG,IAAI,2BAAY,CAAC,yBAAiB,CAAC,CAAC;YAK7D,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACjE,CAAC;QAED,SAAS,CAAC,QAAyB;YACjC,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC;QAClC,CAAC;QAED,KAAK,CAAC,aAAa,CACjB,IAAY,EACZ,aAAqB,EAAE,EACvB,QAAgB,4BAAoB;YAEpC,IAAI,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,EAAE,CAAC;gBACtC,OAAO,KAAK,CAAC;YACf,CAAC;YAED,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;QAChE,CAAC;QAEO,KAAK,CAAC,iBAAiB,CAAC,OAAyB;YACvD,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;gBAC1D,OAAO,KAAK,CAAC;YACf,CAAC;YAED,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC;YACvC,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;YAC1C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;YAEhF,8CAA8C;YAC9C,IAAI,OAAgB,CAAC;YACrB,IAAI,OAAO,MAAM,KAAK,SAAS,EAAE,CAAC;gBAChC,OAAO,GAAG,MAAM,CAAC;YACnB,CAAC;iBAAM,CAAC;gBACN,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;gBACzB,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;oBACnB,OAAO,CAAC,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC;gBACxC,CAAC;YACH,CAAC;YAED,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,CAAC,aAAa,CAAC,IAAI,GAAG,IAAI,CAAC;YACpC,CAAC;YAED,OAAO,OAAO,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  /**
3
3
  * RangeController - Adds generic range control capability
4
+ * Supports optional instanceId for multi-instance range control
4
5
  */
5
6
  Object.defineProperty(exports, "__esModule", { value: true });
6
7
  exports.RangeController = RangeController;
@@ -22,16 +23,17 @@ function RangeController(Base) {
22
23
  onAdjustRangeValue(callback) {
23
24
  this.adjustRangeValueCallback = callback;
24
25
  }
25
- async sendRangeValueEvent(rangeValue, cause = types_1.PHYSICAL_INTERACTION) {
26
+ async sendRangeValueEvent(rangeValue, instanceId = '', cause = types_1.PHYSICAL_INTERACTION) {
26
27
  if (this.rangeEventLimiter.isLimited()) {
27
28
  return false;
28
29
  }
29
- return this.sendEvent('setRangeValue', { rangeValue }, cause);
30
+ return this.sendEvent('setRangeValue', { rangeValue }, cause, instanceId);
30
31
  }
31
32
  async handleRangeRequest(request) {
32
33
  if (request.action === 'setRangeValue' && this.rangeValueCallback) {
33
34
  const rangeValue = request.requestValue.rangeValue;
34
- const success = await this.rangeValueCallback(this.getDeviceId(), rangeValue);
35
+ const instanceId = request.instance || '';
36
+ const success = await this.rangeValueCallback(this.getDeviceId(), rangeValue, instanceId);
35
37
  if (success) {
36
38
  request.responseValue.rangeValue = rangeValue;
37
39
  }
@@ -39,7 +41,8 @@ function RangeController(Base) {
39
41
  }
40
42
  if (request.action === 'adjustRangeValue' && this.adjustRangeValueCallback) {
41
43
  const rangeValueDelta = request.requestValue.rangeValueDelta;
42
- const success = await this.adjustRangeValueCallback(this.getDeviceId(), rangeValueDelta);
44
+ const instanceId = request.instance || '';
45
+ const success = await this.adjustRangeValueCallback(this.getDeviceId(), rangeValueDelta, instanceId);
43
46
  if (success) {
44
47
  request.responseValue.rangeValue = rangeValueDelta;
45
48
  }
@@ -1 +1 @@
1
- {"version":3,"file":"RangeController.js","sourceRoot":"","sources":["../../src/capabilities/RangeController.ts"],"names":[],"mappings":";AAAA;;GAEG;;AA0BH,0CAyDC;AAhFD,uDAAoD;AAEpD,yCAAwE;AAqBxE,SAAgB,eAAe,CAAyC,IAAO;IAC7E,OAAO,KAAM,SAAQ,IAAI;QAKvB,8DAA8D;QAC9D,YAAY,GAAG,IAAW;YACxB,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;YANT,uBAAkB,GAA8B,IAAI,CAAC;YACrD,6BAAwB,GAAoC,IAAI,CAAC;YACjE,sBAAiB,GAAG,IAAI,2BAAY,CAAC,yBAAiB,CAAC,CAAC;YAK9D,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAClE,CAAC;QAED,YAAY,CAAC,QAA4B;YACvC,IAAI,CAAC,kBAAkB,GAAG,QAAQ,CAAC;QACrC,CAAC;QAED,kBAAkB,CAAC,QAAkC;YACnD,IAAI,CAAC,wBAAwB,GAAG,QAAQ,CAAC;QAC3C,CAAC;QAED,KAAK,CAAC,mBAAmB,CACvB,UAAkB,EAClB,QAAgB,4BAAoB;YAEpC,IAAI,IAAI,CAAC,iBAAiB,CAAC,SAAS,EAAE,EAAE,CAAC;gBACvC,OAAO,KAAK,CAAC;YACf,CAAC;YAED,OAAO,IAAI,CAAC,SAAS,CAAC,eAAe,EAAE,EAAE,UAAU,EAAE,EAAE,KAAK,CAAC,CAAC;QAChE,CAAC;QAEO,KAAK,CAAC,kBAAkB,CAAC,OAAyB;YACxD,IAAI,OAAO,CAAC,MAAM,KAAK,eAAe,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBAClE,MAAM,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,UAAU,CAAC;gBACnD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,UAAU,CAAC,CAAC;gBAE9E,IAAI,OAAO,EAAE,CAAC;oBACZ,OAAO,CAAC,aAAa,CAAC,UAAU,GAAG,UAAU,CAAC;gBAChD,CAAC;gBAED,OAAO,OAAO,CAAC;YACjB,CAAC;YAED,IAAI,OAAO,CAAC,MAAM,KAAK,kBAAkB,IAAI,IAAI,CAAC,wBAAwB,EAAE,CAAC;gBAC3E,MAAM,eAAe,GAAG,OAAO,CAAC,YAAY,CAAC,eAAe,CAAC;gBAC7D,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,eAAe,CAAC,CAAC;gBAEzF,IAAI,OAAO,EAAE,CAAC;oBACZ,OAAO,CAAC,aAAa,CAAC,UAAU,GAAG,eAAe,CAAC;gBACrD,CAAC;gBAED,OAAO,OAAO,CAAC;YACjB,CAAC;YAED,OAAO,KAAK,CAAC;QACf,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"RangeController.js","sourceRoot":"","sources":["../../src/capabilities/RangeController.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AA4BH,0CAgEC;AAzFD,uDAAoD;AAEpD,yCAAwE;AAuBxE,SAAgB,eAAe,CAAyC,IAAO;IAC7E,OAAO,KAAM,SAAQ,IAAI;QAKvB,8DAA8D;QAC9D,YAAY,GAAG,IAAW;YACxB,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;YANT,uBAAkB,GAA8B,IAAI,CAAC;YACrD,6BAAwB,GAAoC,IAAI,CAAC;YACjE,sBAAiB,GAAG,IAAI,2BAAY,CAAC,yBAAiB,CAAC,CAAC;YAK9D,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAClE,CAAC;QAED,YAAY,CAAC,QAA4B;YACvC,IAAI,CAAC,kBAAkB,GAAG,QAAQ,CAAC;QACrC,CAAC;QAED,kBAAkB,CAAC,QAAkC;YACnD,IAAI,CAAC,wBAAwB,GAAG,QAAQ,CAAC;QAC3C,CAAC;QAED,KAAK,CAAC,mBAAmB,CACvB,UAAkB,EAClB,aAAqB,EAAE,EACvB,QAAgB,4BAAoB;YAEpC,IAAI,IAAI,CAAC,iBAAiB,CAAC,SAAS,EAAE,EAAE,CAAC;gBACvC,OAAO,KAAK,CAAC;YACf,CAAC;YAED,OAAO,IAAI,CAAC,SAAS,CAAC,eAAe,EAAE,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;QAC5E,CAAC;QAEO,KAAK,CAAC,kBAAkB,CAAC,OAAyB;YACxD,IAAI,OAAO,CAAC,MAAM,KAAK,eAAe,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;gBAClE,MAAM,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,UAAU,CAAC;gBACnD,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;gBAC1C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;gBAE1F,IAAI,OAAO,EAAE,CAAC;oBACZ,OAAO,CAAC,aAAa,CAAC,UAAU,GAAG,UAAU,CAAC;gBAChD,CAAC;gBAED,OAAO,OAAO,CAAC;YACjB,CAAC;YAED,IAAI,OAAO,CAAC,MAAM,KAAK,kBAAkB,IAAI,IAAI,CAAC,wBAAwB,EAAE,CAAC;gBAC3E,MAAM,eAAe,GAAG,OAAO,CAAC,YAAY,CAAC,eAAe,CAAC;gBAC7D,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,IAAI,EAAE,CAAC;gBAC1C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,wBAAwB,CACjD,IAAI,CAAC,WAAW,EAAE,EAClB,eAAe,EACf,UAAU,CACX,CAAC;gBAEF,IAAI,OAAO,EAAE,CAAC;oBACZ,OAAO,CAAC,aAAa,CAAC,UAAU,GAAG,eAAe,CAAC;gBACrD,CAAC;gBAED,OAAO,OAAO,CAAC;YACjB,CAAC;YAED,OAAO,KAAK,CAAC;QACf,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -4,26 +4,35 @@
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.SettingController = SettingController;
7
+ const EventLimiter_1 = require("../core/EventLimiter");
8
+ const types_1 = require("../core/types");
7
9
  function SettingController(Base) {
8
10
  return class extends Base {
9
11
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
12
  constructor(...args) {
11
13
  super(...args);
12
14
  this.settingCallback = null;
15
+ this.settingEventLimiter = new EventLimiter_1.EventLimiter(types_1.EVENT_LIMIT_STATE);
13
16
  this.registerRequestHandler(this.handleSettingRequest.bind(this));
14
17
  }
15
18
  onSetting(callback) {
16
19
  this.settingCallback = callback;
17
20
  }
21
+ async sendSettingEvent(settingId, value, cause = types_1.PHYSICAL_INTERACTION) {
22
+ if (this.settingEventLimiter.isLimited()) {
23
+ return false;
24
+ }
25
+ return this.sendEvent('setSetting', { id: settingId, value }, cause);
26
+ }
18
27
  async handleSettingRequest(request) {
19
28
  if (request.action !== 'setSetting' || !this.settingCallback) {
20
29
  return false;
21
30
  }
22
- const setting = request.requestValue.setting;
31
+ const settingId = request.requestValue.id;
23
32
  const value = request.requestValue.value;
24
- const success = await this.settingCallback(this.getDeviceId(), setting, value);
33
+ const success = await this.settingCallback(this.getDeviceId(), settingId, value);
25
34
  if (success) {
26
- request.responseValue.setting = setting;
35
+ request.responseValue.id = settingId;
27
36
  request.responseValue.value = value;
28
37
  }
29
38
  return success;
@@ -1 +1 @@
1
- {"version":3,"file":"SettingController.js","sourceRoot":"","sources":["../../src/capabilities/SettingController.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAkBH,8CA+BC;AA/BD,SAAgB,iBAAiB,CAAyC,IAAO;IAC/E,OAAO,KAAM,SAAQ,IAAI;QAGvB,8DAA8D;QAC9D,YAAY,GAAG,IAAW;YACxB,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;YAJT,oBAAe,GAA2B,IAAI,CAAC;YAKrD,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACpE,CAAC;QAED,SAAS,CAAC,QAAyB;YACjC,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC;QAClC,CAAC;QAEO,KAAK,CAAC,oBAAoB,CAAC,OAAyB;YAC1D,IAAI,OAAO,CAAC,MAAM,KAAK,YAAY,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;gBAC7D,OAAO,KAAK,CAAC;YACf,CAAC;YAED,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC;YAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC;YACzC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;YAE/E,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,CAAC,aAAa,CAAC,OAAO,GAAG,OAAO,CAAC;gBACxC,OAAO,CAAC,aAAa,CAAC,KAAK,GAAG,KAAK,CAAC;YACtC,CAAC;YAED,OAAO,OAAO,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"SettingController.js","sourceRoot":"","sources":["../../src/capabilities/SettingController.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAqBH,8CA4CC;AA9DD,uDAAoD;AAEpD,yCAAwE;AAgBxE,SAAgB,iBAAiB,CAAyC,IAAO;IAC/E,OAAO,KAAM,SAAQ,IAAI;QAIvB,8DAA8D;QAC9D,YAAY,GAAG,IAAW;YACxB,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;YALT,oBAAe,GAA2B,IAAI,CAAC;YAC/C,wBAAmB,GAAiB,IAAI,2BAAY,CAAC,yBAAiB,CAAC,CAAC;YAK9E,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACpE,CAAC;QAED,SAAS,CAAC,QAAyB;YACjC,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC;QAClC,CAAC;QAED,KAAK,CAAC,gBAAgB,CACpB,SAAiB,EACjB,KAAc,EACd,QAAgB,4BAAoB;YAEpC,IAAI,IAAI,CAAC,mBAAmB,CAAC,SAAS,EAAE,EAAE,CAAC;gBACzC,OAAO,KAAK,CAAC;YACf,CAAC;YAED,OAAO,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,KAAK,CAAC,CAAC;QACvE,CAAC;QAEO,KAAK,CAAC,oBAAoB,CAAC,OAAyB;YAC1D,IAAI,OAAO,CAAC,MAAM,KAAK,YAAY,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;gBAC7D,OAAO,KAAK,CAAC;YACf,CAAC;YAED,MAAM,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;YAC1C,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC;YACzC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;YAEjF,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,CAAC,aAAa,CAAC,EAAE,GAAG,SAAS,CAAC;gBACrC,OAAO,CAAC,aAAa,CAAC,KAAK,GAAG,KAAK,CAAC;YACtC,CAAC;YAED,OAAO,OAAO,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -8,8 +8,12 @@ class MessageQueue {
8
8
  constructor() {
9
9
  this.queue = [];
10
10
  }
11
- push(message) {
12
- this.queue.push(message);
11
+ push(entry) {
12
+ this.queue.push(entry);
13
+ }
14
+ /** Put an entry back at the head, preserving delivery order when a send is deferred. */
15
+ pushFront(entry) {
16
+ this.queue.unshift(entry);
13
17
  }
14
18
  pop() {
15
19
  return this.queue.shift();
@@ -1 +1 @@
1
- {"version":3,"file":"MessageQueue.js","sourceRoot":"","sources":["../../src/core/MessageQueue.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,MAAa,YAAY;IAAzB;QACU,UAAK,GAAa,EAAE,CAAC;IAqB/B,CAAC;IAnBC,IAAI,CAAC,OAAe;QAClB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;IAED,GAAG;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IAC5B,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;IAClB,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC;IACjC,CAAC;CACF;AAtBD,oCAsBC"}
1
+ {"version":3,"file":"MessageQueue.js","sourceRoot":"","sources":["../../src/core/MessageQueue.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAIH,MAAa,YAAY;IAAzB;QACU,UAAK,GAAoB,EAAE,CAAC;IA0BtC,CAAC;IAxBC,IAAI,CAAC,KAAoB;QACvB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAED,wFAAwF;IACxF,SAAS,CAAC,KAAoB;QAC5B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;IAED,GAAG;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IAC5B,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;IAClB,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC;IACjC,CAAC;CACF;AA3BD,oCA2BC"}
@@ -5,6 +5,8 @@
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.Signature = void 0;
7
7
  const crypto_1 = require("crypto");
8
+ const PAYLOAD_MARKER = '"payload":';
9
+ const SIGNATURE_MARKER = ',"signature"';
8
10
  class Signature {
9
11
  constructor(appSecret) {
10
12
  this.appSecret = appSecret;
@@ -14,17 +16,21 @@ class Signature {
14
16
  */
15
17
  hmacBase64(message, key) {
16
18
  const hmac = (0, crypto_1.createHmac)('sha256', key);
17
- hmac.update(message);
19
+ hmac.update(message, 'utf8');
18
20
  return hmac.digest('base64');
19
21
  }
20
22
  /**
21
- * Extract payload from JSON message string
23
+ * Extract the payload from a message exactly as it was received.
24
+ *
25
+ * The bytes between `"payload":` and `,"signature"` are taken verbatim: the
26
+ * sender's key order and spacing are its own and re-encoding a parsed object
27
+ * would produce a different byte string and a failed verification.
22
28
  */
23
29
  extractPayload(messageStr) {
24
- const beginPayload = messageStr.indexOf('"payload":');
25
- const endPayload = messageStr.indexOf(',"signature"', beginPayload);
30
+ const beginPayload = messageStr.indexOf(PAYLOAD_MARKER);
31
+ const endPayload = messageStr.indexOf(SIGNATURE_MARKER, beginPayload);
26
32
  if (beginPayload > 0 && endPayload > 0) {
27
- return messageStr.substring(beginPayload + 10, endPayload);
33
+ return messageStr.substring(beginPayload + PAYLOAD_MARKER.length, endPayload);
28
34
  }
29
35
  return '';
30
36
  }
@@ -37,31 +43,58 @@ class Signature {
37
43
  return this.hmacBase64(payload, this.appSecret);
38
44
  }
39
45
  /**
40
- * Sign a message object
46
+ * Sign a message object in place.
41
47
  */
42
48
  sign(message) {
43
- const payloadStr = JSON.stringify(message.payload);
44
- const signature = this.calculateSignature(payloadStr);
49
+ const signature = this.calculateSignature(JSON.stringify(message.payload));
45
50
  if (!message.signature) {
46
51
  message.signature = { HMAC: '' };
47
52
  }
48
53
  message.signature.HMAC = signature;
49
54
  }
50
55
  /**
51
- * Validate message signature
56
+ * Sign a message and return the exact bytes to transmit.
57
+ *
58
+ * The payload is serialised once and spliced into the envelope, so the HMAC
59
+ * always covers the bytes that go on the wire. Serialising it a second time
60
+ * for transmission is what silently breaks verification on the peer.
61
+ */
62
+ serialize(message) {
63
+ const payloadStr = JSON.stringify(message.payload);
64
+ const hmac = this.calculateSignature(payloadStr);
65
+ message.signature = { HMAC: hmac };
66
+ return (`{"header":${JSON.stringify(message.header)},` +
67
+ `"payload":${payloadStr},` +
68
+ `"signature":{"HMAC":${JSON.stringify(hmac)}}}`);
69
+ }
70
+ /**
71
+ * Validate the signature of a message as it arrived on the wire.
72
+ *
73
+ * @param rawMessage - the received bytes, not a re-encoded object
52
74
  */
53
- validate(message) {
54
- // Timestamp messages don't have signatures
55
- if ('timestamp' in message) {
56
- return true;
75
+ validate(rawMessage) {
76
+ const payload = this.extractPayload(rawMessage);
77
+ if (!payload)
78
+ return false;
79
+ let claimedSignature = '';
80
+ try {
81
+ const parsed = JSON.parse(rawMessage);
82
+ claimedSignature = parsed?.signature?.HMAC ?? '';
57
83
  }
58
- if (!message.signature || !message.signature.HMAC) {
84
+ catch {
59
85
  return false;
60
86
  }
61
- const messageStr = JSON.stringify(message);
62
- const payload = this.extractPayload(messageStr);
63
- const calculatedSignature = this.calculateSignature(payload);
64
- return calculatedSignature === message.signature.HMAC;
87
+ if (!claimedSignature)
88
+ return false;
89
+ return Signature.constantTimeEquals(this.calculateSignature(payload), claimedSignature);
90
+ }
91
+ /** timingSafeEqual throws on unequal lengths; a length mismatch is never a match anyway. */
92
+ static constantTimeEquals(a, b) {
93
+ const bufferA = Buffer.from(a, 'utf8');
94
+ const bufferB = Buffer.from(b, 'utf8');
95
+ if (bufferA.length !== bufferB.length)
96
+ return false;
97
+ return (0, crypto_1.timingSafeEqual)(bufferA, bufferB);
65
98
  }
66
99
  }
67
100
  exports.Signature = Signature;
@@ -1 +1 @@
1
- {"version":3,"file":"Signature.js","sourceRoot":"","sources":["../../src/core/Signature.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,mCAAoC;AAGpC,MAAa,SAAS;IAGpB,YAAY,SAAiB;QAC3B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAED;;OAEG;IACK,UAAU,CAAC,OAAe,EAAE,GAAW;QAC7C,MAAM,IAAI,GAAG,IAAA,mBAAU,EAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACvC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IAED;;OAEG;IACK,cAAc,CAAC,UAAkB;QACvC,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QACtD,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;QAEpE,IAAI,YAAY,GAAG,CAAC,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YACvC,OAAO,UAAU,CAAC,SAAS,CAAC,YAAY,GAAG,EAAE,EAAE,UAAU,CAAC,CAAC;QAC7D,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;IAED;;OAEG;IACH,kBAAkB,CAAC,OAAe;QAChC,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IAClD,CAAC;IAED;;OAEG;IACH,IAAI,CAAC,OAAyB;QAC5B,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACnD,MAAM,SAAS,GAAG,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC;QAEtD,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YACvB,OAAO,CAAC,SAAS,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QACnC,CAAC;QACD,OAAO,CAAC,SAAS,CAAC,IAAI,GAAG,SAAS,CAAC;IACrC,CAAC;IAED;;OAEG;IACH,QAAQ,CAAC,OAAyB;QAChC,2CAA2C;QAC3C,IAAI,WAAW,IAAI,OAAO,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;YAClD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QAChD,MAAM,mBAAmB,GAAG,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAE7D,OAAO,mBAAmB,KAAK,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC;IACxD,CAAC;CACF;AAtED,8BAsEC"}
1
+ {"version":3,"file":"Signature.js","sourceRoot":"","sources":["../../src/core/Signature.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,mCAAqD;AAGrD,MAAM,cAAc,GAAG,YAAY,CAAC;AACpC,MAAM,gBAAgB,GAAG,cAAc,CAAC;AAExC,MAAa,SAAS;IAGpB,YAAY,SAAiB;QAC3B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;IAED;;OAEG;IACK,UAAU,CAAC,OAAe,EAAE,GAAW;QAC7C,MAAM,IAAI,GAAG,IAAA,mBAAU,EAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACvC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7B,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC;IAED;;;;;;OAMG;IACK,cAAc,CAAC,UAAkB;QACvC,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QACxD,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC;QAEtE,IAAI,YAAY,GAAG,CAAC,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YACvC,OAAO,UAAU,CAAC,SAAS,CAAC,YAAY,GAAG,cAAc,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAChF,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;IAED;;OAEG;IACH,kBAAkB,CAAC,OAAe;QAChC,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IAClD,CAAC;IAED;;OAEG;IACH,IAAI,CAAC,OAAyB;QAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;QAE3E,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;YACvB,OAAO,CAAC,SAAS,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QACnC,CAAC;QACD,OAAO,CAAC,SAAS,CAAC,IAAI,GAAG,SAAS,CAAC;IACrC,CAAC;IAED;;;;;;OAMG;IACH,SAAS,CAAC,OAAyB;QACjC,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACnD,MAAM,IAAI,GAAG,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC;QAEjD,OAAO,CAAC,SAAS,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QAEnC,OAAO,CACL,aAAa,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG;YAC9C,aAAa,UAAU,GAAG;YAC1B,uBAAuB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAChD,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,QAAQ,CAAC,UAAkB;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QAChD,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QAE3B,IAAI,gBAAgB,GAAG,EAAE,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAqB,CAAC;YAC1D,gBAAgB,GAAG,MAAM,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC;QACnD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,CAAC,gBAAgB;YAAE,OAAO,KAAK,CAAC;QAEpC,OAAO,SAAS,CAAC,kBAAkB,CAAC,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAC,CAAC;IAC1F,CAAC;IAED,4FAA4F;IACpF,MAAM,CAAC,kBAAkB,CAAC,CAAS,EAAE,CAAS;QACpD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACvC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QAEvC,IAAI,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAEpD,OAAO,IAAA,wBAAe,EAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;CACF;AAzGD,8BAyGC"}