@homebridge-plugins/homebridge-lutron-caseta-leap
Version:
Homebridge support for Lutron
114 lines • 6.11 kB
JavaScript
export class SerenaTiltOnlyWoodBlinds {
platform;
accessory;
bridge;
service;
device;
constructor(platform, accessory, bridge) {
this.platform = platform;
this.accessory = accessory;
this.bridge = bridge;
this.device = accessory.context.device;
this.accessory
.getService(this.platform.api.hap.Service.AccessoryInformation)
.setCharacteristic(this.platform.api.hap.Characteristic.Manufacturer, 'Lutron Electronics Co., Inc')
.setCharacteristic(this.platform.api.hap.Characteristic.Model, this.device.ModelNumber)
.setCharacteristic(this.platform.api.hap.Characteristic.Name, this.device.FullyQualifiedName.join(' '))
.setCharacteristic(this.platform.api.hap.Characteristic.ConfiguredName, this.device.FullyQualifiedName.join(' '))
.setCharacteristic(this.platform.api.hap.Characteristic.SerialNumber, this.device.SerialNumber.toString());
this.service
= this.accessory.getService(this.platform.api.hap.Service.WindowCovering)
|| this.accessory.addService(this.platform.api.hap.Service.WindowCovering);
this.service.setCharacteristic(this.platform.api.hap.Characteristic.Name, this.device.FullyQualifiedName.join(' '));
// create handlers for required characteristics
const getter = (cb) => {
this.handleCurrentPositionGet().then((pos) => {
cb(null, pos);
}, (e) => {
cb(e);
});
};
const setter = (pos, cb) => {
this.handleTargetPositionSet(pos).then(() => {
cb(null, pos);
}, (e) => {
cb(e);
});
};
this.service
.getCharacteristic(this.platform.api.hap.Characteristic.CurrentPosition)
.on("get" /* this.platform.api.hap.CharacteristicEventTypes.GET */, getter);
this.service
.getCharacteristic(this.platform.api.hap.Characteristic.TargetPosition)
.on("get" /* this.platform.api.hap.CharacteristicEventTypes.GET */, getter)
.on("set" /* this.platform.api.hap.CharacteristicEventTypes.SET */, setter);
this.service
.getCharacteristic(this.platform.api.hap.Characteristic.PositionState)
.on("get" /* this.platform.api.hap.CharacteristicEventTypes.GET */, this.handlePositionStateGet.bind(this));
this.platform.on('unsolicited', this.handleUnsolicited.bind(this));
}
// `value` can range from 0-100, but n.b. 50 is flat. The Homekit
// Window Covering's required "Position" characteristic expects 0 to be
// "fully closed" and 100 to be "fully open". As such, we constrain the
// tilt angle to [-90,0] degrees by scaling `value` after the fact.
async handleCurrentPositionGet() {
this.platform.log.debug('blinds', this.device.FullyQualifiedName.join(' '), 'were asked for current or target position');
const tilt = await this.bridge.readBlindsTilt(this.device);
const adj_val = Math.min(100, tilt * 2);
// Per-poll noise — Homebridge re-reads position frequently. Was info
// (visible in normal logs); now debug so steady-state operation stays
// quiet, with global debug bringing it back when troubleshooting.
this.platform.log.debug('Told Homekit that blinds', this.device.FullyQualifiedName.join(' '), 'are at position', adj_val);
return adj_val;
}
async handleTargetPositionSet(value) {
const adj_val = Number(value) / 2;
// Per-command noise — fires every time HomeKit drives the blind. Was
// info; now debug.
this.platform.log.debug('Commanded', this.device.FullyQualifiedName.join(' '), 'blinds to (adjusted) value', adj_val);
await this.bridge.setBlindsTilt(this.device, adj_val);
}
handlePositionStateGet(cb) {
cb(null, this.platform.api.hap.Characteristic.PositionState.STOPPED);
}
handleUnsolicited(response) {
if (response.Header.MessageBodyType === 'OneZoneStatus') {
if (response.Body?.ZoneStatus?.Zone?.href === this.device.LocalZones[0].href) {
const adj_val = Math.min(100, response.Body.ZoneStatus.Tilt * 2);
// Per state-change noise — fires on every physical blind movement.
// Was info; now debug.
this.platform.log.debug('Blinds', this.device.FullyQualifiedName.join(' '), 'reported a new position of', adj_val);
this.accessory
.getService(this.platform.api.hap.Service.WindowCovering)
.getCharacteristic(this.platform.api.hap.Characteristic.TargetPosition)
.updateValue(adj_val);
this.accessory
.getService(this.platform.api.hap.Service.WindowCovering)
.getCharacteristic(this.platform.api.hap.Characteristic.CurrentPosition)
.updateValue(adj_val);
const matterApi = this.platform.api.matter;
if (matterApi && this.accessory?.UUID) {
const tiltPercent100ths = Math.max(0, Math.min(10000, Math.round(adj_val * 100)));
void matterApi.updateAccessoryState(this.accessory.UUID, 'windowCovering', {
currentPositionTiltPercent100ths: tiltPercent100ths,
targetPositionTiltPercent100ths: tiltPercent100ths,
});
}
}
}
}
/**
* Returns a Matter clusters object for this blind.
*/
static getMatterClusters() {
// Tilt-only blinds: Matter §8.3 WindowCovering
// 0 = open (horizontal), 10000 = closed (vertical) in hundredths of percent
return {
windowCovering: {
currentPositionTiltPercent100ths: 0, // 0=horizontal/open
targetPositionTiltPercent100ths: 0, // 0=horizontal/open
},
};
}
}
//# sourceMappingURL=SerenaTiltOnlyWoodBlinds.js.map