From bc257843a191b8ad43743c9af66dc98a17445c0c Mon Sep 17 00:00:00 2001 From: moises <> Date: Mon, 21 May 2018 12:48:24 +0000 Subject: [PATCH] 32_withings/38_netatmo/72_XiaomiDevice: standard battery readings git-svn-id: https://svn.fhem.de/fhem/trunk@16763 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 1 + fhem/FHEM/32_withings.pm | 28 +-- fhem/FHEM/38_netatmo.pm | 84 ++++++-- fhem/FHEM/72_XiaomiDevice.pm | 399 +++++++++++++++++++++++++---------- 4 files changed, 364 insertions(+), 148 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index b21315719..441bdd3bf 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,6 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - change: 32_withings/38_netatmo/72_XiaomiDevice: standard battery readings - bugfix: 98_dewpoint: Use NOTIFYDEV with framework functions - feature: 57_Calendar: new parameter "limit" (forum #87566) - bugfix: 55_DWD_OpenData: updateAlertsCache causing "not a HASH reference" diff --git a/fhem/FHEM/32_withings.pm b/fhem/FHEM/32_withings.pm index 256ad1865..5299d7f6c 100644 --- a/fhem/FHEM/32_withings.pm +++ b/fhem/FHEM/32_withings.pm @@ -47,11 +47,11 @@ my %device_types = ( 0 => "User related", 64 => "Thermometer", ); my %device_models = ( 1 => { 1 => "Smart Scale", 2 => "Wireless Scale", 3 => "Smart Kid Scale", 4 => "Smart Body Analyzer", 5 => "WiFi Body Scale", 6 => "Cardio Scale", 7 => "Body Scale", }, - 2 => { 21 => "Smart Baby Monitor", 22 => "Withings Home", }, - 4 => { 41 => "iOS Blood Pressure Monitor", 42 => "Wireless Blood Pressure Monitor", }, - 16 => { 51 => "Pulse Ox", 52 => "Activite", 53 => "Activite v2", 54 => "Withings Go", 55 => "Withings Steel HR", }, - 32 => { 60 => "Withings Aura", 61 => "Aura Sleep Sensor", }, - 64 => { 70 => "Withings Thermo", }, ); + 2 => { 21 => "Smart Baby Monitor", 22 => "Home", 22 => "Home v2", }, + 4 => { 41 => "iOS Blood Pressure Monitor", 42 => "Wireless Blood Pressure Monitor", 43 => "BPM", 44 => "BPM+", }, + 16 => { 51 => "Pulse Ox", 52 => "Activite", 53 => "Activite v2", 54 => "Go", 55 => "Steel HR", }, + 32 => { 60 => "Aura", 61 => "Sleep Sensor", 61 => "Aura v2", 62 => "Sleep Mat", }, + 64 => { 70 => "Thermo", }, ); #Firmware files: cdnfw_withings_net #Smart Body Analyzer: /wbs02/wbs02_1521.bin @@ -128,6 +128,7 @@ my %measure_types = ( 1 => { name => "Weight (kg)", reading => "weight", }, 93 => { name => "Muscle Mass (%)", reading => "muscleRatio", }, # cardio scale 94 => { name => "Bone Mass (%)", reading => "boneRatio", }, # cardio scale 95 => { name => "Hydration (%)", reading => "hydration", }, # body water + 122 => { name => "Pulse Transit Time (ms)", reading => "pulseTransitTime", }, #-10 => { name => "Speed", reading => "speed", }, #-11 => { name => "Pace", reading => "pace", }, #-12 => { name => "Altitude", reading => "altitude", }, @@ -884,7 +885,7 @@ sub withings_initDevice($) { if( !defined( $attr{$name}{stateFormat} ) ) { - $attr{$name}{stateFormat} = "batteryLevel %"; + $attr{$name}{stateFormat} = "batteryPercent %"; $attr{$name}{stateFormat} = "co2 ppm" if( $device->{model} == 4 ); $attr{$name}{stateFormat} = "voc ppm" if( $device->{model} == 22 ); @@ -916,9 +917,10 @@ sub withings_initUser($) { my $user = withings_getUserDetail( $hash ); $hash->{shortName} = $user->{shortname}; - $hash->{gender} = ($user->{gender}==0)?"male":"female" if( defined($hash->{gender}) ); + $hash->{gender} = ($user->{gender}==0)?"male":"female" if( defined($user->{gender}) ); $hash->{userName} = ($user->{firstname}?$user->{firstname}:"") ." ". ($user->{lastname}?$user->{lastname}:""); $hash->{birthdate} = strftime("%Y-%m-%d", localtime($user->{birthdate})) if( defined($user->{birthdate}) ); + $hash->{age} = sprintf("%.1f",((int(time()) - int($user->{birthdate}))/(60*60*24*365.24225))) if( defined($user->{birthdate}) ); $hash->{created} = $user->{created}; $hash->{modified} = $user->{modified}; @@ -1767,8 +1769,8 @@ sub withings_parseProperties($$) { readingsBeginUpdate($hash); if( defined($detail->{batterylvl}) and $detail->{batterylvl} > 0 and $detail->{type} ne '32' and $detail->{model} ne '22') { - readingsBulkUpdate( $hash, "batteryLevel", $detail->{batterylvl}, 1 ); - readingsBulkUpdate( $hash, "battery", ($detail->{batterylvl}>20?"ok":"low"), 1 ); + readingsBulkUpdate( $hash, "batteryPercent", $detail->{batterylvl}, 1 ); + readingsBulkUpdate( $hash, "batteryState", ($detail->{batterylvl}>20?"ok":"low"), 1 ); } readingsBulkUpdate( $hash, "lastWeighinDate", FmtDateTime($detail->{lastweighindate}), 1 ) if( defined($detail->{lastweighindate}) and $detail->{lastweighindate} > 0 and $detail->{model} ne '60' ); readingsBulkUpdate( $hash, "lastSessionDate", FmtDateTime($detail->{lastsessiondate}), 1 ) if( defined($detail->{lastsessiondate}) ); @@ -3374,9 +3376,9 @@ sub withings_DbLog_splitFn($) { $reading = 'light'; $unit = 'lux'; } - elsif($event =~ m/batteryLevel/) + elsif($event =~ m/batteryPercent/) { - $reading = 'batteryLevel'; + $reading = 'batteryPercent'; $unit = '%'; } else @@ -3592,8 +3594,8 @@ sub withings_weekdays2Int( $ ) {
  • light
  • noise
  • voc
  • -
  • battery
  • -
  • batteryLevel
  • +
  • batteryState
  • +
  • batteryPercent

  • diff --git a/fhem/FHEM/38_netatmo.pm b/fhem/FHEM/38_netatmo.pm index f79f514c7..eb1e2d4f6 100644 --- a/fhem/FHEM/38_netatmo.pm +++ b/fhem/FHEM/38_netatmo.pm @@ -440,6 +440,12 @@ netatmo_Define($$) $hash->{NOTIFYDEV} = "global"; + if(IsDisabled($name) || !defined($name)) { + RemoveInternalTimer($hash); + $hash->{STATE} = "Disabled"; + return undef; + } + if( $init_done ) { netatmo_connect($hash) if( $hash->{SUBTYPE} eq "ACCOUNT" ); netatmo_initDevice($hash) if( $hash->{SUBTYPE} eq "DEVICE" ); @@ -507,6 +513,12 @@ netatmo_Notify($$) RemoveInternalTimer($hash); + if(IsDisabled($name) || !defined($name)) { + RemoveInternalTimer($hash); + $hash->{STATE} = "Disabled"; + return undef; + } + netatmo_connect($hash) if( $hash->{SUBTYPE} eq "ACCOUNT" ); netatmo_initDevice($hash) if( $hash->{SUBTYPE} eq "DEVICE" ); netatmo_initDevice($hash) if( $hash->{SUBTYPE} eq "MODULE" ); @@ -704,7 +716,7 @@ netatmo_getToken($) url => "https://".$hash->{helper}{apiserver}."/oauth2/token", timeout => 5, noshutdown => 1, - data => {grant_type => 'password', client_id => $hash->{helper}{client_id}, client_secret=> $hash->{helper}{client_secret}, username => netatmo_decrypt($hash->{helper}{username}), password => netatmo_decrypt($hash->{helper}{password}), scope => 'read_station read_thermostat write_thermostat write_camera read_camera access_camera read_presence write_presence access_presence read_homecoach'}, + data => {grant_type => 'password', client_id => $hash->{helper}{client_id}, client_secret=> $hash->{helper}{client_secret}, username => netatmo_decrypt($hash->{helper}{username}), password => netatmo_decrypt($hash->{helper}{password}), scope => 'read_station read_thermostat write_thermostat read_camera write_camera access_camera read_presence write_presence access_presence read_homecoach'}, }); netatmo_dispatch( {hash=>$hash,type=>'token'},$err,$data ); @@ -1075,6 +1087,12 @@ netatmo_initDevice($) Log3 $name, 1, "$name: no I/O device"; } + if(IsDisabled($name) || !defined($name)) { + RemoveInternalTimer($hash); + $hash->{STATE} = "Disabled"; + return undef; + } + my $device; if( $hash->{Module} ) { $device = netatmo_getDeviceDetail( $hash, $hash->{Module} ); @@ -1098,8 +1116,8 @@ netatmo_initDevice($) $hash->{last_seen} = FmtDateTime($device->{last_seen}) if(defined($device->{last_seen})); $hash->{wifi_status} = $device->{wifi_status} if(defined($device->{wifi_status})); $hash->{rf_status} = $device->{rf_status} if(defined($device->{rf_status})); - $hash->{battery_percent} = $device->{battery_percent} if(defined($device->{battery_percent})); - $hash->{battery_vp} = $device->{battery_vp} if(defined($device->{battery_vp})); + #$hash->{battery_percent} = $device->{battery_percent} if(defined($device->{battery_percent})); + #$hash->{battery_vp} = $device->{battery_vp} if(defined($device->{battery_vp})); if( $device->{place} ) { $hash->{country} = $device->{place}{country}; @@ -1110,8 +1128,9 @@ netatmo_initDevice($) $hash->{location} = $device->{place}{location}[1] .",". $device->{place}{location}[0]; } - readingsSingleUpdate($hash, "battery", ($device->{battery_percent} > 20) ? "ok" : "low", 1) if(defined($device->{battery_percent})); - readingsSingleUpdate($hash, "battery_percent", $device->{battery_percent}, 1) if(defined($device->{battery_percent})); + readingsSingleUpdate($hash, "batteryState", ($device->{battery_percent} > 20) ? "ok" : "low", 1) if(defined($device->{battery_percent})); + readingsSingleUpdate($hash, "batteryPercent", $device->{battery_percent}, 1) if(defined($device->{battery_percent})); + readingsSingleUpdate($hash, "batteryVoltage", $device->{battery_vp}/1000, 1) if(defined($device->{battery_vp})); my $state_format; if( $device->{data_type} ) { @@ -1145,7 +1164,11 @@ netatmo_initDevice($) $attr{$name}{stateFormat} = $state_format if( !defined($attr{$name}{stateFormat}) && defined($state_format) && defined($name) ); - return undef if(IsDisabled($name) || !defined($name)); + if(IsDisabled($name) || !defined($name)) { + RemoveInternalTimer($hash); + $hash->{STATE} = "Disabled"; + return undef; + } InternalTimer(gettimeofday()+90, "netatmo_poll", $hash); @@ -2497,7 +2520,11 @@ netatmo_poll($) my $name = $hash->{NAME}; - return undef if(IsDisabled($name) || !defined($name)); + if(IsDisabled($name) || !defined($name)) { + RemoveInternalTimer($hash); + $hash->{STATE} = "Disabled"; + return undef; + } # my $resolve = inet_aton($hash->{helper}{apiserver}); # if(!defined($resolve)) @@ -3383,7 +3410,7 @@ netatmo_parseReadings($$;$) my $rain_sum = ReadingsVal($name,"rain_sum",0); $rain_sum += $reading; readingsSingleUpdate($hash,"rain_sum",$rain_sum,1); - Log3 $name, 1, $name.": summed rain ".$reading." (to ".$rain_sum.")"; + Log3 $name, 2, $name.": summed rain ".$reading." (to ".$rain_sum.")"; } @@ -3618,10 +3645,11 @@ netatmo_parseGlobal($$) $device->{wifi_status} = $devicedata->{wifi_status} if(defined($devicedata->{wifi_status})); $device->{rf_status} = $devicedata->{rf_status} if(defined($devicedata->{rf_status})); #$device->{battery_percent} = $devicedata->{battery_percent} if(defined($devicedata->{battery_percent})); - $device->{battery_vp} = $devicedata->{battery_vp} if(defined($devicedata->{battery_vp})); + #$device->{battery_vp} = $devicedata->{battery_vp} if(defined($devicedata->{battery_vp})); - readingsSingleUpdate($device, "battery", ($devicedata->{battery_percent} > 20) ? "ok" : "low", 1) if(defined($devicedata->{battery_percent})); - readingsSingleUpdate($device, "battery_percent", $devicedata->{battery_percent}, 1) if(defined($devicedata->{battery_percent})); + readingsSingleUpdate($device, "batteryState", ($devicedata->{battery_percent} > 20) ? "ok" : "low", 1) if(defined($devicedata->{battery_percent})); + readingsSingleUpdate($device, "batteryPercent", $devicedata->{battery_percent}, 1) if(defined($devicedata->{battery_percent})); + readingsSingleUpdate($device, "batteryVoltage", $devicedata->{battery_vp}/1000, 1) if(defined($devicedata->{battery_vp})); if(defined($devicedata->{modules})) { @@ -3723,10 +3751,11 @@ netatmo_parseGlobal($$) $module->{wifi_status} = $moduledata->{wifi_status} if(defined($moduledata->{wifi_status})); $module->{rf_status} = $moduledata->{rf_status} if(defined($moduledata->{rf_status})); #$module->{battery_percent} = $moduledata->{battery_percent} if(defined($moduledata->{battery_percent})); - $module->{battery_vp} = $moduledata->{battery_vp} if(defined($moduledata->{battery_vp})); + #$module->{battery_vp} = $moduledata->{battery_vp} if(defined($moduledata->{battery_vp})); - readingsSingleUpdate($module, "battery", ($moduledata->{battery_percent} > 20) ? "ok" : "low", 1) if(defined($moduledata->{battery_percent})); - readingsSingleUpdate($module, "battery_percent", $moduledata->{battery_percent}, 1) if(defined($moduledata->{battery_percent})); + readingsSingleUpdate($module, "batteryState", ($moduledata->{battery_percent} > 20) ? "ok" : "low", 1) if(defined($moduledata->{battery_percent})); + readingsSingleUpdate($module, "batteryPercent", $moduledata->{battery_percent}, 1) if(defined($moduledata->{battery_percent})); + readingsSingleUpdate($module, "batteryVoltage", $moduledata->{battery_vp}/1000, 1) if(defined($moduledata->{battery_vp})); }#foreach module @@ -4092,8 +4121,9 @@ netatmo_parseHomeReadings($$;$) $tag->{notify_rule} = $tagdata->{notify_rule}; $tag->{notify_rule} = $tagdata->{notify_rule}; - readingsSingleUpdate($tag, "battery", ($tagdata->{battery_percent} > 20) ? "ok" : "low", 1) if(defined($tagdata->{battery_percent})); - readingsSingleUpdate($tag, "battery_percent", $tagdata->{battery_percent}, 1) if(defined($tagdata->{battery_percent})); + readingsSingleUpdate($tag, "batteryState", ($tagdata->{battery_percent} > 20) ? "ok" : "low", 1) if(defined($tagdata->{battery_percent})); + readingsSingleUpdate($tag, "batteryPercent", $tagdata->{battery_percent}, 1) if(defined($tagdata->{battery_percent})); + readingsSingleUpdate($tag, "batteryVoltage", $tagdata->{battery_vp}/1000, 1) if(defined($tagdata->{battery_vp})); } @@ -4837,7 +4867,7 @@ netatmo_parseThermostatReadings($$;$) $hash->{wifi_status} = $devicedata->{wifi_status} if(defined($devicedata->{wifi_status})); $hash->{rf_status} = $devicedata->{rf_status} if(defined($devicedata->{rf_status})); #$hash->{battery_percent} = $devicedata->{battery_percent} if(defined($devicedata->{battery_percent})); - $hash->{battery_vp} = $devicedata->{battery_vp} if(defined($devicedata->{battery_vp})); + #$hash->{battery_vp} = $devicedata->{battery_vp} if(defined($devicedata->{battery_vp})); $hash->{therm_orientation} = $devicedata->{therm_orientation} if(defined($devicedata->{therm_orientation})); $hash->{therm_relay_cmd} = $devicedata->{therm_relay_cmd} if(defined($devicedata->{therm_relay_cmd})); $hash->{udp_conn} = $devicedata->{udp_conn} if(defined($devicedata->{udp_conn})); @@ -4856,8 +4886,9 @@ netatmo_parseThermostatReadings($$;$) $hash->{timezone} = encode_utf8($devicedata->{place}{timezone}); } - readingsSingleUpdate($hash, "battery", ($devicedata->{battery_percent} > 20) ? "ok" : "low", 1) if(defined($devicedata->{battery_percent})); - readingsSingleUpdate($hash, "battery_percent", $devicedata->{battery_percent}, 1) if(defined($devicedata->{battery_percent})); + readingsSingleUpdate($hash, "batteryState", ($devicedata->{battery_percent} > 20) ? "ok" : "low", 1) if(defined($devicedata->{battery_percent})); + readingsSingleUpdate($hash, "batteryPercent", $devicedata->{battery_percent}, 1) if(defined($devicedata->{battery_percent})); + readingsSingleUpdate($hash, "batteryVoltage", $devicedata->{battery_vp}/1000, 1) if(defined($devicedata->{battery_vp})); if(defined($devicedata->{modules})) @@ -4885,7 +4916,7 @@ netatmo_parseThermostatReadings($$;$) $module->{wifi_status} = $moduledata->{wifi_status} if(defined($moduledata->{wifi_status})); $module->{rf_status} = $moduledata->{rf_status} if(defined($moduledata->{rf_status})); #$module->{battery_percent} = $moduledata->{battery_percent} if(defined($moduledata->{battery_percent})); - $module->{battery_vp} = $moduledata->{battery_vp} if(defined($moduledata->{battery_vp})); + #$module->{battery_vp} = $moduledata->{battery_vp} if(defined($moduledata->{battery_vp})); $module->{therm_orientation} = $moduledata->{therm_orientation} if(defined($moduledata->{therm_orientation})); #$module->{therm_relay_cmd} = $moduledata->{therm_relay_cmd} if(defined($moduledata->{therm_relay_cmd})); $module->{udp_conn} = $moduledata->{udp_conn} if(defined($moduledata->{udp_conn})); @@ -4904,8 +4935,9 @@ netatmo_parseThermostatReadings($$;$) $module->{timezone} = encode_utf8($moduledata->{place}{timezone}); } - readingsSingleUpdate($module, "battery", ($moduledata->{battery_percent} > 20) ? "ok" : "low", 1) if(defined($moduledata->{battery_percent})); - readingsSingleUpdate($module, "battery_percent", $moduledata->{battery_percent}, 1) if(defined($moduledata->{battery_percent})); + readingsSingleUpdate($module, "batteryState", ($moduledata->{battery_percent} > 20) ? "ok" : "low", 1) if(defined($moduledata->{battery_percent})); + readingsSingleUpdate($module, "batteryPercent", $moduledata->{battery_percent}, 1) if(defined($moduledata->{battery_percent})); + readingsSingleUpdate($module, "batteryVoltage", $moduledata->{battery_vp}/1000, 1) if(defined($moduledata->{battery_vp})); #readingsSingleUpdate($module, "name", encode_utf8($moduledata->{module_name}), 1) if(defined($moduledata->{module_name})); my $setmode = "manual"; @@ -6293,6 +6325,14 @@ sub netatmo_DbLog_splitFn($) { $unit = "ug/m3"; } + elsif($event =~ m/batteryPercent/) + { + $unit = "%"; + } + elsif($event =~ m/batteryVoltage/) + { + $unit = "V"; + } else { $value = $parts[1]; diff --git a/fhem/FHEM/72_XiaomiDevice.pm b/fhem/FHEM/72_XiaomiDevice.pm index 664adcb6d..b0b70b8ed 100755 --- a/fhem/FHEM/72_XiaomiDevice.pm +++ b/fhem/FHEM/72_XiaomiDevice.pm @@ -6,7 +6,7 @@ # 2018 Markus Moises < vorname at nachname . de > # # This module connects to Xiaomi Smart Home WiFi devices -# Currently supported: AirPurifier, Robot Vacuum, Smart Fan, UV Humidifier, Lamps +# Currently supported: Air Purifier, Robot Vacuum, Smart Fan, UV Humidifier, Lamps, Rice Cooker, Power Plugs # # https://forum.fhem.de/index.php/topic,73052.0.html # @@ -96,6 +96,26 @@ my %vacuum_errors = ( '0' => "None", '254' => "Bin full", '255' => "Internal error" , ); + +my %cooker_menus = ( '0000' => "None", + '0001' => "Cooking", + '0002' => "Quick cooking", + '0003' => "Rice porridge", + '0004' => "Heat preservation", + '0100' => "Personal settings" , ); + + +my %cooker_stages = ( '00' => "Idle", + '01' => "Preheating", + '02' => "Water-absorbing", + '03' => "Boiling", + '04' => "Gelantinizing", + '05' => "Braising" , ); + + + + + sub XiaomiDevice_Initialize($) { my ($hash) = @_; my $name = $hash->{NAME}; @@ -108,9 +128,9 @@ sub XiaomiDevice_Initialize($) { $hash->{WriteFn} = "XiaomiDevice_Write"; $hash->{DbLog_splitFn}= "XiaomiDevice_DbLog_splitFn"; $hash->{AttrFn} = "XiaomiDevice_Attr"; - $hash->{AttrList} = "subType:AirPurifier,Humidifier,VacuumCleaner,SmartFan,SmartLamp,EyeCare,WaterPurifier,Camera intervalData intervalSettings preset disable:0,1 zone_names point_names ". + $hash->{AttrList} = "subType:AirPurifier,Humidifier,VacuumCleaner,SmartFan,SmartLamp,EyeCare,WaterPurifier,Camera,RiceCooker,PowerPlug intervalData intervalSettings preset disable:0,1 zone_names point_names ". $readingFnAttributes; - + } sub XiaomiDevice_Define($$$) { @@ -156,7 +176,7 @@ sub XiaomiDevice_Define($$$) { } elsif(!defined($hash->{helper}{crypt}) || $hash->{helper}{crypt} ne "Rijndael") { $hash->{helper}{crypt} = "AES"; } - + my $req2 = eval { require Crypt::Rijndael_PP; @@ -195,7 +215,7 @@ sub XiaomiDevice_Define($$$) { if(length($a[3]) == 32) { $hash->{helper}{token} = $a[3]; } elsif(length($a[3]) == 96) { - + my $req3 = eval { require Crypt::ECB; @@ -209,7 +229,7 @@ sub XiaomiDevice_Define($$$) { $attr{$name}{disable} = "1"; return undef; } - + my $key = pack("H*","00000000000000000000000000000000"); my $crypt = Crypt::ECB->new; $crypt->padding(0); @@ -221,7 +241,7 @@ sub XiaomiDevice_Define($$$) { $crypt->cipher('Crypt::Rijndael_PP'); $Crypt::Rijndael_PP::DEFAULT_KEYSIZE = 128; } - $crypt->key($key); + $crypt->key($key); my $e = eval { $key = $crypt->decrypt_hex(substr($a[3],64,32)) }; if($@) { @@ -231,7 +251,7 @@ sub XiaomiDevice_Define($$$) { return undef; } $key = ($key ^ pack('h*','01010101010101010101010101010101')); - $crypt->key($key); + $crypt->key($key); $e = eval { $hash->{helper}{token} = $crypt->decrypt_hex(substr($a[3],0,64)) }; if($@) { @@ -261,7 +281,7 @@ sub XiaomiDevice_Define($$$) { } else { Log3 $name, 2, "$name: no or incorrect token defined!"; } - + #$hash->{helper}{token} = $a[3] if(defined($a[3])); $attr{$name}{subType} = "VacuumCleaner" if( defined($attr{$name}) && !defined($attr{$name}{subType}) ); $attr{$name}{stateFormat} = "pm25 µg/m³ / speed rpm / mode" if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "AirPurifier" && !defined($attr{$name}{stateFormat})); @@ -272,6 +292,8 @@ sub XiaomiDevice_Define($$$) { $attr{$name}{stateFormat} = "power" if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "EyeCare" && !defined($attr{$name}{stateFormat})); $attr{$name}{stateFormat} = "power" if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "WaterPurifier" && !defined($attr{$name}{stateFormat})); $attr{$name}{stateFormat} = "power" if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "Camera" && !defined($attr{$name}{stateFormat})); + $attr{$name}{stateFormat} = "method" if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "RiceCooker" && !defined($attr{$name}{stateFormat})); + $attr{$name}{stateFormat} = "power" if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "PowerPlug" && !defined($attr{$name}{stateFormat})); XiaomiDevice_ReadZones($hash) if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "VacuumCleaner"); @@ -300,14 +322,17 @@ sub XiaomiDevice_Get($@) { my $usage = "Unknown argument $command, choose one of data:noArg settings:noArg wifi_stats:noArg device_info:noArg"; - $usage = "Unknown argument $command, choose one of data:noArg settings:noArg clean_summary:noArg sound:noArg timer_clean:noArg timer_dnd:noArg log_status:noArg map serial_number:noArg wifi_stats:noArg device_info:noArg timezone:noArg" if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "VacuumCleaner"); + if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "VacuumCleaner"){ + $usage = "Unknown argument $command, choose one of data:noArg settings:noArg clean_summary:noArg timer_clean:noArg timer_dnd:noArg log_status:noArg serial_number:noArg wifi_stats:noArg device_info:noArg timezone:noArg"; + $usage .= " map sound:noArg" if(!defined($hash->{model}) || $hash->{model} ne "roborock.vacuum.c1"); + } return $usage if $command eq '?'; if(IsDisabled($name)) { return "XiaomiDevice $name is disabled. Aborting..."; } - + if($command eq 'data') { XiaomiDevice_GetUpdate($hash); @@ -456,7 +481,7 @@ sub XiaomiDevice_Get($@) { # EnableLogUpload:"enable_log_upload", # GetLogUploadStatus:"get_log_upload_status", # SetSoundPackage:"dnld_install_sound", # sid,ID default,0 -# url md5 sid https://awsbj0.fds.api.xiaomi.com/app/voice-pkg/package/english.pkg +# url md5 sid https://awsbj0.fds.api.xiaomi.com/app/voice-pkg/package/english.pkg # {"voice_id":"3","voice_title":"English","voice_sub_title":"Default English Voice","bg_pic":"https:\/\/awsbj0.fds.api.xiaomi.com\/app\/voice-pkg\/pic\/eng_ch.png","voice_pkg_url":"...english.pkg","voice_pkg_md5":"c60ea75cc41e422ade9c82de29b78c36","voice_pre_listen":"https:\/\/awsbj0.fds.api.xiaomi.com\/app\/voice-pkg\/pre_listen\/pre_listen_eng.wav","voice_pri":"13" # GetSoundPackageProgress:"get_sound_progress", # GetCurrentSoundPackage:"get_current_sound"}, @@ -491,7 +516,7 @@ sub XiaomiDevice_Set($$@) { #my ( $hash, $name, $cmd, @arg ) = @_; my ($hash, $name, @aa) = @_; my ($cmd, @arg) = @aa; - + return "XiaomiDevice $name is disabled. Aborting..." if(IsDisabled($name) && $cmd ne '?'); @@ -524,8 +549,8 @@ sub XiaomiDevice_Set($$@) { $list .= " ambstatus:on,off" if(defined(ReadingsVal($name,"ambstatus",undef))); $list .= " notifystatus:on,off" if(defined(ReadingsVal($name,"notifystatus",undef))); $list .= " ambvalue:slider,0,1,100" if(defined(ReadingsVal($name,"ambvalue",undef))); - $list .= " bls:on,off" if(defined(ReadingsVal($name,"bls",undef))); - #$list .= " scene_num" if(defined(ReadingsVal($name,"scene_num",undef))); + $list .= " bls:on,off" if(defined(ReadingsVal($name,"bls",undef))); + #$list .= " scene_num" if(defined(ReadingsVal($name,"scene_num",undef))); } elsif( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "WaterPurifier"){ $list .= " on:noArg off:noArg"; @@ -533,6 +558,17 @@ sub XiaomiDevice_Set($$@) { elsif( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "Camera"){ $list .= " on:noArg off:noArg"; } + elsif( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "RiceCooker"){ + $list .= " stop:noArg nowarn:noArg ack:noArg"; + } + elsif( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "PowerPlug"){ + $list .= " on:noArg off:noArg"; + $list .= " power_mode:green,normal"; + $list .= " wifi_led:on,off"; + $list .= " rt_power:on,off"; + $list .= " usb_power:on,off"; + $list .= " power_price"; + } elsif( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "VacuumCleaner"){ $list .= ' start:noArg stop:noArg pause:noArg spot:noArg charge:noArg locate:noArg dnd_enabled:on,off dnd_start dnd_end move remotecontrol:start,stop,forward,left,right reset_consumable:filter,mainbrush,sidebrush,sensors timezone volume:slider,0,1,100 volume_test:noArg'; $list .= ' carpet_mode:on,off'; @@ -540,7 +576,8 @@ sub XiaomiDevice_Set($$@) { $list .= ' fan_power:slider,1,1,100' if(defined($hash->{model}) && $hash->{model} eq "rockrobo.vacuum.v1"); $list .= ' cleaning_mode:quiet,balanced,turbo,max,mop'; - + + if(!defined($hash->{model}) || $hash->{model} ne "roborock.vacuum.c1") { if(defined($hash->{helper}{zone_names})) { $list .= ' zone:'.$hash->{helper}{zone_names}; } else { @@ -551,6 +588,7 @@ sub XiaomiDevice_Set($$@) { } else { $list .= ' goto'; } + } if (defined($hash->{helper}{timers})&&($hash->{helper}{timers}>0)) { @@ -593,7 +631,7 @@ sub XiaomiDevice_Set($$@) { my $packetid = $hash->{helper}{packetid}; $hash->{helper}{packetid} = $packetid+1; $hash->{helper}{packet}{$packetid} = 'set_light'; - + my $transition = $arg[1]; $transition = 30 if(!defined($transition) || int($transition < 30)); XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"set_default","params":[""]}' ); @@ -602,7 +640,7 @@ sub XiaomiDevice_Set($$@) { readingsSingleUpdate( $hash, "mode_saved", (ReadingsVal($name,"mode","auto").((ReadingsVal($name,"mode","-") eq "favorite") ? (" ".ReadingsVal($name,"favorite","0")) : "")), 1 ); return undef; } - + if ($cmd eq 'json') { return XiaomiDevice_WriteJSON($hash, join(" ", @arg) ); @@ -707,8 +745,12 @@ sub XiaomiDevice_Set($$@) { my $packetid = $hash->{helper}{packetid}; $hash->{helper}{packetid} = $packetid+1; $hash->{helper}{packet}{$packetid} = "app_stop"; + if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "RiceCooker") { + XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"set_func","params":["end02"]}' ); + } else { XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"app_stop","params":[""]}' ); } + } elsif ($cmd eq 'spot') { my $packetid = $hash->{helper}{packetid}; @@ -915,7 +957,7 @@ sub XiaomiDevice_Set($$@) { } elsif($cmd =~ /_days/) { - my @time = split(":",ReadingsVal($name, "timer".$timerno."_time","00:00" )); + my @time = split(":",ReadingsVal($name, "timer".$timerno."_time","12:00" )); my $daysstring = join(" ", @arg); my $program = ReadingsVal($name, "timer".$timerno."_program","start_clean" ); my $power = ReadingsVal($name, "timer".$timerno."_power","77" ); @@ -945,7 +987,7 @@ sub XiaomiDevice_Set($$@) { } elsif($cmd =~ /_program/) { - my @time = split(":",ReadingsVal($name, "timer".$timerno."_time","00:00" )); + my @time = split(":",ReadingsVal($name, "timer".$timerno."_time","12:00" )); my $daysstring = ReadingsVal($name, "timer".$timerno."_days","all" ); my $program = $arg[0]; my $power = ReadingsVal($name, "timer".$timerno."_power","77" ); @@ -975,7 +1017,7 @@ sub XiaomiDevice_Set($$@) { } elsif($cmd =~ /_power/) { - my @time = split(":",ReadingsVal($name, "timer".$timerno."_time","00:00" )); + my @time = split(":",ReadingsVal($name, "timer".$timerno."_time","12:00" )); my $daysstring = ReadingsVal($name, "timer".$timerno."_days","all" ); my $program = ReadingsVal($name, "timer".$timerno."_program","start_clean" ); my $power = $arg[0]; @@ -1025,7 +1067,7 @@ sub XiaomiDevice_Set($$@) { $daysstring .= " ".$arg[5] if(defined($arg[5])); $daysstring .= " ".$arg[6] if(defined($arg[6])); $daysstring .= " ".$arg[7] if(defined($arg[7])); - + my @singledate = split(" ", $daysstring); if(defined($singledate[0]) && defined($singledate[1]) && int($singledate[0])>0 && int($singledate[1])>0) { @@ -1056,7 +1098,7 @@ sub XiaomiDevice_Set($$@) { my $packetid = $hash->{helper}{packetid}; $hash->{helper}{packetid} = $packetid+1; $hash->{helper}{packet}{$packetid} = ($cmd eq 'on') ? 'power_on' : 'power_off'; - + if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "SmartLamp") { my $transition = $arg[0]; @@ -1072,7 +1114,7 @@ sub XiaomiDevice_Set($$@) { my $packetid = $hash->{helper}{packetid}; $hash->{helper}{packetid} = $packetid+1; $hash->{helper}{packet}{$packetid} = 'set_toggle'; - + XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"toggle","params":[""]}' ); #InternalTimer( gettimeofday() + 10, "XiaomiDevice_GetUpdate", $hash); } @@ -1081,7 +1123,7 @@ sub XiaomiDevice_Set($$@) { my $packetid = $hash->{helper}{packetid}; $hash->{helper}{packetid} = $packetid+1; $hash->{helper}{packet}{$packetid} = 'set_light'; - + my $transition = $arg[1]; $transition = 30 if(!defined($transition) || int($transition < 30)); XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"set_bright","params":['.$arg[0].',"smooth",'.$transition.']}' ); @@ -1091,7 +1133,7 @@ sub XiaomiDevice_Set($$@) { my $packetid = $hash->{helper}{packetid}; $hash->{helper}{packetid} = $packetid+1; $hash->{helper}{packet}{$packetid} = 'set_light'; - + my $transition = $arg[1]; $transition = 30 if(!defined($transition) || int($transition < 30)); XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"set_ct_abx","params":['.$arg[0].',"smooth",'.$transition.']}' ); @@ -1101,7 +1143,7 @@ sub XiaomiDevice_Set($$@) { my $packetid = $hash->{helper}{packetid}; $hash->{helper}{packetid} = $packetid+1; $hash->{helper}{packet}{$packetid} = 'set_light'; - + my $transition = $arg[1]; $transition = 30 if(!defined($transition) || int($transition < 30)); XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"set_hsv","params":['.ReadingsVal($name,"hue",0).','.$arg[0].',"smooth",'.$transition.']}' ); @@ -1111,7 +1153,7 @@ sub XiaomiDevice_Set($$@) { my $packetid = $hash->{helper}{packetid}; $hash->{helper}{packetid} = $packetid+1; $hash->{helper}{packet}{$packetid} = 'set_light'; - + my $transition = $arg[1]; $transition = 30 if(!defined($transition) || int($transition < 30)); XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"set_hsv","params":['.$arg[0].','.ReadingsVal($name,"sat",100).',"smooth",'.$transition.']}' ); @@ -1121,7 +1163,7 @@ sub XiaomiDevice_Set($$@) { my $packetid = $hash->{helper}{packetid}; $hash->{helper}{packetid} = $packetid+1; $hash->{helper}{packet}{$packetid} = 'set_light'; - + my $transition = $arg[1]; $transition = 30 if(!defined($transition) || int($transition < 30)); XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"set_cct","params":['.$arg[0].',"smooth",'.$transition.']}' ); @@ -1131,7 +1173,7 @@ sub XiaomiDevice_Set($$@) { my $packetid = $hash->{helper}{packetid}; $hash->{helper}{packetid} = $packetid+1; $hash->{helper}{packet}{$packetid} = 'set_light'; - + my $transition = $arg[1]; $transition = 30 if(!defined($transition) || int($transition < 30)); XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"set_rgb","params":['.$arg[0].',"smooth",'.$transition.']}' ); @@ -1190,7 +1232,7 @@ sub XiaomiDevice_Set($$@) { my $packetid = $hash->{helper}{packetid}; $hash->{helper}{packetid} = $packetid+1; $hash->{helper}{packet}{$packetid} = 'set_limit_hum'; - + my $limit_hum = $arg[0]; $limit_hum = 50 if(!defined($limit_hum) || int($limit_hum < 30) || int($limit_hum > 80)); XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"set_limit_hum","params":['.$limit_hum.']}' ); @@ -1199,7 +1241,7 @@ sub XiaomiDevice_Set($$@) { { my $packetid = $hash->{helper}{packetid}; $hash->{helper}{packetid} = $packetid+1; - + if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "SmartFan") { my $level = ReadingsVal($name, "level", 25); @@ -1211,7 +1253,7 @@ sub XiaomiDevice_Set($$@) { InternalTimer( gettimeofday() + 10, "XiaomiDevice_GetSpeed", $hash); return undef; } - + $hash->{helper}{packet}{$packetid} = ($arg[0] eq 'idle') ? 'mode_idle' : ($arg[0] eq 'auto') ? 'mode_auto' : ($arg[0] eq 'silent') ? 'mode_silent' : ($arg[0] eq 'medium') ? 'mode_medium' : ($arg[0] eq 'high') ? 'mode_high' : 'mode_favorite'; if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "Humidifier") @@ -1222,7 +1264,7 @@ sub XiaomiDevice_Set($$@) { return undef; } } - + XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"set_mode","params":["'.$arg[0].'"]}' ); if($arg[0] eq "favorite" && defined($arg[1])) { my $level = int($arg[1]); @@ -1280,7 +1322,7 @@ sub XiaomiDevice_Set($$@) { my $packetid = $hash->{helper}{packetid}; $hash->{helper}{packetid} = $packetid+1; $hash->{helper}{packet}{$packetid} = "set_poweroff_time"; - + if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "SmartLamp") { XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"start_cf","params":[0,2,"'.($arg[0]*1000).',7,0,0"]}' ); @@ -1353,34 +1395,85 @@ sub XiaomiDevice_Set($$@) { { my $packetid = $hash->{helper}{packetid}; $hash->{helper}{packetid} = $packetid+1; - + $hash->{helper}{packet}{$packetid} = ($arg[0] eq "single") ? 'sleep_single' : 'sleep_close'; XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"set_act_sleep","params":["'.$arg[0].'"]}' ); } elsif ($cmd eq 'wifi_setup') - { - return "WiFi configuration requires SSID and PASSWD as parameters, UID is required in initial setup for MiHome app use.\nset devicename wifi_setup []" if(!defined($arg[0]) || !defined($arg[1])); + { + return "WiFi configuration requires SSID and PASSWD as parameters, UID is required in initial setup for MiHome app use.\nset devicename wifi_setup []" if(!defined($arg[0]) || !defined($arg[1])); my @t = localtime(time); - my $gmt_offset_in_seconds = timegm(@t) - timelocal(@t); + my $gmt_offset_in_seconds = timegm(@t) - timelocal(@t); my $packetid = $hash->{helper}{packetid}; $hash->{helper}{packetid} = $packetid+1; - + $hash->{helper}{packet}{$packetid} = 'wifi_setup'; if(defined($arg[2])) { XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"miIO.config_router","params":{"tz":"Europe\/Berlin","ssid":"'.$arg[0].'","uid":'.$arg[2].',"gmt_offset":'.$gmt_offset_in_seconds.',"passwd":"'.$arg[1].'"}}' ); - return "WiFi configuration initialized for MiHome app use.\n\nSSID: ".$arg[0]."\nPassword: ".$arg[1]."\nXiaomi User ID: ".$arg[2]; + return "WiFi configuration initialized for MiHome app use.\n\nSSID: ".$arg[0]."\nPassword: ".$arg[1]."\nXiaomi User ID: ".$arg[2]; } else { XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"miIO.config_router","params":{"tz":"Europe\/Berlin","ssid":"'.$arg[0].'","gmt_offset":'.$gmt_offset_in_seconds.',"passwd":"'.$arg[1].'"}}' ); - return "WiFi configuration updated.\n\nSSID: ".$arg[0]."\nPassword: ".$arg[1]; + return "WiFi configuration updated.\n\nSSID: ".$arg[0]."\nPassword: ".$arg[1]; } } + elsif ($cmd eq 'nowarn') + { + my $packetid = $hash->{helper}{packetid}; + $hash->{helper}{packetid} = $packetid+1; + $hash->{helper}{packet}{$packetid} = "nowarn"; + XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"set_func","params":["nowarn"]}' ); + } + elsif ($cmd eq 'ack') + { + my $packetid = $hash->{helper}{packetid}; + $hash->{helper}{packetid} = $packetid+1; + $hash->{helper}{packet}{$packetid} = "ack"; + XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"set_func","params":["ack"]}' ); + } + elsif ($cmd eq 'power_mode')#green,normal + { + my $packetid = $hash->{helper}{packetid}; + $hash->{helper}{packetid} = $packetid+1; + $hash->{helper}{packet}{$packetid} = "power_mode"; + XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"set_power_mode","params":["'.$arg[0].'"]}' ); + } + elsif ($cmd eq 'power_price')#1..999 + { + my $packetid = $hash->{helper}{packetid}; + $hash->{helper}{packetid} = $packetid+1; + $hash->{helper}{packet}{$packetid} = "power_price"; + XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"set_power_price","params":['.$arg[0].']}' ); + } + elsif ($cmd eq 'wifi_led')#on,off + { + my $packetid = $hash->{helper}{packetid}; + $hash->{helper}{packetid} = $packetid+1; + $hash->{helper}{packet}{$packetid} = "wifi_led"; + XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"set_wifi_led","params":["'.$arg[0].'"]}' ); + } + elsif ($cmd eq 'rt_power')#on,off + { + my $packetid = $hash->{helper}{packetid}; + $hash->{helper}{packetid} = $packetid+1; + $hash->{helper}{packet}{$packetid} = "rt_power"; + $arg[0] = ($arg[0] eq "on") ? 1 : 0; + XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"set_rt_power","params":['.$arg[0].']}' ); + } + elsif ($cmd eq 'usb_power')#on,off + { + my $packetid = $hash->{helper}{packetid}; + $hash->{helper}{packetid} = $packetid+1; + $hash->{helper}{packet}{$packetid} = "usb_power"; + $arg[0] = ($arg[0] eq "on") ? "set_usb_on" : "set_usb_off"; + XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"'.$arg[0].'","params":[]}' ); + } else { return SetExtensions($hash, $list, $name, @aa) if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "AirPurifier"); - return "Unknown argument $cmd, choose one of $list"; + return "Unknown argument $cmd, choose one of $list"; } return undef; } @@ -1512,13 +1605,23 @@ sub XiaomiDevice_GetUpdate($) elsif( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "WaterPurifier") { $hash->{helper}{packet}{$packetid} = "water_data"; - XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["mode","tds","filter1_life","filter1_state","filter_life","filter_state","life","state","level","volume","filter","usage"]}' ); + XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["power","mode","tds","filter1_life","filter1_state","filter_life","filter_state","life","state","level","volume","filter","usage","temperature","uv_life","uv_state","elecval_state"]}' ); } elsif( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "Camera") { $hash->{helper}{packet}{$packetid} = "camera_data"; XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["auto_low_light"]}' ); } + elsif( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "RiceCooker") + { + $hash->{helper}{packet}{$packetid} = "ricecooker_data"; + XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["func", "menu", "stage", "temp", "t_func", "t_precook", "t_cook", "setting", "delay", "version"]}' ); + } + elsif( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "PowerPlug") + { + $hash->{helper}{packet}{$packetid} = "powerplug_data"; + XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["power", "temperature", "current", "mode", "power_consume_rate", "wifi_led", "power_price", "voltage", "power_factor", "elec_leakage"]}' ); + } return undef; } @@ -1540,7 +1643,7 @@ sub XiaomiDevice_GetSettings($) $hash->{helper}{packet}{$packetid} = "air_settings"; return XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["buzzer","led_b","child_lock","app_extra","act_sleep","sleep_time"]}' ); } - + if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "Humidifier") { my $packetid = $hash->{helper}{packetid}; @@ -1548,7 +1651,7 @@ sub XiaomiDevice_GetSettings($) $hash->{helper}{packet}{$packetid} = "hum_settings"; return XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["buzzer","led_b","child_lock","limit_hum"]}' ); } - + if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "SmartFan") { my $packetid = $hash->{helper}{packetid}; @@ -1556,7 +1659,7 @@ sub XiaomiDevice_GetSettings($) $hash->{helper}{packet}{$packetid} = "fan_data"; return XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["angle","angle_enable","power","bat_charge","battery","speed_level","natural_level","buzzer","led_b","poweroff_time","ac_power","child_lock","temp_dec","humidity","speed"]}' ); } - + if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "SmartLamp") { my $packetid = $hash->{helper}{packetid}; @@ -1564,7 +1667,7 @@ sub XiaomiDevice_GetSettings($) $hash->{helper}{packet}{$packetid} = "lamp_data"; return XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["power","bright","cct","snm","dv","ct","color_mode","delayoff","flowing","flow_params","name","rgb","hue","sat","ambstatus","ambvalue","eyecare","bls","dvalue","kid_mode","skey_act","skey_scene_id","lan_ctrl"]}' ); } - + if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "EyeCare") { my $packetid = $hash->{helper}{packetid}; @@ -1572,17 +1675,29 @@ sub XiaomiDevice_GetSettings($) $hash->{helper}{packet}{$packetid} = "lamp_data"; return XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["power","bright","scene_num","notifystatus","ambstatus","ambvalue","eyecare","bls","dvalue"]}' ); } - + if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "WaterPurifier") { return undef; } - + if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "Camera") { return undef; } - + + if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "RiceCooker") + { + return undef; + } + if( defined($attr{$name}) && defined($attr{$name}{subType}) && $attr{$name}{subType} eq "PowerPlug") + { + my $packetid = $hash->{helper}{packetid}; + $hash->{helper}{packetid} = $packetid+1; + $hash->{helper}{packet}{$packetid} = "powerplug_data"; + return XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_prop","params":["power", "temperature", "current", "mode", "power_consume_rate", "wifi_led", "power_price"]}' ); + } + my $packetid = $hash->{helper}{packetid}; $hash->{helper}{packetid} = $packetid+1; @@ -1600,7 +1715,7 @@ sub XiaomiDevice_GetSettings($) $hash->{helper}{packetid} = $packetid+1; $hash->{helper}{packet}{$packetid} = "get_timer"; XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_timer","params":[""]}' ); - + $packetid = $hash->{helper}{packetid}; $hash->{helper}{packetid} = $packetid+1; $hash->{helper}{packet}{$packetid} = "get_sound_volume"; @@ -1718,7 +1833,7 @@ sub XiaomiDevice_WriteJSON($$) { my ($hash,$json) = @_; my $name = $hash->{NAME}; - + Log3 $name, 4, "$name: write $json (".length($json).")"; if(IsDisabled($name)) { @@ -1728,20 +1843,20 @@ sub XiaomiDevice_WriteJSON($$) XiaomiDevice_initSend($hash) if(!defined($hash->{helper}{last_read}) || $hash->{helper}{last_read} < (int(time())-180) ); - + my $key = Digest::MD5::md5(pack('H*', $hash->{helper}{token})); my $iv = Digest::MD5::md5($key.pack('H*', $hash->{helper}{token})); my $cbc; - + if($hash->{helper}{crypt} ne "Rijndael"){ - $cbc = Crypt::CBC->new(-key => $key, -cipher => 'Crypt::Cipher::AES',-iv => $iv, -literal_key => 1, -header => "none", -keysize => 16 ); + $cbc = Crypt::CBC->new(-key => $key, -cipher => 'Crypt::Cipher::AES',-iv => $iv, -literal_key => 1, -header => "none", -keysize => 16 ); } else { $Crypt::Rijndael_PP::DEFAULT_KEYSIZE = 128; - $cbc = Crypt::CBC->new(-key => $key, -cipher => 'Crypt::Rijndael_PP',-iv => $iv, -literal_key => 1, -header => "none", -keysize => 16 ); + $cbc = Crypt::CBC->new(-key => $key, -cipher => 'Crypt::Rijndael_PP',-iv => $iv, -literal_key => 1, -header => "none", -keysize => 16 ); } my $crypt = $cbc->encrypt_hex($json); $crypt = pack('H*', $crypt); - + if(!defined($hash->{helper}) || !defined($hash->{helper}{sequence}) || !defined($hash->{helper}{dev}) || !defined($hash->{helper}{id}) || !defined($hash->{helper}{token}) ) { RemoveInternalTimer($hash); @@ -1760,7 +1875,7 @@ sub XiaomiDevice_WriteJSON($$) my $data = pack('H*', $package); XiaomiDevice_Write($hash,$data); - + return undef; } @@ -1771,7 +1886,7 @@ sub XiaomiDevice_ParseJSON($$) { my ($hash,$jsonstring) = @_; my $name = $hash->{NAME}; - + Log3 $name, 2, "$name: invalid JSON: $jsonstring" if( $jsonstring !~ m/^{.*}/ ); return undef if( $jsonstring !~ m/^{.*}/ ); $jsonstring =~ s/,,/,/g; @@ -1782,7 +1897,7 @@ sub XiaomiDevice_ParseJSON($$) Log3 $name, 2, "$name: invalid json evaluation: $jsonstring"; return undef; } - + Log3 $name, 5, "$name: parse id ".$json->{id}."\n".Dumper($json); my $msgid = $json->{id}; @@ -1795,7 +1910,7 @@ sub XiaomiDevice_ParseJSON($$) return undef; } Log3 $name, 4, "$name: parse id ".$json->{id}." / ".$msgtype; - + Log3 $name, 4, "$name: msg ref is ".ref($json->{result}); if(defined($json->{error}) && defined($json->{error}{message})) @@ -2001,8 +2116,8 @@ sub XiaomiDevice_ParseJSON($$) readingsBulkUpdate( $hash, "angle_enable", $json->{result}[1], 1 ) if(defined($json->{result}[1])); readingsBulkUpdate( $hash, "power", $json->{result}[2], 1 ) if(defined($json->{result}[2])); readingsBulkUpdate( $hash, "charging", $json->{result}[3], 1 ) if(defined($json->{result}[3])); - readingsBulkUpdate( $hash, "batteryLevel", $json->{result}[4], 1 ) if(defined($json->{result}[4])); - readingsBulkUpdate( $hash, "battery", int($json->{result}[4])<20 ? "low" : "ok", 1 ) if(defined($json->{result}[4])); + readingsBulkUpdate( $hash, "batteryPercent", $json->{result}[4], 1 ) if(defined($json->{result}[4])); + readingsBulkUpdate( $hash, "batteryState", int($json->{result}[4])<20 ? "low" : "ok", 1 ) if(defined($json->{result}[4])); my $fanspeed = 0; $fanspeed = $json->{result}[5] if(defined($json->{result}[5])); $fanspeed = $json->{result}[6] if(defined($json->{result}[6]) && int($json->{result}[6])>0); @@ -2038,24 +2153,29 @@ sub XiaomiDevice_ParseJSON($$) readingsEndUpdate($hash,1); return undef; } - + if($msgtype eq "water_data") { return undef if(!defined($json->{result})); return undef if(ref($json->{result}) ne "ARRAY"); readingsBeginUpdate($hash); - readingsBulkUpdate( $hash, "mode", $json->{result}[0], 1 ) if(defined($json->{result}[0])); - readingsBulkUpdate( $hash, "tds", $json->{result}[1], 1 ) if(defined($json->{result}[1])); - readingsBulkUpdate( $hash, "filter1_life", $json->{result}[2], 1 ) if(defined($json->{result}[2])); - readingsBulkUpdate( $hash, "filter1_state", ($json->{result}[3]), 1 ) if(defined($json->{result}[3])); - readingsBulkUpdate( $hash, "filter_life", $json->{result}[4], 1 ) if(defined($json->{result}[4])); - readingsBulkUpdate( $hash, "filter_state", $json->{result}[5], 1 ) if(defined($json->{result}[5])); - readingsBulkUpdate( $hash, "life", $json->{result}[6], 1 ) if(defined($json->{result}[6])); - readingsBulkUpdate( $hash, "state", $json->{result}[7], 1 ) if(defined($json->{result}[7])); - readingsBulkUpdate( $hash, "level", $json->{result}[8], 1 ) if(defined($json->{result}[8])); - readingsBulkUpdate( $hash, "volume", $json->{result}[9], 1 ) if(defined($json->{result}[9])); - readingsBulkUpdate( $hash, "filter", $json->{result}[10], 1 ) if(defined($json->{result}[10])); - readingsBulkUpdate( $hash, "usage", $json->{result}[11], 1 ) if(defined($json->{result}[11])); + readingsBulkUpdate( $hash, "power", $json->{result}[0], 1 ) if(defined($json->{result}[0])); + readingsBulkUpdate( $hash, "mode", $json->{result}[1], 1 ) if(defined($json->{result}[1])); + readingsBulkUpdate( $hash, "tds", $json->{result}[2], 1 ) if(defined($json->{result}[2])); + readingsBulkUpdate( $hash, "filter1_life", $json->{result}[3], 1 ) if(defined($json->{result}[3])); + readingsBulkUpdate( $hash, "filter1_state", ($json->{result}[4]), 1 ) if(defined($json->{result}[4])); + readingsBulkUpdate( $hash, "filter_life", $json->{result}[5], 1 ) if(defined($json->{result}[5])); + readingsBulkUpdate( $hash, "filter_state", $json->{result}[6], 1 ) if(defined($json->{result}[6])); + readingsBulkUpdate( $hash, "life", $json->{result}[7], 1 ) if(defined($json->{result}[7])); + readingsBulkUpdate( $hash, "state", $json->{result}[8], 1 ) if(defined($json->{result}[8])); + readingsBulkUpdate( $hash, "level", $json->{result}[9], 1 ) if(defined($json->{result}[9])); + readingsBulkUpdate( $hash, "volume", $json->{result}[10], 1 ) if(defined($json->{result}[10])); + readingsBulkUpdate( $hash, "filter", $json->{result}[11], 1 ) if(defined($json->{result}[11])); + readingsBulkUpdate( $hash, "usage", $json->{result}[12], 1 ) if(defined($json->{result}[12])); + readingsBulkUpdate( $hash, "temperature", $json->{result}[13], 1 ) if(defined($json->{result}[13])); + readingsBulkUpdate( $hash, "uv_life", $json->{result}[14], 1 ) if(defined($json->{result}[14])); + readingsBulkUpdate( $hash, "uv_state", $json->{result}[15], 1 ) if(defined($json->{result}[15])); + readingsBulkUpdate( $hash, "elecval_state", $json->{result}[16], 1 ) if(defined($json->{result}[16])); readingsEndUpdate($hash,1); return undef; } @@ -2070,6 +2190,49 @@ sub XiaomiDevice_ParseJSON($$) return undef; } + if($msgtype eq "ricecooker_data") + { + return undef if(!defined($json->{result})); + return undef if(ref($json->{result}) ne "ARRAY"); + readingsBeginUpdate($hash); + readingsBulkUpdate( $hash, "func", $json->{result}[0], 1 ) if(defined($json->{result}[0])); + readingsBulkUpdate( $hash, "menu", $cooker_menus{$json->{result}[1]}, 1 ) if(defined($json->{result}[1])); + #readingsBulkUpdate( $hash, "menu", $json->{result}[1], 1 ) if(defined($json->{result}[1])); + readingsBulkUpdate( $hash, "stage", $cooker_stages{substr($json->{result}[1],0,2)}, 1 ) if(defined($json->{result}[2])); + #readingsBulkUpdate( $hash, "stage", $json->{result}[2], 1 ) if(defined($json->{result}[2])); + readingsBulkUpdate( $hash, "temp", unpack('H*', substr($json->{result}[3],-2)), 1 ) if(defined($json->{result}[3])); + #readingsBulkUpdate( $hash, "temp", $json->{result}[3], 1 ) if(defined($json->{result}[3])); + readingsBulkUpdate( $hash, "t_func", $json->{result}[4], 1 ) if(defined($json->{result}[4])); + readingsBulkUpdate( $hash, "t_precook", $json->{result}[5], 1 ) if(defined($json->{result}[5])); + readingsBulkUpdate( $hash, "t_cook", $json->{result}[6], 1 ) if(defined($json->{result}[6])); + readingsBulkUpdate( $hash, "setting", $json->{result}[7], 1 ) if(defined($json->{result}[7])); + readingsBulkUpdate( $hash, "delay", $json->{result}[8], 1 ) if(defined($json->{result}[8])); + readingsBulkUpdate( $hash, "version", $json->{result}[9], 1 ) if(defined($json->{result}[9])); + readingsEndUpdate($hash,1); + return undef; + } + + if($msgtype eq "powerplug_data") + { + return undef if(!defined($json->{result})); + return undef if(ref($json->{result}) ne "ARRAY"); + return undef if(ref($json->{result}[0]) ne "HASH"); + readingsBeginUpdate($hash); + readingsBulkUpdate( $hash, "power", $json->{result}[0]{power}, 1 ) if(defined($json->{result}[0]{power})); + readingsBulkUpdate( $hash, "temperature", $json->{result}[0]{temperature}, 1 ) if(defined($json->{result}[0]{temperature})); + readingsBulkUpdate( $hash, "current", $json->{result}[0]{current}, 1 ) if(defined($json->{result}[0]{current})); + readingsBulkUpdate( $hash, "power_mode", $json->{result}[0]{mode}, 1 ) if(defined($json->{result}[0]{mode})); + readingsBulkUpdate( $hash, "power_consume_rate", $json->{result}[0]{power_consume_rate}, 1 ) if(defined($json->{result}[0]{power_consume_rate})); + readingsBulkUpdate( $hash, "wifi_led", $json->{result}[0]{wifi_led}, 1 ) if(defined($json->{result}[0]{wifi_led})); + readingsBulkUpdate( $hash, "power_price", $json->{result}[0]{power_price}, 1 ) if(defined($json->{result}[0]{power_price})); + readingsBulkUpdate( $hash, "voltage", $json->{result}[0]{voltage}, 1 ) if(defined($json->{result}[0]{voltage})); + readingsBulkUpdate( $hash, "power_factor", $json->{result}[0]{power_factor}, 1 ) if(defined($json->{result}[0]{power_factor})); + readingsBulkUpdate( $hash, "elec_leakage", $json->{result}[0]{elec_leakage}, 1 ) if(defined($json->{result}[0]{elec_leakage})); + #readingsBulkUpdate( $hash, "setting", (($json->{result}[0]{setting} eq "1")?"yes":"no"), 1 ) if(defined($json->{result}[0]{setting})); + readingsEndUpdate($hash,1); + return undef; + } + #{ "result": [ { "msg_ver": 3, "msg_seq": 4, "state": 8, "battery": 100, "clean_time": 3, "clean_area": 0, "error_code": 0, "map_present": 0, "in_cleaning": 0, "fan_power": 10, "dnd_enabled": 1 } ], "id": 1201 } if($msgtype eq "get_status") { @@ -2090,15 +2253,15 @@ sub XiaomiDevice_ParseJSON($$) #readingsBulkUpdate( $hash, "msg_seq", $json->{result}[0]{msg_seq}, 1 ) if(defined($json->{result}[0]{msg_seq})); if(defined($json->{result}[0]{error_code}) && $json->{result}[0]{error_code} ne "0") { - readingsBulkUpdate( $hash, "state", "Error", 1 ); + readingsBulkUpdate( $hash, "state", "Error", 1 ); } elsif(defined($json->{result}[0]{state}) && defined($json->{result}[0]{battery}) && $json->{result}[0]{battery} eq "100" && $json->{result}[0]{state} eq "8") { - readingsBulkUpdate( $hash, "state", "Docked", 1 ); + readingsBulkUpdate( $hash, "state", "Docked", 1 ); } elsif(defined($json->{result}[0]{state})) { readingsBulkUpdate( $hash, "state", $vacuum_states{$json->{result}[0]{state}}, 1 ); } - readingsBulkUpdate( $hash, "batteryLevel", $json->{result}[0]{battery}, 1 ) if(defined($json->{result}[0]{battery})); - readingsBulkUpdate( $hash, "battery", int($json->{result}[0]{battery})<20 ? "low" : "ok", 1 ) if(defined($json->{result}[0]{battery})); + readingsBulkUpdate( $hash, "batteryPercent", $json->{result}[0]{battery}, 1 ) if(defined($json->{result}[0]{battery})); + readingsBulkUpdate( $hash, "batteryState", int($json->{result}[0]{battery})<20 ? "low" : "ok", 1 ) if(defined($json->{result}[0]{battery})); readingsBulkUpdate( $hash, "last_clean_time", sprintf( "%.2f" ,int($json->{result}[0]{clean_time})/3600), 1) if(defined($json->{result}[0]{clean_time}));#sprintf( "%.1f", int($json->{result}[0]{clean_time})/3600), 1 ); readingsBulkUpdate( $hash, "last_clean_area", sprintf( "%.2f" ,int($json->{result}[0]{clean_area})/1000000), 1 ) if(defined($json->{result}[0]{clean_area})); readingsBulkUpdate( $hash, "error_code", $vacuum_errors{$json->{result}[0]{error_code}}, 1 ) if(defined($json->{result}[0]{error_code})); @@ -2178,7 +2341,7 @@ sub XiaomiDevice_ParseJSON($$) 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])); readingsEndUpdate($hash,1); - + my $i = 0; foreach my $cleanrecord (@{$json->{result}[3]}) { @@ -2190,7 +2353,7 @@ sub XiaomiDevice_ParseJSON($$) $hash->{helper}{history}{$packetid} = $i++; XiaomiDevice_WriteJSON($hash, '{"id":'.$packetid.',"method":"get_clean_record","params":['.$cleanrecord.']}' ); } - + $hash->{helper}{historydays} = $i; $hash->{helper}{cleanrecord} = 0; @@ -2206,7 +2369,7 @@ sub XiaomiDevice_ParseJSON($$) return undef; } - + if($msgtype =~ /get_clean_record/) { return undef if(!defined($json->{result})); @@ -2219,7 +2382,7 @@ sub XiaomiDevice_ParseJSON($$) delete $hash->{helper}{history}{$msgid}; readingsBeginUpdate($hash); - + my $recordnumber = $hash->{helper}{cleanrecord}; foreach my $cleanrecord (@{$json->{result}}) { @@ -2231,7 +2394,7 @@ sub XiaomiDevice_ParseJSON($$) $hash->{helper}{cleanrecord}++; } readingsEndUpdate($hash,1); - + if($daynumber == $hash->{helper}{historydays}-1) { $recordnumber = $hash->{helper}{cleanrecord}; @@ -2255,7 +2418,7 @@ sub XiaomiDevice_ParseJSON($$) next if($i>9); $hash->{helper}{timers} = $i; my @timerstring = @{$timerelement}; - + my $timestamp = $timerstring[0]; my $timerstate = $timerstring[1]; my $timercron = $timerstring[2][0]; @@ -2268,11 +2431,11 @@ sub XiaomiDevice_ParseJSON($$) readingsBulkUpdate( $hash, "timer".$i, $timerstate, 1 ) if(defined($timerstate)); my @timestring = split(" ",$timercron); readingsBulkUpdate( $hash, "timer".$i."_time", sprintf("%02d",$timestring[1]).":".sprintf("%02d",$timestring[0]), 1 ) if(defined($timestring[1])); - + if(defined($timestring[3]) && $timestring[2] ne "*") { readingsBulkUpdate( $hash, "timer".$i."_days", sprintf("%02d",$timestring[2])." ".sprintf("%02d",$timestring[3]), 1 ); - } + } elsif(defined($timestring[4]) && $timestring[4] ne "*") { if($timestring[4] eq "0,1,2,3,4,5,6") @@ -2292,7 +2455,7 @@ sub XiaomiDevice_ParseJSON($$) readingsBulkUpdate( $hash, "timer".$i."_days", join(",", @days), 1 ); } } - elsif(defined($timestring[4]) && $timestring[4] eq "*") + elsif(defined($timestring[4]) && $timestring[4] eq "*") { readingsBulkUpdate( $hash, "timer".$i."_days", "all", 1 ); } @@ -2309,7 +2472,7 @@ sub XiaomiDevice_ParseJSON($$) { fhem( "deletereading $name timer".$i."_program" ); } - + if(defined($timerpower)) { readingsBulkUpdate( $hash, "timer".$i."_power", $timerpower, 1 ); @@ -2318,7 +2481,7 @@ sub XiaomiDevice_ParseJSON($$) { fhem( "deletereading $name timer".$i."_power" ); } - + readingsEndUpdate($hash,1); $i++; } @@ -2351,6 +2514,7 @@ sub XiaomiDevice_ParseJSON($$) { return undef if(!defined($json->{result})); return undef if(ref($json->{result}) ne "ARRAY"); + return readingsSingleUpdate( $hash, "serial_number", $json->{result}[0], 1 ) if(defined($json->{result}[0]) && ref($json->{result}[0]) eq ""); readingsSingleUpdate( $hash, "serial_number", $json->{result}[0]{serial_number}, 1 ) if(defined($json->{result}[0]{serial_number})); return undef; } @@ -2381,12 +2545,16 @@ sub XiaomiDevice_ParseJSON($$) $hash->{mac} = $json->{result}{mac} if(defined($json->{result}{mac})); $hash->{token} = $json->{result}{token} if(defined($json->{result}{token})); $hash->{wifi_firmware} = $json->{result}{wifi_fw_ver} if(defined($json->{result}{wifi_fw_ver})); + $hash->{mcu_firmware} = $json->{result}{mcu_fw_ver} if(defined($json->{result}{mcu_fw_ver})); + $hash->{hardware} = $json->{result}{hw_ver} if(defined($json->{result}{hw_ver})); return undef; } if($msgtype eq "get_current_sound") { return undef if(!defined($json->{result})); return undef if(ref($json->{result}) ne "ARRAY"); + return readingsSingleUpdate( $hash, "current_sound", $json->{result}[0], 1 ) if(defined($json->{result}[0]) && ref($json->{result}[0]) eq ""); + return undef if(ref($json->{result}[0]) ne "HASH"); readingsSingleUpdate( $hash, "current_sound", ($json->{result}[0]{sid_in_use} eq "3" ? "english" : "chinese"), 1 ) if(defined($json->{result}[0]{sid_in_use})); return undef; } @@ -2394,19 +2562,20 @@ sub XiaomiDevice_ParseJSON($$) { return undef if(!defined($json->{result})); return undef if(ref($json->{result}) ne "ARRAY"); - readingsSingleUpdate( $hash, "timezone", $json->{result}[0], 1 ) if(defined($json->{result}[0])); + return readingsSingleUpdate( $hash, "timezone", $json->{result}[0], 1 ) if(defined($json->{result}[0]) && ref($json->{result}[0]) eq ""); + readingsSingleUpdate( $hash, "timezone", $json->{result}[0]{olson}, 1 ) if(defined($json->{result}[0]) && ref($json->{result}[0]) eq "HASH" && defined($json->{result}[0]{olson})); return undef; } - + Log3 $name, 5, "$name: parse result for ".$json->{id}." is ".$json->{result} if($json->{result}); - + return InternalTimer( gettimeofday() + 2, "XiaomiDevice_GetSpeed", $hash) if($msgtype eq "set_level"); return InternalTimer( gettimeofday() + 2, "XiaomiDevice_GetUpdate", $hash) if($msgtype eq "set_light"); return InternalTimer( gettimeofday() + 2, "XiaomiDevice_GetUpdate", $hash) if($msgtype eq "set_toggle"); InternalTimer( gettimeofday() + 2, "XiaomiDevice_GetUpdate", $hash) if($msgtype eq "power_on" || $msgtype eq "power_off"); return InternalTimer( gettimeofday() + 5, "XiaomiDevice_GetUpdate", $hash) if($msgtype eq "set_poweroff_time"); - + return InternalTimer( gettimeofday() + 5, "XiaomiDevice_GetUpdate", $hash) if($msgtype eq "app_start" || $msgtype eq "app_spot" || $msgtype eq "app_zoned_clean"); return InternalTimer( gettimeofday() + 10, "XiaomiDevice_GetUpdate", $hash) if($msgtype eq "app_stop" || $msgtype eq "app_pause" || $msgtype eq "app_goto_target"); return InternalTimer( gettimeofday() + 60, "XiaomiDevice_GetUpdate", $hash) if($msgtype eq "app_charge"); @@ -2419,6 +2588,10 @@ sub XiaomiDevice_ParseJSON($$) return InternalTimer( gettimeofday() + 2, "XiaomiDevice_GetSpeed", $hash) if($msgtype eq "set_limit_hum"); + return InternalTimer( gettimeofday() + 5, "XiaomiDevice_GetUpdate", $hash) if($msgtype eq "nowarn" || $msgtype eq "ack"); + return InternalTimer( gettimeofday() + 5, "XiaomiDevice_GetUpdate", $hash) if($msgtype eq "power_mode" || $msgtype eq "power_price" || $msgtype eq "wifi_led" || $msgtype eq "usb_power" || $msgtype eq "rt_power"); + + return readingsSingleUpdate( $hash, "power", "off", 1 ) if($msgtype eq "power_off"); return readingsSingleUpdate( $hash, "power", "on", 1 ) if($msgtype eq "power_on"); return readingsSingleUpdate( $hash, "mode", "natural", 1 ) if($msgtype eq "mode_natural"); @@ -2469,10 +2642,10 @@ sub XiaomiDevice_ParseJSON($$) return undef; } - + Log3 $name, 3, "$name: type ".$msgtype." not implemented\n".Dumper($json); - + return undef; } @@ -2507,7 +2680,7 @@ sub XiaomiDevice_connect($) $selectlist{$name} = $hash; XiaomiDevice_initSend($hash); - + InternalTimer( gettimeofday() + 4, "XiaomiDevice_GetDeviceDetails", $hash); InternalTimer( gettimeofday() + 7, "XiaomiDevice_GetSettings", $hash); InternalTimer( gettimeofday() + 10, "XiaomiDevice_GetUpdate", $hash); @@ -2602,7 +2775,7 @@ sub XiaomiDevice_Read($) { $hash->{helper}{last_read} = int(time()); - + $data = unpack('H*', $data); my $len = substr($data,4,4); $len = sprintf("%d", hex($len)); @@ -2614,8 +2787,8 @@ sub XiaomiDevice_Read($) { $seq = sprintf("%d", hex($seq)); $hash->{helper}{sequence} = int(time)-$seq; #Log3 $name, 4, "$name - recv seq ".$seq."/".int(time); - - + + my $dev = substr($data,16,4); my $id = substr($data,20,4); $hash->{helper}{dev} = $dev; @@ -2644,7 +2817,7 @@ sub XiaomiDevice_Read($) { elsif($len >= 64) { $data = substr($data,64); - } + } else{ $data = substr($data,-$len); } @@ -2660,19 +2833,19 @@ sub XiaomiDevice_Read($) { $hash->{helper}{ConnectionState} = "connected"; readingsSingleUpdate($hash, "state", "connected", 1) if(ReadingsVal($name, "state", "") eq "disconnected"); } - + RemoveInternalTimer($hash, "XiaomiDevice_connectFail"); $hash->{helper}{delay} = 0; - + my $key = Digest::MD5::md5(pack('H*', $hash->{helper}{token})); my $iv = Digest::MD5::md5($key.pack('H*', $hash->{helper}{token})); my $cbc; if($hash->{helper}{crypt} ne "Rijndael"){ - $cbc = Crypt::CBC->new(-key => $key, -cipher => 'Crypt::Cipher::AES',-iv => $iv, -literal_key => 1, -header => "none", -keysize => 16 ); + $cbc = Crypt::CBC->new(-key => $key, -cipher => 'Crypt::Cipher::AES',-iv => $iv, -literal_key => 1, -header => "none", -keysize => 16 ); } else { $Crypt::Rijndael_PP::DEFAULT_KEYSIZE = 128; - $cbc = Crypt::CBC->new(-key => $key, -cipher => 'Crypt::Rijndael_PP',-iv => $iv, -literal_key => 1, -header => "none", -keysize => 16 ); + $cbc = Crypt::CBC->new(-key => $key, -cipher => 'Crypt::Rijndael_PP',-iv => $iv, -literal_key => 1, -header => "none", -keysize => 16 ); } my $return = $cbc->decrypt_hex($data); @@ -2700,7 +2873,7 @@ sub XiaomiDevice_Write($$) XiaomiDevice_connect($hash); return undef; } - + my $sock = $hash->{CD}; if(!($sock->send($msg))) { @@ -2717,7 +2890,7 @@ sub XiaomiDevice_Write($$) } Log3 $hash, 5, "$name > ".unpack('H*',$msg); - + return undef; } ##################################### @@ -2766,7 +2939,7 @@ sub XiaomiDevice_DbLog_splitFn($) { $reading = $parts[0]; $reading =~ tr/://d; $value = $parts[1]; - + $unit = ""; $unit = "%" if($reading =~ /filter/);; $unit = "%" if($reading =~ /humidity/);; @@ -2775,12 +2948,12 @@ sub XiaomiDevice_DbLog_splitFn($) { $unit = "˚C" if($reading =~ /temperature/); $unit = "h" if($reading =~ /usage/); $unit = "m³" if($reading =~ /volume/); - $unit = "%" if($reading =~ /batteryLevel/);; + $unit = "%" if($reading =~ /batteryPercent/);; $unit = "%" if($reading =~ /fan_power/);; $unit = "h" if($reading =~ /clean_time/);; $unit = "m²" if($reading =~ /clean_area/);; $unit = "%" if($reading =~ /consumables_/);; - + Log3 "dbsplit", 5, "xiaomi dbsplit: ".$event." $reading: $value $unit" if(defined($value)); Log3 "dbsplit", 5, "xiaomi dbsplit: ".$event." $reading" if(!defined($value)); @@ -3040,7 +3213,7 @@ sub XiaomiDevice_DbLog_splitFn($) {
    • subType
      - VacuumCleaner / AirPurifier / SmartFan / Humidifier + VacuumCleaner / AirPurifier / SmartFan / Humidifier / RiceCooker / PowerPlug

    • disable