73_PRESENCE.pm 82 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023
  1. # $Id: 73_PRESENCE.pm 15302 2017-10-22 11:32:19Z markusbloch $
  2. ##############################################################################
  3. #
  4. # 73_PRESENCE.pm
  5. # Checks for the presence of a mobile phone or tablet by network ping or bluetooth detection.
  6. # It reports the presence of this device as state.
  7. #
  8. # Copyright by Markus Bloch
  9. # e-mail: Notausstieg0309@googlemail.com
  10. #
  11. # This file is part of fhem.
  12. #
  13. # Fhem is free software: you can redistribute it and/or modify
  14. # it under the terms of the GNU General Public License as published by
  15. # the Free Software Foundation, either version 2 of the License, or
  16. # (at your option) any later version.
  17. #
  18. # Fhem 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. # You should have received a copy of the GNU General Public License
  24. # along with fhem. If not, see <http://www.gnu.org/licenses/>.
  25. #
  26. ##############################################################################
  27. package main;
  28. use strict;
  29. use warnings;
  30. use Blocking;
  31. use Time::HiRes qw(gettimeofday usleep sleep);
  32. use DevIo;
  33. sub
  34. PRESENCE_Initialize($)
  35. {
  36. my ($hash) = @_;
  37. # Provider
  38. $hash->{ReadFn} = "PRESENCE_Read";
  39. $hash->{ReadyFn} = "PRESENCE_Ready";
  40. $hash->{SetFn} = "PRESENCE_Set";
  41. $hash->{DefFn} = "PRESENCE_Define";
  42. $hash->{NotifyFn} = "PRESENCE_Notify";
  43. $hash->{UndefFn} = "PRESENCE_Undef";
  44. $hash->{AttrFn} = "PRESENCE_Attr";
  45. $hash->{AttrList} = "do_not_notify:0,1 ".
  46. "disable:0,1 ".
  47. "disabledForIntervals ".
  48. "fritzbox_speed:0,1 ".
  49. "ping_count:1,2,3,4,5,6,7,8,9,10 ".
  50. "bluetooth_hci_device ".
  51. "absenceThreshold:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 ".
  52. "presenceThreshold:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 ".
  53. "absenceTimeout ".
  54. "presenceTimeout ".
  55. "powerCmd ".$readingFnAttributes;
  56. }
  57. #####################################
  58. sub
  59. PRESENCE_Define($$)
  60. {
  61. my ($hash, $def) = @_;
  62. my @a = split("[ \t]+", $def);
  63. my $dev;
  64. my $username = getlogin || getpwuid($<) || "[unknown]";
  65. my $name = $hash->{NAME};
  66. $hash->{NOTIFYDEV} = "global";
  67. if(defined($a[2]) and defined($a[3]))
  68. {
  69. if($a[2] eq "local-bluetooth")
  70. {
  71. unless($a[3] =~ /^\s*([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}\s*$/)
  72. {
  73. my $msg = "given address is not a bluetooth hardware address";
  74. Log 2, "PRESENCE ($name) - ".$msg;
  75. return $msg
  76. }
  77. $hash->{MODE} = "local-bluetooth";
  78. $hash->{ADDRESS} = $a[3];
  79. $hash->{TIMEOUT_NORMAL} = (defined($a[4]) ? $a[4] : 30);
  80. $hash->{TIMEOUT_PRESENT} = (defined($a[5]) ? $a[5] : $hash->{TIMEOUT_NORMAL});
  81. }
  82. elsif($a[2] eq "fritzbox")
  83. {
  84. unless(-X "/usr/bin/ctlmgr_ctl")
  85. {
  86. my $msg = "this is not a fritzbox or you running FHEM with the AVM Beta Image. Please use the FHEM FritzBox Image from fhem.de";
  87. Log 2, "PRESENCE ($name) - ".$msg;
  88. return $msg;
  89. }
  90. unless($username eq "root")
  91. {
  92. my $msg = "FHEM is not running under root (currently $username) This check can only performed with root access to the FritzBox";
  93. Log 2, "PRESENCE ($name) - ".$msg;
  94. return $msg;
  95. }
  96. $hash->{MODE} = "fritzbox";
  97. $hash->{ADDRESS} = $a[3];
  98. $hash->{TIMEOUT_NORMAL} = (defined($a[4]) ? $a[4] : 30);
  99. $hash->{TIMEOUT_PRESENT} = (defined($a[5]) ? $a[5] : $hash->{TIMEOUT_NORMAL});
  100. }
  101. elsif($a[2] eq "lan-ping")
  102. {
  103. if(-X "/usr/bin/ctlmgr_ctl" and not $username eq "root")
  104. {
  105. my $msg = "FHEM is not running under root (currently $username) This check can only performed with root access to the FritzBox";
  106. Log 2, "PRESENCE ($name) - ".$msg;
  107. return $msg;
  108. }
  109. $hash->{MODE} = "lan-ping";
  110. $hash->{ADDRESS} = $a[3];
  111. $hash->{TIMEOUT_NORMAL} = (defined($a[4]) ? $a[4] : 30);
  112. $hash->{TIMEOUT_PRESENT} = (defined($a[5]) ? $a[5] : $hash->{TIMEOUT_NORMAL});
  113. }
  114. elsif($a[2] =~ /(shellscript|function)/)
  115. {
  116. if($def =~ /(\S+) \w+ (\S+) ["']{0,1}(.+?)['"]{0,1}\s*(\d*)\s*(\d*)$/s)
  117. {
  118. $hash->{MODE} = $2;
  119. $hash->{helper}{call} = $3;
  120. $hash->{TIMEOUT_NORMAL} = ($4 ne "" ? $4 : 30);
  121. $hash->{TIMEOUT_PRESENT} = ($5 ne "" ? $5 : $hash->{TIMEOUT_NORMAL});
  122. delete($hash->{helper}{ADDRESS});
  123. if($hash->{helper}{call} =~ /\|/)
  124. {
  125. my $msg = "The command contains a pipe ( | ) symbol, which is not allowed.";
  126. Log 2, "PRESENCE ($name) - ".$msg;
  127. return $msg;
  128. }
  129. if($hash->{MODE} eq "function" and not $hash->{helper}{call} =~ /^\{.+\}$/)
  130. {
  131. my $msg = "The function call must be encapsulated by brackets ( {...} ).";
  132. Log 2, "PRESENCE ($name) - ".$msg;
  133. return $msg;
  134. }
  135. }
  136. }
  137. elsif($a[2] eq "lan-bluetooth")
  138. {
  139. unless($a[3] =~ /^\s*([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}\s*$/)
  140. {
  141. my $msg = "given address is not a bluetooth hardware address";
  142. Log 2, "PRESENCE ($name) - ".$msg;
  143. return $msg
  144. }
  145. DevIo_CloseDev($hash);
  146. $hash->{MODE} = "lan-bluetooth";
  147. $hash->{ADDRESS} = $a[3];
  148. $hash->{TIMEOUT_NORMAL} = (defined($a[5]) ? $a[5] : 30);
  149. $hash->{TIMEOUT_PRESENT} = (defined($a[6]) ? $a[6] : $hash->{TIMEOUT_NORMAL});
  150. $dev = $a[4];
  151. $dev .= ":5222" if($dev !~ m/:/ && $dev ne "none" && $dev !~ m/\@/);
  152. $hash->{DeviceName} = $dev;
  153. }
  154. elsif($a[2] eq "event")
  155. {
  156. return "missing arguments for mode event. You need to provide two event regexp" unless(defined($a[4]));
  157. eval { qr/^$a[3]$/ };
  158. return "invalid absent regexp: $@" if($@);
  159. eval { qr/^$a[4]$/ };
  160. return "invalid present regexp: $@" if($@);
  161. $hash->{MODE} = "event";
  162. $hash->{EVENT_ABSENT} = $a[3];
  163. $hash->{EVENT_PRESENT} = $a[4];
  164. $hash->{STATE} = "Initialized" if($init_done);
  165. InternalTimer(gettimeofday(), "PRESENCE_setNotfiyDev", $hash);
  166. }
  167. else
  168. {
  169. my $msg = "unknown mode \"".$a[2]."\" in define statement: Please use lan-ping, lan-bluetooth, local-bluetooth, fritzbox, shellscript, function or event";
  170. Log 2, "PRESENCE ($name) - ".$msg;
  171. return $msg
  172. }
  173. }
  174. else
  175. {
  176. my $msg = "wrong syntax for define statement: define <name> PRESENCE <mode> <device-address> [ <check-interval> [ <present-check-interval> ] ]";
  177. Log 2, "PRESENCE ($name) - $msg";
  178. return $msg;
  179. }
  180. my $timeout = $hash->{TIMEOUT_NORMAL};
  181. my $presence_timeout = $hash->{TIMEOUT_PRESENT};
  182. if(defined($timeout) and not $timeout =~ /^\d+$/)
  183. {
  184. my $msg = "check-interval must be a number";
  185. Log 2, "PRESENCE ($name) - ".$msg;
  186. return $msg;
  187. }
  188. if(defined($timeout) and not $timeout > 0)
  189. {
  190. my $msg = "check-interval must be greater than zero";
  191. Log 2, "PRESENCE ($name) -".$msg;
  192. return $msg;
  193. }
  194. if(defined($presence_timeout) and not $presence_timeout =~ /^\d+$/)
  195. {
  196. my $msg = "presence-check-interval must be a number";
  197. Log 2, "PRESENCE ($name) - ".$msg;
  198. return $msg;
  199. }
  200. if(defined($presence_timeout) and not $presence_timeout > 0)
  201. {
  202. my $msg = "presence-check-interval must be greater than zero";
  203. Log 2, "PRESENCE ($name) - ".$msg;
  204. return $msg;
  205. }
  206. delete($hash->{helper}{cachednr});
  207. readingsSingleUpdate($hash,"model",$hash->{MODE},0);
  208. return undef;
  209. }
  210. #####################################
  211. sub
  212. PRESENCE_Undef($$)
  213. {
  214. my ($hash, $arg) = @_;
  215. RemoveInternalTimer($hash);
  216. if(defined($hash->{helper}{RUNNING_PID}))
  217. {
  218. BlockingKill($hash->{helper}{RUNNING_PID});
  219. }
  220. DevIo_CloseDev($hash);
  221. return undef;
  222. }
  223. #####################################
  224. sub
  225. PRESENCE_Notify($$)
  226. {
  227. my ($hash,$dev) = @_;
  228. return undef if(!defined($hash) or !defined($dev));
  229. my $name = $hash->{NAME};
  230. my $dev_name = $dev->{NAME};
  231. return undef if(!defined($dev_name) or !defined($name));
  232. my $events = deviceEvents($dev,0);
  233. if($dev_name eq "global" and grep(m/^(?:DEFINED $name|MODIFIED $name|INITIALIZED|REREADCFG)$/, @{$events}))
  234. {
  235. if(grep(m/^(?:INITIALIZED|REREADCFG)$/, @{$events}))
  236. {
  237. $hash->{helper}{ABSENT_COUNT} = int(ReadingsVal($name, ".absenceThresholdCounter", 0));
  238. $hash->{helper}{PRESENT_COUNT} = int(ReadingsVal($name, ".presenceThresholdCounter", 0));
  239. }
  240. if($hash->{MODE} =~ /(lan-ping|local-bluetooth|fritzbox|shellscript|function)/)
  241. {
  242. delete $hash->{helper}{RUNNING_PID} if(defined($hash->{helper}{RUNNING_PID}));
  243. RemoveInternalTimer($hash);
  244. InternalTimer(gettimeofday(), "PRESENCE_StartLocalScan", $hash, 0) unless($hash->{helper}{DISABLED});
  245. return undef;
  246. }
  247. elsif($hash->{MODE} eq "lan-bluetooth")
  248. {
  249. delete($hash->{NEXT_OPEN}) if(exists($hash->{NEXT_OPEN}));
  250. return DevIo_OpenDev($hash, 0, "PRESENCE_DoInit");
  251. }
  252. }
  253. elsif($hash->{MODE} eq "event")
  254. {
  255. return undef if($hash->{helper}{DISABLED});
  256. my $re_present = $hash->{EVENT_PRESENT};
  257. my $re_absent = $hash->{EVENT_ABSENT};
  258. Log3 $name, 5, "PRESENCE ($name) - processing events from $dev_name";
  259. foreach my $event (@{$events})
  260. {
  261. if($dev_name =~ m/^$re_present$/ or "$dev_name:$event" =~ m/^$re_present$/)
  262. {
  263. Log3 $name, 5, "PRESENCE ($name) - $dev_name:$event matched present regexp";
  264. readingsBeginUpdate($hash);
  265. PRESENCE_ProcessState($hash, "present");
  266. readingsEndUpdate($hash, 1);
  267. }
  268. elsif($dev_name =~ m/^$re_absent$/ or "$dev_name:$event" =~ m/^$re_absent$/)
  269. {
  270. Log3 $name, 5, "PRESENCE ($name) - $dev_name:$event matched absent regexp";
  271. readingsBeginUpdate($hash);
  272. PRESENCE_ProcessState($hash, "absent");
  273. readingsEndUpdate($hash, 1);
  274. }
  275. }
  276. }
  277. }
  278. #####################################
  279. sub
  280. PRESENCE_Set($@)
  281. {
  282. my ($hash, @a) = @_;
  283. my $name = $hash->{NAME};
  284. return "No Argument given" if(!defined($a[1]));
  285. my $usage = "Unknown argument ".$a[1].", choose one of statusRequest";
  286. my $powerCmd = AttrVal($name, "powerCmd", undef);
  287. $usage .= " power" if(defined($powerCmd));
  288. if($a[1] eq "statusRequest")
  289. {
  290. if($hash->{MODE} ne "lan-bluetooth")
  291. {
  292. Log3 $name, 5, "PRESENCE ($name) - starting local scan";
  293. return PRESENCE_StartLocalScan($hash, 1);
  294. }
  295. else
  296. {
  297. if(exists($hash->{FD}))
  298. {
  299. DevIo_SimpleWrite($hash, "now\n", 2);
  300. }
  301. else
  302. {
  303. return "PRESENCE Definition \"$name\" is not connected to ".$hash->{DeviceName};
  304. }
  305. }
  306. }
  307. elsif(defined($powerCmd) && $a[1] eq "power")
  308. {
  309. my %specials= (
  310. '%NAME' => $name,
  311. '%ADDRESS' => (defined($hash->{ADDRESS}) ? $hash->{ADDRESS} : ""),
  312. '%ARGUMENT' => (defined($a[2]) ? $a[2] : "")
  313. );
  314. $powerCmd = EvalSpecials($powerCmd, %specials);
  315. Log3 $name, 5, "PRESENCE ($name) - executing powerCmd: $powerCmd";
  316. my $return = AnalyzeCommandChain(undef, $powerCmd);
  317. if($return)
  318. {
  319. Log3 $name, 3, "PRESENCE ($name) - executed powerCmd failed: ".$return;
  320. readingsSingleUpdate($hash, "powerCmd", "failed",1);
  321. return "executed powerCmd failed: ".$return;
  322. }
  323. else
  324. {
  325. readingsSingleUpdate($hash, "powerCmd", "executed",1);
  326. }
  327. return undef;
  328. }
  329. else
  330. {
  331. return $usage;
  332. }
  333. }
  334. ##########################
  335. sub
  336. PRESENCE_Attr(@)
  337. {
  338. my @a = @_;
  339. my $hash = $defs{$a[1]};
  340. if($a[0] eq "set" && $a[2] eq "disable")
  341. {
  342. if($a[3] eq "0")
  343. {
  344. readingsSingleUpdate($hash, "state", "defined",0) if(exists($hash->{helper}{DISABLED}) and $hash->{helper}{DISABLED} == 1);
  345. if(defined($hash->{DeviceName}))
  346. {
  347. if(defined($hash->{FD}))
  348. {
  349. PRESENCE_DoInit($hash) if(exists($hash->{helper}{DISABLED}));
  350. $hash->{helper}{DISABLED} = 0;
  351. }
  352. else
  353. {
  354. $hash->{helper}{DISABLED} = 0;
  355. DevIo_OpenDev($hash, 0, "PRESENCE_DoInit");
  356. }
  357. }
  358. else
  359. {
  360. $hash->{helper}{DISABLED} = 0;
  361. PRESENCE_StartLocalScan($hash);
  362. }
  363. $hash->{helper}{DISABLED} = 0;
  364. }
  365. elsif($a[3] eq "1")
  366. {
  367. if(defined($hash->{FD}))
  368. {
  369. DevIo_SimpleWrite($hash, "stop\n", 2);
  370. }
  371. RemoveInternalTimer($hash);
  372. $hash->{helper}{DISABLED} = 1;
  373. readingsSingleUpdate($hash, "state", "disabled",1);
  374. }
  375. }
  376. elsif($a[0] eq "del" && $a[2] eq "disable")
  377. {
  378. readingsSingleUpdate($hash, "state", "defined",0) if(exists($hash->{helper}{DISABLED}) and $hash->{helper}{DISABLED} == 1);
  379. if(defined($hash->{DeviceName}))
  380. {
  381. $hash->{helper}{DISABLED} = 0;
  382. if(defined($hash->{FD}))
  383. {
  384. PRESENCE_DoInit($hash) if(exists($hash->{helper}{DISABLED}));
  385. }
  386. else
  387. {
  388. DevIo_OpenDev($hash, 0, "PRESENCE_DoInit");
  389. }
  390. }
  391. else
  392. {
  393. $hash->{helper}{DISABLED} = 0;
  394. PRESENCE_StartLocalScan($hash);
  395. }
  396. }
  397. elsif($a[0] eq "set" and $a[2] eq "powerOnFn")
  398. {
  399. my $powerOnFn = $a[3];
  400. $powerOnFn =~ s/^\s+//;
  401. $powerOnFn =~ s/\s+$//;
  402. if($powerOnFn eq "")
  403. {
  404. return "powerOnFn contains no value";
  405. }
  406. }
  407. elsif($a[0] eq "set" and $a[2] eq "absenceThreshold")
  408. {
  409. return $a[2]." must be a valid integer number" if($a[3] !~ /^\d+$/);
  410. return $a[2]." is not applicable for mode 'event'" if($hash->{MODE} eq "event");
  411. }
  412. elsif($a[0] eq "set" and $a[2] eq "presenceThreshold")
  413. {
  414. return $a[2]." must be a valid integer number" if($a[3] !~ /^\d+$/);
  415. return $a[2]." is not applicable for mode 'event'" if($hash->{MODE} eq "event");
  416. }
  417. elsif($a[0] eq "set" and $a[2] eq "absenceTimeout")
  418. {
  419. return $a[2]." is only applicable for mode 'event'" if($hash->{MODE} ne "event");
  420. if($a[3] !~ /^\d?\d(?::\d\d){0,2}$/)
  421. {
  422. return "not a valid time frame value. See commandref for the correct syntax.";
  423. }
  424. }
  425. elsif($a[0] eq "set" and $a[2] eq "presenceTimeout")
  426. {
  427. return $a[2]." is only applicable for mode 'event'" if($hash->{MODE} ne "event");
  428. if($a[3] !~ /^\d?\d(?::\d\d){0,2}$/)
  429. {
  430. return "not a valid time frame value. See commandref for the correct syntax.";
  431. }
  432. }
  433. return undef;
  434. }
  435. #####################################
  436. # Receives an event and creates several readings for event triggering
  437. sub
  438. PRESENCE_Read($)
  439. {
  440. my ($hash) = @_;
  441. my $name = $hash->{NAME};
  442. my $buf = DevIo_SimpleRead($hash);
  443. return "" if(!defined($buf));
  444. chomp $buf;
  445. readingsBeginUpdate($hash);
  446. for my $line (split /^/, $buf)
  447. {
  448. Log3 $name, 5, "PRESENCE ($name) - received data: $line";
  449. if($line =~ /^absence|absent/)
  450. {
  451. if(!$hash->{helper}{DISABLED} and $hash->{helper}{CURRENT_TIMEOUT} eq "present" and $hash->{TIMEOUT_NORMAL} != $hash->{TIMEOUT_PRESENT})
  452. {
  453. $hash->{helper}{CURRENT_TIMEOUT} = "normal";
  454. Log3 $name, 4 , "PRESENCE ($name) - changing to normal timeout every ".$hash->{TIMEOUT_NORMAL}." seconds";
  455. DevIo_SimpleWrite($hash, $hash->{ADDRESS}."|".$hash->{TIMEOUT_NORMAL}."\n", 2);
  456. }
  457. unless($hash->{helper}{DISABLED})
  458. {
  459. PRESENCE_ProcessState($hash, "absent");
  460. if($line=~ /^[^;]+;(.+)$/)
  461. {
  462. PRESENCE_ProcessAddonData($hash, $1);
  463. }
  464. }
  465. }
  466. elsif($line =~ /present;(.+?)$/)
  467. {
  468. if(!$hash->{helper}{DISABLED} and $hash->{helper}{CURRENT_TIMEOUT} eq "normal" and $hash->{TIMEOUT_NORMAL} != $hash->{TIMEOUT_PRESENT})
  469. {
  470. $hash->{helper}{CURRENT_TIMEOUT} = "present";
  471. Log3 $name, 4 , "PRESENCE ($name) - changing to present timeout every ".$hash->{TIMEOUT_PRESENT}." seconds";
  472. DevIo_SimpleWrite($hash, $hash->{ADDRESS}."|".$hash->{TIMEOUT_PRESENT}."\n", 2);
  473. }
  474. unless($hash->{helper}{DISABLED})
  475. {
  476. PRESENCE_ProcessState($hash, "present");
  477. my $data = $1;
  478. if($data =~ /\S=\S/)
  479. {
  480. PRESENCE_ProcessAddonData($hash, $data);
  481. }
  482. else
  483. {
  484. if($1 =~ /^(.*);(.+)$/)
  485. {
  486. readingsBulkUpdate($hash, "room", $2);
  487. readingsBulkUpdate($hash, "device_name", $1);
  488. }
  489. else
  490. {
  491. readingsBulkUpdate($hash, "device_name", $1);
  492. }
  493. }
  494. }
  495. }
  496. elsif($line eq "command accepted")
  497. {
  498. readingsBulkUpdate($hash, "command_accepted", "yes");
  499. }
  500. elsif($line eq "command rejected")
  501. {
  502. readingsBulkUpdate($hash, "command_accepted", "no");
  503. }
  504. elsif($line =~ /socket_closed;(.+?)$/)
  505. {
  506. Log3 $name, 3, "PRESENCE ($name) - collectord lost connection to room $1";
  507. }
  508. elsif($line =~ /socket_reconnected;(.+?)$/)
  509. {
  510. Log3 $name , 3, "PRESENCE ($name) - collectord reconnected to room $1";
  511. }
  512. elsif($line =~ /error;(.+?)$/)
  513. {
  514. Log3 $name, 3, "PRESENCE ($name) - room $1 cannot execute hcitool to check device";
  515. }
  516. elsif($line =~ /error$/)
  517. {
  518. Log3 $name, 3, "PRESENCE ($name) - presenced cannot execute hcitool to check device ";
  519. }
  520. }
  521. readingsEndUpdate($hash, 1);
  522. }
  523. #####################################
  524. sub
  525. PRESENCE_Ready($)
  526. {
  527. my ($hash) = @_;
  528. return DevIo_OpenDev($hash, 1, "PRESENCE_DoInit") if($hash->{MODE} eq "lan-bluetooth");
  529. }
  530. ##########################################################################################################################
  531. #
  532. # Functions for local testing with Blocking.pm to ensure a smooth FHEM processing
  533. #
  534. ##########################################################################################################################
  535. #####################################
  536. sub PRESENCE_StartLocalScan($;$)
  537. {
  538. my ($hash, $local) = @_;
  539. my $name = $hash->{NAME};
  540. my $mode = $hash->{MODE};
  541. $local = 0 unless(defined($local));
  542. if(not (exists($hash->{ADDRESS}) or exists($hash->{helper}{call})))
  543. {
  544. return undef;
  545. }
  546. unless(exists($hash->{helper}{RUNNING_PID}))
  547. {
  548. $hash->{STATE} = "active" if($hash->{STATE} eq "???" or $hash->{STATE} eq "defined");
  549. if($local == 0)
  550. {
  551. Log3 $name, 5, "PRESENCE ($name) - stopping timer";
  552. RemoveInternalTimer($hash);
  553. }
  554. if($mode eq "local-bluetooth")
  555. {
  556. Log3 $name, 5, "PRESENCE ($name) - starting blocking call for mode local-bluetooth";
  557. $hash->{helper}{RUNNING_PID} = BlockingCall("PRESENCE_DoLocalBluetoothScan", $name."|".$hash->{ADDRESS}."|".$local."|".AttrVal($name, "bluetooth_hci_device", ""), "PRESENCE_ProcessLocalScan", 60, "PRESENCE_ProcessAbortedScan", $hash);
  558. }
  559. elsif($mode eq "lan-ping")
  560. {
  561. Log3 $name, 5, "PRESENCE ($name) - starting blocking call for mode lan-ping";
  562. $hash->{helper}{RUNNING_PID} = BlockingCall("PRESENCE_DoLocalPingScan", $name."|".$hash->{ADDRESS}."|".$local."|".AttrVal($name, "ping_count", "4"), "PRESENCE_ProcessLocalScan", 60, "PRESENCE_ProcessAbortedScan", $hash);
  563. }
  564. elsif($mode eq "fritzbox")
  565. {
  566. Log3 $name, 5, "PRESENCE ($name) - starting blocking call for mode fritzbox";
  567. $hash->{helper}{RUNNING_PID} = BlockingCall("PRESENCE_DoLocalFritzBoxScan", $name."|".$hash->{ADDRESS}."|".$local."|".AttrVal($name, "fritzbox_speed", "0"), "PRESENCE_ProcessLocalScan", 60, "PRESENCE_ProcessAbortedScan", $hash);
  568. }
  569. elsif($mode eq "shellscript")
  570. {
  571. Log3 $name, 5, "PRESENCE ($name) - starting blocking call for mode shellscript";
  572. $hash->{helper}{RUNNING_PID} = BlockingCall("PRESENCE_DoLocalShellScriptScan", $name."|".$hash->{helper}{call}."|".$local, "PRESENCE_ProcessLocalScan", 60, "PRESENCE_ProcessAbortedScan", $hash);
  573. }
  574. elsif($mode eq "function")
  575. {
  576. Log3 $name, 5, "PRESENCE ($name) - starting blocking call for mode function";
  577. $hash->{helper}{RUNNING_PID} = BlockingCall("PRESENCE_DoLocalFunctionScan", $name."|".$hash->{helper}{call}."|".$local, "PRESENCE_ProcessLocalScan", 60, "PRESENCE_ProcessAbortedScan", $hash);
  578. }
  579. if(!$hash->{helper}{RUNNING_PID} and $mode =~ /^local-bluetooth|lan-ping|fritzbox|shellscript|function$/)
  580. {
  581. delete($hash->{helper}{RUNNING_PID});
  582. my $seconds = (ReadingsVal($name, "state", "absent") eq "present" ? $hash->{TIMEOUT_PRESENT} : $hash->{TIMEOUT_NORMAL});
  583. Log3 $hash->{NAME}, 4, "PRESENCE ($name) - fork failed, rescheduling next check in $seconds seconds";
  584. RemoveInternalTimer($hash);
  585. InternalTimer(gettimeofday()+$seconds, "PRESENCE_StartLocalScan", $hash, 0) unless($hash->{helper}{DISABLED});
  586. }
  587. return undef;
  588. }
  589. else
  590. {
  591. Log3 $hash->{NAME}, 4, "PRESENCE ($name) - another check is currently running. skipping check";
  592. if($local == 0)
  593. {
  594. my $seconds = (ReadingsVal($name, "state", "absent") eq "present" ? $hash->{TIMEOUT_PRESENT} : $hash->{TIMEOUT_NORMAL});
  595. Log3 $hash->{NAME}, 4, "PRESENCE ($name) - rescheduling next check in $seconds seconds";
  596. RemoveInternalTimer($hash);
  597. InternalTimer(gettimeofday()+$seconds, "PRESENCE_StartLocalScan", $hash, 0) unless($hash->{helper}{DISABLED});
  598. }
  599. return "another check is currently running";
  600. }
  601. }
  602. #####################################
  603. sub PRESENCE_DoLocalPingScan($)
  604. {
  605. my ($string) = @_;
  606. my ($name, $device, $local, $count) = split("\\|", $string);
  607. Log3 $name, 5, "PRESENCE ($name) - starting ping scan: $string";
  608. my $retcode;
  609. my $return;
  610. my $temp;
  611. $SIG{CHLD} = 'IGNORE';
  612. if($^O =~ m/(Win|cygwin)/)
  613. {
  614. $temp = qx(ping -n $count -4 $device);
  615. if(defined($temp) and $temp ne "")
  616. {
  617. chomp $temp;
  618. Log3 $name, 5, "PRESENCE ($name) - ping command returned with output:\n$temp";
  619. $return = "$name|$local|".($temp =~ /TTL=\d+/ ? "present" : "absent");
  620. }
  621. else
  622. {
  623. $return = "$name|$local|error|Could not execute ping command: \"ping -n $count -4 $device\"";
  624. }
  625. }
  626. elsif($^O =~ m/solaris/)
  627. {
  628. $temp = qx(ping $device 4);
  629. if(defined($temp) and $temp ne "")
  630. {
  631. chomp $temp;
  632. Log3 $name, 5, "PRESENCE ($name) - ping command returned with output:\n$temp";
  633. $return = "$name|$local|".($temp =~ /is alive/ ? "present" : "absent");
  634. }
  635. else
  636. {
  637. $return = "$name|$local|error|Could not execute ping command: \"ping -n $count -4 $device\"";
  638. }
  639. }
  640. else
  641. {
  642. $temp = qx(ping -c $count $device 2>&1);
  643. if(defined($temp) and $temp ne "")
  644. {
  645. chomp $temp;
  646. Log3 $name, 5, "PRESENCE ($name) - ping command returned with output:\n$temp";
  647. $return = "$name|$local|".(($temp =~ /\d+ [Bb]ytes (from|von)/ and not $temp =~ /[Uu]nreachable/) ? "present" : "absent");
  648. }
  649. else
  650. {
  651. $return = "$name|$local|error|Could not execute ping command: \"ping -c $count $device\"";
  652. }
  653. }
  654. return $return;
  655. }
  656. #####################################
  657. sub PRESENCE_ExecuteFritzBoxCMD($$)
  658. {
  659. my ($name, $cmd) = @_;
  660. my $status;
  661. my $wait;
  662. while(-e "/var/tmp/fhem-PRESENCE-cmd-lock.tmp" and (stat("/var/tmp/fhem-PRESENCE-cmd-lock.tmp"))[9] > (gettimeofday() - 2))
  663. {
  664. $wait = int(rand(4))+2;
  665. Log3 $name, 5, "PRESENCE ($name) - ctlmgr_ctl is locked. waiting $wait seconds...";
  666. $wait = 1000000*$wait;
  667. usleep $wait;
  668. }
  669. unlink("/var/tmp/fhem-PRESENCE-cmd-lock.tmp") if(-e "/var/tmp/fhem-PRESENCE-cmd-lock.tmp");
  670. qx(touch /var/tmp/fhem-PRESENCE-cmd-lock.tmp);
  671. Log3 $name, 5, "PRESENCE ($name) - executing ctlmgr_ctl: $cmd";
  672. $status = qx($cmd);
  673. usleep 200000;
  674. unlink("/var/tmp/fhem-PRESENCE-cmd-lock.tmp") if(-e "/var/tmp/fhem-PRESENCE-cmd-lock.tmp");
  675. return $status;
  676. }
  677. #####################################
  678. sub PRESENCE_DoLocalFritzBoxScan($)
  679. {
  680. my ($string) = @_;
  681. my ($name, $device, $local, $speedcheck) = split("\\|", $string);
  682. Log3 $name, 5, "PRESENCE ($name) - starting fritzbox scan: $string";
  683. my $number = 0;
  684. my $status = 0;
  685. my $speed;
  686. $SIG{CHLD} = 'IGNORE';
  687. my $check_command = ($device =~ /^\s*([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}\s*$/ ? "mac" : "name");
  688. $device = uc $device if($device =~ /^\s*([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}\s*$/);
  689. if(defined($defs{$name}{helper}{cachednr}))
  690. {
  691. $number = $defs{$name}{helper}{cachednr};
  692. Log3 $name, 5, "PRESENCE ($name) - try checking $name as device $device with cached number $number";
  693. my $cached_name = "";
  694. $cached_name = PRESENCE_ExecuteFritzBoxCMD($name, "/usr/bin/ctlmgr_ctl r landevice settings/landevice$number/$check_command");
  695. chomp $cached_name;
  696. # only use the cached $number if it has still the correct device name
  697. if($cached_name eq $device)
  698. {
  699. Log3 $name, 5, "PRESENCE ($name) - checking state with cached number ($number)";
  700. $status = PRESENCE_ExecuteFritzBoxCMD($name, "/usr/bin/ctlmgr_ctl r landevice settings/landevice$number/active");
  701. chomp $status;
  702. if($status ne "0" and $speedcheck eq "1")
  703. {
  704. $speed = PRESENCE_ExecuteFritzBoxCMD($name, "/usr/bin/ctlmgr_ctl r landevice settings/landevice$number/speed");
  705. chomp $speed;
  706. Log3 $name, 5, "PRESENCE ($name) - speed check returned: $speed";
  707. $speed = undef if($speed eq "0");
  708. }
  709. Log3 $name, 5, "PRESENCE ($name) - ctlmgr_ctl (cached: $number) returned: $status";
  710. if(not $status =~ /^\s*\d+\s*$/)
  711. {
  712. return "$name|$local|error|could not execute ctlmgr_ctl (cached)";
  713. }
  714. return ($status == 0 ? "$name|$local|absent|$number" : "$name|$local|present|$number").($speedcheck == 1 and defined($speed) ? "|$speed" :"");
  715. }
  716. else
  717. {
  718. Log3 $name, 5, "PRESENCE ($name) - cached device ($cached_name) does not match expected device ($device). perform a full scan";
  719. }
  720. }
  721. my $max = PRESENCE_ExecuteFritzBoxCMD($name, "/usr/bin/ctlmgr_ctl r landevice settings/landevice/count");
  722. chomp $max;
  723. Log3 $name, 5, "PRESENCE ($name) - ctlmgr_ctl (getting device count) returned: $max";
  724. if(not $max =~ /^\s*\d+\s*$/)
  725. {
  726. return "$name|$local|error|could not execute ctlmgr_ctl";
  727. }
  728. my $net_device;
  729. $number = 0;
  730. while($number <= $max)
  731. {
  732. $net_device = PRESENCE_ExecuteFritzBoxCMD($name, "/usr/bin/ctlmgr_ctl r landevice settings/landevice$number/$check_command");
  733. chomp $net_device;
  734. Log3 $name, 5, "PRESENCE ($name) - checking device number $number ($net_device)";
  735. if($net_device eq $device)
  736. {
  737. $status = PRESENCE_ExecuteFritzBoxCMD($name, "/usr/bin/ctlmgr_ctl r landevice settings/landevice$number/active");
  738. chomp $status;
  739. if($status ne "0" and $speedcheck eq "1")
  740. {
  741. $speed = PRESENCE_ExecuteFritzBoxCMD($name, "/usr/bin/ctlmgr_ctl r landevice settings/landevice$number/speed");
  742. chomp $speed;
  743. Log3 $name, 5, "PRESENCE ($name) - speed check returned: $speed";
  744. $speed = undef if($speed eq "0");
  745. }
  746. Log3 $name, 5, "PRESENCE ($name) - state for device number $net_device is $status";
  747. last;
  748. }
  749. $number++;
  750. }
  751. return ($status == 0 ? "$name|$local|absent" : "$name|$local|present").($number <= $max ? "|$number" : "|").($speedcheck == 1 and defined($speed) ? "|$speed" : "");
  752. }
  753. #####################################
  754. sub PRESENCE_DoLocalBluetoothScan($)
  755. {
  756. my ($string) = @_;
  757. my ($name, $device, $local, $btdevice) = split("\\|", $string);
  758. my $devname;
  759. my $return;
  760. my $wait = 1;
  761. my $ps;
  762. my $psargs = "ax";
  763. Log3 $name, 5, "PRESENCE ($name) - starting bluetooth scan: $string";
  764. $SIG{CHLD} = 'IGNORE';
  765. if(qx(ps --help 2>&1) =~ /BusyBox/g)
  766. {
  767. Log3 $name, 5, "PRESENCE ($name) - found busybox variant of ps command, using \"w\" as parameter";
  768. $psargs = "w";
  769. }
  770. else
  771. {
  772. Log3 $name, 5, "PRESENCE ($name) - found standard variant of ps command, using \"ax\" as parameter";
  773. $psargs = "ax";
  774. }
  775. Log3 $name, 4, "PRESENCE ($name) - executing: which hcitool";
  776. my $hcitool = qx(which hcitool);
  777. Log3 $name, 4, "PRESENCE ($name) - 'which hcitool' returns: $hcitool";
  778. chomp $hcitool;
  779. if(-x $hcitool)
  780. {
  781. my $options = ($btdevice ? "-i $btdevice" : "");
  782. while($wait)
  783. { # check if another hcitool process is running
  784. $ps = qx(ps $psargs | grep hcitool | grep -v grep);
  785. if(not $ps =~ /^\s*$/)
  786. {
  787. # sleep between 1 and 5 seconds and try again
  788. Log3 $name, 5, "PRESENCE ($name) - another hcitool command is running. waiting...";
  789. sleep(rand(4)+1);
  790. }
  791. else
  792. {
  793. $wait = 0;
  794. }
  795. }
  796. Log3 $name, 5, "PRESENCE ($name) - executing: hcitool name $device";
  797. $devname = qx(hcitool $options name $device);
  798. chomp($devname);
  799. Log3 $name, 4, "PRESENCE ($name) - hcitool returned: $devname";
  800. if(not $devname =~ /^\s*$/)
  801. {
  802. $return = "$name|$local|present|$devname";
  803. }
  804. else
  805. {
  806. $return = "$name|$local|absent";
  807. }
  808. }
  809. else
  810. {
  811. $return = "$name|$local|error|no hcitool binary found. Please check that the bluez-package is properly installed";
  812. }
  813. return $return;
  814. }
  815. #####################################
  816. sub PRESENCE_DoLocalShellScriptScan($)
  817. {
  818. my ($string) = @_;
  819. my ($name, $call, $local) = split("\\|", $string);
  820. my $ret;
  821. my $return;
  822. Log3 $name, 5, "PRESENCE ($name) - starting local shell script scan: $string";
  823. $SIG{CHLD} = 'IGNORE';
  824. $ret = qx($call 2>&1);
  825. if(defined($ret))
  826. {
  827. chomp $ret;
  828. Log3 $name, 5, "PRESENCE ($name) - script output: $ret";
  829. }
  830. if(not defined($ret))
  831. {
  832. $return = "$name|$local|error|scriptcall doesn't return any output";
  833. }
  834. elsif($ret eq "1")
  835. {
  836. $return = "$name|$local|present";
  837. }
  838. elsif($ret eq "0")
  839. {
  840. $return = "$name|$local|absent";
  841. }
  842. else
  843. {
  844. $ret =~ s/\n/<<line-break>>/g;
  845. $return = "$name|$local|error|unexpected script output (expected 0 or 1): $ret";
  846. }
  847. return $return;
  848. }
  849. #####################################
  850. sub PRESENCE_DoLocalFunctionScan($)
  851. {
  852. my ($string) = @_;
  853. my ($name, $call, $local) = split("\\|", $string);
  854. my $ret;
  855. my $return;
  856. Log3 $name, 5, "PRESENCE ($name) - execute perl function: $string";
  857. $SIG{CHLD} = 'IGNORE';
  858. $ret = AnalyzeCommandChain(undef, $call);
  859. chomp $ret;
  860. Log3 $name, 5, "PRESENCE ($name) - function returned with: $ret";
  861. if(not defined($ret))
  862. {
  863. $return = "$name|$local|error|function call doesn't return any output";
  864. }
  865. elsif($ret eq "1")
  866. {
  867. $return = "$name|$local|present";
  868. }
  869. elsif($ret eq "0")
  870. {
  871. $return = "$name|$local|absent";
  872. }
  873. else
  874. {
  875. $ret =~ s/\n/<<line-break>>/g;
  876. $return = "$name|$local|error|unexpected function output (expected 0 or 1): $ret";
  877. }
  878. return $return;
  879. }
  880. #####################################
  881. sub PRESENCE_ProcessLocalScan($)
  882. {
  883. my ($string) = @_;
  884. return unless(defined($string));
  885. my @a = split("\\|",$string);
  886. my $hash = $defs{$a[0]};
  887. my $local = $a[1];
  888. my $name = $hash->{NAME};
  889. Log3 $hash->{NAME}, 5, "PRESENCE ($name) - blocking scan result: $string";
  890. delete($hash->{helper}{RUNNING_PID});
  891. if($hash->{helper}{DISABLED})
  892. {
  893. Log3 $hash->{NAME}, 5, "PRESENCE ($name) - don't process the scan result, as $name is disabled";
  894. return;
  895. }
  896. if(defined($hash->{helper}{RETRY_COUNT}))
  897. {
  898. Log3 $hash->{NAME}, 2, "PRESENCE ($name) - check returned a valid result after ".$hash->{helper}{RETRY_COUNT}." unsuccesful ".($hash->{helper}{RETRY_COUNT} > 1 ? "retries" : "retry");
  899. delete($hash->{helper}{RETRY_COUNT});
  900. }
  901. if($hash->{MODE} eq "fritzbox" and defined($a[3]) and $a[3] ne "")
  902. {
  903. $hash->{helper}{cachednr} = $a[3] if(($a[2] eq "present") || ($a[2] eq "absent"));
  904. }
  905. elsif($hash->{MODE} eq "fritzbox" and defined($hash->{helper}{cachednr}))
  906. {
  907. delete($hash->{helper}{cachednr});
  908. }
  909. readingsBeginUpdate($hash);
  910. PRESENCE_ProcessState($hash, $a[2]) unless($hash->{helper}{DISABLED});
  911. if($a[2] eq "present")
  912. {
  913. readingsBulkUpdate($hash, "device_name", $a[3]) if(defined($a[3]) and $hash->{MODE} =~ /^(lan-bluetooth|local-bluetooth)$/ );
  914. if($hash->{MODE} eq "fritzbox" and defined($a[4]))
  915. {
  916. readingsBulkUpdate($hash, "speed", $a[4]);
  917. }
  918. }
  919. elsif($a[2] eq "absent")
  920. {
  921. if($hash->{MODE} eq "fritzbox" and defined($a[4]))
  922. {
  923. readingsBulkUpdate($hash, "speed", $a[4]);
  924. }
  925. }
  926. elsif($a[2] eq "error")
  927. {
  928. $a[3] =~ s/<<line-break>>/\n/g;
  929. Log3 $hash->{NAME}, 2, "PRESENCE ($name) - error while processing check: ".$a[3];
  930. }
  931. readingsEndUpdate($hash, 1);
  932. #Schedule the next check withing $timeout if it is a regular run
  933. if($local eq "0")
  934. {
  935. my $seconds = ($a[2] eq "present" ? $hash->{TIMEOUT_PRESENT} : $hash->{TIMEOUT_NORMAL});
  936. Log3 $hash->{NAME}, 4, "PRESENCE ($name) - rescheduling next check in $seconds seconds";
  937. RemoveInternalTimer($hash);
  938. InternalTimer(gettimeofday()+$seconds, "PRESENCE_StartLocalScan", $hash) unless($hash->{helper}{DISABLED});
  939. }
  940. }
  941. #####################################
  942. sub PRESENCE_ProcessAbortedScan($)
  943. {
  944. my ($hash, $msg) = @_;
  945. my $name = $hash->{NAME};
  946. delete($hash->{helper}{RUNNING_PID});
  947. RemoveInternalTimer($hash);
  948. if(defined($hash->{helper}{RETRY_COUNT}))
  949. {
  950. if($hash->{helper}{RETRY_COUNT} >= 3)
  951. {
  952. Log3 $hash->{NAME}, 2, "PRESENCE ($name) - device could not be checked after ".$hash->{helper}{RETRY_COUNT}." ".($hash->{helper}{RETRY_COUNT} > 1 ? "retries" : "retry"). " (resuming normal operation): $msg" if($hash->{helper}{RETRY_COUNT} == 3);
  953. InternalTimer(gettimeofday()+10, "PRESENCE_StartLocalScan", $hash, 0) unless($hash->{helper}{DISABLED});
  954. $hash->{helper}{RETRY_COUNT}++;
  955. }
  956. else
  957. {
  958. Log3 $hash->{NAME}, 2, "PRESENCE ($name) - device could not be checked after ".$hash->{helper}{RETRY_COUNT}." ".($hash->{helper}{RETRY_COUNT} > 1 ? "retries" : "retry")." (retrying in 10 seconds): $msg";
  959. InternalTimer(gettimeofday()+10, "PRESENCE_StartLocalScan", $hash, 0) unless($hash->{helper}{DISABLED});
  960. $hash->{helper}{RETRY_COUNT}++;
  961. }
  962. }
  963. else
  964. {
  965. $hash->{helper}{RETRY_COUNT} = 1;
  966. InternalTimer(gettimeofday()+10, "PRESENCE_StartLocalScan", $hash, 0) unless($hash->{helper}{DISABLED});
  967. Log3 $hash->{NAME}, 2, "PRESENCE ($name) - device could not be checked (retrying in 10 seconds): $msg"
  968. }
  969. readingsSingleUpdate($hash, "state", "timeout",1);
  970. }
  971. ##########################################################################################################################
  972. #
  973. # Helper Functions
  974. #
  975. ##########################################################################################################################
  976. #####################################
  977. sub PRESENCE_DoInit($)
  978. {
  979. my ($hash) = @_;
  980. if(not exists($hash->{helper}{DISABLED}) or (exists($hash->{helper}{DISABLED}) and $hash->{helper}{DISABLED} == 0))
  981. {
  982. readingsSingleUpdate($hash, "state", "active",0);
  983. $hash->{helper}{CURRENT_TIMEOUT} = "normal";
  984. DevIo_SimpleWrite($hash, $hash->{ADDRESS}."|".$hash->{TIMEOUT_NORMAL}."\n", 2);
  985. }
  986. else
  987. {
  988. readingsSingleUpdate($hash, "state", "disabled",0);
  989. }
  990. return undef;
  991. }
  992. #####################################
  993. sub PRESENCE_calculateThreshold($)
  994. {
  995. my ($value) = @_;
  996. if(defined($value) and $value ne "")
  997. {
  998. if($value =~ /^(\d?\d):(\d\d)$/)
  999. {
  1000. $value = $1 * 60 + $2;
  1001. }
  1002. elsif($value =~ /^(\d?\d):(\d\d):(\d\d)$/)
  1003. {
  1004. $value = $1 * 3600 + $2 * 60 + $3;
  1005. }
  1006. elsif($value !~ /^\d?\d+$/)
  1007. {
  1008. $value = 0;
  1009. }
  1010. }
  1011. else
  1012. {
  1013. $value = 0;
  1014. }
  1015. return $value;
  1016. }
  1017. #####################################
  1018. sub PRESENCE_ThresholdTrigger($)
  1019. {
  1020. my ($hash) = @_;
  1021. if($hash->{helper}{DISABLED})
  1022. {
  1023. delete($hash->{helper}{NEW_STATE});
  1024. return undef;
  1025. }
  1026. if($hash->{helper}{NEW_STATE})
  1027. {
  1028. readingsBeginUpdate($hash);
  1029. readingsBulkUpdateIfChanged($hash, "state", $hash->{helper}{NEW_STATE});
  1030. readingsBulkUpdateIfChanged($hash, "presence", $hash->{helper}{NEW_STATE});
  1031. readingsEndUpdate($hash, 1);
  1032. $hash->{helper}{CURRENT_STATE} = $hash->{helper}{NEW_STATE};
  1033. delete($hash->{helper}{NEW_STATE});
  1034. }
  1035. }
  1036. #####################################
  1037. sub PRESENCE_ProcessState($$)
  1038. {
  1039. my ($hash, $state) = @_;
  1040. my $name = $hash->{NAME};
  1041. my $current_state = $hash->{helper}{CURRENT_STATE} ? $hash->{helper}{CURRENT_STATE} : "";
  1042. my $new_state = $hash->{helper}{NEW_STATE} ? $hash->{helper}{NEW_STATE} : "";
  1043. my $absenceThreshold = AttrVal($name, "absenceThreshold", 1);
  1044. my $presenceThreshold = AttrVal($name, "presenceThreshold", 1);
  1045. my $absenceTimeout = PRESENCE_calculateThreshold(AttrVal($name, "absenceTimeout", ""));
  1046. my $presenceTimeout = PRESENCE_calculateThreshold(AttrVal($name, "presenceTimeout", ""));
  1047. if($state eq "absent")
  1048. {
  1049. RemoveInternalTimer($hash, "PRESENCE_ThresholdTrigger");
  1050. my $count = ($hash->{helper}{ABSENT_COUNT} ? $hash->{helper}{ABSENT_COUNT} : 0);
  1051. if($hash->{MODE} eq "event")
  1052. {
  1053. if($absenceTimeout > 0 and $current_state ne "absent" and $new_state ne "absent")
  1054. {
  1055. readingsBulkUpdate($hash, "state", "maybe absent");
  1056. readingsBulkUpdate($hash, "presence", "maybe absent");
  1057. $hash->{helper}{NEW_STATE} = "absent";
  1058. InternalTimer(gettimeofday()+$absenceTimeout, "PRESENCE_ThresholdTrigger", $hash);
  1059. }
  1060. else
  1061. {
  1062. readingsBulkUpdate($hash, "state", "absent");
  1063. readingsBulkUpdate($hash, "presence", "absent");
  1064. $hash->{helper}{CURRENT_STATE} = "absent";
  1065. delete($hash->{helper}{NEW_STATE});
  1066. }
  1067. }
  1068. else
  1069. {
  1070. if(++$count >= $absenceThreshold)
  1071. {
  1072. readingsBulkUpdate($hash, ".presenceThresholdCounter", 0);
  1073. readingsBulkUpdate($hash, ".absenceThresholdCounter", ($count-1));
  1074. readingsBulkUpdate($hash, "state", "absent");
  1075. readingsBulkUpdate($hash, "presence", "absent");
  1076. }
  1077. else
  1078. {
  1079. $hash->{helper}{ABSENT_COUNT} = $count;
  1080. readingsBulkUpdate($hash, ".presenceThresholdCounter", 0);
  1081. readingsBulkUpdate($hash, ".absenceThresholdCounter", $count);
  1082. readingsBulkUpdate($hash, "state", "maybe absent");
  1083. readingsBulkUpdate($hash, "presence", "maybe absent");
  1084. Log3 $name, 4, "PRESENCE ($name) - device is absent after $count check".($count == 1 ? "" : "s").". ".($absenceThreshold-$count)." check".(($absenceThreshold-$count) == 1 ? "" : "s")." left before going absent";
  1085. }
  1086. }
  1087. delete($hash->{helper}{PRESENT_COUNT});
  1088. }
  1089. elsif($state eq "present")
  1090. {
  1091. RemoveInternalTimer($hash, "PRESENCE_ThresholdTrigger");
  1092. my $count = ($hash->{helper}{PRESENT_COUNT} ? $hash->{helper}{PRESENT_COUNT} : 0);
  1093. if($hash->{MODE} eq "event")
  1094. {
  1095. if($presenceTimeout > 0 and $current_state ne "present" and $new_state ne "present")
  1096. {
  1097. readingsBulkUpdate($hash, "state", "maybe present");
  1098. readingsBulkUpdate($hash, "presence", "maybe present");
  1099. $hash->{helper}{NEW_STATE} = "present";
  1100. InternalTimer(gettimeofday()+$presenceTimeout, "PRESENCE_ThresholdTrigger", $hash);
  1101. }
  1102. else
  1103. {
  1104. readingsBulkUpdate($hash, "state", "present");
  1105. readingsBulkUpdate($hash, "presence", "present");
  1106. $hash->{helper}{CURRENT_STATE} = "present";
  1107. delete($hash->{helper}{NEW_STATE});
  1108. }
  1109. }
  1110. else
  1111. {
  1112. if(++$count >= $presenceThreshold)
  1113. {
  1114. readingsBulkUpdate($hash, ".absenceThresholdCounter", 0);
  1115. readingsBulkUpdate($hash, ".presenceThresholdCounter", ($count-1));
  1116. readingsBulkUpdate($hash, "state", "present");
  1117. readingsBulkUpdate($hash, "presence", "present");
  1118. $hash->{helper}{CURRENT_STATE} = "present";
  1119. }
  1120. else
  1121. {
  1122. $hash->{helper}{PRESENT_COUNT} = $count;
  1123. readingsBulkUpdate($hash, ".absenceThresholdCounter", 0);
  1124. readingsBulkUpdate($hash, ".presenceThresholdCounter", $count);
  1125. readingsBulkUpdate($hash, "state", "maybe present");
  1126. readingsBulkUpdate($hash, "presence", "maybe present");
  1127. Log3 $name, 4, "PRESENCE ($name) - device is present after $count check".($count == 1 ? "" : "s").". ".($presenceThreshold-$count)." check".(($presenceThreshold-$count) == 1 ? "" : "s")." left before going present";
  1128. }
  1129. }
  1130. delete($hash->{helper}{ABSENT_COUNT});
  1131. }
  1132. else
  1133. {
  1134. readingsBulkUpdate($hash, "state", $state);
  1135. }
  1136. }
  1137. #####################################
  1138. sub PRESENCE_ProcessAddonData($$)
  1139. {
  1140. my ($hash, $data) = @_;
  1141. my ($a, $h) = parseParams($data, ";");
  1142. foreach my $key (sort keys %{$h})
  1143. {
  1144. readingsBulkUpdate($hash, $key, $h->{$key}) if(defined($h->{$key}));
  1145. }
  1146. return undef;
  1147. }
  1148. #####################################
  1149. sub PRESENCE_setNotfiyDev($)
  1150. {
  1151. my ($hash) = @_;
  1152. notifyRegexpChanged($hash,"(global|".$hash->{EVENT_PRESENT}."|".$hash->{EVENT_ABSENT}.")");
  1153. }
  1154. 1;
  1155. =pod
  1156. =item helper
  1157. =item summary provides presence detection checks
  1158. =item summary_DE stellt eine Anwesenheitserkennung zur Verf&uuml;gung
  1159. =begin html
  1160. <a name="PRESENCE"></a>
  1161. <h3>PRESENCE</h3>
  1162. <ul>
  1163. The PRESENCE module provides several possibilities to check the presence of mobile phones or similar mobile devices such as tablets.
  1164. <br><br>
  1165. This module provides several operational modes to serve your needs. These are:<br><br>
  1166. <ul>
  1167. <li><b>lan-ping</b> - A presence check of a device via network ping in your LAN/WLAN.</li>
  1168. <li><b>fritzbox</b> - A presence check by requesting the device state from the FritzBox internals (only available when running FHEM on a FritzBox!).</li>
  1169. <li><b>local-bluetooth</b> - A presence check by searching directly for a given bluetooth device nearby.</li>
  1170. <li><b>function</b> - A presence check by using your own perl function which returns a presence state.</li>
  1171. <li><b>shellscript</b> - A presence check by using an self-written script or binary which returns a presence state.</li>
  1172. <li><b>event</b> - A presence check by listening to FHEM events of other definitions.</li>
  1173. <li><b>lan-bluetooth</b> - A presence check of a bluetooth device via LAN network by connecting to a presenced or collectord instance.</li>
  1174. </ul>
  1175. <br>
  1176. Each mode can be optionally configured with a specific check interval and a present check interval.<br><br>
  1177. <ul>
  1178. <li>check-interval - The interval in seconds between each presence check. Default value: 30 seconds</li>
  1179. <li>present-check-interval - The interval in seconds between each presence check in case the device is <i>present</i>. Otherwise the normal check-interval will be used.</li>
  1180. </ul>
  1181. <br><br>
  1182. <a name="PRESENCEdefine"></a>
  1183. <b>Define</b><br><br>
  1184. <ul><b>Mode: lan-ping</b><br><br>
  1185. <code>define &lt;name&gt; PRESENCE lan-ping &lt;ip-address&gt; [ &lt;check-interval&gt; [ &lt;present-check-interval&gt; ] ]</code><br>
  1186. <br>
  1187. Checks for a network device via PING requests and reports its presence state.<br><br>
  1188. <u>Example</u><br><br>
  1189. <code>define iPhone PRESENCE lan-ping 192.168.179.21</code><br>
  1190. <br>
  1191. <b>Mode: fritzbox</b><br><br>
  1192. <code>define &lt;name&gt; PRESENCE fritzbox &lt;device-name/mac-address&gt; [ &lt;check-interval&gt; [ &lt;present-check-interval&gt; ] ]</code><br>
  1193. <br>
  1194. Checks for a network device by requesting the internal state on a FritzBox via ctlmgr_ctl. The device-name must be the same as shown in the network overview of the FritzBox or can be substituted by the MAC address with the format XX:XX:XX:XX:XX:XX<br><br>
  1195. <i>This check is only applicable when FHEM is running on a FritzBox! The detection of absence can take about 10-15 minutes!</i><br><br>
  1196. <u>Example</u><br><br>
  1197. <code>define iPhone PRESENCE fritzbox iPhone-6</code><br>
  1198. <code>define iPhone PRESENCE fritzbox 00:06:08:05:0D:00</code><br><br>
  1199. <b>Mode: local-bluetooth</b><br><br>
  1200. <code>define &lt;name&gt; PRESENCE local-bluetooth &lt;bluetooth-address&gt; [ &lt;check-interval&gt; [ &lt;present-check-interval&gt; ] ]</code><br>
  1201. <br>
  1202. Checks for a bluetooth device and reports its presence state. For this mode the shell command "hcitool" is required (provided with a <a href="http://www.bluez.org" target="_new">bluez</a> installation under Debian via APT), as well
  1203. as a functional bluetooth device directly attached to your machine.<br><br>
  1204. <u>Example</u><br><br>
  1205. <code>define iPhone PRESENCE local-bluetooth 0a:8d:4f:51:3c:8f</code><br><br>
  1206. <b>Mode: function</b><br><br>
  1207. <code>define &lt;name&gt; PRESENCE function {...} [ &lt;check-interval&gt; [ &lt;present-check-interval&gt; ] ]</code><br>
  1208. <br>
  1209. Checks for a presence state via perl-code. You can use a self-written perl function to obtain the presence state of a specific device (e.g. via SNMP check).<br><br>
  1210. The function must return 0 (absent) or 1 (present). An example can be found in the <a href="http://www.fhemwiki.de/wiki/Anwesenheitserkennung" target="_new">FHEM-Wiki</a>.<br><br>
  1211. <u>Example</u><br><br>
  1212. <code>define iPhone PRESENCE function {snmpCheck("10.0.1.1","0x44d77429f35c")}</code><br><br>
  1213. <b>Mode: shellscript</b><br><br>
  1214. <code>define &lt;name&gt; PRESENCE shellscript "&lt;path&gt; [&lt;arg1&gt;] [&lt;argN&gt;]..." [ &lt;check-interval&gt; [ &lt;present-check-interval&gt; ] ]</code><br>
  1215. <br>
  1216. Checks for a presence state via shell script. You can use a self-written script or binary in any language to obtain the presence state of a specific device (e.g. via SNMP check).<br><br>
  1217. The shell must return 0 (absent) or 1 (present) on <u>console (STDOUT)</u>. Any other values will be treated as an error<br><br>
  1218. <u>Example</u><br><br>
  1219. <code>define iPhone PRESENCE shellscript "/opt/check_device.sh iPhone"</code><br><br>
  1220. <b>Mode: event</b><br><br>
  1221. <code>define &lt;name&gt; PRESENCE evemt &lt;absent-regexp&gt; &lt;present-regexp&gt;</code><br>
  1222. <br>
  1223. Listens for events of other FHEM definitions to determine a presence state. You must provide two event regexp's in the same style as for the <a href="#notify">notify</a> module.<br><br>
  1224. If an event matches one of the provides regexps, the presence state will be changed.<br><br>
  1225. <u>Example</u><br><br>
  1226. <code>define Presence_John PRESENCE event Door_Switch:off Door_Switch:on</code><br><br>
  1227. <b>Mode: lan-bluetooth</b><br><br>
  1228. Checks for a bluetooth device with the help of presenced or collectord. They can be installed where-ever you like, just must be accessible via network.
  1229. The given device will be checked for presence status.<br>
  1230. <br>
  1231. <code>define &lt;name&gt; PRESENCE lan-bluetooth &lt;bluetooth-address&gt; &lt;ip-address&gt;[:port] [ &lt;check-interval&gt; ]</code><br>
  1232. <br>
  1233. The default port is 5111 (presenced). Alternatly you can use port 5222 (collectord)<br>
  1234. <br>
  1235. <u>Example</u><br><br>
  1236. <code>define iPhone PRESENCE lan-bluetooth 0a:4f:36:d8:f9:89 127.0.0.1:5222</code><br><br>
  1237. <u>presenced</u><br><br>
  1238. <ul>The presence is a perl network daemon, which provides presence checks of multiple bluetooth devices over network.
  1239. It listens on TCP port 5111 for incoming connections from a FHEM PRESENCE instance or a running collectord.<br>
  1240. <PRE>
  1241. Usage:
  1242. presenced [-d] [-p &lt;port&gt;] [-P &lt;filename&gt;]
  1243. presenced [-h | --help]
  1244. Options:
  1245. -p, --port
  1246. TCP Port which should be used (Default: 5111)
  1247. -P, --pid-file
  1248. PID file for storing the local process id (Default: /var/run/presenced.pid)
  1249. -d, --daemon
  1250. detach from terminal and run as background daemon
  1251. -v, --verbose
  1252. Print detailed log output
  1253. -h, --help
  1254. Print detailed help screen
  1255. </PRE>
  1256. It uses the hcitool command (provided by a <a href="http://www.bluez.org" target="_new">bluez</a> installation)
  1257. to make a paging request to the given bluetooth address (like 01:B4:5E:AD:F6:D3). The devices must not be visible, but
  1258. still activated to receive bluetooth requests.<br><br>
  1259. If a device is present, this is send to FHEM, as well as the device name as reading.<br><br>
  1260. The presenced is available as:<br><br>
  1261. <ul>
  1262. <li>direct perl script file: <a href="https://svn.fhem.de/trac/export/HEAD/trunk/fhem/contrib/PRESENCE/presenced" target="_new">presenced</a></li>
  1263. <li>.deb package for Debian (noarch): <a href="https://svn.fhem.de/trac/export/HEAD/trunk/fhem/contrib/PRESENCE/deb/presenced-1.4.deb" target="_new">presenced-1.4.deb</a></li>
  1264. <li>.deb package for Raspberry Pi (raspbian): <a href="https://svn.fhem.de/trac/export/HEAD/trunk/fhem/contrib/PRESENCE/deb/presenced-rpi-1.4.deb" target="_new">presenced-rpi-1.4.deb</a></li>
  1265. </ul>
  1266. </ul><br><br>
  1267. <u>lepresenced</u><br><br>
  1268. <ul>lepresenced is a Perl network daemon that provides presence checks of
  1269. multiple bluetooth devices over network. In contrast to presenced,
  1270. lepresenced covers <u>Bluetooth 4.0 (low energy) devices, i. e.
  1271. Gigaset G-Tags, FitBit Charges.</u>
  1272. lepresenced listens on TCP port 5333 for connections of a PRESENCE definition
  1273. or collectord.<br>
  1274. <PRE>
  1275. Usage:
  1276. lepresenced --bluetoothdevice &lt;bluetooth device&gt; --listenaddress &lt;listen address&gt; --listenport &lt;listen port&gt; --loglevel &lt;log level&gt; --daemon
  1277. lepresenced -b &lt;bluetooth device&gt; -a &lt;listen address&gt; -p &lt;listen port> -l &lt;log level&gt; -d
  1278. valid log levels:
  1279. LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG. Default: LOG_INFO
  1280. Examples:
  1281. lepresenced --bluetoothdevice hci0 --listenaddress 127.0.0.1 --listenport 5333 --daemon
  1282. lepresenced --loglevel LOG_DEBUG --daemon
  1283. </PRE>
  1284. To detect the presence of a device, it uses the command <i>hcitool lescan</i> (package:
  1285. <a href="http://www.bluez.org" target="_new">bluez</a>) to continuously listen to
  1286. beacons of Bluetooth LE devices.
  1287. <br><br>
  1288. If a device is present, this is send to FHEM, as well as the device name as reading.<br><br>
  1289. The presenced is available as:<br><br>
  1290. <ul>
  1291. <li>Perl script: <a href="https://svn.fhem.de/trac/export/HEAD/trunk/fhem/contrib/PRESENCE/lepresenced" target="_new">lepresenced</a></li>
  1292. <li>.deb package (noarch): <a href="https://svn.fhem.de/trac/export/HEAD/trunk/fhem/contrib/PRESENCE/deb/" target="_new">contrib/PRESENCE/deb/</a></li>
  1293. </ul>
  1294. </ul><br><br>
  1295. <u>collectord</u><br><br>
  1296. <ul>
  1297. The collectord is a perl network daemon, which handles connections to several presenced installations to search for multiple bluetooth devices over network.<br><br>
  1298. It listens on TCP port 5222 for incoming connections from a FHEM presence instance.
  1299. <PRE>
  1300. Usage:
  1301. collectord -c &lt;configfile&gt; [-d] [-p &lt;port&gt;] [-P &lt;pidfile&gt;]
  1302. collectord [-h | --help]
  1303. Options:
  1304. -c, --configfile &lt;configfile&gt;
  1305. The config file which contains the room and timeout definitions
  1306. -p, --port
  1307. TCP Port which should be used (Default: 5222)
  1308. -P, --pid-file
  1309. PID file for storing the local process id (Default: /var/run/collectord.pid)
  1310. -d, --daemon
  1311. detach from terminal and run as background daemon
  1312. -v, --verbose
  1313. Print detailed log output
  1314. -l, --logfile &lt;logfile&gt;
  1315. log to the given logfile
  1316. -h, --help
  1317. Print detailed help screen
  1318. </PRE>
  1319. Before the collectord can be used, it needs a config file, where all different rooms, which have a presenced detector, will be listed. This config file looks like:
  1320. <br><br>
  1321. <PRE>
  1322. # room definition
  1323. # ===============
  1324. #
  1325. [room-name] # name of the room
  1326. address=192.168.0.10 # ip-address or hostname
  1327. port=5111 # tcp port which should be used (5111 is default)
  1328. presence_timeout=120 # timeout in seconds for each check when devices are present
  1329. absence_timeout=20 # timeout in seconds for each check when devices are absent
  1330. [living room]
  1331. address=192.168.0.11
  1332. port=5111
  1333. presence_timeout=180
  1334. absence_timeout=20
  1335. </PRE>
  1336. If a device is present in any of the configured rooms, this is send to FHEM, as well as the device name as reading and the room which has detected the device.<br><br>
  1337. The collectord is available as:<br><br>
  1338. <ul>
  1339. <li>direct perl script file: <a href="https://svn.fhem.de/trac/export/HEAD/trunk/fhem/contrib/PRESENCE/collectord" target="_new">collectord</a></li>
  1340. <li>.deb package for Debian (noarch): <a href="https://svn.fhem.de/trac/export/HEAD/trunk/fhem/contrib/PRESENCE/deb/collectord-1.7.deb" target="_new">collectord-1.7.deb</a></li>
  1341. </ul>
  1342. </ul><br><br>
  1343. </ul>
  1344. <br>
  1345. <a name="PRESENCEset"></a>
  1346. <b>Set</b>
  1347. <ul>
  1348. <li><b>statusRequest</b> - Schedules an immediatly check.</li>
  1349. <li><b>power</b> - Executes the given power command which is set as attribute to power (on or off) the device (only when attribute "powerCmd" is set)</li>
  1350. </ul>
  1351. <br>
  1352. <a name="PRESENCEget"></a>
  1353. <b>Get</b>
  1354. <ul>
  1355. N/A
  1356. </ul>
  1357. <br>
  1358. <a name="PRESENCE_attr"></a>
  1359. <b>Attributes</b><br><br>
  1360. <ul>
  1361. <li><a href="#do_not_notify">do_not_notify</a></li>
  1362. <li><a href="#readingFnAttributes">readingFnAttributes</a></li><br>
  1363. <li><a name="PRESENCE_disable">disable</a></li>
  1364. If this attribute is activated, an active check will be disabled.<br><br>
  1365. Possible values: 0 => not disabled , 1 => disabled<br>
  1366. Default Value is 0 (not disabled)<br><br>
  1367. <li><a name="PRESENCE_absenceThreshold">absenceThreshold</a></li><br> <i>(Not in Mode "event" applicable)</i><br>
  1368. The number of checks that have to result in "absent" before the state of the PRESENCE definition is changed to "absent".
  1369. This can be used to verify the absence of a device with multiple check runs before the state is finally changed to "absent".
  1370. If this attribute is set to a value &gt;1, the reading state and presence will be set to "maybe absent" during the absence verification.<br><br>
  1371. Default Value is 1 (no absence verification)<br><br>
  1372. <li><a name="PRESENCE_presenceThreshold">presenceThreshold</a></li><br> <i>(Not in Mode "event" applicable)</i><br>
  1373. The number of checks that have to result in "present" before the state of the PRESENCE definition is changed to "present".
  1374. This can be used to verify the permanent presence of a device with multiple check runs before the state is finally changed to "present".
  1375. If this attribute is set to a value &gt;1, the reading state and presence will be set to "maybe present" during the presence verification.<br><br>
  1376. Default Value is 1 (no presence verification)<br><br>
  1377. <li><a name="PRESENCE_absenceTimeout">absenceTimeout</a></li><br> <i>(Only in Mode "event" applicable)</i><br>
  1378. The timeout after receiving an "absent" event, before the state of the PRESENCE definition is switched to "absent".
  1379. This can be used to verify the permanent absence by waiting a specific time frame to not receive an "present" event.
  1380. If this timeout is reached with no "present" event received in the meantime, the presence state will finally be set to "absent".
  1381. The timeout is given in HH:MM:SS format, where hours and minutes are optional.
  1382. If this attribute is set to a valid value, the reading state and presence will be set to "maybe absent" during the absence verification.<br><br>
  1383. Default Value is 0 (no absence verification)<br><br>
  1384. <li><a name="PRESENCE_presenceTimeout">presenceTimeout</a></li><br> <i>(Only in Mode "event" applicable)</i><br>
  1385. The timeout after receiving an "present" event, before the state of the PRESENCE definition is switched to "present".
  1386. This can be used to verify the permanent presence by waiting a specific time frame to not receive an "absent" event.
  1387. If this timeout is reached with no "absent" event received in the meantime, the presence state will finally be set to "present".
  1388. The timeout is given in HH:MM:SS format, where hours and minutes are optional.
  1389. If this attribute is set to a valid value, the reading state and presence will be set to "maybe present" during the presence verification.<br><br>
  1390. Default Value is 0 (no presence verification)<br><br>
  1391. <li><a name="PRESENCE_ping_count">ping_count</a></li> (Only in Mode "ping" applicable)<br>
  1392. Changes the count of the used ping packets to recognize a present state. Depending on your network performance sometimes a packet can be lost or blocked.<br><br>
  1393. Default Value is 4 (packets)<br><br>
  1394. <li><a name="PRESENCE_bluetooth_hci_device">bluetooth_hci_device</a></li> (Only in Mode "local-bluetooth" applicable)<br>
  1395. Set a specific bluetooth HCI device to use for scanning. If you have multiple bluetooth modules connected, you can select a specific one to use for scanning (e.g. hci0, hci1, ...).<br><br>
  1396. <li><a name="PRESENCE_fritzbox_speed">fritzbox_speed</a></li> (Only in Mode "fritzbox" applicable)<br>
  1397. When this attribute is enabled, the network speed is checked in addition to the device state.<br>
  1398. This only makes sense for wireless devices connected directly to the FritzBox.
  1399. <br><br>
  1400. Possible values: 0 => do not check speed, 1 => check speed when device is active<br>
  1401. Default value is 0 (no speed check)
  1402. <br><br>
  1403. <li><a name="PRESENCE_powerCmd">powerCmd</a></li><br>
  1404. Define a FHEM command, which powers on or off the device.<br><br>
  1405. When executing the powerCmd (set command: power) following placeholders will be replaced by there corresponding values:<br><br>
  1406. <ul>
  1407. <li><code>$NAME</code> - name of the PRESENCE definition</li>
  1408. <li><code>$ADDRESS</code> - the address of the PRESENCE definition as given in the define statement</li>
  1409. <li><code>$ARGUMENT</code> - the argument given to the power set command (e.g. "on" or "off)</li>
  1410. </ul>
  1411. <br>
  1412. Example FHEM commands:<br><br>
  1413. <ul>
  1414. <li><code>set PowerSwitch_1 on</code></li>
  1415. <li><code>set PowerSwitch_1 $ARGUMENT</code></li>
  1416. <li><code>"/opt/power_on.sh $ADDRESS"</code></li>
  1417. <li><code>{powerOn("$ADDRESS", "username", "password")}</code></li>
  1418. </ul>
  1419. </ul>
  1420. <br>
  1421. <a name="PRESENCEevents"></a>
  1422. <b>Generated Events:</b><br><br>
  1423. <ul>
  1424. <u>General Events:</u><br><br>
  1425. <ul>
  1426. <li><b>state</b>: (absent|maybe absent|present|disabled|error|timeout) - The state of the device, check errors or "disabled" when the <a href="#PRESENCE_disable">disable</a> attribute is enabled</li>
  1427. <li><b>presence</b>: (absent|maybe absent|present) - The state of the device. The value "maybe absent" only occurs if <a href="#PRESENCE_absenceThreshold">absenceThreshold</a> is activated.</li>
  1428. <li><b>powerCmd</b>: (executed|failed) - power command was executed or has failed</li>
  1429. </ul><br><br>
  1430. <u>Bluetooth specific events:</u><br><br>
  1431. <ul>
  1432. <li><b>device_name</b>: $name - The name of the Bluetooth device in case it's present</li>
  1433. </ul><br><br>
  1434. <u>presenced/collectord specific events:</u><br><br>
  1435. <ul>
  1436. <li><b>command_accepted</b>: $command_accepted (yes|no) - Was the last command acknowleged and accepted by the presenced or collectord?</li>
  1437. <li><b>room</b>: $room - If the module is connected with a collector daemon this event shows the room, where the device is located (as defined in the collectord config file)</li>
  1438. </ul>
  1439. </ul>
  1440. </ul>
  1441. =end html
  1442. =begin html_DE
  1443. <a name="PRESENCE"></a>
  1444. <h3>PRESENCE</h3>
  1445. <ul>
  1446. Das PRESENCE Module bietet mehrere M&ouml;glichkteiten um die Anwesenheit von Handys/Smartphones oder anderen mobilen Ger&auml;ten (z.B. Tablets) zu erkennen.
  1447. <br><br>
  1448. Dieses Modul bietet dazu mehrere Modis an um Anwesenheit zu erkennen. Diese sind:<br><br>
  1449. <ul>
  1450. <li><b>lan-ping</b> - Eine Erkennung auf Basis von Ping-Tests im lokalen LAN/WLAN</li>
  1451. <li><b>fritzbox</b> - Eine Erkennung aufgrund der internen Abfrage des Status auf der FritzBox (nur m&ouml;glich, wenn FHEM auf einer FritzBox l&auml;uft)</li>
  1452. <li><b>local-bluetooth</b> - Eine Erkennung auf Basis von Bluetooth-Abfragen durch den FHEM Server. Das Ger&auml;t muss dabei in Empfangsreichweite sein, aber nicht sichtbar sein</li>
  1453. <li><b>function</b> - Eine Erkennung mithilfe einer selbst geschriebenen Perl-Funktion, welche den Anwesenheitsstatus ermittelt.</li>
  1454. <li><b>shellscript</b> - Eine Erkennung mithilfe eines selbst geschriebenen Skriptes oder Programm (egal in welcher Sprache).</li>
  1455. <li><b>event</b> - Eine Erkennung basierend auf Events einer anderen Definition in FHEM. </li>
  1456. <li><b>lan-bluetooth</b> - Eine Erkennung durch Bluetooth-Abfragen via Netzwerk (LAN/WLAN) in ein oder mehreren R&auml;umen</li>
  1457. </ul>
  1458. <br>
  1459. Jeder Modus kann optional mit spezifischen Pr&uuml;f-Intervallen ausgef&uuml;hrt werden.<br><br>
  1460. <ul>
  1461. <li>check-interval - Das normale Pr&uuml;finterval in Sekunden f&uuml;r eine Anwesenheitspr&uuml;fung. Standardwert: 30 Sekunden</li>
  1462. <li>present-check-interval - Das Pr&uuml;finterval in Sekunden, wenn ein Ger&auml;t anwesend (<i>present</i>) ist. Falls nicht angegeben, wird der Wert aus check-interval verwendet</li>
  1463. </ul>
  1464. <br><br>
  1465. <a name="PRESENCEdefine"></a>
  1466. <b>Define</b><br><br>
  1467. <ul><b>Modus: lan-ping</b><br><br>
  1468. <code>define &lt;name&gt; PRESENCE lan-ping &lt;IP-Addresse oder Hostname&gt; [ &lt;Interval&gt; [ &lt;Anwesend-Interval&gt; ] ]</code><br>
  1469. <br>
  1470. Pr&uuml;ft ob ein Ger&auml;t &uuml;ber Netzwerk (&uuml;blicherweise WLAN) auf Ping-Anfragen reagiert und setzt entsprechend den Anwesenheitsstatus.<br><br>
  1471. <u>Beispiel</u><br><br>
  1472. <code>define iPhone PRESENCE lan-ping 192.168.179.21</code><br><br>
  1473. <b>Modus: fritzbox</b><br><br>
  1474. <code>define &lt;name&gt; PRESENCE fritzbox &lt;Ger&auml;tename/MAC-Adresse&gt; [ &lt;Interval&gt; [ &lt;Anwesend-Interval&gt; ] ]</code><br>
  1475. <br>
  1476. Pr&uuml;ft ob ein Ger&auml;t welches per WLAN mit der FritzBox verbunden ist, erreichbar durch Abfrage des Status mit dem Befehl ctlmgr_ctl.
  1477. Der Ger&auml;tename (Parameter: &lt;Ger&auml;tename&gt;) muss dem Namen entsprechen, welcher im Men&uuml;punkt "Heimnetz" auf der FritzBox-Oberfl&auml;che angezeigt wird oder kann durch die MAC-Adresse im Format XX:XX:XX:XX:XX:XX ersetzt werden.<br><br>
  1478. <i>Dieser Modus ist nur verwendbar, wenn FHEM auf einer FritzBox l&auml;uft! Die Erkennung einer Abwesenheit kann ca. 10-15 Minuten dauern!</i><br><br>
  1479. <u>Beispiel</u><br><br>
  1480. <code>define iPhone PRESENCE fritzbox iPhone-6</code><br>
  1481. <code>define iPhone PRESENCE fritzbox 00:06:08:05:0D:00</code><br><br>
  1482. <b>Modus: local-bluetooth</b><br><br>
  1483. <code>define &lt;name&gt; PRESENCE local-bluetooth &lt;Bluetooth-Adresse&gt; [ &lt;Interval&gt; [ &lt;Anwesend-Interval&gt; ] ]</code><br>
  1484. <br>
  1485. Pr&uuml;ft ob ein Bluetooth-Ger&auml;t abgefragt werden kann und meldet dies als Anwesenheit. F&uuml;r diesen Modus wird der Shell-Befehl "hcitool" ben&ouml;tigt
  1486. (wird durch das Paket <a href="http://www.bluez.org" target="_new">bluez</a> bereitgestellt), sowie ein funktionierender Bluetooth-Empf&auml;nger (intern oder als USB-Stick)<br><br>
  1487. <u>Beispiel</u><br><br>
  1488. <code>define iPhone PRESENCE local-bluetooth 0a:4f:36:d8:f9:8</code><br><br>
  1489. <b>Modus: function</b><br><br>
  1490. <code>define &lt;name&gt; PRESENCE function {...} [ &lt;Interval&gt; [ &lt;Anwesend-Interval&gt; ] ]</code><br>
  1491. <br>
  1492. Pr&uuml;ft den Anwesenheitsstatus mithilfe einer selbst geschriebenen Perl-Funktion (z.B. SNMP Abfrage).<br><br>
  1493. Diese Funktion muss 0 (Abwesend) oder 1 (Anwesend) zur&uuml;ckgeben. Ein entsprechendes Beispiel findet man im <a href="http://www.fhemwiki.de/wiki/Anwesenheitserkennung" target="_new">FHEM-Wiki</a>.<br><br>
  1494. <u>Beispiel</u><br><br>
  1495. <code>define iPhone PRESENCE function {snmpCheck("10.0.1.1","0x44d77429f35c")</code><br><br>
  1496. <b>Mode: shellscript</b><br><br>
  1497. <code>define &lt;name&gt; PRESENCE shellscript "&lt;Skript-Pfad&gt; [&lt;arg1&gt;] [&lt;argN&gt;]..." [ &lt;Interval&gt; [ &lt;Anwesend-Interval&gt; ] ]</code><br>
  1498. <br>
  1499. Pr&uuml;ft den Anwesenheitsstatus mithilfe eines selbst geschrieben Skripts oder Programmes (egal in welcher Programmier-/Skriptsprache)<br><br>
  1500. Der Aufruf dieses Skriptes muss eine 0 (Abwesend) oder 1 (Anwesend) auf der <u>Kommandozeile (STDOUT)</u> ausgeben. Alle anderen Werte/Ausgaben werden als Fehler behandelt.<br><br>
  1501. <u>Beispiel</u><br><br>
  1502. <code>define iPhone PRESENCE shellscript "/opt/check_device.sh iPhone"</code><br><br>
  1503. <b>Mode: event</b><br><br>
  1504. <code>define &lt;name&gt; PRESENCE event &lt;Abwesend-Regexp&gt; &lt;Anwesend-Regexp&gt;</code><br>
  1505. <br>
  1506. Lauscht auf Events von anderen Definitionen innerhalb von FHEM um die Anwesenheit darzustellen.
  1507. Die regul&auml;ren Ausdr&uuml;cke f&uuml;r An- und Abwesenheit entsprechen dabei der Syntax von <a href="#notify">notify</a>.<br><br>
  1508. Sobald innerhalb von FHEM ein Event gefeuert wird, welches auf die Abwesend-Regexp bzw. Anwesend-Regexp passt, wird der Status entsprechend in PRESENCE gesetzt.<br><br>
  1509. <u>Beispiel</u><br><br>
  1510. <code>define Anwesenheit PRESENCE event Tuerschalter:off Tuerschalter:on</code><br><br>
  1511. <b>Modus: lan-bluetooth</b><br><br>
  1512. Pr&uuml;ft ein Bluetooth-Ger&auml;t auf Anwesenheit &uuml;ber Netzwerk mit Hilfe von presenced oder collectord. Diese k&ouml;nnen auf jeder Maschine installiert werden,
  1513. welche eine Standard-Perl-Umgebung bereitstellt und &uuml;ber Netzwerk erreichbar ist.
  1514. <br>
  1515. <br>
  1516. <code>define &lt;name&gt; PRESENCE lan-bluetooth &lt;Bluetooth-Adresse&gt; &lt;IP-Adresse&gt;[:Port] [ &lt;Interval&gt; ]</code><br>
  1517. <br>
  1518. Der Standardport ist 5111 (presenced). Alternativ kann man den Port 5222 (collectord) nutzen. Generell ist der Port aber frei w&auml;hlbar.<br><br>
  1519. <u>Beispiel</u><br><br>
  1520. <code>define iPhone PRESENCE lan-bluetooth 0a:4f:36:d8:f9:89 127.0.0.1:5222</code><br><br>
  1521. <u>presenced</u><br><br>
  1522. <ul>Der presenced ist ein Perl Netzwerkdienst, welcher eine Bluetooth-Anwesenheitserkennung von ein oder mehreren Ger&auml;ten &uuml;ber Netzwerk bereitstellt.
  1523. Dieser lauscht standardm&auml;&szlig;ig auf TCP Port 5111 nach eingehenden Verbindungen von dem PRESENCE Modul oder einem collectord.<br>
  1524. <PRE>
  1525. Usage:
  1526. presenced -d [-p &lt;port&gt;] [-P &lt;filename&gt;]
  1527. presenced [-h | --help]
  1528. Options:
  1529. -p, --port
  1530. TCP Port which should be used (Default: 5111)
  1531. -P, --pid-file
  1532. PID file for storing the local process id (Default: /var/run/presenced.pid)
  1533. -d, --daemon
  1534. detach from terminal and run as background daemon
  1535. -v, --verbose
  1536. Print detailed log output
  1537. -h, --help
  1538. Print detailed help screen
  1539. </PRE>
  1540. Zur Bluetooth-Abfrage wird der Shell-Befehl "hcitool" verwendet (Paket: <a href="http://www.bluez.org" target="_new">bluez</a>)
  1541. um sogenannte "Paging-Request" an die gew&uuml;nschte Bluetooth Adresse (z.B. 01:B4:5E:AD:F6:D3) durchzuf&uuml;hren. Das Ger&auml;t muss dabei nicht sichtbar sein, allerdings st&auml;ndig aktiviert sein
  1542. um Bluetooth-Anfragen zu beantworten.
  1543. <br><br>
  1544. Wenn ein Ger&auml;t anwesend ist, wird dies an FHEM &uuml;bermittelt zusammen mit dem Ger&auml;tenamen als Reading.<br><br>
  1545. Der presenced ist zum Download verf&uuml;gbar als:<br><br>
  1546. <ul>
  1547. <li>Perl Skript: <a href="https://svn.fhem.de/trac/export/HEAD/trunk/fhem/contrib/PRESENCE/presenced" target="_new">presenced</a></li>
  1548. <li>.deb Paket f&uuml;r Debian (architekturunabh&auml;ngig): <a href="https://svn.fhem.de/trac/export/HEAD/trunk/fhem/contrib/PRESENCE/deb/presenced-1.4.deb" target="_new">presenced-1.4.deb</a></li>
  1549. <li>.deb Paket f&uuml;r Raspberry Pi (raspbian): <a href="https://svn.fhem.de/trac/export/HEAD/trunk/fhem/contrib/PRESENCE/deb/presenced-rpi-1.4.deb" target="_new">presenced-rpi-1.4.deb</a></li>
  1550. </ul>
  1551. </ul><br><br>
  1552. <u>lepresenced</u><br><br>
  1553. <ul>lepresenced ist ein Perl Netzwerkdienst, der analog zu presenced eine
  1554. Bluetooth-Anwesenheitserkennung von ein oder mehreren Ger&auml;ten
  1555. &uuml;ber Netzwerk bereitstellt. Im Gegensatz zu presenced unterst&uuml;tzt
  1556. lepresenced <u>Bluetooth 4.0 (Low Energy) Ger&auml;te wie z. B. Gigaset G-Tags,
  1557. FitBit Charges.</u>
  1558. lepresenced lauscht standardm&auml;&szlig;ig auf TCP Port 5333 und wartet
  1559. auf eingehende Verbindungen des PRESENCE-Moduls bzw. von collectord.<br>
  1560. <PRE>
  1561. Usage:
  1562. lepresenced --bluetoothdevice &lt;bluetooth device&gt; --listenaddress &lt;listen address&gt; --listenport &lt;listen port&gt; --loglevel &lt;log level&gt; --daemon
  1563. lepresenced -b &lt;bluetooth device&gt; -a &lt;listen address&gt; -p &lt;listen port> -l &lt;log level> -d
  1564. valid log levels:
  1565. LOG_CRIT, LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG. Default: LOG_INFO
  1566. Examples:
  1567. lepresenced --bluetoothdevice hci0 --listenaddress 127.0.0.1 --listenport 5333 --daemon
  1568. lepresenced --loglevel LOG_DEBUG --daemon
  1569. </PRE>
  1570. Zur Bluetooth-Abfrage wird der Befehl <i>hcitool lescan</i> (Paket:
  1571. <a href="http://www.bluez.org" target="_new">bluez</a>) verwendet, der
  1572. fortw&auml;hrend auf die Beacons der Bluetooth-LE-Ger&auml;te lauscht.
  1573. <br><br>
  1574. Wenn ein Ger&auml;t anwesend ist, wird dies an FHEM &uuml;bermittelt zusammen mit dem Ger&auml;tenamen als Reading.<br><br>
  1575. Der le presenced ist zum Download verf&uuml;gbar als:<br><br>
  1576. <ul>
  1577. <li>Perl Skript: <a href="https://svn.fhem.de/trac/export/HEAD/trunk/fhem/contrib/PRESENCE/lepresenced" target="_new">lepresenced</a></li>
  1578. <li>.deb Paket (architekturunabh&auml;ngig) unter <a href="https://svn.fhem.de/trac/export/HEAD/trunk/fhem/contrib/PRESENCE/deb/" target="_new">contrib/PRESENCE/deb/</a></li>
  1579. </ul>
  1580. </ul><br><br>
  1581. <u>collectord</u><br><br>
  1582. <ul>
  1583. Der collectord ist ein Perl Netzwerk Dienst, welcher Verbindungen zu mehreren presenced-Instanzen verwaltet um eine koordinierte Suche nach ein oder mehreren Bluetooth-Ger&auml;ten &uuml;ber Netzwerk durchzuf&uuml;hren.<br><br>
  1584. Er lauscht auf TCP port 5222 nach eingehenden Verbindungen von einem PRESENCE Modul.
  1585. <PRE>
  1586. Usage:
  1587. collectord -c &lt;configfile&gt; [-d] [-p &lt;port&gt;] [-P &lt;pidfile&gt;]
  1588. collectord [-h | --help]
  1589. Options:
  1590. -c, --configfile &lt;configfile&gt;
  1591. The config file which contains the room and timeout definitions
  1592. -p, --port
  1593. TCP Port which should be used (Default: 5222)
  1594. -P, --pid-file
  1595. PID file for storing the local process id (Default: /var/run/collectord.pid)
  1596. -d, --daemon
  1597. detach from terminal and run as background daemon
  1598. -v, --verbose
  1599. Print detailed log output
  1600. -l, --logfile &lt;logfile&gt;
  1601. log to the given logfile
  1602. -h, --help
  1603. Print detailed help screen
  1604. </PRE>
  1605. Bevor der collectord verwendet werden kann, ben&ouml;tigt er eine Konfigurationsdatei in welcher alle R&auml;ume mit einem presenced-Agenten eingetragen sind. Diese Datei sieht wie folgt aus:
  1606. <br><br>
  1607. <PRE>
  1608. # Raum Definitionen
  1609. # =================
  1610. #
  1611. [Raum-Name] # Name des Raumes
  1612. address=192.168.0.10 # IP-Adresse oder Hostname
  1613. port=5111 # TCP Port welcher benutzt werden soll (standardm&auml;&szlig;ig 5111)
  1614. presence_timeout=120 # Pr&uuml;finterval in Sekunden f&uuml;r jede Abfrage eines Ger&auml;tes, welches anwesend ist
  1615. absence_timeout=20 # Pr&uuml;finterval in Sekunden f&uuml;r jede Abfrage eines Ger&auml;tes, welches abwesend ist
  1616. [Wohnzimmer]
  1617. address=192.168.0.11
  1618. port=5111
  1619. presence_timeout=180
  1620. absence_timeout=20
  1621. </PRE>
  1622. <br>
  1623. Wenn ein Ger&auml;t in irgend einem Raum anwesend ist, wird dies an FHEM &uuml;bermittelt, zusammen mit dem Ger&auml;tenamen und dem Raum, in welchem das Ger&auml;t erkannt wurde.<br><br>
  1624. Der collectord ist zum Download verf&uuml;gbar als:<br><br>
  1625. <ul>
  1626. <li>Perl Skript: <a href="https://svn.fhem.de/trac/export/HEAD/trunk/fhem/contrib/PRESENCE/collectord" target="_new">collectord</a></li>
  1627. <li>.deb Paket f&uuml;r Debian (architekturunabh&auml;ngig): <a href="https://svn.fhem.de/trac/export/HEAD/trunk/fhem/contrib/PRESENCE/deb/collectord-1.7.deb" target="_new">collectord-1.7.deb</a></li>
  1628. </ul>
  1629. </ul>
  1630. </ul>
  1631. <br>
  1632. <a name="PRESENCEset"></a>
  1633. <b>Set</b>
  1634. <ul>
  1635. <li><b>statusRequest</b> - Startet einen sofortigen Check.</li>
  1636. <li><b>power</b> - Startet den powerCmd-Befehl welche durch den Parameter powerCmd angegeben ist (Nur wenn das Attribut "powerCmd" definiert ist)</li>
  1637. </ul>
  1638. <br>
  1639. <a name="PRESENCEget"></a>
  1640. <b>Get</b>
  1641. <ul>
  1642. N/A
  1643. </ul>
  1644. <br>
  1645. <a name="PRESENCEattr"></a>
  1646. <b>Attributes</b><br><br>
  1647. <ul>
  1648. <li><a href="#do_not_notify">do_not_notify</a></li>
  1649. <li><a href="#readingFnAttributes">readingFnAttributes</a></li><br>
  1650. <li><a name="PRESENCE_disable">disable</a></li>
  1651. Wenn dieses Attribut aktiviert ist, wird die Anwesenheitserkennung nicht mehr durchgef&uuml;hrt.<br><br>
  1652. M&ouml;gliche Werte: 0 => Erkennung durchf&uuml;hren , 1 => Keine Erkennungen durchf&uuml;hren<br>
  1653. Standardwert ist 0 (Erkennung durchf&uuml;hren)<br><br>
  1654. <li><a name="PRESENCE_absenceThreshold">absenceThreshold</a></li> <i>(Nicht im Modus "event" anwendbar)</i><br>
  1655. Die Anzahl an Checks, welche in "absent" resultieren m&uuml;ssen, bevor der Status der PRESENCE-Definition auf "absent" wechselt.
  1656. Mit dieser Funktion kann man die Abwesenheit eines Ger&auml;tes verifizieren bevor der Status final auf "absent" ge&auml;ndert wird.
  1657. Wenn dieses Attribut auf einen Wert &gt;1 gesetzt ist, werden die Readings "state" und "presence" auf den Wert "maybe absent" gesetzt,
  1658. bis der Status final auf "absent" wechselt.<br><br>
  1659. Standardwert ist 1 (keine Abwesenheitsverifizierung)<br><br>
  1660. <li><a name="PRESENCE_presenceThreshold">presenceThreshold</a></li> <i>(Nicht im Modus "event" anwendbar)</i><br>
  1661. Die Anzahl an Checks, welche in "present" resultieren m&uuml;ssen, bevor der Status der PRESENCE-Definition auf "present" wechselt.
  1662. Mit dieser Funktion kann man die Anwesenheit eines Ger&auml;tes verifizieren bevor der Status final auf "present" ge&auml;ndert wird.
  1663. Wenn dieses Attribut auf einen Wert &gt;1 gesetzt ist, werden die Readings "state" und "presence" auf den Wert "maybe present" gesetzt,
  1664. bis der Status final auf "present" wechselt.<br><br>
  1665. Standardwert ist 1 (keine Anwesenheitsverifizierung)<br><br>
  1666. <li><a name="PRESENCE_absenceTimeout">absenceTimeout</a></li> <i>(Nur im Modus "event" anwendbar)</i><br>
  1667. Die Dauer, die nach einem "absent"-Event gewartet werden soll, bis der Status der PRESENCE-Definition tats&auml;chlich auf "absent" ge&auml;ndert werden soll.
  1668. Die Dauer kann dabei im Format HH:MM:SS angegeben werden, wobei Stunden und Minuten optional sind.
  1669. Wenn dieses Attribut auf einen g&uuml;ltigen Wert gesetzt ist, werden die Readings "state" und "presence" bei einem "absent"-Event zun&auml;chst auf den Wert "maybe absent" gesetzt.
  1670. Sobald das parametrisierte Zeitfenster um ist, wird der Status final auf "absent" gesetzt.<br><br>
  1671. Standardwert ist 0 Sekunden (keine Statusverz&ouml;gerung)<br><br>
  1672. <li><a name="PRESENCE_presenceTimeout">presenceTimeout</a></li> <i>(Nur im Modus "event" anwendbar)</i><br>
  1673. Die Dauer, die nach einem "present"-Event gewartet werden soll, bis der Status der PRESENCE-Definition tats&auml;chlich auf "present" ge&auml;ndert werden soll.
  1674. Die Dauer kann dabei im Format HH:MM:SS angegeben werden, wobei Stunden und Minuten optional sind.
  1675. Wenn dieses Attribut auf einen g&uuml;ltigen Wert gesetzt ist, werden die Readings "state" und "presence" bei einem "present"-Event zun&auml;chst auf den Wert "maybe present" gesetzt.
  1676. Sobald das parametrisierte Zeitfenster um ist, wird der Status final auf "present" gesetzt.<br><br>
  1677. Standardwert ist 0 Sekunden (keine Statusverz&ouml;gerung)<br><br>
  1678. <li><a name="PRESENCE_ping_count">ping_count</a></li> (Nur im Modus "ping" anwendbar)<br>
  1679. Ver&auml;ndert die Anzahl der Ping-Pakete die gesendet werden sollen um die Anwesenheit zu erkennen.
  1680. Je nach Netzwerkstabilit&auml;t k&ouml;nnen erste Pakete verloren gehen oder blockiert werden.<br><br>
  1681. Standardwert ist 4 (Versuche)<br><br>
  1682. <li><a name="PRESENCE_bluetooth_hci_device">bluetooth_hci_device</a></li> (Nur im Modus "local-bluetooth" anwendbar)<br>
  1683. Sofern man mehrere Bluetooth-Empf&auml;nger verf&uuml;gbar hat, kann man mit diesem Attribut ein bestimmten Empf&auml;nger ausw&auml;hlen, welcher zur Erkennung verwendet werden soll (bspw. hci0, hci1, ...). Es muss dabei ein vorhandener HCI-Ger&auml;tename angegeben werden wie z.B. <code>hci0</code>.
  1684. <br><br>
  1685. <li><a name="PRESENCE_fritzbox_speed">fritzbox_speed</a></li> (Nur im Modus "fritzbox")<br>
  1686. Zus&auml;tzlich zum Status des Ger&auml;ts wird die aktuelle Verbindungsgeschwindigkeit ausgegeben<br>
  1687. Das macht nur bei WLAN Ger&auml;ten Sinn, die direkt mit der FritzBox verbunden sind. Bei abwesenden Ger&auml;ten wird als Geschwindigkeit 0 ausgegeben.
  1688. <br><br>
  1689. M&ouml;gliche Werte: 0 => Geschwindigkeit nicht pr&uuml;fen, 1 => Geschwindigkeit pr&uuml;fen<br>
  1690. Standardwert ist 0 (Keine Geschwindigkeitspr&uuml;fung)
  1691. <br><br>
  1692. <li><a name="PRESENCE_powerCmd">powerCmd</a></li><br>
  1693. Ein FHEM-Befehl, welcher das Ger&auml;t schalten kann.<br><br>
  1694. Wenn der power-Befehl ausgef&uuml;hrt wird (set-Befehl: power) werden folgende Platzhalter durch ihre entsprechenden Werte ersetzt:<br><br>
  1695. <ul>
  1696. <li><code>$NAME</code> - Name der PRESENCE-Definition</li>
  1697. <li><code>$ADDRESS</code> - Die &uuml;berwachte Addresse der PRESENCE Definition, wie sie im define-Befehl angegeben wurde.</li>
  1698. <li><code>$ARGUMENT</code> - Das Argument, was dem Set-Befehl "power" &uuml;bergeben wurde. (z.B. "on" oder "off")</li>
  1699. </ul>
  1700. <br>
  1701. Beispielhafte FHEM-Befehle:<br><br>
  1702. <ul>
  1703. <li><code>set PowerSwitch_1 on</code></li>
  1704. <li><code>set PowerSwitch_1 $ARGUMENT</code></li>
  1705. <li><code>"/opt/power_on.sh $ADDRESS"</code></li>
  1706. <li><code>{powerOn("$ADDRESS", "username", "password")}</code></li>
  1707. </ul>
  1708. </ul>
  1709. <br>
  1710. <a name="PRESENCEevents"></a>
  1711. <b>Generierte Events:</b><br><br>
  1712. <ul>
  1713. <u>Generelle Events:</u><br><br>
  1714. <ul>
  1715. <li><b>state</b>: (absent|maybe absent|present|disabled|error|timeout) - Der Anwesenheitsstatus eine Ger&auml;tes (absent = abwesend; present = anwesend) oder "disabled" wenn das <a href="#PRESENCE_disable">disable</a>-Attribut aktiviert ist</li>
  1716. <li><b>presence</b>: (absent|maybe absent|present) - Der Anwesenheitsstatus eine Ger&auml;tes (absent = abwesend; present = anwesend). Der Wert "maybe absent" (vielleicht abwesend) tritt nur auf, sofern das Attribut <a href="#PRESENCE_absenceThreshold">absenceThreshold</a> aktiviert ist.</li>
  1717. <li><b>powerCmd</b>: (executed|failed) - Ausf&uuml;hrung des power-Befehls war erfolgreich.</li>
  1718. </ul><br><br>
  1719. <u>Bluetooth-spezifische Events:</u><br><br>
  1720. <ul>
  1721. <li><b>device_name</b>: $name - Der Name des Bluetooth-Ger&auml;tes, wenn es anwesend (Status: present) ist</li>
  1722. </ul><br><br>
  1723. <u>presenced-/collectord-spezifische Events:</u><br><br>
  1724. <ul>
  1725. <li><b>command_accepted</b>: $command_accepted (yes|no) - Wurde das letzte Kommando an den presenced/collectord akzeptiert (yes = ja, no = nein)?</li>
  1726. <li><b>room</b>: $room - Wenn das Modul mit einem collectord verbunden ist, zeigt dieses Event den Raum an, in welchem dieses Ger&auml;t erkannt wurde (Raumname entsprechend der Konfigurationsdatei des collectord)</li>
  1727. </ul>
  1728. </ul>
  1729. </ul>
  1730. =end html_DE
  1731. =cut