feat(vendor.dreame): Add support for the STYTJO6ZHM

This commit is contained in:
Sören Beye 2022-05-01 16:52:19 +02:00
parent deca9e8bf3
commit 519c5b4a43
16 changed files with 588 additions and 27 deletions

View File

@ -3,6 +3,7 @@ const DreameGen2LidarValetudoRobot = require("./DreameGen2LidarValetudoRobot");
const DreameGen2ValetudoRobot = require("./DreameGen2ValetudoRobot");
const DreameValetudoRobot = require("./DreameValetudoRobot");
const MiioValetudoRobot = require("../MiioValetudoRobot");
const ValetudoSelectionPreset = require("../../entities/core/ValetudoSelectionPreset");
class DreameD9ProValetudoRobot extends DreameGen2LidarValetudoRobot {
/**
@ -14,6 +15,21 @@ class DreameD9ProValetudoRobot extends DreameGen2LidarValetudoRobot {
constructor(options) {
super(options);
this.registerCapability(new capabilities.DreameCarpetModeControlCapability({
robot: this,
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.CARPET_MODE.PIID
}));
this.registerCapability(new capabilities.DreameWaterUsageControlCapability({
robot: this,
presets: Object.keys(DreameValetudoRobot.WATER_GRADES).map(k => {
return new ValetudoSelectionPreset({name: k, value: DreameValetudoRobot.WATER_GRADES[k]});
}),
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.WATER_USAGE.PIID
}));
this.registerCapability(new capabilities.DreameConsumableMonitoringCapability({
robot: this,
miot_properties: {

View File

@ -3,6 +3,7 @@ const DreameGen2LidarValetudoRobot = require("./DreameGen2LidarValetudoRobot");
const DreameGen2ValetudoRobot = require("./DreameGen2ValetudoRobot");
const DreameValetudoRobot = require("./DreameValetudoRobot");
const MiioValetudoRobot = require("../MiioValetudoRobot");
const ValetudoSelectionPreset = require("../../entities/core/ValetudoSelectionPreset");
class DreameD9ValetudoRobot extends DreameGen2LidarValetudoRobot {
/**
@ -14,6 +15,21 @@ class DreameD9ValetudoRobot extends DreameGen2LidarValetudoRobot {
constructor(options) {
super(options);
this.registerCapability(new capabilities.DreameCarpetModeControlCapability({
robot: this,
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.CARPET_MODE.PIID
}));
this.registerCapability(new capabilities.DreameWaterUsageControlCapability({
robot: this,
presets: Object.keys(DreameValetudoRobot.WATER_GRADES).map(k => {
return new ValetudoSelectionPreset({name: k, value: DreameValetudoRobot.WATER_GRADES[k]});
}),
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.WATER_USAGE.PIID
}));
this.registerCapability(new capabilities.DreameConsumableMonitoringCapability({
robot: this,
miot_properties: {

View File

@ -1,6 +1,9 @@
const DreameGen2ValetudoRobot = require("./DreameGen2ValetudoRobot");
const capabilities = require("./capabilities");
const DreameValetudoRobot = require("./DreameValetudoRobot");
const ValetudoSelectionPreset = require("../../entities/core/ValetudoSelectionPreset");
class DreameGen2VSlamValetudoRobot extends DreameGen2ValetudoRobot {
constructor(options) {
@ -12,6 +15,21 @@ class DreameGen2VSlamValetudoRobot extends DreameGen2ValetudoRobot {
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.PERSISTENT_MAPS.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.PERSISTENT_MAPS.PROPERTIES.ENABLED.PIID
}));
this.registerCapability(new capabilities.DreameWaterUsageControlCapability({
robot: this,
presets: Object.keys(DreameValetudoRobot.WATER_GRADES).map(k => {
return new ValetudoSelectionPreset({name: k, value: DreameValetudoRobot.WATER_GRADES[k]});
}),
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.WATER_USAGE.PIID
}));
this.registerCapability(new capabilities.DreameCarpetModeControlCapability({
robot: this,
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.CARPET_MODE.PIID
}));
}
}

View File

@ -37,6 +37,15 @@ class DreameGen2ValetudoRobot extends DreameValetudoRobot {
)
);
/** @type {Array<{siid: number, piid: number, did: number}>} */
this.statePropertiesToPoll = this.getStatePropertiesToPoll().map(e => {
return {
siid: e.siid,
piid: e.piid,
did: this.deviceId
};
});
this.lastMapPoll = new Date(0);
this.mode = 0; //Idle
@ -75,15 +84,6 @@ class DreameGen2ValetudoRobot extends DreameValetudoRobot {
piid: MIOT_SERVICES.VACUUM_2.PROPERTIES.FAN_SPEED.PIID
}));
this.registerCapability(new capabilities.DreameWaterUsageControlCapability({
robot: this,
presets: Object.keys(DreameValetudoRobot.WATER_GRADES).map(k => {
return new ValetudoSelectionPreset({name: k, value: DreameValetudoRobot.WATER_GRADES[k]});
}),
siid: MIOT_SERVICES.VACUUM_2.SIID,
piid: MIOT_SERVICES.VACUUM_2.PROPERTIES.WATER_USAGE.PIID
}));
this.registerCapability(new capabilities.DreameLocateCapability({
robot: this,
siid: MIOT_SERVICES.AUDIO.SIID,
@ -225,12 +225,6 @@ class DreameGen2ValetudoRobot extends DreameValetudoRobot {
install_voicepack_piid: MIOT_SERVICES.AUDIO.PROPERTIES.INSTALL_VOICEPACK.PIID
}));
this.registerCapability(new capabilities.DreameCarpetModeControlCapability({
robot: this,
siid: MIOT_SERVICES.VACUUM_2.SIID,
piid: MIOT_SERVICES.VACUUM_2.PROPERTIES.CARPET_MODE.PIID
}));
this.registerCapability(new capabilities.DreamePendingMapChangeHandlingCapability({
robot: this,
miot_actions: {
@ -349,6 +343,7 @@ class DreameGen2ValetudoRobot extends DreameValetudoRobot {
case MIOT_SERVICES.SIDE_BRUSH.SIID:
case MIOT_SERVICES.FILTER.SIID:
case MIOT_SERVICES.SENSOR.SIID:
case MIOT_SERVICES.MOP.SIID:
this.parseAndUpdateState([e]);
break;
case MIOT_SERVICES.DEVICE.SIID:
@ -390,8 +385,13 @@ class DreameGen2ValetudoRobot extends DreameValetudoRobot {
return false;
}
async pollState() {
const response = await this.sendCommand("get_properties", [
/**
* May be extended by children
*
* @return {Array<{piid: number, siid: number}>}
*/
getStatePropertiesToPoll() {
return [
{
siid: MIOT_SERVICES.VACUUM_2.SIID,
piid: MIOT_SERVICES.VACUUM_2.PROPERTIES.MODE.PIID
@ -424,11 +424,14 @@ class DreameGen2ValetudoRobot extends DreameValetudoRobot {
siid: MIOT_SERVICES.BATTERY.SIID,
piid: MIOT_SERVICES.BATTERY.PROPERTIES.CHARGING.PIID
}
].map(e => {
e.did = this.deviceId;
];
}
return e;
}));
async pollState() {
const response = await this.sendCommand(
"get_properties",
this.statePropertiesToPoll
);
if (response) {
this.parseAndUpdateState(response);
@ -520,6 +523,7 @@ class DreameGen2ValetudoRobot extends DreameValetudoRobot {
case MIOT_SERVICES.VACUUM_2.PROPERTIES.CARPET_MODE.PIID:
case MIOT_SERVICES.VACUUM_2.PROPERTIES.KEY_LOCK.PIID:
case MIOT_SERVICES.VACUUM_2.PROPERTIES.OBSTACLE_AVOIDANCE.PIID:
case MIOT_SERVICES.VACUUM_2.PROPERTIES.MOP_DOCK_STATE.PIID:
//ignored for now
break;
@ -552,6 +556,7 @@ class DreameGen2ValetudoRobot extends DreameValetudoRobot {
case MIOT_SERVICES.SIDE_BRUSH.SIID:
case MIOT_SERVICES.FILTER.SIID:
case MIOT_SERVICES.SENSOR.SIID:
case MIOT_SERVICES.MOP.SIID:
if (this.capabilities[ConsumableMonitoringCapability.TYPE]) {
this.capabilities[ConsumableMonitoringCapability.TYPE].parseConsumablesMessage(elem);
}

View File

@ -5,6 +5,7 @@ const DreameQuirkFactory = require("./DreameQuirkFactory");
const DreameValetudoRobot = require("./DreameValetudoRobot");
const MiioValetudoRobot = require("../MiioValetudoRobot");
const QuirksCapability = require("../../core/capabilities/QuirksCapability");
const ValetudoSelectionPreset = require("../../entities/core/ValetudoSelectionPreset");
class DreameL10ProValetudoRobot extends DreameGen2LidarValetudoRobot {
/**
@ -20,6 +21,21 @@ class DreameL10ProValetudoRobot extends DreameGen2LidarValetudoRobot {
robot: this
});
this.registerCapability(new capabilities.DreameCarpetModeControlCapability({
robot: this,
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.CARPET_MODE.PIID
}));
this.registerCapability(new capabilities.DreameWaterUsageControlCapability({
robot: this,
presets: Object.keys(DreameValetudoRobot.WATER_GRADES).map(k => {
return new ValetudoSelectionPreset({name: k, value: DreameValetudoRobot.WATER_GRADES[k]});
}),
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.WATER_USAGE.PIID
}));
this.registerCapability(new capabilities.DreameConsumableMonitoringCapability({
robot: this,
miot_properties: {

View File

@ -310,6 +310,12 @@ module.exports = {
OBSTACLE_AVOIDANCE: {
PIID: 21
},
MOP_DOCK_SETTINGS: {
PIID: 23
},
MOP_DOCK_STATE: {
PIID: 25
},
KEY_LOCK: {
PIID: 27
},
@ -448,6 +454,22 @@ module.exports = {
}
}
},
MOP: {
SIID: 18,
PROPERTIES: {
TIME_LEFT: { //Hours
PIID: 2
},
PERCENT_LEFT: {
PIID: 1
}
},
ACTIONS: {
RESET: {
AIID: 1
}
}
},
MAP: {
SIID: 6,
PROPERTIES: {

View File

@ -1,5 +1,6 @@
const DreameMiotHelper = require("./DreameMiotHelper");
const DreameMiotServices = require("./DreameMiotServices");
const DreameUtils = require("./DreameUtils");
const Quirk = require("../../core/Quirk");
class DreameQuirkFactory {
@ -198,6 +199,124 @@ class DreameQuirkFactory {
);
}
});
case DreameQuirkFactory.KNOWN_QUIRKS.MOP_ONLY_MODE:
return new Quirk({
id: id,
title: "Mop Only",
description: "Disable the vacuum functionality when the mop pads are attached.",
options: ["on", "off"],
getter: async () => {
const res = await this.helper.readProperty(
DreameMiotServices["GEN2"].VACUUM_2.SIID,
DreameMiotServices["GEN2"].VACUUM_2.PROPERTIES.MOP_DOCK_SETTINGS.PIID
);
const deserializedResponse = DreameUtils.DESERIALIZE_MOP_DOCK_SETTINGS(res);
switch (deserializedResponse.operationMode) {
case 1:
return "on";
case 0:
return "off";
default:
throw new Error(`Received invalid value ${deserializedResponse.operationMode}`);
}
},
setter: async (value) => {
let val;
switch (value) {
case "on":
val = 1;
break;
case "off":
val = 0;
break;
default:
throw new Error(`Received invalid value ${value}`);
}
const res = await this.helper.readProperty(
DreameMiotServices["GEN2"].VACUUM_2.SIID,
DreameMiotServices["GEN2"].VACUUM_2.PROPERTIES.MOP_DOCK_SETTINGS.PIID
);
const deserializedResponse = DreameUtils.DESERIALIZE_MOP_DOCK_SETTINGS(res);
return this.helper.writeProperty(
DreameMiotServices["GEN2"].VACUUM_2.SIID,
DreameMiotServices["GEN2"].VACUUM_2.PROPERTIES.MOP_DOCK_SETTINGS.PIID,
DreameUtils.SERIALIZE_MOP_DOCK_SETTINGS(
deserializedResponse.waterGrade,
deserializedResponse.padCleaningFrequency,
val
)
);
}
});
case DreameQuirkFactory.KNOWN_QUIRKS.MOP_CLEANING_FREQUENCY:
return new Quirk({
id: id,
title: "Mop Cleaning Frequency",
description: "Determine how often the robot should clean and re-wet its mopping pads during a cleanup.",
options: ["every_segment", "every_5_m2", "every_10_m2", "every_15_m2"],
getter: async () => {
const res = await this.helper.readProperty(
DreameMiotServices["GEN2"].VACUUM_2.SIID,
DreameMiotServices["GEN2"].VACUUM_2.PROPERTIES.MOP_DOCK_SETTINGS.PIID
);
const deserializedResponse = DreameUtils.DESERIALIZE_MOP_DOCK_SETTINGS(res);
switch (deserializedResponse.padCleaningFrequency) {
case 0:
return "every_segment";
case 5:
return "every_5_m2";
case 10:
return "every_10_m2";
case 15:
return "every_15_m2";
default:
throw new Error(`Received invalid value ${deserializedResponse.operationMode}`);
}
},
setter: async (value) => {
let val;
switch (value) {
case "every_segment":
val = 0;
break;
case "every_5_m2":
val = 5;
break;
case "every_10_m2":
val = 10;
break;
case "every_15_m2":
val = 15;
break;
default:
throw new Error(`Received invalid value ${value}`);
}
const res = await this.helper.readProperty(
DreameMiotServices["GEN2"].VACUUM_2.SIID,
DreameMiotServices["GEN2"].VACUUM_2.PROPERTIES.MOP_DOCK_SETTINGS.PIID
);
const deserializedResponse = DreameUtils.DESERIALIZE_MOP_DOCK_SETTINGS(res);
return this.helper.writeProperty(
DreameMiotServices["GEN2"].VACUUM_2.SIID,
DreameMiotServices["GEN2"].VACUUM_2.PROPERTIES.MOP_DOCK_SETTINGS.PIID,
DreameUtils.SERIALIZE_MOP_DOCK_SETTINGS(
deserializedResponse.waterGrade,
val,
deserializedResponse.operationMode
)
);
}
});
default:
throw new Error(`There's no quirk with id ${id}`);
}
@ -208,7 +327,9 @@ DreameQuirkFactory.KNOWN_QUIRKS = {
CARPET_MODE_SENSITIVITY: "f8cb91ab-a47a-445f-b300-0aac0d4937c0",
TIGHT_MOP_PATTERN: "8471c118-f1e1-4866-ad2e-3c11865a5ba8",
AUTO_EMPTY_INTERVAL: "d38118f2-fb5d-4ed9-b668-262db15e5269",
OBSTACLE_AVOIDANCE: "4e386a76-b5f9-4f12-b04e-b8539a507163"
OBSTACLE_AVOIDANCE: "4e386a76-b5f9-4f12-b04e-b8539a507163",
MOP_ONLY_MODE: "6afbb882-c4c4-4672-b008-887454e6e0d1",
MOP_CLEANING_FREQUENCY: "a6709b18-57af-4e11-8b4c-8ae33147ab34"
};
module.exports = DreameQuirkFactory;

View File

@ -0,0 +1,186 @@
const capabilities = require("./capabilities");
const DreameGen2LidarValetudoRobot = require("./DreameGen2LidarValetudoRobot");
const DreameGen2ValetudoRobot = require("./DreameGen2ValetudoRobot");
const DreameQuirkFactory = require("./DreameQuirkFactory");
const DreameValetudoRobot = require("./DreameValetudoRobot");
const MiioValetudoRobot = require("../MiioValetudoRobot");
const QuirksCapability = require("../../core/capabilities/QuirksCapability");
const ValetudoSelectionPreset = require("../../entities/core/ValetudoSelectionPreset");
const DreameUtils = require("./DreameUtils");
const entities = require("../../entities");
const Logger = require("../../Logger");
const stateAttrs = entities.state.attributes;
const WATER_GRADES = {
[stateAttrs.PresetSelectionStateAttribute.INTENSITY.LOW]: 1,
[stateAttrs.PresetSelectionStateAttribute.INTENSITY.MEDIUM]: 2,
[stateAttrs.PresetSelectionStateAttribute.INTENSITY.HIGH]: 3,
};
class DreameSTYTJO6ZHMValetudoRobot extends DreameGen2LidarValetudoRobot {
/**
*
* @param {object} options
* @param {import("../../Configuration")} options.config
* @param {import("../../ValetudoEventStore")} options.valetudoEventStore
*/
constructor(options) {
super(options);
const QuirkFactory = new DreameQuirkFactory({
robot: this
});
this.registerCapability(new capabilities.DreameConsumableMonitoringCapability({
robot: this,
miot_properties: {
main_brush: {
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.MAIN_BRUSH.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.MAIN_BRUSH.PROPERTIES.TIME_LEFT.PIID
},
side_brush: {
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.SIDE_BRUSH.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.SIDE_BRUSH.PROPERTIES.TIME_LEFT.PIID
},
filter: {
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.FILTER.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.FILTER.PROPERTIES.TIME_LEFT.PIID
},
sensor: {
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.SENSOR.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.SENSOR.PROPERTIES.TIME_LEFT.PIID
},
mop: {
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.MOP.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.MOP.PROPERTIES.TIME_LEFT.PIID
}
},
miot_actions: {
reset_main_brush: {
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.MAIN_BRUSH.SIID,
aiid: DreameGen2ValetudoRobot.MIOT_SERVICES.MAIN_BRUSH.ACTIONS.RESET.AIID
},
reset_side_brush: {
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.SIDE_BRUSH.SIID,
aiid: DreameGen2ValetudoRobot.MIOT_SERVICES.SIDE_BRUSH.ACTIONS.RESET.AIID
},
reset_filter: {
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.FILTER.SIID,
aiid: DreameGen2ValetudoRobot.MIOT_SERVICES.FILTER.ACTIONS.RESET.AIID
},
reset_sensor: {
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.SENSOR.SIID,
aiid: DreameGen2ValetudoRobot.MIOT_SERVICES.SENSOR.ACTIONS.RESET.AIID
},
reset_mop: {
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.MOP.SIID,
aiid: DreameGen2ValetudoRobot.MIOT_SERVICES.MOP.ACTIONS.RESET.AIID
}
},
}));
this.registerCapability(new capabilities.DreameKeyLockCapability({
robot: this,
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.KEY_LOCK.PIID
}));
this.registerCapability(new capabilities.DreameMopDockWaterUsageControlCapability({
robot: this,
presets: Object.keys(WATER_GRADES).map(k => {
return new ValetudoSelectionPreset({name: k, value: WATER_GRADES[k]});
}),
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.MOP_DOCK_SETTINGS.PIID
}));
this.registerCapability(new QuirksCapability({
robot: this,
quirks: [
QuirkFactory.getQuirk(DreameQuirkFactory.KNOWN_QUIRKS.MOP_ONLY_MODE),
QuirkFactory.getQuirk(DreameQuirkFactory.KNOWN_QUIRKS.MOP_CLEANING_FREQUENCY)
]
}));
}
getStatePropertiesToPoll() {
const superProps = super.getStatePropertiesToPoll();
return [
// We don't have to poll the water usage piid as it doesn't control anything on this robot
...superProps.filter(e => {
return !(
e.siid === DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.SIID &&
e.piid === DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.WATER_USAGE.PIID
);
}),
{
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.MOP_DOCK_SETTINGS.PIID
}
];
}
parseAndUpdateState(data) {
if (!Array.isArray(data)) {
Logger.error("Received non-array state", data);
return;
}
data.forEach(elem => {
switch (elem.siid) {
case DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.SIID: {
switch (elem.piid) {
case DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.MOP_DOCK_SETTINGS.PIID: {
const deserializedValue = DreameUtils.DESERIALIZE_MOP_DOCK_SETTINGS(elem.value);
let matchingWaterGrade = Object.keys(WATER_GRADES).find(key => {
return WATER_GRADES[key] === deserializedValue.waterGrade;
});
this.state.upsertFirstMatchingAttribute(new stateAttrs.PresetSelectionStateAttribute({
metaData: {
rawValue: elem.value
},
type: stateAttrs.PresetSelectionStateAttribute.TYPE.WATER_GRADE,
value: matchingWaterGrade
}));
break;
}
}
break;
}
}
});
// This filters out both the regular water-grade piid and the mopDock special one as otherwise those would confuse the state
return super.parseAndUpdateState(data.filter(e => {
return (
!(
e.siid === DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.SIID &&
e.piid === DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.WATER_USAGE.PIID
) &&
!(
e.siid === DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.SIID &&
e.piid === DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.MOP_DOCK_SETTINGS.PIID
)
);
}));
}
getModelName() {
return "STYTJO6ZHM";
}
static IMPLEMENTATION_AUTO_DETECTION_HANDLER() {
const deviceConf = MiioValetudoRobot.READ_DEVICE_CONF(DreameValetudoRobot.DEVICE_CONF_PATH);
return !!(deviceConf && deviceConf.model === "dreame.vacuum.p2149o");
}
}
module.exports = DreameSTYTJO6ZHMValetudoRobot;

View File

@ -0,0 +1,38 @@
class DreameUtils {
/**
*
* @param {number} input
* @return {{padCleaningFrequency: number, operationMode: number, waterGrade: number}}
*/
static DESERIALIZE_MOP_DOCK_SETTINGS(input) {
const padded = input.toString(2).padStart(24, "0");
return {
waterGrade: parseInt(padded.substring(0,8),2),
padCleaningFrequency: parseInt(padded.substring(8,16),2),
operationMode: parseInt(padded.substring(16,24),2),
};
}
/**
*
* @param {number} waterGrade
* @param {number} padCleaningFrequency
* @param {number} operationMode
* @return {number}
*/
static SERIALIZE_MOP_DOCK_SETTINGS(waterGrade, padCleaningFrequency, operationMode) {
const waterGradeInt8Str = waterGrade.toString(2).padStart(8, "0");
const padCleaningFrequencyInt8Str = padCleaningFrequency.toString(2).padStart(8, "0");
const operationModeInt8Str = operationMode.toString(2).padStart(8, "0");
return parseInt(
`${waterGradeInt8Str}${padCleaningFrequencyInt8Str}${operationModeInt8Str}`,
2
);
}
}
module.exports = DreameUtils;

View File

@ -5,6 +5,7 @@ const DreameQuirkFactory = require("./DreameQuirkFactory");
const DreameValetudoRobot = require("./DreameValetudoRobot");
const MiioValetudoRobot = require("../MiioValetudoRobot");
const QuirksCapability = require("../../core/capabilities/QuirksCapability");
const ValetudoSelectionPreset = require("../../entities/core/ValetudoSelectionPreset");
class DreameZ10ProValetudoRobot extends DreameGen2LidarValetudoRobot {
@ -21,6 +22,21 @@ class DreameZ10ProValetudoRobot extends DreameGen2LidarValetudoRobot {
robot: this
});
this.registerCapability(new capabilities.DreameCarpetModeControlCapability({
robot: this,
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.CARPET_MODE.PIID
}));
this.registerCapability(new capabilities.DreameWaterUsageControlCapability({
robot: this,
presets: Object.keys(DreameValetudoRobot.WATER_GRADES).map(k => {
return new ValetudoSelectionPreset({name: k, value: DreameValetudoRobot.WATER_GRADES[k]});
}),
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.WATER_USAGE.PIID
}));
this.registerCapability(new capabilities.DreameConsumableMonitoringCapability({
robot: this,
miot_properties: {

View File

@ -30,6 +30,10 @@ class DreameConsumableMonitoringCapability extends ConsumableMonitoringCapabilit
* @param {number} options.miot_actions.reset_sensor.siid
* @param {number} options.miot_actions.reset_sensor.aiid
*
* @param {object} [options.miot_actions.reset_mop]
* @param {number} options.miot_actions.reset_mop.siid
* @param {number} options.miot_actions.reset_mop.aiid
*
*
* @param {object} options.miot_properties
* @param {object} options.miot_properties.main_brush
@ -47,6 +51,10 @@ class DreameConsumableMonitoringCapability extends ConsumableMonitoringCapabilit
* @param {object} [options.miot_properties.sensor]
* @param {number} options.miot_properties.sensor.siid
* @param {number} options.miot_properties.sensor.piid
*
* @param {object} [options.miot_properties.mop]
* @param {number} options.miot_properties.mop.siid
* @param {number} options.miot_properties.mop.piid
*/
constructor(options) {
super(options);
@ -73,6 +81,10 @@ class DreameConsumableMonitoringCapability extends ConsumableMonitoringCapabilit
props.push(this.miot_properties.sensor);
}
if (this.miot_properties.mop) {
props.push(this.miot_properties.mop);
}
const response = await this.robot.sendCommand("get_properties", props.map(e => {
return Object.assign({}, e, {did: this.robot.deviceId});
@ -128,6 +140,15 @@ class DreameConsumableMonitoringCapability extends ConsumableMonitoringCapabilit
}
}
break;
case ConsumableStateAttribute.TYPE.MOP:
if (this.miot_actions.reset_mop) {
switch (subType) {
case ConsumableStateAttribute.SUB_TYPE.ALL:
payload = this.miot_actions.reset_mop;
break;
}
}
break;
}
@ -203,8 +224,11 @@ class DreameConsumableMonitoringCapability extends ConsumableMonitoringCapabilit
}
default:
if (this.miot_properties.sensor) {
if (msg.siid === this.miot_properties.sensor.siid && msg.piid === this.miot_properties.sensor.piid) {
if (
this.miot_properties.sensor &&
msg.siid === this.miot_properties.sensor.siid
) {
if (msg.piid === this.miot_properties.sensor.piid) {
consumable = new ConsumableStateAttribute({
type: ConsumableStateAttribute.TYPE.SENSOR,
subType: ConsumableStateAttribute.SUB_TYPE.ALL,
@ -214,6 +238,20 @@ class DreameConsumableMonitoringCapability extends ConsumableMonitoringCapabilit
}
});
}
} else if (
this.miot_properties.mop &&
msg.siid === this.miot_properties.mop.siid
) {
if (msg.piid === this.miot_properties.mop.piid) {
consumable = new ConsumableStateAttribute({
type: ConsumableStateAttribute.TYPE.MOP,
subType: ConsumableStateAttribute.SUB_TYPE.ALL,
remaining: {
value: Math.round(Math.max(0, msg.value * 60)),
unit: ConsumableStateAttribute.UNITS.MINUTES
}
});
}
} else {
Logger.warn("Unhandled consumable update", msg);
}
@ -255,6 +293,16 @@ class DreameConsumableMonitoringCapability extends ConsumableMonitoringCapabilit
);
}
if (this.miot_properties.mop) {
availableConsumables.push(
{
type: ConsumableStateAttribute.TYPE.MOP,
subType: ConsumableStateAttribute.SUB_TYPE.ALL,
unit: ConsumableStateAttribute.UNITS.MINUTES
}
);
}
return {
availableConsumables: availableConsumables
};

View File

@ -0,0 +1,56 @@
const DreameMiotHelper = require("../DreameMiotHelper");
const DreameUtils = require("../DreameUtils");
const WaterUsageControlCapability = require("../../../core/capabilities/WaterUsageControlCapability");
/**
* @extends WaterUsageControlCapability<import("../DreameValetudoRobot")>
*/
class DreameMopDockWaterUsageControlCapability extends WaterUsageControlCapability {
/**
* @param {object} options
* @param {import("../DreameValetudoRobot")} options.robot
* @param {Array<import("../../../entities/core/ValetudoSelectionPreset")>} options.presets
*
* @param {number} options.siid MIOT Service ID
* @param {number} options.piid MIOT Property ID
*/
constructor(options) {
super(options);
this.siid = options.siid;
this.piid = options.piid;
this.helper = new DreameMiotHelper({robot: this.robot});
}
/**
* @param {string} preset
* @returns {Promise<void>}
*/
async selectPreset(preset) {
const matchedPreset = this.presets.find(p => {
return p.name === preset;
});
if (matchedPreset) {
const res = await this.helper.readProperty(this.siid, this.piid);
const deserializedResponse = DreameUtils.DESERIALIZE_MOP_DOCK_SETTINGS(res);
return this.helper.writeProperty(
this.siid,
this.piid,
DreameUtils.SERIALIZE_MOP_DOCK_SETTINGS(
matchedPreset.value,
deserializedResponse.padCleaningFrequency,
deserializedResponse.operationMode
)
);
} else {
throw new Error("Invalid Preset");
}
}
}
module.exports = DreameMopDockWaterUsageControlCapability;

View File

@ -32,7 +32,7 @@ class DreameWaterUsageControlCapability extends WaterUsageControlCapability {
});
if (matchedPreset) {
await this.helper.writeProperty(this.siid, this.piid, matchedPreset.value);
return this.helper.writeProperty(this.siid, this.piid, matchedPreset.value);
} else {
throw new Error("Invalid Preset");
}

View File

@ -21,6 +21,7 @@ module.exports = {
DreameMapSegmentRenameCapability: require("./DreameMapSegmentRenameCapability"),
DreameMapSegmentationCapability: require("./DreameMapSegmentationCapability"),
DreameMappingPassCapability: require("./DreameMappingPassCapability"),
DreameMopDockWaterUsageControlCapability: require("./DreameMopDockWaterUsageControlCapability"),
DreamePendingMapChangeHandlingCapability: require("./DreamePendingMapChangeHandlingCapability"),
DreamePersistentMapControlCapability: require("./DreamePersistentMapControlCapability"),
DreameSpeakerTestCapability: require("./DreameSpeakerTestCapability"),
@ -28,5 +29,5 @@ module.exports = {
DreameTotalStatisticsCapability: require("./DreameTotalStatisticsCapability"),
DreameVoicePackManagementCapability: require("./DreameVoicePackManagementCapability"),
DreameWaterUsageControlCapability: require("./DreameWaterUsageControlCapability"),
DreameZoneCleaningCapability: require("./DreameZoneCleaningCapability")
DreameZoneCleaningCapability: require("./DreameZoneCleaningCapability"),
};

View File

@ -6,5 +6,6 @@ module.exports = {
"DreameF9ValetudoRobot": require("./DreameF9ValetudoRobot"),
"DreameL10ProValetudoRobot": require("./DreameL10ProValetudoRobot"),
"DreameMovaZ500ValetudoRobot": require("./DreameMovaZ500ValetudoRobot"),
"DreameSTYTJO6ZHMValetudoRobot": require("./DreameSTYTJO6ZHMValetudoRobot"),
"DreameZ10ProValetudoRobot": require("./DreameZ10ProValetudoRobot")
};

View File

@ -259,7 +259,8 @@ function getModelDescription(vendor, model) {
*/
const HIDDEN_IMPLEMENTATIONS = [
"RoborockS6MaxVValetudoRobot",
"RoborockS7ValetudoRobot"
"RoborockS7ValetudoRobot",
"DreameSTYTJO6ZHMValetudoRobot"
];