Skip to content

Commit

Permalink
feat(vendor.dreame): Operation mode control
Browse files Browse the repository at this point in the history
  • Loading branch information
Hypfer committed Oct 10, 2022
1 parent d1ad1af commit aefa436
Show file tree
Hide file tree
Showing 29 changed files with 355 additions and 135 deletions.
28 changes: 28 additions & 0 deletions backend/lib/core/capabilities/OperationModeControlCapability.js
@@ -0,0 +1,28 @@
const NotImplementedError = require("../NotImplementedError");
const PresetSelectionCapability = require("./PresetSelectionCapability");

/**
* @template {import("../ValetudoRobot")} T
* @extends PresetSelectionCapability<T>
*/
class OperationModeControlCapability extends PresetSelectionCapability {
/**
* @abstract
* @param {string} preset
* @returns {Promise<void>}
*/
async selectPreset(preset) {
throw new NotImplementedError();
}

/**
* @returns {string}
*/
getType() {
return OperationModeControlCapability.TYPE;
}
}

OperationModeControlCapability.TYPE = "OperationModeControlCapability";

module.exports = OperationModeControlCapability;
1 change: 1 addition & 0 deletions backend/lib/core/capabilities/index.js
Expand Up @@ -20,6 +20,7 @@ module.exports = {
MappingPassCapability: require("./MappingPassCapability"),
MopDockCleanManualTriggerCapability: require("./MopDockCleanManualTriggerCapability"),
MopDockDryManualTriggerCapability: require("./MopDockDryManualTriggerCapability"),
OperationModeControlCapability: require("./OperationModeControlCapability"),
PendingMapChangeHandlingCapability: require("./PendingMapChangeHandlingCapability"),
PersistentMapControlCapability: require("./PersistentMapControlCapability"),
PresetSelectionCapability: require("./PresetSelectionCapability"),
Expand Down

This file was deleted.

Expand Up @@ -35,6 +35,7 @@ class PresetSelectionStateAttribute extends StateAttribute {
PresetSelectionStateAttribute.TYPE = Object.freeze({
FAN_SPEED: "fan_speed",
WATER_GRADE: "water_grade",
OPERATION_MODE: "operation_mode",
});

/**
Expand All @@ -52,5 +53,15 @@ PresetSelectionStateAttribute.INTENSITY = Object.freeze({
CUSTOM: "custom",
});

/**
* @enum {string}
*
*/
PresetSelectionStateAttribute.MODE = Object.freeze({
VACUUM: "vacuum",
MOP: "mop",
VACUUM_AND_MOP: "vacuum_and_mop",
});


module.exports = PresetSelectionStateAttribute;
Expand Up @@ -13,7 +13,8 @@
"type": "string",
"enum": [
"fan_speed",
"water_grade"
"water_grade",
"operation_mode"
]
},
"value": {
Expand All @@ -26,7 +27,11 @@
"high",
"max",
"turbo",
"custom"
"custom",

"vacuum",
"mop",
"vacuum_and_mop"
]
},
"customValue": {
Expand Down
1 change: 0 additions & 1 deletion backend/lib/entities/state/attributes/index.js
Expand Up @@ -3,7 +3,6 @@ module.exports = {
BatteryStateAttribute: require("./BatteryStateAttribute"),
ConsumableStateAttribute: require("./ConsumableStateAttribute"),
DockStatusStateAttribute: require("./DockStatusStateAttribute"),
OperationModeStateAttribute: require("./OperationModeStateAttribute"),
PresetSelectionStateAttribute: require("./PresetSelectionStateAttribute"),
StatusStateAttribute: require("./StatusStateAttribute")
};
Expand Up @@ -100,6 +100,27 @@ class PresetSelectionCapabilityMqttHandle extends CapabilityMqttHandle {
);
});

} else if (options.capability.getType() === capabilities.OperationModeControlCapability.TYPE) {
this.controller.withHass((hass) => {
prop.attachHomeAssistantComponent(
new InLineHassComponent({
hass: hass,
robot: this.robot,
name: capabilities.OperationModeControlCapability.TYPE,
friendlyName: CAPABILITIES_TO_FRIENDLY_NAME_MAPPING[capabilities.OperationModeControlCapability.TYPE],
componentType: ComponentType.SELECT,
autoconf: {
state_topic: prop.getBaseTopic(),
value_template: "{{ value }}",
command_topic: prop.getBaseTopic() + "/set",
options: this.capability.getPresets(),
icon: "mdi:developer-board",
entity_category: EntityCategory.CONFIG,
}
})
);
});

}
})
);
Expand All @@ -113,6 +134,7 @@ class PresetSelectionCapabilityMqttHandle extends CapabilityMqttHandle {
const CAPABILITIES_TO_FRIENDLY_NAME_MAPPING = {
[capabilities.FanSpeedControlCapability.TYPE]: "Fan speed",
[capabilities.WaterUsageControlCapability.TYPE]: "Water grade",
[capabilities.OperationModeControlCapability.TYPE]: "Operation mode",
};

const CAPABILITIES_TO_STATE_ATTR_MAPPING = {
Expand All @@ -124,6 +146,10 @@ const CAPABILITIES_TO_STATE_ATTR_MAPPING = {
attributeClass: stateAttrs.PresetSelectionStateAttribute.name,
attributeType: stateAttrs.PresetSelectionStateAttribute.TYPE.WATER_GRADE
},
[capabilities.OperationModeControlCapability.TYPE]: {
attributeClass: stateAttrs.PresetSelectionStateAttribute.name,
attributeType: stateAttrs.PresetSelectionStateAttribute.TYPE.OPERATION_MODE
},
};

module.exports = PresetSelectionCapabilityMqttHandle;
1 change: 1 addition & 0 deletions backend/lib/mqtt/handles/HandleMappings.js
Expand Up @@ -11,6 +11,7 @@ const CAPABILITY_TYPE_TO_HANDLE_MAPPING = {
[capabilities.LocateCapability.TYPE]: capabilityHandles.LocateCapabilityMqttHandle,
[capabilities.MapSegmentationCapability.TYPE]: capabilityHandles.MapSegmentationCapabilityMqttHandle,
[capabilities.WaterUsageControlCapability.TYPE]: capabilityHandles.PresetSelectionCapabilityMqttHandle,
[capabilities.OperationModeControlCapability.TYPE]: capabilityHandles.PresetSelectionCapabilityMqttHandle,
[capabilities.WifiConfigurationCapability.TYPE]: capabilityHandles.WifiConfigurationCapabilityMqttHandle,
[capabilities.ZoneCleaningCapability.TYPE]: capabilityHandles.ZoneCleaningCapabilityMqttHandle,
[capabilities.AutoEmptyDockManualTriggerCapability.TYPE]: capabilityHandles.AutoEmptyDockManualTriggerCapabilityMqttHandle,
Expand Down
15 changes: 14 additions & 1 deletion backend/lib/robots/dreame/DreameGen2ValetudoRobot.js
Expand Up @@ -2,6 +2,7 @@ const capabilities = require("./capabilities");

const ConsumableMonitoringCapability = require("../../core/capabilities/ConsumableMonitoringCapability");
const DreameMiotServices = require("./DreameMiotServices");
const DreameUtils = require("./DreameUtils");
const DreameValetudoRobot = require("./DreameValetudoRobot");
const entities = require("../../entities");
const LinuxTools = require("../../utils/LinuxTools");
Expand Down Expand Up @@ -546,6 +547,19 @@ class DreameGen2ValetudoRobot extends DreameValetudoRobot {

break;
}
case MIOT_SERVICES.VACUUM_2.PROPERTIES.MOP_DOCK_SETTINGS.PIID: {
const deserializedValue = DreameUtils.DESERIALIZE_MOP_DOCK_SETTINGS(elem.value);

let matchingOperationMode = Object.keys(DreameValetudoRobot.OPERATION_MODES).find(key => {
return DreameValetudoRobot.OPERATION_MODES[key] === deserializedValue.operationMode;
});

this.state.upsertFirstMatchingAttribute(new stateAttrs.PresetSelectionStateAttribute({
type: stateAttrs.PresetSelectionStateAttribute.TYPE.OPERATION_MODE,
value: matchingOperationMode
}));
break;
}
case MIOT_SERVICES.VACUUM_2.PROPERTIES.CLEANING_TIME.PIID:
case MIOT_SERVICES.VACUUM_2.PROPERTIES.CLEANING_AREA.PIID:
case MIOT_SERVICES.VACUUM_2.PROPERTIES.STATE_CHANGE_TIMESTAMP.PIID:
Expand All @@ -555,7 +569,6 @@ class DreameGen2ValetudoRobot extends DreameValetudoRobot {
case MIOT_SERVICES.VACUUM_2.PROPERTIES.KEY_LOCK.PIID:
case MIOT_SERVICES.VACUUM_2.PROPERTIES.OBSTACLE_AVOIDANCE.PIID:
case MIOT_SERVICES.VACUUM_2.PROPERTIES.POST_CHARGE_CONTINUE.PIID:
case MIOT_SERVICES.VACUUM_2.PROPERTIES.MOP_DOCK_SETTINGS.PIID:
//ignored for now
break;
case 38:
Expand Down
20 changes: 20 additions & 0 deletions backend/lib/robots/dreame/DreameL10ProValetudoRobot.js
Expand Up @@ -83,6 +83,15 @@ class DreameL10ProValetudoRobot extends DreameGen2LidarValetudoRobot {
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.KEY_LOCK.PIID
}));

this.registerCapability(new capabilities.DreameOperationModeControlCapability({
robot: this,
presets: Object.keys(DreameValetudoRobot.OPERATION_MODES).map(k => {
return new ValetudoSelectionPreset({name: k, value: DreameValetudoRobot.OPERATION_MODES[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: [
Expand All @@ -98,6 +107,17 @@ class DreameL10ProValetudoRobot extends DreameGen2LidarValetudoRobot {
}));
}

getStatePropertiesToPoll() {
const superProps = super.getStatePropertiesToPoll();

return [
...superProps,
{
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.MOP_DOCK_SETTINGS.PIID
}
];
}

getModelName() {
return "L10 Pro";
Expand Down
13 changes: 13 additions & 0 deletions backend/lib/robots/dreame/DreameL10SUltraValetudoRobot.js
Expand Up @@ -150,6 +150,15 @@ class DreameL10SUltraValetudoRobot extends DreameGen2LidarValetudoRobot {
additionalCleanupParametersPiid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.ADDITIONAL_CLEANUP_PROPERTIES.PIID
}));

this.registerCapability(new capabilities.DreameOperationModeControlCapability({
robot: this,
presets: Object.keys(DreameValetudoRobot.OPERATION_MODES).map(k => {
return new ValetudoSelectionPreset({name: k, value: DreameValetudoRobot.OPERATION_MODES[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: [
Expand Down Expand Up @@ -185,6 +194,10 @@ class DreameL10SUltraValetudoRobot extends DreameGen2LidarValetudoRobot {
{
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.MOP_DOCK_STATUS.PIID
},
{
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.MOP_DOCK_SETTINGS.PIID
}
];
}
Expand Down
10 changes: 10 additions & 0 deletions backend/lib/robots/dreame/DreameMopValetudoRobot.js
@@ -1,6 +1,7 @@
const capabilities = require("./capabilities");
const DreameGen2LidarValetudoRobot = require("./DreameGen2LidarValetudoRobot");
const DreameGen2ValetudoRobot = require("./DreameGen2ValetudoRobot");
const DreameValetudoRobot = require("./DreameValetudoRobot");
const ValetudoSelectionPreset = require("../../entities/core/ValetudoSelectionPreset");

const DreameUtils = require("./DreameUtils");
Expand Down Expand Up @@ -103,6 +104,15 @@ class DreameMopValetudoRobot extends DreameGen2LidarValetudoRobot {
additionalCleanupParametersPiid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.ADDITIONAL_CLEANUP_PROPERTIES.PIID
}));

this.registerCapability(new capabilities.DreameOperationModeControlCapability({
robot: this,
presets: Object.keys(DreameValetudoRobot.OPERATION_MODES).map(k => {
return new ValetudoSelectionPreset({name: k, value: DreameValetudoRobot.OPERATION_MODES[k]});
}),
siid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.SIID,
piid: DreameGen2ValetudoRobot.MIOT_SERVICES.VACUUM_2.PROPERTIES.MOP_DOCK_SETTINGS.PIID
}));


this.state.upsertFirstMatchingAttribute(new entities.state.attributes.DockStatusStateAttribute({
value: entities.state.attributes.DockStatusStateAttribute.VALUE.IDLE
Expand Down
1 change: 0 additions & 1 deletion backend/lib/robots/dreame/DreameP2149ValetudoRobot.js
Expand Up @@ -21,7 +21,6 @@ class DreameP2149ValetudoRobot extends DreameMopValetudoRobot {
this.registerCapability(new QuirksCapability({
robot: this,
quirks: [
QuirkFactory.getQuirk(DreameQuirkFactory.KNOWN_QUIRKS.MOP_DOCK_MOP_ONLY_MODE),
QuirkFactory.getQuirk(DreameQuirkFactory.KNOWN_QUIRKS.MOP_DOCK_MOP_CLEANING_FREQUENCY),
QuirkFactory.getQuirk(DreameQuirkFactory.KNOWN_QUIRKS.MOP_DOCK_UV_TREATMENT)
]
Expand Down
55 changes: 0 additions & 55 deletions backend/lib/robots/dreame/DreameQuirkFactory.js
Expand Up @@ -199,60 +199,6 @@ class DreameQuirkFactory {
);
}
});
case DreameQuirkFactory.KNOWN_QUIRKS.MOP_DOCK_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({
waterGrade: deserializedResponse.waterGrade,
padCleaningFrequency: deserializedResponse.padCleaningFrequency,
operationMode: val
})
);
}
});
case DreameQuirkFactory.KNOWN_QUIRKS.MOP_DOCK_MOP_CLEANING_FREQUENCY:
return new Quirk({
id: id,
Expand Down Expand Up @@ -648,7 +594,6 @@ DreameQuirkFactory.KNOWN_QUIRKS = {
TIGHT_MOP_PATTERN: "8471c118-f1e1-4866-ad2e-3c11865a5ba8",
AUTO_EMPTY_INTERVAL: "d38118f2-fb5d-4ed9-b668-262db15e5269",
OBSTACLE_AVOIDANCE: "4e386a76-b5f9-4f12-b04e-b8539a507163",
MOP_DOCK_MOP_ONLY_MODE: "6afbb882-c4c4-4672-b008-887454e6e0d1",
MOP_DOCK_MOP_CLEANING_FREQUENCY: "a6709b18-57af-4e11-8b4c-8ae33147ab34",
MOP_DOCK_UV_TREATMENT: "7f97b603-967f-44f0-9dfb-35bcdc21f433",
CARPET_DETECTION_SENSOR: "38362a9d-6c8f-430a-aaaa-fd454e93e816",
Expand Down
6 changes: 6 additions & 0 deletions backend/lib/robots/dreame/DreameValetudoRobot.js
Expand Up @@ -384,6 +384,12 @@ DreameValetudoRobot.WATER_GRADES = Object.freeze({
[stateAttrs.PresetSelectionStateAttribute.INTENSITY.HIGH]: 3,
});

DreameValetudoRobot.OPERATION_MODES = Object.freeze({
[stateAttrs.PresetSelectionStateAttribute.MODE.VACUUM_AND_MOP]: 0,
[stateAttrs.PresetSelectionStateAttribute.MODE.MOP]: 1,
[stateAttrs.PresetSelectionStateAttribute.MODE.VACUUM]: 2,
});

DreameValetudoRobot.MOP_DOCK_STATUS_MAP = Object.freeze({
0: stateAttrs.DockStatusStateAttribute.VALUE.IDLE,
1: stateAttrs.DockStatusStateAttribute.VALUE.CLEANING,
Expand Down
1 change: 0 additions & 1 deletion backend/lib/robots/dreame/DreameW10ValetudoRobot.js
Expand Up @@ -21,7 +21,6 @@ class DreameW10ValetudoRobot extends DreameMopValetudoRobot {
this.registerCapability(new QuirksCapability({
robot: this,
quirks: [
QuirkFactory.getQuirk(DreameQuirkFactory.KNOWN_QUIRKS.MOP_DOCK_MOP_ONLY_MODE),
QuirkFactory.getQuirk(DreameQuirkFactory.KNOWN_QUIRKS.MOP_DOCK_MOP_CLEANING_FREQUENCY),
QuirkFactory.getQuirk(DreameQuirkFactory.KNOWN_QUIRKS.MOP_DOCK_WET_DRY_SWITCH),
QuirkFactory.getQuirk(DreameQuirkFactory.KNOWN_QUIRKS.CARPET_DETECTION_SENSOR),
Expand Down

0 comments on commit aefa436

Please sign in to comment.