write to gateway, bugfix, first time master

add:
write with encrypted key to change the color from the gateway.
change:
decode json data
bugfix:
error in function write
error in function updatesingle
This commit is contained in:
2017-03-18 23:15:27 +01:00
parent e817428ae8
commit 07e41816f7
2 changed files with 162 additions and 40 deletions

View File

@@ -25,21 +25,31 @@ package main;
use strict;
use warnings;
use strict;
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 $version = "0.02";
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,
);
#####################################
@@ -56,12 +66,14 @@ sub XiaomiSmartHome_Initialize($) {
$hash->{ReadFn} = 'XiaomiSmartHome_Read';
$hash->{WriteFn} = "XiaomiSmartHome_Write";
$hash->{AttrList} = "disable:1,0 " .
"Room " .
"Room " .
$readingFnAttributes;
$hash->{MatchList} = { "1:XiaomiSmartHome_Device" => "^.+magnet",
"2:XiaomiSmartHome_Device" => "^.+motion",
"3:XiaomiSmartHome_Device" => "^.+sensor_ht",
"4:XiaomiSmartHome_Device" => "^.+switch"};
FHEM_colorpickerInit();
}
#####################################
@@ -85,14 +97,14 @@ sub XiaomiSmartHome_Read($) {
Log3 $name, 5, "$name> Read:" . $buf;
if ($decoded->{'model'} eq 'gateway'){
if ($decoded->{'cmd'} eq 'report'){
my @status = split('\"', $decoded->{'data'});
if ($status[1] eq 'rgb'){
Log3 $name, 4, "$name>" . " SID: " . $decoded->{'sid'} . " Type: Gateway" . " RGB: " . ($status[2] =~ /([\d]+)/)[0] ;
readingsSingleUpdate($hash, "RGB", ($status[2] =~ /([\d]+)/)[0] , 1 );
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, $decoded->{'sid'}, 'heartbeat', 1 );
readingsSingleUpdate($hash, 'HEARTBEAT', $decoded->{'sid'}, 1 );
readingsSingleUpdate($hash, 'token', $decoded->{'token'}, 1 );
}
}
@@ -137,7 +149,10 @@ sub XiaomiSmartHome_Define($$) {
$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;
@@ -153,14 +168,31 @@ sub XiaomiSmartHome_Undef($$) {
}
#####################################
sub XiaomiSmartHome_Write($$)
sub XiaomiSmartHome_Write($$$)
{
my ($hash,$cmd,$sid) = @_;
my $name = $hash->{NAME};
my $msg = '{"cmd":"' .$cmd . '","sid":"' . $sid . '"}';
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});
}
return Log3 $name, 4, "Master ($name) - socket not connected" unless($hash->{CD});
Log3 $name, 4, "$name> $msg " . $hash->{GATEYWAY};
my $sock = $hash->{CD};
@@ -169,14 +201,26 @@ sub XiaomiSmartHome_Write($$)
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 ) = @_;
my $name = $hash->{NAME};
if ($opt eq "UpdateAll")
{
XiaomiSmartHome_updateAllReadings($hash);
@@ -211,11 +255,80 @@ sub XiaomiSmartHome_Notify($$)
}
#####################################
sub XiaomiSmartHome_Set($@) {
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(@) {
}
@@ -295,7 +408,7 @@ sub XiaomiSmartHome_updateSingleReading($$)
my $MAXLEN = 1024;
Log3 $name, 4, "$name> PushSingelRead:" . $sensor;
XiaomiSmartHome_Write($hash, $sensor);
XiaomiSmartHome_Write($hash, 'read', $sensor);
}
#####################################
@@ -318,8 +431,7 @@ sub XiaomiSmartHome_updateAllReadings($)
if ($json){
Log3 $name, 5, "$name> Read:" . $msg;
if ($decoded->{'cmd'} eq 'get_id_list_ack'){
my @sensors = split('\"', $decoded->{'data'});
@sensors = grep {$_ ne ',' and $_ ne ']' and $_ ne '[' } @sensors;
my @sensors = @{decode_json($decoded->{data})};
foreach my $sensor (@sensors)
{
$msg = '{"cmd":"read","sid":"' . $sensor . '" }';

View File

@@ -25,7 +25,7 @@ package main;
use strict;
use warnings;
my $version = "0.02";
my $version = "0.05";
sub XiaomiSmartHome_Device_updateSReading($);
#####################################
@@ -73,24 +73,34 @@ sub XiaomiSmartHome_Device_Read($$$){
my $sid = $decoded->{'sid'};
my $model = $decoded->{'model'};
Log3 $name, 5, "$name: SID: " . $hash->{SID} . " " . $hash->{TYPE};
my @status = split('\"', $decoded->{'data'});
if ($status[1] eq 'status'){
Log3 $name, 3, "$name>" . " SID: " . $sid . " Type: " . $hash->{MODEL} . " Status: " . $status[3];
readingsSingleUpdate($hash, "state", "$status[3]", 1 );
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($status[1] eq 'voltage'){
Log3 $name, 3, "$name>" . " SID: " . $sid . " Type: " . $hash->{MODEL} . " Voltage: " . $status[3];
readingsSingleUpdate($hash, "voltage", "$status[3]", 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($status[1] eq 'temperature'){
Log3 $name, 3, "$name>" . " SID: " . $sid . " Type: " . $hash->{MODEL} . " Temperature: " . $status[3];
$status[3] =~ s/(^[-+]?\d+?(?=(?>(?:\d{2})+)(?!\d))|\G\d{2}(?=\d))/$1./g;
readingsSingleUpdate($hash, "temperature", "$status[3]", 1 );
elsif(defined $data->{voltage}){
Log3 $name, 3, "$name>" . " SID: " . $sid . " Type: " . $hash->{MODEL} . " Voltage: " . $data->{voltage};
readingsSingleUpdate($hash, "voltage", "$data->{voltage}", 1 );
}
elsif($status[1] eq 'humidity'){
Log3 $name, 3, "$name>" . " SID: " . $sid . " Type: " . $hash->{MODEL} . " Humidity: " . $status[3];
$status[3] =~ s/(^[-+]?\d+?(?=(?>(?:\d{2})+)(?!\d))|\G\d{2}(?=\d))/$1./g;
readingsSingleUpdate($hash, "humidity", "$status[3]", 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 );
@@ -170,10 +180,10 @@ sub XiaomiSmartHome_Device_Define($$) {
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 with Code: ". $hash->{DEV};
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' if( !defined( $attr{$name}{devStateIcon} ) );
$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} ) );