homebridge-wyze-smart-home-justoy-forked
Version:
Wyze Smart Home plugin for Homebridge forked by justoy, which supports wyze battery cam pro
248 lines (221 loc) • 8.41 kB
JavaScript
const { Service, Characteristic } = require("../types");
const WyzeAccessory = require("./WyzeAccessory");
const noResponse = new Error("No Response");
noResponse.toString = () => {
return noResponse.message;
};
module.exports = class WyzeLock extends WyzeAccessory {
constructor(plugin, homeKitAccessory) {
super(plugin, homeKitAccessory);
if (this.plugin.config.pluginLoggingEnabled)
this.plugin.log(
`[Lock] Retrieving previous service for "${this.display_name} (${this.mac})"`
);
this.lockService = this.homeKitAccessory.getService(Service.LockMechanism);
if (this.plugin.config.pluginLoggingEnabled)
this.plugin.log(
`[Lock] [Door Contact] Retrieving previous service for "${this.display_name} (${this.mac})"`
);
this.contactService = this.homeKitAccessory.getService(
Service.ContactSensor
);
if (this.plugin.config.pluginLoggingEnabled)
this.plugin.log(
`[Lock] [Battery] Retrieving previous service for "${this.display_name} (${this.mac})"`
);
this.batteryService = this.homeKitAccessory.getService(Service.Battery);
if (!this.lockService) {
if (this.plugin.config.pluginLoggingEnabled)
this.plugin.log(
`[Lock] Adding service for "${this.display_name} (${this.mac})"`
);
this.lockService = this.homeKitAccessory.addService(
Service.LockMechanism
);
}
if (!this.contactService) {
if (this.plugin.config.pluginLoggingEnabled)
this.plugin.log(
`[Lock] [Door Contact] Adding service for "${this.display_name} (${this.mac})"`
);
this.contactService = this.homeKitAccessory.addService(
Service.ContactSensor
);
}
if (!this.batteryService) {
if (this.plugin.config.pluginLoggingEnabled)
this.plugin.log(
`[Lock] [Battery] Adding service for "${this.display_name} (${this.mac})"`
);
this.batteryService = this.homeKitAccessory.addService(Service.Battery);
}
this.batteryService
.getCharacteristic(Characteristic.BatteryLevel)
.onGet(this.getBatteryStatus.bind(this));
this.batteryService
.getCharacteristic(Characteristic.StatusLowBattery)
.onGet(this.getLowBatteryStatus.bind(this));
this.contactService
.getCharacteristic(Characteristic.ContactSensorState)
.onGet(this.getDoorStatus.bind(this));
this.lockService
.getCharacteristic(Characteristic.LockCurrentState)
.onGet(this.getLockCurrentState.bind(this));
this.lockService
.getCharacteristic(Characteristic.LockTargetState)
.onGet(this.getLockTargetState.bind(this))
.onSet(this.setLockTargetState.bind(this));
}
async updateCharacteristics(device) {
if (this.plugin.config.pluginLoggingEnabled)
this.plugin.log(
`[Lock] Updating status "${this.display_name} (${this.mac}) to noResponse"`
);
if (device.conn_state === 0) {
//this.getLockCurrentState().updateValue(noResponse)
this.lockService
.getCharacteristic(Characteristic.LockCurrentState)
.updateValue(noResponse);
} else {
if (this.plugin.config.pluginLoggingEnabled)
this.plugin.log(
`[Lock] Updating status of "${this.display_name} (${this.mac})"`
);
const propertyList = await this.plugin.client.getLockInfo(
this.mac,
this.product_model
);
let lockProperties = propertyList.device;
const prop_key = Object.keys(lockProperties);
for (const element of prop_key) {
const prop = element;
switch (prop) {
case "onoff_line":
this.lockOnOffline = lockProperties[prop];
break;
case "power":
// Lock Battery
this.batteryService
.getCharacteristic(Characteristic.BatteryLevel)
.updateValue(
this.plugin.client.checkBatteryVoltage(lockProperties[prop])
);
this.lockPower = lockProperties[prop];
break;
case "door_open_status":
// Door Status
this.lockService
.getCharacteristic(Characteristic.ContactSensorState)
.updateValue(
this.plugin.client.getLockDoorState(lockProperties[prop])
);
this.door_open_status = lockProperties[prop];
break;
case "trash_mode":
this.trash_mode = lockProperties[prop];
break;
}
}
let lockerStatusProperties = propertyList.device.locker_status;
const prop_keyLock = Object.keys(lockerStatusProperties);
for (const element of prop_keyLock) {
const prop = element;
switch (prop) {
case "hardlock":
// Door Locked Status
this.lockService
.getCharacteristic(Characteristic.LockCurrentState)
.updateValue(
this.plugin.client.getLockState(lockerStatusProperties[prop])
);
this.hardlock = lockerStatusProperties[prop];
break;
}
}
}
}
async getLockCurrentState() {
if (this.plugin.config.pluginLoggingEnabled)
this.plugin.log(
`[Lock] Getting Current State "${this.display_name} (${this.mac}) to ${this.hardlock}"`
);
if (this.hardlock == 2) {
return Characteristic.LockTargetState.UNSECURED;
} else {
return Characteristic.LockTargetState.SECURED;
}
}
async getLockTargetState() {
if (this.plugin.config.pluginLoggingEnabled)
this.plugin.log(
`[Lock] Getting Target State "${this.display_name} (${this.mac}) to ${this.hardlock}"`
);
if (this.hardlock === 2) {
return Characteristic.LockTargetState.UNSECURED;
} else {
return Characteristic.LockTargetState.SECURED;
}
}
async getDoorStatus() {
if (this.plugin.config.pluginLoggingEnabled)
this.plugin.log(
`[Lock] Getting Door Status "${this.display_name} (${this.mac}) to ${this.door_open_status}"`
);
if (this.door_open_status == 1) {
return Characteristic.ContactSensorState.CONTACT_NOT_DETECTED; // 1
} else {
return Characteristic.ContactSensorState.CONTACT_DETECTED; // 0
}
}
async getBatteryStatus() {
if (this.plugin.config.pluginLoggingEnabled)
this.plugin.log(
`[Lock] Getting Battery Status "${this.display_name} (${this.mac}) to ${this.lockPower}"`
);
return this.plugin.client.checkBatteryVoltage(this.lockPower);
}
async getLowBatteryStatus() {
if (this.plugin.config.pluginLoggingEnabled)
this.plugin.log(
`[Lock] Getting Low Battery Status "${this.display_name} (${
this.mac
}) to ${this.plugin.client.checkLowBattery(this.lockPower)}"`
);
return this.plugin.client.checkLowBattery(this.lockPower);
}
async setLockTargetState(targetState) {
if (this.plugin.config.pluginLoggingEnabled)
this.plugin.log(`[Lock] Setting Target State "${targetState}"`); // this is zero or 1
await this.plugin.client.controlLock(
this.mac,
this.product_model,
targetState === Characteristic.LockCurrentState.SECURED
? "remoteLock"
: "remoteUnlock"
);
// Takes a few seconds for the lock command to actually update lock state property
// Poll every second to see if the lock state has changed to what we expect, or time out after 30 attempts
//await this.poll(async () => await this.getLockCurrentState(), currentState => currentState === targetState, 1000, 10)
this.lockService.setCharacteristic(
Characteristic.LockCurrentState,
targetState === Characteristic.LockTargetState.SECURED
? Characteristic.LockCurrentState.SECURED
: Characteristic.LockCurrentState.UNSECURED
);
}
async poll(fn, validate, interval, maxAttempts) {
let attempts = 0;
const executePoll = async (resolve, reject) => {
const result = await fn();
attempts++;
if (validate(result)) {
return resolve(result);
} else if (maxAttempts && maxAttempts === attempts) {
return reject(new Error("Exceeded maximum attempts"));
} else {
setTimeout(executePoll, interval, resolve, reject);
}
};
return new Promise(executePoll);
}
};