@riddix/hamh 2.1.0-alpha.566 → 2.1.0-alpha.567

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.
@@ -177925,18 +177925,6 @@ var RvcOperationalStateServerBase = class extends RvcOperationalStateServer {
177925
177925
  this.state.operationalError = { errorStateId: ErrorState.NoError };
177926
177926
  await super.initialize();
177927
177927
  const homeAssistant = await this.agent.load(HomeAssistantEntityBehavior);
177928
- try {
177929
- const events = this.events;
177930
- events.operationalError$Changed?.on(() => {
177931
- logger195.info(
177932
- `#287 diag[3]: operationalError$Changed fired for ${homeAssistant.entityId}`
177933
- );
177934
- });
177935
- } catch (e) {
177936
- logger195.warn(
177937
- `#287 diag[3]: failed to attach operationalError$Changed listener: ${e instanceof Error ? e.message : String(e)}`
177938
- );
177939
- }
177940
177928
  this.update(homeAssistant.entity);
177941
177929
  this.reactTo(homeAssistant.onChange, this.update);
177942
177930
  }
@@ -177949,9 +177937,6 @@ var RvcOperationalStateServerBase = class extends RvcOperationalStateServer {
177949
177937
  this.agent
177950
177938
  );
177951
177939
  const previousState = this.state.operationalState;
177952
- logger195.info(
177953
- `#287 diag[2]: update() reactor invoked for ${entity.entity_id} \u2014 prevState=${previousState} newState=${newState} nonceBefore=${this.keepaliveNonce}`
177954
- );
177955
177940
  this.keepaliveNonce = !this.keepaliveNonce;
177956
177941
  const errorStateId = newState === OperationalState4.Error ? ErrorState.Stuck : ErrorState.NoError;
177957
177942
  const operationalError = { errorStateId };
@@ -181186,20 +181171,32 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
181186
181171
  lastState;
181187
181172
  pendingMappedChange = false;
181188
181173
  flushUpdate;
181189
- /** Periodic keepalive timer that re-pushes the last known state so
181190
- * Apple Home (iOS) doesn't show "Updating..." due to stale
181191
- * subscription data when the vacuum is idle and no HA events fire. */
181174
+ /** Periodic keepalive timer that writes directly to the
181175
+ * RvcOperationalState cluster in a fresh transaction so Apple Home
181176
+ * (iOS) doesn't show "Updating..." due to stale subscription data
181177
+ * when the vacuum is idle and no HA events fire.
181178
+ *
181179
+ * Previous approaches that pushed state through
181180
+ * HomeAssistantEntityBehavior failed because the reactor writes
181181
+ * (RvcOperationalStateServer.update()) run inside the postCommit
181182
+ * phase of the HAEntityBehavior transaction — those writes are
181183
+ * buffered but never committed, so no attrsChanged event reaches
181184
+ * the ServerSubscription.
181185
+ *
181186
+ * Writing directly via endpoint.act() creates an independent
181187
+ * transaction that goes through the full commit lifecycle. */
181192
181188
  keepaliveTimer;
181189
+ /** Alternating nonce so operationalError is structurally different
181190
+ * each keepalive tick. matter.js's Datasource uses isDeepEqual;
181191
+ * toggling errorStateDetails between absent and "" changes the
181192
+ * property count so the struct is never deep-equal. */
181193
+ keepaliveNonce = false;
181193
181194
  constructor(type, entityId, customName, mappedEntityIds) {
181194
181195
  super(type, entityId, customName, mappedEntityIds);
181195
181196
  this.flushUpdate = debounce6(this.flushPendingUpdate.bind(this), 50);
181196
181197
  this.keepaliveTimer = setInterval(() => {
181197
181198
  if (this.lastState) {
181198
- logger203.info(
181199
- `#287 diag[1]: keepalive tick for ${this.entityId} \u2014 scheduling flushUpdate`
181200
- );
181201
- this.pendingMappedChange = true;
181202
- this.flushUpdate(this.lastState);
181199
+ this.pushKeepalive();
181203
181200
  }
181204
181201
  }, 55e3);
181205
181202
  }
@@ -181211,6 +181208,35 @@ var ServerModeVacuumEndpoint = class _ServerModeVacuumEndpoint extends EntityEnd
181211
181208
  this.flushUpdate.clear();
181212
181209
  await super.delete();
181213
181210
  }
181211
+ /**
181212
+ * Write directly to the RvcOperationalState cluster in a fresh
181213
+ * transaction. The nonce toggles errorStateDetails between absent
181214
+ * and "" so the struct is structurally different each call,
181215
+ * guaranteeing matter.js emits attrsChanged → subscription report.
181216
+ */
181217
+ async pushKeepalive() {
181218
+ try {
181219
+ await this.act("vacuum-keepalive", async (agent) => {
181220
+ const opState = agent.get(RvcOperationalStateServer);
181221
+ this.keepaliveNonce = !this.keepaliveNonce;
181222
+ const errorStateId = opState.state.operationalError.errorStateId;
181223
+ const operationalError = { errorStateId };
181224
+ if (this.keepaliveNonce) {
181225
+ operationalError.errorStateDetails = "";
181226
+ }
181227
+ opState.state.operationalError = operationalError;
181228
+ });
181229
+ } catch (e) {
181230
+ if (e instanceof TransactionDestroyedError || e instanceof DestroyedDependencyError) {
181231
+ return;
181232
+ }
181233
+ const msg = e instanceof Error ? e.message : String(e);
181234
+ if (msg.includes("Endpoint storage inaccessible")) {
181235
+ return;
181236
+ }
181237
+ logger203.debug(`Keepalive failed for ${this.entityId}: ${msg}`);
181238
+ }
181239
+ }
181214
181240
  async updateStates(states) {
181215
181241
  const state = states[this.entityId] ?? {};
181216
181242
  const mappedChanged = this.hasMappedEntityChanged(states);