UNPKG

homebridge-aeg-robot

Version:

AEG RX9 / Electrolux Pure i9 robot vacuum plugin for Homebridge

139 lines 6.38 kB
// Homebridge plugin for AEG RX 9 / Electrolux Pure i9 robot vacuum // Copyright © 2022-2023 Alexander Thoukydides import { formatList, formatMilliseconds, MS, plural } from './utils.js'; import { RX9BatteryStatus, RX9Dustbin, RX92PowerMode, RX9RobotStatus } from './aegapi-rx9-types.js'; // Descriptions of the robot activity const activityNames = { [RX9RobotStatus.Cleaning]: 'CLEANING', [RX9RobotStatus.PausedCleaning]: 'PAUSED during cleaning', [RX9RobotStatus.SpotCleaning]: 'SPOT CLEANING', [RX9RobotStatus.PausedSpotCleaning]: 'PAUSED during spot cleaning', [RX9RobotStatus.Return]: 'returning HOME', [RX9RobotStatus.PausedReturn]: 'PAUSED during return home', [RX9RobotStatus.ReturnForPitstop]: 'returning HOME; it will resume cleaning when charged', [RX9RobotStatus.PausedReturnForPitstop]: 'PAUSED during return home; it will resume cleaning when charged', [RX9RobotStatus.Charging]: 'CHARGING', [RX9RobotStatus.Sleeping]: 'SLEEPING (either charged on dock or idle off dock)', [RX9RobotStatus.Error]: 'in an ERROR state', [RX9RobotStatus.Pitstop]: 'CHARGING; it will resume cleaning when charged', [RX9RobotStatus.ManualSteering]: 'being STEERED MANUALLY', [RX9RobotStatus.FirmwareUpgrade]: 'performing a FIRMWARE UPGRADE' }; // Descriptions of the robot battery levels const batteryNames = { [RX9BatteryStatus.Dead]: 'DEAD', [RX9BatteryStatus.CriticalLow]: 'CRITICALLY LOW', [RX9BatteryStatus.Low]: 'LOW', [RX9BatteryStatus.Medium]: 'MEDIUM', [RX9BatteryStatus.High]: 'HIGH', [RX9BatteryStatus.FullyCharged]: 'FULLY CHARGED' }; // Descriptions of dustbin states const dustbinNames = { [RX9Dustbin.Unknown]: 'UNKNOWN', [RX9Dustbin.Present]: 'PRESENT (and not full)', [RX9Dustbin.Missing]: 'MISSING', [RX9Dustbin.Full]: 'FULL (and requires emptying)' }; // Descriptions of power modes const powerModeNames = { [RX92PowerMode.Quiet]: 'QUIET (lower energy consumption and quieter)', [RX92PowerMode.Smart]: 'SMART (cleans quietly on hard surfaces, uses full power on carpets)', [RX92PowerMode.Power]: 'POWER (optimal cleaning performance, higher energy consumption)' }; // Logging of information about a robot export class AEGRobotLog { robot; // Logger log; // Reported error messages loggedHealthErrors = new Set(); // Construct a robot logger constructor(robot) { this.robot = robot; this.log = robot.log; this.logOnce(); this.logStatus(); this.logMessages(); } // Log static information about the robot once at startup logOnce() { const redacted = !this.robot.config.debug.includes('Log Appliance IDs'); if (!redacted) this.log.info(`Product ID ${this.robot.applianceId}`); this.robot.once('info', () => { this.log.info(`${this.robot.brand} ${this.robot.model}`); this.log.info(`Product number code ${this.robot.pnc}`); if (!redacted) this.log.info(`Serial number ${this.robot.sn}`); this.log.info(`My name is "${this.robot.name}"`); }); } // Log initial values and changes for other status logStatus() { this.robot.on('capabilities', (capabilities) => { this.log.info(`Supports ${plural(capabilities.length, 'capability')}: ${formatList([...capabilities].sort())}`); }).on('hardware', (hardware) => { this.log.info(`Hardware platform ${hardware}`); }).on('firmware', (firmware) => { this.log.info(`Firmware version ${firmware} installed`); }).on('battery', (battery) => { if (battery === undefined) this.log.info('Unknown battery level'); else this.log.info(`Battery level is ${batteryNames[battery]}`); }).on('activity', (activity) => { if (activity === undefined) this.log.info('Unknown robot status'); else this.log.info(`Robot is ${activityNames[activity]}`); }).on('dustbin', (dustbin) => { if (dustbin === undefined) this.log.info('Unknown dustbin status'); else this.log.info(`Dust collection bin is ${dustbinNames[dustbin]}`); }).on('rawPower', (power) => { if (power === undefined) this.log.info('Unknown power mode'); else this.log.info(`Power mode is set to ${powerModeNames[power]}`); }).on('rawEco', (eco) => { if (eco === undefined) this.log.info('Unknown ECO mode'); else this.log.info(`ECO mode is ${eco ? 'enabled' : 'disabled'}`); }).on('enabled', (enabled) => { this.log.log(enabled ? "info" /* LogLevel.INFO */ : "warn" /* LogLevel.WARN */, `Robot is ${enabled ? 'enabled' : 'disabled'}`); }).on('connected', (connected) => { this.log.log(connected ? "info" /* LogLevel.INFO */ : "warn" /* LogLevel.WARN */, `Robot ${connected ? 'is' : 'is NOT'} connected to the cloud servers`); }).on('isError', (err) => { this.logHealth(err); }); } // Log changes to cloud server health logHealth(err) { if (err) { const message = err instanceof Error ? err.message : JSON.stringify(err); if (!this.loggedHealthErrors.has(message)) { this.loggedHealthErrors.add(message); this.log.error(`Lost connection to cloud servers: ${message}`); } } else { this.loggedHealthErrors.clear(); this.log.info('Successfully connected to cloud servers'); } } // Log messages from the robot logMessages() { this.robot.on('message', (message) => { const age = `${formatMilliseconds(Date.now() - message.timestamp * MS)} ago`; const bits = [`type=${message.type}`]; if (message.userErrorID) bits.push(`user-error=${message.userErrorID}`); if (message.internalErrorID) bits.push(`internal-error=${message.internalErrorID}`); this.log.warn(`Message: ${message.text} (${age})`); this.log.debug(`Message: ${formatList(bits)}`); }); } } //# sourceMappingURL=aeg-robot-log.js.map