homebridge-tydom 0.32.2 → 0.33.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.
package/README.md
CHANGED
|
@@ -201,6 +201,7 @@ Pointing `hostname` at your gateway's IP (e.g. `192.168.0.42`) skips Delta Dore'
|
|
|
201
201
|
| `name` | any | Rename the accessory. |
|
|
202
202
|
| `category` | any | Pin the device to a HAP category, bypassing hardware detection. See [Category overrides](#category-overrides). |
|
|
203
203
|
| `trigger` | switches | Publish a stateless switch that returns to off, rather than a toggle. |
|
|
204
|
+
| `autoShutdownDelay` | switches, outlets, fans | Switch off again this many **milliseconds** after coming on, whether that was from HomeKit or from the device's own button. Off by default. |
|
|
204
205
|
| `smokeDetector` | temperature sensors | Publish as a smoke detector instead. |
|
|
205
206
|
| `delay` | garage doors | Travel time in **milliseconds**. Defaults to `20000`. |
|
|
206
207
|
| `autoCloseDelay` | garage doors | Close again this many **milliseconds** after opening. Off by default. |
|
package/config.schema.json
CHANGED
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
"patternProperties": {
|
|
116
116
|
"^[0-9]+$": { "type": "object" }
|
|
117
117
|
},
|
|
118
|
-
"description": "Per-device overrides, keyed by numeric device id. Free-form, because the options a device accepts depend on what it is — edit this in the JSON config rather than here.\n\n```json\n\"settings\": {\n \"1234567\": { \"name\": \"Kitchen blind\" },\n \"1234568\": { \"trigger\": true },\n \"1234569\": { \"delay\": 10000, \"autoCloseDelay\": 300000 },\n \"1657464\": {\n \"pin\": \"1234\",\n \"sensors\": true,\n \"aliases\": { \"stay\": [1], \"night\": [1, 2] }\n }\n}\n```\n\n`name` renames the accessory and `category` pins it to a HAP category, bypassing hardware detection. `trigger` turns a switch into a stateless one that returns to off. On a garage door, `delay` is the travel time in **milliseconds** (default 20000) and `autoCloseDelay` closes it again after that many milliseconds. `smokeDetector` publishes a temperature sensor as a smoke detector. On an alarm: `pin` overrides the platform PIN, `sensors: false` drops the companion accessory that holds one contact sensor per opening detector (it is published by default), `aliases` says which armed-zone combinations HomeKit should show as Home and Night, and `legacy` selects the CTX60 protocol (detected automatically; only set it if detection is wrong)."
|
|
118
|
+
"description": "Per-device overrides, keyed by numeric device id. Free-form, because the options a device accepts depend on what it is — edit this in the JSON config rather than here.\n\n```json\n\"settings\": {\n \"1234567\": { \"name\": \"Kitchen blind\" },\n \"1234568\": { \"trigger\": true },\n \"1234569\": { \"delay\": 10000, \"autoCloseDelay\": 300000 },\n \"1657464\": {\n \"pin\": \"1234\",\n \"sensors\": true,\n \"aliases\": { \"stay\": [1], \"night\": [1, 2] }\n }\n}\n```\n\n`name` renames the accessory and `category` pins it to a HAP category, bypassing hardware detection. `trigger` turns a switch into a stateless one that returns to off. On a garage door, `delay` is the travel time in **milliseconds** (default 20000) and `autoCloseDelay` closes it again after that many milliseconds. `autoShutdownDelay` switches a switch, outlet or fan off again that many milliseconds after it comes on, counted from the moment it was switched on rather than from the last thing the gateway reported, and it follows the device's own button as well as HomeKit. `smokeDetector` publishes a temperature sensor as a smoke detector. On an alarm: `pin` overrides the platform PIN, `sensors: false` drops the companion accessory that holds one contact sensor per opening detector (it is published by default), `aliases` says which armed-zone combinations HomeKit should show as Home and Night, and `legacy` selects the CTX60 protocol (detected automatically; only set it if detection is wrong)."
|
|
119
119
|
},
|
|
120
120
|
"debug": {
|
|
121
121
|
"title": "Verbose logging",
|
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-
|
|
4
|
+
const { default: TydomPlatform } = await import("./platform-Bp1Glauz.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) {
|
|
@@ -4813,6 +4823,14 @@ const EXPOSED_THERMIC_LEVELS = /* @__PURE__ */ new Set([
|
|
|
4813
4823
|
"ECO",
|
|
4814
4824
|
"COMFORT"
|
|
4815
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;
|
|
4816
4834
|
/** An electric heating controller (Delta Dore RF4890, RF6600FP). */
|
|
4817
4835
|
var ThermostatAccessory = class extends BaseAccessory {
|
|
4818
4836
|
#service;
|
|
@@ -4859,9 +4877,13 @@ var ThermostatAccessory = class extends BaseAccessory {
|
|
|
4859
4877
|
debugGet(CurrentHeatingCoolingState, this.#service);
|
|
4860
4878
|
const data = await this.#read();
|
|
4861
4879
|
const authorization = getTydomDataPropValue(data, "authorization");
|
|
4862
|
-
const setpoint =
|
|
4863
|
-
const temperature =
|
|
4880
|
+
const setpoint = findTydomDataPropValue(data, "setpoint");
|
|
4881
|
+
const temperature = findTydomDataPropValue(data, "temperature");
|
|
4864
4882
|
let nextValue = CurrentHeatingCoolingState.OFF;
|
|
4883
|
+
if (typeof setpoint !== "number" || typeof temperature !== "number") {
|
|
4884
|
+
debugGetResult(CurrentHeatingCoolingState, this.#service, nextValue);
|
|
4885
|
+
return nextValue;
|
|
4886
|
+
}
|
|
4865
4887
|
if (authorization === "HEATING" && setpoint > temperature) nextValue = CurrentHeatingCoolingState.HEAT;
|
|
4866
4888
|
else if (authorization === "COOLING" && temperature > setpoint) nextValue = CurrentHeatingCoolingState.COOL;
|
|
4867
4889
|
debugGetResult(CurrentHeatingCoolingState, this.#service, nextValue);
|
|
@@ -4901,14 +4923,15 @@ var ThermostatAccessory = class extends BaseAccessory {
|
|
|
4901
4923
|
});
|
|
4902
4924
|
this.#service.getCharacteristic(CurrentTemperature).onGet(async () => {
|
|
4903
4925
|
debugGet(CurrentTemperature, this.#service);
|
|
4904
|
-
const
|
|
4926
|
+
const data = await this.#read();
|
|
4927
|
+
const temperature = findTydomDataPropValue(data, "temperature") ?? findTydomDataPropValue(data, "setpoint") ?? UNKNOWN_TEMPERATURE;
|
|
4905
4928
|
debugGetResult(CurrentTemperature, this.#service, temperature);
|
|
4906
4929
|
return temperature;
|
|
4907
4930
|
});
|
|
4908
4931
|
this.#service.getCharacteristic(TargetTemperature).onGet(async () => {
|
|
4909
4932
|
debugGet(TargetTemperature, this.#service);
|
|
4910
4933
|
const data = await this.#read();
|
|
4911
|
-
const setpoint =
|
|
4934
|
+
const setpoint = findTydomDataPropValue(data, "setpoint") ?? findTydomDataPropValue(data, "temperature") ?? UNKNOWN_TEMPERATURE;
|
|
4912
4935
|
debugGetResult(TargetTemperature, this.#service, setpoint);
|
|
4913
4936
|
return setpoint;
|
|
4914
4937
|
}).onSet(async (value) => {
|
|
@@ -5188,10 +5211,26 @@ const createWindowCoveringAccessory = (deps) => new WindowCoveringAccessory(deps
|
|
|
5188
5211
|
var MappedAccessory = class extends BaseAccessory {
|
|
5189
5212
|
#service;
|
|
5190
5213
|
#bindings;
|
|
5214
|
+
/** How long after switching on to switch off again, if the user asked. */
|
|
5215
|
+
#autoShutdownMs;
|
|
5216
|
+
/** The writable `On` binding, which is the only thing there is to switch off. */
|
|
5217
|
+
#onBinding;
|
|
5218
|
+
#autoShutdownTimer;
|
|
5219
|
+
/**
|
|
5220
|
+
* What the `On` characteristic last read, so a repeat can be told from a
|
|
5221
|
+
* change. The periodic refresh pushes the current state whether or not it
|
|
5222
|
+
* moved, and re-arming on those would push the shutdown out for as long as
|
|
5223
|
+
* the device stayed on — which is the one thing it must not do.
|
|
5224
|
+
*/
|
|
5225
|
+
#isOn;
|
|
5191
5226
|
constructor(deps, spec) {
|
|
5192
5227
|
super(deps);
|
|
5193
5228
|
this.#service = this.service(spec.service(this.platform.Service));
|
|
5194
5229
|
this.#bindings = spec.bindings(this.platform.Characteristic);
|
|
5230
|
+
const { autoShutdownDelay } = this.accessory.context.settings ?? {};
|
|
5231
|
+
this.#autoShutdownMs = autoShutdownDelay;
|
|
5232
|
+
const { On } = this.platform.Characteristic;
|
|
5233
|
+
this.#onBinding = this.#bindings.find((binding) => binding.characteristic === On && binding.toTydom);
|
|
5195
5234
|
for (const binding of this.#bindings) this.#bind(binding);
|
|
5196
5235
|
}
|
|
5197
5236
|
/** The service this accessory publishes. Exposed for tests. */
|
|
@@ -5220,9 +5259,45 @@ var MappedAccessory = class extends BaseAccessory {
|
|
|
5220
5259
|
value: tydomValue
|
|
5221
5260
|
}]);
|
|
5222
5261
|
debugSetResult(characteristic, service, value, tydomValue);
|
|
5262
|
+
this.#trackAutoShutdown(binding, value);
|
|
5223
5263
|
});
|
|
5224
5264
|
}
|
|
5225
5265
|
/**
|
|
5266
|
+
* Arm or stand down the auto-shutdown, following the device's on state.
|
|
5267
|
+
*
|
|
5268
|
+
* Called from both directions on purpose: the Home app is the case nobody
|
|
5269
|
+
* needs help with, and a switch left on at the wall is the case the setting
|
|
5270
|
+
* exists for.
|
|
5271
|
+
*/
|
|
5272
|
+
#trackAutoShutdown(binding, value) {
|
|
5273
|
+
if (this.#autoShutdownMs === void 0 || binding !== this.#onBinding) return;
|
|
5274
|
+
const isOn = value === true;
|
|
5275
|
+
if (isOn === this.#isOn) return;
|
|
5276
|
+
this.#isOn = isOn;
|
|
5277
|
+
this.clearTimer(this.#autoShutdownTimer);
|
|
5278
|
+
this.#autoShutdownTimer = void 0;
|
|
5279
|
+
if (!isOn) return;
|
|
5280
|
+
this.#autoShutdownTimer = this.setTimer(() => {
|
|
5281
|
+
this.#autoShutdownTimer = void 0;
|
|
5282
|
+
this.#switchOff(binding);
|
|
5283
|
+
}, this.#autoShutdownMs);
|
|
5284
|
+
}
|
|
5285
|
+
async #switchOff(binding) {
|
|
5286
|
+
const { On } = this.platform.Characteristic;
|
|
5287
|
+
this.#isOn = false;
|
|
5288
|
+
try {
|
|
5289
|
+
await this.api.putDeviceData(this.deviceId, this.endpointId, [{
|
|
5290
|
+
name: binding.prop,
|
|
5291
|
+
value: binding.toTydom?.(false)
|
|
5292
|
+
}]);
|
|
5293
|
+
} catch (err) {
|
|
5294
|
+
this.platform.log.error(`Failed to switch off ${this.accessory.displayName} after its shutdown delay: ${String(err)}`);
|
|
5295
|
+
return;
|
|
5296
|
+
}
|
|
5297
|
+
debugSetUpdate(On, this.#service, false);
|
|
5298
|
+
this.#service.updateCharacteristic(On, false);
|
|
5299
|
+
}
|
|
5300
|
+
/**
|
|
5226
5301
|
* The mapping a pushed property feeds, or undefined if this binding ignores
|
|
5227
5302
|
* it. Read and push therefore agree by construction.
|
|
5228
5303
|
*/
|
|
@@ -5237,6 +5312,7 @@ var MappedAccessory = class extends BaseAccessory {
|
|
|
5237
5312
|
const next = map(value);
|
|
5238
5313
|
debugSetUpdate(binding.characteristic, this.#service, next);
|
|
5239
5314
|
this.#service.updateCharacteristic(binding.characteristic, next);
|
|
5315
|
+
this.#trackAutoShutdown(binding, next);
|
|
5240
5316
|
}
|
|
5241
5317
|
}
|
|
5242
5318
|
};
|
|
@@ -5652,4 +5728,4 @@ var TydomPlatform = class {
|
|
|
5652
5728
|
//#endregion
|
|
5653
5729
|
export { TydomPlatform as default };
|
|
5654
5730
|
|
|
5655
|
-
//# sourceMappingURL=platform-
|
|
5731
|
+
//# sourceMappingURL=platform-Bp1Glauz.mjs.map
|