-
-
Notifications
You must be signed in to change notification settings - Fork 408
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
153 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
139 changes: 139 additions & 0 deletions
139
backend/lib/robots/dreame/capabilities/DreameManualControlCapability.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
const AttributeSubscriber = require("../../../entities/AttributeSubscriber"); | ||
const CallbackAttributeSubscriber = require("../../../entities/CallbackAttributeSubscriber"); | ||
const ManualControlCapability = require("../../../core/capabilities/ManualControlCapability"); | ||
const {StatusStateAttribute} = require("../../../entities/state/attributes"); | ||
|
||
/** | ||
* @extends ManualControlCapability<import("../DreameGen2ValetudoRobot")> | ||
*/ | ||
class DreameManualControlCapability extends ManualControlCapability { | ||
/** | ||
* | ||
* @param {object} options | ||
* @param {object} options.miot_properties | ||
* @param {object} options.miot_properties.manual_control | ||
* @param {number} options.miot_properties.manual_control.siid | ||
* @param {number} options.miot_properties.manual_control.piid | ||
* | ||
* @param {import("../DreameGen2ValetudoRobot")} options.robot | ||
* @class | ||
*/ | ||
constructor(options) { | ||
super(Object.assign({}, options, { | ||
supportedMovementCommands: [ | ||
ManualControlCapability.MOVEMENT_COMMAND_TYPE.FORWARD, | ||
ManualControlCapability.MOVEMENT_COMMAND_TYPE.BACKWARD, | ||
ManualControlCapability.MOVEMENT_COMMAND_TYPE.ROTATE_CLOCKWISE, | ||
ManualControlCapability.MOVEMENT_COMMAND_TYPE.ROTATE_COUNTERCLOCKWISE | ||
] | ||
})); | ||
|
||
this.miot_properties = options.miot_properties; | ||
|
||
this.keepAliveTimeout = undefined; | ||
|
||
this.robot.state.subscribe( | ||
new CallbackAttributeSubscriber((eventType, status, prevStatus) => { | ||
if ( | ||
eventType === AttributeSubscriber.EVENT_TYPE.CHANGE && | ||
//@ts-ignore | ||
status.value !== StatusStateAttribute.VALUE.MANUAL_CONTROL && | ||
prevStatus && | ||
//@ts-ignore | ||
prevStatus.value === StatusStateAttribute.VALUE.MANUAL_CONTROL | ||
) { | ||
this.disableManualControl().then(() => {}).catch(() => {}); | ||
} | ||
}), | ||
{attributeClass: StatusStateAttribute.name} | ||
); | ||
|
||
this.lastCommand = new Date(0).getTime(); | ||
this.active = false; | ||
} | ||
|
||
/** | ||
* @returns {Promise<void>} | ||
*/ | ||
async enableManualControl() { | ||
if (this.active === false) { | ||
await this.sendRemoteControlCommand(0, 0, true); | ||
this.active = true; | ||
await this.sendAndScheduleKeepAlive(); | ||
} | ||
} | ||
|
||
/** | ||
* @returns {Promise<void>} | ||
*/ | ||
async disableManualControl() { | ||
clearTimeout(this.keepAliveTimeout); | ||
this.active = false; | ||
} | ||
|
||
async sendAndScheduleKeepAlive() { | ||
clearTimeout(this.keepAliveTimeout); | ||
|
||
if (new Date().getTime() - this.lastCommand >= 700) { | ||
await this.sendRemoteControlCommand(0,0, false); | ||
} | ||
|
||
this.keepAliveTimeout = setTimeout(async () => { | ||
await this.sendAndScheduleKeepAlive(); | ||
}, 700); | ||
} | ||
|
||
/** | ||
* @param {import("../../../core/capabilities/ManualControlCapability").ValetudoManualControlMovementCommandType} movementCommand | ||
* @returns {Promise<void>} | ||
*/ | ||
async manualControl(movementCommand) { | ||
switch (movementCommand) { | ||
case ManualControlCapability.MOVEMENT_COMMAND_TYPE.FORWARD: | ||
return this.sendRemoteControlCommand(300, 0, false); | ||
case ManualControlCapability.MOVEMENT_COMMAND_TYPE.BACKWARD: | ||
return this.sendRemoteControlCommand(-300, 0, false); | ||
case ManualControlCapability.MOVEMENT_COMMAND_TYPE.ROTATE_CLOCKWISE: | ||
return this.sendRemoteControlCommand(0, -80, false); | ||
case ManualControlCapability.MOVEMENT_COMMAND_TYPE.ROTATE_COUNTERCLOCKWISE: | ||
return this.sendRemoteControlCommand(0, 80, false); | ||
default: | ||
throw new Error("Invalid movementCommand."); | ||
} | ||
} | ||
|
||
/** | ||
* @private | ||
* @param {number} velocity | ||
* @param {number} angle | ||
* @param {boolean} audioHint | ||
* @returns {Promise<void>} | ||
*/ | ||
async sendRemoteControlCommand(velocity, angle, audioHint) { | ||
const res = await this.robot.sendCommand("set_properties", [ | ||
{ | ||
did: this.robot.deviceId, | ||
siid: this.miot_properties.manual_control.siid, | ||
piid: this.miot_properties.manual_control.piid, | ||
value: JSON.stringify({ | ||
spdv: velocity, | ||
spdw: angle, | ||
audio: audioHint === true ? "true" : "false", | ||
random: Math.floor(Math.random() * 1000) | ||
}) | ||
} | ||
]); | ||
this.lastCommand = new Date().getTime(); | ||
|
||
if (res?.length === 1) { | ||
if (res[0].code !== 0) { | ||
throw new Error("Error code " + res[0].code); | ||
} | ||
} else { | ||
throw new Error("Received invalid response"); | ||
} | ||
} | ||
} | ||
|
||
module.exports = DreameManualControlCapability; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters