@matter/node 0.16.6 → 0.16.8-alpha.0-20260123-dff2cae52

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 (31) hide show
  1. package/dist/cjs/behavior/system/software-update/OtaAnnouncements.d.ts +5 -1
  2. package/dist/cjs/behavior/system/software-update/OtaAnnouncements.d.ts.map +1 -1
  3. package/dist/cjs/behavior/system/software-update/OtaAnnouncements.js +32 -8
  4. package/dist/cjs/behavior/system/software-update/OtaAnnouncements.js.map +1 -1
  5. package/dist/cjs/behavior/system/software-update/SoftwareUpdateManager.d.ts +2 -4
  6. package/dist/cjs/behavior/system/software-update/SoftwareUpdateManager.d.ts.map +1 -1
  7. package/dist/cjs/behavior/system/software-update/SoftwareUpdateManager.js +18 -24
  8. package/dist/cjs/behavior/system/software-update/SoftwareUpdateManager.js.map +1 -1
  9. package/dist/cjs/behaviors/thermostat/ThermostatServer.d.ts.map +1 -1
  10. package/dist/cjs/behaviors/thermostat/ThermostatServer.js +12 -8
  11. package/dist/cjs/behaviors/thermostat/ThermostatServer.js.map +1 -1
  12. package/dist/cjs/node/integration/ProtocolService.js +6 -4
  13. package/dist/cjs/node/integration/ProtocolService.js.map +1 -1
  14. package/dist/esm/behavior/system/software-update/OtaAnnouncements.d.ts +5 -1
  15. package/dist/esm/behavior/system/software-update/OtaAnnouncements.d.ts.map +1 -1
  16. package/dist/esm/behavior/system/software-update/OtaAnnouncements.js +32 -8
  17. package/dist/esm/behavior/system/software-update/OtaAnnouncements.js.map +1 -1
  18. package/dist/esm/behavior/system/software-update/SoftwareUpdateManager.d.ts +2 -4
  19. package/dist/esm/behavior/system/software-update/SoftwareUpdateManager.d.ts.map +1 -1
  20. package/dist/esm/behavior/system/software-update/SoftwareUpdateManager.js +18 -24
  21. package/dist/esm/behavior/system/software-update/SoftwareUpdateManager.js.map +1 -1
  22. package/dist/esm/behaviors/thermostat/ThermostatServer.d.ts.map +1 -1
  23. package/dist/esm/behaviors/thermostat/ThermostatServer.js +12 -8
  24. package/dist/esm/behaviors/thermostat/ThermostatServer.js.map +1 -1
  25. package/dist/esm/node/integration/ProtocolService.js +6 -4
  26. package/dist/esm/node/integration/ProtocolService.js.map +1 -1
  27. package/package.json +7 -7
  28. package/src/behavior/system/software-update/OtaAnnouncements.ts +36 -9
  29. package/src/behavior/system/software-update/SoftwareUpdateManager.ts +23 -30
  30. package/src/behaviors/thermostat/ThermostatServer.ts +15 -9
  31. package/src/node/integration/ProtocolService.ts +6 -4
@@ -21,33 +21,51 @@ const logger = new Logger("OTAAnnouncements");
21
21
 
