diff --git a/71_XiaomiSmartHome.pm b/71_XiaomiSmartHome.pm new file mode 100644 index 0000000..21204c2 --- /dev/null +++ b/71_XiaomiSmartHome.pm @@ -0,0 +1,503 @@ +############################################################################### +# +# 03.2017 torte +# All rights reserved +# +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# any later version. +# +# The GNU General Public License can be found at +# http://www.gnu.org/copyleft/gpl.html. +# A copy is found in the textfile GPL.txt and important notices to the license +# from the author is found in LICENSE.txt distributed with these scripts. +# +# This script is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# +############################################################################### + +package main; + +use strict; +use warnings; +use JSON qw( decode_json ); +use Data::Dumper; +use IO::Socket; +use IO::Socket::Multicast; +use Crypt::CBC; +use Color; +use SetExtensions; + +sub XiaomiSmartHome_Notify($$); +sub XiaomiSmartHome_updateSingleReading($$); +my $iv="\x17\x99\x6d\x09\x3d\x28\xdd\xb3\xba\x69\x5a\x2e\x6f\x58\x56\x2e"; +my $version = "0.05"; +my %XiaomiSmartHome_gets = ( + "getDevices" => ["get_id_list", '^.+get_id_list_ack' ], + +); + +my %sets = ( + "password" => 1, + "rgb:colorpicker,RGB" => 1, + #"pct:colorpicker,BRI,0,300,1300" =>1, + "off" => 0, + "on" => 0, +); + + +##################################### + +sub XiaomiSmartHome_Initialize($) { + my ($hash) = @_; + + $hash->{Clients} = "XiaomiSmartHome_Device"; + $hash->{DefFn} = 'XiaomiSmartHome_Define'; + $hash->{UndefFn} = 'XiaomiSmartHome_Undef'; + $hash->{NotifyFn} = 'XiaomiSmartHome_Notify'; + $hash->{SetFn} = 'XiaomiSmartHome_Set'; + $hash->{GetFn} = 'XiaomiSmartHome_Get'; + $hash->{AttrFn} = 'XiaomiSmartHome_Attr'; + $hash->{ReadFn} = 'XiaomiSmartHome_Read'; + $hash->{WriteFn} = "XiaomiSmartHome_Write"; + $hash->{AttrList} = "disable:1,0 " . + "Room " . + $readingFnAttributes; + + $hash->{MatchList} = { "1:XiaomiSmartHome_Device" => "^.+magnet", + "2:XiaomiSmartHome_Device" => "^.+motion", + "3:XiaomiSmartHome_Device" => "^.+sensor_ht", + "4:XiaomiSmartHome_Device" => "^.+switch"}; + FHEM_colorpickerInit(); +} +##################################### + +sub XiaomiSmartHome_Read($) { + my ($hash) = @_; + my $name = $hash->{NAME}; + my $buf = ""; + my $ret = sysread($hash->{CD}, $buf, 1024); + if (!defined($ret) || $ret <= 0) + { + XiaomiSmartHome_disconnect($hash); + + InternalTimer(gettimeofday() + 2, "XiaomiSmartHome_connect", $hash, 0); + return; + } + + my $json = $hash->{helper}{JSON}->incr_parse($buf); + my $decoded = decode_json($buf); + if ($json) + { + Log3 $name, 5, "$name> Read:" . $buf; + if ($decoded->{'model'} eq 'gateway'){ + if ($decoded->{'cmd'} eq 'report'){ + my $data = decode_json($decoded->{data}); + if (defined $data->{rgb}){ + Log3 $name, 4, "$name>" . " SID: " . $decoded->{'sid'} . " Type: Gateway" . " RGB: " . $data->{rgb} ; + readingsSingleUpdate($hash, "RGB", $data->{rgb} , 1 ); + } + } + elsif ($decoded->{'cmd'} eq 'heartbeat'){ + readingsSingleUpdate($hash, 'HEARTBEAT', $decoded->{'sid'}, 1 ); + readingsSingleUpdate($hash, 'token', $decoded->{'token'}, 1 ); + } + } + else { + Log3 $name, 4, "$name> Dispatch! " . $buf; + Dispatch($hash, $buf, undef); + } + } +} +##################################### + + +sub XiaomiSmartHome_getLocalIP(){ + my $socket = IO::Socket::INET->new( Proto => 'udp', + PeerAddr => '8.8.8.8:53', # google dns + ); + return '' if( !$socket ); + my $ip = $socket->sockhost; + close( $socket ); + return $ip if( $ip ); + + return ''; +} +##################################### + +sub XiaomiSmartHome_Define($$) { + my ($hash, $def) = @_; + my @param = split('[ \t]+', $def); + + if(int(@param) < 3) { + return "too few parameters: define XiaomiSmartHome "; + } + my $definition = $param[2]; + + $hash->{DEF} = $definition; + $hash->{NOTIFYDEV} = "global"; + $hash->{NAME} = $param[0]; + $hash->{VERSION} = $version; + $hash->{GATEYWAY} = $param[2]; + $hash->{FHEMIP} = XiaomiSmartHome_getLocalIP(); + $hash->{STATE} = "initialized"; + $hash->{helper}{host} = $definition; + $hash->{helper}{JSON} = JSON->new->utf8(); + Log3 $hash->{NAME}, 5, "$hash->{NAME}> $definition"; + $attr{$hash->{NAME}}{webCmd} = "rgb:rgb ff0000:rgb 00ff00:rgb 0000ff:on:off"; + # Define devStateIcon + $attr{$hash->{NAME}}{devStateIcon} = '{Color_devStateIcon(ReadingsVal($name,"rgb","000000"))}' if(!defined($attr{$hash->{NAME}}{devStateIcon})); + $attr{$hash->{NAME}}{room} = "MiSmartHome" if( !defined( $attr{$hash->{NAME}}{room} ) ); + XiaomiSmartHome_connect($hash) if( $init_done); + + return undef; +} +##################################### + +sub XiaomiSmartHome_Undef($$) { + my ($hash, $arg) = @_; + + XiaomiSmartHome_disconnect($hash); + # nothing to do + return undef; +} +##################################### + +sub XiaomiSmartHome_Write($$$) +{ + my ($hash,$cmd,$val) = @_; + my $name = $hash->{NAME}; + my $msg; + if ($cmd eq 'read') + { + $msg = '{"cmd":"' .$cmd . '","sid":"' . $val . '"}'; + } + elsif ($cmd eq 'rgb') + { + # TODO SID des Gateway nicht im und aus dem Reading!! + $msg = '{"cmd":"write","model":"gateway","sid":"' . $hash->{READINGS}{HEARTBEAT}{VAL} . '","short_id":0,"key":"8","data":"{\"rgb\":' . $val . ',\"key\":\"'. XiaomiSmartHome_EncryptKey($hash) .'\"}" }'; + + } + elsif ($cmd eq 'illumination') + { + # TODO SID des Gateway nicht im und aus dem Reading!! + #$msg = '{"cmd":"write","model":"gateway","sid":"' . $hash->{READINGS}{HEARTBEAT}{VAL} . '","short_id":0,"key":"8","data":"{\"illumination\":\"' . $val . '\",\"key\":\"'. XiaomiSmartHome_EncryptKey($hash) .'\"}" }'; + $msg = '{"cmd":"write","model":"gateway","sid":"' . $hash->{READINGS}{HEARTBEAT}{VAL} . '","short_id":0,"key":"8","data":"{\"mid\":\"3\",\"key\":\"'. XiaomiSmartHome_EncryptKey($hash) .'\"}" }'; + #$msg = '{"cmd":"write","model":"gateway","sid":"' . $hash->{READINGS}{HEARTBEAT}{VAL} . '","short_id":0,"key":"8","data":"{\"hue\":\"170\",\"saturation\":\"254\", \"color_temperature\":\"65279\", \"x\":\"10\", \"y\":\"10\",\"key\":\"'. XiaomiSmartHome_EncryptKey($hash) .'\"}" }'; + + + } + return Log3 $name, 4, "Master ($name) - socket not connected" unless($hash->{CD}); + + Log3 $name, 4, "$name> $msg " . $hash->{GATEYWAY}; + my $sock = $hash->{CD}; + my $MAXLEN = 1024; + $sock->mcast_send($msg,$hash->{GATEYWAY} .':9898') or die "send: $!"; + + return undef; +} +##################################### + +sub XiaomiSmartHome_EncryptKey($) +{ + my ($hash) = @_; + if (defined $hash->{READINGS}{password}{VAL}){ + my $key = $hash->{READINGS}{password}{VAL}; + my $cipher = Crypt::CBC->new(-key => $key, -cipher => 'Cipher::AES',-iv => $iv, -literal_key => 1, -header => "none", -keysize => 16 ); + my $encryptkey = $cipher->encrypt_hex($hash->{READINGS}{token}{VAL}); + $encryptkey = substr($encryptkey, 0, 32); + return $encryptkey; + } + return undef; +} +##################################### + +sub XiaomiSmartHome_Get($@) +{ + my ($hash , $name, $opt, $args ) = @_; + + if ($opt eq "UpdateAll") + { + XiaomiSmartHome_updateAllReadings($hash); + Log3 $name, 5, "$name> UpdateALLReadings Started"; + } + elsif($opt eq "UpdateSingle") + { + XiaomiSmartHome_updateSingleReading($hash,$args); + Log3 $name, 5, "$name> UpdateSingel Started"; + } + else + { + return "unknown argument $opt choose one of UpdateAll:noArg UpdateSingle"; + } +} +##################################### + +sub XiaomiSmartHome_Notify($$) +{ + my ($own_hash, $dev_hash) = @_; + my $ownName = $own_hash->{NAME}; # own name / hash + Log3 $ownName, 3, "$ownName> NotifyStart"; + return "" if(IsDisabled($ownName)); # Return without any further action if the module is disabled + + 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, 3, "$ownName> Starting Connect"; + XiaomiSmartHome_connect($own_hash); + } +} +##################################### + +sub XiaomiSmartHome_Set($@) + { + my ( $hash, $name, $cmd, @args ) = @_; + my $dec_num; + my @match = grep( $_ =~ /^$cmd($|:)/, keys %sets ); + #-- check argument + return SetExtensions($hash, join(" ", keys %sets), $name, $cmd, @args) unless @match == 1; + return "$cmd expects $sets{$match[0]} parameters" unless (@args eq $sets{$match[0]}); + + return "\"set $name\" needs at least one argument" unless(defined($cmd)); + + + if($cmd eq "password") + { + if($args[0] =~ /^[a-zA-Z0-9_.-]*$/) + { + readingsSingleUpdate( $hash, $cmd, $args[0], 0 ); + return; + } + else + { + return "Unknown value $args[0] for $cmd, wrong password, it must be hex"; + } + } + elsif($cmd eq "rgb") + { + my $ownName = $hash->{NAME}; + Log3 $ownName, 4, "$ownName> Set $cmd, $args[0]"; + $dec_num = sprintf("%d", hex('ff' . $args[0])); + Log3 $ownName, 4, "$ownName> Set $cmd, $dec_num"; + readingsSingleUpdate( $hash, 'rgb', $args[0], 1 ); + readingsSingleUpdate( $hash, 'state', 'on', 1 ); + XiaomiSmartHome_Write($hash,$cmd,$dec_num); + } + elsif($cmd eq "off") + { + $hash->{helper}{prevrgbvalue} = $hash->{READINGS}{rgb}{VAL}; + readingsSingleUpdate( $hash, 'state', 'off', 1 ); + readingsSingleUpdate( $hash, 'rgb', 'off', 1 ); + XiaomiSmartHome_Write($hash,'rgb', 0); + } + elsif($cmd eq "on") + { + readingsSingleUpdate( $hash, 'state', 'on', 1 ); + if ($hash->{helper}{prevrgbvalue}) + { + $dec_num = sprintf("%d", hex('ff' . $hash->{helper}{prevrgbvalue})); + readingsSingleUpdate( $hash, 'rgb', $hash->{helper}{prevrgbvalue}, 1 ); + XiaomiSmartHome_Write($hash,'rgb', $dec_num); + } + else + { + XiaomiSmartHome_Write($hash,'rgb', 1677786880); + readingsSingleUpdate( $hash, 'rgb', '00ff00', 1 ); + } + } + elsif($cmd eq "pct") + { + my $ownName = $hash->{NAME}; + Log3 $ownName, 4, "$ownName> Set $cmd, $args[0]"; + XiaomiSmartHome_Write($hash,'illumination',$args[0]); + } + + else + { + return "Unknown argument! $cmd, $args[0], choose one of password rgb"; + } +} +##################################### + + +##################################### + + +sub XiaomiSmartHome_Attr(@) { + +} +##################################### + +sub XiaomiSmartHome_connect($) +{ + my $hash = shift; + my $name = $hash->{NAME}; + Log3 $name, 3, "$name> ConnectStart"; + + XiaomiSmartHome_disconnect($hash); + + Log3 $name, 4, "$name> connecting"; + + my $sock = IO::Socket::Multicast->new( Proto => 'udp', LocalPort =>'9898', ReuseAddr => 1) or die "Creating socket: $!\n"; + $sock->mcast_add('224.0.0.50', $hash->{fhemIP} ) || die "Couldn't set group: $!\n"; #$hash->{fhemIP} + $sock->mcast_ttl(32); + $sock->mcast_loopback(1); + + if ($sock) + { + Log3 $name, 3, "$name> connected"; + + $hash->{helper}{ConnectionState} = "Connected"; + + if ($hash->{helper}{ConnectionState} ne ReadingsVal($name, "state", "" )) + { + readingsSingleUpdate($hash, "state", $hash->{helper}{ConnectionState}, 1); + } + + $hash->{FD} = $sock->fileno(); + $hash->{CD} = $sock; + + $selectlist{$name} = $hash; + + XiaomiSmartHome_updateAllReadings($hash); + } + else + { + Log3 $name, 1, "$name> connect to $hash->{helper}{host} failed"; + } + + return undef; +} +##################################### + +sub XiaomiSmartHome_disconnect($) +{ + my $hash = shift; + my $name = $hash->{NAME}; + + RemoveInternalTimer($hash); + $hash->{helper}{ConnectionState} = "Disconnected"; + if ($hash->{helper}{ConnectionState} ne ReadingsVal($name, "state", "" )) + { + readingsSingleUpdate($hash, "state", $hash->{helper}{ConnectionState}, 1); + } + + return if (!$hash->{CD}); + Log3 $name, 3, "$name> disconnecting"; + + close($hash->{CD}); + delete($hash->{CD}); + + + return undef; +} +##################################### + +sub XiaomiSmartHome_updateSingleReading($$) +{ + my ($hash, $sensor) = @_; + my $name = $hash->{NAME}; + my $GATEYWAY = $hash->{GATEYWAY}; + my $sock = $hash->{CD}; + my $MAXLEN = 1024; + + Log3 $name, 4, "$name> PushSingelRead:" . $sensor; + XiaomiSmartHome_Write($hash, 'read', $sensor); + +} +##################################### + +sub XiaomiSmartHome_updateAllReadings($) +{ + my $hash = shift; + my $name = $hash->{NAME}; + my $GATEYWAY = $hash->{GATEYWAY}; + my $sock = $hash->{CD}; + my $MAXLEN = 1024; + + my $msg = '{"cmd" : "get_id_list"}'; + $sock->mcast_send($msg, $GATEYWAY .':9898') or die "send: $!"; + eval { + $sock->recv($msg, $MAXLEN) or die "recv: $!"; + Log3 $name, 5, "$name> " . $msg; + my $json = $hash->{helper}{JSON}->incr_parse($msg); + my $decoded = decode_json($msg); + if ($json){ + Log3 $name, 5, "$name> Read:" . $msg; + if ($decoded->{'cmd'} eq 'get_id_list_ack'){ + my @sensors = @{decode_json($decoded->{data})}; + foreach my $sensor (@sensors) + { + $msg = '{"cmd":"read","sid":"' . $sensor . '" }'; + Log3 $name, 4, "$name> PushRead:" . $sensor; + my $msg = '{"cmd":"read","sid":"' . $sensor . '" }'; + $sock->mcast_send($msg, $GATEYWAY .':9898') or die "send: $!"; + eval { + $sock->recv($msg, $MAXLEN) or die "recv: $!"; + Log3 $name, 5, "$name> " . $msg; + Dispatch($hash, $msg, undef); + } + + } + } + } + } +} +##################################### + +1; + +=pod +=item device +=item summary Module fpr XiaomiSmartHome Gateway to use with FHEM +=item summary_DE Modul um ein XiaomiSmartHome Gateyway in FHEM zu nutzen + +=begin html + + +

