matterbridge-dyson-robot
Version:
A Matterbridge plugin that connects Dyson robot vacuums and air treatment devices to the Matter smart home ecosystem via their local or cloud MQTT APIs.
156 lines • 6.55 kB
JavaScript
// Matterbridge plugin for Dyson robot vacuum and air treatment devices
// Copyright © 2025 Alexander Thoukydides
import { Behavior } from 'matterbridge/matter';
import { ClusterModel, FieldElement } from 'matterbridge/matter';
import { RvcOperationalState } from 'matterbridge/matter/clusters';
import { RvcCleanModeBehavior, RvcOperationalStateBehavior, RvcRunModeBehavior } from 'matterbridge/matter/behaviors';
import { ChangeToModeError, RvcOperationalStateError } from './error-360.js';
import { assertIsDefined, assertIsInstanceOf, logError } from './utils.js';
// Robot Vacuum Cleaner Run Mode cluster modes
export var RvcRunMode360;
(function (RvcRunMode360) {
RvcRunMode360[RvcRunMode360["Idle"] = 0] = "Idle";
RvcRunMode360[RvcRunMode360["Cleaning"] = 1] = "Cleaning";
RvcRunMode360[RvcRunMode360["Mapping"] = 2] = "Mapping";
})(RvcRunMode360 || (RvcRunMode360 = {}));
// Robot Vacuum Cleaner Clean Mode cluster modes
export var RvcCleanMode360;
(function (RvcCleanMode360) {
RvcCleanMode360[RvcCleanMode360["Quiet"] = 0] = "Quiet";
RvcCleanMode360[RvcCleanMode360["Quick"] = 1] = "Quick";
RvcCleanMode360[RvcCleanMode360["High"] = 2] = "High";
RvcCleanMode360[RvcCleanMode360["MaxBoost"] = 3] = "MaxBoost";
RvcCleanMode360[RvcCleanMode360["Auto"] = 4] = "Auto"; // Vis Nav: Auto
})(RvcCleanMode360 || (RvcCleanMode360 = {}));
// OperationalStatus manufacturer error
export const VENDOR_ERROR_360 = 0x80;
// Command handling behaviour for the endpoint
export class BehaviorDevice360 {
log;
// Registered command handlers
commands = {};
// Construct new command handling behaviour
constructor(log) {
this.log = log;
}
// Set a command handler
setCommandHandler(command, handler) {
if (this.commands[command])
throw new Error(`Handler already registered for command ${command}`);
this.commands[command] = handler;
}
// Execute a command handler
async executeCommand(command, ...args) {
const handler = this.commands[command];
if (!handler)
throw new Error(`${command} not implemented`);
await handler(...args);
}
}
export class Behavior360 extends Behavior {
static id = 'dyson-rvc';
}
// eslint-disable-next-line @typescript-eslint/no-namespace
(function (Behavior360) {
class State {
device;
}
Behavior360.State = State;
})(Behavior360 || (Behavior360 = {}));
// Implement command handlers for the RVC Run Mode cluster
export class RvcRunModeServer360 extends RvcRunModeBehavior {
// ChangeToMode command handler
async changeToMode({ newMode }) {
const { device } = this.agent.get(Behavior360).state;
const { log } = device;
try {
// Check whether it is a valid request
log.debug(`RVC Run Mode command: ChangeToMode ${newMode}...`);
const supported = this.state.supportedModes.some(({ mode }) => mode === newMode);
if (!supported)
throw new ChangeToModeError.UnsupportedMode;
// Attempt to change the mode
await device.executeCommand('ChangeRunMode', newMode);
// Success
log.debug(`RVC Run Mode command: ChangeToMode ${newMode} - OK`);
return ChangeToModeError.toResponse();
}
catch (err) {
logError(log, 'RVC Run Mode ChangeToMode', err);
return ChangeToModeError.toResponse(err);
}
}
}
// Implement command handlers for the RVC Clean Mode cluster
export class RvcCleanModeServer360 extends RvcCleanModeBehavior {
// ChangeToMode command handler
async changeToMode({ newMode }) {
const { device } = this.agent.get(Behavior360).state;
const { log } = device;
try {
// Check whether it is a valid request
log.debug(`RVC Clean Mode command: ChangeToMode ${newMode}...`);
const supported = this.state.supportedModes.some(({ mode }) => mode === newMode);
if (!supported)
throw new ChangeToModeError.UnsupportedMode;
// Attempt to change the mode
await device.executeCommand('ChangeCleanMode', newMode);
// Success
log.debug(`RVC Clean Mode command: ChangeToMode ${newMode} - OK`);
return ChangeToModeError.toResponse();
}
catch (err) {
logError(log, 'RVC Clean Mode ChangeToMode', err);
return ChangeToModeError.toResponse(err);
}
}
}
// Implement command handlers for the RVC Operational State cluster
export class RvcOperationalStateServer360 extends RvcOperationalStateBehavior {
static {
const schema = RvcOperationalStateServer360.schema;
assertIsInstanceOf(schema, ClusterModel);
const extendEnum = (name, values) => {
const element = schema.datatypes.find(e => e.name === name);
assertIsDefined(element);
element.children = [...element.children, ...values];
};
// Add a manufacturer-specific ErrorState value
extendEnum('ErrorStateEnum', [
FieldElement({
name: 'OtherError',
id: VENDOR_ERROR_360,
conformance: 'O',
description: 'The device has an error that is not covered by the Matter-defined error states'
})
]);
}
// Common command handler
async command(command, defaultErrorId) {
const { device } = this.agent.get(Behavior360).state;
const { log } = device;
try {
log.debug(`RVC Operational State command: ${command}...`);
await device.executeCommand(command);
log.debug(`RVC Operational State command: ${command} - OK`);
return RvcOperationalStateError.toResponse();
}
catch (err) {
logError(log, `RVC Operational State ${command}`, err);
return RvcOperationalStateError.toResponse(err, defaultErrorId);
}
}
// Pause command handler
pause() {
return this.command('Pause', RvcOperationalState.ErrorState.CommandInvalidInState);
}
// Resume command handler
resume() {
return this.command('Resume', RvcOperationalState.ErrorState.UnableToStartOrResume);
}
// GoHome command handler
goHome() {
return this.command('GoHome', RvcOperationalState.ErrorState.CommandInvalidInState);
}
}
//# sourceMappingURL=endpoint-360-behavior.js.map