homebridge-roborock-control
Version:
A Homebridge plugin to control Roborock vacuum cleaners.
122 lines • 5.68 kB
JavaScript
import { PyRoborockCmd, PyRoborockState } from '../roborock/roborock-api.js';
import { Log } from '../util/log.js';
import { PollingAccessory } from './pollingAccessory.js';
// Names of properties present in the DeviceState.
const kVacuumState = 'state';
const kFanPower = 'fan_power';
const kBatteryLevel = 'battery';
const kCleaning = 'is_cleaning';
const kLowBattery = 'is_low_battery';
const kCharging = 'is_charging';
/**
* An instance of this class is created for each Roborock accessory.
*/
export class RoborockAccessory extends PollingAccessory {
platform;
accessory;
rrClient;
static kLowBatteryPercent = 15;
batteryService;
fanService;
constructor(platform, accessory, rrClient) {
// Initialize the base class first, then initialize the services.
super(platform, accessory);
this.platform = platform;
this.accessory = accessory;
this.rrClient = rrClient;
//
// Create a Fan to control the main vacuum function.
//
this.fanService =
(this.accessory.getService(this.platform.Service.Fan) ||
this.accessory.addService(this.platform.Service.Fan));
//
// Add a service to report the battery level.
//
this.batteryService =
this.accessory.getService(this.platform.Service.Battery) ||
this.accessory.addService(this.platform.Service.Battery);
//
// Set up the handlers for each service and characteristic.
//
this.setupHandlers();
}
async setupHandlers() {
// Convenience references to Characteristic and Service.
const Characteristic = this.platform.Characteristic;
// Perform a refresh of the device to establish the initial state.
await this.refreshDeviceState();
// Register handlers for all dynamic characteristics.
this.fanService.getCharacteristic(Characteristic.On)
.onGet(() => this.getProperty(kCleaning))
.onSet((active) => this.setDeviceState(!!active));
this.batteryService.getCharacteristic(Characteristic.BatteryLevel)
.onGet(() => this.getProperty(kBatteryLevel));
this.batteryService.getCharacteristic(Characteristic.ChargingState)
.onGet(() => this.getProperty(kCharging));
this.batteryService.getCharacteristic(Characteristic.StatusLowBattery)
.onGet(() => this.getProperty(kLowBattery));
}
// Checks the given property and throws an exception if it is undefined. Used
// by getters to detect a communication failure.
getProperty(propertyName) {
const propertyValue = this.currentState()[propertyName];
if (propertyValue === undefined) {
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
}
return propertyValue;
}
// Update the vacuum's state and make sure the change is reflected in Homekit.
async setDeviceState(active) {
Log.debug(`Setting vacuum state to ${active ? 'active' : 'inactive'}`);
if (!await this.rrClient.sendCommand(active ? await PyRoborockCmd.APP_START :
await PyRoborockCmd.APP_CHARGE)) {
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
}
this.refreshDeviceState();
}
//
// Implementations of virtual superclass methods.
//
// Return true if services have been initialized.
servicesReady() {
return !!(this.fanService && this.batteryService);
}
// Obtain and return the device's current state.
async getDeviceState(lastState) {
// If we don't get a response, set the device state to empty.
const rawDeviceState = await this.rrClient.getStatus();
if (!rawDeviceState) {
Log.debug('No response from device, returning empty state');
return {};
}
// Otherwise, build the new device state from the response.
const devState = {
[kVacuumState]: await rawDeviceState[kVacuumState],
[kFanPower]: await rawDeviceState[kFanPower],
[kBatteryLevel]: await rawDeviceState[kBatteryLevel],
};
devState[kCleaning] =
(devState[kVacuumState] === await PyRoborockState.cleaning.valueOf());
devState[kLowBattery] =
(devState[kBatteryLevel] <= RoborockAccessory.kLowBatteryPercent);
devState[kCharging] =
(devState[kVacuumState] === await PyRoborockState.charging.valueOf());
return devState;
}
// Push the current state to Homekit.
async updateHomekitState(currentState) {
// Don't try to push an update if the device state is empty. This signifies
// that the most recent attempt to retrieve the device state failed.
if (JSON.stringify(currentState) === JSON.stringify({})) {
Log.debug('Empty device state, skipping Homekit push update');
return;
}
// Otherwise, update each of our services.
this.fanService.updateCharacteristic(this.platform.Characteristic.On, currentState[kCleaning]);
this.batteryService.updateCharacteristic(this.platform.Characteristic.BatteryLevel, currentState[kBatteryLevel]);
this.batteryService.updateCharacteristic(this.platform.Characteristic.StatusLowBattery, currentState[kLowBattery]);
this.batteryService.updateCharacteristic(this.platform.Characteristic.ChargingState, currentState[kCharging]);
}
}
//# sourceMappingURL=roborockAccessory.js.map