XiaomiSmartHome

+
    + XiaomiSmartHome implements the XiaomiSmartHome Gateway and Sensors. + +
    + Define +
      + define <name> XiaomiSmartHome <IP or Hostname> +

      + Example: define XiaomiSmartHome XiaomiSmartHome 192.168.1.xxx +

      +
    +
    +
+=end html + +=begin html_DE + + +

XiaomiSmartHome

+
    + XiaomiSmartHome implements the XiaomiSmartHome Gateway and Sensors. + + Define +
      + define <name> XiaomiSmartHome <IP or Hostname> +

      + Example: define XiaomiSmartHome XiaomiSmartHome 192.168.1.xxx +

      + +
    +
    +
+=end html + + +=cut + + + diff --git a/71_XiaomiSmartHome_Device.pm b/71_XiaomiSmartHome_Device.pm new file mode 100644 index 0000000..3da5309 --- /dev/null +++ b/71_XiaomiSmartHome_Device.pm @@ -0,0 +1,233 @@ +############################################################################### +# +# 03.2017 torte +# All rights reserved +# +# This script is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# any later version. +# +# The GNU General Public License can be found at +# http://www.gnu.org/copyleft/gpl.html. +# A copy is found in the textfile GPL.txt and important notices to the license +# from the author is found in LICENSE.txt distributed with these scripts. +# +# This script is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# +############################################################################### +package main; + +use strict; +use warnings; + +my $version = "0.05"; +sub XiaomiSmartHome_Device_updateSReading($); + +##################################### + +sub XiaomiSmartHome_Device_Initialize($) +{ + my ($hash) = @_; + + $hash->{Match} = "^.+magnet|motion|sensor_ht|switch"; + $hash->{DefFn} = "XiaomiSmartHome_Device_Define"; + #$hash->{SetFn} = "XiaomiSmartHome_Device_Set"; + $hash->{UndefFn} = "XiaomiSmartHome_Device_Undef"; + $hash->{ParseFn} = "XiaomiSmartHome_Device_Parse"; + + $hash->{AttrList} = "IODev follow-on-for-timer:1,0 follow-on-timer ". + "do_not_notify:1,0 ignore:1,0 dummy:1,0 showtime:1,0 valueFn:textField-long motionOffTimer:1,5,10 ". + $readingFnAttributes ; +} +##################################### + +sub XiaomiSmartHome_Device_mot($$) +{ + my ($hash, $mot) = @_; + + InternalTimer(gettimeofday()+$mot, "XiaomiSmartHome_Device_on_timeout",$hash, 0); + # on-for-timer is now a on. + + +} +##################################### + +sub XiaomiSmartHome_Device_on_timeout($){ + my ($hash) = @_; + my $name = $hash->{LASTInputDev}; + if ($hash->{STATE} eq 'motion') { + readingsSingleUpdate($hash, "state", "off", 1 ); + Log3 $name, 3, "$name>" . " SID: " . $hash->{SID} . " Type: " . $hash->{MODEL} . " Status: off"; + } +} +##################################### +sub XiaomiSmartHome_Device_Read($$$){ + my ($hash, $msg, $name) = @_; + my $decoded = decode_json($msg); + + my $sid = $decoded->{'sid'}; + my $model = $decoded->{'model'}; + Log3 $name, 5, "$name: SID: " . $hash->{SID} . " " . $hash->{TYPE}; + my $data = decode_json($decoded->{data}); + #my @status = split('\"', $decoded->{'data'}); + if (defined $data->{status}){ + Log3 $name, 3, "$name>" . " SID: " . $sid . " Type: " . $hash->{MODEL} . " Status: " . $data->{status}; + readingsSingleUpdate($hash, "state", "$data->{status}", 1 ); + if ($data->{status} eq 'motion' && $hash->{MODEL} eq 'motion'){ + readingsSingleUpdate($hash, "no_motion", "0", 1 ); + } + } + elsif(defined $data->{no_motion}){ + Log3 $name, 3, "$name>" . " SID: " . $sid . " Type: " . $hash->{MODEL} . " NO_motion: " . $data->{no_motion}; + readingsSingleUpdate($hash, "no_motion", "$data->{no_motion}", 1 ); + } + elsif(defined $data->{voltage}){ + Log3 $name, 3, "$name>" . " SID: " . $sid . " Type: " . $hash->{MODEL} . " Voltage: " . $data->{voltage}; + readingsSingleUpdate($hash, "voltage", "$data->{voltage}", 1 ); + } + elsif(defined $data->{temperature}){ + my $temp = $data->{temperature}; + $temp =~ s/(^[-+]?\d+?(?=(?>(?:\d{2})+)(?!\d))|\G\d{2}(?=\d))/$1./g; + Log3 $name, 3, "$name>" . " SID: " . $sid . " Type: " . $hash->{MODEL} . " Temperature: " . $temp; + readingsSingleUpdate($hash, "temperature", "$temp", 1 ); + } + elsif(defined $data->{humidity}){ + my $hum = $data->{humidity}; + $hum =~ s/(^[-+]?\d+?(?=(?>(?:\d{2})+)(?!\d))|\G\d{2}(?=\d))/$1./g; + Log3 $name, 3, "$name>" . " SID: " . $sid . " Type: " . $hash->{MODEL} . " Humidity: " . $hum; + readingsSingleUpdate($hash, "humidity", "$hum", 1 ); + } + elsif ($decoded->{'cmd'} eq 'heartbeat'){ + readingsSingleUpdate($hash, $decoded->{'sid'}, 'heartbeat', 1 ); + } + XiaomiSmartHome_Device_update($hash); + return $hash->{NAME}; + + +} +##################################### + +sub XiaomiSmartHome_Device_Parse($$) { + my ($io_hash, $msg) = @_; + my $decoded = decode_json($msg); + + my $sid = $decoded->{'sid'}; + my $model = $decoded->{'model'}; + my $name = $io_hash->{NAME}; + + if (my $hash = $modules{XiaomiSmartHome_Device}{defptr}{$sid}) + { + Log3 $name, 4, "$name> IS DEFINED " . $model . " : " .$sid; + XiaomiSmartHome_Device_Read($hash, $msg, $name); + } + else + { + + Log3 $name, 4, "$name> UNDEFINED " . $model . " : " .$sid; + return "UNDEFINED $sid XiaomiSmartHome_Device $model $name"; + } +} +##################################### + +sub XiaomiSmartHome_Device_update($){ + my ($hash) = @_; + my $model = $hash->{MODEL}; + my $name = $hash->{NAME}; + my $value_fn = AttrVal( $name, "valueFn", "" ); + my $mot = AttrVal( $name, "motionOffTimer", "" ); + if( $value_fn =~ m/^{.*}$/s ) { + + my $LASTCMD = ReadingsVal($name,"lastCmd",undef); + + my $value_fn = eval $value_fn; + Log3 $name, 3, $name .": valueFn: ". $@ if($@); + return undef if( !defined($value_fn) ); + } + if( $model eq 'motion') { + XiaomiSmartHome_Device_mot($hash, $mot) if( $mot); + } +} +##################################### + + +sub XiaomiSmartHome_Device_Define($$) { + my ($hash, $def) = @_; + my ($name, $modul, $type, $iodev) = split("[ \t]+", $def); + #Log3 "test", 3, "Define status = " . $status; + $hash->{TYPE} = $modul; + $hash->{MODEL} = $type; + $hash->{SID} = $name; + $hash->{NAME} = $name; + $hash->{VERSION} = $version; + $hash->{STATE} = 'initialized'; + $modules{XiaomiSmartHome_Device}{defptr}{$name} = $hash; + AssignIoPort($hash,$iodev); + + if(defined($hash->{IODev}->{NAME})) { + my $IOname = $hash->{IODev}->{NAME}; + Log3 $name, 3, $IOname . "> " .$name. ": " . $type . " I/O device is " . $hash->{IODev}->{NAME}; + } else { + Log3 $name, 1, "$name $type - no I/O device"; + } + $iodev = $hash->{IODev}->{NAME}; + + my $d = $modules{XiaomiSmartHome_Device}{defptr}{$name}; + + return "XiaomiSmartHome device $hash->{SID} on XiaomiSmartHome $iodev already defined as $d->{NAME}." if( defined($d) && $d->{IODev} == $hash->{IODev} && $d->{NAME} ne $name ); + + Log3 $name, 3, $iodev . "> " . $name . ": defined as ". $hash->{MODEL}; + $attr{$name}{room} = "MiSmartHome" if( !defined( $attr{$name}{room} ) ); + if( $type eq 'motion') { + $attr{$name}{devStateIcon} = 'motion:motion_detector@red off:motion_detector@green no_motion:motion_detector@green' if( !defined( $attr{$name}{devStateIcon} ) ); + } + elsif ( $type eq 'magnet') { + $attr{$name}{devStateIcon} = 'open:fts_door_open@red close:fts_door@green' if( !defined( $attr{$name}{devStateIcon} ) ); + } + elsif ( $type eq 'sensor_ht') { + $attr{$name}{stateFormat} = 'temperature °C, humidity %' if( !defined( $attr{$name}{stateFormat} ) ); + } + + if( $init_done ) { + InternalTimer( gettimeofday()+int(rand(2)), "XiaomiSmartHome_Device_updateSReading", $hash, 0 ); + Log3 $name, 4, $iodev . "> " . $name . " Init Done set InternalTimer for Update"; + } +} +##################################### +sub XiaomiSmartHome_Device_updateSReading($) { + + my $hash = shift; + #my $name = $hash->{NAME}; + #Log3 $name, 3, $name . " Updae SR"; + IOWrite($hash,'read',"$hash->{SID}"); +} +##################################### + +##################################### +sub XiaomiSmartHome_Device_Undef($) +{ + my ($hash, $arg) = @_; + my $name = $hash->{NAME}; + my $iodev = $hash->{IODev}->{NAME}; + RemoveInternalTimer($hash); + delete($modules{XiaomiSmartHome_Device}{defptr}{$hash->{SID}}); + Log3 $name, 3, "$iodev> $name - device deleted"; + return undef; + +} +1; +##################################### +=pod +=begin html + + +

XiaomiSmartHome_Device

+ + +=end html + +=cut \ No newline at end of file