From e62029721586162cdcf550eadedb638dd6c59662 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Sat, 13 Jan 2018 20:24:13 +0100 Subject: [PATCH] add set devicename --- 74_XiaomiBTLESens.pm | 197 +++++++++++++++++++++++++++++-------------- 1 file changed, 132 insertions(+), 65 deletions(-) diff --git a/74_XiaomiBTLESens.pm b/74_XiaomiBTLESens.pm index 64b2101..18663d7 100644 --- a/74_XiaomiBTLESens.pm +++ b/74_XiaomiBTLESens.pm @@ -47,14 +47,14 @@ use JSON; use Blocking; -my $version = "1.99.31"; +my $version = "1.99.37"; my %XiaomiModels = ( - flowerSens => {'read' => '0x35' ,'write' => '0x33' ,'writeValue' => 'A01F' ,'battery' => '0x38' ,'firmware' => '0x38'}, - thermoHygroSens => {'read' => 'none' ,'write' => '0x10' ,'writeValue' => '0100' ,'battery' => '0x18' ,'firmware' => '0x24'}, + flowerSens => {'rdata' => '0x35' ,'wdata' => '0x33' ,'wdataValue' => 'A01F' ,'battery' => '0x38' ,'firmware' => '0x38'}, + thermoHygroSens => {'wdata' => '0x10' ,'wdataValue' => '0100' ,'battery' => '0x18' ,'firmware' => '0x24' ,'devicename' => '0x3'}, ); my %CallBatteryAge = ( '8h' => 28800, @@ -76,24 +76,33 @@ sub XiaomiBTLESens_stateRequestTimer($); sub XiaomiBTLESens_Set($$@); sub XiaomiBTLESens_Get($$@); sub XiaomiBTLESens_Notify($$); -sub XiaomiBTLESens_CallBattery($); -sub XiaomiBTLESens_CallFirmware($); + +sub XiaomiBTLESens_ReadBattery($); +sub XiaomiBTLESens_ReadFirmware($); sub XiaomiBTLESens_ReadSensData($); +sub XiaomiBTLESens_ReadDeviceName($); +sub XiaomiBTLESens_WriteDeviceName($$); sub XiaomiBTLESens_WriteSensData($); + sub XiaomiBTLESens_ExecGatttool_Run($); sub XiaomiBTLESens_ExecGatttool_Done($); sub XiaomiBTLESens_ExecGatttool_Aborted($); sub XiaomiBTLESens_ProcessingNotification($@); sub XiaomiBTLESens_WriteReadings($$); sub XiaomiBTLESens_ProcessingErrors($$); +sub XiaomiBTLESens_encodeJSON($); + sub XiaomiBTLESens_CallBattery_IsUpdateTimeAgeToOld($$); sub XiaomiBTLESens_CallBattery_Timestamp($); sub XiaomiBTLESens_CallBattery_UpdateTimeAge($); -sub XiaomiBTLESens_encodeJSON($); +sub XiaomiBTLESens_CreateDevicenameHEX($); + sub XiaomiBTLESens_FlowerSensHandle0x35($$); sub XiaomiBTLESens_FlowerSensHandle0x38($$); sub XiaomiBTLESens_ThermoHygroSensHandle0x18($$); sub XiaomiBTLESens_ThermoHygroSensHandle0x10($$); +sub XiaomiBTLESens_ThermoHygroSensHandle0x24($$); + @@ -281,30 +290,25 @@ sub XiaomiBTLESens_stateRequest($) { } elsif( !IsDisabled($name) ) { if( ReadingsVal($name,'firmware','none') ne 'none' ) { - - return XiaomiBTLESens_CallBattery($hash) + + return XiaomiBTLESens_ReadBattery($hash) if( XiaomiBTLESens_CallBattery_IsUpdateTimeAgeToOld($hash,$CallBatteryAge{AttrVal($name,'BatteryFirmwareAge','24h')}) ); - - - if( ReadingsVal($name, 'firmware', '') eq '2.6.2') { - XiaomiBTLESens_ReadSensData($hash); + if( $hash->{helper}{CallSensDataCounter} < 1 ) { + XiaomiBTLESens_WriteSensData($hash); + $hash->{helper}{CallSensDataCounter} = $hash->{helper}{CallSensDataCounter} + 1; + } else { - if( $hash->{helper}{CallSensDataCounter} < 1 ) { - XiaomiBTLESens_WriteSensData($hash); - $hash->{helper}{CallSensDataCounter} = $hash->{helper}{CallSensDataCounter} + 1; - - } else { - $readings{'lastGattError'} = 'charWrite faild'; - XiaomiBTLESens_WriteReadings($hash,\%readings); - $hash->{helper}{CallSensDataCounter} = 0; - return; - } + $readings{'lastGattError'} = 'charWrite faild'; + XiaomiBTLESens_WriteReadings($hash,\%readings); + $hash->{helper}{CallSensDataCounter} = 0; + return; } } else { - XiaomiBTLESens_CallFirmware($hash); + XiaomiBTLESens_ReadFirmware($hash); + InternalTimer( gettimeofday() + 120, "XiaomiBTLESens_ReadDeviceName", $hash ) if( AttrVal($name,'model','thermoHygroSens') eq 'thermoHygroSens' ); } readingsSingleUpdate($hash,"state","fetch sensor data",1); @@ -330,18 +334,17 @@ sub XiaomiBTLESens_stateRequestTimer($) { readingsSingleUpdate ( $hash, "state", "disabled", 1 ); } - InternalTimer( gettimeofday()+$hash->{INTERVAL}+int(rand(300)), "XiaomiBTLESens_stateRequestTimer", $hash, 1 ); + InternalTimer( gettimeofday()+$hash->{INTERVAL}+int(rand(600)), "XiaomiBTLESens_stateRequestTimer", $hash ); Log3 $name, 4, "XiaomiBTLESens ($name) - stateRequestTimer: Call Request Timer"; } -sub XiaomiBTLESens_CallBattery($) { +sub XiaomiBTLESens_ReadBattery($) { my $hash = shift; my $name = $hash->{NAME}; my $mac = $hash->{BTMAC}; - my $handle; $hash->{helper}{RUNNING_PID} = BlockingCall("XiaomiBTLESens_ExecGatttool_Run", $name."|".$mac."|read|".$XiaomiModels{$attr{$name}{model}}{battery}, "XiaomiBTLESens_ExecGatttool_Done", 60, "XiaomiBTLESens_ExecGatttool_Aborted", $hash) unless(exists($hash->{helper}{RUNNING_PID})); @@ -349,13 +352,12 @@ sub XiaomiBTLESens_CallBattery($) { Log3 $name, 4, "XiaomiBTLESens ($name) - CallBattery: call function ExecGatttool_Run"; } -sub XiaomiBTLESens_CallFirmware($) { +sub XiaomiBTLESens_ReadFirmware($) { my $hash = shift; my $name = $hash->{NAME}; my $mac = $hash->{BTMAC}; - my $handle; $hash->{helper}{RUNNING_PID} = BlockingCall("XiaomiBTLESens_ExecGatttool_Run", $name."|".$mac."|read|".$XiaomiModels{$attr{$name}{model}}{firmware}, "XiaomiBTLESens_ExecGatttool_Done", 60, "XiaomiBTLESens_ExecGatttool_Aborted", $hash) unless(exists($hash->{helper}{RUNNING_PID})); @@ -363,6 +365,19 @@ sub XiaomiBTLESens_CallFirmware($) { Log3 $name, 4, "XiaomiBTLESens ($name) - CallFirmware: call function ExecGatttool_Run"; } +sub XiaomiBTLESens_ReadDeviceName($) { + + my $hash = shift; + + my $name = $hash->{NAME}; + my $mac = $hash->{BTMAC}; + + + $hash->{helper}{RUNNING_PID} = BlockingCall("XiaomiBTLESens_ExecGatttool_Run", $name."|".$mac."|read|".$XiaomiModels{$attr{$name}{model}}{devicename}, "XiaomiBTLESens_ExecGatttool_Done", 60, "XiaomiBTLESens_ExecGatttool_Aborted", $hash) unless(exists($hash->{helper}{RUNNING_PID})); + + Log3 $name, 4, "XiaomiBTLESens ($name) - CallDeviceName: call function ExecGatttool_Run"; +} + sub XiaomiBTLESens_ReadSensData($) { my $hash = shift; @@ -371,7 +386,7 @@ sub XiaomiBTLESens_ReadSensData($) { my $mac = $hash->{BTMAC}; - $hash->{helper}{RUNNING_PID} = BlockingCall("XiaomiBTLESens_ExecGatttool_Run", $name."|".$mac."|read|".$XiaomiModels{$attr{$name}{model}}{read}, "XiaomiBTLESens_ExecGatttool_Done", 60, "XiaomiBTLESens_ExecGatttool_Aborted", $hash) unless(exists($hash->{helper}{RUNNING_PID})); + $hash->{helper}{RUNNING_PID} = BlockingCall("XiaomiBTLESens_ExecGatttool_Run", $name."|".$mac."|read|".$XiaomiModels{$attr{$name}{model}}{rdata}, "XiaomiBTLESens_ExecGatttool_Done", 60, "XiaomiBTLESens_ExecGatttool_Aborted", $hash) unless(exists($hash->{helper}{RUNNING_PID})); Log3 $name, 4, "XiaomiBTLESens ($name) - CallSensData: call function ExecGatttool_Run"; } @@ -384,25 +399,39 @@ sub XiaomiBTLESens_WriteSensData($) { my $mac = $hash->{BTMAC}; - $hash->{helper}{RUNNING_PID} = BlockingCall("XiaomiBTLESens_ExecGatttool_Run", $name."|".$mac."|write|".$XiaomiModels{$attr{$name}{model}}{write}."|".$XiaomiModels{$attr{$name}{model}}{writeValue}, "XiaomiBTLESens_ExecGatttool_Done", 60, "XiaomiBTLESens_ExecGatttool_Aborted", $hash) unless(exists($hash->{helper}{RUNNING_PID})); + $hash->{helper}{RUNNING_PID} = BlockingCall("XiaomiBTLESens_ExecGatttool_Run", $name."|".$mac."|write|".$XiaomiModels{$attr{$name}{model}}{wdata}."|".$XiaomiModels{$attr{$name}{model}}{wdataValue}, "XiaomiBTLESens_ExecGatttool_Done", 60, "XiaomiBTLESens_ExecGatttool_Aborted", $hash) unless(exists($hash->{helper}{RUNNING_PID})); Log3 $name, 4, "XiaomiBTLESens ($name) - WriteSensData: call function ExecGatttool_Run"; } +sub XiaomiBTLESens_WriteDeviceName($$) { + + my ($hash,$value) = @_; + + my $name = $hash->{NAME}; + my $mac = $hash->{BTMAC}; + + + $hash->{helper}{RUNNING_PID} = BlockingCall("XiaomiBTLESens_ExecGatttool_Run", $name."|".$mac."|write|".$XiaomiModels{$attr{$name}{model}}{devicename}."|".$value, "XiaomiBTLESens_ExecGatttool_Done", 60, "XiaomiBTLESens_ExecGatttool_Aborted", $hash) unless(exists($hash->{helper}{RUNNING_PID})); + + Log3 $name, 4, "XiaomiBTLESens ($name) - WriteDeviceName: call function ExecGatttool_Run"; +} + sub XiaomiBTLESens_Set($$@) { my ($hash, $name, @aa) = @_; my ($cmd, @args) = @aa; - if( $cmd eq 'clearFirmwareReading' ) { - return "usage: clearFirmwareReading" if( @args != 0 ); - - readingsSingleUpdate($hash,'firmware','',0); + if( $cmd eq 'devicename' ) { + return "usage: devicename " if( @args < 1 ); + + my $devicename = join( " ", @args ); + XiaomiBTLESens_WriteDeviceName($hash,XiaomiBTLESens_CreateDevicenameHEX($devicename)); } else { - my $list = ""; - $list = "clearFirmwareReading:noArg" if( AttrVal($name,'model','none') eq 'flowerSens'); + my $list = "devicename" if( AttrVal($name,'model','thermoHygroSens') eq 'thermoHygroSens' ); + return "Unknown argument $cmd, choose one of $list"; } @@ -415,13 +444,13 @@ sub XiaomiBTLESens_Get($$@) { my ($cmd, @args) = @aa; - if( $cmd eq 'statusRequest' ) { - return "usage: statusRequest" if( @args != 0 ); + if( $cmd eq 'sensorData' ) { + return "usage: sensorData" if( @args != 0 ); XiaomiBTLESens_stateRequest($hash); } else { - my $list = "statusRequest:noArg"; + my $list = "sensorData:noArg"; return "Unknown argument $cmd, choose one of $list"; } @@ -468,7 +497,7 @@ sub XiaomiBTLESens_ExecGatttool_Run($) { $cmd .= "gatttool -i $hci -b $mac "; $cmd .= "--char-read -a $handle" if($gattCmd eq 'read'); $cmd .= "--char-write-req -a $handle -n $value" if($gattCmd eq 'write'); - $cmd = "timeout 15 ".$cmd." --listen" if( AttrVal($name,"model","none") eq 'thermoHygroSens' and $gattCmd eq 'write'); + $cmd = "timeout 15 ".$cmd." --listen" if( AttrVal($name,"model","none") eq 'thermoHygroSens' and $gattCmd eq 'write' and $handle eq '0x10'); $cmd .= " 2>&1 /dev/null"; $cmd .= "'" if($sshHost ne 'none'); $cmd = "ssh $sshHost 'gatttool -i $hci -b $mac --char-write-req -a 0x33 -n A01F && gatttool -i $hci -b $mac --char-read -a 0x35 2>&1 /dev/null'" if($sshHost ne 'none' and $gattCmd eq 'write' and AttrVal($name,"model","none") eq 'flowerSens'); @@ -488,8 +517,8 @@ sub XiaomiBTLESens_ExecGatttool_Run($) { Log3 $name, 4, "XiaomiBTLESens ($name) - ExecGatttool_Run: gatttool result ".join(",", @gtResult); - $handle = '0x35' if($sshHost ne 'none' and $gattCmd eq 'write'); - $gattCmd = 'read' if($sshHost ne 'none' and $gattCmd eq 'write'); + $handle = '0x35' if($sshHost ne 'none' and $gattCmd eq 'write' and AttrVal($name,'model','none') eq 'flowerSens'); + $gattCmd = 'read' if($sshHost ne 'none' and $gattCmd eq 'write' and AttrVal($name,'model','none') eq 'flowerSens'); $gtResult[1] = 'no data response' unless( defined($gtResult[1]) ); @@ -533,12 +562,12 @@ sub XiaomiBTLESens_ExecGatttool_Done($) { } - if( ($respstate eq 'ok' and $gattCmd eq 'read') or ($respstate eq 'ok' and $gattCmd eq 'write' and AttrVal($name,'model','none') eq 'thermoHygroSens') ) { - XiaomiBTLESens_ProcessingNotification($hash,$handle,$decode_json->{gtResult}); - - } elsif( $respstate eq 'ok' and $gattCmd eq 'write' and AttrVal($name,'model','none') ne 'thermoHygroSens' ) { + if( $respstate eq 'ok' and $gattCmd eq 'write' and AttrVal($name,'model','none') eq 'flowerSens' ) { XiaomiBTLESens_ReadSensData($hash); + } elsif( $respstate eq 'ok' ) { + XiaomiBTLESens_ProcessingNotification($hash,$handle,$decode_json->{gtResult}); + } else { XiaomiBTLESens_ProcessingErrors($hash,$decode_json->{gtResult}); } @@ -603,8 +632,16 @@ sub XiaomiBTLESens_ProcessingNotification($@) { $readings = XiaomiBTLESens_ThermoHygroSensHandle0x24($hash,$notification) } + + elsif( $handle eq '0x3' ) { + ### Thermo/Hygro Sens - Read and Write Devicename + Log3 $name, 4, "XiaomiBTLESens ($name) - ProcessingNotification: handle 0x3"; + + $readings = XiaomiBTLESens_ThermoHygroSensHandle0x3($hash,$notification) + } } + XiaomiBTLESens_WriteReadings($hash,$readings); } @@ -701,8 +738,6 @@ sub XiaomiBTLESens_ThermoHygroSensHandle0x10($$) { $notification =~ s/\s+//g; # 54 3d 31 37 2e 33 20 48 3d 35 32 2e 35 00 - #my $temp = pack('H*',join(' ',substr($notification,4,8))); # 31 37 2e 33 - #my $hum = pack('H*',join(' ',substr($notification,18,8))); # 35 32 2e 35 my $temp = pack('H*',substr($notification,4,8)); # 31 37 2e 33 my $hum = pack('H*',substr($notification,18,8)); # 35 32 2e 35 @@ -733,6 +768,26 @@ sub XiaomiBTLESens_ThermoHygroSensHandle0x24($$) { return \%readings; } +sub XiaomiBTLESens_ThermoHygroSensHandle0x3($$) { + ### Thermo/Hygro Sens - Read and Write Devicename + my ($hash,$notification) = @_; + + my $name = $hash->{NAME}; + my %readings; + + + Log3 $name, 5, "XiaomiBTLESens ($name) - Thermo/Hygro Sens Handle0x24"; + + $notification =~ s/\s+//g; + + my $devname = pack('H*',$notification); + + $readings{'devicename'} = $devname; + + $hash->{helper}{CallBattery} = 0; + return \%readings; +} + sub XiaomiBTLESens_WriteReadings($$) { my ($hash,$readings) = @_; @@ -756,9 +811,6 @@ sub XiaomiBTLESens_WriteReadings($$) { DoTrigger($name, 'minFertility ' . ($readings->{fertility}{fertility}>AttrVal($name,'maxFertility',0)?'high':'ok')) if( AttrVal($name,'maxFertility','none') ne 'none' ); - DoTrigger($name, 'minTemp ' . ($readings->{temperature}{temperature}>AttrVal($name,'maxTemp',0)?'high':'ok')) if( AttrVal($name,'maxTemp','none') ne 'none' ); - DoTrigger($name, 'minMoisture ' . ($readings->{moisture}{moisture}>AttrVal($name,'maxMoisture',0)?'high':'ok')) if( AttrVal($name,'maxMoisture','none') ne 'none' ); @@ -767,9 +819,14 @@ sub XiaomiBTLESens_WriteReadings($$) { } } + if( defined($readings->{temperature}) ) { + DoTrigger($name, 'minTemp ' . ($readings->{temperature}{temperature}>AttrVal($name,'maxTemp',0)?'high':'ok')) if( AttrVal($name,'maxTemp','none') ne 'none' ); + } + Log3 $name, 4, "XiaomiBTLESens ($name) - WriteReadings: Readings were written"; $hash->{helper}{CallSensDataCounter} = 0; @@ -834,6 +891,16 @@ sub XiaomiBTLESens_CallBattery_IsUpdateTimeAgeToOld($$) { return (XiaomiBTLESens_CallBattery_UpdateTimeAge($hash)>$maxAge ? 1:0); } +sub XiaomiBTLESens_CreateDevicenameHEX($) { + + my $devicename = shift; + + my $devicenameHex = unpack("H*", $devicename); + + + return $devicenameHex; +} + @@ -848,15 +915,15 @@ sub XiaomiBTLESens_CallBattery_IsUpdateTimeAgeToOld($$) { =pod =item device -=item summary Modul to retrieves data from a Xiaomi Flower Monitor -=item summary_DE Modul um Daten vom Xiaomi Flower Monitor aus zu lesen +=item summary Modul to retrieves data from a Xiaomi BTLE Sensor +=item summary_DE Modul um Daten vom Xiaomi BTLE Sensor aus zu lesen =begin html -

Xiaomi Flower Monitor

+

Xiaomi BTLE Sensor

    - XiaomiBTLESens - Retrieves data from a Xiaomi Flower Monitor + XiaomiBTLESens - Retrieves data from a Xiaomi BTLE Sensor
    With this module it is possible to read the data from a sensor and to set it as reading.
    Gatttool and hcitool is required to use this modul. (apt-get install bluez) @@ -872,7 +939,7 @@ sub XiaomiBTLESens_CallBattery_IsUpdateTimeAgeToOld($$) {

This statement creates a XiaomiBTLESens with the name Weihnachtskaktus and the Bluetooth Mac C4:7C:8D:62:42:6F.
- After the device has been created, the current data of the Xiaomi Flower Monitor is automatically read from the device. + After the device has been created, the current data of the Xiaomi BTLE Sensor is automatically read from the device.

@@ -891,14 +958,14 @@ sub XiaomiBTLESens_CallBattery_IsUpdateTimeAgeToOld($$) { Set
    -
  • clearFirmwareReading - clear firmware reading for new begin.
  • +



Get
    -
  • statusRequest - retrieves the current state of the Xiaomi Flower Monitor.
  • +
  • sensorData - retrieves the current data of the Xiaomi sensor



@@ -930,9 +997,9 @@ sub XiaomiBTLESens_CallBattery_IsUpdateTimeAgeToOld($$) { =begin html_DE -

Xiaomi Flower Monitor

+

Xiaomi BTLE Sensor

    - XiaomiBTLESens - liest Daten von einem Xiaomi Flower Monitor + XiaomiBTLESens - liest Daten von einem Xiaomi BTLE Sensor
    Dieser Modul liest Daten von einem Sensor und legt sie in den Readings ab.
    Auf dem (Linux) FHEM-Server werden gatttool und hcitool vorausgesetzt. (sudo apt install bluez) @@ -948,17 +1015,17 @@ sub XiaomiBTLESens_CallBattery_IsUpdateTimeAgeToOld($$) {

Der Befehl legt ein Device vom Typ XiaomiBTLESens an mit dem Namen Weihnachtskaktus und der Bluetooth MAC C4:7C:8D:62:42:6F.
- Nach dem Anlegen des Device werden umgehend und automatisch die aktuellen Daten vom betroffenen Xiaomi Flower Monitor gelesen. + Nach dem Anlegen des Device werden umgehend und automatisch die aktuellen Daten vom betroffenen Xiaomi BTLE Sensor gelesen.

Readings
    -
  • state - Status des Flower Monitor oder eine Fehlermeldung falls Fehler beim letzten Kontakt auftraten.
  • +
  • state - Status des BTLE Sensor oder eine Fehlermeldung falls Fehler beim letzten Kontakt auftraten.
  • battery - aktueller Batterie-Status in Abhängigkeit vom Wert batteryLevel.
  • batteryLevel - aktueller Ladestand der Batterie in Prozent.
  • fertility - Wert des Fruchtbarkeitssensors (Bodenleitfähigkeit)
  • -
  • firmware - aktuelle Firmware-Version des Flower Monitor
  • +
  • firmware - aktuelle Firmware-Version des BTLE Sensor
  • lux - aktuelle Lichtintensität
  • moisture - aktueller Feuchtigkeitswert
  • temperature - aktuelle Temperatur
  • @@ -967,14 +1034,14 @@ sub XiaomiBTLESens_CallBattery_IsUpdateTimeAgeToOld($$) { Set
      -
    • clearFirmwareReading - löscht das Reading firmware für/nach Upgrade
    • +



    Get
      -
    • statusRequest - aktive Abfrage des aktuellen Status des Xiaomi Flower Monitor und seiner Werte
    • +
    • sensorData - aktive Abfrage der Sensors Werte