Prototype 1

This commit is contained in:
Sören Beye 2022-07-25 18:54:15 +02:00
parent 8c1ba9ea2f
commit 7b51670598
29 changed files with 417 additions and 119 deletions

View File

@ -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);

View File

@ -25,7 +25,12 @@ class ValetudoRobot {
this.state = new entities.state.RobotState({
map: ValetudoRobot.DEFAULT_MAP
});
}
/**
* @public
*/
initialize() {
this.initInternalSubscriptions();
}

View File

@ -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;

View File

@ -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"),

View File

@ -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;
}
}

View File

@ -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;

View File

@ -34,7 +34,6 @@ class Dreame1CValetudoRobot extends DreameValetudoRobot {
)
);
this.lastMapPoll = new Date(0);
this.isCharging = false;
this.registerCapability(new capabilities.Dreame1CBasicControlCapability({

View File

@ -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;

View File

@ -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;
}
}
}

View 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;

View File

@ -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: {

View File

@ -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

View File

@ -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;

View File

@ -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"),

View File

@ -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({

View File

@ -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({

View File

@ -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({

View File

@ -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, "")}`
);
}
}

View File

@ -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;

View File

@ -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"),

View File

@ -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,

View File

@ -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;

View File

@ -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"
}
}
}
}
}
}
}
}

View File

@ -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"),

View File

@ -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"
}
}
}

View File

@ -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.

View File

@ -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)

View File

@ -18,7 +18,7 @@ export enum Capability {
MapSegmentEdit = "MapSegmentEditCapability",
MapSegmentRename = "MapSegmentRenameCapability",
MapSegmentation = "MapSegmentationCapability",
MapSnapshot = "MapSnapshotCapability",
MapSnapshot = "MapSnapshotRestoreCapability",
MappingPass = "MappingPassCapability",
PersistentMapControl = "PersistentMapControlCapability",
SpeakerTest = "SpeakerTestCapability",

View File

@ -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"},