Xiaomi WiFi Devices Modul (Vacuum/Airpurifier/Fan) - 72_XiaomiDevice (Support)

Begonnen von Markus M., 11 Juni 2017, 12:48:58

Vorheriges Thema - Nächstes Thema

tomcat.x

FHEM: 6.1 auf Raspi 3, Raspbian (Buster), Perl v5.28.1
Sender/Empfänger: 2 x CULv3, Duofern Stick, HM-MOD-RPI-PCB
Gateways: FRITZ!Box 6591 (OS: 7.39), Trädfri, ConBee 2,  piVCCU, OpenMQTTGateway
Sensoren/Aktoren: FRITZ!DECT, FS20, FHT, HMS, HomeMatic, Trädfri, DuoFern, NetAtmo

bismosa

Hallo!

Ich bin auf der Suche nach einer Lösung die Karten auf dem S5 Max zu wechseln auch über diesen Thread gestolpert.
Immer nach einem Wechsel des Stockwerkes kann ich keine Einzelraumreinigung über FHEM starten, da die falsche Karte als Basis verwendet wird.

Da es bisher scheinbar keine einfache Lösung gibt, habe ich mir eine Lösung zusammengeschustert  ;)
Vielleicht hilft dies ja auch dem einen oder anderen  :)
Natürlich wäre es schön, wenn das auch im Modul übernommen werden könnte. Ich habe aber auch gelesen, dass die Entwicklung derzeit eingestellt ist.

