feat(vendor.midea): Pet obstacle avoidance and stain cleaning

This commit is contained in:
Sören Beye 2025-09-07 14:46:34 +02:00
parent f94f3c1101
commit 0ea2a50a4d
6 changed files with 128 additions and 33 deletions

View File

@ -424,6 +424,51 @@ class MideaQuirkFactory {
}).toHexString());
}
});
case MideaQuirkFactory.KNOWN_QUIRKS.STAIN_CLEANING:
return new Quirk({
id: id,
title: "Stain Cleaning",
description: "When enabled, during a clean the robot will detect stains and spills and will exert special care. Note that due to optical similarities, this feature is mutually exclusive to the PetObstacleAvoidance.",
options: ["off", "on"],
getter: async () => {
const response = await this.robot.sendCommand(new MSmartPacket({
messageType: MSmartPacket.MESSAGE_TYPE.ACTION,
payload: MSmartPacket.buildPayload(MSmartConst.ACTION.GET_STATUS)
}).toHexString());
const parsedResponse = BEightParser.PARSE(response);
if (parsedResponse instanceof MSmartStatusDTO) {
return parsedResponse.stain_clean_switch ? "on" : "off";
} else {
throw new Error("Invalid response from robot");
}
},
setter: async (value) => {
let val;
switch (value) {
case "off":
val = 0;
break;
case "on":
val = 1;
break;
default:
throw new Error(`Invalid stain cleaning value: ${value}`);
}
await this.robot.sendCommand(new MSmartPacket({
messageType: MSmartPacket.MESSAGE_TYPE.SETTING,
payload: MSmartPacket.buildPayload(
MSmartConst.SETTING.SET_VARIOUS_TOGGLES,
Buffer.from([
0x08, // Stain Cleaning
val
])
)
}).toHexString());
}
});
default:
throw new Error(`There's no quirk with id ${id}`);
}
@ -439,6 +484,7 @@ MideaQuirkFactory.KNOWN_QUIRKS = {
CARPET_FIRST: "4b100fec-08d5-4227-9edf-eb0198d6ea20",
DEEP_CARPET_CLEANING: "d2ad3f99-c1b0-4195-9a98-4f13bdb0f1e8",
INCREASED_CARPET_AVOIDANCE: "f3ff1c65-9fe7-4312-b196-83ce91107fe8",
STAIN_CLEANING: "d4688a29-a6e4-43c2-ab3a-08ddae40655c",
};
module.exports = MideaQuirkFactory;

View File

@ -148,6 +148,7 @@ class MideaValetudoRobot extends ValetudoRobot {
capabilities.MideaMopDockMopWashTemperatureControlCapability,
capabilities.MideaCarpetModeControlCapability,
capabilities.MideaCarpetSensorModeControlCapability,
capabilities.MideaPetObstacleAvoidanceControlCapability,
].forEach(capability => {
this.registerCapability(new capability({robot: this}));
});
@ -172,6 +173,7 @@ class MideaValetudoRobot extends ValetudoRobot {
quirkFactory.getQuirk(MideaQuirkFactory.KNOWN_QUIRKS.CARPET_FIRST),
quirkFactory.getQuirk(MideaQuirkFactory.KNOWN_QUIRKS.DEEP_CARPET_CLEANING),
quirkFactory.getQuirk(MideaQuirkFactory.KNOWN_QUIRKS.INCREASED_CARPET_AVOIDANCE),
quirkFactory.getQuirk(MideaQuirkFactory.KNOWN_QUIRKS.STAIN_CLEANING),
]
}));

View File

@ -13,12 +13,10 @@ class MideaObstacleAvoidanceControlCapability extends ObstacleAvoidanceControlCa
* @returns {Promise<boolean>}
*/
async isEnabled() {
const packet = new MSmartPacket({
const response = await this.robot.sendCommand(new MSmartPacket({
messageType: MSmartPacket.MESSAGE_TYPE.ACTION,
payload: MSmartPacket.buildPayload(MSmartConst.ACTION.GET_STATUS)
});
const response = await this.robot.sendCommand(packet.toHexString());
}).toHexString());
const parsedResponse = BEightParser.PARSE(response);
if (parsedResponse instanceof MSmartStatusDTO) {
@ -32,12 +30,10 @@ class MideaObstacleAvoidanceControlCapability extends ObstacleAvoidanceControlCa
* @returns {Promise<void>}
*/
async enable() {
const statusPacket = new MSmartPacket({
const response = await this.robot.sendCommand(new MSmartPacket({
messageType: MSmartPacket.MESSAGE_TYPE.ACTION,
payload: MSmartPacket.buildPayload(MSmartConst.ACTION.GET_STATUS)
});
const response = await this.robot.sendCommand(statusPacket.toHexString());
}).toHexString());
const status = BEightParser.PARSE(response);
if (!(status instanceof MSmartStatusDTO)) {
@ -45,21 +41,19 @@ class MideaObstacleAvoidanceControlCapability extends ObstacleAvoidanceControlCa
}
if (status.ai_recognition_switch === false) {
const tosPacket = new MSmartPacket({
await this.robot.sendCommand(new MSmartPacket({
messageType: MSmartPacket.MESSAGE_TYPE.SETTING,
payload: MSmartPacket.buildPayload(
MSmartConst.SETTING.SET_VARIOUS_TOGGLES,
Buffer.from([
0x0f, // AI Recognition (ToS)
0x0f, // AI Recognition
0x01 // true
])
)
});
await this.robot.sendCommand(tosPacket.toHexString());
}).toHexString());
}
const avoidancePacket = new MSmartPacket({
await this.robot.sendCommand(new MSmartPacket({
messageType: MSmartPacket.MESSAGE_TYPE.SETTING,
payload: MSmartPacket.buildPayload(
MSmartConst.SETTING.SET_VARIOUS_TOGGLES,
@ -68,16 +62,14 @@ class MideaObstacleAvoidanceControlCapability extends ObstacleAvoidanceControlCa
0x01 // true
])
)
});
await this.robot.sendCommand(avoidancePacket.toHexString());
}).toHexString());
}
/**
* @returns {Promise<void>}
*/
async disable() {
const packet = new MSmartPacket({
await this.robot.sendCommand(new MSmartPacket({
messageType: MSmartPacket.MESSAGE_TYPE.SETTING,
payload: MSmartPacket.buildPayload(
MSmartConst.SETTING.SET_VARIOUS_TOGGLES,
@ -86,9 +78,7 @@ class MideaObstacleAvoidanceControlCapability extends ObstacleAvoidanceControlCa
0x00 // false
])
)
});
await this.robot.sendCommand(packet.toHexString());
}).toHexString());
}
}

