homebridge-tydom 0.32.1 → 0.32.3

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.
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { n as PLUGIN_NAME, t as PLATFORM_NAME } from "./env-B2-Dw8C2.mjs";
2
2
  //#region src/index.ts
3
3
  process.setSourceMapsEnabled(true);
4
- const { default: TydomPlatform } = await import("./platform-BssMTVVc.mjs");
4
+ const { default: TydomPlatform } = await import("./platform-d-jxZ1Nu.mjs");
5
5
  var src_default = (homebridge) => {
6
6
  homebridge.registerPlatform(PLUGIN_NAME, PLATFORM_NAME, TydomPlatform);
7
7
  };
@@ -3021,6 +3021,15 @@ const parseDiscoveryResponse = (uri, schema, payload) => {
3021
3021
  * device is not the shape the accessory was built for — which is a bug in the
3022
3022
  * signature table, not a value HomeKit should be handed.
3023
3023
  */
3024
+ /**
3025
+ * The value of a property the hardware may simply not carry.
3026
+ *
3027
+ * `getTydomDataPropValue` asserts, which is right for a property the device
3028
+ * type guarantees. Delta Dore's HVAC range guarantees less than it looks: some
3029
+ * endpoints expose a thermic level and no numbers at all, no `setpoint` and no
3030
+ * `temperature`, and asserting on those turned every read into an error.
3031
+ */
3032
+ const findTydomDataPropValue = (data, name) => data.find((prop) => prop.name === name)?.value;
3024
3033
  const getTydomDataPropValue = (data, name) => {
3025
3034
  const item = data.find((prop) => prop.name === name);
3026
3035
  assert(item, `Missing property with name="${name}" in endpoint data`);
@@ -4039,11 +4048,12 @@ var GarageDoorAccessory = class extends BaseAccessory {
4039
4048
  this.#computedPosition = doorState === CurrentDoorState.OPEN ? 100 : 0;
4040
4049
  }
4041
4050
  #setDoorState(doorState) {
4042
- const { CurrentDoorState } = this.platform.Characteristic;
4051
+ const { CurrentDoorState, TargetDoorState } = this.platform.Characteristic;
4043
4052
  debug(`assignCurrentDoorState=${styleString(this.#label(doorState))}`);
4044
4053
  this.#currentDoorState = doorState;
4045
4054
  this.#settleTarget(doorState);
4046
4055
  this.#service.updateCharacteristic(CurrentDoorState, doorState);
4056
+ if (doorState === CurrentDoorState.OPEN || doorState === CurrentDoorState.CLOSED) this.#service.updateCharacteristic(TargetDoorState, this.#targetDoorState);
4047
4057
  }
4048
4058
  /** A door at rest, open or closed, was headed exactly there. */
4049
4059
  #settleTarget(doorState) {
@@ -4381,19 +4391,28 @@ const isZoneArmed = (alarmData, zoneProp) => {
4381
4391
  if (alarmMode === "ON") return true;
4382
4392
  return alarmData.find((prop) => prop.name === zoneProp)?.value === "ON";
4383
4393
  };
4394
+ /**
4395
+ * The arming mode a data snapshot describes, ignoring whether the siren is on.
4396
+ *
4397
+ * This is what `SecuritySystemTargetState` asks for, and the reason it cannot
4398
+ * share `getStateForAlarmData`: the target has no `ALARM_TRIGGERED`, so the
4399
+ * return type excludes it and HAP can never be handed a value it would reject.
4400
+ */
4401
+ const getTargetStateForAlarmData = (alarmData, aliases, settings) => {
4402
+ const alarmMode = alarmData.find((prop) => prop.name === "alarmMode")?.value;
4403
+ if (alarmMode === "ON") return ALARM_STATE.AWAY_ARM;
4404
+ if (typeof alarmMode === "string" && ["ZONE", "PART"].includes(alarmMode)) return getStateForActiveZones(getActiveZones(alarmData, settings), aliases);
4405
+ return ALARM_STATE.DISARMED;
4406
+ };
4384
4407
  /** The HomeKit state for a full alarm data snapshot. */
4385
4408
  const getStateForAlarmData = (alarmData, aliases, settings) => {
4386
- const find = (name) => alarmData.find((prop) => prop.name === name)?.value;
4387
- const alarmState = find("alarmState");
4388
- const alarmMode = find("alarmMode");
4409
+ const alarmState = alarmData.find((prop) => prop.name === "alarmState")?.value;
4389
4410
  if (typeof alarmState === "string" && [
4390
4411
  "DELAYED",
4391
4412
  "ON",
4392
4413
  "QUIET"
4393
4414
  ].includes(alarmState)) return ALARM_STATE.ALARM_TRIGGERED;
4394
- if (alarmMode === "ON") return ALARM_STATE.AWAY_ARM;
4395
- if (typeof alarmMode === "string" && ["ZONE", "PART"].includes(alarmMode)) return getStateForActiveZones(getActiveZones(alarmData, settings), aliases);
4396
- return ALARM_STATE.DISARMED;
4415
+ return getTargetStateForAlarmData(alarmData, aliases, settings);
4397
4416
  };
4398
4417
  //#endregion
4399
4418
  //#region src/accessories/security-system-accessory.ts
@@ -4449,7 +4468,7 @@ var SecuritySystemAccessory = class extends BaseAccessory {
4449
4468
  });