Ich nutze einen Weg über python-miio ( https://github.com/rytilahti/python-miio )
Dies muss zunächst installiert werden (bei mir ein Raspberry mit buster):

pip3 install cryptography==35
pip3 install python-miio

cryptography in einer älteren Version, damit nicht im Vorfeld rust installiert werden muss.

Den Rest mache ich mit einem DOIF. Hier habe ich darauf geachtet, dass die Befehle das System nicht ausbremsen. Nach Abruf der Daten werden diese per Telnet an das DOIF gesendet.
<token> mit dem eigentlichen Token ersetzen
<IP> mit der IP-Adresse ersetzen

defmod di_Roborock DOIF
\
getMaps{\
##mystr=`mirobo --ip <IP> --token <token> raw-command get_multi_maps_list 1 | tail -n +2`;;  perl /opt/fhem/fhem.pl 7072 "setreading di_Roborock maps ${mystr}"\
\
my $command="mirobo --ip <IP> --token <token> raw-command get_multi_maps_list 1 | tail -n +2";;\
my $fhemstring="perl /opt/fhem/fhem.pl 7072 \"setreading $SELF maps \${mystr}\"";;\
my $mapdata="mystr=`$command`;; $fhemstring";;\
system ("{ $mapdata;; }&");;\
\
}\
getActiveMap{\
##Keine lesbare Rückmeldung! Nur Inkrement\
my $command="mirobo --ip <IP> --token <token> raw-command get_map_v1 1 | tail -n +2";;\
my $fhemstring="perl /opt/fhem/fhem.pl 7072 \"setreading $SELF activeMap \${mystr}\"";;\
my $mapdata="mystr=`$command`;; $fhemstring";;\
system ("{ $mapdata;; }&");;\
}\
\
setMapUnten{\
my $Num=0;;\
set_Reading("MapLoaded", "false",1);;\
my $command="mirobo --ip <IP> --token <token> raw-command load_multi_map $Num,1 | tail -n +2";;\
my $fhemstring="perl /opt/fhem/fhem.pl 7072 \"setreading $SELF MapLoaded \${mystr}\"";;\
my $mapdata="mystr=`$command`;; $fhemstring";;\
system ("{ $mapdata;; }&");;\
}\
setMapKeller{\
my $Num=1;;\
set_Reading("MapLoaded", "false",1);;\
my $command="mirobo --ip <IP> --token <token> raw-command load_multi_map $Num,1 | tail -n +2";;\
my $fhemstring="perl /opt/fhem/fhem.pl 7072 \"setreading $SELF MapLoaded \${mystr}\"";;\
my $mapdata="mystr=`$command`;; $fhemstring";;\
system ("{ $mapdata;; }&");;\
}\
setMapOben{\
my $Num=2;;\
set_Reading("MapLoaded", "false",1);;\
my $command="mirobo --ip <IP> --token <token> raw-command load_multi_map $Num,1 | tail -n +2";;\
my $fhemstring="perl /opt/fhem/fhem.pl 7072 \"setreading $SELF MapLoaded \${mystr}\"";;\
my $mapdata="mystr=`$command`;; $fhemstring";;\
system ("{ $mapdata;; }&");;\
}
attr di_Roborock room Sauger
attr di_Roborock userReadings MapLoadedTxt:MapLoaded.* {\
my $var=ReadingsVal($name,"MapLoaded","false");;\
$var =~ s/\[\'//g;;\
$var =~ s/\'\]//g;;\
return $var;;\
}

setstate di_Roborock initialized
setstate di_Roborock 2023-01-09 18:45:33 mode enabled
setstate di_Roborock 2023-01-09 18:45:35 state initialized


set di_Roborock getMaps
Erzeugt ein reading im JSON-Format. Hier werden die IDs und die Map-Namen ausgelesen.

set di_Roborock getActiveMap
War mein Versuch, die aktive Karte auszulesen. Bei jedem Auslesen hat man jedoch nur ein Increment. Die aktive Karte kann wohl nicht ausgelesen werden.

set di_Roborock setMapXYZ
Lädt die entsprechende Karte. (In der App kann man sehen, das die Karte auch wirklich geändert wird...ggf. die App einmal neu starten)
Erzeugt ein reading "MapLoaded". Bei Erfolg ein "['ok']".
Da ich ein reading "ok" für FTUI benötige, habe ich noch das userReading "MapLoadedTxt" hinzugefügt. Hier bleibt dann nur noch das "ok" übrig.

Das laden der Karte geht sehr schnell (anders als in der App). Deswegen lade ich die Karte jedes Mal vor beginn der Reinigung.

Gruß
Bismosa
1x nanoCUL 433MHz (SlowRF Intertechno) für Fenstersensoren
1x nanoCUL 868Mhz für MAX (9x HT 1xWT)
1x ZigBee CUL
Weiteres: Squeezebox server, Kindle Display, ESP8266, Löterfahrung, ...

RappaSan

Seit neuestem haben wir zu Hause einen Roborock S7 MaxV Ultra in Betrieb.
Soweit alles gut bisher, aber ist es normal, daß die history_0 bis history_9 nicht mehr in den readings sind?

erdnar

... "normal" in so fern, dass es bei mir auch nicht auftaucht  :-\
ErdnaR

DeeSPe

Mein Roborock S7 wird automatisch anhand der Bewohneranwesenheit von FHEM gesteuert.
Das klappt auch soweit sehr gut.
Allerdings wird immer im "normalen" Modus "Alle" gereinigt. Ich habe mir eigentlich eine individuelle Raumreinigung zusammengestellt, bei der nur bestimmte Räume auch gewischt werden sollen. Wenn ich vor der automatischen Reinigung in der App auf "individuell" umstelle klappt das auch, nur ist die Einstellung nach jeder Reinigung wieder zurückgesetzt und bei der nächsten automatischen Reinigung wird wieder überall gewischt.
Wie kann ich denn über FHEM mitgeben dass er vor Beginn der Reinigung wieder auf "individuell" umstellt?

Gruß
Dan
MAINTAINER: 22_HOMEMODE, 98_Hyperion, 98_FileLogConvert, 98_serviced

Als kleine Unterstützung für meine Programmierungen könnt ihr mir gerne einen Kaffee spendieren: https://buymeacoff.ee/DeeSPe

erdnar

Hallo Dan,
ich verstehe zwar deine Frage nicht richtig (z.B. habe ich keinen "normalen" Modus im Fhem), gebe aber mal meinen Senf dazu:
Meinen Sauger (ist als XiaomiDevice im Fhem) starte ich immer über DOIFs (egal ob über Anwesenheitserkennung (ganze Wohnung) oder per Zuruf, z.B. "Alexa starte Küche saugen"), die den gewünschten Modus einstellen:({fhem("
set MQ_GollumSchalter OFF;
sleep 2;
set MQ_GollumSchalter ON;
sleep 10;
set SaugerS7 reconnect;
sleep 10;
set SaugerS7 cleaning_mode turbo;
sleep 2;
set SaugerS7 zone Küche
")}

Wobei "MQ_GollumSchalter" in Verbindung mit "reconnect" den ggfs. tief schlafenden Sauger wachrütteln.
Wenn ich die Reinigung lediglich mit "set SaugerS7 start" aktiviere, nimmt er natürlich die gerade eingestellten Reinigungsparameter, wobei ich immer leicht feucht wischen lasse (so lange das Wasser reicht) und die Teppicherkennung aktiv habe.
ErdnaR

DeeSPe

Hey ErdnaR,

danke für Deinen Hilfeversuch.
Mit "normalem" Modus meinte ich den "Vac & Mop" Modus.
Eigentlich suchte ich eine Möglichkeit per FHEM zwischen "Vac & Mop", "Wischen", "Saugen" und "Individuell" umzuschalten, da ich alles auf "Individuell" eingerichtet habe und bis heute dachte dass der Roboter beim täglichen Start der Reinigung alleine wieder auf "Vac & Mop" zurückfällt.
Nach heutiger erneuter Analyse des Codes meiner DailyWork Funktion ist mir aber aufgefallen dass nach meinem Startbefehl noch "set cleaning_mode turbo" hinterher kam (Relikt von meinem S1). Und genau das war wohl das Problem. Sobald man den "cleaning_mode" umstellt wechselt der Roboter wieder auf "Vac & Mop". Nachdem ich das heute heraus gelöscht habe ist er brav im Modus "Individuell" gefahren und steht nach der Reinigung auch immer noch in diesem Modus.

Mein eigentliches Problem ist damit gelöst.
Schön wäre es aber trotzdem wenn man per FHEM die verschiedenen Modi umschalten könnte, denn eigentlich würde es mir reichen wenn der Roboter alle 2 Tage wischt und sonst nur saugt.

Gruß
Dan

P.S. Das mit dem Wachrütteln habe ich nicht ganz verstanden. Meiner reagiert eigentlich immer wenn ich ihn per FHEM anspreche, es sei denn er ist disconnected.
MAINTAINER: 22_HOMEMODE, 98_Hyperion, 98_FileLogConvert, 98_serviced

Als kleine Unterstützung für meine Programmierungen könnt ihr mir gerne einen Kaffee spendieren: https://buymeacoff.ee/DeeSPe

erdnar

Zitat von: DeeSPe am 05 Februar 2023, 20:26:13
P.S. Das mit dem Wachrütteln habe ich nicht ganz verstanden. Meiner reagiert eigentlich immer wenn ich ihn per FHEM anspreche, es sei denn er ist disconnected.

Genau dafür ist das. Der Gollumschalter schaltet die Ladestation aus/an, danach connected er meist wieder.
ErdnaR

DeeSPe

Ahhh, also ist Gollumschalter die schaltbare Steckdose für den S7!
Danke für die Erklärung.

Gruß
Dan
MAINTAINER: 22_HOMEMODE, 98_Hyperion, 98_FileLogConvert, 98_serviced

Als kleine Unterstützung für meine Programmierungen könnt ihr mir gerne einen Kaffee spendieren: https://buymeacoff.ee/DeeSPe

gorefield

Moin,

seit kurzem werkelt ein Roborock S7 Pro Ultra in unserem Heim und mir fiel nach Einbindung in FHEM über das Modul 72_XiaomiDevice (wie auch schon anderen zuvor) auf, dass die Readings history_0-9 sowie last_clean_area|time und last_timestamp nicht da waren oder nicht gefüllt wurden.

Ein wenig Ursachenvorschung führte mich dann zu den JSON-Antworten des Bots, die anders strukturiert sind als vorherige Generationen. Mit ein wenig probieren und Trial and Error habe ich (eine mir) passende Version des Moduls geschrieben. Es ist ziemlich quick and dirty (zumal es mein erster Ausflug überhaupt in Perl ist), aber es funktioniert hier gut.

Die neuen Abfragen im Code sind auf das Model roborock.vacuum.a62 gemünzt, wie sich der S7 Pro Ultra hier meldet. Alles andere sollte unverändert weiter funktionieren.

Hier das Diff als Kurzform:

3449c3449,3450
<         return undef if(!defined($json->{result}));
---
>     return undef if(!defined($json->{result}));
>     return undef if(ref($json->{result}) ne "ARRAY");
3452,3465c3453,3455
<
<     if($hash->{model} eq "roborock.vacuum.a62") # Roborock S7 Pro Ultra
<     {
<       return undef if(ref($json->{result}) ne "HASH");
<       readingsBulkUpdate( $hash, "total_clean_time", sprintf("%.2f",int($json->{result}{clean_time})/3600), 1 ) if(defined($json->{result}{clean_time}));
<       readingsBulkUpdate( $hash, "total_clean_area", sprintf( "%.2f" ,int($json->{result}{clean_area})/1000000), 1 ) if(defined($json->{result}{clean_area}));
<       readingsBulkUpdate( $hash, "total_cleans", $json->{result}{clean_count}, 1 ) if(defined($json->{result}{clean_count}));
<     } else {
<       return undef if(ref($json->{result}) ne "ARRAY");
<       readingsBulkUpdate( $hash, "total_clean_time", sprintf("%.2f",int($json->{result}[0])/3600), 1 ) if(defined($json->{result}[0]));
<       readingsBulkUpdate( $hash, "total_clean_area", sprintf( "%.2f" ,int($json->{result}[1])/1000000), 1 ) if(defined($json->{result}[1]));
<       readingsBulkUpdate( $hash, "total_cleans", $json->{result}[2], 1 ) if(defined($json->{result}[2]));
<     }
<
---
>     readingsBulkUpdate( $hash, "total_clean_time", sprintf("%.2f",int($json->{result}[0])/3600), 1 ) if(defined($json->{result}[0]));
>     readingsBulkUpdate( $hash, "total_clean_area", sprintf( "%.2f" ,int($json->{result}[1])/1000000), 1 ) if(defined($json->{result}[1]));
>     readingsBulkUpdate( $hash, "total_cleans", $json->{result}[2], 1 ) if(defined($json->{result}[2]));
3468,3474d3457
<     my @records = ();
<     if($hash->{model} eq "roborock.vacuum.a62") # Roborock S7 Pro Ultra
<     {
<       @records = @{$json->{result}{records}};
<     } else {
<       @records = @{$json->{result}[3]};
<     }
3477c3460
<     foreach my $cleanrecord (@records) {
---
>     foreach my $cleanrecord (@{$json->{result}[3]}) {
3498a3482
>
3517a3502
>       my @cleanrecord = @{$cleanrecord};
3519,3532c3504,3506
<       if($hash->{model} eq "roborock.vacuum.a62") # Roborock S7 Pro Ultra
<       {
<         return undef if(ref($cleanrecord) ne "HASH");
<
<         readingsBulkUpdate( $hash, "last_timestamp", $cleanrecord->{begin}, 1 ) if($recordnumber == 0 && defined($json->{result}[0]));
<         readingsBulkUpdate( $hash, "history_".$recordnumber, FmtDateTime($cleanrecord->{begin}).": ".sprintf( "%.2f" ,int($cleanrecord->{area})/1000000)."m² in ".sprintf("%.2f",int($cleanrecord->{duration})/3600)."h, ".(($cleanrecord->{complete} eq "0")?"not finished":"finished cleaning"), 1 ) if($recordnumber < 10 && defined($json->{result}[0]));
<       } else {
<         return undef if(ref($cleanrecord) ne "ARRAY");
<
<         my @cleanrecord = @{$cleanrecord};
<         #Log3 $name, 2, "$name: $history $day $daynumber \n".Dumper($cleanrecord);
<         readingsBulkUpdate( $hash, "last_timestamp", $cleanrecord[0], 1 ) if($recordnumber == 0 && defined($json->{result}[0]));
<         readingsBulkUpdate( $hash, "history_".$recordnumber, FmtDateTime($cleanrecord[0]).": ".sprintf( "%.2f" ,int($cleanrecord[3])/1000000)."m² in ".sprintf("%.2f",int($cleanrecord[2])/3600)."h, ".(($cleanrecord[5] eq "0")?"not finished":"finished cleaning"), 1 ) if($recordnumber < 10 && defined($json->{result}[0]));
<       }
---
>       #Log3 $name, 2, "$name: $history $day $daynumber \n".Dumper($cleanrecord);
>       readingsBulkUpdate( $hash, "last_timestamp", $cleanrecord[0], 1 ) if($recordnumber == 0 && defined($json->{result}[0]));
>       readingsBulkUpdate( $hash, "history_".$recordnumber, FmtDateTime($cleanrecord[0]).": ".sprintf( "%.2f" ,int($cleanrecord[3])/1000000)."m² in ".sprintf("%.2f",int($cleanrecord[2])/3600)."h, ".(($cleanrecord[5] eq "0")?"not finished":"finished cleaning"), 1 ) if($recordnumber < 10 && defined($json->{result}[0]));
3535d3508
<


Ich hoffe, das hilft dem Einen oder Anderen weiter.

Viele Grüße
GF