Skip to content
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • 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

  • Standard: (Kein Skin)
  • Kein Skin
Einklappen
ioBroker Logo

Community Forum

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

NEWS

  • UPDATE 31.10.: Amazon Alexa - ioBroker Skill läuft aus ?
    apollon77A
    apollon77
    48
    3
    8.6k

  • Monatsrückblick – September 2025
    BluefoxB
    Bluefox
    13
    1
    2.1k

  • Neues Video "KI im Smart Home" - ioBroker plus n8n
    BluefoxB
    Bluefox
    16
    1
    2.8k

Roborock S5 "cleaning area" per Tastendruck?

Geplant Angeheftet Gesperrt Verschoben Skripten / Logik
javascript
57 Beiträge 9 Kommentatoren 10.3k Aufrufe 15 Watching
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • K kaiserm

    @AlCalzone Vielen Dank.

    Ich probiere gerade noch was anderes:

    Alexa reagiert und spricht den Text.
    Egal wie ich die Koordinaten eingebe. Der S50 macht keinen Mucks.
    GFebe ich nur die ersten beiden Punkte ohne Klammern aber mit Komma an fährt er aus der Ladestation, irrt etwas umher und sagt dann er kann nicht zum Ziel fahren.

    Man müsste doch in den GoToPoint Datenpunkt direkt die Koordinate reinschreiben können?!

    Habe alles probiert. Mit Klammern, ohne ... mit Kommas

    Gebe ich ihm nur die ersten beiden Zahlen fährt er aus der Docking Station irrt kurz umher und sagt dann er kann nicht an die Koordinate fahren.

    Im Log finde ich immer nur: GoTo only work with two arguments seperated by

    LG Martin

    fb33cc15-1813-4d1b-bb7d-36794475ee4d-grafik.png

    M Offline
    M Offline
    Mor9oth
    schrieb am zuletzt editiert von
    #37

    @kaiserm Hey! Vielen Dank für das Blocky! Finde ich eine tolle Funktion! Habe ich gestern auch mal direkt nachgebaut, allerdings funktioniert es bei mir leider nicht. :-( Jetzt hoffe ich hier auf etwas Hilfe ...
    Im Protokoll kommt die Meldung:

    09:35:03.244	info	javascript.0 (1571) Start javascript script.js.common.Leere_den_Staubbehälter
    09:35:03.247	info	javascript.0 (1571) script.js.common.Leere_den_Staubbehälter: registered 1 subscription and 0 schedules
    

    Der Roborock fährt weder los noch gibt die Alexa den Speak-Befehl aus. Allerdings gibt Sie als Antwort auf meine Spracheingabe die Bestätigung "ok" bzw. den "Pieps" aus.

    Das Blocky scheint mir identisch nachgebaut zu sein. Aus folgenden Pfaden kommen die Variabeln:

    • summary: Alexa2/0/History/summary

    • speak: Alexa2/0/Echo-Devices/Geräte-NUMMER/Commands/speak

    • go to Point: Mihome-vacuum/0/control/goto

    • Die Koordianten kommen aus Flolevac (Vgl. @AlCalzone )

    Screenshot Blocky mit Protokoll:
    blocky-staubbehälter-leeren.jpg

    Oder muss ich noch etwas ganz anderes machen, bzw. haben damit das Script überhaupt funktioniert? Bin leider ein Noob, daher komme ich leider nicht weiter ...

    Was ich auch noch probiert habe: Ich habe dem Script einen "smartName" gegeben, damit dieses im Cloud-Adapter als "Smart Gerät" auftaucht und dann in den Alexa-Routinen per Sprachsteuerung integrierbar wird. Auch um synonyme Befehle definieren zu können. Ist das richtig, bzw. notwendig mit dem Blocky-Script? Immerhin ist doch der speak-Befehl eine Bedingung im Script ...

    Könnte mir bitte jemand helfen?

    1 Antwort Letzte Antwort
    0
    • 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.

      M Offline
      M Offline
      Mor9oth
      schrieb am zuletzt editiert von
      #38

      @AlCalzone Sauber! Das Script funktioniert nun auch bei mir hervorragend! Besten Dank! :blush: :clap:

      1 Antwort Letzte Antwort
      0
      • 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.

        0 Offline
        0 Offline
        0018
        schrieb am zuletzt editiert von 0018
        #39

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

        @kaiserm Also wie versprochen:

        Muss als TypeScript angelegt werden und benötigt das zusätzliche Paket "axios":

        Bekomme bei mir folgende Meldung:

        javascript.0	2019-12-07 00:24:41.947	warn	ERROR: Cannot find module 'axios'.
        javascript.0	2019-12-07 00:24:41.947	warn	^
        javascript.0	2019-12-07 00:24:41.947	warn	import axios from "axios";
        javascript.0	2019-12-07 00:24:41.947	warn	(2094) script.js.common.4_MiRobot.MiRobot_Zone_neu: TypeScript compilation had errors:
        

        Habe im JS Instanz das Paket axios hinzugefügt, aber die Meldung kommt trotzdem noch. Muss ich das noch irgendwo anders eintragen?

        Gibt es einen Grund warum der Sauger erst zu einem Spot fährt, bevor die Zonenreinigung startet?

        Mfg
        0018

        AlCalzoneA 1 Antwort Letzte Antwort
        0
        • 0 0018

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

          @kaiserm Also wie versprochen:

          Muss als TypeScript angelegt werden und benötigt das zusätzliche Paket "axios":

          Bekomme bei mir folgende Meldung:

          javascript.0	2019-12-07 00:24:41.947	warn	ERROR: Cannot find module 'axios'.
          javascript.0	2019-12-07 00:24:41.947	warn	^
          javascript.0	2019-12-07 00:24:41.947	warn	import axios from "axios";
          javascript.0	2019-12-07 00:24:41.947	warn	(2094) script.js.common.4_MiRobot.MiRobot_Zone_neu: TypeScript compilation had errors:
          

          Habe im JS Instanz das Paket axios hinzugefügt, aber die Meldung kommt trotzdem noch. Muss ich das noch irgendwo anders eintragen?

          Gibt es einen Grund warum der Sauger erst zu einem Spot fährt, bevor die Zonenreinigung startet?

          AlCalzoneA Offline
          AlCalzoneA Offline
          AlCalzone
          Developer
          schrieb am zuletzt editiert von
          #40

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

          Habe im JS Instanz das Paket axios hinzugefügt, aber die Meldung kommt trotzdem noch. Muss ich das noch irgendwo anders eintragen?

          Ich bin mir nicht sicher, ob der aktuelle Adapter damit schon released ist. Wenn es die Option "Activate syntax help for these npm modules:" gibt, trag da auch noch axios ein. Ansonsten folgende Zeile über den Import schreiben:

          // @ts-ignore
          

          Gibt es einen Grund warum der Sauger erst zu einem Spot fährt, bevor die Zonenreinigung startet?

          Ja, das ist leiser als wenn er im Reinigungs-Modus in die Zone fährt.

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

          1 Antwort Letzte Antwort
          0
          • 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.

            M Offline
            M Offline
            Mor9oth
            schrieb am zuletzt editiert von
            #41

            Hey @AlCalzone kann man dein Script denn auch so anpassen/erweitern, dass es zusätzlich zu den Räumen auch Positionen gibt, die nur angefahren werden könnten? Um z.B. den Staubbehälter vor dem Mülleimer leeren zu können? "Fahre Staubsauger zum Mülleimer"
            Zudem wäre es denke ich auch interessant wenn man die Saugstufen in Abhängigkeit machen könnten. Im Bad könnte ich beispielsweise immer eine starke Stufe vertragen da hier unser Katzenklo steht. Oder kann man es in Abhängikeit vom Raum so einstellen, dass der ZoneCleanup auch mehrfach ausgeführt wird? Über ein paar Scripterweiterungen würde ich micht mega freuen :-)

            AlCalzoneA 1 Antwort Letzte Antwort
            0
            • M Mor9oth

              Hey @AlCalzone kann man dein Script denn auch so anpassen/erweitern, dass es zusätzlich zu den Räumen auch Positionen gibt, die nur angefahren werden könnten? Um z.B. den Staubbehälter vor dem Mülleimer leeren zu können? "Fahre Staubsauger zum Mülleimer"
              Zudem wäre es denke ich auch interessant wenn man die Saugstufen in Abhängigkeit machen könnten. Im Bad könnte ich beispielsweise immer eine starke Stufe vertragen da hier unser Katzenklo steht. Oder kann man es in Abhängikeit vom Raum so einstellen, dass der ZoneCleanup auch mehrfach ausgeführt wird? Über ein paar Scripterweiterungen würde ich micht mega freuen :-)

              AlCalzoneA Offline
              AlCalzoneA Offline
              AlCalzone
              Developer
              schrieb am zuletzt editiert von
              #42

              @Mor9oth Gute Ideen. Eventuell komme ich morgen dazu - aber keine Garantie!

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

              M 1 Antwort Letzte Antwort
              0
              • AlCalzoneA AlCalzone

                @Mor9oth Gute Ideen. Eventuell komme ich morgen dazu - aber keine Garantie!

                M Offline
                M Offline
                Mor9oth
                schrieb am zuletzt editiert von
                #43

                @AlCalzone Freut mich sehr :+1:

                1 Antwort Letzte Antwort
                0
                • AlCalzoneA AlCalzone

                  @0018
                  Siehe Skript:

                  // Koordiaten zählen von unten links (0,0) nach oben rechts (51200,51200)
                  // Die folgenden Koordinaten gehen von nicht rotierter Karte (Winkel 0) aus
                  

                  Winkel 0 heißt "Robo links vom Dock". Wenn das bei dir anders ist, müsstest du in Zeile 119ff die Interpretation der Winkel ändern. Beispiel: Sauger steht bei dir normalerweise rechts, dann 180 => 0, 270 => 90, 90 => 270, 0 => 180.

                  0 Offline
                  0 Offline
                  0018
                  schrieb am zuletzt editiert von
                  #44

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

                  Mfg
                  0018

                  AlCalzoneA 1 Antwort Letzte Antwort
                  0
                  • 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
                    schrieb am zuletzt editiert von
                    #45

                    @0018 Inwiefern stimmt es nicht mehr überein?

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

                    0 1 Antwort Letzte Antwort
                    0
                    • AlCalzoneA AlCalzone

                      @0018 Inwiefern stimmt es nicht mehr überein?

                      0 Offline
                      0 Offline
                      0018
                      schrieb am zuletzt editiert von 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 Antwort Letzte Antwort
                      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
                        schrieb am zuletzt editiert von 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 Antwort Letzte Antwort
                        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
                          schrieb am zuletzt editiert von
                          #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 Antwort Letzte Antwort
                          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
                            schrieb am zuletzt editiert von
                            #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 Antwort Letzte Antwort
                            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
                              schrieb am zuletzt editiert von
                              #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 Antwort Letzte Antwort
                              0
                              • 0 Offline
                                0 Offline
                                0018
                                schrieb am zuletzt editiert von
                                #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 Antwort Letzte Antwort
                                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
                                  schrieb am zuletzt editiert von
                                  #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 Antwort Letzte Antwort
                                  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
                                    schrieb am zuletzt editiert von
                                    #53

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

                                    AlCalzoneA 1 Antwort Letzte Antwort
                                    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
                                      schrieb am zuletzt editiert von 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 Antwort Letzte Antwort
                                      0
                                      • M Offline
                                        M Offline
                                        Mor9oth
                                        schrieb am zuletzt editiert von
                                        #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 Antwort Letzte Antwort
                                        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
                                          schrieb am zuletzt editiert von
                                          #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 Antwort Letzte Antwort
                                          0
                                          Antworten
                                          • In einem neuen Thema antworten
                                          Anmelden zum Antworten
                                          • Älteste zuerst
                                          • Neuste zuerst
                                          • Meiste Stimmen


                                          Support us

                                          ioBroker
                                          Community Adapters
                                          Donate

                                          444

                                          Online

                                          32.4k

                                          Benutzer

                                          81.5k

                                          Themen

                                          1.3m

                                          Beiträge
                                          Community
                                          Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
                                          ioBroker Community 2014-2025
                                          logo
                                          • Anmelden

                                          • Du hast noch kein Konto? Registrieren

                                          • Anmelden oder registrieren, um zu suchen
                                          • Erster Beitrag
                                            Letzter Beitrag
                                          0
                                          • Home
                                          • Aktuell
                                          • Tags
                                          • Ungelesen 0
                                          • Kategorien
                                          • Unreplied
                                          • Beliebt
                                          • GitHub
                                          • Docu
                                          • Hilfe