2018-01-09 06:52:50 +00:00
###############################################################################
2018-10-18 12:31:00 +00:00
#
2022-01-15 19:43:08 +00:00
# Developed with VSCodium and richterger perl plugin.
2018-01-09 06:52:50 +00:00
#
2022-01-15 19:43:08 +00:00
# (c) 2017-2022 Copyright: Marko Oldenburg (fhemdevelopment at cooltux dot net)
2018-01-09 06:52:50 +00:00
# All rights reserved
#
2020-01-01 20:04:17 +00:00
# Special thanks goes to:
# - Charlie71: add special patch
2020-05-10 17:40:59 +00:00
# - Holger S: patch to support Mijia LYWSD03MMC devices
2020-01-01 20:04:17 +00:00
#
#
2018-01-09 06:52:50 +00:00
# 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.
#
#
# $Id$
#
###############################################################################
#$cmd = "qx(gatttool -i $hci -b $mac --char-write-req -a 0x33 -n A01F";
#$cmd = "qx(gatttool -i $hci -b $mac --char-read -a 0x35"; # Sensor Daten
#$cmd = "qx(gatttool -i $hci -b $mac --char-read -a 0x38"; # Firmware und Batterie
# e8 00 00 58 0f 00 00 34 f1 02 02 3c 00 fb 34 9b
2019-07-02 10:13:05 +00:00
package FHEM::XiaomiBTLESens ;
2018-10-18 12:11:12 +00:00
use strict ;
use warnings ;
2024-10-12 06:30:33 +00:00
# use experimental qw /switch/; deprecated
2022-01-15 19:43:08 +00:00
2018-10-18 12:11:12 +00:00
use POSIX ;
2019-06-05 08:49:04 +00:00
use FHEM::Meta ;
2018-10-18 12:11:12 +00:00
2019-07-02 10:13:05 +00:00
use GPUtils qw( GP_Import GP_Export ) ;
2018-10-18 12:11:12 +00:00
2022-01-15 19:43:08 +00:00
my $ missingModul = q{ } ;
2019-07-02 09:46:31 +00:00
# try to use JSON::MaybeXS wrapper
# for chance of better performance + open code
eval {
require JSON::MaybeXS ;
import JSON:: MaybeXS qw( decode_json encode_json ) ;
1 ;
2022-01-15 19:43:08 +00:00
} or do {
2019-07-02 09:46:31 +00:00
# try to use JSON wrapper
# for chance of better performance
eval {
# JSON preference order
local $ ENV { PERL_JSON_BACKEND } =
'Cpanel::JSON::XS,JSON::XS,JSON::PP,JSON::backportPP'
unless ( defined ( $ ENV { PERL_JSON_BACKEND } ) ) ;
require JSON ;
import JSON qw( decode_json encode_json ) ;
1 ;
2022-01-15 19:43:08 +00:00
} or do {
2019-07-02 09:46:31 +00:00
# In rare cases, Cpanel::JSON::XS may
# be installed but JSON|JSON::MaybeXS not ...
eval {
require Cpanel::JSON::XS ;
import Cpanel::JSON:: XS qw( decode_json encode_json ) ;
1 ;
2022-01-15 19:43:08 +00:00
} or do {
2019-07-02 09:46:31 +00:00
# In rare cases, JSON::XS may
# be installed but JSON not ...
eval {
require JSON::XS ;
import JSON:: XS qw( decode_json encode_json ) ;
1 ;
2022-01-15 19:43:08 +00:00
} or do {
2019-07-02 09:46:31 +00:00
# Fallback to built-in JSON which SHOULD
# be available since 5.014 ...
eval {
require JSON::PP ;
import JSON:: PP qw( decode_json encode_json ) ;
1 ;
2022-01-15 19:43:08 +00:00
} or do {
2019-07-02 09:46:31 +00:00
# Fallback to JSON::backportPP in really rare cases
require JSON::backportPP ;
import JSON:: backportPP qw( decode_json encode_json ) ;
1 ;
2022-01-15 19:43:08 +00:00
} ;
} ;
} ;
} ;
} ;
2019-07-02 09:46:31 +00:00
2022-01-15 19:43:08 +00:00
eval { require Blocking ; 1 } or $ missingModul . = "Blocking " ;
2018-10-18 12:31:00 +00:00
2018-10-18 12:11:12 +00:00
#use Data::Dumper; only for Debugging
## Import der FHEM Funktionen
2019-07-02 10:13:05 +00:00
#-- Run before package compilation
2018-10-18 12:11:12 +00:00
BEGIN {
2019-07-02 10:13:05 +00:00
# Import from main context
2018-10-18 12:11:12 +00:00
GP_Import (
qw( readingsSingleUpdate
readingsBulkUpdate
readingsBulkUpdateIfChanged
readingsBeginUpdate
readingsEndUpdate
defs
modules
Log3
CommandAttr
AttrVal
ReadingsVal
IsDisabled
deviceEvents
init_done
gettimeofday
InternalTimer
RemoveInternalTimer
DoTrigger
BlockingKill
2018-10-19 02:09:54 +00:00
BlockingCall
2019-02-15 08:41:01 +00:00
FmtDateTime
2019-07-02 09:51:40 +00:00
readingFnAttributes
2019-02-15 08:41:01 +00:00
makeDeviceName )
2018-10-18 12:11:12 +00:00
) ;
}
my % XiaomiModels = (
2018-10-18 12:31:00 +00:00
flowerSens = > {
'rdata' = > '0x35' ,
'wdata' = > '0x33' ,
'wdataValue' = > 'A01F' ,
'wdatalisten' = > 0 ,
'battery' = > '0x38' ,
'firmware' = > '0x38'
} ,
thermoHygroSens = > {
'wdata' = > '0x10' ,
'wdataValue' = > '0100' ,
'wdatalisten' = > 1 ,
'battery' = > '0x18' ,
'firmware' = > '0x24' ,
'devicename' = > '0x3'
} ,
2019-10-11 09:37:58 +00:00
clearGrassSens = > {
'rdata' = > '0x1e' ,
'wdata' = > '0x10' ,
'wdataValue' = > '0100' ,
'wdatalisten' = > 2 ,
2019-10-12 06:43:31 +00:00
'battery' = > '0x3b' ,
2019-10-11 09:37:58 +00:00
'firmware' = > '0x2a' ,
'devicename' = > '0x3'
} ,
2020-05-10 13:07:35 +00:00
mijiaLYWSD03MMC = > {
'wdata' = > '0x38' ,
'wdataValue' = > '0100' ,
'wdatalisten' = > 3 ,
'battery' = > '0x1b' ,
'firmware' = > '0x12' ,
'devicename' = > '0x3'
} ,
2018-10-18 12:31:00 +00:00
) ;
my % CallBatteryAge = (
'8h' = > 28800 ,
'16h' = > 57600 ,
'24h' = > 86400 ,
'32h' = > 115200 ,
'40h' = > 144000 ,
'48h' = > 172800
) ;
2022-01-15 19:43:08 +00:00
sub ::XiaomiBTLESens_Initialize { goto & Initialize }
2020-04-24 13:56:10 +00:00
sub Initialize {
my $ hash = shift ;
2019-07-02 09:46:31 +00:00
2020-04-24 13:56:10 +00:00
$ hash - > { SetFn } = \ & Set ;
$ hash - > { GetFn } = \ & Get ;
$ hash - > { DefFn } = \ & Define ;
$ hash - > { NotifyFn } = \ & Notify ;
$ hash - > { UndefFn } = \ & Undef ;
$ hash - > { AttrFn } = \ & Attr ;
2019-07-02 09:46:31 +00:00
$ hash - > { AttrList } =
2020-05-10 12:25:53 +00:00
'interval '
. 'disable:1 '
. 'disabledForIntervals '
. 'hciDevice:hci0,hci1,hci2 '
. 'batteryFirmwareAge:8h,16h,24h,32h,40h,48h '
. 'minFertility '
. 'maxFertility '
. 'minTemp '
. 'maxTemp '
. 'minMoisture '
. 'maxMoisture '
. 'minLux '
. 'maxLux '
. 'sshHost '
. 'psCommand '
2020-05-10 13:07:35 +00:00
. 'model:flowerSens,thermoHygroSens,clearGrassSens,mijiaLYWSD03MMC '
2020-05-10 12:25:53 +00:00
. 'blockingCallLoglevel:2,3,4,5 '
2019-07-02 09:46:31 +00:00
. $ readingFnAttributes ;
2020-04-24 13:56:10 +00:00
$ hash - > { parseParams } = 1 ;
2019-07-02 09:47:13 +00:00
2019-07-02 09:46:31 +00:00
return FHEM::Meta:: InitMod ( __FILE__ , $ hash ) ;
}
2020-04-24 13:56:10 +00:00
sub Define {
2020-05-10 12:30:44 +00:00
my $ hash = shift ;
my $ arg_ref = shift ;
2022-01-15 19:43:08 +00:00
my $ version ;
return $@ unless ( FHEM::Meta:: SetInternals ( $ hash ) ) ;
2018-10-18 12:31:00 +00:00
2022-01-15 19:43:08 +00:00
$ version = FHEM::Meta:: Get ( $ hash , 'version' ) ;
our $ VERSION = $ version ;
2019-07-02 09:47:13 +00:00
2020-05-10 12:25:53 +00:00
return 'too few parameters: define <name> XiaomiBTLESens <BTMAC>'
2020-05-10 12:30:44 +00:00
if ( scalar ( @ { $ arg_ref } ) != 3 ) ;
2018-10-18 12:31:00 +00:00
return
"Cannot define XiaomiBTLESens device. Perl modul ${missingModul}is missing."
if ( $ missingModul ) ;
2020-05-10 12:30:44 +00:00
my $ name = $ arg_ref - > [ 0 ] ;
my $ mac = $ arg_ref - > [ 2 ] ;
2018-10-18 12:31:00 +00:00
2019-07-02 09:47:13 +00:00
$ hash - > { BTMAC } = $ mac ;
$ hash - > { VERSION } = version - > parse ( $ VERSION ) - > normal ;
$ hash - > { INTERVAL } = 300 ;
$ hash - > { helper } { CallSensDataCounter } = 0 ;
$ hash - > { helper } { CallBattery } = 0 ;
2020-05-10 12:25:53 +00:00
$ hash - > { NOTIFYDEV } = 'global,' . $ name ;
2019-07-02 09:47:13 +00:00
$ hash - > { loglevel } = 4 ;
2018-10-18 12:31:00 +00:00
2020-05-10 12:25:53 +00:00
readingsSingleUpdate ( $ hash , 'state' , 'initialized' , 0 ) ;
2018-10-18 12:31:00 +00:00
CommandAttr ( undef , $ name . ' room XiaomiBTLESens' )
if ( AttrVal ( $ name , 'room' , 'none' ) eq 'none' ) ;
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 3 ,
"XiaomiBTLESens ($name) - defined with BTMAC $hash->{BTMAC}" ) ;
2018-10-18 12:31:00 +00:00
$ modules { XiaomiBTLESens } { defptr } { $ hash - > { BTMAC } } = $ hash ;
2018-01-09 06:52:50 +00:00
2020-04-24 13:56:10 +00:00
return ;
}
2018-01-09 06:52:50 +00:00
2020-04-24 13:56:10 +00:00
sub Undef {
my $ hash = shift ;
my $ arg = shift ;
2018-10-18 12:31:00 +00:00
my $ mac = $ hash - > { BTMAC } ;
2018-01-09 06:52:50 +00:00
my $ name = $ hash - > { NAME } ;
2018-10-18 12:31:00 +00:00
2018-01-09 06:52:50 +00:00
RemoveInternalTimer ( $ hash ) ;
2018-10-18 12:31:00 +00:00
BlockingKill ( $ hash - > { helper } { RUNNING_PID } )
if ( defined ( $ hash - > { helper } { RUNNING_PID } ) ) ;
delete ( $ modules { XiaomiBTLESens } { defptr } { $ mac } ) ;
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 3 , "Sub XiaomiBTLESens_Undef ($name) - delete device $name" ) ;
2018-01-09 06:52:50 +00:00
2020-04-24 13:56:10 +00:00
return ;
}
2018-01-09 06:52:50 +00:00
2020-04-24 13:56:10 +00:00
sub Attr {
2018-01-09 06:52:50 +00:00
my ( $ cmd , $ name , $ attrName , $ attrVal ) = @ _ ;
2018-10-18 12:31:00 +00:00
my $ hash = $ defs { $ name } ;
2018-01-09 06:52:50 +00:00
2024-10-12 06:30:33 +00:00
if ( $ attrName eq 'disable' ) {
if ( $ cmd eq 'set' && $ attrVal == 1 ) {
RemoveInternalTimer ( $ hash ) ;
2018-10-18 12:31:00 +00:00
2024-10-12 06:30:33 +00:00
readingsSingleUpdate ( $ hash , 'state' , 'disabled' , 1 ) ;
Log3 ( $ name , 3 , "XiaomiBTLESens ($name) - disabled" ) ;
}
2018-01-09 06:52:50 +00:00
2024-10-12 06:30:33 +00:00
elsif ( $ cmd eq 'del' ) {
Log3 ( $ name , 3 , "XiaomiBTLESens ($name) - enabled" ) ;
2018-01-09 06:52:50 +00:00
}
2024-10-12 06:30:33 +00:00
}
elsif ( $ attrName eq 'disabledForIntervals' ) {
if ( $ cmd eq 'set' ) {
## no critic (Only use a capturing group if you plan to use the captured value)
return
2020-05-10 12:25:53 +00:00
'check disabledForIntervals Syntax HH:MM-HH:MM or HH:MM-HH:MM HH:MM-HH:MM ...'
2024-10-12 06:30:33 +00:00
if ( $ attrVal !~ /^((\d{2}:\d{2})-(\d{2}:\d{2})\s?)+$/x ) ;
## use critic
Log3 ( $ name , 3 , "XiaomiBTLESens ($name) - disabledForIntervals" ) ;
stateRequest ( $ hash ) ;
2018-01-09 06:52:50 +00:00
}
2018-10-18 12:31:00 +00:00
2024-10-12 06:30:33 +00:00
elsif ( $ cmd eq 'del' ) {
Log3 ( $ name , 3 , "XiaomiBTLESens ($name) - enabled" ) ;
readingsSingleUpdate ( $ hash , 'state' , 'active' , 1 ) ;
}
}
elsif ( $ attrName eq 'interval' ) {
RemoveInternalTimer ( $ hash ) ;
2018-10-18 12:31:00 +00:00
2024-10-12 06:30:33 +00:00
if ( $ cmd eq 'set' ) {
if ( $ attrVal < 120 ) {
Log3 ( $ name , 3 ,
2020-04-24 13:56:10 +00:00
"XiaomiBTLESens ($name) - interval too small, please use something >= 120 (sec), default is 300 (sec)"
2024-10-12 06:30:33 +00:00
) ;
return
2020-05-10 12:25:53 +00:00
'interval too small, please use something >= 120 (sec), default is 300 (sec)' ;
2018-10-18 12:31:00 +00:00
}
2024-10-12 06:30:33 +00:00
else {
$ hash - > { INTERVAL } = $ attrVal ;
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 3 ,
2024-10-12 06:30:33 +00:00
"XiaomiBTLESens ($name) - set interval to $attrVal" ) ;
2018-01-09 06:52:50 +00:00
}
}
2024-10-12 06:30:33 +00:00
elsif ( $ cmd eq 'del' ) {
$ hash - > { INTERVAL } = 300 ;
Log3 ( $ name , 3 ,
"XiaomiBTLESens ($name) - set interval to default" ) ;
}
}
elsif ( $ attrName eq 'blockingCallLoglevel' ) {
if ( $ cmd eq 'set' ) {
$ hash - > { loglevel } = $ attrVal ;
Log3 ( $ name , 3 ,
"XiaomiBTLESens ($name) - set blockingCallLoglevel to $attrVal"
) ;
}
2018-01-09 06:52:50 +00:00
2024-10-12 06:30:33 +00:00
elsif ( $ cmd eq 'del' ) {
$ hash - > { loglevel } = 4 ;
Log3 ( $ name , 3 ,
"XiaomiBTLESens ($name) - set blockingCallLoglevel to default"
) ;
2018-01-09 06:52:50 +00:00
}
}
2018-10-18 12:31:00 +00:00
2020-04-24 13:56:10 +00:00
return ;
2018-01-09 06:52:50 +00:00
}
2020-04-24 13:56:10 +00:00
sub Notify {
my $ hash = shift ;
my $ dev = shift ;
2018-01-09 06:52:50 +00:00
my $ name = $ hash - > { NAME } ;
2018-10-18 12:31:00 +00:00
return stateRequestTimer ( $ hash ) if ( IsDisabled ( $ name ) ) ;
2018-01-09 06:52:50 +00:00
my $ devname = $ dev - > { NAME } ;
my $ devtype = $ dev - > { TYPE } ;
2018-10-18 12:31:00 +00:00
my $ events = deviceEvents ( $ dev , 1 ) ;
return if ( ! $ events ) ;
stateRequestTimer ( $ hash )
if (
(
(
(
2022-01-15 19:43:08 +00:00
grep { /^DEFINED.$name$/x } @ { $ events }
or grep { /^DELETEATTR.$name.disable$/x } @ { $ events }
or grep { /^ATTR.$name.disable.0$/x } @ { $ events }
or grep { /^DELETEATTR.$name.interval$/x } @ { $ events }
or grep { /^DELETEATTR.$name.model$/x } @ { $ events }
or grep { /^ATTR.$name.model.+/x } @ { $ events }
or grep { /^ATTR.$name.interval.[0-9]+/x } @ { $ events }
2018-10-18 12:31:00 +00:00
)
2020-05-10 12:25:53 +00:00
&& $ devname eq 'global'
2018-10-18 12:31:00 +00:00
)
2022-01-15 19:43:08 +00:00
or grep { /^resetBatteryTimestamp$/x } @ { $ events }
2018-10-18 12:31:00 +00:00
)
2020-05-10 12:25:53 +00:00
&& $ init_done
|| (
2018-10-18 12:31:00 +00:00
(
2022-01-15 19:43:08 +00:00
grep { /^INITIALIZED$/x } @ { $ events }
or grep { /^REREADCFG$/x } @ { $ events }
or grep { /^MODIFIED.$name$/x } @ { $ events }
2018-10-18 12:31:00 +00:00
)
2020-05-10 12:25:53 +00:00
&& $ devname eq 'global'
2018-10-18 12:31:00 +00:00
)
) ;
CreateParamGatttool ( $ hash , 'read' ,
$ XiaomiModels { AttrVal ( $ name , 'model' , '' ) } { devicename } )
if (
2020-05-10 13:07:35 +00:00
(
AttrVal ( $ name , 'model' , 'thermoHygroSens' ) eq 'thermoHygroSens'
2024-10-12 06:30:33 +00:00
|| AttrVal ( $ name , 'model' , 'mijiaLYWSD03MMC' ) eq
'mijiaLYWSD03MMC'
2020-05-10 13:07:35 +00:00
)
2020-05-10 12:25:53 +00:00
&& $ devname eq $ name
2022-01-15 19:43:08 +00:00
&& grep { /^$name.firmware.+/x } @ { $ events }
2018-10-18 12:31:00 +00:00
) ;
2018-01-16 14:14:52 +00:00
2018-01-09 06:52:50 +00:00
return ;
}
2020-04-24 13:56:10 +00:00
sub stateRequest {
my $ hash = shift ;
2018-01-09 06:52:50 +00:00
2018-10-18 12:31:00 +00:00
my $ name = $ hash - > { NAME } ;
2018-01-09 06:52:50 +00:00
my % readings ;
2018-10-18 12:31:00 +00:00
if ( AttrVal ( $ name , 'model' , 'none' ) eq 'none' ) {
2020-05-10 12:25:53 +00:00
readingsSingleUpdate ( $ hash , 'state' , 'set attribute model first' , 1 ) ;
2018-10-18 12:31:00 +00:00
}
elsif ( ! IsDisabled ( $ name ) ) {
if ( ReadingsVal ( $ name , 'firmware' , 'none' ) ne 'none' ) {
return CreateParamGatttool ( $ hash , 'read' ,
$ XiaomiModels { AttrVal ( $ name , 'model' , '' ) } { battery } )
if (
CallBattery_IsUpdateTimeAgeToOld (
$ hash ,
2024-10-12 06:30:33 +00:00
$ CallBatteryAge {
AttrVal ( $ name , 'BatteryFirmwareAge' , '24h' )
}
2018-10-18 12:31:00 +00:00
)
) ;
2019-10-11 10:25:37 +00:00
if ( $ hash - > { helper } { CallSensDataCounter } < 1
2020-05-10 12:25:53 +00:00
&& AttrVal ( $ name , 'model' , '' ) ne 'clearGrassSens' )
2019-10-11 09:37:58 +00:00
{
2018-10-18 12:31:00 +00:00
CreateParamGatttool (
$ hash ,
'write' ,
$ XiaomiModels { AttrVal ( $ name , 'model' , '' ) } { wdata } ,
$ XiaomiModels { AttrVal ( $ name , 'model' , '' ) } { wdataValue }
) ;
$ hash - > { helper } { CallSensDataCounter } =
$ hash - > { helper } { CallSensDataCounter } + 1 ;
}
2019-10-11 10:25:37 +00:00
elsif ( $ hash - > { helper } { CallSensDataCounter } < 1
2020-05-10 12:25:53 +00:00
&& AttrVal ( $ name , 'model' , '' ) eq 'clearGrassSens' )
2019-10-11 09:37:58 +00:00
{
2019-10-11 10:25:37 +00:00
CreateParamGatttool ( $ hash , 'read' ,
2019-10-11 09:37:58 +00:00
$ XiaomiModels { AttrVal ( $ name , 'model' , '' ) } { rdata } ,
) ;
$ hash - > { helper } { CallSensDataCounter } =
$ hash - > { helper } { CallSensDataCounter } + 1 ;
}
2018-10-18 12:31:00 +00:00
else {
2018-01-13 19:24:13 +00:00
$ readings { 'lastGattError' } = 'charWrite faild' ;
2018-10-18 12:31:00 +00:00
WriteReadings ( $ hash , \ % readings ) ;
2018-01-13 19:24:13 +00:00
$ hash - > { helper } { CallSensDataCounter } = 0 ;
return ;
2018-01-09 06:52:50 +00:00
}
}
2018-10-18 12:31:00 +00:00
else {
2018-01-16 11:52:48 +00:00
2018-10-18 12:31:00 +00:00
CreateParamGatttool ( $ hash , 'read' ,
$ XiaomiModels { AttrVal ( $ name , 'model' , '' ) } { firmware } ) ;
}
}
else {
2020-05-10 12:25:53 +00:00
readingsSingleUpdate ( $ hash , 'state' , 'disabled' , 1 ) ;
2018-01-09 06:52:50 +00:00
}
2020-04-24 13:56:10 +00:00
return ;
}
2018-01-09 06:52:50 +00:00
2020-04-24 13:56:10 +00:00
sub stateRequestTimer {
my $ hash = shift ;
2018-01-09 06:52:50 +00:00
2018-10-18 12:31:00 +00:00
my $ name = $ hash - > { NAME } ;
2018-01-20 05:57:13 +00:00
2018-05-08 15:25:22 +00:00
RemoveInternalTimer ( $ hash ) ;
2018-10-18 12:11:12 +00:00
stateRequest ( $ hash ) ;
2018-01-20 05:57:13 +00:00
2018-10-18 12:31:00 +00:00
InternalTimer ( gettimeofday ( ) + $ hash - > { INTERVAL } + int ( rand ( 300 ) ) ,
2022-01-15 19:43:08 +00:00
\ & FHEM::XiaomiBTLESens:: stateRequestTimer , $ hash ) ;
2018-10-18 12:31:00 +00:00
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 4 ,
"XiaomiBTLESens ($name) - stateRequestTimer: Call Request Timer" ) ;
return ;
2018-01-09 06:52:50 +00:00
}
2022-01-15 19:43:08 +00:00
sub Set {
2020-05-10 13:07:35 +00:00
my $ hash = shift ;
my $ arg_ref = shift ;
my $ name = shift @$ arg_ref ;
my $ cmd = shift @$ arg_ref
// return qq{ "set $name" needs at least one argument } ;
2018-01-09 06:52:50 +00:00
2018-01-16 11:52:48 +00:00
my $ mod ;
my $ handle ;
2018-10-18 12:31:00 +00:00
my $ value = 'write' ;
if ( $ cmd eq 'devicename' ) {
2020-05-10 12:30:44 +00:00
return 'usage: devicename <name>' if ( scalar ( @ { $ arg_ref } ) < 1 ) ;
2018-01-13 19:24:13 +00:00
2024-10-12 06:30:33 +00:00
$ mod = 'write' ;
$ handle =
$ XiaomiModels { AttrVal ( $ name , 'model' , '' ) } { devicename } ;
$ value = CreateDevicenameHEX ( makeDeviceName ( $ arg_ref - > [ 0 ] ) ) ;
2018-10-18 12:31:00 +00:00
}
elsif ( $ cmd eq 'resetBatteryTimestamp' ) {
2024-10-12 06:30:33 +00:00
return 'usage: resetBatteryTimestamp'
if ( scalar ( @ { $ arg_ref } ) != 0 ) ;
2018-10-18 12:31:00 +00:00
$ hash - > { helper } { updateTimeCallBattery } = 0 ;
2018-03-17 11:07:33 +00:00
return ;
2018-10-18 12:31:00 +00:00
}
else {
2020-05-10 12:25:53 +00:00
my $ list = q{ } ;
$ list . = 'resetBatteryTimestamp:noArg'
if ( AttrVal ( $ name , 'model' , 'none' ) ne 'none' ) ;
$ list . = ' devicename'
2018-10-18 12:31:00 +00:00
if (
2020-05-10 13:07:35 +00:00
(
AttrVal ( $ name , 'model' , 'thermoHygroSens' ) eq
'thermoHygroSens'
|| AttrVal ( $ name , 'model' , 'mijiaLYWSD03MMC' ) eq
'mijiaLYWSD03MMC'
)
&& AttrVal ( $ name , 'model' , 'none' ) ne 'none'
) ;
2018-10-18 12:31:00 +00:00
2018-01-09 06:52:50 +00:00
return "Unknown argument $cmd, choose one of $list" ;
}
2018-10-18 12:31:00 +00:00
CreateParamGatttool ( $ hash , $ mod , $ handle , $ value ) ;
2020-04-24 13:56:10 +00:00
return ;
2018-01-09 06:52:50 +00:00
}
2020-04-24 13:56:10 +00:00
sub Get {
2020-05-10 13:07:35 +00:00
my $ hash = shift ;
my $ arg_ref = shift ;
my $ name = shift @$ arg_ref ;
my $ cmd = shift @$ arg_ref
// return qq{ "set $name" needs at least one argument } ;
2018-10-18 12:31:00 +00:00
my $ mod = 'read' ;
2018-01-16 11:52:48 +00:00
my $ handle ;
2018-10-18 12:31:00 +00:00
if ( $ cmd eq 'sensorData' ) {
2020-05-10 12:30:44 +00:00
return 'usage: sensorData' if ( scalar ( @ { $ arg_ref } ) != 0 ) ;
2018-01-09 06:52:50 +00:00
2018-10-18 12:11:12 +00:00
stateRequest ( $ hash ) ;
2018-10-18 12:31:00 +00:00
}
elsif ( $ cmd eq 'firmware' ) {
2020-05-10 12:30:44 +00:00
return 'usage: firmware' if ( scalar ( @ { $ arg_ref } ) != 0 ) ;
2018-10-18 12:31:00 +00:00
2022-01-15 19:43:08 +00:00
$ mod = 'read' ;
2018-10-18 12:31:00 +00:00
$ handle = $ XiaomiModels { AttrVal ( $ name , 'model' , '' ) } { firmware } ;
}
elsif ( $ cmd eq 'devicename' ) {
2020-05-10 12:30:44 +00:00
return "usage: devicename" if ( scalar ( @ { $ arg_ref } ) != 0 ) ;
2018-10-18 12:31:00 +00:00
2024-10-12 06:30:33 +00:00
$ mod = 'read' ;
$ handle =
$ XiaomiModels { AttrVal ( $ name , 'model' , '' ) } { devicename } ;
2018-10-18 12:31:00 +00:00
}
else {
2020-05-10 12:25:53 +00:00
my $ list = q{ } ;
$ list . = 'sensorData:noArg firmware:noArg'
if ( AttrVal ( $ name , 'model' , 'none' ) ne 'none' ) ;
$ list . = ' devicename:noArg'
2018-10-18 12:31:00 +00:00
if (
2020-05-10 13:07:35 +00:00
(
AttrVal ( $ name , 'model' , 'thermoHygroSens' ) eq
'thermoHygroSens'
|| AttrVal ( $ name , 'model' , 'mijiaLYWSD03MMC' ) eq
'mijiaLYWSD03MMC'
)
&& AttrVal ( $ name , 'model' , 'none' ) ne 'none'
) ;
2018-01-09 06:52:50 +00:00
return "Unknown argument $cmd, choose one of $list" ;
}
2018-01-16 11:52:48 +00:00
2018-10-18 12:31:00 +00:00
CreateParamGatttool ( $ hash , $ mod , $ handle ) if ( $ cmd ne 'sensorData' ) ;
2018-01-16 11:52:48 +00:00
2020-04-24 13:56:10 +00:00
return ;
2018-01-09 06:52:50 +00:00
}
2020-04-24 13:56:10 +00:00
sub CreateParamGatttool {
2018-10-18 12:31:00 +00:00
my ( $ hash , $ mod , $ handle , $ value ) = @ _ ;
2020-04-24 13:56:10 +00:00
2018-10-18 12:31:00 +00:00
my $ name = $ hash - > { NAME } ;
my $ mac = $ hash - > { BTMAC } ;
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 4 ,
"XiaomiBTLESens ($name) - Run CreateParamGatttool with mod: $mod" ) ;
2018-01-16 11:52:48 +00:00
2018-10-18 12:31:00 +00:00
if ( $ mod eq 'read' ) {
$ hash - > { helper } { RUNNING_PID } = BlockingCall (
2020-05-10 12:25:53 +00:00
'FHEM::XiaomiBTLESens::ExecGatttool_Run' ,
$ name . '|' . $ mac . '|' . $ mod . '|' . $ handle ,
'FHEM::XiaomiBTLESens::ExecGatttool_Done' ,
2018-10-18 12:31:00 +00:00
90 ,
2020-05-10 12:25:53 +00:00
'FHEM::XiaomiBTLESens::ExecGatttool_Aborted' ,
2018-10-18 12:31:00 +00:00
$ hash
2020-05-10 12:25:53 +00:00
) if ( ! exists ( $ hash - > { helper } { RUNNING_PID } ) ) ;
2018-01-16 11:52:48 +00:00
2020-05-10 12:25:53 +00:00
readingsSingleUpdate ( $ hash , 'state' , 'read sensor data' , 1 ) ;
2018-01-16 11:52:48 +00:00
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 5 ,
"XiaomiBTLESens ($name) - Read XiaomiBTLESens_ExecGatttool_Run $name|$mac|$mod|$handle"
) ;
2018-10-18 12:31:00 +00:00
}
elsif ( $ mod eq 'write' ) {
$ hash - > { helper } { RUNNING_PID } = BlockingCall (
2020-05-10 12:25:53 +00:00
'FHEM::XiaomiBTLESens::ExecGatttool_Run' ,
$ name . '|'
. $ mac . '|'
. $ mod . '|'
. $ handle . '|'
. $ value . '|'
2018-10-18 12:31:00 +00:00
. $ XiaomiModels { AttrVal ( $ name , 'model' , '' ) } { wdatalisten } ,
2020-05-10 12:25:53 +00:00
'FHEM::XiaomiBTLESens::ExecGatttool_Done' ,
2018-10-18 12:31:00 +00:00
90 ,
2020-05-10 12:25:53 +00:00
'FHEM::XiaomiBTLESens::ExecGatttool_Aborted' ,
2018-10-18 12:31:00 +00:00
$ hash
2020-05-10 12:25:53 +00:00
) if ( ! exists ( $ hash - > { helper } { RUNNING_PID } ) ) ;
2018-10-18 12:31:00 +00:00
2020-05-10 12:25:53 +00:00
readingsSingleUpdate ( $ hash , 'state' , 'write sensor data' , 1 ) ;
2018-10-18 12:31:00 +00:00
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 5 ,
"XiaomiBTLESens ($name) - Write XiaomiBTLESens_ExecGatttool_Run $name|$mac|$mod|$handle|$value"
) ;
2018-01-16 11:52:48 +00:00
}
2020-04-24 13:56:10 +00:00
return ;
2018-01-16 11:52:48 +00:00
}
2020-04-24 13:56:10 +00:00
sub Gatttool_executeCommand {
2022-01-15 19:43:08 +00:00
my @ command = @ _ ;
## no critic (Backtick operator used . Use IPC::Open3 instead)
my $ command = join q{ } , @ command ;
2020-01-08 02:56:18 +00:00
return ( $ _ = qx{ $command 2>&1 } , $? >> 8 ) ;
2019-12-22 19:40:06 +00:00
}
2020-01-08 02:41:41 +00:00
2020-04-24 13:56:10 +00:00
sub ExecGatttool_Run {
2018-10-18 12:31:00 +00:00
my $ string = shift ;
my ( $ name , $ mac , $ gattCmd , $ handle , $ value , $ listen ) =
2020-05-11 10:32:39 +00:00
split '\|' , $ string ;
2020-05-10 12:25:53 +00:00
my $ sshHost = AttrVal ( $ name , 'sshHost' , 'none' ) ;
2018-01-09 06:52:50 +00:00
my $ gatttool ;
2018-01-16 14:14:52 +00:00
my $ json_notification ;
2018-01-09 06:52:50 +00:00
2022-01-15 19:43:08 +00:00
## no critic (Backtick operator used . Use IPC::Open3 instead)
$ gatttool = qx( which gatttool ) if ( $ sshHost eq 'none' ) ;
2018-10-18 12:31:00 +00:00
$ gatttool = qx( ssh $sshHost 'which gatttool' ) if ( $ sshHost ne 'none' ) ;
2022-01-15 19:43:08 +00:00
## use critic
2018-01-09 06:52:50 +00:00
chomp $ gatttool ;
2018-10-18 12:31:00 +00:00
2020-05-10 12:25:53 +00:00
if ( defined ( $ gatttool ) && ( $ gatttool ) ) {
2018-10-18 12:31:00 +00:00
2018-01-09 06:52:50 +00:00
my $ cmd ;
my $ loop ;
my @ gtResult ;
2022-01-15 19:43:08 +00:00
my $ wait = 1 ;
my $ hci = AttrVal ( $ name , 'hciDevice' , 'hci0' ) ;
2018-10-18 12:31:00 +00:00
2022-01-15 19:43:08 +00:00
$ cmd . = "ssh $sshHost '" if ( $ sshHost ne 'none' ) ;
$ cmd . = "timeout 10 " if ( $ listen ) ;
2018-10-18 12:31:00 +00:00
$ 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 . = " --listen" if ( $ listen ) ;
2020-01-08 02:41:41 +00:00
# $cmd .= " 2>&1 /dev/null";
2019-12-22 19:40:06 +00:00
$ cmd . = " 2>&1" ;
2020-01-08 02:41:41 +00:00
$ cmd . = "'" if ( $ sshHost ne 'none' ) ;
2018-10-18 12:31:00 +00:00
2020-01-08 02:41:41 +00:00
$ 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 '"
2020-05-10 13:07:35 +00:00
if ( $ sshHost ne 'none'
2020-05-10 12:25:53 +00:00
&& $ gattCmd eq 'write'
&& AttrVal ( $ name , 'model' , 'none' ) eq 'flowerSens' ) ;
2018-10-18 12:31:00 +00:00
while ( $ wait ) {
2018-01-09 06:52:50 +00:00
2018-10-18 12:31:00 +00:00
my $ grepGatttool ;
my $ gatttoolCmdlineStaticEscaped =
2020-01-08 02:41:41 +00:00
BTLE_CmdlinePreventGrepFalsePositive ( "gatttool -i $hci -b $mac" ) ;
my $ psCommand = AttrVal ( $ name , 'psCommand' , 'ps ax' ) ;
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 5 ,
"XiaomiBTLESens ($name) - ExecGatttool_Run: Execute Command $psCommand | grep -E $gatttoolCmdlineStaticEscaped"
) ;
2020-01-08 02:41:41 +00:00
2022-01-15 19:43:08 +00:00
## no critic (Backtick operator used . Use IPC::Open3 instead)
2020-01-08 02:41:41 +00:00
$ grepGatttool =
qx( $psCommand | grep -E \'$gatttoolCmdlineStaticEscaped\' )
2018-10-18 12:31:00 +00:00
if ( $ sshHost eq 'none' ) ;
2020-01-08 02:41:41 +00:00
$ grepGatttool =
qx( ssh $sshHost '$psCommand | grep -E "$gatttoolCmdlineStaticEscaped"' )
2018-10-18 12:31:00 +00:00
if ( $ sshHost ne 'none' ) ;
2022-01-15 19:43:08 +00:00
## use critic
2018-10-18 12:31:00 +00:00
2022-01-15 19:43:08 +00:00
if ( not $ grepGatttool =~ /^\s*$/x ) {
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 3 ,
"XiaomiBTLESens ($name) - ExecGatttool_Run: another gatttool process is running. waiting..."
) ;
2018-01-09 06:52:50 +00:00
sleep ( 1 ) ;
2018-10-18 12:31:00 +00:00
}
else {
2018-01-09 06:52:50 +00:00
$ wait = 0 ;
}
}
2018-02-10 02:08:12 +00:00
2018-01-09 06:52:50 +00:00
$ loop = 0 ;
2020-01-08 02:41:41 +00:00
my $ returnString ;
my $ returnCode = 1 ;
2018-01-09 06:52:50 +00:00
do {
2018-02-10 18:48:11 +00:00
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 5 ,
"XiaomiBTLESens ($name) - ExecGatttool_Run: call gatttool with command: $cmd and loop $loop"
) ;
2020-01-08 02:41:41 +00:00
2020-04-24 13:56:10 +00:00
( $ returnString , $ returnCode ) = Gatttool_executeCommand ( $ cmd ) ;
2022-01-15 19:43:08 +00:00
@ gtResult = split /:\s/x , $ returnString ;
2018-02-10 18:48:11 +00:00
2022-01-15 19:43:08 +00:00
Log3 (
$ name ,
5 ,
2020-04-24 13:56:10 +00:00
"XiaomiBTLESens ($name) - ExecGatttool_Run: gatttool loop result "
2022-01-15 19:43:08 +00:00
. join q{ , } ,
@ gtResult
) ;
2018-02-10 18:48:11 +00:00
2020-01-08 02:41:41 +00:00
$ returnCode = 2
2020-05-10 12:25:53 +00:00
if ( ! defined ( $ gtResult [ 0 ] ) ) ;
2018-10-18 12:31:00 +00:00
2020-01-09 03:58:46 +00:00
$ loop + + ;
2024-10-12 06:30:33 +00:00
} while ( $ loop < 5
&& ( $ returnCode != 0 && $ returnCode != 124 ) ) ;
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 3 ,
"XiaomiBTLESens ($name) - ExecGatttool_Run: errorcode: \"$returnCode\", ErrorString: \"$returnString\""
2020-05-10 12:25:53 +00:00
) if ( $ returnCode != 0 && $ returnCode != 124 ) ;
2018-10-18 12:31:00 +00:00
2022-01-15 19:43:08 +00:00
Log3 (
$ name ,
4 ,
2020-04-24 13:56:10 +00:00
"XiaomiBTLESens ($name) - ExecGatttool_Run: gatttool result "
2022-01-15 19:43:08 +00:00
. join q{ , } ,
@ gtResult
) ;
2018-02-10 18:48:11 +00:00
2018-10-18 12:31:00 +00:00
$ handle = '0x35'
2020-05-10 13:07:35 +00:00
if ( $ sshHost ne 'none'
2020-05-10 12:25:53 +00:00
&& $ gattCmd eq 'write'
&& AttrVal ( $ name , 'model' , 'none' ) eq 'flowerSens' ) ;
2018-10-18 12:31:00 +00:00
$ gattCmd = 'read'
2020-05-10 13:07:35 +00:00
if ( $ sshHost ne 'none'
2020-05-10 12:25:53 +00:00
&& $ gattCmd eq 'write'
&& AttrVal ( $ name , 'model' , 'none' ) eq 'flowerSens' ) ;
2018-01-09 06:52:50 +00:00
$ gtResult [ 1 ] = 'no data response'
2020-05-10 12:25:53 +00:00
if ( ! defined ( $ gtResult [ 1 ] ) ) ;
2018-02-10 18:48:11 +00:00
2020-05-10 12:25:53 +00:00
if ( $ gtResult [ 1 ] ne 'no data response' && $ listen ) {
2020-05-11 10:32:39 +00:00
( $ gtResult [ 1 ] ) = split '\n' , $ gtResult [ 1 ] ;
2022-01-15 19:43:08 +00:00
$ gtResult [ 1 ] =~ s/\\n//xg ;
2018-02-10 18:48:11 +00:00
}
2018-10-18 12:31:00 +00:00
$ json_notification = encodeJSON ( $ gtResult [ 1 ] ) ;
2022-01-15 19:43:08 +00:00
if ( $ gtResult [ 1 ] =~ /^([0-9a-f]{2}(\s?))*$/x ) {
2018-01-09 06:52:50 +00:00
return "$name|$mac|ok|$gattCmd|$handle|$json_notification" ;
2018-10-18 12:31:00 +00:00
}
2020-05-10 12:25:53 +00:00
elsif ( $ returnCode == 0 && $ gattCmd eq 'write' ) {
2018-10-18 12:31:00 +00:00
if ( $ sshHost ne 'none' ) {
ExecGatttool_Run ( $ name . "|" . $ mac . "|read|0x35" ) ;
}
else {
2018-01-09 06:52:50 +00:00
return "$name|$mac|ok|$gattCmd|$handle|$json_notification" ;
}
2018-10-18 12:31:00 +00:00
}
else {
2018-01-09 06:52:50 +00:00
return "$name|$mac|error|$gattCmd|$handle|$json_notification" ;
}
2018-10-18 12:31:00 +00:00
}
else {
$ json_notification = encodeJSON (
'no gatttool binary found. Please check if bluez-package is properly installed'
) ;
2018-01-16 14:14:52 +00:00
return "$name|$mac|error|$gattCmd|$handle|$json_notification" ;
2018-01-09 06:52:50 +00:00
}
2020-04-24 13:56:10 +00:00
return ;
}
2018-01-09 06:52:50 +00:00
2020-04-24 13:56:10 +00:00
sub ExecGatttool_Done {
2018-10-18 12:31:00 +00:00
my $ string = shift ;
2020-04-24 13:56:10 +00:00
2018-10-18 12:31:00 +00:00
my ( $ name , $ mac , $ respstate , $ gattCmd , $ handle , $ json_notification ) =
2022-01-15 19:43:08 +00:00
split ( /\|/x , $ string ) ;
2018-10-18 12:31:00 +00:00
my $ hash = $ defs { $ name } ;
delete ( $ hash - > { helper } { RUNNING_PID } ) ;
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 5 ,
"XiaomiBTLESens ($name) - ExecGatttool_Done: Helper is disabled. Stop processing"
) if ( $ hash - > { helper } { DISABLED } ) ;
2018-10-18 12:31:00 +00:00
return if ( $ hash - > { helper } { DISABLED } ) ;
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 5 ,
"XiaomiBTLESens ($name) - ExecGatttool_Done: gatttool return string: $string"
) ;
2018-10-18 12:31:00 +00:00
my $ decode_json = eval { decode_json ( $ json_notification ) } ;
if ( $@ ) {
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 4 ,
"XiaomiBTLESens ($name) - ExecGatttool_Done: JSON error while request: $@"
) ;
2018-10-18 12:31:00 +00:00
}
2022-01-15 19:43:08 +00:00
if ( $ respstate
&& $ respstate eq 'ok'
2020-05-10 12:25:53 +00:00
&& $ gattCmd eq 'write'
&& AttrVal ( $ name , 'model' , 'none' ) eq 'flowerSens' )
2018-10-18 12:31:00 +00:00
{
CreateParamGatttool ( $ hash , 'read' ,
$ XiaomiModels { AttrVal ( $ name , 'model' , '' ) } { rdata } ) ;
}
2022-01-15 19:43:08 +00:00
elsif ( $ respstate
&& $ respstate eq 'ok' )
{
2018-10-18 12:31:00 +00:00
ProcessingNotification ( $ hash , $ gattCmd , $ handle ,
$ decode_json - > { gtResult } ) ;
}
else {
ProcessingErrors ( $ hash , $ decode_json - > { gtResult } ) ;
2018-01-09 06:52:50 +00:00
}
2020-04-24 13:56:10 +00:00
return ;
2018-01-09 06:52:50 +00:00
}
2020-04-24 13:56:10 +00:00
sub ExecGatttool_Aborted {
my $ hash = shift ;
2018-01-09 06:52:50 +00:00
2018-10-18 12:31:00 +00:00
my $ name = $ hash - > { NAME } ;
2018-01-09 06:52:50 +00:00
my % readings ;
2018-10-18 12:31:00 +00:00
delete ( $ hash - > { helper } { RUNNING_PID } ) ;
2020-05-10 12:25:53 +00:00
readingsSingleUpdate ( $ hash , 'state' , 'unreachable' , 1 ) ;
2018-10-18 12:31:00 +00:00
$ readings { 'lastGattError' } =
'The BlockingCall Process terminated unexpectedly. Timedout' ;
WriteReadings ( $ hash , \ % readings ) ;
2018-01-09 06:52:50 +00:00
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 4 ,
"XiaomiBTLESens ($name) - ExecGatttool_Aborted: The BlockingCall Process terminated unexpectedly. Timedout"
) ;
2018-01-09 06:52:50 +00:00
2020-04-24 13:56:10 +00:00
return ;
}
2018-01-09 06:52:50 +00:00
2020-04-24 13:56:10 +00:00
sub ProcessingNotification {
2018-10-18 12:31:00 +00:00
my ( $ hash , $ gattCmd , $ handle , $ notification ) = @ _ ;
my $ name = $ hash - > { NAME } ;
2018-01-09 06:52:50 +00:00
my $ readings ;
2018-10-18 12:31:00 +00:00
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 4 , "XiaomiBTLESens ($name) - ProcessingNotification" ) ;
2018-10-18 12:31:00 +00:00
if ( AttrVal ( $ name , 'model' , 'none' ) eq 'flowerSens' ) {
if ( $ handle eq '0x38' ) {
2018-01-09 06:52:50 +00:00
### Flower Sens - Read Firmware and Battery Data
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 4 ,
"XiaomiBTLESens ($name) - ProcessingNotification: handle 0x38"
) ;
2018-10-18 12:31:00 +00:00
$ readings = FlowerSensHandle0x38 ( $ hash , $ notification ) ;
}
elsif ( $ handle eq '0x35' ) {
2018-01-09 06:52:50 +00:00
### Flower Sens - Read Sensor Data
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 4 ,
"XiaomiBTLESens ($name) - ProcessingNotification: handle 0x35"
) ;
2018-10-18 12:31:00 +00:00
$ readings = FlowerSensHandle0x35 ( $ hash , $ notification ) ;
2018-01-09 06:52:50 +00:00
}
2018-10-18 12:31:00 +00:00
}
elsif ( AttrVal ( $ name , 'model' , 'none' ) eq 'thermoHygroSens' ) {
2024-10-12 06:30:33 +00:00
if ( $ handle eq '0x18' ) {
### Thermo/Hygro Sens - Read Battery Data
Log3 ( $ name , 4 ,
"XiaomiBTLESens ($name) - ProcessingNotification: handle 0x18"
) ;
2018-10-18 12:31:00 +00:00
2024-10-12 06:30:33 +00:00
$ readings = ThermoHygroSensHandle0x18 ( $ hash , $ notification ) ;
}
elsif ( $ handle eq '0x10' ) {
### Thermo/Hygro Sens - Read Sensor Data
Log3 ( $ name , 4 ,
"XiaomiBTLESens ($name) - ProcessingNotification: handle 0x10"
) ;
2018-10-18 12:31:00 +00:00
2024-10-12 06:30:33 +00:00
$ readings = ThermoHygroSensHandle0x10 ( $ hash , $ notification ) ;
}
elsif ( $ handle eq '0x24' ) {
### Thermo/Hygro Sens - Read Firmware Data
Log3 ( $ name , 4 ,
"XiaomiBTLESens ($name) - ProcessingNotification: handle 0x24"
) ;
2018-10-18 12:31:00 +00:00
2024-10-12 06:30:33 +00:00
$ readings = ThermoHygroSensHandle0x24 ( $ hash , $ notification ) ;
}
elsif ( $ handle eq '0x3' ) {
### Thermo/Hygro Sens - Read and Write Devicename
Log3 ( $ name , 4 ,
"XiaomiBTLESens ($name) - ProcessingNotification: handle 0x3" ) ;
2018-10-18 12:31:00 +00:00
2024-10-12 06:30:33 +00:00
return CreateParamGatttool ( $ hash , 'read' ,
$ XiaomiModels { AttrVal ( $ name , 'model' , '' ) } { devicename } )
if ( $ gattCmd ne 'read' ) ;
$ readings = ThermoHygroSensHandle0x3 ( $ hash , $ notification ) ;
2018-01-13 19:24:13 +00:00
}
2018-01-09 06:52:50 +00:00
}
2020-05-10 13:07:35 +00:00
elsif ( AttrVal ( $ name , 'model' , 'none' ) eq 'mijiaLYWSD03MMC' ) {
2024-10-12 06:30:33 +00:00
if ( $ handle eq '0x1b' ) {
### mijiaLYWSD03MMC - Read Battery Data
Log3 ( $ name , 4 ,
"XiaomiBTLESens ($name) - ProcessingNotification: handle 0x1b"
) ;
2020-05-10 13:07:35 +00:00
2024-10-12 06:30:33 +00:00
$ readings = mijiaLYWSD03MMC_Handle0x1b ( $ hash , $ notification ) ;
}
elsif ( $ handle eq '0x38' ) {
### mijiaLYWSD03MMC - Read Sensor Data
Log3 ( $ name , 4 ,
"XiaomiBTLESens ($name) - ProcessingNotification: handle 0x38"
) ;
2020-05-10 13:07:35 +00:00
2024-10-12 06:30:33 +00:00
$ readings = mijiaLYWSD03MMC_Handle0x38 ( $ hash , $ notification ) ;
}
elsif ( $ handle eq '0x12' ) {
### mijiaLYWSD03MMC - Read Firmware Data
Log3 ( $ name , 4 ,
"XiaomiBTLESens ($name) - ProcessingNotification: handle 0x12"
) ;
2020-05-10 13:07:35 +00:00
2024-10-12 06:30:33 +00:00
$ readings = mijiaLYWSD03MMC_Handle0x12 ( $ hash , $ notification ) ;
}
elsif ( $ handle eq '0x3' ) {
### mijiaLYWSD03MMC - Read and Write Devicename
Log3 ( $ name , 4 ,
"XiaomiBTLESens ($name) - ProcessingNotification: handle 0x3" ) ;
2020-05-10 13:07:35 +00:00
2024-10-12 06:30:33 +00:00
return CreateParamGatttool ( $ hash , 'read' ,
$ XiaomiModels { AttrVal ( $ name , 'model' , '' ) } { devicename } )
unless ( $ gattCmd eq 'read' ) ;
$ readings = mijiaLYWSD03MMC_Handle0x3 ( $ hash , $ notification ) ;
2020-05-10 13:07:35 +00:00
}
}
2019-10-11 09:37:58 +00:00
elsif ( AttrVal ( $ name , 'model' , 'none' ) eq 'clearGrassSens' ) {
2024-10-12 06:30:33 +00:00
if ( $ handle eq '0x3b' ) {
### Clear Grass Sens - Read Battery Data
Log3 ( $ name , 4 ,
"XiaomiBTLESens ($name) - ProcessingNotification: handle 0x3b"
) ;
2019-10-11 09:37:58 +00:00
2024-10-12 06:30:33 +00:00
$ readings = ClearGrassSensHandle0x3b ( $ hash , $ notification ) ;
}
elsif ( $ handle eq '0x1e' ) {
### Clear Grass Sens - Read Sensor Data
Log3 ( $ name , 4 ,
"XiaomiBTLESens ($name) - ProcessingNotification: handle 0x1e"
) ;
2019-10-11 09:37:58 +00:00
2024-10-12 06:30:33 +00:00
$ readings = ClearGrassSensHandle0x1e ( $ hash , $ notification ) ;
}
elsif ( $ handle eq '0x2a' ) {
### Clear Grass Sens - Read Firmware Data
Log3 ( $ name , 4 ,
"XiaomiBTLESens ($name) - ProcessingNotification: handle 0x2a"
) ;
2019-10-11 09:37:58 +00:00
2024-10-12 06:30:33 +00:00
$ readings = ClearGrassSensHandle0x2a ( $ hash , $ notification ) ;
}
elsif ( $ handle eq '0x3' ) {
### Clear Grass Sens - Read and Write Devicename
Log3 ( $ name , 4 ,
"XiaomiBTLESens ($name) - ProcessingNotification: handle 0x3" ) ;
2019-10-11 09:37:58 +00:00
2024-10-12 06:30:33 +00:00
return CreateParamGatttool ( $ hash , 'read' ,
$ XiaomiModels { AttrVal ( $ name , 'model' , '' ) } { devicename } )
if ( $ gattCmd ne 'read' ) ;
$ readings = ClearGrassSensHandle0x3 ( $ hash , $ notification ) ;
2019-10-11 09:37:58 +00:00
}
}
2018-10-18 12:31:00 +00:00
2020-04-24 13:56:10 +00:00
return WriteReadings ( $ hash , $ readings ) ;
2018-01-09 06:52:50 +00:00
}
2020-04-24 13:56:10 +00:00
sub FlowerSensHandle0x38 {
2018-01-09 06:52:50 +00:00
### FlowerSens - Read Firmware and Battery Data
2020-04-24 13:56:10 +00:00
my $ hash = shift ;
my $ notification = shift ;
2018-10-18 12:31:00 +00:00
my $ name = $ hash - > { NAME } ;
2018-01-09 06:52:50 +00:00
my % readings ;
2018-10-18 12:31:00 +00:00
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 4 , "XiaomiBTLESens ($name) - FlowerSens Handle0x38" ) ;
2018-01-09 06:52:50 +00:00
2022-01-15 19:43:08 +00:00
my @ dataBatFw = split /\s/x , $ notification ;
2018-01-20 05:57:13 +00:00
2018-06-01 06:34:18 +00:00
### neue Vereinheitlichung für Batteriereadings Forum #800017
2018-10-18 12:31:00 +00:00
$ readings { 'batteryPercent' } = hex ( "0x" . $ dataBatFw [ 0 ] ) ;
$ readings { 'batteryState' } =
( hex ( "0x" . $ dataBatFw [ 0 ] ) > 15 ? "ok" : "low" ) ;
$ readings { 'firmware' } =
( $ dataBatFw [ 2 ] - 30 ) . "."
. ( $ dataBatFw [ 4 ] - 30 ) . "."
. ( $ dataBatFw [ 6 ] - 30 ) ;
2018-01-11 13:12:41 +00:00
$ hash - > { helper } { CallBattery } = 1 ;
2018-10-18 12:11:12 +00:00
CallBattery_Timestamp ( $ hash ) ;
2020-04-24 13:56:10 +00:00
2018-01-09 06:52:50 +00:00
return \ % readings ;
}
2020-04-24 13:56:10 +00:00
sub FlowerSensHandle0x35 {
2018-01-09 06:52:50 +00:00
### Flower Sens - Read Sensor Data
2020-04-24 13:56:10 +00:00
my $ hash = shift ;
my $ notification = shift ;
2018-10-18 12:31:00 +00:00
my $ name = $ hash - > { NAME } ;
2018-01-09 06:52:50 +00:00
my % readings ;
2018-10-18 12:31:00 +00:00
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 4 , "XiaomiBTLESens ($name) - FlowerSens Handle0x35" ) ;
2018-01-09 06:52:50 +00:00
2022-01-15 19:43:08 +00:00
my @ dataSensor = split /\s/x , $ notification ;
2018-01-09 06:52:50 +00:00
2018-10-18 12:11:12 +00:00
return stateRequest ( $ hash )
2020-05-10 12:25:53 +00:00
if ( $ dataSensor [ 0 ] eq "aa"
&& $ dataSensor [ 1 ] eq "bb"
&& $ dataSensor [ 2 ] eq "cc"
&& $ dataSensor [ 3 ] eq "dd"
&& $ dataSensor [ 4 ] eq "ee"
&& $ dataSensor [ 5 ] eq "ff" ) ;
2018-10-18 12:31:00 +00:00
if ( $ dataSensor [ 1 ] eq "ff" ) {
$ readings { 'temperature' } =
( hex ( "0x" . $ dataSensor [ 1 ] . $ dataSensor [ 0 ] ) - hex ( "0xffff" ) ) /
10 ;
}
else {
$ readings { 'temperature' } =
hex ( "0x" . $ dataSensor [ 1 ] . $ dataSensor [ 0 ] ) / 10 ;
}
$ readings { 'lux' } = hex ( "0x" . $ dataSensor [ 4 ] . $ dataSensor [ 3 ] ) ;
$ readings { 'moisture' } = hex ( "0x" . $ dataSensor [ 7 ] ) ;
$ readings { 'fertility' } = hex ( "0x" . $ dataSensor [ 9 ] . $ dataSensor [ 8 ] ) ;
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 4 ,
"XiaomiBTLESens ($name) - FlowerSens Handle0x35 - lux: "
. $ readings { lux }
. ", moisture: "
. $ readings { moisture }
. ", fertility: "
. $ readings { fertility } ) ;
2018-10-18 12:31:00 +00:00
2018-01-11 13:12:41 +00:00
$ hash - > { helper } { CallBattery } = 0 ;
2020-04-24 13:56:10 +00:00
2018-01-09 06:52:50 +00:00
return \ % readings ;
}
2020-04-24 13:56:10 +00:00
sub ThermoHygroSensHandle0x18 {
2018-01-11 13:12:41 +00:00
### Thermo/Hygro Sens - Battery Data
2020-04-24 13:56:10 +00:00
my $ hash = shift ;
my $ notification = shift ;
2018-10-18 12:31:00 +00:00
my $ name = $ hash - > { NAME } ;
2018-01-09 06:52:50 +00:00
my % readings ;
2018-10-18 12:31:00 +00:00
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 4 , "XiaomiBTLESens ($name) - Thermo/Hygro Sens Handle0x18" ) ;
2018-10-18 12:31:00 +00:00
2018-01-24 04:53:45 +00:00
chomp ( $ notification ) ;
2022-01-15 19:43:08 +00:00
$ notification =~ s/\s+//xg ;
2018-07-17 06:20:48 +00:00
2018-06-12 06:28:25 +00:00
### neue Vereinheitlichung für Batteriereadings Forum #800017
2018-10-18 12:31:00 +00:00
$ readings { 'batteryPercent' } = hex ( "0x" . $ notification ) ;
$ readings { 'batteryState' } =
2020-05-10 12:25:53 +00:00
( hex ( "0x" . $ notification ) > 15 ? 'ok' : 'low' ) ;
2018-10-18 12:31:00 +00:00
2018-01-11 13:12:41 +00:00
$ hash - > { helper } { CallBattery } = 1 ;
2018-10-18 12:11:12 +00:00
CallBattery_Timestamp ( $ hash ) ;
2020-04-24 13:56:10 +00:00
2018-01-09 06:52:50 +00:00
return \ % readings ;
}
2020-04-24 13:56:10 +00:00
sub ThermoHygroSensHandle0x10 {
2018-01-09 06:52:50 +00:00
### Thermo/Hygro Sens - Read Sensor Data
2020-04-24 13:56:10 +00:00
my $ hash = shift ;
my $ notification = shift ;
2018-10-18 12:31:00 +00:00
my $ name = $ hash - > { NAME } ;
2018-01-09 06:52:50 +00:00
my % readings ;
2018-10-18 12:31:00 +00:00
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 4 , "XiaomiBTLESens ($name) - Thermo/Hygro Sens Handle0x10" ) ;
2018-10-18 12:31:00 +00:00
2018-10-18 12:11:12 +00:00
return stateRequest ( $ hash )
2022-01-15 19:43:08 +00:00
if ( $ notification !~ /^([0-9a-f]{2}(\s?))*$/x ) ;
2018-10-18 12:31:00 +00:00
2022-01-15 19:43:08 +00:00
my @ numberOfHex = split /\s/x , $ notification ;
2018-01-09 06:52:50 +00:00
2022-01-15 19:43:08 +00:00
$ notification =~ s/\s+//xg ;
2018-11-22 11:24:13 +00:00
2018-10-18 12:31:00 +00:00
$ readings { 'temperature' } = pack ( 'H*' , substr ( $ notification , 4 , 8 ) ) ;
2022-01-15 19:43:08 +00:00
$ readings { 'humidity' } = pack (
2019-07-02 09:47:13 +00:00
'H*' ,
substr (
$ notification ,
(
(
2020-05-10 13:07:35 +00:00
scalar ( @ numberOfHex ) == 14 || ( scalar ( @ numberOfHex ) == 13
2020-05-10 12:25:53 +00:00
&& $ readings { 'temperature' } > 9 )
2019-07-02 09:47:13 +00:00
) ? 18 : 16
) ,
8
)
) ;
2018-02-10 13:44:38 +00:00
2018-01-11 13:12:41 +00:00
$ hash - > { helper } { CallBattery } = 0 ;
2020-04-24 13:56:10 +00:00
2018-01-11 13:12:41 +00:00
return \ % readings ;
}
2020-04-24 13:56:10 +00:00
sub ThermoHygroSensHandle0x24 {
2018-01-11 13:12:41 +00:00
### Thermo/Hygro Sens - Read Firmware Data
2020-04-24 13:56:10 +00:00
my $ hash = shift ;
my $ notification = shift ;
2018-10-18 12:31:00 +00:00
my $ name = $ hash - > { NAME } ;
2018-01-11 13:12:41 +00:00
my % readings ;
2018-10-18 12:31:00 +00:00
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 4 , "XiaomiBTLESens ($name) - Thermo/Hygro Sens Handle0x24" ) ;
2018-01-11 13:12:41 +00:00
2022-01-15 19:43:08 +00:00
$ notification =~ s/\s+//xg ;
2018-01-11 13:12:41 +00:00
2018-10-18 12:31:00 +00:00
$ readings { 'firmware' } = pack ( 'H*' , $ notification ) ;
2018-01-11 13:12:41 +00:00
$ hash - > { helper } { CallBattery } = 0 ;
2020-04-24 13:56:10 +00:00
2018-01-09 06:52:50 +00:00
return \ % readings ;
}
2020-04-24 13:56:10 +00:00
sub ThermoHygroSensHandle0x3 {
2018-01-13 19:24:13 +00:00
### Thermo/Hygro Sens - Read and Write Devicename
2020-04-24 13:56:10 +00:00
my $ hash = shift ;
my $ notification = shift ;
2018-10-18 12:31:00 +00:00
my $ name = $ hash - > { NAME } ;
2018-01-13 19:24:13 +00:00
my % readings ;
2018-10-18 12:31:00 +00:00
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 4 , "XiaomiBTLESens ($name) - Thermo/Hygro Sens Handle0x3" ) ;
2018-01-13 19:24:13 +00:00
2022-01-15 19:43:08 +00:00
$ notification =~ s/\s+//xg ;
2018-01-13 19:24:13 +00:00
2018-10-18 12:31:00 +00:00
$ readings { 'devicename' } = pack ( 'H*' , $ notification ) ;
2018-01-13 19:24:13 +00:00
2018-10-18 12:31:00 +00:00
$ hash - > { helper } { CallBattery } = 0 ;
2020-04-24 13:56:10 +00:00
2018-01-13 19:24:13 +00:00
return \ % readings ;
}
2022-01-15 19:43:08 +00:00
sub mijiaLYWSD03MMC_Handle0x1b {
2020-05-10 13:07:35 +00:00
### mijiaLYWSD03MMC - Battery Data
my ( $ hash , $ notification ) = @ _ ;
my $ name = $ hash - > { NAME } ;
my % readings ;
Log3 ( $ name , 4 , "XiaomiBTLESens ($name) - mijiaLYWSD03MMC Handle0x1b" ) ;
chomp ( $ notification ) ;
2022-01-15 19:43:08 +00:00
$ notification =~ s/\s+//xg ;
2020-05-10 13:07:35 +00:00
### neue Vereinheitlichung für Batteriereadings Forum #800017
$ readings { 'batteryPercent' } = $ notification ; ###hex( "0x" . $notification );
$ readings { 'batteryState' } =
( hex ( "0x" . $ notification ) > 15 ? "ok" : "low" ) ;
$ hash - > { helper } { CallBattery } = 1 ;
CallBattery_Timestamp ( $ hash ) ;
return \ % readings ;
}
2022-01-15 19:43:08 +00:00
sub mijiaLYWSD03MMC_Handle0x38 {
2020-05-10 13:07:35 +00:00
### mijiaLYWSD03MMC - Read Sensor Data
my ( $ hash , $ notification ) = @ _ ;
my $ name = $ hash - > { NAME } ;
my % readings ;
Log3 ( $ name , 4 , "XiaomiBTLESens ($name) - mijiaLYWSD03MMC Handle0x38" ) ;
return stateRequest ( $ hash )
2022-01-15 19:43:08 +00:00
unless ( $ notification =~ /^([0-9a-f]{2}(\s?))*$/x ) ;
2020-05-10 13:07:35 +00:00
2022-01-15 19:43:08 +00:00
my @ splitVal = split /\s/x , $ notification ;
2020-05-10 13:07:35 +00:00
2022-01-15 19:43:08 +00:00
$ notification =~ s/\s+//xg ;
2020-05-10 13:07:35 +00:00
2022-01-15 19:43:08 +00:00
$ readings { 'temperature' } =
hex ( "0x" . $ splitVal [ 1 ] . $ splitVal [ 0 ] ) > 20000
? ( - 65536 + hex ( "0x" . $ splitVal [ 1 ] . $ splitVal [ 0 ] ) ) / 100
: hex ( "0x" . $ splitVal [ 1 ] . $ splitVal [ 0 ] ) / 100 ;
$ readings { 'humidity' } = hex ( "0x" . $ splitVal [ 2 ] ) ;
2020-05-10 13:07:35 +00:00
$ hash - > { helper } { CallBattery } = 0 ;
2022-01-15 19:43:08 +00:00
2020-05-10 13:07:35 +00:00
return \ % readings ;
}
2022-01-15 19:43:08 +00:00
sub mijiaLYWSD03MMC_Handle0x12 {
2020-05-10 13:07:35 +00:00
### mijiaLYWSD03MMC - Read Firmware Data
my ( $ hash , $ notification ) = @ _ ;
my $ name = $ hash - > { NAME } ;
my % readings ;
Log3 ( $ name , 4 , "XiaomiBTLESens ($name) - mijiaLYWSD03MMC Handle0x12" ) ;
2022-01-15 19:43:08 +00:00
$ notification =~ s/\s+//xg ;
2020-05-10 13:07:35 +00:00
$ readings { 'firmware' } = pack ( 'H*' , $ notification ) ;
$ hash - > { helper } { CallBattery } = 0 ;
2022-01-15 19:43:08 +00:00
2020-05-10 13:07:35 +00:00
return \ % readings ;
}
2022-01-15 19:43:08 +00:00
sub mijiaLYWSD03MMC_Handle0x3 {
2020-05-10 13:07:35 +00:00
### mijiaLYWSD03MMC - Read and Write Devicename
my ( $ hash , $ notification ) = @ _ ;
my $ name = $ hash - > { NAME } ;
my % readings ;
Log3 ( $ name , 4 , "XiaomiBTLESens ($name) - mijiaLYWSD03MMC Handle0x3" ) ;
2022-01-15 19:43:08 +00:00
$ notification =~ s/\s+//xg ;
2020-05-10 13:07:35 +00:00
$ readings { 'devicename' } = pack ( 'H*' , $ notification ) ;
$ hash - > { helper } { CallBattery } = 0 ;
2022-01-15 19:43:08 +00:00
2020-05-10 13:07:35 +00:00
return \ % readings ;
}
2020-04-24 13:56:10 +00:00
sub ClearGrassSensHandle0x3b {
2019-10-11 09:37:58 +00:00
### Clear Grass Sens - Battery Data
2020-04-24 13:56:10 +00:00
my $ hash = shift ;
my $ notification = shift ;
2019-10-11 09:37:58 +00:00
my $ name = $ hash - > { NAME } ;
my % readings ;
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 4 , "XiaomiBTLESens ($name) - Clear Grass Sens Handle0x3b" ) ;
2019-10-11 09:37:58 +00:00
chomp ( $ notification ) ;
2022-01-15 19:43:08 +00:00
$ notification =~ s/\s+//xg ;
2019-10-11 09:37:58 +00:00
### neue Vereinheitlichung für Batteriereadings Forum #800017
2019-10-11 11:55:56 +00:00
$ readings { 'batteryPercent' } = hex ( substr ( $ notification , 14 , 2 ) ) ;
2019-10-11 09:37:58 +00:00
$ readings { 'batteryState' } =
2020-05-10 12:25:53 +00:00
( hex ( substr ( $ notification , 14 , 2 ) ) > 15 ? 'ok' : 'low' ) ;
2019-10-11 09:37:58 +00:00
$ hash - > { helper } { CallBattery } = 1 ;
CallBattery_Timestamp ( $ hash ) ;
2020-04-24 13:56:10 +00:00
2019-10-11 09:37:58 +00:00
return \ % readings ;
}
2020-04-24 13:56:10 +00:00
sub ClearGrassSensHandle0x1e {
2019-10-11 09:37:58 +00:00
### Clear Grass Sens - Read Sensor Data
2020-04-24 13:56:10 +00:00
my $ hash = shift ;
my $ notification = shift ;
2019-10-11 09:37:58 +00:00
my $ name = $ hash - > { NAME } ;
my % readings ;
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 4 , "XiaomiBTLESens ($name) - Clear Grass Sens Handle0x1e" ) ;
2019-10-11 09:37:58 +00:00
return stateRequest ( $ hash )
2022-01-15 19:43:08 +00:00
if ( $ notification !~ /^([0-9a-f]{2}(\s?))*$/x ) ;
2019-10-11 09:37:58 +00:00
2022-01-15 19:43:08 +00:00
my @ numberOfHex = split /\s/x , $ notification ;
2019-10-11 09:37:58 +00:00
2022-01-15 19:43:08 +00:00
$ notification =~ s/\s+//xg ;
2019-10-11 09:37:58 +00:00
2019-10-11 10:25:37 +00:00
$ readings { 'temperature' } = hex ( substr ( $ notification , 4 , 2 ) ) / 10 ;
$ readings { 'humidity' } =
hex ( substr ( $ notification , 11 , 1 ) . substr ( $ notification , 8 , 2 ) ) /
10 ;
2019-10-11 09:37:58 +00:00
$ hash - > { helper } { CallBattery } = 0 ;
2020-04-24 13:56:10 +00:00
2019-10-11 09:37:58 +00:00
return \ % readings ;
}
2020-04-24 13:56:10 +00:00
sub ClearGrassSensHandle0x2a {
2019-10-11 09:37:58 +00:00
### Clear Grass Sens - Read Firmware Data
2020-04-24 13:56:10 +00:00
my $ hash = shift ;
my $ notification = shift ;
2019-10-11 09:37:58 +00:00
my $ name = $ hash - > { NAME } ;
my % readings ;
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 4 , "XiaomiBTLESens ($name) - Clear Grass Sens Handle0x2a" ) ;
2019-10-11 09:37:58 +00:00
2022-01-15 19:43:08 +00:00
$ notification =~ s/\s+//xg ;
2019-10-11 09:37:58 +00:00
$ readings { 'firmware' } = pack ( 'H*' , $ notification ) ;
$ hash - > { helper } { CallBattery } = 0 ;
2020-04-24 13:56:10 +00:00
2019-10-11 09:37:58 +00:00
return \ % readings ;
}
2020-04-24 13:56:10 +00:00
sub ClearGrassSensHandle0x3 {
2019-10-11 09:37:58 +00:00
### Clear Grass Sens - Read and Write Devicename
2020-04-24 13:56:10 +00:00
my $ hash = shift ;
my $ notification = shift ;
2019-10-11 09:37:58 +00:00
my $ name = $ hash - > { NAME } ;
my % readings ;
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 4 , "XiaomiBTLESens ($name) - Clear Grass Sens Handle0x3" ) ;
2019-10-11 09:37:58 +00:00
2022-01-15 19:43:08 +00:00
$ notification =~ s/\s+//xg ;
2019-10-11 09:37:58 +00:00
$ readings { 'devicename' } = pack ( 'H*' , $ notification ) ;
$ hash - > { helper } { CallBattery } = 0 ;
2020-04-24 13:56:10 +00:00
2019-10-11 09:37:58 +00:00
return \ % readings ;
}
2020-04-24 13:56:10 +00:00
sub WriteReadings {
my $ hash = shift ;
my $ readings = shift ;
2018-01-09 06:52:50 +00:00
2018-10-18 12:31:00 +00:00
my $ name = $ hash - > { NAME } ;
2018-01-09 06:52:50 +00:00
readingsBeginUpdate ( $ hash ) ;
2018-10-18 12:31:00 +00:00
while ( my ( $ r , $ v ) = each % { $ readings } ) {
readingsBulkUpdate ( $ hash , $ r , $ v ) ;
2018-01-09 06:52:50 +00:00
}
2018-10-18 12:31:00 +00:00
readingsBulkUpdateIfChanged ( $ hash , "state" ,
( $ readings - > { 'lastGattError' } ? 'error' : 'active' ) )
if ( AttrVal ( $ name , 'model' , 'none' ) eq 'flowerSens' ) ;
readingsBulkUpdateIfChanged (
$ hash , "state" ,
(
$ readings - > { 'lastGattError' }
? 'error'
: 'T: '
. ReadingsVal ( $ name , 'temperature' , 0 ) . ' H: '
. ReadingsVal ( $ name , 'humidity' , 0 )
)
2019-10-11 10:25:37 +00:00
)
if ( AttrVal ( $ name , 'model' , 'none' ) eq 'thermoHygroSens'
2020-05-10 13:07:35 +00:00
|| AttrVal ( $ name , 'model' , 'none' ) eq 'mijiaLYWSD03MMC'
2020-05-10 12:25:53 +00:00
|| AttrVal ( $ name , 'model' , 'none' ) eq 'clearGrassSens' ) ;
2018-10-18 12:31:00 +00:00
readingsEndUpdate ( $ hash , 1 ) ;
if ( AttrVal ( $ name , 'model' , 'none' ) eq 'flowerSens' ) {
if ( defined ( $ readings - > { temperature } ) ) {
DoTrigger (
$ name ,
'minFertility '
. (
$ readings - > { fertility } < AttrVal ( $ name , 'minFertility' , 0 )
? 'low'
: 'ok'
)
) if ( AttrVal ( $ name , 'minFertility' , 'none' ) ne 'none' ) ;
DoTrigger (
$ name ,
'maxFertility '
. (
$ readings - > { fertility } > AttrVal ( $ name , 'maxFertility' , 0 )
? 'high'
: 'ok'
)
) if ( AttrVal ( $ name , 'maxFertility' , 'none' ) ne 'none' ) ;
DoTrigger (
$ name ,
'minMoisture '
. (
$ readings - > { moisture } < AttrVal ( $ name , 'minMoisture' , 0 )
? 'low'
: 'ok'
)
) if ( AttrVal ( $ name , 'minMoisture' , 'none' ) ne 'none' ) ;
DoTrigger (
$ name ,
'maxMoisture '
. (
$ readings - > { moisture } > AttrVal ( $ name , 'maxMoisture' , 0 )
? 'high'
: 'ok'
)
) if ( AttrVal ( $ name , 'maxMoisture' , 'none' ) ne 'none' ) ;
DoTrigger (
$ name ,
'minLux '
. (
$ readings - > { lux } < AttrVal ( $ name , 'minLux' , 0 )
? 'low'
: 'ok'
)
) if ( AttrVal ( $ name , 'minLux' , 'none' ) ne 'none' ) ;
DoTrigger (
$ name ,
'maxLux '
. (
$ readings - > { lux } > AttrVal ( $ name , 'maxLux' , 0 )
? 'high'
: 'ok'
)
) if ( AttrVal ( $ name , 'maxLux' , 'none' ) ne 'none' ) ;
2018-01-09 06:52:50 +00:00
}
}
2018-10-18 12:31:00 +00:00
if ( defined ( $ readings - > { temperature } ) ) {
DoTrigger (
$ name ,
'minTemp '
. (
$ readings - > { temperature } < AttrVal ( $ name , 'minTemp' , 0 )
? 'low'
: 'ok'
)
) if ( AttrVal ( $ name , 'minTemp' , 'none' ) ne 'none' ) ;
DoTrigger (
$ name ,
'maxTemp '
. (
$ readings - > { temperature } > AttrVal ( $ name , 'maxTemp' , 0 )
? 'high'
: 'ok'
)
) if ( AttrVal ( $ name , 'maxTemp' , 'none' ) ne 'none' ) ;
}
2018-01-09 06:52:50 +00:00
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 4 ,
"XiaomiBTLESens ($name) - WriteReadings: Readings were written" ) ;
2018-01-09 06:52:50 +00:00
$ hash - > { helper } { CallSensDataCounter } = 0 ;
2018-10-18 12:31:00 +00:00
stateRequest ( $ hash ) if ( $ hash - > { helper } { CallBattery } == 1 ) ;
2018-01-09 06:52:50 +00:00
2020-04-24 13:56:10 +00:00
return ;
}
2018-01-09 06:52:50 +00:00
2020-04-24 13:56:10 +00:00
sub ProcessingErrors {
my $ hash = shift ;
my $ notification = shift ;
2018-10-18 12:31:00 +00:00
my $ name = $ hash - > { NAME } ;
2018-01-09 06:52:50 +00:00
my % readings ;
2018-10-18 12:31:00 +00:00
2020-04-24 13:56:10 +00:00
Log3 ( $ name , 4 , "XiaomiBTLESens ($name) - ProcessingErrors" ) ;
2018-01-09 06:52:50 +00:00
$ readings { 'lastGattError' } = $ notification ;
2018-10-18 12:31:00 +00:00
2020-04-24 13:56:10 +00:00
return WriteReadings ( $ hash , \ % readings ) ;
2018-01-09 06:52:50 +00:00
}
#### my little Helper
2020-04-24 13:56:10 +00:00
sub encodeJSON {
2018-10-18 12:31:00 +00:00
my $ gtResult = shift ;
2018-01-09 06:52:50 +00:00
chomp ( $ gtResult ) ;
2018-03-19 12:34:45 +00:00
2018-10-18 12:31:00 +00:00
my % response = ( 'gtResult' = > $ gtResult ) ;
return encode_json ( \ % response ) ;
2018-01-09 06:52:50 +00:00
}
## Routinen damit Firmware und Batterie nur alle X male statt immer aufgerufen wird
2020-04-24 13:56:10 +00:00
sub CallBattery_Timestamp {
2018-10-18 12:31:00 +00:00
my $ hash = shift ;
2018-01-09 06:52:50 +00:00
# get timestamp
2018-10-18 12:31:00 +00:00
$ hash - > { helper } { updateTimeCallBattery } =
gettimeofday ( ) ; # in seconds since the epoch
$ hash - > { helper } { updateTimestampCallBattery } = FmtDateTime ( gettimeofday ( ) ) ;
2022-01-15 19:43:08 +00:00
return ;
2018-01-09 06:52:50 +00:00
}
2020-04-24 13:56:10 +00:00
sub CallBattery_UpdateTimeAge {
2018-10-18 12:31:00 +00:00
my $ hash = shift ;
2018-01-09 06:52:50 +00:00
2018-10-18 12:31:00 +00:00
$ hash - > { helper } { updateTimeCallBattery } = 0
if ( not defined ( $ hash - > { helper } { updateTimeCallBattery } ) ) ;
2018-01-11 13:12:41 +00:00
my $ UpdateTimeAge = gettimeofday ( ) - $ hash - > { helper } { updateTimeCallBattery } ;
2018-10-18 12:31:00 +00:00
2018-01-09 06:52:50 +00:00
return $ UpdateTimeAge ;
}
2020-04-24 13:56:10 +00:00
sub CallBattery_IsUpdateTimeAgeToOld {
my $ hash = shift ;
my $ maxAge = shift ;
2018-10-18 12:31:00 +00:00
return ( CallBattery_UpdateTimeAge ( $ hash ) > $ maxAge ? 1 : 0 ) ;
2018-01-09 06:52:50 +00:00
}
2020-04-24 13:56:10 +00:00
sub CreateDevicenameHEX {
2018-10-18 12:31:00 +00:00
my $ devicename = shift ;
my $ devicenameHex = unpack ( "H*" , $ devicename ) ;
2018-01-13 19:24:13 +00:00
return $ devicenameHex ;
}
2020-04-24 13:56:10 +00:00
sub BTLE_CmdlinePreventGrepFalsePositive {
2018-10-18 12:31:00 +00:00
2018-03-16 18:33:59 +00:00
# https://stackoverflow.com/questions/9375711/more-elegant-ps-aux-grep-v-grep
# Given abysmal (since external-command-based) performance in the first place, we'd better
# avoid an *additional* grep process plus pipe...
my $ cmdline = shift ;
2022-01-15 19:43:08 +00:00
$ cmdline =~ s/(.)(.*)/[$1]$2/x ;
2020-04-24 13:56:10 +00:00
2018-03-16 18:33:59 +00:00
return $ cmdline ;
}
2018-01-09 06:52:50 +00:00
1 ;
= pod
= item device
2018-03-24 12:16:15 +00:00
= item summary Modul to retrieves data from a Xiaomi BTLE Sensors
= item summary_DE Modul um Daten vom Xiaomi BTLE Sensoren aus zu lesen
2018-01-09 06:52:50 +00:00
= begin html
< a name = "XiaomiBTLESens" > </a>
2018-01-13 19:24:13 +00:00
<h3> Xiaomi BTLE Sensor </h3>
2018-01-09 06:52:50 +00:00
<ul>
2018-01-13 19:24:13 +00:00
<u> <b> XiaomiBTLESens - Retrieves data from a Xiaomi BTLE Sensor </b> </u>
2018-01-09 06:52:50 +00:00
<br>
With this module it is possible to read the data from a sensor and to set it as reading . </br>
Gatttool and hcitool is required to use this modul . ( apt - get install bluez )
<br> <br>
< a name = "XiaomiBTLESensdefine" > </a>
<b> Define </b>
<ul> <br>
<code> define & lt ; name & gt ; XiaomiBTLESens & lt ; BT - MAC & gt ; </code>
<br> <br>
Example:
<ul> <br>
<code> define Weihnachtskaktus XiaomiBTLESens C4:7C:8D:62:42:6F </code> <br>
</ul>
<br>
This statement creates a XiaomiBTLESens with the name Weihnachtskaktus and the Bluetooth Mac C4:7C:8D:62:42:6F . <br>
2018-03-24 12:16:15 +00:00
After the device has been created and the model attribut is set , the current data of the Xiaomi BTLE Sensor is automatically read from the device .
2018-01-09 06:52:50 +00:00
</ul>
<br> <br>
< a name = "XiaomiBTLESensreadings" > </a>
<b> Readings </b>
<ul>
<li> state - Status of the flower sensor or error message if any errors . </li>
2018-06-12 06:28:25 +00:00
<li> batteryState - current battery state dependent on batteryLevel . </li>
<li> batteryPercent - current battery level in percent . </li>
2018-01-09 06:52:50 +00:00
<li> fertility - Values for the fertilizer content </li>
<li> firmware - current device firmware </li>
<li> lux - current light intensity </li>
<li> moisture - current moisture content </li>
<li> temperature - current temperature </li>
</ul>
<br> <br>
< a name = "XiaomiBTLESensset" > </a>
<b> Set </b>
<ul>
2018-01-16 11:52:48 +00:00
<li> devicename - set a devicename </li>
2018-03-17 11:07:33 +00:00
<li> resetBatteryTimestamp - when the battery was changed </li>
2018-01-09 06:52:50 +00:00
<br>
</ul>
<br> <br>
< a name = "XiaomiBTLESensget" > </a>
<b> Get </b>
<ul>
2018-01-13 19:24:13 +00:00
<li> sensorData - retrieves the current data of the Xiaomi sensor </li>
2018-01-16 11:52:48 +00:00
<li> devicename - fetch devicename </li>
<li> firmware - fetch firmware </li>
2018-01-09 06:52:50 +00:00
<br>
</ul>
<br> <br>
< a name = "XiaomiBTLESensattribut" > </a>
<b> Attributes </b>
<ul>
<li> disable - disables the device </li>
<li> disabledForIntervals - disable device for interval time ( 13 : 00 - 18 : 30 or 13 : 00 - 18 : 30 22 : 00 - 23 : 00 ) </li>
<li> interval - interval in seconds for statusRequest </li>
<li> minFertility - min fertility value for low warn event </li>
2018-03-24 12:16:15 +00:00
<li> hciDevice - select bluetooth dongle device </li>
<li> model - set model type </li>
2018-01-09 06:52:50 +00:00
<li> maxFertility - max fertility value for High warn event </li>
<li> minMoisture - min moisture value for low warn event </li>
<li> maxMoisture - max moisture value for High warn event </li>
<li> minTemp - min temperature value for low warn event </li>
<li> maxTemp - max temperature value for high warn event </li>
<li> minlux - min lux value for low warn event </li>
<li> maxlux - max lux value for high warn event
<br>
Event Example for min / max Value ' s: 2017 - 03 - 16 11 : 08 : 05 XiaomiBTLESens Dracaena minMoisture low <br>
Event Example for min /max Value's: 2017-03-16 11:08:06 XiaomiBTLESens Dracaena maxTemp high</ li >
<li> sshHost - FQD - Name or IP of ssh remote system / you must configure your ssh system for certificate authentication. For better handling you can config ssh Client with .ssh/co nfig file </li>
<li> batteryFirmwareAge - how old can the reading befor fetch new data </li>
<li> blockingCallLoglevel - Blocking . pm Loglevel for BlockingCall Logoutput </li>
</ul>
</ul>
= end html
= begin html_DE
< a name = "XiaomiBTLESens" > </a>
2018-01-13 19:24:13 +00:00
<h3> Xiaomi BTLE Sensor </h3>
2018-01-09 06:52:50 +00:00
<ul>
2018-01-13 19:24:13 +00:00
<u> <b> XiaomiBTLESens - liest Daten von einem Xiaomi BTLE Sensor </b> </u>
2018-01-09 06:52:50 +00:00
< br / >
Dieser Modul liest Daten von einem Sensor und legt sie in den Readings ab . < br / >
Auf dem ( Linux ) FHEM - Server werden gatttool und hcitool vorausgesetzt . ( sudo apt install bluez )
< br /><br / >
< a name = "XiaomiBTLESensdefine" > </a>
<b> Define </b>
<ul> < br / >
<code> define & lt ; name & gt ; XiaomiBTLESens & lt ; BT - MAC & gt ; </code>
< br /><br / >
Beispiel:
<ul> < br / >
<code> define Weihnachtskaktus XiaomiBTLESens C4:7C:8D:62:42:6F </code> < br / >
</ul>
< br / >
2018-11-22 11:24:13 +00:00
Der Befehl legt ein Device vom Typ XiaomiBTLESens mit dem Namen Weihnachtskaktus und der Bluetooth MAC C4:7C:8D:62:42:6F an . < br / >
2018-03-24 12:16:15 +00:00
Nach dem Anlegen des Device und setzen des korrekten model Attributes werden umgehend und automatisch die aktuellen Daten vom betroffenen Xiaomi BTLE Sensor gelesen .
2018-01-09 06:52:50 +00:00
</ul>
< br /><br / >
< a name = "XiaomiBTLESensreadings" > </a>
<b> Readings </b>
<ul>
2018-01-13 19:24:13 +00:00
<li> state - Status des BTLE Sensor oder eine Fehlermeldung falls Fehler beim letzten Kontakt auftraten . </li>
2018-06-12 06:28:25 +00:00
<li> batteryState - aktueller Batterie - Status in Abhängigkeit vom Wert batteryLevel . </li>
<li> batteryPercent - aktueller Ladestand der Batterie in Prozent . </li>
2018-01-09 06:52:50 +00:00
<li> fertility - Wert des Fruchtbarkeitssensors ( Bodenleitf & auml ; higkeit ) </li>
2018-01-13 19:24:13 +00:00
<li> firmware - aktuelle Firmware - Version des BTLE Sensor </li>
2018-03-24 12:16:15 +00:00
<li> lastGattError - Fehlermeldungen vom gatttool </li>
2018-01-09 06:52:50 +00:00
<li> lux - aktuelle Lichtintensit & auml ; t </li>
<li> moisture - aktueller Feuchtigkeitswert </li>
<li> temperature - aktuelle Temperatur </li>
</ul>
< br /><br / >
< a name = "XiaomiBTLESensset" > </a>
<b> Set </b>
<ul>
2018-03-17 11:07:33 +00:00
<li> resetBatteryTimestamp - wenn die Batterie gewechselt wurde </li>
2018-03-24 12:16:15 +00:00
<li> devicename - setzt einen Devicenamen </li>
2018-01-16 11:52:48 +00:00
< br / >
</ul>
< br /><br / >
2018-01-09 06:52:50 +00:00
< a name = "XiaomiBTLESensGet" > </a>
<b> Get </b>
<ul>
2018-01-13 19:24:13 +00:00
<li> sensorData - aktive Abfrage der Sensors Werte </li>
2018-01-16 11:52:48 +00:00
<li> devicename - liest den Devicenamen aus </li>
<li> firmware - liest die Firmeware aus </li>
2018-01-09 06:52:50 +00:00
< br / >
</ul>
< br /><br / >
< a name = "XiaomiBTLESensattribut" > </a>
<b> Attribute </b>
<ul>
<li> disable - deaktiviert das Device </li>
<li> interval - Interval in Sekunden zwischen zwei Abfragen </li>
<li> disabledForIntervals - deaktiviert das Gerät für den angegebenen Zeitinterval ( 13 : 00 - 18 : 30 or 13 : 00 - 18 : 30 22 : 00 - 23 : 00 ) </li>
<li> minFertility - min Fruchtbarkeits - Grenzwert f & uuml ; r ein Ereignis minFertility low </li>
2018-03-24 12:16:15 +00:00
<li> hciDevice - Auswahl bei mehreren Bluetooth Dongeln </li>
<li> model - setzt das Model </li>
2018-01-09 06:52:50 +00:00
<li> maxFertility - max Fruchtbarkeits - Grenzwert f & uuml ; r ein Ereignis maxFertility high </li>
<li> minMoisture - min Feuchtigkeits - Grenzwert f & uuml ; r ein Ereignis minMoisture low </li>
<li> maxMoisture - max Feuchtigkeits - Grenzwert f & uuml ; r ein Ereignis maxMoisture high </li>
<li> minTemp - min Temperatur - Grenzwert f & uuml ; r ein Ereignis minTemp low </li>
<li> maxTemp - max Temperatur - Grenzwert f & uuml ; r ein Ereignis maxTemp high </li>
<li> minlux - min Helligkeits - Grenzwert f & uuml ; r ein Ereignis minlux low </li>
<li> maxlux - max Helligkeits - Grenzwert f & uuml ; r ein Ereignis maxlux high
< br /><br / > Beispiele f & uuml ; r min /max-Ereignisse:<br / >
2017 - 03 - 16 11 : 08 : 05 XiaomiBTLESens Dracaena minMoisture low < br / >
2017 - 03 - 16 11 : 08 : 06 XiaomiBTLESens Dracaena maxTemp high < br /><br / > </li>
<li> sshHost - FQDN oder IP - Adresse eines entfernten SSH - Systems . Das SSH - System ist auf eine Zertifikat basierte Authentifizierung zu konfigurieren . Am elegantesten geschieht das mit einer . ssh /config Datei auf dem SSH-Client.</ li >
<li> batteryFirmwareAge - wie alt soll der Timestamp des Readings sein bevor eine Aktuallisierung statt findet </li>
<li> blockingCallLoglevel - Blocking . pm Loglevel für BlockingCall Logausgaben </li>
</ul>
</ul>
= end html_DE
2019-06-05 08:49:04 +00:00
= for : application / json ; q = META . json 74 _XiaomiBTLESens . pm
{
"abstract" : "Modul to retrieves data from a Xiaomi BTLE Sensors" ,
"x_lang" : {
"de" : {
"abstract" : "Modul um Daten vom Xiaomi BTLE Sensoren aus zu lesen"
}
} ,
"keywords" : [
"fhem-mod-device" ,
"fhem-core" ,
"Flower" ,
"BTLE" ,
"Xiaomi" ,
"Sensor" ,
"Bluetooth LE"
] ,
"release_status" : "stable" ,
"license" : "GPL_2" ,
2022-01-15 19:43:08 +00:00
"version" : "v3.0.1" ,
2019-06-05 08:49:04 +00:00
"author" : [
"Marko Oldenburg <leongaultier@gmail.com>"
] ,
"x_fhem_maintainer" : [
"CoolTux"
] ,
"x_fhem_maintainer_github" : [
"LeonGaultier"
] ,
"prereqs" : {
"runtime" : {
"requires" : {
"FHEM" : 5.00918799 ,
"perl" : 5.016 ,
2019-07-02 09:46:31 +00:00
"Meta" : 1 ,
"Blocking" : 1 ,
"JSON" : 1
2019-06-05 08:49:04 +00:00
} ,
"recommends" : {
2019-07-02 09:46:31 +00:00
"JSON" : 0
2019-06-05 08:49:04 +00:00
} ,
"suggests" : {
2019-07-02 09:46:31 +00:00
"Cpanel::JSON::XS" : 0 ,
"JSON::XS" : 0
2019-06-05 08:49:04 +00:00
}
}
}
}
= end : application / json ; q = META . json
2018-01-09 06:52:50 +00:00
= cut