22
22
  export class OtaAnnouncements {
23
23
  #announcementQueue = new Array<PeerAddress>();
24
- #announcementTimer: Timer;
24
+ #announcementTimer?: Timer;
25
25
  #announcementDelayTimer: Timer;
26
26
  #ownNodeId: NodeId;
27
27
  #ownFabricIndex: FabricIndex;
28
28
  #ownVendorId: VendorId;
29
29
  #node: ServerNode;
30
30
  #otaProviderEndpoint: EndpointNumber;
31
- #announcementInterval: Duration;
31
+ #announcementInterval?: Duration;
32
32
  #currentAnnouncementPromise?: Promise<void>;
33
33
 
34
- constructor(endpoint: Endpoint, ownFabric: Fabric, interval: Duration) {
34
+ constructor(endpoint: Endpoint, ownFabric: Fabric) {
35
35
  this.#node = Node.forEndpoint(endpoint) as ServerNode;
36
36
  this.#ownNodeId = ownFabric.rootNodeId;
37
37
  this.#ownFabricIndex = ownFabric.fabricIndex;
38
38
  this.#ownVendorId = ownFabric.rootVendorId;
39
39
  this.#otaProviderEndpoint = endpoint.number;
40
+
41
+ // When announcing to multiple nodes, min 1s pause between, let's do some more, but no need for random
42
+ this.#announcementDelayTimer = Time.getTimer("OTA Node announcement delay", Seconds(10), () =>
43
+ this.#processQueueEntry(),
44
+ );
45
+ }
46
+
47
+ /**
48
+ * Set the interval to a time value or undefined to disable announcements
49
+ */
50
+ set interval(interval: Duration | undefined) {
51
+ if (interval === undefined) {
52
+ this.#announcementInterval = undefined;
53
+ this.#announcementTimer?.stop();
54
+ this.#announcementTimer = undefined;
55
+ return;
56
+ }
57
+
40
58
  if (interval < Hours(24)) {
41
59
  logger.warn("Announcements interval is too short, consider increasing it to at least 24 hours.");
42
60
  interval = Hours(24);
43
61
  }
62
+ if (interval === this.#announcementInterval) {
63
+ return;
64
+ }
44
65
  // The daily start time should have a random jitter of + >= 60s, so just increase the time randomly a bit
45
66
  this.#announcementInterval = Millis(interval + Seconds(Math.floor(Math.random() * 120) + 60));
46
67
 
47
- // When announcing to multiple nodes, min 1s pause between, let's do some more, but no need for random
48
- this.#announcementDelayTimer = Time.getTimer("OTA Node announcement delay", Seconds(10), () =>
49
- this.#processQueueEntry(),
50
- );
68
+ this.#announcementTimer?.stop();
51
69
 
52
70
  const initialDelay = Millis(Seconds(Math.floor(Math.random() * 300)) + Minutes(10));
53
71
  logger.debug(`Initial OTA announcement delay is ${Duration.format(initialDelay)}`);
@@ -57,7 +75,10 @@ export class OtaAnnouncements {
57
75
  }
58
76
 
59
77
  #initializeAnnouncements() {
60
- this.#announcementTimer.stop();
78
+ if (this.#announcementInterval === undefined) {
79
+ return;
80
+ }
81
+ this.#announcementTimer?.stop();
61
82
  this.#announcementTimer = Time.getTimer("OTA All Nodes announcement timer", this.#announcementInterval, () =>
62
83
  this.#queueAllPeers(),
63
84
  );
@@ -65,6 +86,9 @@ export class OtaAnnouncements {
65
86
  }
66
87
 
67
88
  #queueAllPeers() {
89
+ if (this.#announcementTimer === undefined) {
90
+ return;
91
+ }
68
92
  this.#announcementTimer.stop();
