From 0be3e068943788b2b9ea391fb7880e3ee3fa5f3f Mon Sep 17 00:00:00 2001 From: T0RST3N Date: Mon, 22 May 2017 20:07:54 +0200 Subject: [PATCH] Version 1.0 add: support for sensors all up to now 86sw1,86sw2,ctrl_neutral1,ctrl_neutral2,rgbw_light add: ringvol set ringtone and volume in one step. add: support for multigatways add: commandref english and german add: support if gateway is not reachable fix: eval all decode json to prevent perl crash --- FHEM/71_XiaomiSmartHome.pm | 365 +++++++++++++++++++++++------- FHEM/71_XiaomiSmartHome_Device.pm | 261 +++++++++++++++++++-- controls_mish.txt | 4 +- 3 files changed, 532 insertions(+), 98 deletions(-) diff --git a/FHEM/71_XiaomiSmartHome.pm b/FHEM/71_XiaomiSmartHome.pm index 588861d..ea06662 100644 --- a/FHEM/71_XiaomiSmartHome.pm +++ b/FHEM/71_XiaomiSmartHome.pm @@ -48,9 +48,8 @@ use SetExtensions; sub XiaomiSmartHome_Notify($$); sub XiaomiSmartHome_updateSingleReading($$); -sub XiaomiSmartHome_updateAllReadings($); my $iv="\x17\x99\x6d\x09\x3d\x28\xdd\xb3\xba\x69\x5a\x2e\x6f\x58\x56\x2e"; -my $version = "0.23"; +my $version = "1.00"; my %XiaomiSmartHome_gets = ( "getDevices" => ["get_id_list", '^.+get_id_list_ack' ], @@ -60,10 +59,11 @@ my %sets = ( "password" => 1, "rgb:colorpicker,RGB" => 1, "pct:colorpicker,BRI,0,1,100" => 1, - "volume:slider,0,1,100" => 1, "off" => 0, "on" => 0, + "volume:slider,1,1,100" => 0, "ringtone:0,1,2,3,4,5,6,7,8,13,21,22,23,24,25,26,27,28,29,10000,10001" => 1, + "ringvol" => 2, ); @@ -90,7 +90,11 @@ sub XiaomiSmartHome_Initialize($) { "4:XiaomiSmartHome_Device" => "^.+switch", "5:XiaomiSmartHome_Device" => "^.+cube", "6:XiaomiSmartHome_Device" => "^.+plug", - "7:XiaomiSmartHome_Device" => "^.+86sw2"}; + "7:XiaomiSmartHome_Device" => "^.+86sw1", + "8:XiaomiSmartHome_Device" => "^.+86sw2", + "9:XiaomiSmartHome_Device" => "^.+ctrl_neutral1", + "10:XiaomiSmartHome_Device" => "^.+ctrl_neutral2", + "11:XiaomiSmartHome_Device" => "^.+rgbw_light"}; FHEM_colorpickerInit(); } ##################################### @@ -110,21 +114,31 @@ sub XiaomiSmartHome_Read($) { } my $json = $hash->{helper}{JSON}->incr_parse($buf); - my $decoded = decode_json($buf); + my $decoded = eval{decode_json($buf)}; + if ($@) { + Log3 $name, 1, "$name: Read> Error while request: $@"; + return; + } if ( ! $decoded ) { - Log3 $name, 4, "$name: Read> Error no JSON Data " . $buf; + Log3 $name, 5, "$name: Read> Error no JSON Data " . $buf; return; } if ($json) { Log3 $name, 5, "$name: Read> " . $buf; - readingsBeginUpdate( $hash ); if ($decoded->{'cmd'} eq 'report' && $decoded->{'model'} eq 'gateway' || $decoded->{'cmd'} eq 'heartbeat' && $decoded->{'model'} eq 'gateway' || $decoded->{'cmd'} eq 'write_ack'){ + if ($decoded->{'sid'} ne $hash->{SID} ){ + Log3 $name, 5, "$name: Read> $decoded->{'sid'} not matching with my SID $hash->{SID} skipping"; + return; + } readingsBeginUpdate( $hash ); if ($decoded->{'model'} && $decoded->{'model'} eq 'gateway' ){ if ($decoded->{'cmd'} eq 'report'){ - my $data = decode_json($decoded->{data}); - Log3 $name, 5, "$name: Read>" . " Data: " . $data ; + my $data = eval{decode_json($decoded->{data})}; + if ($@) { + Log3 $name, 1, "$name: Read> Error while request: $@"; + return; + } if (defined $data->{rgb}){ Log3 $name, 3, "$name: Read>" . " SID: " . $decoded->{'sid'} . " Type: Gateway" . " RGB: " . $data->{rgb} ; readingsBulkUpdate($hash, "RGB", $data->{rgb} , 1 ); @@ -135,10 +149,16 @@ sub XiaomiSmartHome_Read($) { } } elsif ($decoded->{'cmd'} eq 'heartbeat'){ - my $data = decode_json($decoded->{data}); + my $data = eval{decode_json($decoded->{data})}; + if ($@) { + Log3 $name, 1, "$name: Read> Error while request: $@"; + return; + } if ($data->{ip} eq $hash->{GATEWAY_IP}){ readingsBulkUpdate($hash, 'heartbeat', $decoded->{'sid'}, 1 ); readingsBulkUpdate($hash, 'token', $decoded->{'token'}, 1 ); + Log3 $name, 4, "$name: Read> Heartbeat from $data->{ip} received with $decoded->{'sid'}"; + #$hash->{SID} = $decoded->{'sid'}; } else { Log3 $name, 4, "$name: Read> IP-Heartbeat Data didnt match! $data->{ip} " . $hash->{GATEWAY_IP} ; @@ -146,8 +166,16 @@ sub XiaomiSmartHome_Read($) { } } if ($decoded->{'cmd'} eq 'write_ack'){ + if ($decoded->{'sid'} ne $hash->{SID} ){ + Log3 $name, 5, "$name: Read> $decoded->{'sid'} not matching with my SID $hash->{SID} skipping"; + return; + } Log3 $name, 4, "$name: Read> Write answer " . $hash->{GATEWAY} ; - my $data = decode_json($decoded->{data}); + my $data = eval{decode_json($decoded->{data})}; + if ($@) { + Log3 $name, 1, "$name: Read> Error while request: $@"; + return; + } if ($data->{error}){ readingsBulkUpdate($hash, 'heartbeat', $data->{error}, 1 ); } @@ -160,7 +188,15 @@ sub XiaomiSmartHome_Read($) { return; } if ($decoded->{'cmd'} eq 'get_id_list_ack'){ - my @sensors = @{decode_json($decoded->{data})}; + if ($decoded->{'sid'} ne $hash->{SID} ){ + Log3 $name, 5, "$name: Read> $decoded->{'sid'} not matching with my SID $hash->{SID} skipping"; + return; + } + my @sensors = eval{ @{decode_json($decoded->{data})}}; + if ($@) { + Log3 $name, 1, "$name: Read> Error while request: $@"; + return; + } foreach my $sensor (@sensors) { Log3 $name, 4, "$name: Read> PushRead:" . $sensor; @@ -169,7 +205,7 @@ sub XiaomiSmartHome_Read($) { return; } - Log3 $name, 4, "$name: Read> dispatch " . $buf; + Log3 $name, 5, "$name: Read> Dispatch " . $buf; Dispatch($hash, $buf, undef); @@ -194,43 +230,53 @@ sub XiaomiSmartHome_getLocalIP(){ sub XiaomiSmartHome_getGatewaySID($){ my ($hash, $def) = @_; my $name = $hash->{NAME}; + my $ip = $hash->{GATEWAY_IP}; my $json; my $decoded; my $timeout = $hash->{TIMEOUT} ? $hash->{TIMEOUT} : 3; my $sidsock = IO::Socket::Multicast->new( Proto => 'udp',LocalAddr => $hash->{FHEMIP}, LocalPort =>'4321', ReuseAddr => 1, Timeout => $timeout) or die "Creating socket: $!\n"; - $sidsock->setsockopt(SOL_SOCKET, SO_RCVTIMEO, pack('l!l!', 30, 0)) or die "setsockopt: $!"; + $sidsock->setsockopt(SOL_SOCKET, SO_RCVTIMEO, pack('l!l!', 10, 0)) or die "setsockopt: $!"; if ($sidsock){ my $msg = '{"cmd":"whois"}'; - $sidsock->mcast_send($msg,'224.0.0.50:4321') or die "send: $!"; + $sidsock->mcast_send($msg,$ip . ':4321') or die "send: $!"; eval { $sidsock->recv($msg, 1024) or die "recv: $!"; $json = $hash->{helper}{JSON}->incr_parse($msg); - $decoded = decode_json($msg); - $sidsock->close(); + Log3 $name, 5, "$name: getGatewaySID> Answer $msg"; + $decoded = eval{decode_json($msg)}; + if ($@) { + Log3 $name, 1, "$name: getGatewaySID> Error while request: $@"; + return; + } + if ($json) { + if ($decoded->{'ip'} eq $ip){ + Log3 $name, 4, "$name: getGatewaySID> Find SID for Gateway: $decoded->{sid}"; + $sidsock->close(); + $hash->{SID} = $decoded->{sid}; + return $decoded->{sid}; + } + else { + Log3 $name, 4, "$name: getGatewaySID> whois Data didnt match! $decoded->{sid} $decoded->{'ip'} ". $ip ; + } + } }; if ($@) { - Log3 $name, 1, "$name: getGatewaySID> Error $@\n"; + Log3 $name, 1, "$name: getGatewaySID> Error no response from whois!! STOP!!"; $hash->{STATE} = "Disconnected"; XiaomiSmartHome_disconnect($hash); return undef; } } - if ($json) - { - if ($decoded->{'sid'} ne '') - { - Log3 $name, 4, "$name: Find SID for Gateway: $decoded->{'sid'}"; - return $decoded->{'sid'}; - } - } else { Log3 $name, 5, "$name: Did not find a SID for Gateway disconnecting"; $hash->{STATE} = "Disconnected"; XiaomiSmartHome_disconnect($hash); + return undef; } } ##################################### + sub XiaomiSmartHome_Define($$) { my ($hash, $def) = @_; my @param = split('[ \t]+', $def); @@ -243,22 +289,37 @@ sub XiaomiSmartHome_Define($$) { if ( ! $p->ping($param[2])){ $hash->{STATE} = "Disconnected"; XiaomiSmartHome_disconnect($hash); - Log3 $name, 5, "$name: Ping ERROR Gateway disconnecting"; + Log3 $name, 5, "$name: Define> Ping ERROR Gateway disconnecting"; $p->close(); } + my $GATEWAY_IP = $param[2]; my $definition = $param[2]; $hash->{DEF} = $definition; $hash->{NOTIFYDEV} = "global"; $hash->{NAME} = $param[0]; $hash->{VERSION} = $version; $hash->{GATEWAY} = $param[2]; - $hash->{GATEWAY_IP} = $param[2]; $hash->{helper}{JSON} = JSON->new->utf8(); $hash->{FHEMIP} = XiaomiSmartHome_getLocalIP(); $hash->{STATE} = "initialized"; - $hash->{SID} = XiaomiSmartHome_getGatewaySID($hash); - $hash->{helper}{host} = $definition; - Log3 $name, 5, "$name: $definition"; + $hash->{helper}{host} = $definition; + if( $hash->{GATEWAY} !~ m/^\d+\.\d+\.\d+\.\d+$/ ){ + eval { + $GATEWAY_IP = inet_ntoa(inet_aton($hash->{GATEWAY})) ; + $hash->{GATEWAY_IP} = $GATEWAY_IP; + Log3 $name, 1, "$name: Define> Set GATEWAYs IP: " . $GATEWAY_IP; + }; + if ($@) { + Log3 $name, 1, "$name: Define> Error $@\n"; + $hash->{STATE} = "Disconnected"; + XiaomiSmartHome_disconnect($hash); + return undef; + } + } + $hash->{GATEWAY_IP} = $GATEWAY_IP; + #$hash->{SID} = XiaomiSmartHome_getGatewaySID($hash); + + Log3 $name, 5, "$name: Define> $definition"; # Define devStateIcon $attr{$hash->{NAME}}{devStateIcon} = '{Color_devStateIcon(ReadingsVal($name,"rgb","000000"))}' if(!defined($attr{$hash->{NAME}}{devStateIcon})); @@ -288,6 +349,7 @@ sub XiaomiSmartHome_Write($@) return undef; } else{ + #Check DNS if IP has changed my $p = Net::Ping->new(); if ( ! $p->ping($hash->{GATEWAY})){ Log3 $name, 1, "$name: Write> Ping to $hash->{helper}{host} failed"; @@ -304,39 +366,53 @@ sub XiaomiSmartHome_Write($@) { $msg = '{"cmd":"' .$cmd . '","sid":"' . $val . '"}'; } - if ($cmd eq 'get_id_list') + elsif ($cmd eq 'get_id_list') { - Log3 $name, 5, "$name: Write> Get all Sensors"; + Log3 $name, 4, "$name: Write> Get all Sensors"; $msg = '{"cmd" : "get_id_list"}'; } - if ( $hash->{READINGS}{password}{VAL} !~ /^[a-zA-Z0-9]{16}$/ ) - { - Log3 $name, 1, "$name: Write> Password not SET!"; - readingsSingleUpdate($hash, "password", "giveaPassword!", 1); - return "for $cmd, wrong password, it must be hex and 16 characters"; - } else { - if ($cmd eq 'rgb') + if ( $hash->{READINGS}{password}{VAL} !~ /^[a-zA-Z0-9]{16}$/ ) { - $msg = '{"cmd":"write","model":"gateway","sid":"' . $hash->{SID} . '","short_id":0,"key":"8","data":"{\"rgb\":' . $val . ',\"key\":\"'. XiaomiSmartHome_EncryptKey($hash) .'\"}" }'; + Log3 $name, 1, "$name: Write> Password not SET!"; + readingsSingleUpdate($hash, "password", "giveaPassword!", 1); + return "for $cmd, wrong password, it must be hex and 16 characters"; } - if ($cmd eq 'pct') - { - $msg = '{"cmd":"write","model":"gateway","sid":"' . $hash->{SID} . '","short_id":0,"key":"8","data":"{\"rgb\":' . $val . ',\"key\":\"'. XiaomiSmartHome_EncryptKey($hash) .'\"}" }'; - } - if ($cmd eq 'power') - { - $msg = '{"cmd":"write","model":"plug","sid":"' . $iohash->{SID} . '","data":"{\"status\":\"' . $val . '\",\"key\":\"'. XiaomiSmartHome_EncryptKey($hash) .'\"}" }'; - } - if ($cmd eq 'ringtone') - { - my $vol = $hash->{READINGS}{volume}{VAL}; - $msg = '{"cmd":"write","model":"gateway","sid":"' . $hash->{SID} . '","short_id":0,"key":"8","data":"{\"mid\":' . $val . ',\"vol\":' . $vol . ',\"key\":\"'. XiaomiSmartHome_EncryptKey($hash) .'\"}" }'; - } - if ($cmd eq 'volume') - { - my $rt = $hash->{READINGS}{ringtone}{VAL}; - $msg = '{"cmd":"write","model":"gateway","sid":"' . $hash->{SID} . '","short_id":0,"key":"8","data":"{\"mid\":' . $rt . ',\"vol\":' .$val . ',\"key\":\"'. XiaomiSmartHome_EncryptKey($hash) .'\"}" }'; + else { + if ($cmd eq 'rgb') + { + $msg = '{"cmd":"write","model":"gateway","sid":"' . $hash->{SID} . '","short_id":0,"key":"8","data":"{\"rgb\":' . $val . ',\"key\":\"'. XiaomiSmartHome_EncryptKey($hash) .'\"}" }'; + } + if ($cmd eq 'pct') + { + $msg = '{"cmd":"write","model":"gateway","sid":"' . $hash->{SID} . '","short_id":0,"key":"8","data":"{\"rgb\":' . $val . ',\"key\":\"'. XiaomiSmartHome_EncryptKey($hash) .'\"}" }'; + } + if ($cmd eq 'power') + { + $msg = '{"cmd":"write","model":"plug","sid":"' . $iohash->{SID} . '","data":"{\"status\":\"' . $val . '\",\"key\":\"'. XiaomiSmartHome_EncryptKey($hash) .'\"}" }'; + } + if ($cmd eq 'ctrl') + { + $msg = '{"cmd":"write","model":"ctrl_neutral1","sid":"' . $iohash->{SID} . '","data":"{\"channel_0\":\"' . $val . '\",\"key\":\"'. XiaomiSmartHome_EncryptKey($hash) .'\"}" }'; + } + if ($cmd eq 'channel_0') + { + $msg = '{"cmd":"write","model":"ctrl_neutral2","sid":"' . $iohash->{SID} . '","data":"{\"channel_0\":\"' . $val . '\",\"key\":\"'. XiaomiSmartHome_EncryptKey($hash) .'\"}" }'; + } + if ($cmd eq 'channel_1') + { + $msg = '{"cmd":"write","model":"ctrl_neutral2","sid":"' . $iohash->{SID} . '","data":"{\"channel_1\":\"' . $val . '\",\"key\":\"'. XiaomiSmartHome_EncryptKey($hash) .'\"}" }'; + } + if ($cmd eq 'ringtone') + { + my $vol = $hash->{READINGS}{volume}{VAL}; + $msg = '{"cmd":"write","model":"gateway","sid":"' . $hash->{SID} . '","short_id":0,"key":"8","data":"{\"mid\":' . $val . ',\"vol\":' . $vol . ',\"key\":\"'. XiaomiSmartHome_EncryptKey($hash) .'\"}" }'; + } + if ($cmd eq 'volume') + { + my $rt = $hash->{READINGS}{ringtone}{VAL}; + $msg = '{"cmd":"write","model":"gateway","sid":"' . $hash->{SID} . '","short_id":0,"key":"8","data":"{\"mid\":' . $rt . ',\"vol\":' .$val . ',\"key\":\"'. XiaomiSmartHome_EncryptKey($hash) .'\"}" }'; + } } } return Log3 $name, 4, "$name: Write> - socket not connected" unless($hash->{CD}); @@ -391,20 +467,20 @@ sub XiaomiSmartHome_Get($@) sub XiaomiSmartHome_Notify($$) { - my ($own_hash, $dev_hash) = @_; - my $ownName = $own_hash->{NAME}; # own name / hash + my ($hash, $dev_hash) = @_; + my $ownName = $hash->{NAME}; # own name / hash Log3 $ownName, 5, "$ownName: Notify> NotifyStart"; return "" if(IsDisabled($ownName)); # Return without any further action if the module is disabled - $attr{$own_hash->{NAME}}{webCmd} = "pct:rgb:rgb ff0000:rgb 00ff00:rgb 0000ff:on:off" if ( ! $attr{$own_hash->{NAME}}{webCmd} || $attr{$own_hash->{NAME}}{webCmd} eq "rgb:rgb ff0000:rgb 00ff00:rgb 0000ff:on:off" ); - readingsSingleUpdate($own_hash, "pct", 100, 1) if ( ! $own_hash->{READINGS}{pct}{VAL}); - readingsSingleUpdate($own_hash, "ringtone", 21, 1) if ( ! $own_hash->{READINGS}{ringtone}{VAL}); - readingsSingleUpdate($own_hash, "volume", 10, 1) if ( ! $own_hash->{READINGS}{volume}{VAL}); + $attr{$hash->{NAME}}{webCmd} = "pct:rgb:rgb ff0000:rgb 00ff00:rgb 0000ff:on:off" if ( ! $attr{$hash->{NAME}}{webCmd} || $attr{$hash->{NAME}}{webCmd} eq "rgb:rgb ff0000:rgb 00ff00:rgb 0000ff:on:off" ); + readingsSingleUpdate($hash, "pct", 100, 1) if ( ! $hash->{READINGS}{pct}{VAL}); + readingsSingleUpdate($hash, "ringtone", 21, 1) if ( ! $hash->{READINGS}{ringtone}{VAL}); + readingsSingleUpdate($hash, "volume", 10, 1) if ( ! $hash->{READINGS}{volume}{VAL}); my $devName = $dev_hash->{NAME}; # Device that created the events my $events = deviceEvents($dev_hash, 1); if($devName eq "global" && grep(m/^INITIALIZED|REREADCFG$/, @{$events})) { Log3 $ownName, 5, "$ownName: Notify> Starting Connect after global"; - XiaomiSmartHome_connect($own_hash); + XiaomiSmartHome_connect($hash) if ($hash->{SID}); CommandDeleteReading( undef, "$ownName HEARTBEAT" ) if(defined(ReadingsVal($ownName,"HEARTBEAT",undef))); } } @@ -494,11 +570,14 @@ sub XiaomiSmartHome_Set($@) my $rgb = sprintf("%d", hex($MIpct . $hash->{helper}{prevrgbvalue})); XiaomiSmartHome_Write($hash,'pct',$rgb); } - elsif($cmd eq "ringtone") + elsif($cmd eq "ringtone" || $cmd eq "ringvol" ) { my $ownName = $hash->{NAME}; Log3 $ownName, 4, "$ownName: Set> $cmd, $args[0]"; readingsSingleUpdate( $hash, 'ringtone', $args[0], 1 ); + if ($args[1]){ + readingsSingleUpdate( $hash, 'volume', $args[1], 1 ); + } XiaomiSmartHome_Write($hash,'ringtone',$args[0]); } elsif($cmd eq "volume") @@ -563,7 +642,7 @@ sub XiaomiSmartHome_connect($) return undef; } } - + XiaomiSmartHome_getGatewaySID($hash); my $timeout = $hash->{TIMEOUT} ? $hash->{TIMEOUT} : 3; my $sock = IO::Socket::Multicast->new( Proto => 'udp', LocalPort =>'9898', ReuseAddr => 1, Timeout => $timeout) or die "Creating socket: $!\n"; $sock->setsockopt(SOL_SOCKET, SO_RCVTIMEO, pack('l!l!', 30, 0)) or die "setsockopt: $!"; @@ -574,7 +653,7 @@ sub XiaomiSmartHome_connect($) $sock->mcast_ttl(32); $sock->mcast_loopback(1); $hash->{helper}{ConnectionState} = "Connected"; - $hash->{SID} = XiaomiSmartHome_getGatewaySID($hash); + #$hash->{SID} = XiaomiSmartHome_getGatewaySID($hash); if ($hash->{helper}{ConnectionState} ne ReadingsVal($name, "state", "" )) { readingsSingleUpdate($hash, "state", $hash->{helper}{ConnectionState}, 1); @@ -637,7 +716,7 @@ sub XiaomiSmartHome_updateAllReadings($) { my $hash = shift; my $name = $hash->{NAME}; - Log3 $name, 5, "$name> updateAllReadings> Starting UpdateALLReadings"; + Log3 $name, 5, "$name: updateAllReadings> Starting UpdateALLReadings"; my $GATEWAY; my $p = Net::Ping->new(); if ( ! $p->ping($hash->{GATEWAY})){ @@ -673,14 +752,15 @@ sub XiaomiSmartHome_updateAllReadings($) XiaomiSmartHome_Write($hash, 'get_id_list'); } -##################################### + 1; =pod =item device -=item summary Module for XiaomiSmartHome Gateway to use with FHEM -=item summary_DE Modul um ein XiaomiSmartHome Gateway in FHEM zu nutzen +=item summary Module to control XiaomiSmartHome Gateway +=item summary_DE Modul zum steuern des XiaomiSmartHome Gateway + =begin html @@ -689,16 +769,77 @@ sub XiaomiSmartHome_updateAllReadings($) + =end html =begin html_DE @@ -706,23 +847,81 @@ sub XiaomiSmartHome_updateAllReadings($)

XiaomiSmartHome

-=end html + +=end html_DE =cut diff --git a/FHEM/71_XiaomiSmartHome_Device.pm b/FHEM/71_XiaomiSmartHome_Device.pm index 5974650..ee0f848 100644 --- a/FHEM/71_XiaomiSmartHome_Device.pm +++ b/FHEM/71_XiaomiSmartHome_Device.pm @@ -25,7 +25,7 @@ package main; use strict; use warnings; -my $version = "0.21"; +my $version = "1.00"; sub XiaomiSmartHome_Device_updateSReading($); @@ -35,7 +35,7 @@ sub XiaomiSmartHome_Device_Initialize($) { my ($hash) = @_; - $hash->{Match} = "^.+magnet|motion|sensor_ht|switch|plug|cube|86sw2"; + $hash->{Match} = "^.+magnet|motion|sensor_ht|switch|plug|cube|86sw1|86sw2|ctrl_neutral1|ctrl_neutral2|rgbw_light"; $hash->{DefFn} = "XiaomiSmartHome_Device_Define"; $hash->{SetFn} = "XiaomiSmartHome_Device_Set"; $hash->{UndefFn} = "XiaomiSmartHome_Device_Undef"; @@ -66,6 +66,8 @@ sub XiaomiSmartHome_Device_Set($@) $setlist .= "motionOffTimer:1,5,10 " if ($hash->{MODEL} eq 'motion'); #$setlist = "open:noArg close:noArg " if ($hash->{MODEL} eq 'magnet'); $setlist .= "power:on,off " if ($hash->{MODEL} eq 'plug'); + $setlist .= "ctrl:on,off " if ($hash->{MODEL} eq 'ctrl_neutral1'); + $setlist .= "channel_0:on,off channel_1:on,off " if ($hash->{MODEL} eq 'ctrl_neutral2'); if($cmd eq "power") { @@ -80,6 +82,45 @@ sub XiaomiSmartHome_Device_Set($@) return; } } + if($cmd eq "ctrl") + { + if($args[0] eq "on") + { + IOWrite($hash,"ctrl","on",$hash); + return; + } + elsif($args[0] eq "off") + { + IOWrite($hash,"ctrl","off",$hash); + return; + } + } + if($cmd eq "channel_0") + { + if($args[0] eq "on") + { + IOWrite($hash,"channel_0","on",$hash); + return; + } + elsif($args[0] eq "off") + { + IOWrite($hash,"channel_0","off",$hash); + return; + } + } + if($cmd eq "channel_1") + { + if($args[0] eq "on") + { + IOWrite($hash,"channel_1","on",$hash); + return; + } + elsif($args[0] eq "off") + { + IOWrite($hash,"channel_1","off",$hash); + return; + } + } # if($cmd eq "open") # { # readingsSingleUpdate($hash, "state", "open", 1 ); @@ -109,11 +150,19 @@ sub XiaomiSmartHome_Device_on_timeout($){ ##################################### sub XiaomiSmartHome_Device_Read($$$){ my ($hash, $msg, $name) = @_; - my $decoded = decode_json($msg); + my $decoded = eval{decode_json($msg)}; + if ($@) { + Log3 $name, 1, "$name: DEV_Read> Error while request: $@"; + return; + } my $sid = $decoded->{'sid'}; my $model = $decoded->{'model'}; Log3 $name, 5, "$name: DEV_Read> SID: " . $hash->{SID} . " " . $hash->{TYPE}; - my $data = decode_json($decoded->{data}); + my $data = eval{decode_json($decoded->{data})}; + if ($@) { + Log3 $name, 1, "$name: DEV_Read> Error while request data: $@"; + return; + } readingsBeginUpdate( $hash ); if (defined $data->{status}){ Log3 $name, 3, "$name: DEV_Read>" . " Name: " . $hash->{NAME} . " SID: " . $sid . " Type: " . $hash->{MODEL} . " Status: " . $data->{status}; @@ -150,7 +199,7 @@ sub XiaomiSmartHome_Device_Read($$$){ Log3 $name, 3, "$name: DEV_Read>" . " Name: " . $hash->{NAME} . " SID: " . $sid . " Type: " . $hash->{MODEL} . " Humidity: " . $hum; readingsBulkUpdate($hash, "humidity", "$hum", 1 ); } - #86sw2 start + #86sw1 + 86sw2 + ctrl_neutral1 + ctrl_neutral2 start if (defined $data->{channel_0}){ Log3 $name, 3, "$name: DEV_Read>" . " Name: " . $hash->{NAME} . " SID: " . $sid . " Type: " . $hash->{MODEL} . " Channel_0: " . $data->{channel_0}; readingsBulkUpdate($hash, "channel_0", "$data->{channel_0}", 1 ); @@ -159,7 +208,7 @@ sub XiaomiSmartHome_Device_Read($$$){ Log3 $name, 3, "$name: DEV_Read>" . " SID: " . $sid . " Type: " . $hash->{MODEL} . " Channel_1: " . $data->{channel_1}; readingsBulkUpdate($hash, "channel_1", "$data->{channel_1}", 1 ); } - #86sw2 end + #86sw1 + 86sw2 + ctrl_neutral1 + ctrl_neutral2 end #plug start if (defined $data->{load_voltage}){ Log3 $name, 3, "$name: DEV_Read>" . " Name: " . $hash->{NAME} . " SID: " . $sid . " Type: " . $hash->{MODEL} . " LOAD_Voltage: " . $data->{load_voltage}; @@ -178,6 +227,32 @@ sub XiaomiSmartHome_Device_Read($$$){ readingsBulkUpdate($hash, "inuse", "$data->{inuse}", 1 ); } #plug end + #rgbw_light start + if (defined $data->{level}){ + Log3 $name, 3, "$name: DEV_Read>" . " Name: " . $hash->{NAME} . " SID: " . $sid . " Type: " . $hash->{MODEL} . " Level: " . $data->{level}; + readingsBulkUpdate($hash, "level", "$data->{level}", 1 ); + } + if (defined $data->{hue}){ + Log3 $name, 3, "$name: DEV_Read>" . " Name: " . $hash->{NAME} . " SID: " . $sid . " Type: " . $hash->{MODEL} . " Hue: " . $data->{hue}; + readingsBulkUpdate($hash, "hue", "$data->{hue}", 1 ); + } + if (defined $data->{saturation}){ + Log3 $name, 3, "$name:" . " Name: " . $hash->{NAME} . " SID: " . $sid . " Type: " . $hash->{MODEL} . " Saturation: " . $data->{saturation}; + readingsBulkUpdate($hash, "saturation", "$data->{saturation}", 1 ); + } + if (defined $data->{color_temperature}){ + Log3 $name, 3, "$name: DEV_Read>" . " Name: " . $hash->{NAME} . " SID: " . $sid . " Type: " . $hash->{MODEL} . " Color_temperature: " . $data->{color_temperature}; + readingsBulkUpdate($hash, "color_temperature", "$data->{color_temperature}", 1 ); + } + if (defined $data->{x}){ + Log3 $name, 3, "$name: DEV_Read>" . " Name: " . $hash->{NAME} . " SID: " . $sid . " Type: " . $hash->{MODEL} . " X: " . $data->{x}; + readingsBulkUpdate($hash, "x", "$data->{x}", 1 ); + } + if (defined $data->{y}){ + Log3 $name, 3, "$name: DEV_Read>" . " Name: " . $hash->{NAME} . " SID: " . $sid . " Type: " . $hash->{MODEL} . " Y: " . $data->{y}; + readingsBulkUpdate($hash, "y", "$data->{y}", 1 ); + } + #rgbw_light end #cube start if (defined $data->{rotate}){ Log3 $name, 3, "$name: DEV_Read>" . " Name: " . $hash->{NAME} . " SID: " . $sid . " Type: " . $hash->{MODEL} . " Rotate: " . $data->{rotate}; @@ -196,12 +271,15 @@ sub XiaomiSmartHome_Device_Read($$$){ sub XiaomiSmartHome_Device_Parse($$) { my ($io_hash, $msg) = @_; - my $decoded = decode_json($msg); - + + my $name = $io_hash->{NAME}; + my $decoded = eval{decode_json($msg)}; + if ($@) { + Log3 $name, 1, "$name: DEV_Parse> Error while request: $@"; + return; + } my $sid = $decoded->{'sid'}; my $model = $decoded->{'model'}; - my $name = $io_hash->{NAME}; - if (my $io_hash = $modules{XiaomiSmartHome_Device}{defptr}{$sid}) { Log3 $name, 4, "$name: DEV_Parse> IS DEFINED " . $model . " : " .$sid; @@ -306,14 +384,171 @@ sub XiaomiSmartHome_Device_Undef($) } 1; -##################################### + =pod +=item device +=item summary Module to control XiaomiSmartHome Gateway + + =begin html -

XiaomiSmartHome_Device

- +

XiaomiSmartHome

+ =end html +=begin html_DE + + +

XiaomiSmartHome

+ + +=end html_DE + =cut \ No newline at end of file diff --git a/controls_mish.txt b/controls_mish.txt index bc13ad3..a6dfeba 100644 --- a/controls_mish.txt +++ b/controls_mish.txt @@ -1,2 +1,2 @@ -UPD 2017-05-04_19:31:19 23384 FHEM/71_XiaomiSmartHome.pm -UPD 2017-05-01_13:14:43 11699 FHEM/71_XiaomiSmartHome_Device.pm \ No newline at end of file +UPD 2017-05-18_11:42:17 32000 FHEM/71_XiaomiSmartHome.pm +UPD 2017-05-18_11:38:12 21164 FHEM/71_XiaomiSmartHome_Device.pm \ No newline at end of file