4450
4469
  service.getCharacteristic(SecuritySystemTargetState).onGet(async () => {
4451
4470
  debugGet(SecuritySystemTargetState, service);
4452
- const nextValue = await this.#currentState();
4471
+ const nextValue = await this.#targetState();
4453
4472
  debugGetResult(SecuritySystemTargetState, service, nextValue);
4454
4473
  return nextValue;
4455
4474
  }).onSet(async (value) => {
@@ -4475,6 +4494,17 @@ var SecuritySystemAccessory = class extends BaseAccessory {
4475
4494
  const data = await this.#read();
4476
4495
  return getStateForAlarmData(data, this.#settings.aliases ?? {}, this.#settings);
4477
4496
  }
4497
+ /**
4498
+ * The mode the panel is set to, which is what the target characteristic asks.
4499
+ *
4500
+ * Deliberately not `#currentState`: that one answers `ALARM_TRIGGERED` while
4501
+ * the siren is going, and the target has no such value, so HAP rejected it.
4502
+ * A ringing alarm says nothing about what the panel was armed to anyway.
4503
+ */
4504
+ async #targetState() {
4505
+ const data = await this.#read();
4506
+ return getTargetStateForAlarmData(data, this.#settings.aliases ?? {}, this.#settings);
4507
+ }
4478
4508
  async #readZoneLabels() {
4479
4509
  if (this.#legacy) {
4480
4510
  this.platform.log.warn(`Setting up legacy zones`);
@@ -4562,11 +4592,15 @@ var SecuritySystemAccessory = class extends BaseAccessory {
4562
4592
  return;
4563
4593
  }
4564
4594
  const aliases = this.#settings.aliases ?? {};
4565
- const targetZones = target === SecuritySystemTargetState.STAY_ARM ? aliases.stay : aliases.night;
4566
- if (Array.isArray(targetZones) && targetZones.length > 0) {
4567
- await this.#setZones(targetZones, "ON");
4568
- if (this.#service) debugSetResult(SecuritySystemTargetState, this.#service, target, "ON");
4595
+ const isStay = target === SecuritySystemTargetState.STAY_ARM;
4596
+ const alias = isStay ? "stay" : "night";
4597
+ const targetZones = aliases[alias];
4598
+ if (!Array.isArray(targetZones) || targetZones.length === 0) {
4599
+ this.platform.log.warn(`Cannot arm ${this.name} to ${isStay ? "Home" : "Night"}: no zones are configured for that mode, so nothing was sent and the Home app will report the change as failed. Add {"settings": {"${this.deviceId}": {"aliases": {"${alias}": [1]}}}} listing the zones it should arm, numbered as they are on the panel's keypad.`);
4600
+ return;
4569
4601
  }
4602
+ await this.#setZones(targetZones, "ON");
4603
+ if (this.#service) debugSetResult(SecuritySystemTargetState, this.#service, target, "ON");
4570
4604
  }
4571
4605
  async #setZones(zoneIds, value) {
4572
4606
  if (!this.#legacy) {
@@ -4583,15 +4617,12 @@ var SecuritySystemAccessory = class extends BaseAccessory {
4583
4617
  });
4584
4618
  }
4585
4619
  #handleData(updates) {
4586
- const { SecuritySystemCurrentState, ContactSensorState, On } = this.platform.Characteristic;
4620
+ const { ContactSensorState, On } = this.platform.Characteristic;
4587
4621
  const aliases = this.#settings.aliases ?? {};
4588
4622
  for (const { name, value } of updates) {
4589
4623
  if (name === "alarmMode") {
4590
4624
  const nextValue = value === "OFF" ? ALARM_STATE.DISARMED : value === "ON" ? ALARM_STATE.AWAY_ARM : value === "PART" || value === "ZONE" ? getStateForActiveZones(getActiveZones(updates, this.#settings), aliases) : void 0;
4591
- if (nextValue !== void 0 && this.#service) {
4592
- debugSetUpdate(SecuritySystemCurrentState, this.#service, nextValue);
4593
- this.#service.updateCharacteristic(SecuritySystemCurrentState, nextValue);
4594
- }
4625
+ if (nextValue !== void 0) this.#setState(nextValue);
4595
4626
  if (value === "OFF") this.#syncZoneSwitches([]);
4596
4627
  else if (value === "ON") this.#syncZoneSwitches(null);
4597
4628
  continue;
@@ -4669,11 +4700,22 @@ var SecuritySystemAccessory = class extends BaseAccessory {
4669
4700
  service.updateCharacteristic(On, nextValue);
4670
4701
  }
4671
4702
  }
4703
+ /**
4704
+ * Settle both arming characteristics from an authoritative mode.
4705
+ *
4706
+ * The target moves with the current state, so that arming from the panel's
4707
+ * own keypad does not leave HomeKit showing the old mode — a stale target is
4708
+ * what makes "arm away" in the Home app look like it did nothing. The
4709
+ * parameter type excludes `ALARM_TRIGGERED`, so the target can never be
4710
+ * handed a value it would reject.
4711
+ */
4672
4712
  #setState(state) {
4673
4713
  if (!this.#service) return;
4674
- const { SecuritySystemCurrentState } = this.platform.Characteristic;
4714
+ const { SecuritySystemCurrentState, SecuritySystemTargetState } = this.platform.Characteristic;
4675
4715
  debugSetUpdate(SecuritySystemCurrentState, this.#service, state);
4676
4716
  this.#service.updateCharacteristic(SecuritySystemCurrentState, state);
4717
+ debugSetUpdate(SecuritySystemTargetState, this.#service, state);
4718
+ this.#service.updateCharacteristic(SecuritySystemTargetState, state);
4677
4719
  }
4678
4720
  };
4679
4721
  const createSecuritySystemAccessory = (deps) => {
@@ -4781,6 +4823,14 @@ const EXPOSED_THERMIC_LEVELS = /* @__PURE__ */ new Set([
4781
4823
  "ECO",
4782
4824
  "COMFORT"
4783
4825
  ]);
4826
+ /**
4827
+ * What to report when the endpoint carries no temperature at all.
4828
+ *
4829
+ * HAP's own default for `CurrentTemperature`, so a device with no thermometer
4830
+ * looks the same as one that has not answered yet, rather than inventing a
4831
+ * reading.
4832
+ */
4833
+ const UNKNOWN_TEMPERATURE = 0;
4784
4834
  /** An electric heating controller (Delta Dore RF4890, RF6600FP). */
4785
4835
  var ThermostatAccessory = class extends BaseAccessory {
4786
4836
  #service;
@@ -4827,9 +4877,13 @@ var ThermostatAccessory = class extends BaseAccessory {
4827
4877
  debugGet(CurrentHeatingCoolingState, this.#service);
4828
4878
  const data = await this.#read();
4829
4879
  const authorization = getTydomDataPropValue(data, "authorization");
4830
- const setpoint = getTydomDataPropValue(data, "setpoint");
4831
- const temperature = getTydomDataPropValue(data, "temperature");
4880
+ const setpoint = findTydomDataPropValue(data, "setpoint");
4881
+ const temperature = findTydomDataPropValue(data, "temperature");
4832
4882
  let nextValue = CurrentHeatingCoolingState.OFF;
4883
+ if (typeof setpoint !== "number" || typeof temperature !== "number") {
4884
+ debugGetResult(CurrentHeatingCoolingState, this.#service, nextValue);
4885
+ return nextValue;
4886
+ }
4833
4887
  if (authorization === "HEATING" && setpoint > temperature) nextValue = CurrentHeatingCoolingState.HEAT;
4834
4888
  else if (authorization === "COOLING" && temperature > setpoint) nextValue = CurrentHeatingCoolingState.COOL;
4835
4889
  debugGetResult(CurrentHeatingCoolingState, this.#service, nextValue);
@@ -4869,14 +4923,15 @@ var ThermostatAccessory = class extends BaseAccessory {
4869
4923
  });
4870
4924
  this.#service.getCharacteristic(CurrentTemperature).onGet(async () => {
4871
4925
  debugGet(CurrentTemperature, this.#service);
4872
- const temperature = getTydomDataPropValue(await this.#read(), "temperature");
4926
+ const data = await this.#read();
4927
+ const temperature = findTydomDataPropValue(data, "temperature") ?? findTydomDataPropValue(data, "setpoint") ?? UNKNOWN_TEMPERATURE;
4873
4928
  debugGetResult(CurrentTemperature, this.#service, temperature);
4874
4929
  return temperature;
4875
4930
  });
4876
4931
  this.#service.getCharacteristic(TargetTemperature).onGet(async () => {
4877
4932
  debugGet(TargetTemperature, this.#service);
4878
4933
  const data = await this.#read();
4879
- const setpoint = getTydomDataPropValue(data, "setpoint") ?? getTydomDataPropValue(data, "temperature");
4934
+ const setpoint = findTydomDataPropValue(data, "setpoint") ?? findTydomDataPropValue(data, "temperature") ?? UNKNOWN_TEMPERATURE;
4880
4935
  debugGetResult(TargetTemperature, this.#service, setpoint);
4881
4936
  return setpoint;
4882
4937
  }).onSet(async (value) => {
@@ -5620,4 +5675,4 @@ var TydomPlatform = class {
5620
5675
  //#endregion
5621
5676
  export { TydomPlatform as default };
5622
5677
 
5623
- //# sourceMappingURL=platform-BssMTVVc.mjs.map
5678
+ //# sourceMappingURL=platform-d-jxZ1Nu.mjs.map