@hov3rcraft/homebridge-eufy-robovac
Version:
Homebridge support for Eufy RoboVac
248 lines • 13.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EufyRobovacAccessory = void 0;
const robovac_api_1 = require("./robovac-api");
class EufyRobovacAccessory {
constructor(platform, accessory, config, log) {
this.callbackTimeout = 3000;
this.cachingDuration = 60000;
this.lowBatteryThreshold = 10;
log.debug('Initializing EufyRobovacAccessory...');
this.platform = platform;
this.accessory = accessory;
this.log = log;
this.name = accessory.displayName;
this.connectionConfig = {
deviceId: config.deviceId,
localKey: config.localKey,
deviceIp: config.deviceIp
};
this.findButtonEnabled = config.findButtonEnabled;
this.batteryInformationEnabled = config.batteryInformationEnabled;
this.errorSensorEnabled = config.errorSensorEnabled;
// set accessory information
this.informationService = this.accessory.getService(this.platform.Service.AccessoryInformation);
this.informationService.getCharacteristic(this.platform.Characteristic.Identify).onSet(this.setIdentify.bind(this));
this.informationService.setCharacteristic(this.platform.Characteristic.Manufacturer, 'Eufy');
this.informationService.setCharacteristic(this.platform.Characteristic.Model, 'RoboVac');
this.informationService.setCharacteristic(this.platform.Characteristic.Name, this.name);
this.informationService.setCharacteristic(this.platform.Characteristic.SerialNumber, config.deviceId);
this.informationService.setCharacteristic(this.platform.Characteristic.FirmwareRevision, "unknown");
// create main service for the vacuum cleaner
if (config.useSwitchService) {
this.vacuumService = this.accessory.getService("Vacuum") || this.accessory.addService(this.platform.Service.Switch, "Vacuum", "VACUUM");
}
else {
this.vacuumService = this.accessory.getService("Vacuum") || this.accessory.addService(this.platform.Service.Fan, "Vacuum");
}
this.vacuumService.getCharacteristic(this.platform.Characteristic.On)
.onGet(this.getRunning.bind(this))
.onSet(this.setRunning.bind(this));
// create find robot service
if (this.findButtonEnabled) {
this.findRobotService = this.accessory.getService("FindRobot") || this.accessory.addService(this.platform.Service.Switch, "FindRobot", "FIND_ROBOT");
this.findRobotService.getCharacteristic(this.platform.Characteristic.On)
.onGet(this.getFindRobot.bind(this))
.onSet(this.setFindRobot.bind(this));
}
// create battery service
if (this.batteryInformationEnabled) {
this.batteryService = this.accessory.getService("Battery") || this.accessory.addService(this.platform.Service.Battery, "Battery", "BATTERY");
this.batteryService.getCharacteristic(this.platform.Characteristic.StatusLowBattery)
.onGet(this.getLowBattery.bind(this));
this.batteryService.getCharacteristic(this.platform.Characteristic.BatteryLevel)
.onGet(this.getBatteryLevel.bind(this));
this.batteryService.getCharacteristic(this.platform.Characteristic.ChargingState)
.onGet(this.getCharging.bind(this));
}
// create error sensor service
if (this.errorSensorEnabled) {
this.errorSensorService = this.accessory.getService("ErrorSensor") || this.accessory.addService(this.platform.Service.MotionSensor, "ErrorSensor", "ERROR_SENSOR");
this.errorSensorService.getCharacteristic(this.platform.Characteristic.MotionDetected)
.onGet(this.getErrorStatus.bind(this));
}
this.roboVac = new robovac_api_1.RoboVac(this.connectionConfig, this.updateCharacteristics.bind(this), this.cachingDuration, this.log);
this.log.info('Finished initializing accessory:', this.name);
}
/**
* Handle the "GET" requests from HomeKit
* These are sent when HomeKit wants to know the current state of the accessory, for example, checking if a Light bulb is on.
*/
async getRunning() {
this.log.debug(`getRunning for ${this.name}`);
try {
return await Promise.race([
this.roboVac.getRunning(),
new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Request timed out")), this.callbackTimeout);
})
]);
}
catch (_a) {
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
}
}
async getFindRobot() {
this.log.debug(`getFindRobot for ${this.name}`);
try {
return await Promise.race([
this.roboVac.getFindRobot(),
new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Request timed out")), this.callbackTimeout);
})
]);
}
catch (_a) {
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
}
}
async getLowBattery() {
this.log.debug(`getLowBattery for ${this.name}`);
try {
const battery_level = await Promise.race([
this.roboVac.getBatteryLevel(),
new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Request timed out")), this.callbackTimeout);
})
]);
return battery_level <= this.lowBatteryThreshold;
}
catch (_a) {
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
}
}
async getBatteryLevel() {
this.log.debug(`getBatteryLevel for ${this.name}`);
try {
return await Promise.race([
this.roboVac.getBatteryLevel(),
new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Request timed out")), this.callbackTimeout);
})
]);
}
catch (_a) {
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
}
}
async getCharging() {
this.log.debug(`getCharging for ${this.name}`);
try {
const work_status = await Promise.race([
this.roboVac.getWorkStatus(),
new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Request timed out")), this.callbackTimeout);
})
]);
return this.workStatusToChargingState(work_status);
}
catch (_a) {
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
}
}
async getErrorStatus() {
this.log.debug(`getErrorStatus for ${this.name}`);
try {
const error_code = await Promise.race([
this.roboVac.getErrorCode(),
new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Request timed out")), this.callbackTimeout);
})
]);
return error_code !== robovac_api_1.ErrorCode.NO_ERROR;
}
catch (_a) {
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
}
}
/**
* Handle "SET" requests from HomeKit
* These are sent when the user changes the state of an accessory, for example, turning on a Light bulb.
*/
async setRunning(state) {
this.log.debug(`setRunning for ${this.name} set to ${state}`);
if (!state && this.roboVac.getRunningCached() == false) {
// don't send additional "GO HOME" command when already off
this.log.debug(`setRunning for ${this.name} set to ${state} received, but not sending GO HOME command because according to cache the device is already off`);
return;
}
try {
return await Promise.race([
(state) ? this.roboVac.setPlayPause(true) : this.roboVac.setGoHome(true),
new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Request timed out")), this.callbackTimeout);
})
]);
}
catch (_a) {
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
}
}
async setFindRobot(state) {
this.log.debug(`setFindRobot for ${this.name} set to ${state}`);
try {
return await Promise.race([
this.roboVac.setFindRobot(state),
new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Request timed out")), this.callbackTimeout);
})
]);
}
catch (_a) {
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
}
}
/**
* Handle requests to set the "Identify" characteristic
*/
setIdentify(value) {
this.log.info('Triggered SET Identify:', value);
}
updateCharacteristics(statusResponse) {
this.log.debug(`updateCharacteristics for ${this.name}`);
let counter = 0;
if (statusResponse.dps[robovac_api_1.StatusDps.RUNNING] !== undefined) {
this.log.debug(`updating ${robovac_api_1.statusDpsFriendlyNames.get(robovac_api_1.StatusDps.RUNNING)} for ${this.name} to ${statusResponse.dps[robovac_api_1.StatusDps.RUNNING]}`);
this.vacuumService.updateCharacteristic(this.platform.Characteristic.On, statusResponse.dps[robovac_api_1.StatusDps.RUNNING]);
counter++;
}
if (this.findRobotService && statusResponse.dps[robovac_api_1.StatusDps.FIND_ROBOT] !== undefined) {
this.log.debug(`updating ${robovac_api_1.statusDpsFriendlyNames.get(robovac_api_1.StatusDps.FIND_ROBOT)} for ${this.name} to ${statusResponse.dps[robovac_api_1.StatusDps.FIND_ROBOT]}`);
this.findRobotService.updateCharacteristic(this.platform.Characteristic.On, statusResponse.dps[robovac_api_1.StatusDps.FIND_ROBOT]);
counter++;
}
if (this.batteryService) {
if (statusResponse.dps[robovac_api_1.StatusDps.BATTERY_LEVEL] !== undefined) {
this.log.debug(`updating ${robovac_api_1.statusDpsFriendlyNames.get(robovac_api_1.StatusDps.BATTERY_LEVEL)} for ${this.name} to ${statusResponse.dps[robovac_api_1.StatusDps.BATTERY_LEVEL]}`);
this.batteryService.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, statusResponse.dps[robovac_api_1.StatusDps.BATTERY_LEVEL] <= this.lowBatteryThreshold);
this.batteryService.updateCharacteristic(this.platform.Characteristic.BatteryLevel, statusResponse.dps[robovac_api_1.StatusDps.BATTERY_LEVEL]);
counter++;
}
if (statusResponse.dps[robovac_api_1.StatusDps.WORK_STATUS] !== undefined) {
this.log.debug(`updating ${robovac_api_1.statusDpsFriendlyNames.get(robovac_api_1.StatusDps.WORK_STATUS)} for ${this.name} to ${statusResponse.dps[robovac_api_1.StatusDps.WORK_STATUS]}`);
this.batteryService.updateCharacteristic(this.platform.Characteristic.ChargingState, this.workStatusToChargingState(statusResponse.dps[robovac_api_1.StatusDps.WORK_STATUS]));
counter++;
}
}
if (this.errorSensorService && statusResponse.dps[robovac_api_1.StatusDps.ERROR_CODE] !== undefined) {
const is_error = (statusResponse.dps[robovac_api_1.StatusDps.ERROR_CODE] !== robovac_api_1.ErrorCode.NO_ERROR);
this.log.debug(`updating Error Sensor status for ${this.name} to ${is_error}`);
this.errorSensorService.updateCharacteristic(this.platform.Characteristic.MotionDetected, is_error);
if (is_error)
this.log.info(`${this.name} reported a device error: ${(0, robovac_api_1.getErrorCodeFriendlyName)(statusResponse.dps[robovac_api_1.StatusDps.ERROR_CODE])}`);
counter++;
}
this.log.debug(`updateCharacteristics for ${this.name} complete - updated ${counter} characteristics.`);
}
workStatusToChargingState(workStatus) {
switch (workStatus) {
case robovac_api_1.WorkStatus.CHARGING:
case robovac_api_1.WorkStatus.CHARGING_COMPLETED:
return this.platform.Characteristic.ChargingState.CHARGING;
default:
return this.platform.Characteristic.ChargingState.NOT_CHARGING;
}
}
}
exports.EufyRobovacAccessory = EufyRobovacAccessory;
//# sourceMappingURL=accessory.js.map