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

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
+ /** Monotonic counter that ensures operationalError is structurally
181190
+ * different on every keepalive tick. matter.js's Datasource uses
181191
+ * isDeepEqual; a unique errorStateLabel value guarantees the struct
181192
+ * is never deep-equal to its predecessor. */
181193
+ keepaliveCounter = 0;
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,37 @@ 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. Each call produces a unique errorStateLabel value
181214
+ * so the struct is never deep-equal to its predecessor,
181215
+ * guaranteeing matter.js emits attrsChanged → subscription report.
181216
+ */
181217
+ async pushKeepalive() {
181218
+ try {
181219
+ this.keepaliveCounter++;
181220
+ const counter = this.keepaliveCounter;
181221
+ logger203.info(`Keepalive #${counter} for ${this.entityId}`);
181222
+ await this.act("vacuum-keepalive", async (agent) => {
181223
+ const opState = agent.get(RvcOperationalStateServer);
181224
+ const errorStateId = opState.state.operationalError.errorStateId;
181225
+ opState.state.operationalError = {
181226
+ errorStateId,
181227
+ errorStateLabel: `k${counter}`
181228
+ };
181229
+ });
181230
+ logger203.info(`Keepalive #${counter} committed for ${this.entityId}`);
181231
+ } catch (e) {
181232
+ if (e instanceof TransactionDestroyedError || e instanceof DestroyedDependencyError) {
181233
+ return;
181234
+ }
181235
+ const msg = e instanceof Error ? e.message : String(e);
181236
+ if (msg.includes("Endpoint storage inaccessible")) {
181237
+ return;
181238
+ }
181239
+ logger203.warn(`Keepalive failed for ${this.entityId}: ${msg}`);
181240
+ }
181241
+ }
181214
181242
  async updateStates(states) {
181215
181243
  const state = states[this.entityId] ?? {};
181216
181244
  const mappedChanged = this.hasMappedEntityChanged(states);