View File

@ -15,12 +15,10 @@ class MideaObstacleImagesCapability extends ObstacleImagesCapability {
* @returns {Promise<boolean>}
*/
async isEnabled() {
const packet = new MSmartPacket({
const response = await this.robot.sendCommand(new MSmartPacket({
messageType: MSmartPacket.MESSAGE_TYPE.ACTION,
payload: MSmartPacket.buildPayload(MSmartConst.ACTION.GET_STATUS)
});
const response = await this.robot.sendCommand(packet.toHexString());
}).toHexString());
const parsedResponse = BEightParser.PARSE(response);
if (parsedResponse instanceof MSmartStatusDTO) {
@ -34,7 +32,7 @@ class MideaObstacleImagesCapability extends ObstacleImagesCapability {
* @returns {Promise<void>}
*/
async enable() {
const packet = new MSmartPacket({
await this.robot.sendCommand(new MSmartPacket({
messageType: MSmartPacket.MESSAGE_TYPE.SETTING,
payload: MSmartPacket.buildPayload(
MSmartConst.SETTING.SET_VARIOUS_TOGGLES,
@ -43,16 +41,14 @@ class MideaObstacleImagesCapability extends ObstacleImagesCapability {
0x01 // true
])
)
});
await this.robot.sendCommand(packet.toHexString());
}).toHexString());
}
/**
* @returns {Promise<void>}
*/
async disable() {
const packet = new MSmartPacket({
await this.robot.sendCommand(new MSmartPacket({
messageType: MSmartPacket.MESSAGE_TYPE.SETTING,
payload: MSmartPacket.buildPayload(
MSmartConst.SETTING.SET_VARIOUS_TOGGLES,
@ -61,9 +57,7 @@ class MideaObstacleImagesCapability extends ObstacleImagesCapability {
0x00 // false
])
)
});
await this.robot.sendCommand(packet.toHexString());
}).toHexString());
}
/**

View File

@ -0,0 +1,62 @@
const BEightParser = require("../../../msmart/BEightParser");
const MSmartConst = require("../../../msmart/MSmartConst");
const MSmartPacket = require("../../../msmart/MSmartPacket");
const MSmartStatusDTO = require("../../../msmart/dtos/MSmartStatusDTO");
const PetObstacleAvoidanceControlCapability = require("../../../core/capabilities/PetObstacleAvoidanceControlCapability");
/**
* @extends PetObstacleAvoidanceControlCapability<import("../MideaValetudoRobot")>
*/
class MideaPetObstacleAvoidanceControlCapability extends PetObstacleAvoidanceControlCapability {
/**
* @returns {Promise<boolean>}
*/
async isEnabled() {
const response = await this.robot.sendCommand(new MSmartPacket({
messageType: MSmartPacket.MESSAGE_TYPE.ACTION,
payload: MSmartPacket.buildPayload(MSmartConst.ACTION.GET_STATUS)
}).toHexString());
const parsedResponse = BEightParser.PARSE(response);
if (parsedResponse instanceof MSmartStatusDTO) {
return parsedResponse.pet_mode_switch;
} else {
throw new Error("Invalid response from robot");
}
}
/**
* @returns {Promise<void>}
*/
async enable() {
await this.robot.sendCommand(new MSmartPacket({
messageType: MSmartPacket.MESSAGE_TYPE.SETTING,
payload: MSmartPacket.buildPayload(
MSmartConst.SETTING.SET_VARIOUS_TOGGLES,
Buffer.from([
0x0e, // Pet Mode
0x01 // true
])
)
}).toHexString());
}
/**
* @returns {Promise<void>}
*/
async disable() {
await this.robot.sendCommand(new MSmartPacket({
messageType: MSmartPacket.MESSAGE_TYPE.SETTING,
payload: MSmartPacket.buildPayload(
MSmartConst.SETTING.SET_VARIOUS_TOGGLES,
Buffer.from([
0x0e, // Pet mode
0x00 // false
])
)
}).toHexString());
}
}
module.exports = MideaPetObstacleAvoidanceControlCapability;

View File

@ -24,6 +24,7 @@ module.exports = {
MideaObstacleAvoidanceControlCapability: require("./MideaObstacleAvoidanceControlCapability"),
MideaObstacleImagesCapability: require("./MideaObstacleImagesCapability"),
MideaOperationModeControlCapability: require("./MideaOperationModeControlCapability"),
MideaPetObstacleAvoidanceControlCapability: require("./MideaPetObstacleAvoidanceControlCapability"),
MideaSpeakerTestCapability: require("./MideaSpeakerTestCapability"),
MideaSpeakerVolumeControlCapability: require("./MideaSpeakerVolumeControlCapability"),
MideaWaterUsageControlCapability: require("./MideaWaterUsageControlCapability"),