homebridge
Version:
HomeKit support for the impatient
85 lines • 3.95 kB
JavaScript
/**
* FanControl Cluster Behavior
*
* Handles fan control commands for fans
*/
import { FanControlServer } from '@matter/main/behaviors/fan-control';
import { Status, StatusResponseError } from '@matter/main/types';
import { MatterStatus } from '../errors.js';
import { getRegistryManager } from './EndpointContext.js';
/**
* Custom FanControl Server that calls plugin handlers
*/
export class HomebridgeFanControlServer extends FanControlServer {
/**
* Get the registry for this behavior's endpoint
*/
getRegistry() {
return getRegistryManager(this.endpoint).getRegistry(this.endpoint.id);
}
initialize() {
super.initialize();
// React to fanMode attribute changes (on/off)
this.reactTo(this.events.fanMode$Changed, this.#handleFanModeChange, { offline: true });
// React to percentSetting attribute changes (speed)
this.reactTo(this.events.percentSetting$Changed, this.#handlePercentSettingChange, { offline: true });
}
async #handleFanModeChange(value, oldValue) {
const endpointId = this.endpoint.id;
const registry = this.getRegistry();
try {
// Execute user handler
await registry.executeHandler(endpointId, 'fanControl', 'fanModeChange', { fanMode: value, oldFanMode: oldValue });
// Sync state to cache
// When turning off (fanMode = 0), also set percentSetting and percentCurrent to 0
// This ensures the UI correctly reflects the off state
const stateUpdate = { fanMode: value };
if (value === 0) {
stateUpdate.percentSetting = 0;
stateUpdate.percentCurrent = 0;
}
registry.syncStateToCache(endpointId, 'fanControl', stateUpdate);
}
catch (error) {
// If user handler already threw a StatusResponseError, propagate it as-is
// This sends a proper Matter protocol error response to the controller
if (MatterStatus.isMatterProtocolError(error)) {
throw error;
}
// For other errors, wrap in appropriate StatusResponseError
// This prevents the endpoint from crashing and keeps the device online
const message = error instanceof Error ? error.message : String(error);
throw new StatusResponseError(`Failed to change fan mode: ${message}`, Status.Failure);
}
}
async #handlePercentSettingChange(value, oldValue) {
const endpointId = this.endpoint.id;
const registry = this.getRegistry();
try {
// Execute user handler
await registry.executeHandler(endpointId, 'fanControl', 'percentSettingChange', { percentSetting: value, oldPercentSetting: oldValue });
// Sync state to cache
// When setting to 0%, also set fanMode to 0 (Off) for UI consistency
const stateUpdate = {
percentSetting: value ?? undefined,
percentCurrent: value ?? undefined,
};
if (value === 0) {
stateUpdate.fanMode = 0;
}
registry.syncStateToCache(endpointId, 'fanControl', stateUpdate);
}
catch (error) {
// If user handler already threw a StatusResponseError, propagate it as-is
// This sends a proper Matter protocol error response to the controller
if (MatterStatus.isMatterProtocolError(error)) {
throw error;
}
// For other errors, wrap in appropriate StatusResponseError
// This prevents the endpoint from crashing and keeps the device online
const message = error instanceof Error ? error.message : String(error);
throw new StatusResponseError(`Failed to change fan speed: ${message}`, Status.Failure);
}
}
}
//# sourceMappingURL=FanControlBehavior.js.map