Skip to content
  • Home
  • Recent
  • Tags
  • 0 Unread 0
  • Categories
  • Unreplied
  • Popular
  • GitHub
  • Docu
  • Hilfe
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
ioBroker Logo

Community Forum

donate donate
  1. ioBroker Community Home
  2. Deutsch
  3. Skripten / Logik
  4. Roborock S5 "cleaning area" per Tastendruck?

NEWS

  • Monatsrückblick Januar/Februar 2026 ist online!
    BluefoxB
    Bluefox
    18
    1
    770

  • Jahresrückblick 2025 – unser neuer Blogbeitrag ist online! ✨
    BluefoxB
    Bluefox
    18
    1
    6.2k

  • Neuer Blogbeitrag: Monatsrückblick - Dezember 2025 🎄
    BluefoxB
    Bluefox
    13
    1
    1.5k

Roborock S5 "cleaning area" per Tastendruck?

Scheduled Pinned Locked Moved Skripten / Logik
javascript
57 Posts 9 Posters 11.5k Views 15 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • 0 0018

    @AlCalzone sagte in Roborock S5 "cleaning area" per Tastendruck?:
    Beispiel: Sauger steht bei dir normalerweise rechts, dann 180 => 0, 270 => 90, 90 => 270, 0 => 180.

    Ja genau, mein Sauger steht rechts vom Dock. Habe jetzt nachdem ich die Daten angepasst habe, leider das Problem das meine Bereiche nicht mehr mit den Koordinaten aus der Valetudo Map
    übereinstimmen. Muss ich hier auch noch was drehen? Den die Zonenkordination sind ja dann auch gedreht oder?

    AlCalzoneA Offline
    AlCalzoneA Offline
    AlCalzone
    Developer
    wrote on last edited by
    #45

    @0018 Inwiefern stimmt es nicht mehr überein?

    Warum `sudo` böse ist: https://forum.iobroker.net/post/17109

    0 1 Reply Last reply
    0
    • AlCalzoneA AlCalzone

      @0018 Inwiefern stimmt es nicht mehr überein?

      0 Offline
      0 Offline
      0018
      wrote on last edited by 0018
      #46

      @AlCalzone Damit das ganze besser zu erklären ist hier mit Screenshots.

      1. So sieht meine Karte in Valetudo und die dazugehörigen Koordinaten der Zone1 aus:
        Karte Valetudo

      2. Aus meiner Sicht steht mein Robo rechts vom Dock, daher folgende Anpassung deines Scriptes:

      const rooms: Record<string, Rectangle[]> = {
          "Test": [[26397, 22791, 29302, 24138]],
      };
      

      ab Zeile 119:

          // Valetudo zählt von oben links nach unten rechts, d.h. die Y-Koordinaten
          // sind entgegengesetzt der mathematischen Definition
          let c2r = [robot[0] - charger[0], charger[1] - robot[1]];
          const angle = Math.atan2(c2r[1], c2r[0]) * 180 / Math.PI;
          log("angle = " + angle);
          if (angle <= 45 && angle >= -45) {
              log("Sauger steht rechts");
              return 0;
          } else if (angle > 45 && angle < 135) {
              log("Sauger steht oben");
              return 90;
          } else if (angle < -45 && angle > -135) {
              log("Sauger steht unten");
              return 270;
          } else {
              return 180;
          }
      }
      
      1. Wenn ich dann die Zonnenreinigung starte, sieht das auf der Vaeltuda Map dann wie folgt aus:
        Robot180

      2. Als test mal mit dem Standard:

          // Valetudo zählt von oben links nach unten rechts, d.h. die Y-Koordinaten
          // sind entgegengesetzt der mathematischen Definition
          let c2r = [robot[0] - charger[0], charger[1] - robot[1]];
          const angle = Math.atan2(c2r[1], c2r[0]) * 180 / Math.PI;
          log("angle = " + angle);
          if (angle <= 45 && angle >= -45) {
              log("Sauger steht rechts");
              return 180;
          } else if (angle > 45 && angle < 135) {
              log("Sauger steht oben");
              return 270;
          } else if (angle < -45 && angle > -135) {
              log("Sauger steht unten");
              return 90;
          } else {
              return 0;
          }
      }
      

      ergibt folgendes Ergebniss:
      Robot0

      Habe auch schon 90 + 270 probiert, leider auch ohne Erfolg.
      Im Script habe ich mal den Angle mit geloggt und bei den Versuchen kommt immer ähnliches raus....

      javascript.0	2019-12-09 21:12:16.299	info	(2094) script.js.common.4_MiRobot.MiRobot_Zone_neu: angle = -2.981461219982192
      javascript.0	2019-12-09 21:06:13.153	info	(2094) script.js.common.4_MiRobot.MiRobot_Zone_neu: angle = 2.8912695962205643
      javascript.0	2019-12-09 20:56:30.228	info	(2094) script.js.common.4_MiRobot.MiRobot_Zone_neu: angle = -1.461310090881229
      

      Vielleicht siehst du ja auf Anhieb woran es liegt?

      Mfg
      0018

      AlCalzoneA 1 Reply Last reply
      0
      • 0 0018

        @AlCalzone Damit das ganze besser zu erklären ist hier mit Screenshots.

        1. So sieht meine Karte in Valetudo und die dazugehörigen Koordinaten der Zone1 aus:
          Karte Valetudo

        2. Aus meiner Sicht steht mein Robo rechts vom Dock, daher folgende Anpassung deines Scriptes:

        const rooms: Record<string, Rectangle[]> = {
            "Test": [[26397, 22791, 29302, 24138]],
        };
        

        ab Zeile 119:

            // Valetudo zählt von oben links nach unten rechts, d.h. die Y-Koordinaten
            // sind entgegengesetzt der mathematischen Definition
            let c2r = [robot[0] - charger[0], charger[1] - robot[1]];
            const angle = Math.atan2(c2r[1], c2r[0]) * 180 / Math.PI;
            log("angle = " + angle);
            if (angle <= 45 && angle >= -45) {
                log("Sauger steht rechts");
                return 0;
            } else if (angle > 45 && angle < 135) {
                log("Sauger steht oben");
                return 90;
            } else if (angle < -45 && angle > -135) {
                log("Sauger steht unten");
                return 270;
            } else {
                return 180;
            }
        }
        
        1. Wenn ich dann die Zonnenreinigung starte, sieht das auf der Vaeltuda Map dann wie folgt aus:
          Robot180

        2. Als test mal mit dem Standard:

            // Valetudo zählt von oben links nach unten rechts, d.h. die Y-Koordinaten
            // sind entgegengesetzt der mathematischen Definition
            let c2r = [robot[0] - charger[0], charger[1] - robot[1]];
            const angle = Math.atan2(c2r[1], c2r[0]) * 180 / Math.PI;
            log("angle = " + angle);
            if (angle <= 45 && angle >= -45) {
                log("Sauger steht rechts");
                return 180;
            } else if (angle > 45 && angle < 135) {
                log("Sauger steht oben");
                return 270;
            } else if (angle < -45 && angle > -135) {
                log("Sauger steht unten");
                return 90;
            } else {
                return 0;
            }
        }
        

        ergibt folgendes Ergebniss:
        Robot0

        Habe auch schon 90 + 270 probiert, leider auch ohne Erfolg.
        Im Script habe ich mal den Angle mit geloggt und bei den Versuchen kommt immer ähnliches raus....

        javascript.0	2019-12-09 21:12:16.299	info	(2094) script.js.common.4_MiRobot.MiRobot_Zone_neu: angle = -2.981461219982192
        javascript.0	2019-12-09 21:06:13.153	info	(2094) script.js.common.4_MiRobot.MiRobot_Zone_neu: angle = 2.8912695962205643
        javascript.0	2019-12-09 20:56:30.228	info	(2094) script.js.common.4_MiRobot.MiRobot_Zone_neu: angle = -1.461310090881229
        

        Vielleicht siehst du ja auf Anhieb woran es liegt?

        AlCalzoneA Offline
        AlCalzoneA Offline
        AlCalzone
        Developer
        wrote on last edited by AlCalzone
        #47

        @0018 sagte in Roborock S5 "cleaning area" per Tastendruck?:

        Vielleicht siehst du ja auf Anhieb woran es liegt?

        Auf dem 1. Bild im Dock sieht es für mich so aus als zeigt der Sauger nach links, d.h. Winkel 0°. Hast du die Koordinaten direkt aus Valetudo? Dann müssen ggf. die Y-Koordinaten aller Zonen invertiert werden, da Valetudo anscheinend anders zählt als der Sauger selbst:

        y(für Skript) = 51200 - y(Valetudo)
        

        Warum `sudo` böse ist: https://forum.iobroker.net/post/17109

        0 1 Reply Last reply
        0
        • AlCalzoneA AlCalzone

          @0018 sagte in Roborock S5 "cleaning area" per Tastendruck?:

          Vielleicht siehst du ja auf Anhieb woran es liegt?

          Auf dem 1. Bild im Dock sieht es für mich so aus als zeigt der Sauger nach links, d.h. Winkel 0°. Hast du die Koordinaten direkt aus Valetudo? Dann müssen ggf. die Y-Koordinaten aller Zonen invertiert werden, da Valetudo anscheinend anders zählt als der Sauger selbst:

          y(für Skript) = 51200 - y(Valetudo)
          
          0 Offline
          0 Offline
          0018
          wrote on last edited by
          #48

          @AlCalzone Ja, die Koordinaten sind direkt aus Valetudo unter http://IPdesSaugers/zone
          Habe jetzt die Y-Koordinaten nach Vorgabe umgerechnet und jetzt stimmen die Zonen überein.
          Der Tipp mit

          // @ts-ignore 
          

          hat auch funktioniert. Scheint also auf den ersten Blick erstmal alles zu passen. Ich werde es jetzt die nächsten Wochen mal testen.

          Vielen Dank!

          Mfg
          0018

          1 Reply Last reply
          1
          • AlCalzoneA AlCalzone

            @kaiserm Also wie versprochen:

            Hier die Variante, die bei mir im Einsatz ist (inklusive Test der Kartenorientierung via Valetudo). Muss als TypeScript angelegt werden und benötigt das zusätzliche Paket "axios":

            import axios from "axios";
            
            // Hier den Hostnamen und Zugangsdaten eintragen, unter dem Valetudo erreichbar ist
            const roboHostname = "rockrobo.fritz.box";
            const valetudoAuth = {
            	username: 'valetudo-username',
            	password: 'valetudo-password'
            }
            // Hier den State eintragen, der den aktuellen Sauger-Status angibt
            const idVacuumState = "mihome-vacuum.0.info.state";
            
            const center: Point = [25600, 25600];
            // Koordiaten zählen von unten links (0,0) nach oben rechts (51200,51200)
            // Die folgenden Koordinaten gehen von nicht rotierter Karte (Winkel 0) aus
            const rooms: Record<string, Rectangle[]> = {
                "Küche": [[21600, 29100, 23500, 33100]],
                "Flur": [[18400, 24000, 22200, 27100], [19500, 27100, 21600, 30400]],
                "Wohnzimmer": [[17600, 20000, 21600, 24000], [21600, 19200, 24200, 23200]],
                "Schlafzimmer": [[24100, 23200, 26100, 27400], [22100, 23800, 24100, 27400]],
                "Bad": [[21500, 27500, 23200, 29000]],
            };
            
            // ===============================================================================
            
            type Rectangle = [number, number, number, number];
            type Point = [number, number];
            
            /** rotates a rectangle by 90° around the absolute origin */
            function rotate90([x1, y1, x2, y2]: Rectangle): Rectangle {
                return [-y2, x1, -y1, x2];
            }
            
            /** rotates a rectangle by 180° around the absolute origin */
            function rotate180([x1, y1, x2, y2]: Rectangle): Rectangle {
                return [-x2, -y2, -x1, -y1];
            }
            
            /** Rotates a rectangle around the given center by the given angle */
            function rotate(rect: Rectangle, center: Point, angle: number) {
                rect = [rect[0] - center[0], rect[1] - center[1], rect[2] - center[0], rect[3] - center[1]];
                if (angle % 180 === 90) {
                    rect = rotate90(rect);
                    angle -= 90;
                }
                if (angle === 180) {
                    rect = rotate180(rect);
                }
                rect = [rect[0] + center[0], rect[1] + center[1], rect[2] + center[0], rect[3] + center[1]];
                return rect;
            }
            
            for (const room of Object.keys(rooms) as (keyof typeof rooms)[]) {
                createState(`Staubsauger.${room}`, {
                    type: "boolean",
                    read: true,
                    write: true,
                    role: "switch",
                    name: `${room} saugen`,
                });
                on({ id: `javascript.${instance}.Staubsauger.${room}`, val: true, ack: false }, async () => {
                    if (getState(idVacuumState).val !== 8) await cancelCurrentAction();
            
                    if (getMapRotation() === -1 /* unbekannt */) {
                        // We need to test the map rotation
                        const rotation = await testMapRotation();
                        log(`Die Karte ist ${rotation !== 0 ? `um ${rotation}° ` : "nicht "}rotiert.`);
                        await rememberMapRotation(rotation);
                    }
            
                    // Now that we know how the map is rotated, we can clean the room
                    cleanRoom(room);
                });
            }
            
            createState(`Staubsauger.stop`, {
                type: "boolean",
                read: true,
                write: true,
                role: "switch",
                name: `Staubsauger anhalten`,
            });
            on({ id: `javascript.${instance}.Staubsauger.stop`, val: true, ack: false }, () => {
                stopCleanup();
            });
            
            const idMapRotated = "Staubsauger.info.mapRotated";
            createState(idMapRotated, {
                type: "number",
                read: true,
                write: false,
                role: "indicator",
                states: {
                    "-1": "unknown",
                    "0": "Robo links vom Dock",
                    "90": "Robo unten vom Dock",
                    "180": "Robo rechts vom Dock",
                    "270": "Robo oben vom Dock",
                },
                name: `Wie die Karte rotiert ist`,
            });
            
            /** 
             * Bestimmt die Kartenrotation wenn der Staubsauger neben dem Dock steht
             * 0 Grad bedeutet, der Sauger steht links
             */
            async function testMapRotation(): Promise<number> {
                log("Teste Kartenorientierung...");
                const { data: { charger, robot } } = await axios({
                    url: `http://${roboHostname}/api/map/latest`,
                    auth: valetudoAuth
                });
            
                // Valetudo zählt von oben links nach unten rechts, d.h. die Y-Koordinaten
                // sind entgegengesetzt der mathematischen Definition
                let c2r = [robot[0] - charger[0], charger[1] - robot[1]];
                const angle = Math.atan2(c2r[1], c2r[0]) * 180 / Math.PI;
                if (angle <= 45 && angle >= -45) {
                    // Sauger steht rechts
                    return 180;
                } else if (angle > 45 && angle < 135) {
                    // Sauger steht oben
                    return 270;
                } else if (angle < -45 && angle > -135) {
                    // Sauger steht unten
                    return 90;
                } else {
                    return 0;
                }
            }
            
            function rememberMapRotation(rotation: number): Promise<void> {
                return setStateAsync(idMapRotated, rotation);
            }
            function getMapRotation(): number {
                return getState(idMapRotated).val;
            }
            
            // "Forget" map rotation when the vacuum starts charging
            on({ id: idVacuumState, val: 8 /* charging */ }, (obj) => {
                setState(idMapRotated, -1 /* unknown */);
                // And reset all control states
                for (const room of Object.keys(rooms) as (keyof typeof rooms)[]) {
                    setState(`Staubsauger.${room}`, false, true);
                }
                setState(`Staubsauger.stop`, false, true);
            });
            
            async function beginCleanup(): Promise<void> {
            }
            
            async function cancelCurrentAction(): Promise<void> {
                setState("mihome-vacuum.0.control.pause", true);
                // wait for the "paused" status before going home
                await waitForPauseOrSleep();
            }
            
            async function stopCleanup(): Promise<void> {
                log(`Saugvorgang abgebrochen!`);
                setState(`Staubsauger.stop`, true, true);
            
                if (getState(idVacuumState).val !== 8) {
                    await cancelCurrentAction();
            
                    await setStateAsync('mihome-vacuum.0.control.home', true);
                    // wait for the "charging" status before resolving
                    await waitFor(idVacuumState, 8);
                }
            
                log(`Staubsauger ist in der Basis`);
            
                setState(`Staubsauger.stop`, false, true);
            }
            
            async function cleanRoom(room: keyof typeof rooms): Promise<void> {
                log(`Saugvorgang für ${room} gestartet!`);
                setState(`Staubsauger.${room}`, true, true);
            
                const mapRotation = getMapRotation();
                const originalCoords = rooms[room];
                log(`original coordinates: ${JSON.stringify(originalCoords)}`);
                const roomCoords = rooms[room].map(rect => rotate(rect, center, mapRotation));
                log(`rotated coordinates: ${JSON.stringify(roomCoords)}`);
            
                const coords = roomCoords[0];
                const targetCoords = [
                    ((coords[0] + coords[2]) / 2).toFixed(0),
                    ((coords[1] + coords[3]) / 2).toFixed(0),
                ]
            
                // go to center of first zone
                const gotoString = targetCoords.join(",");
                await setStateAsync("mihome-vacuum.0.control.goTo", gotoString);
                log(`Fahre zur Mitte von ${room}`);
                await wait(10000);
                await waitForPauseOrSleep();
            
                if (getState("Staubsauger.stop").val) return;
            
                const zoneCleanString = roomCoords.map(zone => {
                    return "[" + zone.concat(1).map(coord => coord.toString()).join(",") + "]";
                }).join(",");
            
                log("Starte Zonenreinigung...");
                await setStateAsync("mihome-vacuum.0.control.zoneClean", zoneCleanString);
                // wait for the cleanup to finish
                await waitFor(idVacuumState, 6);
            }
            
            async function waitForPauseOrSleep(): Promise<void> {
                log("Warte auf Zustand schlafen oder Pause...");
                switch (getState(idVacuumState).val) {
                    case 3:
                    case 10:
                        log(" => Zustand bereits aktiv!");
                        return;
                    default:
                        await Promise.race([
                            waitFor(idVacuumState, 10),
                            waitFor(idVacuumState, 3),
                        ]);
                        log(" => Zustand erreicht!");
                }
            }
            
            function wait(ms: number): Promise<void> {
                return new Promise(resolve => {
                    setTimeout(resolve, ms);
                });
            }
            
            function waitFor(stateID: string, value: any): Promise<void> {
                return new Promise(resolve => {
                    const handler = (obj: iobJS.ChangedStateObject) => {
                        if (obj.newState.val === value) {
                            unsubscribe(handler);
                            resolve();
                        }
                    }
                    subscribe(stateID, handler);
                })
            }
            
            function setStateAsync(id: string, state: any): Promise<void> {
                return new Promise(res => {
                    setState(id, state, () => res());
                });
            }
            

            Wenn du die automatische Rotation nicht benötigst oder kein Valetudo hast, wäre folgendes die vereinfachte Variante:

            // Hier den State eintragen, der den aktuellen Sauger-Status angibt
            const idVacuumState = "mihome-vacuum.0.info.state";
            
            // Koordiaten zählen von unten links (0,0) nach oben rechts (51200,51200)
            // Die folgenden Koordinaten gehen von nicht rotierter Karte (Winkel 0) aus
            const rooms: Record<string, Rectangle[]> = {
            	"Küche": [[21600, 29100, 23500, 33100]],
            	"Flur": [[18400, 24000, 22200, 27100], [19500, 27100, 21600, 30400]],
            	"Wohnzimmer": [[17600, 20000, 21600, 24000], [21600, 19200, 24200, 23200]],
            	"Schlafzimmer": [[24100, 23200, 26100, 27400], [22100, 23800, 24100, 27400]],
            	"Bad": [[21500, 27500, 23200, 29000]],
            };
            
            // ===============================================================================
            
            type Rectangle = [number, number, number, number];
            
            for (const room of Object.keys(rooms) as (keyof typeof rooms)[]) {
            	createState(`Staubsauger.${room}`, {
            		type: "boolean",
            		read: true,
            		write: true,
            		role: "switch",
            		name: `${room} saugen`,
            	});
            	on({ id: `javascript.${instance}.Staubsauger.${room}`, val: true, ack: false }, async () => {
            		if (getState(idVacuumState).val !== 8) await cancelCurrentAction();
            		
            		cleanRoom(room);
            	});
            }
            
            createState(`Staubsauger.stop`, {
            	type: "boolean",
            	read: true,
            	write: true,
            	role: "switch",
            	name: `Staubsauger anhalten`,
            });
            on({ id: `javascript.${instance}.Staubsauger.stop`, val: true, ack: false }, () => {
            	stopCleanup();
            });
            
            // Reset all control states when the vacuum starts charging
            on({ id: idVacuumState, val: 8 /* charging */ }, (obj) => {
            	for (const room of Object.keys(rooms) as (keyof typeof rooms)[]) {
            		setState(`Staubsauger.${room}`, false, true);
            	}
            	setState(`Staubsauger.stop`, false, true);
            });
            
            async function cancelCurrentAction(): Promise<void> {
            	setState("mihome-vacuum.0.control.pause", true);
            	// wait for the "paused" status before going home
            	await waitForPauseOrSleep();
            }
            
            async function stopCleanup(): Promise<void> {
            	log(`Saugvorgang abgebrochen!`);
            	setState(`Staubsauger.stop`, true, true);
            
            	if (getState(idVacuumState).val !== 8) {
            		await cancelCurrentAction();
            
            		await setStateAsync('mihome-vacuum.0.control.home', true);
            		// wait for the "charging" status before resolving
            		await waitFor(idVacuumState, 8);
            	}
            
            	log(`Staubsauger ist in der Basis`);
            
            	setState(`Staubsauger.stop`, false, true);
            }
            
            async function cleanRoom(room: keyof typeof rooms): Promise<void> {
            	log(`Saugvorgang für ${room} gestartet!`);
            	setState(`Staubsauger.${room}`, true, true);
            
            	const roomCoords = rooms[room];
            
            	const coords = roomCoords[0];
            	const targetCoords = [
            		((coords[0] + coords[2]) / 2).toFixed(0),
            		((coords[1] + coords[3]) / 2).toFixed(0),
            	]
            
            	// go to center of first zone
            	const gotoString = targetCoords.join(",");
            	await setStateAsync("mihome-vacuum.0.control.goTo", gotoString);
            	log(`Fahre zur Mitte von ${room}`);
            	await wait(10000);
            	await waitForPauseOrSleep();
            
            	if (getState("Staubsauger.stop").val) return;
            
            	const zoneCleanString = roomCoords.map(zone => {
            		return "[" + zone.concat(1).map(coord => coord.toString()).join(",") + "]";
            	}).join(",");
            
            	log("Starte Zonenreinigung...");
            	await setStateAsync("mihome-vacuum.0.control.zoneClean", zoneCleanString);
            	// wait for the cleanup to finish
            	await waitFor(idVacuumState, 6);
            }
            
            async function waitForPauseOrSleep(): Promise<void> {
            	log("Warte auf Zustand schlafen oder Pause...");
            	switch (getState(idVacuumState).val) {
            		case 3:
            		case 10:
            			log(" => Zustand bereits aktiv!");
            			return;
            		default:
            			await Promise.race([
            				waitFor(idVacuumState, 10),
            				waitFor(idVacuumState, 3),
            			]);
            			log(" => Zustand erreicht!");
            	}
            }
            
            function wait(ms: number): Promise<void> {
            	return new Promise(resolve => {
            		setTimeout(resolve, ms);
            	});
            }
            
            function waitFor(stateID: string, value: any): Promise<void> {
            	return new Promise(resolve => {
            		const handler = (obj: iobJS.ChangedStateObject) => {
            			if (obj.newState.val === value) {
            				unsubscribe(handler);
            				resolve();
            			}
            		}
            		subscribe(stateID, handler);
            	})
            }
            
            function setStateAsync(id: string, state: any): Promise<void> {
            	return new Promise(res => {
            		setState(id, state, () => res());
            	});
            }
            

            Beide Varianten legen für jeden Raum und für Stop einen State an, den du dann jeweils mit einer Alexa-Routine ansprechen kannst.

            Chris_71C Offline
            Chris_71C Offline
            Chris_71
            wrote on last edited by
            #49

            @AlCalzone Erstmal danke für dein Script
            Leider scheint es bei mir nicht zu funktionieren, laut Log bleibt es bei
            script.js.Xiaomi_Sauger.Zonenreinigung_Kartenrotation: Teste Kartenorientierung... stehen.
            Ich nutze den mihome-vacuum 1.9.2 mit Valetudo RE 0.8.1, der Datenpunkt mapRotated bleibt auf unkown(-1).

            AlCalzoneA 1 Reply Last reply
            0
            • Chris_71C Chris_71

              @AlCalzone Erstmal danke für dein Script
              Leider scheint es bei mir nicht zu funktionieren, laut Log bleibt es bei
              script.js.Xiaomi_Sauger.Zonenreinigung_Kartenrotation: Teste Kartenorientierung... stehen.
              Ich nutze den mihome-vacuum 1.9.2 mit Valetudo RE 0.8.1, der Datenpunkt mapRotated bleibt auf unkown(-1).

              AlCalzoneA Offline
              AlCalzoneA Offline
              AlCalzone
              Developer
              wrote on last edited by
              #50

              @Chris_71 Hast du die Zugangsdaten und Hostnamen bzw. IP korrekt eingetragen?
              Kannst du mit diesen Zugangsdaten im Browser die folgende Adresse öffnen?

              http://<Adresse-Des-Saugers>/api/map/latest
              

              Warum `sudo` böse ist: https://forum.iobroker.net/post/17109

              Chris_71C 1 Reply Last reply
              0
              • 0 Offline
                0 Offline
                0018
                wrote on last edited by
                #51

                @Chris_71 sagte in Roborock S5 "cleaning area" per Tastendruck?:

                Valetudo RE 0.8.1

                Ich glaube das liegt an Valetudo RE 0.8.1. Dort bekommt man über /api/map/latest nichts mehr angezeigt, sondern es wird eine Datei ohne Endung heruntergeladen. Der Inhalt ist mit dem Notepad++ nicht lesbar.

                Seit meinem Update von Valetudo auf Valetudo RE 0.8.1 habe ich das gleiche Problem. Siehe auch hier von @base im Beitrag hier

                Mfg
                0018

                AlCalzoneA 1 Reply Last reply
                0
                • 0 0018

                  @Chris_71 sagte in Roborock S5 "cleaning area" per Tastendruck?:

                  Valetudo RE 0.8.1

                  Ich glaube das liegt an Valetudo RE 0.8.1. Dort bekommt man über /api/map/latest nichts mehr angezeigt, sondern es wird eine Datei ohne Endung heruntergeladen. Der Inhalt ist mit dem Notepad++ nicht lesbar.

                  Seit meinem Update von Valetudo auf Valetudo RE 0.8.1 habe ich das gleiche Problem. Siehe auch hier von @base im Beitrag hier

                  AlCalzoneA Offline
                  AlCalzoneA Offline
                  AlCalzone
                  Developer
                  wrote on last edited by
                  #52

                  @0018 Kannst du vielleicht mal im Browser schauen, welche Adresse diese Version lädt, um an die Map zu kommen?

                  Warum `sudo` böse ist: https://forum.iobroker.net/post/17109

                  1 Reply Last reply
                  0
                  • AlCalzoneA AlCalzone

                    @Chris_71 Hast du die Zugangsdaten und Hostnamen bzw. IP korrekt eingetragen?
                    Kannst du mit diesen Zugangsdaten im Browser die folgende Adresse öffnen?

                    http://<Adresse-Des-Saugers>/api/map/latest
                    
                    Chris_71C Offline
                    Chris_71C Offline
                    Chris_71
                    wrote on last edited by
                    #53

                    @AlCalzone Da passiert das gleiche das auch @0018 geschrieben hat.
                    Eine Karte kann man über /api/simple_map abrufen (PNG-Datei).

                    AlCalzoneA 1 Reply Last reply
                    0
                    • Chris_71C Chris_71

                      @AlCalzone Da passiert das gleiche das auch @0018 geschrieben hat.
                      Eine Karte kann man über /api/simple_map abrufen (PNG-Datei).

                      AlCalzoneA Offline
                      AlCalzoneA Offline
                      AlCalzone
                      Developer
                      wrote on last edited by AlCalzone
                      #54

                      @Chris_71 dann muss man die Karte anders decoden. Ich brauche nicht die Pixel sondern die Koordinaten von sauger und Station um die rotation zu erkennen

                      @Meistertr hast du ne Idee wie sich das genau geändert hat?

                      Warum `sudo` böse ist: https://forum.iobroker.net/post/17109

                      1 Reply Last reply
                      0
                      • M Offline
                        M Offline
                        Mor9oth
                        wrote on last edited by
                        #55

                        @AlCalzone: Hey! Ich hoffe, du kannst mir helfen! Ich nutze dein Script und plötzlich, seit ca. einer Woche, funktioniert es nicht mehr. Ich bekomme plötzlich im Log:

                        State value to set for "mihome-vacuum.0.info.water_box" has to be type "string" but received type "boolean"
                        

                        sobald ich einen der Räume ansteuere. (per Sprache oder per Alexa (Gerät/Button).

                        Geändert habe ich nichts. Die relevanten Adapter scheinen auch alle richtig angebunden zu sein. Siehe Screenshot:
                        Screenshot Capture - 2021-08-04 - 12-00-28.png

                        Die Verbindung zum Roborock habe ich mit dem "Find" erfolgreich getestet. Muss ich was am script verändern, um die Raumsteuerung wieder zum Laufen zu bringen?

                        Dein Script lief bei mir Jahre so:

                        // Hier den State eintragen, der den aktuellen Sauger-Status angibt
                        
                        const idVacuumState = "mihome-vacuum.0.info.state";
                        
                         
                        
                        // Koordiaten zählen von unten links (0,0) nach oben rechts (51200,51200)
                        
                        // Die folgenden Koordinaten gehen von nicht rotierter Karte (Winkel 0) aus
                        
                        const rooms: Record<string, Rectangle[]> = {
                        
                        	"Küche": [[21386,18786,23486,22936]],
                        
                        	"Flur": [[18323,22873,23623,24523]],
                        
                        	"Wohnzimmer": [[23470,18841,27620,24341]],
                        
                        	"Schlafzimmer": [[18235,24479,22285,28829]],
                        
                        	"Bad": [[18273,19241,20723,23091]],
                        
                            "Arbeitszimmer": [[22413,24357,27463,28657]],
                        
                            "Wohnbereich": [[23470,18841,27620,24341],[21386,18786,23486,22936]],
                        
                            "Wohnung": [[23470,18841,27620,24341],[21386,18786,23486,22936],[18323,22873,23623,24523],[18273,19241,20723,23091],[18235,24479,22285,28829]]
                            
                        };
                        
                         
                        
                        // ===============================================================================
                        
                        
                        type Rectangle = [number, number, number, number];
                        
                        
                         
                        
                        for (const room of Object.keys(rooms) as (keyof typeof rooms)[]) {
                        
                        	createState(`Staubsauger.${room}`, {
                        
                        		type: "boolean",
                        
                        		read: true,
                        
                        		write: true,
                        
                        		role: "switch",
                        
                        		name: `${room} saugen`,
                        
                        	});
                        
                        	on({ id: `javascript.${instance}.Staubsauger.${room}`, val: true, ack: false }, async () => {
                        
                        		if (getState(idVacuumState).val !== 8) await cancelCurrentAction();
                        
                        		
                        
                        		cleanRoom(room);
                        
                        	});
                        
                        }
                        
                         
                        
                        createState(`Staubsauger.stop`, {
                        
                        	type: "boolean",
                        
                        	read: true,
                        
                        	write: true,
                        
                        	role: "switch",
                        
                        	name: `Staubsauger anhalten`,
                        
                        });
                        
                        on({ id: `javascript.${instance}.Staubsauger.stop`, val: true, ack: false }, () => {
                        
                        	stopCleanup();
                        
                        });
                        
                         
                        
                        // Reset all control states when the vacuum starts charging
                        
                        on({ id: idVacuumState, val: 8 /* charging */ }, (obj) => {
                        
                        	for (const room of Object.keys(rooms) as (keyof typeof rooms)[]) {
                        
                        		setState(`Staubsauger.${room}`, false, true);
                        
                        	}
                        
                        	setState(`Staubsauger.stop`, false, true);
                        
                        });
                        
                         
                        
                        async function cancelCurrentAction(): Promise<void> {
                        
                        	setState("mihome-vacuum.0.control.pause", true);
                        
                        	// wait for the "paused" status before going home
                        
                        	await waitForPauseOrSleep();
                        
                        }
                        
                         
                        
                        async function stopCleanup(): Promise<void> {
                        
                        	log(`Saugvorgang abgebrochen!`);
                        
                        	setState(`Staubsauger.stop`, true, true);
                        
                         
                        
                        	if (getState(idVacuumState).val !== 8) {
                        
                        		await cancelCurrentAction();
                        
                         
                        
                        		await setStateAsync('mihome-vacuum.0.control.home', true);
                        
                        		// wait for the "charging" status before resolving
                        
                        		await waitFor(idVacuumState, 8);
                        
                        	}
                        
                         
                        
                        	log(`Staubsauger ist in der Basis`);
                        
                         
                        
                        	setState(`Staubsauger.stop`, false, true);
                        
                        }
                        
                         
                        
                        async function cleanRoom(room: keyof typeof rooms): Promise<void> {
                        
                        	log(`Saugvorgang für ${room} gestartet!`);
                        
                        	setState(`Staubsauger.${room}`, true, true);
                        
                         
                        
                        	const roomCoords = rooms[room];
                        
                         
                        
                        	const coords = roomCoords[0];
                        
                        	const targetCoords = [
                        
                        		((coords[0] + coords[2]) / 2).toFixed(0),
                        
                        		((coords[1] + coords[3]) / 2).toFixed(0),
                        
                        	]
                        
                         
                        
                        	// go to center of first zone
                        
                        	const gotoString = targetCoords.join(",");
                        
                        	await setStateAsync("mihome-vacuum.0.control.goTo", gotoString);
                        
                        	log(`Fahre zur Mitte von ${room}`);
                        
                        	await wait(10000);
                        
                        	await waitForPauseOrSleep();
                        
                         
                        
                        	if (getState("Staubsauger.stop").val) return;
                        
                         
                        
                        	const zoneCleanString = roomCoords.map(zone => {
                        
                        		return "[" + zone.concat(1).map(coord => coord.toString()).join(",") + "]";
                        
                        	}).join(",");
                        
                         
                        
                        	log("Starte Zonenreinigung...");
                        
                        	await setStateAsync("mihome-vacuum.0.control.zoneClean", zoneCleanString);
                        
                        	// wait for the cleanup to finish
                        
                        	await waitFor(idVacuumState, 6);
                        
                        }
                        
                         
                        
                        async function waitForPauseOrSleep(): Promise<void> {
                        
                        	log("Warte auf Zustand schlafen oder Pause...");
                        
                        	switch (getState(idVacuumState).val) {
                        
                        		case 3:
                        
                        		case 10:
                        
                        			log(" => Zustand bereits aktiv!");
                        
                        			return;
                        
                        		default:
                        
                        			await Promise.race([
                        
                        				waitFor(idVacuumState, 10),
                        
                        				waitFor(idVacuumState, 3),
                        
                        			]);
                        
                        			log(" => Zustand erreicht!");
                        
                        	}
                        
                        }
                        
                         
                        
                        function wait(ms: number): Promise<void> {
                        
                        	return new Promise(resolve => {
                        
                        		setTimeout(resolve, ms);
                        
                        	});
                        
                        }
                        
                         
                        
                        function waitFor(stateID: string, value: any): Promise<void> {
                        
                        	return new Promise(resolve => {
                        
                        		const handler = (obj: iobJS.ChangedStateObject) => {
                        
                        			if (obj.newState.val === value) {
                        
                        				unsubscribe(handler);
                        
                        				resolve();
                        
                        			}
                        
                        		}
                        
                        		subscribe(stateID, handler);
                        
                        	})
                        
                        }
                        
                         
                        
                        function setStateAsync(id: string, state: any): Promise<void> {
                        
                        	return new Promise(res => {
                        
                        		setState(id, state, () => res());
                        
                        	});
                        
                        }
                        
                        AlCalzoneA 1 Reply Last reply
                        0
                        • M Mor9oth

                          @AlCalzone: Hey! Ich hoffe, du kannst mir helfen! Ich nutze dein Script und plötzlich, seit ca. einer Woche, funktioniert es nicht mehr. Ich bekomme plötzlich im Log:

                          State value to set for "mihome-vacuum.0.info.water_box" has to be type "string" but received type "boolean"
                          

                          sobald ich einen der Räume ansteuere. (per Sprache oder per Alexa (Gerät/Button).

                          Geändert habe ich nichts. Die relevanten Adapter scheinen auch alle richtig angebunden zu sein. Siehe Screenshot:
                          Screenshot Capture - 2021-08-04 - 12-00-28.png

                          Die Verbindung zum Roborock habe ich mit dem "Find" erfolgreich getestet. Muss ich was am script verändern, um die Raumsteuerung wieder zum Laufen zu bringen?

                          Dein Script lief bei mir Jahre so:

                          // Hier den State eintragen, der den aktuellen Sauger-Status angibt
                          
                          const idVacuumState = "mihome-vacuum.0.info.state";
                          
                           
                          
                          // Koordiaten zählen von unten links (0,0) nach oben rechts (51200,51200)
                          
                          // Die folgenden Koordinaten gehen von nicht rotierter Karte (Winkel 0) aus
                          
                          const rooms: Record<string, Rectangle[]> = {
                          
                          	"Küche": [[21386,18786,23486,22936]],
                          
                          	"Flur": [[18323,22873,23623,24523]],
                          
                          	"Wohnzimmer": [[23470,18841,27620,24341]],
                          
                          	"Schlafzimmer": [[18235,24479,22285,28829]],
                          
                          	"Bad": [[18273,19241,20723,23091]],
                          
                              "Arbeitszimmer": [[22413,24357,27463,28657]],
                          
                              "Wohnbereich": [[23470,18841,27620,24341],[21386,18786,23486,22936]],
                          
                              "Wohnung": [[23470,18841,27620,24341],[21386,18786,23486,22936],[18323,22873,23623,24523],[18273,19241,20723,23091],[18235,24479,22285,28829]]
                              
                          };
                          
                           
                          
                          // ===============================================================================
                          
                          
                          type Rectangle = [number, number, number, number];
                          
                          
                           
                          
                          for (const room of Object.keys(rooms) as (keyof typeof rooms)[]) {
                          
                          	createState(`Staubsauger.${room}`, {
                          
                          		type: "boolean",
                          
                          		read: true,
                          
                          		write: true,
                          
                          		role: "switch",
                          
                          		name: `${room} saugen`,
                          
                          	});
                          
                          	on({ id: `javascript.${instance}.Staubsauger.${room}`, val: true, ack: false }, async () => {
                          
                          		if (getState(idVacuumState).val !== 8) await cancelCurrentAction();
                          
                          		
                          
                          		cleanRoom(room);
                          
                          	});
                          
                          }
                          
                           
                          
                          createState(`Staubsauger.stop`, {
                          
                          	type: "boolean",
                          
                          	read: true,
                          
                          	write: true,
                          
                          	role: "switch",
                          
                          	name: `Staubsauger anhalten`,
                          
                          });
                          
                          on({ id: `javascript.${instance}.Staubsauger.stop`, val: true, ack: false }, () => {
                          
                          	stopCleanup();
                          
                          });
                          
                           
                          
                          // Reset all control states when the vacuum starts charging
                          
                          on({ id: idVacuumState, val: 8 /* charging */ }, (obj) => {
                          
                          	for (const room of Object.keys(rooms) as (keyof typeof rooms)[]) {
                          
                          		setState(`Staubsauger.${room}`, false, true);
                          
                          	}
                          
                          	setState(`Staubsauger.stop`, false, true);
                          
                          });
                          
                           
                          
                          async function cancelCurrentAction(): Promise<void> {
                          
                          	setState("mihome-vacuum.0.control.pause", true);
                          
                          	// wait for the "paused" status before going home
                          
                          	await waitForPauseOrSleep();
                          
                          }
                          
                           
                          
                          async function stopCleanup(): Promise<void> {
                          
                          	log(`Saugvorgang abgebrochen!`);
                          
                          	setState(`Staubsauger.stop`, true, true);
                          
                           
                          
                          	if (getState(idVacuumState).val !== 8) {
                          
                          		await cancelCurrentAction();
                          
                           
                          
                          		await setStateAsync('mihome-vacuum.0.control.home', true);
                          
                          		// wait for the "charging" status before resolving
                          
                          		await waitFor(idVacuumState, 8);
                          
                          	}
                          
                           
                          
                          	log(`Staubsauger ist in der Basis`);
                          
                           
                          
                          	setState(`Staubsauger.stop`, false, true);
                          
                          }
                          
                           
                          
                          async function cleanRoom(room: keyof typeof rooms): Promise<void> {
                          
                          	log(`Saugvorgang für ${room} gestartet!`);
                          
                          	setState(`Staubsauger.${room}`, true, true);
                          
                           
                          
                          	const roomCoords = rooms[room];
                          
                           
                          
                          	const coords = roomCoords[0];
                          
                          	const targetCoords = [
                          
                          		((coords[0] + coords[2]) / 2).toFixed(0),
                          
                          		((coords[1] + coords[3]) / 2).toFixed(0),
                          
                          	]
                          
                           
                          
                          	// go to center of first zone
                          
                          	const gotoString = targetCoords.join(",");
                          
                          	await setStateAsync("mihome-vacuum.0.control.goTo", gotoString);
                          
                          	log(`Fahre zur Mitte von ${room}`);
                          
                          	await wait(10000);
                          
                          	await waitForPauseOrSleep();
                          
                           
                          
                          	if (getState("Staubsauger.stop").val) return;
                          
                           
                          
                          	const zoneCleanString = roomCoords.map(zone => {
                          
                          		return "[" + zone.concat(1).map(coord => coord.toString()).join(",") + "]";
                          
                          	}).join(",");
                          
                           
                          
                          	log("Starte Zonenreinigung...");
                          
                          	await setStateAsync("mihome-vacuum.0.control.zoneClean", zoneCleanString);
                          
                          	// wait for the cleanup to finish
                          
                          	await waitFor(idVacuumState, 6);
                          
                          }
                          
                           
                          
                          async function waitForPauseOrSleep(): Promise<void> {
                          
                          	log("Warte auf Zustand schlafen oder Pause...");
                          
                          	switch (getState(idVacuumState).val) {
                          
                          		case 3:
                          
                          		case 10:
                          
                          			log(" => Zustand bereits aktiv!");
                          
                          			return;
                          
                          		default:
                          
                          			await Promise.race([
                          
                          				waitFor(idVacuumState, 10),
                          
                          				waitFor(idVacuumState, 3),
                          
                          			]);
                          
                          			log(" => Zustand erreicht!");
                          
                          	}
                          
                          }
                          
                           
                          
                          function wait(ms: number): Promise<void> {
                          
                          	return new Promise(resolve => {
                          
                          		setTimeout(resolve, ms);
                          
                          	});
                          
                          }
                          
                           
                          
                          function waitFor(stateID: string, value: any): Promise<void> {
                          
                          	return new Promise(resolve => {
                          
                          		const handler = (obj: iobJS.ChangedStateObject) => {
                          
                          			if (obj.newState.val === value) {
                          
                          				unsubscribe(handler);
                          
                          				resolve();
                          
                          			}
                          
                          		}
                          
                          		subscribe(stateID, handler);
                          
                          	})
                          
                          }
                          
                           
                          
                          function setStateAsync(id: string, state: any): Promise<void> {
                          
                          	return new Promise(res => {
                          
                          		setState(id, state, () => res());
                          
                          	});
                          
                          }
                          
                          AlCalzoneA Offline
                          AlCalzoneA Offline
                          AlCalzone
                          Developer
                          wrote on last edited by
                          #56

                          @mor9oth sagte in Roborock S5 "cleaning area" per Tastendruck?:

                          State value to set for "mihome-vacuum.0.info.water_box" has to be type "string" but received type "boolean"

                          Der State wird im Skript nicht verwendet. Das muss woanders her kommen.

                          Warum `sudo` böse ist: https://forum.iobroker.net/post/17109

                          M 1 Reply Last reply
                          0
                          • AlCalzoneA AlCalzone

                            @mor9oth sagte in Roborock S5 "cleaning area" per Tastendruck?:

                            State value to set for "mihome-vacuum.0.info.water_box" has to be type "string" but received type "boolean"

                            Der State wird im Skript nicht verwendet. Das muss woanders her kommen.

                            M Offline
                            M Offline
                            Mor9oth
                            wrote on last edited by
                            #57

                            @alcalzone Ok, danke für das Feedback! Dann suche ich mal weiter ^^

                            1 Reply Last reply
                            0

                            Hello! It looks like you're interested in this conversation, but you don't have an account yet.

                            Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

                            With your input, this post could be even better 💗

                            Register Login
                            Reply
                            • Reply as topic
                            Log in to reply
                            • Oldest to Newest
                            • Newest to Oldest
                            • Most Votes


                            Support us

                            ioBroker
                            Community Adapters
                            Donate

                            592

                            Online

                            32.8k

                            Users

                            82.7k

                            Topics

                            1.3m

                            Posts
                            Community
                            Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
                            ioBroker Community 2014-2025
                            logo
                            • Login

                            • Don't have an account? Register

                            • Login or register to search.
                            • First post
                              Last post
                            0
                            • Home
                            • Recent
                            • Tags
                            • Unread 0
                            • Categories
                            • Unreplied
                            • Popular
                            • GitHub
                            • Docu
                            • Hilfe