69
93
  for (const peer of this.#node.peers) {
70
94
  if (!peer.lifecycle.isCommissioned || !peer.lifecycle.isOnline) {
@@ -81,6 +105,9 @@ export class OtaAnnouncements {
81
105
 
82
106
  // Queue a peer because processing is delayed and better to check /get peer anew when we process it
83
107
  #queuePeer(peerAddress: PeerAddress) {
108
+ if (this.#announcementTimer === undefined) {
109
+ return;
110
+ }
84
111
  this.#announcementQueue.push(peerAddress);
85
112
  logger.debug("Queued peer", peerAddress, "for OTA announcement;", this.#announcementQueue.length, "queued");
86
113
  if (this.#announcementQueue.length > 0 && !this.#announcementTimer.isRunning) {
@@ -236,7 +263,7 @@ export class OtaAnnouncements {
236
263
  }
237
264
 
238
265
  async close() {
239
- this.#announcementTimer.stop();
266
+ this.#announcementTimer?.stop();
240
267
  this.#announcementDelayTimer.stop();
241
268
  if (this.#currentAnnouncementPromise !== undefined) {
242
269
  await this.#currentAnnouncementPromise;
@@ -144,7 +144,8 @@ export class SoftwareUpdateManager extends Behavior {
144
144
  await this.#nodeOnline();
145
145
  }
146
146
 
147
- this.reactTo(this.events.announceAsDefaultProvider$Changed, this.#announceAsDefaultProviderChanged);
147
+ this.reactTo(this.events.announceAsDefaultProvider$Changed, this.#updateAnnouncementSettings);
148
+ this.reactTo(this.events.announcementInterval$Changed, this.#updateAnnouncementSettings);
148
149
  }
149
150
 
150
151
  async #nodeOnline() {
@@ -153,7 +154,19 @@ export class SoftwareUpdateManager extends Behavior {
153
154
  this.internal.announcements = undefined;
154
155
  }
155
156
 
156
- this.#announceAsDefaultProviderChanged(this.state.announceAsDefaultProvider);
157
+ // For now let's just provide update functionality when we are the fabric owner
158
+ // In theory we could also claim that for any other fabric but could conflict with the main controller of
159
+ // the fabric that then also claims being "the one provider".
160
+ const fabricAuthority = this.env.get(FabricAuthority);
161
+ const ownFabric = fabricAuthority.fabrics[0];
162
+ if (!ownFabric) {
163
+ // Can only happen if the SoftwareUpdateManager is used without any commissioned nodes
164
+ logger.info(`No commissioned peers yet, cannot check for OTA updates. Wait for Fabric being added.`);
165
+ fabricAuthority.fabricAdded.once(this.callback(this.#nodeOnline));
166
+ return;
167
+ }
168
+ this.internal.announcements = new OtaAnnouncements(this.endpoint, ownFabric);
169
+ this.#updateAnnouncementSettings();
157
170
 
158
171
  // Randomly force the first update check 5-10 minutes after startup
159
172
  const delay = Millis(Seconds(Math.floor(Math.random() * 300)) + Minutes(5));
@@ -166,33 +179,14 @@ export class SoftwareUpdateManager extends Behavior {
166
179
  ).start();
167
180
  }
168
181
 
169
- #announceAsDefaultProviderChanged(enabled: boolean) {
170
- if (!!this.internal.announcements === enabled) {
182
+ #updateAnnouncementSettings() {
183
+ if (this.internal.announcements === undefined) {
171
184
  return;
172
185
  }
173
-
174
- if (enabled) {
175
- // For now let's just provide update functionality when we are the fabric owner
176
- // In theory we could also claim that for any other fabric but could conflict with the main controller of
177
- // the fabric that then also claims being "the one provider".
178
- const fabricAuthority = this.env.get(FabricAuthority);
179
- const ownFabric = fabricAuthority.fabrics[0];
180
- if (!ownFabric) {
181
- // Can only happen if the SoftwareUpdateManager is used without any commissioned nodes
182
- logger.info(`No commissioned peers yet, cannot check for OTA updates. Wait for Fabric being added.`);
183
- fabricAuthority.fabricAdded.once(this.callback(this.#nodeOnline));
184
- return;
185
- }
186
-
187
- this.internal.announcements = new OtaAnnouncements(
188
- this.endpoint,
189
- ownFabric,
190
- this.state.announcementInterval,
191
- );
186
+ if (this.state.announceAsDefaultProvider) {
187
+ this.internal.announcements.interval = this.state.announcementInterval;
192
188
  } else {
193
- const announcements = this.internal.announcements!;
194
- this.internal.announcements = undefined;
195
- this.env.runtime.add(announcements.close());
189
+ this.internal.announcements.interval = undefined;
196
190
  }
197
191
  }
198
192
 
@@ -851,10 +845,7 @@ export namespace SoftwareUpdateManager {
851
845
  /** Default Update check Interval */
852
846
  updateCheckInterval = Hours(24);
853
847
 
854
- /**
855
- * Announce this controller as Update provider to all nodes
856
- * Defaults to false because we saw strange effects in the field
857
- */
848
+ /** Announce this controller as Update provider to all nodes */
858
849
  announceAsDefaultProvider = false;
859
850
 
860
851
  /** Interval to Announces this controller as Update provider. Must not be lower than 24h! */
@@ -890,5 +881,7 @@ export namespace SoftwareUpdateManager {
890
881
  updateDone = Observable<[peer: PeerAddress]>();
891
882
 
892
883
  announceAsDefaultProvider$Changed = Observable<[announceAsDefaultProvider: boolean]>();
884
+
885
+ announcementInterval$Changed = Observable<[announcementInterval: Duration]>();
893
886
  }
894
887
  }
@@ -335,7 +335,9 @@ export class ThermostatBaseServer extends ThermostatBehaviorLogicBase {
335
335
 
336
336
  // TODO Add support for correct Time handling, leave disabled for now
337
337
  if (this.state.setpointHoldExpiryTimestamp === undefined) {
338
- //this.state.setpointHoldExpiryTimestamp = null;
338
+ //this.agent.asLocalActor(() => {
339
+ // this.state.setpointHoldExpiryTimestamp = null;
340
+ //});
339
341
  } else {
340
342
  logger.warn(
341
343
  "Handling for setpointHoldExpiryTimestamp is not yet implemented. To use this attribute you need to install the needed logic yourself",
@@ -405,7 +407,9 @@ export class ThermostatBaseServer extends ThermostatBehaviorLogicBase {
405
407
  }
406
408
  break;
407
409
  }
408
- this.state.thermostatRunningMode = newState;
410
+ this.agent.asLocalActor(() => {
411
+ this.state.thermostatRunningMode = newState;
412
+ });
409
413
  }
410
414
 
411
415
  /**
@@ -1000,13 +1004,15 @@ export class ThermostatBaseServer extends ThermostatBehaviorLogicBase {
1000
1004
 
1001
1005
  #handleSystemModeChange(newMode: Thermostat.SystemMode) {
1002
1006
  if (this.state.thermostatRunningMode !== undefined && newMode !== Thermostat.SystemMode.Auto) {
1003
- if (newMode === Thermostat.SystemMode.Off) {
1004
- this.state.thermostatRunningMode = Thermostat.ThermostatRunningMode.Off;
1005
- } else if (newMode === Thermostat.SystemMode.Heat) {
1006
- this.state.thermostatRunningMode = Thermostat.ThermostatRunningMode.Heat;
1007
- } else if (newMode === Thermostat.SystemMode.Cool) {
1008
- this.state.thermostatRunningMode = Thermostat.ThermostatRunningMode.Cool;
1009
- }
1007
+ this.agent.asLocalActor(() => {
1008
+ if (newMode === Thermostat.SystemMode.Off) {
1009
+ this.state.thermostatRunningMode = Thermostat.ThermostatRunningMode.Off;
1010
+ } else if (newMode === Thermostat.SystemMode.Heat) {
1011
+ this.state.thermostatRunningMode = Thermostat.ThermostatRunningMode.Heat;
1012
+ } else if (newMode === Thermostat.SystemMode.Cool) {
1013
+ this.state.thermostatRunningMode = Thermostat.ThermostatRunningMode.Cool;
1014
+ }
1015
+ });
1010
1016
  }
1011
1017
  }
1012
1018
 
@@ -610,8 +610,9 @@ function invokeCommand(
610
610
  result = Promise.resolve(result)
611
611
  .then(result => {
612
612
  if (isObject(result)) {
613
- logger.debug(
614
- "Invoke result",
613
+ logger.info(
614
+ "Invoke",
615
+ Mark.OUTBOUND,
615
616
  Diagnostic.strong(`${path.toString()}.${command.name}`),
616
617
  session.transaction!.via,
617
618
  Diagnostic.dict(result),
@@ -622,8 +623,9 @@ function invokeCommand(
622
623
  .finally(() => activity?.[Symbol.dispose]());
623
624
  } else {
624
625
  if (isObject(result)) {
625
- logger.debug(
626
- "Invoke result",
626
+ logger.info(
627
+ "Invoke",
628
+ Mark.OUTBOUND,
627
629
  Diagnostic.strong(`${path.toString()}.${command.name}`),
628
630
  session.transaction.via,
629
631
  Diagnostic.dict(result),