UNPKG

matterbridge-aeg-robot

Version:

A Matterbridge plugin that connects AEG RX 9 / Electrolux Pure i9 robot vacuums to the Matter smart home ecosystem.

135 lines 7.04 kB
// Matterbridge plugin for AEG RX9 / Electrolux Pure i9 robot vacuum // Copyright © 2025 Alexander Thoukydides import { formatList, formatMilliseconds, MS, plural } from './utils.js'; import { RX9BatteryStatus, RX9Dustbin, RX92PowerMode, RX9RobotStatus } from './aegapi-rx9-types.js'; import { RR, RT, RV } from './logger.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]: { value: 'UNKNOWN' }, [RX9Dustbin.Present]: { value: 'PRESENT', extra: 'not full' }, [RX9Dustbin.Missing]: { value: 'MISSING' }, [RX9Dustbin.Full]: { value: 'FULL', extra: 'requires emptying' } }; // Descriptions of power modes const powerModeNames = { [RX92PowerMode.Quiet]: { value: 'QUIET', extra: 'lower energy consumption and quieter' }, [RX92PowerMode.Smart]: { value: 'SMART', extra: 'cleans quietly on hard surfaces, uses full power on carpets' }, [RX92PowerMode.Power]: { value: 'POWER', extra: 'optimal cleaning performance, higher energy consumption' } }; // Logging of information about a robot export class AEGApplianceRX9Log { appliance; // Logger log; // Reported error messages loggedHealthErrors = new Set(); // Construct a robot logger constructor(appliance) { this.appliance = appliance; // Use the appliance's logger this.log = appliance.log; // Start logging information about the robot this.logStatic(); this.logStatus(); this.logMessages(); } // Log static information about the robot once at startup logStatic() { const { applianceType, brand, capabilities, colour, model, variant } = this.appliance; const redacted = !this.appliance.config.debugFeatures.includes('Log Appliance IDs'); this.log.info(`${RT}My name is "${RV}${this.appliance.applianceName}${RT}"${RR}`); this.log.info(`${RV}${brand} ${applianceType}${RT} (${RV}${model}${RT}/${RV}${variant}${RT}) ${RV}${colour}${RR}`); if (!redacted) this.log.info(`${RT}Product ID ${RV}${this.appliance.applianceId}${RR}`); this.log.info(`${RT}Product number code ${RV}${this.appliance.pnc}${RR}`); if (!redacted) this.log.info(`${RT}Serial number ${RV}${this.appliance.serialNumber}${RR}`); this.log.info(`${RT}Hardware platform ${RV}${this.appliance.platform}${RR}`); const capabilityValues = [...capabilities].sort().map(c => `${RV}${c}${RT}`); this.log.info(`${RT}Supports ${plural(capabilities.length, 'capability')}: ${formatList(capabilityValues)}${RR}`); } // Log initial values and changes for other status logStatus() { this.appliance.on('firmwareVersion', (firmwareVersion) => { this.log.info(`${RT}Firmware version ${RV}${firmwareVersion}${RT} installed${RR}`); }).on('batteryStatus', (batteryStatus) => { this.log.info(`${RT}Battery level is ${RV}${batteryNames[batteryStatus]}${RR}`); }).on('robotStatus', (robotStatus) => { this.log.info(`${RT}Robot is ${RV}${activityNames[robotStatus]}${RR}`); }).on('fauxStatus', (fauxStatus) => { if (fauxStatus === this.appliance.state.robotStatus) return; this.log.info(`${RT}Predicting robot will be ${RV}${activityNames[fauxStatus]}${RR}`); }).on('dustbinStatus', (dustbinStatus) => { const { value, extra } = dustbinNames[dustbinStatus]; this.log.info(`${RT}Dust collection bin is ${RV}${value}${extra ? `${RT} and ${extra}` : ''}${RR}`); }).on('powerMode', (powerMode) => { if (powerMode === undefined) return; const { value, extra } = powerModeNames[powerMode]; this.log.info(`${RT}Power mode is set to ${RV}${value}${RT} (${extra})${RR}`); }).on('ecoMode', (ecoMode) => { if (ecoMode === undefined) return; this.log.info(`${RT}ECO mode is ${RV}${ecoMode ? 'enabled' : 'disabled'}${RR}`); }).on('enabled', (enabled) => { this.log.log(enabled ? "info" /* LogLevel.INFO */ : "warn" /* LogLevel.WARN */, `${RT}Robot is ${RV}${enabled ? 'enabled' : 'disabled'}${RR}`); }).on('connected', (connected) => { this.log.log(connected ? "info" /* LogLevel.INFO */ : "warn" /* LogLevel.WARN */, `${RT}Robot ${RV}${connected ? 'is' : 'is NOT'}${RT} connected to the cloud servers${RR}`); }).on('apiError', (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(`${RT}Successfully connected to cloud servers${RR}`); } } // Log messages from the robot logMessages() { this.appliance.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-appliance-log-rx9.js.map