Skip to content

Commit a6862e1

Browse files
committed
Use homekit controller setup and isAvailable
1 parent bee75cf commit a6862e1

35 files changed

Lines changed: 104 additions & 198 deletions

src/accessory/abstract/base.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export abstract class BaseAccessory<C extends BaseAccessoryConfig = BaseAccessor
2828
.setCharacteristic(dependency.Characteristic.FirmwareRevision, dependency.config.info.version ?? getVersion());
2929
}
3030

31-
this.setup(HKCharacteristicKey.StatusActive, true,
31+
this.homekit.setup(HKCharacteristicKey.StatusActive, true,
3232
'topicGetStatusActive',
3333
this.bindOnUpdateBooleanSingle(HKCharacteristicKey.StatusActive, 'valueStatusActive',
3434
strings.accessory.statusActive, strings.accessory.statusInactive),

src/accessory/abstract/common.ts

Lines changed: 1 addition & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {
2-
Characteristic, CharacteristicProps, CharacteristicSetHandler, CharacteristicValue,
2+
Characteristic, CharacteristicProps, CharacteristicValue,
33
PartialAllowingNull, Perms, PrimitiveTypes, Service,
44
} from 'homebridge';
55

@@ -27,9 +27,6 @@ export type PublishHandler = (topic: string, value: PrimitiveTypes) => void;
2727
type NumberCallback = (value: number) => void;
2828
type BooleanCallback = (value: boolean) => void
2929

30-
const AVAILABILITY_KEY = 'Available';
31-
const HAP_COMMUNICATION_FAILURE = -70402;
32-
3330
export abstract class Common<C extends Assertable> {
3431

3532
constructor(
@@ -100,113 +97,6 @@ export abstract class Common<C extends Assertable> {
10097
return Properties.set(this.identifier, key, value, persist || this.useStoredProperties);
10198
}
10299

103-
protected get isAvailable(): boolean {
104-
return Properties.get(this.identifier, AVAILABILITY_KEY) !== false;
105-
}
106-
107-
protected set isAvailable(value: boolean) {
108-
109-
if (!Properties.set(this.identifier, AVAILABILITY_KEY, value, this.useStoredProperties)) {
110-
return;
111-
}
112-
113-
if (value) {
114-
this.logIfDesired(LogType.ALWAYS, strings.accessory.available);
115-
} else {
116-
this.logIfDesired(LogType.WARNING, strings.accessory.unavailable);
117-
}
118-
}
119-
120-
protected setup(
121-
characteristicKey: CharacteristicKey, defaultValue: CharacteristicValue,
122-
getTopicKey: keyof C, onUpdateHandler: OnUpdateHandler, assertGetTopic: boolean,
123-
setTopicKey: keyof C | undefined = undefined, onSetHandler: CharacteristicSetHandler | undefined = undefined,
124-
): Characteristic | undefined {
125-
126-
const characteristic = this.setupGet(characteristicKey, defaultValue, getTopicKey, onUpdateHandler, assertGetTopic);
127-
if (!characteristic) {
128-
return;
129-
}
130-
131-
this.setupSet(characteristicKey, setTopicKey, onSetHandler);
132-
133-
return characteristic;
134-
}
135-
136-
private setupGet(characteristicKey: CharacteristicKey, defaultValue: CharacteristicValue,
137-
getTopicKey: keyof C, onUpdateHandler: OnUpdateHandler, assertTopic: boolean,
138-
): Characteristic | undefined {
139-
140-
if (!getTopicKey.toString().startsWith('topic')) {
141-
throw new Error(`Trying to fetch topic with unexpected property name '${getTopicKey.toString()}'`);
142-
}
143-
144-
if (assertTopic) {
145-
this.assert(getTopicKey);
146-
}
147-
148-
if (this.config[getTopicKey] === undefined) {
149-
for (const characteristic of this.service.characteristics) {
150-
if (characteristic.UUID === this.characteristicFromKey(characteristicKey).UUID) {
151-
this.service.removeCharacteristic(characteristic);
152-
break;
153-
}
154-
}
155-
return;
156-
}
157-
158-
if (this.isOptionalCharacteristic(characteristicKey)) {
159-
this.service.addOptionalCharacteristic(this.characteristicFromKey(characteristicKey));
160-
}
161-
162-
const characteristic = this.service.getCharacteristic(this.characteristicFromKey(characteristicKey));
163-
164-
const startingValue = !this.useStoredProperties ? defaultValue : (this.getProperty(characteristicKey) ?? defaultValue);
165-
characteristic.setValue(startingValue);
166-
167-
this.setProperty(characteristicKey, startingValue);
168-
169-
characteristic.onGet( async (): Promise<CharacteristicValue> => {
170-
if (!this.isAvailable) {
171-
throw new this.HapStatusError(HAP_COMMUNICATION_FAILURE);
172-
}
173-
return this.getProperty(characteristicKey) ?? startingValue;
174-
});
175-
176-
const onUpdateHandlerWrapper: OnUpdateHandler = async (topic, value) => {
177-
this.isAvailable = true;
178-
await onUpdateHandler(topic, value);
179-
};
180-
181-
this.addTopicHandler({ topic: this.config[getTopicKey] as string, handler: onUpdateHandlerWrapper });
182-
183-
return characteristic;
184-
}
185-
186-
protected setupSet(characteristicKey: CharacteristicKey,
187-
setTopicKey: keyof C | undefined = undefined, onSetHandler: CharacteristicSetHandler | undefined = undefined,
188-
) {
189-
190-
if (setTopicKey === undefined) {
191-
return;
192-
}
193-
194-
if (!setTopicKey.toString().startsWith('topic')) {
195-
throw new Error(`Trying to fetch topic with unexpected property name '${setTopicKey.toString()}'`);
196-
}
197-
198-
if (!onSetHandler) {
199-
throw new Error(`Missing onSetHandler for topic '${setTopicKey.toString()}'`);
200-
}
201-
202-
if (this.isOptionalCharacteristic(characteristicKey)) {
203-
this.service.addOptionalCharacteristic(this.characteristicFromKey(characteristicKey));
204-
}
205-
206-
const characteristic = this.service.getCharacteristic(this.characteristicFromKey(characteristicKey));
207-
characteristic.onSet(onSetHandler);
208-
}
209-
210100
protected setupTopicless(characteristicKey: CharacteristicKey, defaultValue: CharacteristicValue,
211101
onSetCallback?: (value: CharacteristicValue, changed: boolean) => (void), props?: PartialAllowingNull<CharacteristicProps>): Characteristic | undefined {
212102

src/accessory/abstract/mqtt.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ export abstract class MQTTAccessory<C extends MQTTAccessoryConfig> extends Commo
147147
}
148148

149149
this.addTopicHandler({ topic: this.config.topicGetAvailable, handler: (async (_topic: string, value: PrimitiveTypes) => {
150-
this.isAvailable = value === this.getPrimitiveValue('valueAvailable', true);
150+
this.homekit.isAvailable = value === this.getPrimitiveValue('valueAvailable', true);
151151
}).bind(this) });
152152
}
153153

src/accessory/addons/addon.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { PrimitiveTypes, Service } from 'homebridge';
33
import { Common } from '../abstract/common.js';
44
import { MQTTAccessory } from '../abstract/mqtt.js';
55

6+
import { HomeKitController } from '../../controller/homekit.js';
67
import { TopicHandler } from '../../controller/mqtt.js';
78

89
import { AddonType } from '../../model/enums.js';
@@ -47,6 +48,8 @@ export abstract class Addon<C extends AddonConfig> extends Common<C> {
4748
return new constructor(addonService, parentAccessory, config);
4849
}
4950

51+
protected homekit: HomeKitController<C>;
52+
5053
protected readonly topicHandlers: TopicHandler[] = [];
5154

5255
protected constructor(
@@ -55,6 +58,19 @@ export abstract class Addon<C extends AddonConfig> extends Common<C> {
5558
private readonly addonConfig: C,
5659
) {
5760
super(parentAccessory.name);
61+
62+
this.homekit = new HomeKitController({
63+
log: this.log,
64+
config: this.config,
65+
identifier: this.identifier,
66+
name: this.name,
67+
service: this.service,
68+
Characteristic: this.Characteristic,
69+
HapStatusError: this.HapStatusError,
70+
useStoredProperties: this.useStoredProperties,
71+
disableLogging: this.disableLogging,
72+
addTopicHandler: this.addTopicHandler,
73+
});
5874
}
5975

6076
override addTopicHandler(topicHandler: TopicHandler) {

src/accessory/addons/battery.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ export class Battery extends Addon<BatteryConfig> {
3333
) {
3434
super(parentAccessory, service, config);
3535

36-
this.setup(HKCharacteristicKey.BatteryLevel, 100,
36+
this.homekit.setup(HKCharacteristicKey.BatteryLevel, 100,
3737
'topicGetBatteryLevel', this.bindOnUpdateNumeric(HKCharacteristicKey.BatteryLevel, strings.accessory.batteryLevel), false);
3838

39-
this.setup(HKCharacteristicKey.StatusLowBattery, this.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL,
39+
this.homekit.setup(HKCharacteristicKey.StatusLowBattery, this.Characteristic.StatusLowBattery.BATTERY_LEVEL_NORMAL,
4040
'topicGetBatteryLow',
4141
this.bindOnUpdateBooleanSingle(HKCharacteristicKey.StatusLowBattery, 'valueBatteryLow',
4242
strings.accessory.batteryLow, strings.accessory.batteryNotLow, LogType.WARNING),

src/accessory/addons/filter.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,17 @@ export class FilterMaintenance extends Addon<FilterMaintenanceConfig> {
3333
) {
3434
super(parentAccessory, service, config);
3535

36-
this.setup(HKCharacteristicKey.FilterChangeIndication, this.Characteristic.FilterChangeIndication.FILTER_OK,
36+
this.homekit.setup(HKCharacteristicKey.FilterChangeIndication, this.Characteristic.FilterChangeIndication.FILTER_OK,
3737
'topicGetFilterChangeIndication',
3838
this.bindOnUpdateBooleanSingle(HKCharacteristicKey.FilterChangeIndication,'valueFilterChange',
3939
strings.filter.change, strings.filter.ok, LogType.WARNING),
4040
false,
4141
);
4242

43-
this.setup(HKCharacteristicKey.FilterLifeLevel, 100,
43+
this.homekit.setup(HKCharacteristicKey.FilterLifeLevel, 100,
4444
'topicGetFilterLifeLevel', this.bindOnUpdateNumeric(HKCharacteristicKey.FilterLifeLevel, strings.filter.level), false);
4545

46-
this.setupSet(HKCharacteristicKey.ResetFilterIndication, 'topicResetFilterIndication', this.onResetIndication.bind(this));
46+
this.homekit.setupSet(HKCharacteristicKey.ResetFilterIndication, 'topicResetFilterIndication', this.onResetIndication.bind(this));
4747
}
4848

4949
private async onResetIndication(value: CharacteristicValue) {

src/accessory/button/doorbell.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ export class DoorbellAccessory extends ButtonAccessory<DoorbellConfig> {
2525
const brightnessGetTemplate = dependency.config.maximumBrightness < 100 ? strings.doorbell.brightnessValue : strings.doorbell.brightnessPercent;
2626
const brightnessSetTemplate = dependency.config.maximumBrightness < 100 ? strings.doorbell.brightnessValueFuture : strings.doorbell.brightnessPercentFuture;
2727

28-
this.setup(HKCharacteristicKey.Brightness, dependency.config.maximumBrightness,
28+
this.homekit.setup(HKCharacteristicKey.Brightness, dependency.config.maximumBrightness,
2929
'topicGetBrightness',
3030
this.bindOnUpdateNumeric(HKCharacteristicKey.Brightness, brightnessGetTemplate), false,
3131
'topicSetBrightness',
3232
this.bindOnSetNumeric(HKCharacteristicKey.Brightness, 'topicSetBrightness', brightnessSetTemplate),
3333
)?.setProps({ maxValue: dependency.config.maximumBrightness });
3434

35-
this.setup(HKCharacteristicKey.Mute, true,
35+
this.homekit.setup(HKCharacteristicKey.Mute, true,
3636
'topicGetMuted',
3737
this.bindOnUpdateBoolean(HKCharacteristicKey.Mute, 'valueMuted', 'valueUnmuted', strings.doorbell.muted, strings.doorbell.unmuted),
3838
false,
@@ -44,7 +44,7 @@ export class DoorbellAccessory extends ButtonAccessory<DoorbellConfig> {
4444
const volumeGetTemplate = dependency.config.maximumVolume < 100 ? strings.doorbell.volumeValue : strings.doorbell.volumePercent;
4545
const volumeSetTemplate = dependency.config.maximumVolume < 100 ? strings.doorbell.volumeValueFuture : strings.doorbell.volumePercentFuture;
4646

47-
this.setup(HKCharacteristicKey.Volume, dependency.config.maximumVolume,
47+
this.homekit.setup(HKCharacteristicKey.Volume, dependency.config.maximumVolume,
4848
'topicGetVolume',
4949
this.bindOnUpdateNumeric(HKCharacteristicKey.Volume, volumeGetTemplate), false,
5050
'topicSetVolume',

src/accessory/climate/active.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ export abstract class ActiveClimateAccessory<C extends ActiveClimateConfig = Act
1010
constructor(dependency: MQTTAccessoryDependency<C>) {
1111
super(dependency);
1212

13-
this.setup(HKCharacteristicKey.Active, dependency.Characteristic.Active.INACTIVE,
13+
this.homekit.setup(HKCharacteristicKey.Active, dependency.Characteristic.Active.INACTIVE,
1414
'topicGetActive',
1515
this.bindOnUpdateNumericBoolean(HKCharacteristicKey.Active, 'valueStateActive', strings.active.active, strings.active.notActive, true), true,
1616
'topicSetActive',
1717
this.bindOnSetBoolean(HKCharacteristicKey.Active, 'topicSetActive', 'valueStateActive', 'valueStateInactive', dependency.Characteristic.Active.ACTIVE,
1818
strings.active.activeSet, strings.active.inactiveSet),
1919
);
2020

21-
this.setup(HKCharacteristicKey.LockPhysicalControls, dependency.Characteristic.LockPhysicalControls.CONTROL_LOCK_DISABLED,
21+
this.homekit.setup(HKCharacteristicKey.LockPhysicalControls, dependency.Characteristic.LockPhysicalControls.CONTROL_LOCK_DISABLED,
2222
'topicGetLockPhysicalControls',
2323
this.bindOnUpdateNumericBoolean(
2424
HKCharacteristicKey.LockPhysicalControls, 'valueControlLock',
@@ -35,14 +35,14 @@ export abstract class ActiveClimateAccessory<C extends ActiveClimateConfig = Act
3535
rotationLogString = strings.active.rotationPercentUpdate;
3636
}
3737

38-
this.setup(HKCharacteristicKey.RotationSpeed, 0,
38+
this.homekit.setup(HKCharacteristicKey.RotationSpeed, 0,
3939
'topicGetRotationSpeed', this.bindOnUpdateNumeric(HKCharacteristicKey.RotationSpeed, rotationLogString), false,
4040
'topicSetRotationSpeed',
4141
this.bindOnSetPercentOrValue(HKCharacteristicKey.RotationSpeed, 'topicSetRotationSpeed', dependency.config.maximumRotationSpeed,
4242
strings.active.rotationPercentSet, strings.active.rotationValueSet),
4343
)?.setProps({ maxValue: dependency.config.maximumRotationSpeed });
4444

45-
this.setup(HKCharacteristicKey.SwingMode, dependency.Characteristic.SwingMode.SWING_DISABLED,
45+
this.homekit.setup(HKCharacteristicKey.SwingMode, dependency.Characteristic.SwingMode.SWING_DISABLED,
4646
'topicGetSwingMode',
4747
this.bindOnUpdateNumericBoolean(HKCharacteristicKey.SwingMode, 'valueSwingEnabled',
4848
strings.active.swingEnabled, strings.active.swingDisabled),

src/accessory/climate/fan2.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export class FanV2Accessory extends ActiveClimateAccessory<FanV2Config> {
3232
[dependency.Characteristic.CurrentFanState.BLOWING_AIR, strings.fanv2.stateBlowing],
3333
]);
3434

35-
this.setup(HKCharacteristicKey.CurrentFanState, currentStates.get(validCurrentStates[0])!,
35+
this.homekit.setup(HKCharacteristicKey.CurrentFanState, currentStates.get(validCurrentStates[0])!,
3636
'topicGetCurrentFanState',
3737
this.bindOnUpdateState(HKCharacteristicKey.CurrentFanState, currentStates, currentStrings, strings.fanv2.stateUnknown),
3838
false,
@@ -54,15 +54,15 @@ export class FanV2Accessory extends ActiveClimateAccessory<FanV2Config> {
5454
[dependency.Characteristic.TargetFanState.MANUAL, strings.fanv2.stateManual],
5555
]);
5656

57-
this.setup(HKCharacteristicKey.TargetFanState, targetStates.get(validTargetStates[0])!,
57+
this.homekit.setup(HKCharacteristicKey.TargetFanState, targetStates.get(validTargetStates[0])!,
5858
'topicGetTargetFanState',
5959
this.bindOnUpdateState(HKCharacteristicKey.TargetFanState, targetStates, targetStrings, strings.fanv2.stateUnknown),
6060
false,
6161
'topicSetTargetFanState',
6262
this.bindOnSetState(HKCharacteristicKey.TargetFanState, 'topicSetTargetFanState', targetStates, targetStrings, strings.fanv2.badValue),
6363
)?.setProps({ validValues: validTargetStates.map((key) => targetStates.get(key)!) });
6464

65-
this.setup(HKCharacteristicKey.RotationDirection, dependency.Characteristic.RotationDirection.CLOCKWISE,
65+
this.homekit.setup(HKCharacteristicKey.RotationDirection, dependency.Characteristic.RotationDirection.CLOCKWISE,
6666
'topicGetRotationDirection',
6767
this.bindOnUpdateNumericBoolean(HKCharacteristicKey.RotationDirection, 'valueDirectionClockwise',
6868
strings.fanv2.clockwise, strings.fanv2.counterClockwise),

src/accessory/climate/heaterCooler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export class HeaterCoolerAccessory extends ActiveClimateAccessory<HeaterCoolerCo
3838
[dependency.Characteristic.CurrentHeaterCoolerState.COOLING, strings.heaterCooler.stateCooling],
3939
]);
4040

41-
this.setup(HKCharacteristicKey.CurrentHeaterCoolerState, currentStates.get(validCurrentStates[0])!,
41+
this.homekit.setup(HKCharacteristicKey.CurrentHeaterCoolerState, currentStates.get(validCurrentStates[0])!,
4242
'topicGetCurrentHeaterCoolerState',
4343
this.bindOnUpdateState(HKCharacteristicKey.CurrentHeaterCoolerState, currentStates, currentStrings, strings.heaterCooler.unknownValue),
4444
true,
@@ -62,7 +62,7 @@ export class HeaterCoolerAccessory extends ActiveClimateAccessory<HeaterCoolerCo
6262
[dependency.Characteristic.TargetHeaterCoolerState.COOL, strings.heaterCooler.stateCool],
6363
]);
6464

65-
this.setup(HKCharacteristicKey.TargetHeaterCoolerState, targetStates.get(validTargetStates[0])!,
65+
this.homekit.setup(HKCharacteristicKey.TargetHeaterCoolerState, targetStates.get(validTargetStates[0])!,
6666
'topicGetTargetHeaterCoolerState',
6767
this.bindOnUpdateState(HKCharacteristicKey.TargetHeaterCoolerState, targetStates, targetStrings, strings.heaterCooler.unknownValue),
6868
true,

0 commit comments

Comments
 (0)