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.
130 lines (126 loc) • 6.71 kB
JavaScript
// Matterbridge plugin for Dyson robot vacuum and air treatment devices
// Copyright © 2025-2026 Alexander Thoukydides
import { BasicInformation } from 'matterbridge/matter/clusters';
import { RvcCleanMode360 } from './endpoint-360-behavior.js';
import { Dyson360CleaningStrategy, Dyson360EyePowerMode, Dyson360HeuristPowerMode, Dyson360TimelineEvent } from './dyson-360-types.js';
import { DysonDevice360Base } from './dyson-device-360-base.js';
import { DysonDevice360ZonesMixin } from './dyson-device-360-zones.js';
import { dysonRenderMap360Eye, dysonRenderMap360VisNav } from './dyson-device-360-map.js';
/* eslint-disable max-len */
// Common instructions for compatibility warning messages
const DYSON360_COMPATIBILITY_COMMON = `If you are willing to help add support, please follow the detailed instructions in the project README.md for contributing opendyson logs and Proxyman API traces:
https://github.com/thoukydides/matterbridge-dyson-robot#reporting-issues
(expand the "Reporting Issues with Unsupported or Recently Released Products" section)`;
// Device-specific compatibility warning messages
const DYSON360_COMPATIBILITY_HEURIST = `Support for Dyson 360 Heurist (RB02) is incomplete.
The device is currently treated similarly to a Dyson 360 Eye, with partial updates for known MQTT differences. There is a high likelihood of warnings, errors, or missing functionality. Mapping and zone cleaning are not currently supported.
${DYSON360_COMPATIBILITY_COMMON}`;
const DYSON360_COMPATIBILITY_SPOTSCRUB = `Dyson Spot+Scrub Ai (RB05) is not currently supported by this plugin.
Initialisation is expected to fail, and no functionality is implemented for this model. This is not a regression and does not indicate a configuration error.
${DYSON360_COMPATIBILITY_COMMON}`;
/* eslint-enable max-len */
// A Dyson 360 Eye device
export class DysonDevice360Eye extends DysonDevice360Base {
static model = { type: 'N223', number: 'RB01', name: '360 Eye' };
getBatteryPartNumber = () => '968734-02';
getProductAppearance = () => ({
finish: BasicInformation.ProductFinish.Polished,
primaryColor: BasicInformation.Color.Nickel // (or Fuchsia for Japan limited edition)
});
getPowerLevelMaps = () => [
[Dyson360EyePowerMode.Quiet, RvcCleanMode360.Quiet, 'Quiet'],
[Dyson360EyePowerMode.Max, RvcCleanMode360.MaxBoost, 'Max']
];
setPowerLevel = (powerLevel) => this.mqtt.commandSetPowerMode(powerLevel);
getPowerLevel = () => this.mqtt.status.defaultVacuumPowerMode;
// Retrieve details of a completed clean
async getCompletedClean(cleanId) {
const { logMapStyle } = this.config;
if (!this.api || logMapStyle === 'Off')
return 'Unavailable';
// Retrieve details of the specified (or most recent) clean
const history = await this.api.getCleaningHistory360();
const clean = history.Entries.find(entry => entry.Clean === cleanId);
if (!clean)
return 'Not found';
if (clean.IsInterim)
return 'Not ready';
const map = await this.api.getMapImage360(cleanId);
// Render the Map
return dysonRenderMap360Eye(this.log, logMapStyle, clean, map);
}
}
// A Dyson 360 Heurist device
export class DysonDevice360Heurist extends DysonDevice360Base {
static model = { type: '276', number: 'RB02', name: '360 Heurist' };
getBatteryPartNumber = () => '970049-01';
getProductAppearance = () => ({
finish: BasicInformation.ProductFinish.Satin,
primaryColor: BasicInformation.Color.Blue
});
getPowerLevelMaps = () => [
[Dyson360HeuristPowerMode.Quiet, RvcCleanMode360.Quiet, 'Quiet'],
[Dyson360HeuristPowerMode.High, RvcCleanMode360.High, 'High'],
[Dyson360HeuristPowerMode.Max, RvcCleanMode360.MaxBoost, 'Max']
];
setPowerLevel = (powerLevel) => this.mqtt.commandSetPowerMode(powerLevel);
getPowerLevel = () => this.mqtt.status.defaultVacuumPowerMode;
get compatibilityWarning() { return DYSON360_COMPATIBILITY_HEURIST; }
}
// A Dyson 360 Vis Nav device
export class DysonDevice360VisNav extends DysonDevice360ZonesMixin(DysonDevice360Base) {
static model = { type: '277', number: 'RB03', name: '360 Vis Nav' };
getBatteryPartNumber = () => '967864-02';
getProductAppearance = () => ({
finish: BasicInformation.ProductFinish.Satin,
primaryColor: BasicInformation.Color.Blue
});
getPowerLevelMaps = () => [
[Dyson360CleaningStrategy.Auto, RvcCleanMode360.Auto, 'Auto'],
[Dyson360CleaningStrategy.Quick, RvcCleanMode360.Quick, 'Quick'],
[Dyson360CleaningStrategy.Quiet, RvcCleanMode360.Quiet, 'Quiet'],
[Dyson360CleaningStrategy.Boost, RvcCleanMode360.MaxBoost, 'Boost']
];
setPowerLevel = (powerLevel) => this.mqtt.commandSetCleaningStrategy(powerLevel);
getPowerLevel = () => this.mqtt.status.defaultCleaningStrategy;
// Retrieve details of a completed clean
async getCompletedClean(cleanId) {
const { logMapStyle } = this.config;
if (!this.api || logMapStyle === 'Off')
return 'Unavailable';
// Retrieve details of the specified (or most recent) clean
const history = await this.api.getCleanMaps360();
const clean = history.find(entry => entry.cleanId === cleanId);
if (!clean)
return 'Not found';
const interim = clean.cleanTimeline.at(-1)?.eventName !== Dyson360TimelineEvent.RunEnded;
if (interim)
return 'Not ready';
let persistentMap;
if (clean.persistentMap)
persistentMap = await this.api.getPersistentMap360(clean.persistentMap.id);
// Render the map
return dysonRenderMap360VisNav(this.log, logMapStyle, clean, persistentMap);
}
}
// A Dyson 360 Spot+Scrub device
export class DysonDevice360SpotScrub extends DysonDevice360Base {
static model = { type: 'RB05', number: 'RB05', name: 'Spot+Scrub Ai' };
getBatteryPartNumber = () => '975571-01';
getProductAppearance = () => ({
finish: BasicInformation.ProductFinish.Matte,
primaryColor: BasicInformation.Color.Black
});
getPowerLevelMaps = () => [];
setPowerLevel = (powerLevel) => this.mqtt.commandSetPowerMode(powerLevel);
getPowerLevel = () => this.mqtt.status.defaultVacuumPowerMode;
get compatibilityWarning() { return DYSON360_COMPATIBILITY_SPOTSCRUB; }
}
// List of constructors for Dyson robot vacuum devices
export const DYSON_DEVICE_TYPES_360 = [
DysonDevice360Eye,
DysonDevice360Heurist,
DysonDevice360VisNav,
DysonDevice360SpotScrub
];
//# sourceMappingURL=dyson-device-360.js.map