mirror of
https://github.com/Hypfer/Valetudo.git
synced 2025-10-26 11:27:27 +00:00
Prototype 1
This commit is contained in:
parent
8c1ba9ea2f
commit
7b51670598
@ -37,6 +37,8 @@ class Valetudo {
|
||||
config: this.config,
|
||||
valetudoEventStore: this.valetudoEventStore
|
||||
});
|
||||
|
||||
this.robot.initialize();
|
||||
} catch (e) {
|
||||
Logger.error("Error while initializing robot implementation. Shutting down ", e);
|
||||
|
||||
|
||||
@ -25,7 +25,12 @@ class ValetudoRobot {
|
||||
this.state = new entities.state.RobotState({
|
||||
map: ValetudoRobot.DEFAULT_MAP
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
initialize() {
|
||||
this.initInternalSubscriptions();
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ const NotImplementedError = require("../NotImplementedError");
|
||||
* @template {import("../ValetudoRobot")} T
|
||||
* @extends Capability<T>
|
||||
*/
|
||||
class MapSnapshotCapability extends Capability {
|
||||
class MapSnapshotRestoreCapability extends Capability {
|
||||
/**
|
||||
* @abstract
|
||||
* @returns {Promise<Array<import("../../entities/core/ValetudoMapSnapshot")>>}
|
||||
@ -25,10 +25,10 @@ class MapSnapshotCapability extends Capability {
|
||||
}
|
||||
|
||||
getType() {
|
||||
return MapSnapshotCapability.TYPE;
|
||||
return MapSnapshotRestoreCapability.TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
MapSnapshotCapability.TYPE = "MapSnapshotCapability";
|
||||
MapSnapshotRestoreCapability.TYPE = "MapSnapshotRestoreCapability";
|
||||
|
||||
module.exports = MapSnapshotCapability;
|
||||
module.exports = MapSnapshotRestoreCapability;
|
||||
@ -16,7 +16,7 @@ module.exports = {
|
||||
MapSegmentEditCapability: require("./MapSegmentEditCapability"),
|
||||
MapSegmentRenameCapability: require("./MapSegmentRenameCapability"),
|
||||
MapSegmentationCapability: require("./MapSegmentationCapability"),
|
||||
MapSnapshotCapability: require("./MapSnapshotCapability"),
|
||||
MapSnapshotRestoreCapability: require("./MapSnapshotRestoreCapability"),
|
||||
MappingPassCapability: require("./MappingPassCapability"),
|
||||
PendingMapChangeHandlingCapability: require("./PendingMapChangeHandlingCapability"),
|
||||
PersistentMapControlCapability: require("./PersistentMapControlCapability"),
|
||||
|
||||
@ -8,7 +8,6 @@ class ValetudoMapSnapshot extends SerializableEntity {
|
||||
* @param {object} options
|
||||
* @param {string} options.id
|
||||
* @param {Date} [options.timestamp]
|
||||
* @param {import("../map/ValetudoMap")} [options.map]
|
||||
* @param {object} [options.metaData]
|
||||
* @class
|
||||
*/
|
||||
@ -17,7 +16,6 @@ class ValetudoMapSnapshot extends SerializableEntity {
|
||||
|
||||
this.id = options.id;
|
||||
this.timestamp = options.timestamp;
|
||||
this.map = options.map;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -37,7 +37,7 @@ class MiioValetudoRobot extends ValetudoRobot {
|
||||
this.embeddedDummycloudIp = this.implConfig["dummycloudIp"] ?? "127.0.0.1";
|
||||
this.dummycloudBindIp = this.implConfig["dummycloudBindIp"] ?? (this.config.get("embedded") ? "127.0.0.1" : "0.0.0.0");
|
||||
|
||||
this.mapUploadUrlPrefix = this.implConfig.mapUploadUrlPrefix ?? ("http://" + this.embeddedDummycloudIp + ":8079");
|
||||
this.fdsMockUrlPrefix = this.implConfig.fdsMockUrlPrefix ?? (`http://${this.embeddedDummycloudIp}:${MiioValetudoRobot.FDS_MOCK_PORT}`);
|
||||
|
||||
this.localSocket = new RetryWrapper(
|
||||
(() => {
|
||||
@ -160,6 +160,7 @@ class MiioValetudoRobot extends ValetudoRobot {
|
||||
|
||||
res.end();
|
||||
req.socket.destroy();
|
||||
req.socket.destroy();
|
||||
this.fdsUploadMutex.leave();
|
||||
}
|
||||
});
|
||||
@ -168,12 +169,27 @@ class MiioValetudoRobot extends ValetudoRobot {
|
||||
this.fdsMockServer.on("error", (e) => {
|
||||
Logger.error("FDSMockServer Error: ",e);
|
||||
});
|
||||
}
|
||||
|
||||
this.fdsMockServer.listen(8079, this.dummycloudBindIp, function() {
|
||||
Logger.info("FDSMockServer running on port " + 8079);
|
||||
initialize() {
|
||||
super.initialize();
|
||||
|
||||
this.initModelSpecificFDSWebserverRoutes(this.expressApp);
|
||||
|
||||
this.fdsMockServer.listen(MiioValetudoRobot.FDS_MOCK_PORT, this.dummycloudBindIp, function() {
|
||||
Logger.info(`FDSMockServer running on port ${MiioValetudoRobot.FDS_MOCK_PORT}`);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This function allows us to inject custom routes into the fds webserver
|
||||
*
|
||||
* @param {any} app The expressjs app instance
|
||||
*/
|
||||
initModelSpecificFDSWebserverRoutes(app) {
|
||||
//intentional
|
||||
}
|
||||
|
||||
get deviceId() {
|
||||
let deviceId = this.implConfig.deviceId;
|
||||
|
||||
@ -397,7 +413,7 @@ class MiioValetudoRobot extends ValetudoRobot {
|
||||
let result = {ok: true};
|
||||
|
||||
const expires = Math.floor(new Date(new Date().getTime() + 15 * 60000).getTime() / 1000); //+15min;
|
||||
let url = `${this.mapUploadUrlPrefix}/api/miio/fds_upload_handler?ts=${process.hrtime().toString().replace(/,/g, "")}&suffix=${key}&Expires=${expires}`;
|
||||
let url = `${this.fdsMockUrlPrefix}/api/miio/fds_upload_handler?ts=${process.hrtime().toString().replace(/,/g, "")}&suffix=${key}&Expires=${expires}`;
|
||||
|
||||
if (msg.method === "_sync.gen_tmp_presigned_url") {
|
||||
result[key] = indices.map(i => {
|
||||
@ -594,4 +610,6 @@ class MiioValetudoRobot extends ValetudoRobot {
|
||||
const DEVICE_CONF_KEY_VALUE_REGEX = /^(?<key>[A-Za-z\d:.]+)=(?<value>[A-Za-z\d:.]+)$/;
|
||||
const MAX_UPLOAD_FILESIZE = 4 * 1024 * 1024; // 4 MiB
|
||||
|
||||
MiioValetudoRobot.FDS_MOCK_PORT = 8079
|
||||
|
||||
module.exports = MiioValetudoRobot;
|
||||
|
||||
@ -34,7 +34,6 @@ class Dreame1CValetudoRobot extends DreameValetudoRobot {
|
||||
)
|
||||
);
|
||||
|
||||
this.lastMapPoll = new Date(0);
|
||||
this.isCharging = false;
|
||||
|
||||
this.registerCapability(new capabilities.Dreame1CBasicControlCapability({
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
const capabilities = require("./capabilities");
|
||||
|
||||
const ConsumableMonitoringCapability = require("../../core/capabilities/ConsumableMonitoringCapability");
|
||||
const DreameMapSnapshotStore = require("./DreameMapSnapshotStore");
|
||||
const DreameMiotServices = require("./DreameMiotServices");
|
||||
const DreameValetudoRobot = require("./DreameValetudoRobot");
|
||||
const entities = require("../../entities");
|
||||
@ -46,8 +47,6 @@ class DreameGen2ValetudoRobot extends DreameValetudoRobot {
|
||||
};
|
||||
});
|
||||
|
||||
this.lastMapPoll = new Date(0);
|
||||
|
||||
this.mode = 0; //Idle
|
||||
this.isCharging = false;
|
||||
this.errorCode = "0";
|
||||
@ -303,6 +302,20 @@ class DreameGen2ValetudoRobot extends DreameValetudoRobot {
|
||||
}
|
||||
}));
|
||||
|
||||
if (this.config.get("embedded") === true) {
|
||||
this.mapSnapshotStore = new DreameMapSnapshotStore();
|
||||
|
||||
this.mapSnapshotStore.initialize();
|
||||
|
||||
this.registerCapability(new capabilities.DreameMapSnapshotRestoreCapability({
|
||||
robot: this,
|
||||
mapSnapshotStore: this.mapSnapshotStore,
|
||||
siid: MIOT_SERVICES.MAP.SIID,
|
||||
piid: MIOT_SERVICES.MAP.PROPERTIES.MAP_RESTORE.PIID,
|
||||
urlPrefix: this.fdsMockUrlPrefix
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
this.state.upsertFirstMatchingAttribute(new entities.state.attributes.AttachmentStateAttribute({
|
||||
type: entities.state.attributes.AttachmentStateAttribute.TYPE.MOP,
|
||||
@ -570,7 +583,6 @@ class DreameGen2ValetudoRobot extends DreameValetudoRobot {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (this.stateNeedsUpdate === true) {
|
||||
let newState;
|
||||
let statusValue;
|
||||
@ -635,6 +647,41 @@ class DreameGen2ValetudoRobot extends DreameValetudoRobot {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async handleMapSnapshotUpload(data, query, params) {
|
||||
if (this.mapSnapshotStore) {
|
||||
if (this.state.map.metaData.vendorMapId !== undefined) {
|
||||
try {
|
||||
await this.mapSnapshotStore.storeSnapshot(data, this.state.map.metaData.vendorMapId);
|
||||
} catch (e) {
|
||||
Logger.error("Error while storing map snapshot");
|
||||
}
|
||||
} else {
|
||||
Logger.warn("Could not store new map snapshot due to vendorMapId missing in our state");
|
||||
}
|
||||
} else {
|
||||
return super.handleMapSnapshotUpload(data, query, params);
|
||||
}
|
||||
}
|
||||
|
||||
initModelSpecificFDSWebserverRoutes(app) {
|
||||
super.initModelSpecificFDSWebserverRoutes(app);
|
||||
|
||||
if (this.mapSnapshotStore !== undefined) {
|
||||
app.get("/mapSnapshots/:id", (req, res) => {
|
||||
|
||||
//TODO: logging!
|
||||
const snap = this.mapSnapshotStore.getSnapshotFileById(req.params.id);
|
||||
|
||||
if (snap) { //TODO: stream?
|
||||
res.write(snap, "binary");
|
||||
res.end(null, "binary");
|
||||
} else {
|
||||
res.sendStatus(404);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DreameGen2ValetudoRobot.MIOT_SERVICES = MIOT_SERVICES;
|
||||
|
||||
@ -45,6 +45,10 @@ class DreameMapParser {
|
||||
const entities = [];
|
||||
const metaData = {};
|
||||
|
||||
if (type === MAP_DATA_TYPES.RISM) {
|
||||
metaData.vendorMapId = parsedHeader.id;
|
||||
}
|
||||
|
||||
if (parsedHeader.robot_position.valid === true) {
|
||||
entities.push(
|
||||
new Map.PointMapEntity({
|
||||
@ -161,7 +165,11 @@ class DreameMapParser {
|
||||
});
|
||||
|
||||
if (rismResult.metaData?.dreamePendingMapChange !== undefined) {
|
||||
// TODO: if there is a pending map change, the vendorMapId of the rismMap is probably the ID of the new map?
|
||||
metaData.dreamePendingMapChange = rismResult.metaData.dreamePendingMapChange;
|
||||
} else {
|
||||
//TODO: in case the above is true, that map ID can't be trusted.. I think?
|
||||
metaData.vendorMapId = rismResult.metaData.vendorMapId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
145
backend/lib/robots/dreame/DreameMapSnapshotStore.js
Normal file
145
backend/lib/robots/dreame/DreameMapSnapshotStore.js
Normal file
@ -0,0 +1,145 @@
|
||||
const fs = require("fs");
|
||||
const Logger = require("../../Logger");
|
||||
const path = require("path");
|
||||
const Tools = require("../../utils/Tools");
|
||||
const uuid = require("uuid");
|
||||
const ValetudoMapSnapshot = require("../../entities/core/ValetudoMapSnapshot");
|
||||
|
||||
class DreameMapSnapshotStore {
|
||||
constructor() {
|
||||
this.location = "/data/valetudo_map_snapshots";
|
||||
this.manifestLocation = path.join(this.location,DreameMapSnapshotStore.MANIFEST_NAME);
|
||||
|
||||
this.manifest = {
|
||||
version: 1,
|
||||
snapshots: []
|
||||
};
|
||||
}
|
||||
|
||||
initialize() {
|
||||
if (fs.existsSync(this.manifestLocation)) {
|
||||
try {
|
||||
const manifestFromDisk = fs.readFileSync(this.manifestLocation, {"encoding": "utf-8"}).toString();
|
||||
const parsedManifest = JSON.parse(manifestFromDisk);
|
||||
|
||||
this.manifest.snapshots = parsedManifest.snapshots.map(s => {
|
||||
return new ValetudoMapSnapshot({
|
||||
id: s.id,
|
||||
timestamp: s.timestamp,
|
||||
metaData: s.metaData
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
Logger.error("Invalid DreameMapSnapshotStore manifest: ", e.message);
|
||||
|
||||
try {
|
||||
Tools.MK_DIR_PATH(path.dirname(this.manifestLocation));
|
||||
|
||||
const currentContents = fs.readdirSync(this.location);
|
||||
currentContents.forEach(fileName => {
|
||||
fs.rmSync(path.join(this.location, fileName));
|
||||
});
|
||||
} catch (e) {
|
||||
Logger.error("Cleanup of map snapshot location failed ", e.message);
|
||||
}
|
||||
|
||||
|
||||
this.persist();
|
||||
}
|
||||
} else {
|
||||
Tools.MK_DIR_PATH(path.dirname(this.manifestLocation));
|
||||
|
||||
this.persist();
|
||||
}
|
||||
}
|
||||
|
||||
persist() {
|
||||
fs.writeFileSync(this.manifestLocation, JSON.stringify(this.manifest, null, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @returns {Array<ValetudoMapSnapshot>}
|
||||
*/
|
||||
getSnapshots() {
|
||||
return this.manifest.snapshots;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} id
|
||||
* @returns {null|Buffer}
|
||||
*/
|
||||
getSnapshotFileById(id) {
|
||||
//Make sure to check that the ID exists so that nothing requests e.g. id "../../../etc/passwd"
|
||||
const snapshot = this.manifest.snapshots.find(s => {
|
||||
return s.id === id
|
||||
});
|
||||
|
||||
if (snapshot) {
|
||||
try {
|
||||
return fs.readFileSync(path.join(this.location, snapshot.id)); //TODO: promise?
|
||||
} catch(e) {
|
||||
Logger.warn(`Error while reading map snapshot ${snapshot.id} file from disk`, e)
|
||||
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Buffer} buf
|
||||
* @param {number} vendorMapId
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async storeSnapshot(buf, vendorMapId) {
|
||||
const id = uuid.v4();
|
||||
const snapshot = new ValetudoMapSnapshot({
|
||||
id: id,
|
||||
timestamp: new Date(),
|
||||
metaData: {
|
||||
vendorMapId: vendorMapId
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
await fs.promises.writeFile(path.join(this.location, id), buf);
|
||||
} catch (e) {
|
||||
Logger.warn(`Error while storing map snapshot ${id}`, e);
|
||||
|
||||
return; //abort
|
||||
}
|
||||
|
||||
this.manifest.snapshots.push(snapshot);
|
||||
|
||||
//TODO: make sure that the order is correct
|
||||
this.manifest.snapshots.sort((a, b) => {
|
||||
return b.timestamp.getTime() - a.timestamp.getTime();
|
||||
});
|
||||
|
||||
if (this.manifest.snapshots.length > DreameMapSnapshotStore.SNAPSHOT_LIMIT) {
|
||||
const oldestSnapshot = this.manifest.snapshots.pop();
|
||||
|
||||
try {
|
||||
fs.rmSync(path.join(this.location, oldestSnapshot.id));
|
||||
} catch (e) {
|
||||
Logger.warn(`Error while deleting map snapshot ${id}`, e);
|
||||
|
||||
return; //abort
|
||||
}
|
||||
}
|
||||
|
||||
Logger.info(`Stored new map snapshot. ID: ${id}, VendorMapId: ${vendorMapId}`);
|
||||
|
||||
this.persist();
|
||||
}
|
||||
}
|
||||
|
||||
DreameMapSnapshotStore.MANIFEST_NAME = "manifest.json";
|
||||
DreameMapSnapshotStore.SNAPSHOT_LIMIT = 3;
|
||||
|
||||
module.exports = DreameMapSnapshotStore;
|
||||
@ -495,6 +495,13 @@ module.exports = {
|
||||
|
||||
CLOUD_FILE_NAME_2: {
|
||||
PIID: 8 //irrelevant for us
|
||||
},
|
||||
|
||||
MAP_RESTORE: {
|
||||
PIID: 10
|
||||
},
|
||||
MAP_RESTORE_STATUS: {
|
||||
PIID: 11
|
||||
}
|
||||
},
|
||||
ACTIONS: {
|
||||
|
||||
@ -161,15 +161,26 @@ class DreameValetudoRobot extends MiioValetudoRobot {
|
||||
data: data.toString()
|
||||
});
|
||||
} else if (typeof query?.suffix === "string" && query.suffix.endsWith(".tbz2")) {
|
||||
Logger.trace("Received unhandled map backup", {
|
||||
query: query,
|
||||
params: params
|
||||
});
|
||||
await this.handleMapSnapshotUpload(data, query, params);
|
||||
} else {
|
||||
await this.preprocessAndParseMap(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @protected
|
||||
*
|
||||
* @param {Buffer} data
|
||||
* @param {object} query implementation specific query parameters
|
||||
* @param {object} params implementation specific url parameters
|
||||
*/
|
||||
async handleMapSnapshotUpload(data, query, params) {
|
||||
Logger.trace("Received unhandled map backup", {
|
||||
query: query,
|
||||
params: params
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @param {Buffer| string} data
|
||||
|
||||
@ -0,0 +1,57 @@
|
||||
const DreameMiotHelper = require("../DreameMiotHelper");
|
||||
const MapSnapshotRestoreCapability = require("../../../core/capabilities/MapSnapshotRestoreCapability");
|
||||
|
||||
/**
|
||||
* @extends MapSnapshotRestoreCapability<import("../DreameValetudoRobot")>
|
||||
*/
|
||||
class DreameMapSnapshotRestoreCapability extends MapSnapshotRestoreCapability {
|
||||
/**
|
||||
* @param {object} options
|
||||
* @param {import("../DreameValetudoRobot")} options.robot
|
||||
* @param {import("../DreameMapSnapshotStore")} options.mapSnapshotStore
|
||||
*
|
||||
* @param {number} options.siid MIOT Service ID
|
||||
* @param {number} options.piid MIOT Property ID
|
||||
* @param {string} options.urlPrefix
|
||||
*/
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
this.mapSnapshotStore = options.mapSnapshotStore;
|
||||
this.helper = new DreameMiotHelper({robot: this.robot});
|
||||
|
||||
this.siid = options.siid;
|
||||
this.piid = options.piid;
|
||||
|
||||
this.urlPrefix = options.urlPrefix
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @returns {Promise<Array<import("../../../entities/core/ValetudoMapSnapshot")>>}
|
||||
*/
|
||||
async getSnapshots() {
|
||||
return this.mapSnapshotStore.getSnapshots()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("../../../entities/core/ValetudoMapSnapshot")} snapshot
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async restoreSnapshot(snapshot) {
|
||||
const snap = this.mapSnapshotStore.getSnapshots().find(s => {
|
||||
return s.id === snapshot.id;
|
||||
})
|
||||
|
||||
if (!snap) {
|
||||
throw new Error("Missing Snapshot");
|
||||
}
|
||||
|
||||
return this.helper.writeProperty(this.siid, this.piid, JSON.stringify({
|
||||
map_id: snap.metaData.vendorMapId,
|
||||
map_url: `${this.urlPrefix}/mapSnapshots/${snap.id}`
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DreameMapSnapshotRestoreCapability;
|
||||
@ -20,6 +20,7 @@ module.exports = {
|
||||
DreameMapSegmentEditCapability: require("./DreameMapSegmentEditCapability"),
|
||||
DreameMapSegmentRenameCapability: require("./DreameMapSegmentRenameCapability"),
|
||||
DreameMapSegmentationCapability: require("./DreameMapSegmentationCapability"),
|
||||
DreameMapSnapshotRestoreCapability: require("./DreameMapSnapshotRestoreCapability"),
|
||||
DreameMappingPassCapability: require("./DreameMappingPassCapability"),
|
||||
DreameMopDockWaterUsageControlCapability: require("./DreameMopDockWaterUsageControlCapability"),
|
||||
DreamePendingMapChangeHandlingCapability: require("./DreamePendingMapChangeHandlingCapability"),
|
||||
|
||||
@ -14,7 +14,7 @@ class RoborockM1SValetudoRobot extends RoborockValetudoRobot {
|
||||
constructor(options) {
|
||||
super(Object.assign({}, options, {fanSpeeds: FAN_SPEEDS}));
|
||||
|
||||
this.registerCapability(new capabilities.RoborockMapSnapshotCapability({
|
||||
this.registerCapability(new capabilities.RoborockMapSnapshotRestoreCapability({
|
||||
robot: this
|
||||
}));
|
||||
this.registerCapability(new capabilities.RoborockCombinedVirtualRestrictionsCapability({
|
||||
|
||||
@ -16,7 +16,7 @@ class RoborockS5ValetudoRobot extends RoborockValetudoRobot {
|
||||
constructor(options) {
|
||||
super(Object.assign({}, options, {fanSpeeds: FAN_SPEEDS}));
|
||||
|
||||
this.registerCapability(new capabilities.RoborockMapSnapshotCapability({
|
||||
this.registerCapability(new capabilities.RoborockMapSnapshotRestoreCapability({
|
||||
robot: this
|
||||
}));
|
||||
this.registerCapability(new capabilities.RoborockCombinedVirtualRestrictionsCapability({
|
||||
|
||||
@ -16,7 +16,7 @@ class RoborockS6ValetudoRobot extends RoborockValetudoRobot {
|
||||
constructor(options) {
|
||||
super(Object.assign({}, options, {fanSpeeds: FAN_SPEEDS}));
|
||||
|
||||
this.registerCapability(new capabilities.RoborockMapSnapshotCapability({
|
||||
this.registerCapability(new capabilities.RoborockMapSnapshotRestoreCapability({
|
||||
robot: this
|
||||
}));
|
||||
this.registerCapability(new capabilities.RoborockCombinedVirtualRestrictionsCapability({
|
||||
|
||||
@ -133,13 +133,13 @@ class RoborockValetudoRobot extends MiioValetudoRobot {
|
||||
if (Array.isArray(msg.params?.indexes)) {
|
||||
msg.params.indexes.forEach(idx => {
|
||||
mapUploadUrls.push(
|
||||
`${this.mapUploadUrlPrefix}/api/miio/fds_upload_handler/${filename}_${idx}?${process.hrtime().toString().replace(/,/g, "")}`
|
||||
`${this.fdsMockUrlPrefix}/api/miio/fds_upload_handler/${filename}_${idx}?${process.hrtime().toString().replace(/,/g, "")}`
|
||||
);
|
||||
});
|
||||
} else {
|
||||
for (let i = 0; i < 4; i++) {
|
||||
mapUploadUrls.push(
|
||||
`${this.mapUploadUrlPrefix}/api/miio/fds_upload_handler/${filename}_${i}?${process.hrtime().toString().replace(/,/g, "")}`
|
||||
`${this.fdsMockUrlPrefix}/api/miio/fds_upload_handler/${filename}_${i}?${process.hrtime().toString().replace(/,/g, "")}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
const MapSnapshotCapability = require("../../../core/capabilities/MapSnapshotCapability");
|
||||
const MapSnapshotRestoreCapability = require("../../../core/capabilities/MapSnapshotRestoreCapability");
|
||||
const ValetudoMapSnapshot = require("../../../entities/core/ValetudoMapSnapshot");
|
||||
|
||||
/**
|
||||
* @extends MapSnapshotCapability<import("../RoborockValetudoRobot")>
|
||||
* @extends MapSnapshotRestoreCapability<import("../RoborockValetudoRobot")>
|
||||
*/
|
||||
class RoborockMapSnapshotCapability extends MapSnapshotCapability {
|
||||
class RoborockMapSnapshotRestoreCapability extends MapSnapshotRestoreCapability {
|
||||
/**
|
||||
* @returns {Promise<Array<import("../../../entities/core/ValetudoMapSnapshot")>>}
|
||||
*/
|
||||
@ -29,4 +29,4 @@ class RoborockMapSnapshotCapability extends MapSnapshotCapability {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RoborockMapSnapshotCapability;
|
||||
module.exports = RoborockMapSnapshotRestoreCapability;
|
||||
@ -15,7 +15,7 @@ module.exports = {
|
||||
RoborockMapSegmentRenameCapability: require("./RoborockMapSegmentRenameCapability"),
|
||||
RoborockMapSegmentSimpleCapability: require("./RoborockMapSegmentSimpleCapability"),
|
||||
RoborockMapSegmentationCapability: require("./RoborockMapSegmentationCapability"),
|
||||
RoborockMapSnapshotCapability: require("./RoborockMapSnapshotCapability"),
|
||||
RoborockMapSnapshotRestoreCapability: require("./RoborockMapSnapshotRestoreCapability"),
|
||||
RoborockMultiMapMapResetCapability: require("./RoborockMultiMapMapResetCapability"),
|
||||
RoborockMultiMapPersistentMapControlCapability: require("./RoborockMultiMapPersistentMapControlCapability"),
|
||||
RoborockPersistentMapControlCapability: require("./RoborockPersistentMapControlCapability"),
|
||||
|
||||
@ -58,7 +58,7 @@ const CAPABILITY_TYPE_TO_ROUTER_MAPPING = {
|
||||
[capabilities.ZoneCleaningCapability.TYPE]: capabilityRouters.ZoneCleaningCapabilityRouter,
|
||||
[capabilities.GoToLocationCapability.TYPE]: capabilityRouters.GoToLocationCapabilityRouter,
|
||||
[capabilities.WifiConfigurationCapability.TYPE]: capabilityRouters.WifiConfigurationCapabilityRouter,
|
||||
[capabilities.MapSnapshotCapability.TYPE]: capabilityRouters.MapSnapshotCapabilityRouter,
|
||||
[capabilities.MapSnapshotRestoreCapability.TYPE]: capabilityRouters.MapSnapshotRestoreCapabilityRouter,
|
||||
[capabilities.LocateCapability.TYPE]: capabilityRouters.LocateCapabilityRouter,
|
||||
[capabilities.ManualControlCapability.TYPE]: capabilityRouters.ManualControlCapabilityRouter,
|
||||
[capabilities.CombinedVirtualRestrictionsCapability.TYPE]: capabilityRouters.CombinedVirtualRestrictionsCapabilityRouter,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
const CapabilityRouter = require("./CapabilityRouter");
|
||||
const ValetudoMapSnapshot = require("../../entities/core/ValetudoMapSnapshot");
|
||||
|
||||
class MapSnapshotCapabilityRouter extends CapabilityRouter {
|
||||
class MapSnapshotRestoreCapabilityRouter extends CapabilityRouter {
|
||||
initRoutes() {
|
||||
this.router.get("/", async (req, res) => {
|
||||
res.json(await this.capability.getSnapshots());
|
||||
@ -22,4 +22,4 @@ class MapSnapshotCapabilityRouter extends CapabilityRouter {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = MapSnapshotCapabilityRouter;
|
||||
module.exports = MapSnapshotRestoreCapabilityRouter;
|
||||
@ -1,79 +1,79 @@
|
||||
{
|
||||
"/api/v2/robot/capabilities/MapSnapshotCapability": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"MapSnapshotCapability"
|
||||
],
|
||||
"summary": "Get map snapshots",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Ok",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/ValetudoMapSnapshot"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"tags": [
|
||||
"MapSnapshotCapability"
|
||||
],
|
||||
"summary": "Restore map snapshot",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"restore"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/components/responses/200"
|
||||
},
|
||||
"400": {
|
||||
"$ref": "#/components/responses/400"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v2/robot/capabilities/MapSnapshotCapability/properties": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"MapSnapshotCapability"
|
||||
],
|
||||
"summary": "Get various capability-related properties",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Ok",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
"/api/v2/robot/capabilities/MapSnapshotRestoreCapability": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"MapSnapshotRestoreCapability"
|
||||
],
|
||||
"summary": "Get map snapshots",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Ok",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/ValetudoMapSnapshot"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"put": {
|
||||
"tags": [
|
||||
"MapSnapshotRestoreCapability"
|
||||
],
|
||||
"summary": "Restore map snapshots",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"action": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"restore"
|
||||
]
|
||||
},
|
||||
"id": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/components/responses/200"
|
||||
},
|
||||
"400": {
|
||||
"$ref": "#/components/responses/400"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v2/robot/capabilities/MapSnapshotRestoreCapability/properties": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"MapSnapshotRestoreCapability"
|
||||
],
|
||||
"summary": "Get various capability-related properties",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Ok",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11,7 +11,7 @@ module.exports = {
|
||||
MapSegmentEditCapabilityRouter: require("./MapSegmentEditCapabilityRouter"),
|
||||
MapSegmentRenameCapabilityRouter: require("./MapSegmentRenameCapabilityRouter"),
|
||||
MapSegmentationCapabilityRouter: require("./MapSegmentationCapabilityRouter"),
|
||||
MapSnapshotCapabilityRouter: require("./MapSnapshotCapabilityRouter"),
|
||||
MapSnapshotRestoreCapabilityRouter: require("./MapSnapshotRestoreCapabilityRouter"),
|
||||
MappingPassCapabilityRouter: require("./MappingPassCapabilityRouter"),
|
||||
PendingMapChangeHandlingCapabilityRouter: require("./PendingMapChangeHandlingCapabilityRouter"),
|
||||
PresetSelectionCapabilityRouter: require("./PresetSelectionCapabilityRouter"),
|
||||
|
||||
@ -47,7 +47,7 @@ You need to edit the newly created file in order to be able to talk with your ro
|
||||
"deviceId": 12345678,
|
||||
"cloudSecret": "aBcdEfgh",
|
||||
"localSecret": "123456788989074560w34aaffasf",
|
||||
"mapUploadUrlPrefix": "http://192.168.xxx.valetudoIp:8079"
|
||||
"fdsMockUrlPrefix": "http://192.168.xxx.valetudoIp:8079"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,7 +99,7 @@ This capability enables you to clean detected segments.
|
||||
If you're new to Valetudo, you might be referring to Segments as Rooms. It's the same thing.
|
||||
I just didn't like the term room, because they don't necessarily have to actually be rooms.
|
||||
|
||||
## MapSnapshotCapability <a id="MapSnapshotCapability"></a>
|
||||
## MapSnapshotRestoreCapability <a id="MapSnapshotRestoreCapability"></a>
|
||||
|
||||
This capability enables you to list all existing map snapshots as well as restore one of them.
|
||||
|
||||
|
||||
@ -75,7 +75,7 @@ Capability | <a href='#dreame_1c'>1C</a> | <a href='#dreame_1t'>1T</a> | <a href
|
||||
[MapSegmentEditCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#MapSegmentEditCapability) | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:red;">No</span> | <span style="color:green;">Yes</span>
|
||||
[MapSegmentRenameCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#MapSegmentRenameCapability) | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:red;">No</span> | <span style="color:green;">Yes</span>
|
||||
[MapSegmentationCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#MapSegmentationCapability) | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:red;">No</span> | <span style="color:green;">Yes</span>
|
||||
[MapSnapshotCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#MapSnapshotCapability) | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:green;">Yes</span> | <span style="color:red;">No</span> | <span style="color:green;">Yes</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span>
|
||||
[MapSnapshotRestoreCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#MapSnapshotRestoreCapability) | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:green;">Yes</span> | <span style="color:red;">No</span> | <span style="color:green;">Yes</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span>
|
||||
[MappingPassCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#MappingPassCapability) | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:red;">No</span> | <span style="color:green;">Yes</span> | <span style="color:red;">No</span> | <span style="color:green;">Yes</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span>
|
||||
[PendingMapChangeHandlingCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#PendingMapChangeHandlingCapability) | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span>
|
||||
[PersistentMapControlCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#PersistentMapControlCapability) | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:red;">No</span> | <span style="color:red;">No</span> | <span style="color:green;">Yes</span> | <span style="color:red;">No</span> | <span style="color:green;">Yes</span> | <span style="color:red;">No</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:green;">Yes</span> | <span style="color:red;">No</span> | <span style="color:green;">Yes</span>
|
||||
@ -852,7 +852,7 @@ Still works finefor most use-cases
|
||||
- [MapSegmentEditCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#MapSegmentEditCapability)
|
||||
- [MapSegmentRenameCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#MapSegmentRenameCapability)
|
||||
- [MapSegmentationCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#MapSegmentationCapability)
|
||||
- [MapSnapshotCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#MapSnapshotCapability)
|
||||
- [MapSnapshotRestoreCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#MapSnapshotRestoreCapability)
|
||||
- [PersistentMapControlCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#PersistentMapControlCapability)
|
||||
- [QuirksCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#QuirksCapability)
|
||||
- [SpeakerTestCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#SpeakerTestCapability)
|
||||
@ -981,7 +981,7 @@ Still works fine for most use-cases
|
||||
- [MapSegmentEditCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#MapSegmentEditCapability)
|
||||
- [MapSegmentRenameCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#MapSegmentRenameCapability)
|
||||
- [MapSegmentationCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#MapSegmentationCapability)
|
||||
- [MapSnapshotCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#MapSnapshotCapability)
|
||||
- [MapSnapshotRestoreCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#MapSnapshotRestoreCapability)
|
||||
- [PersistentMapControlCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#PersistentMapControlCapability)
|
||||
- [QuirksCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#QuirksCapability)
|
||||
- [SpeakerTestCapability](https://valetudo.cloud/pages/general/capabilities-overview.html#SpeakerTestCapability)
|
||||
|
||||
@ -18,7 +18,7 @@ export enum Capability {
|
||||
MapSegmentEdit = "MapSegmentEditCapability",
|
||||
MapSegmentRename = "MapSegmentRenameCapability",
|
||||
MapSegmentation = "MapSegmentationCapability",
|
||||
MapSnapshot = "MapSnapshotCapability",
|
||||
MapSnapshot = "MapSnapshotRestoreCapability",
|
||||
MappingPass = "MappingPassCapability",
|
||||
PersistentMapControl = "PersistentMapControlCapability",
|
||||
SpeakerTest = "SpeakerTestCapability",
|
||||
|
||||
@ -46,7 +46,7 @@ const options = {
|
||||
{name: "MapResetCapability", description: "Map reset capability"},
|
||||
{name: "MapSegmentEditCapability", description: "Map segment edit capability"},
|
||||
{name: "MapSegmentRenameCapability", description: "Map segment rename capability"},
|
||||
{name: "MapSnapshotCapability", description: "Map snapshots capability"},
|
||||
{name: "MapSnapshotRestoreCapability", description: "Map snapshot restore capability"},
|
||||
{name: "PersistentMapControlCapability", description: "Persistent map control capability"},
|
||||
{name: "SpeakerTestCapability", description: "Speaker test capability"},
|
||||
{name: "SpeakerVolumeControlCapability", description: "Speaker volume control capability"},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user