74_XiaomiBTLESens.pm 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293
  1. ###############################################################################
  2. #
  3. # Developed with Kate
  4. #
  5. # (c) 2017-2018 Copyright: Marko Oldenburg (leongaultier at gmail dot com)
  6. # All rights reserved
  7. #
  8. # This script is free software; you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License as published by
  10. # the Free Software Foundation; either version 2 of the License, or
  11. # any later version.
  12. #
  13. # The GNU General Public License can be found at
  14. # http://www.gnu.org/copyleft/gpl.html.
  15. # A copy is found in the textfile GPL.txt and important notices to the license
  16. # from the author is found in LICENSE.txt distributed with these scripts.
  17. #
  18. # This script is distributed in the hope that it will be useful,
  19. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. # GNU General Public License for more details.
  22. #
  23. #
  24. # $Id: 74_XiaomiBTLESens.pm 17620 2018-10-26 06:49:03Z CoolTux $
  25. #
  26. ###############################################################################
  27. #$cmd = "qx(gatttool -i $hci -b $mac --char-write-req -a 0x33 -n A01F";
  28. #$cmd = "qx(gatttool -i $hci -b $mac --char-read -a 0x35"; # Sensor Daten
  29. #$cmd = "qx(gatttool -i $hci -b $mac --char-read -a 0x38"; # Firmware und Batterie
  30. # e8 00 00 58 0f 00 00 34 f1 02 02 3c 00 fb 34 9b
  31. package main;
  32. use strict;
  33. use warnings;
  34. my $version = "2.4.4";
  35. sub XiaomiBTLESens_Initialize($) {
  36. my ($hash) = @_;
  37. $hash->{SetFn} = "XiaomiBTLESens::Set";
  38. $hash->{GetFn} = "XiaomiBTLESens::Get";
  39. $hash->{DefFn} = "XiaomiBTLESens::Define";
  40. $hash->{NotifyFn} = "XiaomiBTLESens::Notify";
  41. $hash->{UndefFn} = "XiaomiBTLESens::Undef";
  42. $hash->{AttrFn} = "XiaomiBTLESens::Attr";
  43. $hash->{AttrList} =
  44. "interval "
  45. . "disable:1 "
  46. . "disabledForIntervals "
  47. . "hciDevice:hci0,hci1,hci2 "
  48. . "batteryFirmwareAge:8h,16h,24h,32h,40h,48h "
  49. . "minFertility "
  50. . "maxFertility "
  51. . "minTemp "
  52. . "maxTemp "
  53. . "minMoisture "
  54. . "maxMoisture "
  55. . "minLux "
  56. . "maxLux "
  57. . "sshHost "
  58. . "model:flowerSens,thermoHygroSens "
  59. . "blockingCallLoglevel:2,3,4,5 "
  60. . $readingFnAttributes;
  61. foreach my $d ( sort keys %{ $modules{XiaomiBTLESens}{defptr} } ) {
  62. my $hash = $modules{XiaomiBTLESens}{defptr}{$d};
  63. $hash->{VERSION} = $version;
  64. }
  65. }
  66. package XiaomiBTLESens;
  67. my $missingModul = "";
  68. use strict;
  69. use warnings;
  70. use POSIX;
  71. use GPUtils qw(:all)
  72. ; # wird für den Import der FHEM Funktionen aus der fhem.pl benötigt
  73. eval "use JSON;1" or $missingModul .= "JSON ";
  74. eval "use Blocking;1" or $missingModul .= "Blocking ";
  75. #use Data::Dumper; only for Debugging
  76. ## Import der FHEM Funktionen
  77. BEGIN {
  78. GP_Import(
  79. qw(readingsSingleUpdate
  80. readingsBulkUpdate
  81. readingsBulkUpdateIfChanged
  82. readingsBeginUpdate
  83. readingsEndUpdate
  84. defs
  85. modules
  86. Log3
  87. CommandAttr
  88. # attr
  89. AttrVal
  90. ReadingsVal
  91. IsDisabled
  92. deviceEvents
  93. init_done
  94. gettimeofday
  95. InternalTimer
  96. RemoveInternalTimer
  97. DoTrigger
  98. BlockingKill
  99. BlockingCall
  100. FmtDateTime)
  101. );
  102. }
  103. my %XiaomiModels = (
  104. flowerSens => {
  105. 'rdata' => '0x35',
  106. 'wdata' => '0x33',
  107. 'wdataValue' => 'A01F',
  108. 'wdatalisten' => 0,
  109. 'battery' => '0x38',
  110. 'firmware' => '0x38'
  111. },
  112. thermoHygroSens => {
  113. 'wdata' => '0x10',
  114. 'wdataValue' => '0100',
  115. 'wdatalisten' => 1,
  116. 'battery' => '0x18',
  117. 'firmware' => '0x24',
  118. 'devicename' => '0x3'
  119. },
  120. );
  121. my %CallBatteryAge = (
  122. '8h' => 28800,
  123. '16h' => 57600,
  124. '24h' => 86400,
  125. '32h' => 115200,
  126. '40h' => 144000,
  127. '48h' => 172800
  128. );
  129. # declare prototype
  130. sub ExecGatttool_Run($);
  131. sub Define($$) {
  132. my ( $hash, $def ) = @_;
  133. my @a = split( "[ \t][ \t]*", $def );
  134. return "too few parameters: define <name> XiaomiBTLESens <BTMAC>"
  135. if ( @a != 3 );
  136. return
  137. "Cannot define XiaomiBTLESens device. Perl modul ${missingModul}is missing."
  138. if ($missingModul);
  139. my $name = $a[0];
  140. my $mac = $a[2];
  141. $hash->{BTMAC} = $mac;
  142. $hash->{VERSION} = $version;
  143. $hash->{INTERVAL} = 300;
  144. $hash->{helper}{CallSensDataCounter} = 0;
  145. $hash->{helper}{CallBattery} = 0;
  146. $hash->{NOTIFYDEV} = "global,$name";
  147. $hash->{loglevel} = 4;
  148. readingsSingleUpdate( $hash, "state", "initialized", 0 );
  149. CommandAttr( undef, $name . ' room XiaomiBTLESens' )
  150. if ( AttrVal( $name, 'room', 'none' ) eq 'none' );
  151. Log3 $name, 3, "XiaomiBTLESens ($name) - defined with BTMAC $hash->{BTMAC}";
  152. $modules{XiaomiBTLESens}{defptr}{ $hash->{BTMAC} } = $hash;
  153. return undef;
  154. }
  155. sub Undef($$) {
  156. my ( $hash, $arg ) = @_;
  157. my $mac = $hash->{BTMAC};
  158. my $name = $hash->{NAME};
  159. RemoveInternalTimer($hash);
  160. BlockingKill( $hash->{helper}{RUNNING_PID} )
  161. if ( defined( $hash->{helper}{RUNNING_PID} ) );
  162. delete( $modules{XiaomiBTLESens}{defptr}{$mac} );
  163. Log3 $name, 3, "Sub XiaomiBTLESens_Undef ($name) - delete device $name";
  164. return undef;
  165. }
  166. sub Attr(@) {
  167. my ( $cmd, $name, $attrName, $attrVal ) = @_;
  168. my $hash = $defs{$name};
  169. if ( $attrName eq "disable" ) {
  170. if ( $cmd eq "set" and $attrVal eq "1" ) {
  171. RemoveInternalTimer($hash);
  172. readingsSingleUpdate( $hash, "state", "disabled", 1 );
  173. Log3 $name, 3, "XiaomiBTLESens ($name) - disabled";
  174. }
  175. elsif ( $cmd eq "del" ) {
  176. Log3 $name, 3, "XiaomiBTLESens ($name) - enabled";
  177. }
  178. }
  179. elsif ( $attrName eq "disabledForIntervals" ) {
  180. if ( $cmd eq "set" ) {
  181. return
  182. "check disabledForIntervals Syntax HH:MM-HH:MM or 'HH:MM-HH:MM HH:MM-HH:MM ...'"
  183. unless ( $attrVal =~ /^((\d{2}:\d{2})-(\d{2}:\d{2})\s?)+$/ );
  184. Log3 $name, 3, "XiaomiBTLESens ($name) - disabledForIntervals";
  185. stateRequest($hash);
  186. }
  187. elsif ( $cmd eq "del" ) {
  188. Log3 $name, 3, "XiaomiBTLESens ($name) - enabled";
  189. readingsSingleUpdate( $hash, "state", "active", 1 );
  190. }
  191. }
  192. elsif ( $attrName eq "interval" ) {
  193. RemoveInternalTimer($hash);
  194. if ( $cmd eq "set" ) {
  195. if ( $attrVal < 120 ) {
  196. Log3 $name, 3,
  197. "XiaomiBTLESens ($name) - interval too small, please use something >= 120 (sec), default is 300 (sec)";
  198. return
  199. "interval too small, please use something >= 120 (sec), default is 300 (sec)";
  200. }
  201. else {
  202. $hash->{INTERVAL} = $attrVal;
  203. Log3 $name, 3,
  204. "XiaomiBTLESens ($name) - set interval to $attrVal";
  205. }
  206. }
  207. elsif ( $cmd eq "del" ) {
  208. $hash->{INTERVAL} = 300;
  209. Log3 $name, 3, "XiaomiBTLESens ($name) - set interval to default";
  210. }
  211. }
  212. elsif ( $attrName eq "blockingCallLoglevel" ) {
  213. if ( $cmd eq "set" ) {
  214. $hash->{loglevel} = $attrVal;
  215. Log3 $name, 3,
  216. "XiaomiBTLESens ($name) - set blockingCallLoglevel to $attrVal";
  217. }
  218. elsif ( $cmd eq "del" ) {
  219. $hash->{loglevel} = 4;
  220. Log3 $name, 3,
  221. "XiaomiBTLESens ($name) - set blockingCallLoglevel to default";
  222. }
  223. }
  224. return undef;
  225. }
  226. sub Notify($$) {
  227. my ( $hash, $dev ) = @_;
  228. my $name = $hash->{NAME};
  229. return stateRequestTimer($hash) if ( IsDisabled($name) );
  230. my $devname = $dev->{NAME};
  231. my $devtype = $dev->{TYPE};
  232. my $events = deviceEvents( $dev, 1 );
  233. return if ( !$events );
  234. stateRequestTimer($hash)
  235. if (
  236. (
  237. (
  238. (
  239. grep /^DEFINED.$name$/,
  240. @{$events}
  241. or grep /^DELETEATTR.$name.disable$/,
  242. @{$events}
  243. or grep /^ATTR.$name.disable.0$/,
  244. @{$events}
  245. or grep /^DELETEATTR.$name.interval$/,
  246. @{$events}
  247. or grep /^DELETEATTR.$name.model$/,
  248. @{$events}
  249. or grep /^ATTR.$name.model.+/,
  250. @{$events}
  251. or grep /^ATTR.$name.interval.[0-9]+/,
  252. @{$events}
  253. )
  254. and $devname eq 'global'
  255. )
  256. or grep /^resetBatteryTimestamp$/,
  257. @{$events}
  258. )
  259. and $init_done
  260. or (
  261. (
  262. grep /^INITIALIZED$/,
  263. @{$events}
  264. or grep /^REREADCFG$/,
  265. @{$events}
  266. or grep /^MODIFIED.$name$/,
  267. @{$events}
  268. )
  269. and $devname eq 'global'
  270. )
  271. );
  272. CreateParamGatttool( $hash, 'read',
  273. $XiaomiModels{ AttrVal( $name, 'model', '' ) }{devicename} )
  274. if (
  275. AttrVal( $name, 'model', 'thermoHygroSens' ) eq 'thermoHygroSens'
  276. and $devname eq $name
  277. and grep /^$name.firmware.+/,
  278. @{$events}
  279. );
  280. return;
  281. }
  282. sub stateRequest($) {
  283. my ($hash) = @_;
  284. my $name = $hash->{NAME};
  285. my %readings;
  286. if ( AttrVal( $name, 'model', 'none' ) eq 'none' ) {
  287. readingsSingleUpdate( $hash, "state", "set attribute model first", 1 );
  288. }
  289. elsif ( !IsDisabled($name) ) {
  290. if ( ReadingsVal( $name, 'firmware', 'none' ) ne 'none' ) {
  291. return CreateParamGatttool( $hash, 'read',
  292. $XiaomiModels{ AttrVal( $name, 'model', '' ) }{battery} )
  293. if (
  294. CallBattery_IsUpdateTimeAgeToOld(
  295. $hash,
  296. $CallBatteryAge{ AttrVal( $name, 'BatteryFirmwareAge',
  297. '24h' ) }
  298. )
  299. );
  300. if ( $hash->{helper}{CallSensDataCounter} < 1 ) {
  301. CreateParamGatttool(
  302. $hash,
  303. 'write',
  304. $XiaomiModels{ AttrVal( $name, 'model', '' ) }{wdata},
  305. $XiaomiModels{ AttrVal( $name, 'model', '' ) }{wdataValue}
  306. );
  307. $hash->{helper}{CallSensDataCounter} =
  308. $hash->{helper}{CallSensDataCounter} + 1;
  309. }
  310. else {
  311. $readings{'lastGattError'} = 'charWrite faild';
  312. WriteReadings( $hash, \%readings );
  313. $hash->{helper}{CallSensDataCounter} = 0;
  314. return;
  315. }
  316. }
  317. else {
  318. CreateParamGatttool( $hash, 'read',
  319. $XiaomiModels{ AttrVal( $name, 'model', '' ) }{firmware} );
  320. }
  321. }
  322. else {
  323. readingsSingleUpdate( $hash, "state", "disabled", 1 );
  324. }
  325. }
  326. sub stateRequestTimer($) {
  327. my ($hash) = @_;
  328. my $name = $hash->{NAME};
  329. RemoveInternalTimer($hash);
  330. stateRequest($hash);
  331. InternalTimer( gettimeofday() + $hash->{INTERVAL} + int( rand(300) ),
  332. "XiaomiBTLESens::stateRequestTimer", $hash );
  333. Log3 $name, 4,
  334. "XiaomiBTLESens ($name) - stateRequestTimer: Call Request Timer";
  335. }
  336. sub Set($$@) {
  337. my ( $hash, $name, @aa ) = @_;
  338. my ( $cmd, @args ) = @aa;
  339. my $mod;
  340. my $handle;
  341. my $value = 'write';
  342. if ( $cmd eq 'devicename' ) {
  343. return "usage: devicename <name>" if ( @args < 1 );
  344. my $devicename = join( " ", @args );
  345. $mod = 'write';
  346. $handle = $XiaomiModels{ AttrVal( $name, 'model', '' ) }{devicename};
  347. $value = CreateDevicenameHEX( makeDeviceName($devicename) );
  348. }
  349. elsif ( $cmd eq 'resetBatteryTimestamp' ) {
  350. return "usage: resetBatteryTimestamp" if ( @args != 0 );
  351. $hash->{helper}{updateTimeCallBattery} = 0;
  352. return;
  353. }
  354. else {
  355. my $list = "resetBatteryTimestamp:noArg";
  356. $list .= " devicename"
  357. if (
  358. AttrVal( $name, 'model', 'thermoHygroSens' ) eq 'thermoHygroSens' );
  359. return "Unknown argument $cmd, choose one of $list";
  360. }
  361. CreateParamGatttool( $hash, $mod, $handle, $value );
  362. return undef;
  363. }
  364. sub Get($$@) {
  365. my ( $hash, $name, @aa ) = @_;
  366. my ( $cmd, @args ) = @aa;
  367. my $mod = 'read';
  368. my $handle;
  369. if ( $cmd eq 'sensorData' ) {
  370. return "usage: sensorData" if ( @args != 0 );
  371. stateRequest($hash);
  372. }
  373. elsif ( $cmd eq 'firmware' ) {
  374. return "usage: firmware" if ( @args != 0 );
  375. $mod = 'read';
  376. $handle = $XiaomiModels{ AttrVal( $name, 'model', '' ) }{firmware};
  377. }
  378. elsif ( $cmd eq 'devicename' ) {
  379. return "usage: devicename" if ( @args != 0 );
  380. $mod = 'read';
  381. $handle = $XiaomiModels{ AttrVal( $name, 'model', '' ) }{devicename};
  382. }
  383. else {
  384. my $list = "sensorData:noArg firmware:noArg";
  385. $list .= " devicename:noArg"
  386. if (
  387. AttrVal( $name, 'model', 'thermoHygroSens' ) eq 'thermoHygroSens' );
  388. return "Unknown argument $cmd, choose one of $list";
  389. }
  390. CreateParamGatttool( $hash, $mod, $handle ) if ( $cmd ne 'sensorData' );
  391. return undef;
  392. }
  393. sub CreateParamGatttool($@) {
  394. my ( $hash, $mod, $handle, $value ) = @_;
  395. my $name = $hash->{NAME};
  396. my $mac = $hash->{BTMAC};
  397. Log3 $name, 4,
  398. "XiaomiBTLESens ($name) - Run CreateParamGatttool with mod: $mod";
  399. if ( $mod eq 'read' ) {
  400. $hash->{helper}{RUNNING_PID} = BlockingCall(
  401. "XiaomiBTLESens::ExecGatttool_Run",
  402. $name . "|" . $mac . "|" . $mod . "|" . $handle,
  403. "XiaomiBTLESens::ExecGatttool_Done",
  404. 90,
  405. "XiaomiBTLESens::ExecGatttool_Aborted",
  406. $hash
  407. ) unless ( exists( $hash->{helper}{RUNNING_PID} ) );
  408. readingsSingleUpdate( $hash, "state", "read sensor data", 1 );
  409. Log3 $name, 5,
  410. "XiaomiBTLESens ($name) - Read XiaomiBTLESens_ExecGatttool_Run $name|$mac|$mod|$handle";
  411. }
  412. elsif ( $mod eq 'write' ) {
  413. $hash->{helper}{RUNNING_PID} = BlockingCall(
  414. "XiaomiBTLESens::ExecGatttool_Run",
  415. $name . "|"
  416. . $mac . "|"
  417. . $mod . "|"
  418. . $handle . "|"
  419. . $value . "|"
  420. . $XiaomiModels{ AttrVal( $name, 'model', '' ) }{wdatalisten},
  421. "XiaomiBTLESens::ExecGatttool_Done",
  422. 90,
  423. "XiaomiBTLESens::ExecGatttool_Aborted",
  424. $hash
  425. ) unless ( exists( $hash->{helper}{RUNNING_PID} ) );
  426. readingsSingleUpdate( $hash, "state", "write sensor data", 1 );
  427. Log3 $name, 5,
  428. "XiaomiBTLESens ($name) - Write XiaomiBTLESens_ExecGatttool_Run $name|$mac|$mod|$handle|$value";
  429. }
  430. }
  431. sub ExecGatttool_Run($) {
  432. my $string = shift;
  433. my ( $name, $mac, $gattCmd, $handle, $value, $listen ) =
  434. split( "\\|", $string );
  435. my $sshHost = AttrVal( $name, "sshHost", "none" );
  436. my $gatttool;
  437. my $json_notification;
  438. $gatttool = qx(which gatttool) if ( $sshHost eq 'none' );
  439. $gatttool = qx(ssh $sshHost 'which gatttool') if ( $sshHost ne 'none' );
  440. chomp $gatttool;
  441. if ( defined($gatttool) and ($gatttool) ) {
  442. my $cmd;
  443. my $loop;
  444. my @gtResult;
  445. my $wait = 1;
  446. my $sshHost = AttrVal( $name, "sshHost", "none" );
  447. my $hci = AttrVal( $name, "hciDevice", "hci0" );
  448. $cmd .= "ssh $sshHost '" if ( $sshHost ne 'none' );
  449. $cmd .= "timeout 10 " if ($listen);
  450. $cmd .= "gatttool -i $hci -b $mac ";
  451. $cmd .= "--char-read -a $handle" if ( $gattCmd eq 'read' );
  452. $cmd .= "--char-write-req -a $handle -n $value"
  453. if ( $gattCmd eq 'write' );
  454. $cmd .= " --listen" if ($listen);
  455. $cmd .= " 2>&1 /dev/null";
  456. $cmd .= "'" if ( $sshHost ne 'none' );
  457. $cmd =
  458. "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'"
  459. if ( $sshHost ne 'none'
  460. and $gattCmd eq 'write'
  461. and AttrVal( $name, "model", "none" ) eq 'flowerSens' );
  462. while ($wait) {
  463. my $grepGatttool;
  464. my $gatttoolCmdlineStaticEscaped =
  465. CometBlueBTLE_CmdlinePreventGrepFalsePositive(
  466. "gatttool -i $hci -b $mac");
  467. $grepGatttool = qx(ps ax| grep -E \'$gatttoolCmdlineStaticEscaped\')
  468. if ( $sshHost eq 'none' );
  469. $grepGatttool =
  470. qx(ssh $sshHost 'ps ax| grep -E "$gatttoolCmdlineStaticEscaped"')
  471. if ( $sshHost ne 'none' );
  472. if ( not $grepGatttool =~ /^\s*$/ ) {
  473. Log3 $name, 3,
  474. "XiaomiBTLESens ($name) - ExecGatttool_Run: another gatttool process is running. waiting...";
  475. sleep(1);
  476. }
  477. else {
  478. $wait = 0;
  479. }
  480. }
  481. $loop = 0;
  482. do {
  483. Log3 $name, 5,
  484. "XiaomiBTLESens ($name) - ExecGatttool_Run: call gatttool with command: $cmd and loop $loop";
  485. @gtResult = split( ": ", qx($cmd) );
  486. Log3 $name, 5,
  487. "XiaomiBTLESens ($name) - ExecGatttool_Run: gatttool loop result "
  488. . join( ",", @gtResult );
  489. $loop++;
  490. $gtResult[0] = 'connect error'
  491. unless ( defined( $gtResult[0] ) );
  492. } while ( $loop < 5 and $gtResult[0] eq 'connect error' );
  493. Log3 $name, 4,
  494. "XiaomiBTLESens ($name) - ExecGatttool_Run: gatttool result "
  495. . join( ",", @gtResult );
  496. $handle = '0x35'
  497. if ( $sshHost ne 'none'
  498. and $gattCmd eq 'write'
  499. and AttrVal( $name, 'model', 'none' ) eq 'flowerSens' );
  500. $gattCmd = 'read'
  501. if ( $sshHost ne 'none'
  502. and $gattCmd eq 'write'
  503. and AttrVal( $name, 'model', 'none' ) eq 'flowerSens' );
  504. $gtResult[1] = 'no data response'
  505. unless ( defined( $gtResult[1] ) );
  506. if ( $gtResult[1] ne 'no data response' and $listen ) {
  507. ( $gtResult[1] ) = split( "\n", $gtResult[1] );
  508. $gtResult[1] =~ s/\\n//g;
  509. }
  510. $json_notification = encodeJSON( $gtResult[1] );
  511. if ( $gtResult[1] =~ /^([0-9a-f]{2}(\s?))*$/ ) {
  512. return "$name|$mac|ok|$gattCmd|$handle|$json_notification";
  513. }
  514. elsif ( $gtResult[0] ne 'connect error' and $gattCmd eq 'write' ) {
  515. if ( $sshHost ne 'none' ) {
  516. ExecGatttool_Run( $name . "|" . $mac . "|read|0x35" );
  517. }
  518. else {
  519. return "$name|$mac|ok|$gattCmd|$handle|$json_notification";
  520. }
  521. }
  522. else {
  523. return "$name|$mac|error|$gattCmd|$handle|$json_notification";
  524. }
  525. }
  526. else {
  527. $json_notification = encodeJSON(
  528. 'no gatttool binary found. Please check if bluez-package is properly installed'
  529. );
  530. return "$name|$mac|error|$gattCmd|$handle|$json_notification";
  531. }
  532. }
  533. sub ExecGatttool_Done($) {
  534. my $string = shift;
  535. my ( $name, $mac, $respstate, $gattCmd, $handle, $json_notification ) =
  536. split( "\\|", $string );
  537. my $hash = $defs{$name};
  538. delete( $hash->{helper}{RUNNING_PID} );
  539. Log3 $name, 5,
  540. "XiaomiBTLESens ($name) - ExecGatttool_Done: Helper is disabled. Stop processing"
  541. if ( $hash->{helper}{DISABLED} );
  542. return if ( $hash->{helper}{DISABLED} );
  543. Log3 $name, 5,
  544. "XiaomiBTLESens ($name) - ExecGatttool_Done: gatttool return string: $string";
  545. my $decode_json = eval { decode_json($json_notification) };
  546. if ($@) {
  547. Log3 $name, 4,
  548. "XiaomiBTLESens ($name) - ExecGatttool_Done: JSON error while request: $@";
  549. }
  550. if ( $respstate eq 'ok'
  551. and $gattCmd eq 'write'
  552. and AttrVal( $name, 'model', 'none' ) eq 'flowerSens' )
  553. {
  554. CreateParamGatttool( $hash, 'read',
  555. $XiaomiModels{ AttrVal( $name, 'model', '' ) }{rdata} );
  556. }
  557. elsif ( $respstate eq 'ok' ) {
  558. ProcessingNotification( $hash, $gattCmd, $handle,
  559. $decode_json->{gtResult} );
  560. }
  561. else {
  562. ProcessingErrors( $hash, $decode_json->{gtResult} );
  563. }
  564. }
  565. sub ExecGatttool_Aborted($) {
  566. my ($hash) = @_;
  567. my $name = $hash->{NAME};
  568. my %readings;
  569. delete( $hash->{helper}{RUNNING_PID} );
  570. readingsSingleUpdate( $hash, "state", "unreachable", 1 );
  571. $readings{'lastGattError'} =
  572. 'The BlockingCall Process terminated unexpectedly. Timedout';
  573. WriteReadings( $hash, \%readings );
  574. Log3 $name, 4,
  575. "XiaomiBTLESens ($name) - ExecGatttool_Aborted: The BlockingCall Process terminated unexpectedly. Timedout";
  576. }
  577. sub ProcessingNotification($@) {
  578. my ( $hash, $gattCmd, $handle, $notification ) = @_;
  579. my $name = $hash->{NAME};
  580. my $readings;
  581. Log3 $name, 4, "XiaomiBTLESens ($name) - ProcessingNotification";
  582. if ( AttrVal( $name, 'model', 'none' ) eq 'flowerSens' ) {
  583. if ( $handle eq '0x38' ) {
  584. ### Flower Sens - Read Firmware and Battery Data
  585. Log3 $name, 4,
  586. "XiaomiBTLESens ($name) - ProcessingNotification: handle 0x38";
  587. $readings = FlowerSensHandle0x38( $hash, $notification );
  588. }
  589. elsif ( $handle eq '0x35' ) {
  590. ### Flower Sens - Read Sensor Data
  591. Log3 $name, 4,
  592. "XiaomiBTLESens ($name) - ProcessingNotification: handle 0x35";
  593. $readings = FlowerSensHandle0x35( $hash, $notification );
  594. }
  595. }
  596. elsif ( AttrVal( $name, 'model', 'none' ) eq 'thermoHygroSens' ) {
  597. if ( $handle eq '0x18' ) {
  598. ### Thermo/Hygro Sens - Read Battery Data
  599. Log3 $name, 4,
  600. "XiaomiBTLESens ($name) - ProcessingNotification: handle 0x18";
  601. $readings = ThermoHygroSensHandle0x18( $hash, $notification );
  602. }
  603. elsif ( $handle eq '0x10' ) {
  604. ### Thermo/Hygro Sens - Read Sensor Data
  605. Log3 $name, 4,
  606. "XiaomiBTLESens ($name) - ProcessingNotification: handle 0x10";
  607. $readings = ThermoHygroSensHandle0x10( $hash, $notification );
  608. }
  609. elsif ( $handle eq '0x24' ) {
  610. ### Thermo/Hygro Sens - Read Firmware Data
  611. Log3 $name, 4,
  612. "XiaomiBTLESens ($name) - ProcessingNotification: handle 0x24";
  613. $readings = ThermoHygroSensHandle0x24( $hash, $notification );
  614. }
  615. elsif ( $handle eq '0x3' ) {
  616. ### Thermo/Hygro Sens - Read and Write Devicename
  617. Log3 $name, 4,
  618. "XiaomiBTLESens ($name) - ProcessingNotification: handle 0x3";
  619. return CreateParamGatttool( $hash, 'read',
  620. $XiaomiModels{ AttrVal( $name, 'model', '' ) }{devicename} )
  621. unless ( $gattCmd eq 'read' );
  622. $readings = ThermoHygroSensHandle0x3( $hash, $notification );
  623. }
  624. }
  625. WriteReadings( $hash, $readings );
  626. }
  627. sub FlowerSensHandle0x38($$) {
  628. ### FlowerSens - Read Firmware and Battery Data
  629. my ( $hash, $notification ) = @_;
  630. my $name = $hash->{NAME};
  631. my %readings;
  632. Log3 $name, 4, "XiaomiBTLESens ($name) - FlowerSens Handle0x38";
  633. my @dataBatFw = split( " ", $notification );
  634. ### neue Vereinheitlichung für Batteriereadings Forum #800017
  635. $readings{'batteryPercent'} = hex( "0x" . $dataBatFw[0] );
  636. $readings{'batteryState'} =
  637. ( hex( "0x" . $dataBatFw[0] ) > 15 ? "ok" : "low" );
  638. $readings{'firmware'} =
  639. ( $dataBatFw[2] - 30 ) . "."
  640. . ( $dataBatFw[4] - 30 ) . "."
  641. . ( $dataBatFw[6] - 30 );
  642. $hash->{helper}{CallBattery} = 1;
  643. CallBattery_Timestamp($hash);
  644. return \%readings;
  645. }
  646. sub FlowerSensHandle0x35($$) {
  647. ### Flower Sens - Read Sensor Data
  648. my ( $hash, $notification ) = @_;
  649. my $name = $hash->{NAME};
  650. my %readings;
  651. Log3 $name, 4, "XiaomiBTLESens ($name) - FlowerSens Handle0x35";
  652. my @dataSensor = split( " ", $notification );
  653. return stateRequest($hash)
  654. unless ( $dataSensor[0] ne "aa"
  655. and $dataSensor[1] ne "bb"
  656. and $dataSensor[2] ne "cc"
  657. and $dataSensor[3] ne "dd"
  658. and $dataSensor[4] ne "ee"
  659. and $dataSensor[5] ne "ff" );
  660. if ( $dataSensor[1] eq "ff" ) {
  661. $readings{'temperature'} =
  662. ( hex( "0x" . $dataSensor[1] . $dataSensor[0] ) - hex("0xffff") ) /
  663. 10;
  664. }
  665. else {
  666. $readings{'temperature'} =
  667. hex( "0x" . $dataSensor[1] . $dataSensor[0] ) / 10;
  668. }
  669. $readings{'lux'} = hex( "0x" . $dataSensor[4] . $dataSensor[3] );
  670. $readings{'moisture'} = hex( "0x" . $dataSensor[7] );
  671. $readings{'fertility'} = hex( "0x" . $dataSensor[9] . $dataSensor[8] );
  672. Log3 $name, 4,
  673. "XiaomiBTLESens ($name) - FlowerSens Handle0x35 - lux: "
  674. . $readings{lux}
  675. . ", moisture: "
  676. . $readings{moisture}
  677. . ", fertility: "
  678. . $readings{fertility};
  679. $hash->{helper}{CallBattery} = 0;
  680. return \%readings;
  681. }
  682. sub ThermoHygroSensHandle0x18($$) {
  683. ### Thermo/Hygro Sens - Battery Data
  684. my ( $hash, $notification ) = @_;
  685. my $name = $hash->{NAME};
  686. my %readings;
  687. Log3 $name, 4, "XiaomiBTLESens ($name) - Thermo/Hygro Sens Handle0x18";
  688. chomp($notification);
  689. $notification =~ s/\s+//g;
  690. ### neue Vereinheitlichung für Batteriereadings Forum #800017
  691. $readings{'batteryPercent'} = hex( "0x" . $notification );
  692. $readings{'batteryState'} =
  693. ( hex( "0x" . $notification ) > 15 ? "ok" : "low" );
  694. $hash->{helper}{CallBattery} = 1;
  695. CallBattery_Timestamp($hash);
  696. return \%readings;
  697. }
  698. sub ThermoHygroSensHandle0x10($$) {
  699. ### Thermo/Hygro Sens - Read Sensor Data
  700. my ( $hash, $notification ) = @_;
  701. my $name = $hash->{NAME};
  702. my %readings;
  703. Log3 $name, 4, "XiaomiBTLESens ($name) - Thermo/Hygro Sens Handle0x10";
  704. return stateRequest($hash)
  705. unless ( $notification =~ /^([0-9a-f]{2}(\s?))*$/ );
  706. my @numberOfHex = split( ' ', $notification );
  707. $notification =~ s/\s+//g;
  708. $readings{'temperature'} = pack( 'H*', substr( $notification, 4, 8 ) );
  709. $readings{'humidity'} = pack( 'H*', substr( $notification, 18, 8 ) );
  710. $hash->{helper}{CallBattery} = 0;
  711. return \%readings;
  712. }
  713. sub ThermoHygroSensHandle0x24($$) {
  714. ### Thermo/Hygro Sens - Read Firmware Data
  715. my ( $hash, $notification ) = @_;
  716. my $name = $hash->{NAME};
  717. my %readings;
  718. Log3 $name, 4, "XiaomiBTLESens ($name) - Thermo/Hygro Sens Handle0x24";
  719. $notification =~ s/\s+//g;
  720. $readings{'firmware'} = pack( 'H*', $notification );
  721. $hash->{helper}{CallBattery} = 0;
  722. return \%readings;
  723. }
  724. sub ThermoHygroSensHandle0x3($$) {
  725. ### Thermo/Hygro Sens - Read and Write Devicename
  726. my ( $hash, $notification ) = @_;
  727. my $name = $hash->{NAME};
  728. my %readings;
  729. Log3 $name, 4, "XiaomiBTLESens ($name) - Thermo/Hygro Sens Handle0x3";
  730. $notification =~ s/\s+//g;
  731. $readings{'devicename'} = pack( 'H*', $notification );
  732. $hash->{helper}{CallBattery} = 0;
  733. return \%readings;
  734. }
  735. sub WriteReadings($$) {
  736. my ( $hash, $readings ) = @_;
  737. my $name = $hash->{NAME};
  738. readingsBeginUpdate($hash);
  739. while ( my ( $r, $v ) = each %{$readings} ) {
  740. readingsBulkUpdate( $hash, $r, $v );
  741. }
  742. readingsBulkUpdateIfChanged( $hash, "state",
  743. ( $readings->{'lastGattError'} ? 'error' : 'active' ) )
  744. if ( AttrVal( $name, 'model', 'none' ) eq 'flowerSens' );
  745. readingsBulkUpdateIfChanged(
  746. $hash, "state",
  747. (
  748. $readings->{'lastGattError'}
  749. ? 'error'
  750. : 'T: '
  751. . ReadingsVal( $name, 'temperature', 0 ) . ' H: '
  752. . ReadingsVal( $name, 'humidity', 0 )
  753. )
  754. ) if ( AttrVal( $name, 'model', 'none' ) eq 'thermoHygroSens' );
  755. readingsEndUpdate( $hash, 1 );
  756. if ( AttrVal( $name, 'model', 'none' ) eq 'flowerSens' ) {
  757. if ( defined( $readings->{temperature} ) ) {
  758. DoTrigger(
  759. $name,
  760. 'minFertility '
  761. . (
  762. $readings->{fertility} < AttrVal( $name, 'minFertility', 0 )
  763. ? 'low'
  764. : 'ok'
  765. )
  766. ) if ( AttrVal( $name, 'minFertility', 'none' ) ne 'none' );
  767. DoTrigger(
  768. $name,
  769. 'maxFertility '
  770. . (
  771. $readings->{fertility} > AttrVal( $name, 'maxFertility', 0 )
  772. ? 'high'
  773. : 'ok'
  774. )
  775. ) if ( AttrVal( $name, 'maxFertility', 'none' ) ne 'none' );
  776. DoTrigger(
  777. $name,
  778. 'minMoisture '
  779. . (
  780. $readings->{moisture} < AttrVal( $name, 'minMoisture', 0 )
  781. ? 'low'
  782. : 'ok'
  783. )
  784. ) if ( AttrVal( $name, 'minMoisture', 'none' ) ne 'none' );
  785. DoTrigger(
  786. $name,
  787. 'maxMoisture '
  788. . (
  789. $readings->{moisture} > AttrVal( $name, 'maxMoisture', 0 )
  790. ? 'high'
  791. : 'ok'
  792. )
  793. ) if ( AttrVal( $name, 'maxMoisture', 'none' ) ne 'none' );
  794. DoTrigger(
  795. $name,
  796. 'minLux '
  797. . (
  798. $readings->{lux} < AttrVal( $name, 'minLux', 0 )
  799. ? 'low'
  800. : 'ok'
  801. )
  802. ) if ( AttrVal( $name, 'minLux', 'none' ) ne 'none' );
  803. DoTrigger(
  804. $name,
  805. 'maxLux '
  806. . (
  807. $readings->{lux} > AttrVal( $name, 'maxLux', 0 )
  808. ? 'high'
  809. : 'ok'
  810. )
  811. ) if ( AttrVal( $name, 'maxLux', 'none' ) ne 'none' );
  812. }
  813. }
  814. if ( defined( $readings->{temperature} ) ) {
  815. DoTrigger(
  816. $name,
  817. 'minTemp '
  818. . (
  819. $readings->{temperature} < AttrVal( $name, 'minTemp', 0 )
  820. ? 'low'
  821. : 'ok'
  822. )
  823. ) if ( AttrVal( $name, 'minTemp', 'none' ) ne 'none' );
  824. DoTrigger(
  825. $name,
  826. 'maxTemp '
  827. . (
  828. $readings->{temperature} > AttrVal( $name, 'maxTemp', 0 )
  829. ? 'high'
  830. : 'ok'
  831. )
  832. ) if ( AttrVal( $name, 'maxTemp', 'none' ) ne 'none' );
  833. }
  834. Log3 $name, 4,
  835. "XiaomiBTLESens ($name) - WriteReadings: Readings were written";
  836. $hash->{helper}{CallSensDataCounter} = 0;
  837. stateRequest($hash) if ( $hash->{helper}{CallBattery} == 1 );
  838. }
  839. sub ProcessingErrors($$) {
  840. my ( $hash, $notification ) = @_;
  841. my $name = $hash->{NAME};
  842. my %readings;
  843. Log3 $name, 4, "XiaomiBTLESens ($name) - ProcessingErrors";
  844. $readings{'lastGattError'} = $notification;
  845. WriteReadings( $hash, \%readings );
  846. }
  847. #### my little Helper
  848. sub encodeJSON($) {
  849. my $gtResult = shift;
  850. chomp($gtResult);
  851. my %response = ( 'gtResult' => $gtResult );
  852. return encode_json( \%response );
  853. }
  854. ## Routinen damit Firmware und Batterie nur alle X male statt immer aufgerufen wird
  855. sub CallBattery_Timestamp($) {
  856. my $hash = shift;
  857. # get timestamp
  858. $hash->{helper}{updateTimeCallBattery} =
  859. gettimeofday(); # in seconds since the epoch
  860. $hash->{helper}{updateTimestampCallBattery} = FmtDateTime( gettimeofday() );
  861. }
  862. sub CallBattery_UpdateTimeAge($) {
  863. my $hash = shift;
  864. $hash->{helper}{updateTimeCallBattery} = 0
  865. if ( not defined( $hash->{helper}{updateTimeCallBattery} ) );
  866. my $UpdateTimeAge = gettimeofday() - $hash->{helper}{updateTimeCallBattery};
  867. return $UpdateTimeAge;
  868. }
  869. sub CallBattery_IsUpdateTimeAgeToOld($$) {
  870. my ( $hash, $maxAge ) = @_;
  871. return ( CallBattery_UpdateTimeAge($hash) > $maxAge ? 1 : 0 );
  872. }
  873. sub CreateDevicenameHEX($) {
  874. my $devicename = shift;
  875. my $devicenameHex = unpack( "H*", $devicename );
  876. return $devicenameHex;
  877. }
  878. sub CometBlueBTLE_CmdlinePreventGrepFalsePositive($) {
  879. # https://stackoverflow.com/questions/9375711/more-elegant-ps-aux-grep-v-grep
  880. # Given abysmal (since external-command-based) performance in the first place, we'd better
  881. # avoid an *additional* grep process plus pipe...
  882. my $cmdline = shift;
  883. $cmdline =~ s/(.)(.*)/[$1]$2/;
  884. return $cmdline;
  885. }
  886. 1;
  887. =pod
  888. =item device
  889. =item summary Modul to retrieves data from a Xiaomi BTLE Sensors
  890. =item summary_DE Modul um Daten vom Xiaomi BTLE Sensoren aus zu lesen
  891. =begin html
  892. <a name="XiaomiBTLESens"></a>
  893. <h3>Xiaomi BTLE Sensor</h3>
  894. <ul>
  895. <u><b>XiaomiBTLESens - Retrieves data from a Xiaomi BTLE Sensor</b></u>
  896. <br>
  897. With this module it is possible to read the data from a sensor and to set it as reading.</br>
  898. Gatttool and hcitool is required to use this modul. (apt-get install bluez)
  899. <br><br>
  900. <a name="XiaomiBTLESensdefine"></a>
  901. <b>Define</b>
  902. <ul><br>
  903. <code>define &lt;name&gt; XiaomiBTLESens &lt;BT-MAC&gt;</code>
  904. <br><br>
  905. Example:
  906. <ul><br>
  907. <code>define Weihnachtskaktus XiaomiBTLESens C4:7C:8D:62:42:6F</code><br>
  908. </ul>
  909. <br>
  910. This statement creates a XiaomiBTLESens with the name Weihnachtskaktus and the Bluetooth Mac C4:7C:8D:62:42:6F.<br>
  911. 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.
  912. </ul>
  913. <br><br>
  914. <a name="XiaomiBTLESensreadings"></a>
  915. <b>Readings</b>
  916. <ul>
  917. <li>state - Status of the flower sensor or error message if any errors.</li>
  918. <li>batteryState - current battery state dependent on batteryLevel.</li>
  919. <li>batteryPercent - current battery level in percent.</li>
  920. <li>fertility - Values for the fertilizer content</li>
  921. <li>firmware - current device firmware</li>
  922. <li>lux - current light intensity</li>
  923. <li>moisture - current moisture content</li>
  924. <li>temperature - current temperature</li>
  925. </ul>
  926. <br><br>
  927. <a name="XiaomiBTLESensset"></a>
  928. <b>Set</b>
  929. <ul>
  930. <li>devicename - set a devicename</li>
  931. <li>resetBatteryTimestamp - when the battery was changed</li>
  932. <br>
  933. </ul>
  934. <br><br>
  935. <a name="XiaomiBTLESensget"></a>
  936. <b>Get</b>
  937. <ul>
  938. <li>sensorData - retrieves the current data of the Xiaomi sensor</li>
  939. <li>devicename - fetch devicename</li>
  940. <li>firmware - fetch firmware</li>
  941. <br>
  942. </ul>
  943. <br><br>
  944. <a name="XiaomiBTLESensattribut"></a>
  945. <b>Attributes</b>
  946. <ul>
  947. <li>disable - disables the device</li>
  948. <li>disabledForIntervals - disable device for interval time (13:00-18:30 or 13:00-18:30 22:00-23:00)</li>
  949. <li>interval - interval in seconds for statusRequest</li>
  950. <li>minFertility - min fertility value for low warn event</li>
  951. <li>hciDevice - select bluetooth dongle device</li>
  952. <li>model - set model type</li>
  953. <li>maxFertility - max fertility value for High warn event</li>
  954. <li>minMoisture - min moisture value for low warn event</li>
  955. <li>maxMoisture - max moisture value for High warn event</li>
  956. <li>minTemp - min temperature value for low warn event</li>
  957. <li>maxTemp - max temperature value for high warn event</li>
  958. <li>minlux - min lux value for low warn event</li>
  959. <li>maxlux - max lux value for high warn event
  960. <br>
  961. Event Example for min/max Value's: 2017-03-16 11:08:05 XiaomiBTLESens Dracaena minMoisture low<br>
  962. Event Example for min/max Value's: 2017-03-16 11:08:06 XiaomiBTLESens Dracaena maxTemp high</li>
  963. <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/config file</li>
  964. <li>batteryFirmwareAge - how old can the reading befor fetch new data</li>
  965. <li>blockingCallLoglevel - Blocking.pm Loglevel for BlockingCall Logoutput</li>
  966. </ul>
  967. </ul>
  968. =end html
  969. =begin html_DE
  970. <a name="XiaomiBTLESens"></a>
  971. <h3>Xiaomi BTLE Sensor</h3>
  972. <ul>
  973. <u><b>XiaomiBTLESens - liest Daten von einem Xiaomi BTLE Sensor</b></u>
  974. <br />
  975. Dieser Modul liest Daten von einem Sensor und legt sie in den Readings ab.<br />
  976. Auf dem (Linux) FHEM-Server werden gatttool und hcitool vorausgesetzt. (sudo apt install bluez)
  977. <br /><br />
  978. <a name="XiaomiBTLESensdefine"></a>
  979. <b>Define</b>
  980. <ul><br />
  981. <code>define &lt;name&gt; XiaomiBTLESens &lt;BT-MAC&gt;</code>
  982. <br /><br />
  983. Beispiel:
  984. <ul><br />
  985. <code>define Weihnachtskaktus XiaomiBTLESens C4:7C:8D:62:42:6F</code><br />
  986. </ul>
  987. <br />
  988. Der Befehl legt ein Device vom Typ XiaomiBTLESens an mit dem Namen Weihnachtskaktus und der Bluetooth MAC C4:7C:8D:62:42:6F.<br />
  989. Nach dem Anlegen des Device und setzen des korrekten model Attributes werden umgehend und automatisch die aktuellen Daten vom betroffenen Xiaomi BTLE Sensor gelesen.
  990. </ul>
  991. <br /><br />
  992. <a name="XiaomiBTLESensreadings"></a>
  993. <b>Readings</b>
  994. <ul>
  995. <li>state - Status des BTLE Sensor oder eine Fehlermeldung falls Fehler beim letzten Kontakt auftraten.</li>
  996. <li>batteryState - aktueller Batterie-Status in Abhängigkeit vom Wert batteryLevel.</li>
  997. <li>batteryPercent - aktueller Ladestand der Batterie in Prozent.</li>
  998. <li>fertility - Wert des Fruchtbarkeitssensors (Bodenleitf&auml;higkeit)</li>
  999. <li>firmware - aktuelle Firmware-Version des BTLE Sensor</li>
  1000. <li>lastGattError - Fehlermeldungen vom gatttool</li>
  1001. <li>lux - aktuelle Lichtintensit&auml;t</li>
  1002. <li>moisture - aktueller Feuchtigkeitswert</li>
  1003. <li>temperature - aktuelle Temperatur</li>
  1004. </ul>
  1005. <br /><br />
  1006. <a name="XiaomiBTLESensset"></a>
  1007. <b>Set</b>
  1008. <ul>
  1009. <li>resetBatteryTimestamp - wenn die Batterie gewechselt wurde</li>
  1010. <li>devicename - setzt einen Devicenamen</li>
  1011. <br />
  1012. </ul>
  1013. <br /><br />
  1014. <a name="XiaomiBTLESensGet"></a>
  1015. <b>Get</b>
  1016. <ul>
  1017. <li>sensorData - aktive Abfrage der Sensors Werte</li>
  1018. <li>devicename - liest den Devicenamen aus</li>
  1019. <li>firmware - liest die Firmeware aus</li>
  1020. <br />
  1021. </ul>
  1022. <br /><br />
  1023. <a name="XiaomiBTLESensattribut"></a>
  1024. <b>Attribute</b>
  1025. <ul>
  1026. <li>disable - deaktiviert das Device</li>
  1027. <li>interval - Interval in Sekunden zwischen zwei Abfragen</li>
  1028. <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>
  1029. <li>minFertility - min Fruchtbarkeits-Grenzwert f&uuml;r ein Ereignis minFertility low </li>
  1030. <li>hciDevice - Auswahl bei mehreren Bluetooth Dongeln</li>
  1031. <li>model - setzt das Model</li>
  1032. <li>maxFertility - max Fruchtbarkeits-Grenzwert f&uuml;r ein Ereignis maxFertility high </li>
  1033. <li>minMoisture - min Feuchtigkeits-Grenzwert f&uuml;r ein Ereignis minMoisture low </li>
  1034. <li>maxMoisture - max Feuchtigkeits-Grenzwert f&uuml;r ein Ereignis maxMoisture high </li>
  1035. <li>minTemp - min Temperatur-Grenzwert f&uuml;r ein Ereignis minTemp low </li>
  1036. <li>maxTemp - max Temperatur-Grenzwert f&uuml;r ein Ereignis maxTemp high </li>
  1037. <li>minlux - min Helligkeits-Grenzwert f&uuml;r ein Ereignis minlux low </li>
  1038. <li>maxlux - max Helligkeits-Grenzwert f&uuml;r ein Ereignis maxlux high
  1039. <br /><br />Beispiele f&uuml;r min/max-Ereignisse:<br />
  1040. 2017-03-16 11:08:05 XiaomiBTLESens Dracaena minMoisture low<br />
  1041. 2017-03-16 11:08:06 XiaomiBTLESens Dracaena maxTemp high<br /><br /></li>
  1042. <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>
  1043. <li>batteryFirmwareAge - wie alt soll der Timestamp des Readings sein bevor eine Aktuallisierung statt findet</li>
  1044. <li>blockingCallLoglevel - Blocking.pm Loglevel für BlockingCall Logausgaben</li>
  1045. </ul>
  1046. </ul>
  1047. =end html_DE
  1048. =cut