homebridge-roborock-vacuum-update
Version:
Comprehensive Homebridge plugin for Roborock vacuum cleaners with full HomeKit integration including mopping, dock features, and advanced controls.
176 lines • 8.92 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const vacuum_accessory_1 = __importDefault(require("./vacuum_accessory"));
const logger_1 = __importDefault(require("./logger"));
const settings_1 = require("./settings");
const Roborock = require("../roborockLib/roborockAPI").Roborock;
/**
* Roborock App Platform Plugin for Homebridge
* Based on https://github.com/homebridge/homebridge-plugin-template
*/
class RoborockPlatform {
/**
* This constructor is where you should parse the user config
* and discover/register accessories with Homebridge.
*
* @param logger Homebridge logger
* @param config Homebridge platform config
* @param api Homebridge API
*/
constructor(homebridgeLogger, config, api) {
// Used to track restored cached accessories
this.accessories = [];
this.vacuums = [];
this.api = api;
this.Service = this.api.hap.Service;
this.Characteristic = this.api.hap.Characteristic;
this.platformConfig = config;
// Initialise logging utility
this.log = new logger_1.default(homebridgeLogger, this.platformConfig.debugMode);
// Create Roborock App communication module
const username = this.platformConfig.email;
const password = this.platformConfig.password;
const baseURL = this.platformConfig.baseURL;
const debugMode = this.platformConfig.debugMode;
this.roborockAPI = new Roborock({ username: username, password: password, debug: debugMode, baseURL: baseURL, log: this.log });
/**
* When this event is fired it means Homebridge has restored all cached accessories from disk.
* Dynamic Platform plugins should only register new accessories after this event was fired,
* in order to ensure they weren't added to homebridge already. This event can also be used
* to start discovery of new accessories.
*/
this.api.on("didFinishLaunching" /* APIEvent.DID_FINISH_LAUNCHING */, () => {
this.log.debug('Finished launching and restored cached accessories.');
this.configurePlugin();
});
this.api.on("shutdown" /* APIEvent.SHUTDOWN */, () => {
this.log.debug('Shutting down...');
if (this.roborockAPI) {
this.roborockAPI.stopService();
}
});
}
async configurePlugin() {
await this.loginAndDiscoverDevices();
}
async loginAndDiscoverDevices() {
if (!this.platformConfig.email) {
this.log.error('Email is not configured - aborting plugin start. '
+ 'Please set the field `email` in your config and restart Homebridge.');
return;
}
if (!this.platformConfig.password) {
this.log.error('Password is not configured - aborting plugin start. '
+ 'Please set the field `password` in your config and restart Homebridge.');
return;
}
const self = this;
self.roborockAPI.setDeviceNotify(function (id, homeData) {
self.log.debug(`${id} notifyDeviceUpdater:${JSON.stringify(homeData)}`);
for (const vacuum of self.vacuums) {
vacuum.notifyDeviceUpdater(id, homeData);
}
});
self.roborockAPI.startService(function () {
self.log.info("Service started");
//call the discoverDevices function
self.discoverDevices();
});
}
/**
* This function is invoked when Homebridge restores cached accessories from disk at startup.
* It should be used to set up event handlers for characteristics and update respective values.
*/
configureAccessory(accessory) {
this.log.info(`Loading accessory '${accessory.displayName}' from cache.`);
// Store restored accessory in the cached accessories list
// remove duplicates accessories
try {
const existingAccessory = this.accessories.find(a => a.UUID === accessory.UUID);
if (existingAccessory) {
this.log.info(`Removing duplicate accessory '${existingAccessory.displayName}' from cache.`);
this.api.unregisterPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [existingAccessory]);
}
}
catch (e) {
this.log.error("Error loading accessory from cache: " + e);
}
this.accessories.push(accessory);
}
isSupportedDevice(model) {
//model nust starts with "roborock.vacuum."
return model.startsWith("roborock.vacuum.");
}
/**
* Fetches all of the user's devices from Roborock App and sets up handlers.
*
* Accessories must only be registered once. Previously created accessories
* must not be registered again to prevent "duplicate UUID" errors.
*/
async discoverDevices() {
this.log.info('Discovering vacuum devices...');
try {
const self = this;
if (self.roborockAPI.isInited()) {
self.roborockAPI.getVacuumList().forEach(function (device) {
var duid = device.duid;
var name = device.name;
var model = self.roborockAPI.getProductAttribute(duid, "model");
if (!self.isSupportedDevice(model)) {
self.log.info(`Device '${name}' (${duid}) is not supported.`);
return;
}
const uuid = self.api.hap.uuid.generate(device.duid);
const existingAccessory = self.accessories.find(accessory => accessory.UUID === uuid);
if (existingAccessory !== undefined) {
self.log.info(`Restoring accessory '${existingAccessory.displayName}' ` + `(${uuid}) from cache.`);
// If you need to update the accessory.context then you should run
// `api.updatePlatformAccessories`. eg.:
existingAccessory.context = duid;
self.api.updatePlatformAccessories([existingAccessory]);
// Create the accessory handler for the restored accessory
self.createRoborockAccessory(existingAccessory);
}
else {
// The accessory already exists, so we need to create it
self.log.info(`Adding accessory '${name}' (${uuid}).`);
// The accessory does not yet exist, so we need to create it
const accessory = new self.api.platformAccessory(name, uuid);
// Store a copy of the device object in the `accessory.context` property,
// which can be used to store any data about the accessory you may need.
accessory.context = duid;
// Create the accessory handler for the newly create accessory
// this is imported from `platformAccessory.ts`
self.createRoborockAccessory(accessory);
// Link the accessory to your platform
self.api.registerPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [accessory]);
}
});
}
// At this point, we set up all devices from Roborock App, but we did not unregister
// cached devices that do not exist on the Roborock App account anymore.
for (const cachedAccessory of this.accessories) {
if (cachedAccessory.context) {
const vacuum = self.roborockAPI.getVacuumDeviceData(cachedAccessory.context);
if (vacuum === undefined) {
// This cached devices does not exist on the Roborock App account (anymore).
this.log.info(`Removing accessory '${cachedAccessory.displayName}' (${cachedAccessory.context}) ` + 'because it does not exist on the Roborock account anymore.');
this.api.unregisterPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [cachedAccessory]);
}
}
}
}
catch (error) {
this.log.error('An error occurred during device discovery. ' + 'Turn on debug mode for more information.');
this.log.debug(error);
}
}
createRoborockAccessory(accessory) {
this.vacuums.push(new vacuum_accessory_1.default(this, accessory));
}
}
exports.default = RoborockPlatform;
//# sourceMappingURL=platform.js.map