20_ROOMMATE.pm 75 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851
  1. # $Id: 20_ROOMMATE.pm 13439 2017-02-18 21:10:08Z loredo $
  2. ##############################################################################
  3. #
  4. # 20_ROOMMATE.pm
  5. # Submodule of 10_RESIDENTS.
  6. #
  7. # Copyright by Julian Pawlowski
  8. # e-mail: julian.pawlowski at gmail.com
  9. #
  10. # This file is part of fhem.
  11. #
  12. # Fhem is free software: you can redistribute it and/or modify
  13. # it under the terms of the GNU General Public License as published by
  14. # the Free Software Foundation, either version 2 of the License, or
  15. # (at your option) any later version.
  16. #
  17. # Fhem is distributed in the hope that it will be useful,
  18. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  19. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  20. # GNU General Public License for more details.
  21. #
  22. # You should have received a copy of the GNU General Public License
  23. # along with fhem. If not, see <http://www.gnu.org/licenses/>.
  24. #
  25. ##############################################################################
  26. package main;
  27. use strict;
  28. use warnings;
  29. use Time::Local;
  30. use Data::Dumper;
  31. require RESIDENTStk;
  32. no if $] >= 5.017011, warnings => 'experimental';
  33. sub ROOMMATE_Set($@);
  34. sub ROOMMATE_Define($$);
  35. sub ROOMMATE_Notify($$);
  36. sub ROOMMATE_Undefine($$);
  37. ###################################
  38. sub ROOMMATE_Initialize($) {
  39. my ($hash) = @_;
  40. Log3 $hash, 5, "ROOMMATE_Initialize: Entering";
  41. $hash->{SetFn} = "ROOMMATE_Set";
  42. $hash->{DefFn} = "ROOMMATE_Define";
  43. $hash->{NotifyFn} = "ROOMMATE_Notify";
  44. $hash->{UndefFn} = "ROOMMATE_Undefine";
  45. $hash->{AttrList} =
  46. "rr_locationHome rr_locationWayhome rr_locationUnderway rr_autoGoneAfter:12,16,24,26,28,30,36,48,60 rr_showAllStates:0,1 rr_realname:group,alias rr_states:multiple-strict,home,gotosleep,asleep,awoken,absent,gone rr_locations rr_moods rr_moodDefault rr_moodSleepy rr_passPresenceTo rr_noDuration:0,1 rr_wakeupDevice rr_geofenceUUIDs rr_presenceDevices "
  47. . $readingFnAttributes;
  48. }
  49. ###################################
  50. sub ROOMMATE_Define($$) {
  51. my ( $hash, $def ) = @_;
  52. my @a = split( "[ \t][ \t]*", $def );
  53. my $name = $hash->{NAME};
  54. my $name_attr;
  55. Log3 $name, 5, "ROOMMATE $name: called function ROOMMATE_Define()";
  56. if ( int(@a) < 2 ) {
  57. my $msg =
  58. "Wrong syntax: define <name> ROOMMATE [RESIDENTS-DEVICE-NAMES]";
  59. Log3 $name, 4, $msg;
  60. return $msg;
  61. }
  62. $hash->{TYPE} = "ROOMMATE";
  63. my $parents = ( defined( $a[2] ) ? $a[2] : "" );
  64. # unregister at parent objects if we get modified
  65. my @registeredResidentgroups;
  66. my $modified = 0;
  67. if ( defined( $hash->{RESIDENTGROUPS} ) && $hash->{RESIDENTGROUPS} ne "" ) {
  68. $modified = 1;
  69. @registeredResidentgroups =
  70. split( /,/, $hash->{RESIDENTGROUPS} );
  71. # unregister at parent objects
  72. foreach my $parent (@registeredResidentgroups) {
  73. if ( defined( $defs{$parent} )
  74. && $defs{$parent}{TYPE} eq "RESIDENTS" )
  75. {
  76. fhem("set $parent unregister $name");
  77. Log3 $name, 4,
  78. "ROOMMATE $name: Unregistered at RESIDENTS device $parent";
  79. }
  80. }
  81. }
  82. # register at parent objects
  83. $hash->{RESIDENTGROUPS} = "";
  84. if ( $parents ne "" ) {
  85. @registeredResidentgroups = split( /,/, $parents );
  86. foreach my $parent (@registeredResidentgroups) {
  87. if ( !defined( $defs{$parent} ) ) {
  88. Log3 $name, 3,
  89. "ROOMMATE $name: Unable to register at RESIDENTS device $parent (not existing)";
  90. next;
  91. }
  92. if ( $defs{$parent}{TYPE} ne "RESIDENTS" ) {
  93. Log3 $name, 3,
  94. "ROOMMATE $name: Device $parent is not a RESIDENTS device (wrong type)";
  95. next;
  96. }
  97. fhem("set $parent register $name");
  98. $hash->{RESIDENTGROUPS} .= $parent . ",";
  99. Log3 $name, 4,
  100. "ROOMMATE $name: Registered at RESIDENTS device $parent";
  101. }
  102. }
  103. else {
  104. $modified = 0;
  105. }
  106. # set reverse pointer
  107. $modules{ROOMMATE}{defptr}{$name} = \$hash;
  108. readingsBeginUpdate($hash);
  109. # set default settings on first define
  110. if ( $init_done && !defined( $hash->{OLDDEF} ) ) {
  111. my $groupname = $name;
  112. $groupname =~ s/^rr_//;
  113. $attr{$name}{group} = $groupname;
  114. $attr{$name}{alias} = "Status";
  115. $attr{$name}{devStateIcon} =
  116. ".*home:user_available:absent .*absent:user_away:home .*gone:user_ext_away:home .*gotosleep:scene_toilet:asleep .*asleep:scene_sleeping:awoken .*awoken:scene_sleeping_alternat:home .*:user_unknown:home";
  117. $attr{$name}{icon} = "people_sensor";
  118. $attr{$name}{rr_realname} = "group";
  119. $attr{$name}{sortby} = "1";
  120. $attr{$name}{webCmd} = "state";
  121. $attr{$name}{room} = $attr{ $registeredResidentgroups[0] }{room}
  122. if ( @registeredResidentgroups
  123. && exists( $attr{ $registeredResidentgroups[0] }{room} ) );
  124. }
  125. # trigger for modified objects
  126. unless ( $modified == 0 ) {
  127. readingsBulkUpdate( $hash, "state", ReadingsVal( $name, "state", "" ) );
  128. }
  129. readingsEndUpdate( $hash, 1 );
  130. # run timers
  131. InternalTimer(
  132. gettimeofday() + 15,
  133. "ROOMMATE_StartInternalTimers",
  134. $hash, 0
  135. );
  136. # Injecting AttrFn for use with RESIDENTS Toolkit
  137. if ( !defined( $modules{dummy}{AttrFn} ) ) {
  138. $modules{dummy}{AttrFn} = "RESIDENTStk_AttrFnDummy";
  139. }
  140. elsif ( $modules{dummy}{AttrFn} ne "RESIDENTStk_AttrFnDummy" ) {
  141. Log3 $name, 4,
  142. "RESIDENTStk $name: concurrent AttrFn already defined for dummy module ("
  143. . $modules{dummy}{AttrFn}
  144. . "). Some attribute based functions like auto-creations will not be available.";
  145. }
  146. return undef;
  147. }
  148. ###################################
  149. sub ROOMMATE_Undefine($$) {
  150. my ( $hash, $name ) = @_;
  151. RESIDENTStk_RemoveInternalTimer( "AutoGone", $hash );
  152. RESIDENTStk_RemoveInternalTimer( "DurationTimer", $hash );
  153. if ( defined( $hash->{RESIDENTGROUPS} ) ) {
  154. my @registeredResidentgroups =
  155. split( /,/, $hash->{RESIDENTGROUPS} );
  156. # unregister at parent objects
  157. foreach my $parent (@registeredResidentgroups) {
  158. if ( defined( $defs{$parent} )
  159. && $defs{$parent}{TYPE} eq "RESIDENTS" )
  160. {
  161. fhem("set $parent unregister $name");
  162. Log3 $name, 4,
  163. "ROOMMATE $name: Unregistered at RESIDENTS device $parent";
  164. }
  165. }
  166. }
  167. # release reverse pointer
  168. delete $modules{ROOMMATE}{defptr}{$name};
  169. return undef;
  170. }
  171. ###################################
  172. sub ROOMMATE_Notify($$) {
  173. my ( $hash, $dev ) = @_;
  174. my $devName = $dev->{NAME};
  175. my $hashName = $hash->{NAME};
  176. # process global:INITIALIZED
  177. if ( $dev->{NAME} eq "global"
  178. && grep( m/^INITIALIZED$/, @{ $dev->{CHANGED} } ) )
  179. {
  180. my @registeredWakeupdevs =
  181. split( /,/, AttrVal( $hashName, "rr_wakeupDevice", 0 ) );
  182. # if we have registered wakeup devices
  183. if (@registeredWakeupdevs) {
  184. # look for at devices for each wakeup device
  185. foreach my $wakeupDev (@registeredWakeupdevs) {
  186. my $wakeupAtdevice = AttrVal( $wakeupDev, "wakeupAtdevice", 0 );
  187. # make sure computeAfterInit is set at at-device
  188. # and re-calculate on our own this time
  189. if ( defined( $defs{$wakeupAtdevice} )
  190. && $defs{$wakeupAtdevice}{TYPE} eq "at"
  191. && AttrVal( $wakeupAtdevice, "computeAfterInit", 0 ) ne
  192. "1" )
  193. {
  194. Log3 $wakeupDev, 3,
  195. "RESIDENTStk $wakeupDev: Correcting '$wakeupAtdevice' attribute computeAfterInit required for correct recalculation after reboot";
  196. fhem "attr $wakeupAtdevice computeAfterInit 1";
  197. my $command;
  198. ( $command, undef ) =
  199. split( "[ \t]+", $defs{$wakeupAtdevice}{DEF}, 2 );
  200. $command =~ s/^[*+]//;
  201. return at_Set( $defs{$wakeupAtdevice},
  202. ( $wakeupAtdevice, "modifyTimeSpec", $command ) );
  203. }
  204. }
  205. }
  206. }
  207. # process child notifies
  208. elsif ( $devName ne $hashName ) {
  209. my @registeredWakeupdevs =
  210. split( ',', AttrVal( $hashName, "rr_wakeupDevice", "" ) );
  211. my @presenceDevices =
  212. split( ',', AttrVal( $hashName, "rr_presenceDevices", "" ) );
  213. # if we have registered wakeup devices
  214. if (@registeredWakeupdevs) {
  215. # if this is a notification of a registered wakeup device
  216. if ( $devName ~~ @registeredWakeupdevs ) {
  217. # Some previous notify deleted the array.
  218. return
  219. if ( !$dev->{CHANGED} );
  220. foreach my $change ( @{ $dev->{CHANGED} } ) {
  221. RESIDENTStk_wakeupSet( $devName, $change );
  222. }
  223. return;
  224. }
  225. # process sub-child notifies: *_wakeupDevice
  226. foreach my $wakeupDev (@registeredWakeupdevs) {
  227. # if this is a notification of a registered sub dummy device
  228. # of one of our wakeup devices
  229. if (
  230. AttrVal( $wakeupDev, "wakeupResetSwitcher", "" ) eq $devName
  231. && $dev->{TYPE} eq "dummy" )
  232. {
  233. # Some previous notify deleted the array.
  234. return
  235. if ( !$dev->{CHANGED} );
  236. foreach my $change ( @{ $dev->{CHANGED} } ) {
  237. RESIDENTStk_wakeupSet( $wakeupDev, $change )
  238. if ( $change ne "off" );
  239. }
  240. last;
  241. }
  242. }
  243. }
  244. # process PRESENCE
  245. if ( @presenceDevices && $devName ~~ @presenceDevices ) {
  246. my $counter = {
  247. absent => 0,
  248. present => 0,
  249. };
  250. for (@presenceDevices) {
  251. my $presenceState =
  252. ReadingsVal( $_, "presence", ReadingsVal( $_, "state", "" ) );
  253. next
  254. unless ( $presenceState =~
  255. /^((absent|disappeared|unavailable)|(present|appeared|available|))$/i
  256. );
  257. $counter->{absent}++ if ($2);
  258. $counter->{present}++ if ($3);
  259. }
  260. if ( $counter->{absent} && !$counter->{present} ) {
  261. Log3 $hashName, 4,
  262. "ROOMMATE $hashName: "
  263. . "Syncing status with $devName = absent";
  264. fhem "set $hashName:FILTER=presence=present absent";
  265. }
  266. elsif ( $counter->{present} ) {
  267. Log3 $hashName, 4,
  268. "ROOMMATE $hashName: "
  269. . "Syncing status with $devName = present";
  270. fhem "set $hashName:FILTER=presence=absent home";
  271. }
  272. }
  273. }
  274. return;
  275. }
  276. ###################################
  277. sub ROOMMATE_Set($@) {
  278. my ( $hash, @a ) = @_;
  279. my $name = $hash->{NAME};
  280. my $state = ReadingsVal( $name, "state", "initialized" );
  281. my $presence = ReadingsVal( $name, "presence", "undefined" );
  282. my $mood = ReadingsVal( $name, "mood", "-" );
  283. my $location = ReadingsVal( $name, "location", "undefined" );
  284. my $silent = 0;
  285. Log3 $name, 5, "ROOMMATE $name: called function ROOMMATE_Set()";
  286. return "No Argument given" if ( !defined( $a[1] ) );
  287. # depending on current FHEMWEB instance's allowedCommands,
  288. # restrict set commands if there is "set-user" in it
  289. my $adminMode = 1;
  290. my $FWallowedCommands = 0;
  291. $FWallowedCommands = AttrVal( $FW_wname, "allowedCommands", 0 )
  292. if ( defined($FW_wname) );
  293. if ( $FWallowedCommands && $FWallowedCommands =~ m/\bset-user\b/ ) {
  294. $adminMode = 0;
  295. return "Forbidden command: set " . $a[1]
  296. if ( lc( $a[1] ) eq "create" );
  297. }
  298. # states
  299. my $states = (
  300. defined( $attr{$name}{rr_states} ) ? $attr{$name}{rr_states}
  301. : (
  302. defined( $attr{$name}{rr_showAllStates} )
  303. && $attr{$name}{rr_showAllStates} == 1
  304. ? "home,gotosleep,asleep,awoken,absent,gone"
  305. : "home,gotosleep,absent,gone"
  306. )
  307. );
  308. $states = $state . "," . $states if ( $states !~ /$state/ );
  309. $states =~ s/ /,/g;
  310. # moods
  311. my $moods = (
  312. defined( $attr{$name}{rr_moods} )
  313. ? $attr{$name}{rr_moods} . ",toggle"
  314. : "calm,relaxed,happy,excited,lonely,sad,bored,stressed,uncomfortable,sleepy,angry,toggle"
  315. );
  316. $moods = $mood . "," . $moods if ( $moods !~ /$mood/ );
  317. $moods =~ s/ /,/g;
  318. # locations
  319. my $locations = (
  320. defined( $attr{$name}{rr_locations} )
  321. ? $attr{$name}{rr_locations}
  322. : ""
  323. );
  324. if ( $locations !~ /$location/
  325. && $locations ne "" )
  326. {
  327. $locations = ":" . $location . "," . $locations;
  328. }
  329. elsif ( $locations ne "" ) {
  330. $locations = ":" . $locations;
  331. }
  332. $locations =~ s/ /,/g;
  333. my $usage = "Unknown argument " . $a[1] . ", choose one of state:$states";
  334. $usage .= " mood:$moods";
  335. $usage .= " location$locations";
  336. if ($adminMode) {
  337. $usage .= " create:wakeuptimer";
  338. $usage .= ",locationMap"
  339. if ( ReadingsVal( $name, "locationLat", "-" ) ne "-"
  340. && ReadingsVal( $name, "locationLong", "-" ) ne "-" );
  341. }
  342. # silentSet
  343. if ( $a[1] eq "silentSet" ) {
  344. $silent = 1;
  345. my $first = shift @a;
  346. $a[0] = $first;
  347. }
  348. # states
  349. if ( $a[1] eq "state"
  350. || $a[1] eq "home"
  351. || $a[1] eq "gotosleep"
  352. || $a[1] eq "asleep"
  353. || $a[1] eq "awoken"
  354. || $a[1] eq "absent"
  355. || $a[1] eq "gone" )
  356. {
  357. my $newstate;
  358. # if not direct
  359. if (
  360. $a[1] eq "state"
  361. && defined( $a[2] )
  362. && ( $a[2] eq "home"
  363. || $a[2] eq "gotosleep"
  364. || $a[2] eq "asleep"
  365. || $a[2] eq "awoken"
  366. || $a[2] eq "absent"
  367. || $a[2] eq "gone" )
  368. )
  369. {
  370. $newstate = $a[2];
  371. }
  372. elsif ( defined( $a[2] ) ) {
  373. return
  374. "Invalid 2nd argument, choose one of home gotosleep asleep awoken absent gone ";
  375. }
  376. else {
  377. $newstate = $a[1];
  378. }
  379. Log3 $name, 2, "ROOMMATE set $name " . $newstate if ( !$silent );
  380. # if state changed
  381. if ( $state ne $newstate ) {
  382. readingsBeginUpdate($hash);
  383. readingsBulkUpdate( $hash, "lastState", $state );
  384. readingsBulkUpdate( $hash, "state", $newstate );
  385. my $datetime = TimeNow();
  386. # reset mood
  387. my $mood_default =
  388. ( defined( $attr{$name}{"rr_moodDefault"} ) )
  389. ? $attr{$name}{"rr_moodDefault"}
  390. : "calm";
  391. my $mood_sleepy =
  392. ( defined( $attr{$name}{"rr_moodSleepy"} ) )
  393. ? $attr{$name}{"rr_moodSleepy"}
  394. : "sleepy";
  395. if (
  396. $mood ne "-"
  397. && ( $newstate eq "gone"
  398. || $newstate eq "none"
  399. || $newstate eq "absent"
  400. || $newstate eq "asleep" )
  401. )
  402. {
  403. Log3 $name, 4,
  404. "ROOMMATE $name: implicit mood change caused by state "
  405. . $newstate;
  406. ROOMMATE_Set( $hash, $name, "silentSet", "mood", "-" );
  407. }
  408. elsif ( $mood ne $mood_sleepy
  409. && ( $newstate eq "gotosleep" || $newstate eq "awoken" ) )
  410. {
  411. Log3 $name, 4,
  412. "ROOMMATE $name: implicit mood change caused by state "
  413. . $newstate;
  414. ROOMMATE_Set( $hash, $name, "silentSet", "mood", $mood_sleepy );
  415. }
  416. elsif ( ( $mood eq "-" || $mood eq $mood_sleepy )
  417. && $newstate eq "home" )
  418. {
  419. Log3 $name, 4,
  420. "ROOMMATE $name: implicit mood change caused by state "
  421. . $newstate;
  422. ROOMMATE_Set( $hash, $name, "silentSet", "mood",
  423. $mood_default );
  424. }
  425. # if state is asleep, start sleep timer
  426. readingsBulkUpdate( $hash, "lastSleep", $datetime )
  427. if ( $newstate eq "asleep" );
  428. # if prior state was asleep, update sleep statistics
  429. if ( $state eq "asleep"
  430. && ReadingsVal( $name, "lastSleep", "" ) ne "" )
  431. {
  432. readingsBulkUpdate( $hash, "lastAwake", $datetime );
  433. readingsBulkUpdate(
  434. $hash,
  435. "lastDurSleep",
  436. RESIDENTStk_TimeDiff(
  437. $datetime, ReadingsVal( $name, "lastSleep", "" )
  438. )
  439. );
  440. readingsBulkUpdate(
  441. $hash,
  442. "lastDurSleep_cr",
  443. RESIDENTStk_TimeDiff(
  444. $datetime, ReadingsVal( $name, "lastSleep", "" ),
  445. "min"
  446. )
  447. );
  448. }
  449. # calculate presence state
  450. my $newpresence =
  451. ( $newstate ne "none"
  452. && $newstate ne "gone"
  453. && $newstate ne "absent" )
  454. ? "present"
  455. : "absent";
  456. # stop any running wakeup-timers in case state changed
  457. my $wakeupState = ReadingsVal( $name, "wakeup", 0 );
  458. if ( $wakeupState > 0 ) {
  459. my $wakeupDeviceList = AttrVal( $name, "rr_wakeupDevice", 0 );
  460. for my $wakeupDevice ( split /,/, $wakeupDeviceList ) {
  461. next if !$wakeupDevice;
  462. if ( defined( $defs{$wakeupDevice} )
  463. && $defs{$wakeupDevice}{TYPE} eq "dummy" )
  464. {
  465. # forced-stop only if resident is not present anymore
  466. if ( $newpresence eq "present" ) {
  467. Log3 $name, 4,
  468. "ROOMMATE $name: ending wakeup-timer $wakeupDevice";
  469. fhem "set $wakeupDevice:FILTER=running!=0 end";
  470. }
  471. else {
  472. Log3 $name, 4,
  473. "ROOMMATE $name: stopping wakeup-timer $wakeupDevice";
  474. fhem "set $wakeupDevice:FILTER=running!=0 stop";
  475. }
  476. }
  477. }
  478. }
  479. # if presence changed
  480. if ( $newpresence ne $presence ) {
  481. readingsBulkUpdate( $hash, "presence", $newpresence );
  482. # update location
  483. my @location_home =
  484. split( ' ', AttrVal( $name, "rr_locationHome", "home" ) );
  485. my @location_underway =
  486. split( ' ',
  487. AttrVal( $name, "rr_locationUnderway", "underway" ) );
  488. my @location_wayhome =
  489. split( ' ',
  490. AttrVal( $name, "rr_locationWayhome", "wayhome" ) );
  491. my $searchstring = quotemeta($location);
  492. if ( !$silent && $newpresence eq "present" ) {
  493. if ( !grep( m/^$searchstring$/, @location_home )
  494. && $location ne $location_home[0] )
  495. {
  496. Log3 $name, 4,
  497. "ROOMMATE $name: implicit location change caused by state "
  498. . $newstate;
  499. ROOMMATE_Set( $hash, $name, "silentSet", "location",
  500. $location_home[0] );
  501. }
  502. }
  503. else {
  504. if ( !$silent
  505. && !grep( m/^$searchstring$/, @location_underway )
  506. && $location ne $location_underway[0] )
  507. {
  508. Log3 $name, 4,
  509. "ROOMMATE $name: implicit location change caused by state "
  510. . $newstate;
  511. ROOMMATE_Set( $hash, $name, "silentSet", "location",
  512. $location_underway[0] );
  513. }
  514. }
  515. # reset wayhome
  516. if ( ReadingsVal( $name, "wayhome", 1 ) > 0 ) {
  517. readingsBulkUpdate( $hash, "wayhome", "0" );
  518. }
  519. # update statistics
  520. if ( $newpresence eq "present" ) {
  521. readingsBulkUpdate( $hash, "lastArrival", $datetime );
  522. # absence duration
  523. if ( ReadingsVal( $name, "lastDeparture", "-" ) ne "-" ) {
  524. readingsBulkUpdate(
  525. $hash,
  526. "lastDurAbsence",
  527. RESIDENTStk_TimeDiff(
  528. $datetime,
  529. ReadingsVal( $name, "lastDeparture", "-" )
  530. )
  531. );
  532. readingsBulkUpdate(
  533. $hash,
  534. "lastDurAbsence_cr",
  535. RESIDENTStk_TimeDiff(
  536. $datetime,
  537. ReadingsVal( $name, "lastDeparture", "-" ),
  538. "min"
  539. )
  540. );
  541. }
  542. }
  543. else {
  544. readingsBulkUpdate( $hash, "lastDeparture", $datetime );
  545. # presence duration
  546. if ( ReadingsVal( $name, "lastArrival", "-" ) ne "-" ) {
  547. readingsBulkUpdate(
  548. $hash,
  549. "lastDurPresence",
  550. RESIDENTStk_TimeDiff(
  551. $datetime,
  552. ReadingsVal( $name, "lastArrival", "-" )
  553. )
  554. );
  555. readingsBulkUpdate(
  556. $hash,
  557. "lastDurPresence_cr",
  558. RESIDENTStk_TimeDiff(
  559. $datetime,
  560. ReadingsVal( $name, "lastArrival", "-" ), "min"
  561. )
  562. );
  563. }
  564. }
  565. # adjust linked objects
  566. if ( defined( $attr{$name}{"rr_passPresenceTo"} )
  567. && $attr{$name}{"rr_passPresenceTo"} ne "" )
  568. {
  569. my @linkedObjects =
  570. split( ' ', $attr{$name}{"rr_passPresenceTo"} );
  571. foreach my $object (@linkedObjects) {
  572. if (
  573. defined( $defs{$object} )
  574. && $defs{$object} ne $name
  575. && defined( $defs{$object}{TYPE} )
  576. && ( $defs{$object}{TYPE} eq "ROOMMATE"
  577. || $defs{$object}{TYPE} eq "GUEST" )
  578. && ReadingsVal( $object, "state", "" ) ne "gone"
  579. && ReadingsVal( $object, "state", "" ) ne "none"
  580. )
  581. {
  582. fhem("set $object $newstate");
  583. }
  584. }
  585. }
  586. }
  587. # calculate duration timers
  588. ROOMMATE_DurationTimer( $hash, $silent );
  589. readingsEndUpdate( $hash, 1 );
  590. # enable or disable AutoGone timer
  591. if ( $newstate eq "absent" ) {
  592. ROOMMATE_AutoGone($hash);
  593. }
  594. elsif ( $state eq "absent" ) {
  595. RESIDENTStk_RemoveInternalTimer( "AutoGone", $hash );
  596. }
  597. }
  598. }
  599. # mood
  600. elsif ( $a[1] eq "mood" ) {
  601. if ( defined( $a[2] ) && $a[2] ne "" ) {
  602. Log3 $name, 2, "ROOMMATE set $name mood " . $a[2] if ( !$silent );
  603. readingsBeginUpdate($hash) if ( !$silent );
  604. if ( $a[2] eq "toggle"
  605. && ReadingsVal( $name, "lastMood", "" ) ne "" )
  606. {
  607. readingsBulkUpdate( $hash, "mood",
  608. ReadingsVal( $name, "lastMood", "" ) );
  609. readingsBulkUpdate( $hash, "lastMood", $mood );
  610. }
  611. elsif ( $mood ne $a[2] ) {
  612. readingsBulkUpdate( $hash, "lastMood", $mood )
  613. if ( $mood ne "-" );
  614. readingsBulkUpdate( $hash, "mood", $a[2] );
  615. }
  616. readingsEndUpdate( $hash, 1 ) if ( !$silent );
  617. }
  618. else {
  619. return "Invalid 2nd argument, choose one of mood toggle";
  620. }
  621. }
  622. # location
  623. elsif ( $a[1] eq "location" ) {
  624. if ( defined( $a[2] ) && $a[2] ne "" ) {
  625. Log3 $name, 2, "ROOMMATE set $name location " . $a[2]
  626. if ( !$silent );
  627. if ( $location ne $a[2] ) {
  628. my $searchstring;
  629. readingsBeginUpdate($hash) if ( !$silent );
  630. # read attributes
  631. my @location_home =
  632. split( ' ', AttrVal( $name, "rr_locationHome", "home" ) );
  633. my @location_underway =
  634. split( ' ',
  635. AttrVal( $name, "rr_locationUnderway", "underway" ) );
  636. my @location_wayhome =
  637. split( ' ',
  638. AttrVal( $name, "rr_locationWayhome", "wayhome" ) );
  639. $searchstring = quotemeta($location);
  640. readingsBulkUpdate( $hash, "lastLocation", $location )
  641. if ( $location ne "wayhome"
  642. && !grep( m/^$searchstring$/, @location_underway ) );
  643. readingsBulkUpdate( $hash, "location", $a[2] )
  644. if ( $a[2] ne "wayhome" );
  645. # wayhome detection
  646. $searchstring = quotemeta($location);
  647. if (
  648. (
  649. $a[2] eq "wayhome"
  650. || grep( m/^$searchstring$/, @location_wayhome )
  651. )
  652. && ( $presence eq "absent" )
  653. )
  654. {
  655. Log3 $name, 3,
  656. "ROOMMATE $name: on way back home from $location";
  657. readingsBulkUpdate( $hash, "wayhome", "1" )
  658. if ( ReadingsVal( $name, "wayhome", "0" ) ne "1" );
  659. }
  660. readingsEndUpdate( $hash, 1 ) if ( !$silent );
  661. # auto-updates
  662. $searchstring = quotemeta( $a[2] );
  663. if (
  664. (
  665. $a[2] eq "home"
  666. || grep( m/^$searchstring$/, @location_home )
  667. )
  668. && $state ne "home"
  669. && $state ne "gotosleep"
  670. && $state ne "asleep"
  671. && $state ne "awoken"
  672. && $state ne "initialized"
  673. )
  674. {
  675. Log3 $name, 4,
  676. "ROOMMATE $name: implicit state change caused by location "
  677. . $a[2];
  678. ROOMMATE_Set( $hash, $name, "silentSet", "state", "home" );
  679. }
  680. elsif (
  681. (
  682. $a[2] eq "underway"
  683. || grep( m/^$searchstring$/, @location_underway )
  684. )
  685. && $state ne "gone"
  686. && $state ne "none"
  687. && $state ne "absent"
  688. && $state ne "initialized"
  689. )
  690. {
  691. Log3 $name, 4,
  692. "ROOMMATE $name: implicit state change caused by location "
  693. . $a[2];
  694. ROOMMATE_Set( $hash, $name, "silentSet", "state",
  695. "absent" );
  696. }
  697. }
  698. }
  699. else {
  700. return "Invalid 2nd argument, choose one of location ";
  701. }
  702. }
  703. # create
  704. elsif ( lc( $a[1] ) eq "create" ) {
  705. if ( !defined( $a[2] ) ) {
  706. return
  707. "Invalid 2nd argument, choose one of wakeuptimer locationMap ";
  708. }
  709. elsif ( lc( $a[2] ) eq "wakeuptimer" ) {
  710. my $i = "1";
  711. my $wakeuptimerName = $name . "_wakeuptimer" . $i;
  712. my $created = 0;
  713. until ($created) {
  714. if ( defined( $defs{$wakeuptimerName} ) ) {
  715. $i++;
  716. $wakeuptimerName = $name . "_wakeuptimer" . $i;
  717. }
  718. else {
  719. my $sortby = AttrVal( $name, "sortby", -1 );
  720. $sortby++;
  721. # create new dummy device
  722. fhem "define $wakeuptimerName dummy";
  723. fhem "attr $wakeuptimerName alias Wake-up Timer $i";
  724. fhem
  725. "attr $wakeuptimerName comment Auto-created by ROOMMATE module for use with RESIDENTS Toolkit";
  726. fhem
  727. "attr $wakeuptimerName devStateIcon OFF:general_aus\@red:reset running:general_an\@green:stop .*:general_an\@orange:nextRun%20OFF";
  728. fhem "attr $wakeuptimerName group " . $attr{$name}{group}
  729. if ( defined( $attr{$name}{group} ) );
  730. fhem "attr $wakeuptimerName icon time_timer";
  731. fhem "attr $wakeuptimerName room " . $attr{$name}{room}
  732. if ( defined( $attr{$name}{room} ) );
  733. fhem
  734. "attr $wakeuptimerName setList nextRun:OFF,00:00,00:15,00:30,00:45,01:00,01:15,01:30,01:45,02:00,02:15,02:30,02:45,03:00,03:15,03:30,03:45,04:00,04:15,04:30,04:45,05:00,05:15,05:30,05:45,06:00,06:15,06:30,06:45,07:00,07:15,07:30,07:45,08:00,08:15,08:30,08:45,09:00,09:15,09:30,09:45,10:00,10:15,10:30,10:45,11:00,11:15,11:30,11:45,12:00,12:15,12:30,12:45,13:00,13:15,13:30,13:45,14:00,14:15,14:30,14:45,15:00,15:15,15:30,15:45,16:00,16:15,16:30,16:45,17:00,17:15,17:30,17:45,18:00,18:15,18:30,18:45,19:00,19:15,19:30,19:45,20:00,20:15,20:30,20:45,21:00,21:15,21:30,21:45,22:00,22:15,22:30,22:45,23:00,23:15,23:30,23:45 reset:noArg trigger:noArg start:noArg stop:noArg end:noArg";
  735. fhem "attr $wakeuptimerName userattr wakeupUserdevice";
  736. fhem "attr $wakeuptimerName sortby " . $sortby
  737. if ($sortby);
  738. fhem "attr $wakeuptimerName wakeupUserdevice $name";
  739. fhem "attr $wakeuptimerName webCmd nextRun";
  740. # register slave device
  741. my $wakeupDevice = AttrVal( $name, "rr_wakeupDevice", 0 );
  742. if ( !$wakeupDevice ) {
  743. fhem "attr $name rr_wakeupDevice $wakeuptimerName";
  744. }
  745. elsif ( $wakeupDevice !~ /(.*,?)($wakeuptimerName)(.*,?)/ )
  746. {
  747. fhem "attr $name rr_wakeupDevice "
  748. . $wakeupDevice
  749. . ",$wakeuptimerName";
  750. }
  751. # trigger first update
  752. fhem "set $wakeuptimerName nextRun OFF";
  753. $created = 1;
  754. }
  755. }
  756. return
  757. "Dummy $wakeuptimerName and other pending devices created and pre-configured.\nYou may edit Macro_$wakeuptimerName to define your wake-up actions\nand at_$wakeuptimerName for optional at-device adjustments.";
  758. }
  759. elsif ( lc( $a[2] ) eq "locationmap" ) {
  760. my $locationmapName = $name . "_map";
  761. if ( defined( $defs{$locationmapName} ) ) {
  762. return
  763. "Device $locationmapName existing already, delete it first to have it re-created.";
  764. }
  765. else {
  766. my $sortby = AttrVal( $name, "sortby", -1 );
  767. $sortby++;
  768. # create new weblink device
  769. fhem "define $locationmapName weblink htmlCode {
  770. '<ul style=\"width: 400px;; overflow: hidden;; height: 300px;;\">
  771. <iframe name=\"$locationmapName\" src=\"https://www.google.com/maps/embed/v1/place?key=AIzaSyB66DvcpbXJ5eWgIkzxpUN2s_9l3_6fegM&q='
  772. .ReadingsVal('$name','locationLat','')
  773. .','
  774. .ReadingsVal('$name','locationLong','')
  775. .'&zoom=13\" width=\"480\" height=\"480\" frameborder=\"0\" style=\"border:0;; margin-top: -165px;; margin-left: -135px;;\">
  776. </iframe>
  777. </ul>'
  778. }";
  779. fhem "attr $locationmapName alias Current Location";
  780. fhem
  781. "attr $locationmapName comment Auto-created by ROOMMATE module";
  782. fhem "attr $locationmapName group " . $attr{$name}{group}
  783. if ( defined( $attr{$name}{group} ) );
  784. fhem "attr $locationmapName room " . $attr{$name}{room}
  785. if ( defined( $attr{$name}{room} ) );
  786. }
  787. return "Weblink device $locationmapName was created.";
  788. }
  789. }
  790. # return usage hint
  791. else {
  792. return $usage;
  793. }
  794. return undef;
  795. }
  796. ############################################################################################################
  797. #
  798. # Begin of helper functions
  799. #
  800. ############################################################################################################
  801. ###################################
  802. sub ROOMMATE_AutoGone($;$) {
  803. my ( $mHash, @a ) = @_;
  804. my $hash = ( $mHash->{HASH} ) ? $mHash->{HASH} : $mHash;
  805. my $name = $hash->{NAME};
  806. RESIDENTStk_RemoveInternalTimer( "AutoGone", $hash );
  807. if ( ReadingsVal( $name, "state", "home" ) eq "absent" ) {
  808. my ( $date, $time, $y, $m, $d, $hour, $min, $sec, $timestamp,
  809. $timeDiff );
  810. my $timestampNow = gettimeofday();
  811. my $timeout = (
  812. defined( $attr{$name}{rr_autoGoneAfter} )
  813. ? $attr{$name}{rr_autoGoneAfter}
  814. : "36"
  815. );
  816. ( $date, $time ) = split( ' ', $hash->{READINGS}{state}{TIME} );
  817. ( $y, $m, $d ) = split( '-', $date );
  818. ( $hour, $min, $sec ) = split( ':', $time );
  819. $m -= 01;
  820. $timestamp = timelocal( $sec, $min, $hour, $d, $m, $y );
  821. $timeDiff = $timestampNow - $timestamp;
  822. if ( $timeDiff >= $timeout * 3600 ) {
  823. Log3 $name, 3,
  824. "ROOMMATE $name: AutoGone timer changed state to 'gone'";
  825. ROOMMATE_Set( $hash, $name, "silentSet", "state", "gone" );
  826. }
  827. else {
  828. my $runtime = $timestamp + $timeout * 3600;
  829. Log3 $name, 4, "ROOMMATE $name: AutoGone timer scheduled: $runtime";
  830. RESIDENTStk_InternalTimer( "AutoGone", $runtime,
  831. "ROOMMATE_AutoGone", $hash, 1 );
  832. }
  833. }
  834. return undef;
  835. }
  836. ###################################
  837. sub ROOMMATE_DurationTimer($;$) {
  838. my ( $mHash, @a ) = @_;
  839. my $hash = ( $mHash->{HASH} ) ? $mHash->{HASH} : $mHash;
  840. my $name = $hash->{NAME};
  841. my $silent = ( defined( $a[0] ) && $a[0] eq "1" ) ? 1 : 0;
  842. my $timestampNow = gettimeofday();
  843. my $diff;
  844. my $durPresence = "0";
  845. my $durAbsence = "0";
  846. my $durSleep = "0";
  847. RESIDENTStk_RemoveInternalTimer( "DurationTimer", $hash );
  848. if ( !defined( $attr{$name}{rr_noDuration} )
  849. || $attr{$name}{rr_noDuration} == 0 )
  850. {
  851. # presence timer
  852. if ( ReadingsVal( $name, "presence", "absent" ) eq "present"
  853. && ReadingsVal( $name, "lastArrival", "-" ) ne "-" )
  854. {
  855. $durPresence =
  856. $timestampNow -
  857. time_str2num( ReadingsVal( $name, "lastArrival", "" ) );
  858. }
  859. # absence timer
  860. if ( ReadingsVal( $name, "presence", "present" ) eq "absent"
  861. && ReadingsVal( $name, "lastDeparture", "-" ) ne "-" )
  862. {
  863. $durAbsence =
  864. $timestampNow -
  865. time_str2num( ReadingsVal( $name, "lastDeparture", "" ) );
  866. }
  867. # sleep timer
  868. if ( ReadingsVal( $name, "state", "home" ) eq "asleep"
  869. && ReadingsVal( $name, "lastSleep", "-" ) ne "-" )
  870. {
  871. $durSleep =
  872. $timestampNow -
  873. time_str2num( ReadingsVal( $name, "lastSleep", "" ) );
  874. }
  875. my $durPresence_hr =
  876. ( $durPresence > 0 )
  877. ? RESIDENTStk_sec2time($durPresence)
  878. : "00:00:00";
  879. my $durPresence_cr =
  880. ( $durPresence > 60 ) ? int( $durPresence / 60 + 0.5 ) : 0;
  881. my $durAbsence_hr =
  882. ( $durAbsence > 0 ) ? RESIDENTStk_sec2time($durAbsence) : "00:00:00";
  883. my $durAbsence_cr =
  884. ( $durAbsence > 60 ) ? int( $durAbsence / 60 + 0.5 ) : 0;
  885. my $durSleep_hr =
  886. ( $durSleep > 0 ) ? RESIDENTStk_sec2time($durSleep) : "00:00:00";
  887. my $durSleep_cr = ( $durSleep > 60 ) ? int( $durSleep / 60 + 0.5 ) : 0;
  888. readingsBeginUpdate($hash) if ( !$silent );
  889. readingsBulkUpdate( $hash, "durTimerPresence_cr", $durPresence_cr )
  890. if ( ReadingsVal( $name, "durTimerPresence_cr", "" ) ne
  891. $durPresence_cr );
  892. readingsBulkUpdate( $hash, "durTimerPresence", $durPresence_hr )
  893. if (
  894. ReadingsVal( $name, "durTimerPresence", "" ) ne $durPresence_hr );
  895. readingsBulkUpdate( $hash, "durTimerAbsence_cr", $durAbsence_cr )
  896. if (
  897. ReadingsVal( $name, "durTimerAbsence_cr", "" ) ne $durAbsence_cr );
  898. readingsBulkUpdate( $hash, "durTimerAbsence", $durAbsence_hr )
  899. if ( ReadingsVal( $name, "durTimerAbsence", "" ) ne $durAbsence_hr );
  900. readingsBulkUpdate( $hash, "durTimerSleep_cr", $durSleep_cr )
  901. if ( ReadingsVal( $name, "durTimerSleep_cr", "" ) ne $durSleep_cr );
  902. readingsBulkUpdate( $hash, "durTimerSleep", $durSleep_hr )
  903. if ( ReadingsVal( $name, "durTimerSleep", "" ) ne $durSleep_hr );
  904. readingsEndUpdate( $hash, 1 ) if ( !$silent );
  905. }
  906. RESIDENTStk_InternalTimer( "DurationTimer", $timestampNow + 60,
  907. "ROOMMATE_DurationTimer", $hash, 1 );
  908. return undef;
  909. }
  910. ###################################
  911. sub ROOMMATE_SetLocation($$$;$$$$$$) {
  912. my ( $name, $location, $trigger, $id, $time, $lat, $long, $address,
  913. $device ) = @_;
  914. my $hash = $defs{$name};
  915. my $state = ReadingsVal( $name, "state", "initialized" );
  916. my $presence = ReadingsVal( $name, "presence", "present" );
  917. my $currLocation = ReadingsVal( $name, "location", "-" );
  918. my $currWayhome = ReadingsVal( $name, "wayhome", "0" );
  919. my $currLat = ReadingsVal( $name, "locationLat", "-" );
  920. my $currLong = ReadingsVal( $name, "locationLong", "-" );
  921. my $currAddr = ReadingsVal( $name, "locationAddr", "" );
  922. $id = "-" if ( !$id || $id eq "" );
  923. $lat = "-" if ( !$lat || $lat eq "" );
  924. $long = "-" if ( !$long || $long eq "" );
  925. $address = "" if ( !$address );
  926. $time = "" if ( !$time );
  927. $device = "" if ( !$device );
  928. Log3 $name, 5,
  929. "ROOMMATE $name: received location information: id=$id name=$location trig=$trigger date=$time lat=$lat long=$long address:$address device=$device";
  930. my $searchstring;
  931. readingsBeginUpdate($hash);
  932. # read attributes
  933. my @location_home =
  934. split( ' ', AttrVal( $name, "rr_locationHome", "home" ) );
  935. my @location_underway =
  936. split( ' ', AttrVal( $name, "rr_locationUnderway", "underway" ) );
  937. my @location_wayhome =
  938. split( ' ', AttrVal( $name, "rr_locationWayhome", "wayhome" ) );
  939. $searchstring = quotemeta($location);
  940. # update locationPresence
  941. readingsBulkUpdate( $hash, "locationPresence", "present" )
  942. if ( $trigger == 1 );
  943. readingsBulkUpdate( $hash, "locationPresence", "absent" )
  944. if ( $trigger == 0 );
  945. # check for implicit state change
  946. #
  947. my $stateChange = 0;
  948. # home
  949. if ( $location eq "home" || grep( m/^$searchstring$/, @location_home ) ) {
  950. Log3 $name, 5, "ROOMMATE $name: received signal from home location";
  951. # home
  952. if ( $state ne "home"
  953. && $state ne "gotosleep"
  954. && $state ne "asleep"
  955. && $state ne "awoken"
  956. && $trigger eq "1" )
  957. {
  958. $stateChange = 1;
  959. }
  960. # absent
  961. elsif ($state ne "gone"
  962. && $state ne "none"
  963. && $state ne "absent"
  964. && $trigger eq "0" )
  965. {
  966. $stateChange = 2;
  967. }
  968. }
  969. # underway
  970. elsif ($location eq "underway"
  971. || $location eq "wayhome"
  972. || grep( m/^$searchstring$/, @location_underway )
  973. || grep( m/^$searchstring$/, @location_wayhome ) )
  974. {
  975. Log3 $name, 5, "ROOMMATE $name: received signal from underway location";
  976. # absent
  977. $stateChange = 2
  978. if ( $state ne "gone"
  979. && $state ne "none"
  980. && $state ne "absent" );
  981. }
  982. # wayhome
  983. if (
  984. $location eq "wayhome"
  985. || ( grep( m/^$searchstring$/, @location_wayhome )
  986. && $trigger eq "0" )
  987. )
  988. {
  989. Log3 $name, 5, "ROOMMATE $name: wayhome signal received";
  990. # wayhome=true
  991. if (
  992. (
  993. ( $location eq "wayhome" && $trigger eq "1" )
  994. || ( $location ne "wayhome" && $trigger eq "0" )
  995. )
  996. && ReadingsVal( $name, "wayhome", "0" ) ne "1"
  997. )
  998. {
  999. Log3 $name, 3, "ROOMMATE $name: on way back home from $location";
  1000. readingsBulkUpdate( $hash, "wayhome", "1" );
  1001. }
  1002. # wayhome=false
  1003. elsif ($location eq "wayhome"
  1004. && $trigger eq "0"
  1005. && ReadingsVal( $name, "wayhome", "0" ) ne "0" )
  1006. {
  1007. Log3 $name, 3,
  1008. "ROOMMATE $name: seems not to be on way back home anymore";
  1009. readingsBulkUpdate( $hash, "wayhome", "0" );
  1010. }
  1011. }
  1012. # activate wayhome tracing when reaching another location while wayhome=1
  1013. elsif ( $stateChange == 0 && $trigger == 1 && $currWayhome == 1 ) {
  1014. Log3 $name, 3,
  1015. "ROOMMATE $name: seems to stay at $location before coming home";
  1016. readingsBulkUpdate( $hash, "wayhome", "2" );
  1017. }
  1018. # revert wayhome during active wayhome tracing
  1019. elsif ( $stateChange == 0 && $trigger == 0 && $currWayhome == 2 ) {
  1020. Log3 $name, 3,
  1021. "ROOMMATE $name: finally on way back home from $location";
  1022. readingsBulkUpdate( $hash, "wayhome", "1" );
  1023. }
  1024. my $currLongDiff = 0;
  1025. my $currLatDiff = 0;
  1026. $currLongDiff =
  1027. maxNum( ReadingsVal( $name, "lastLocationLong", 0 ), $currLong ) -
  1028. minNum( ReadingsVal( $name, "lastLocationLong", 0 ), $currLong )
  1029. if ( $currLong ne "-" );
  1030. $currLatDiff =
  1031. maxNum( ReadingsVal( $name, "lastLocationLat", 0 ), $currLat ) -
  1032. minNum( ReadingsVal( $name, "lastLocationLat", 0 ), $currLat )
  1033. if ( $currLat ne "-" );
  1034. if (
  1035. $trigger == 1
  1036. && ( $stateChange > 0
  1037. || ReadingsVal( $name, "lastLocation", "-" ) ne $currLocation
  1038. || $currLongDiff > 0.00002
  1039. || $currLatDiff > 0.00002 )
  1040. )
  1041. {
  1042. Log3 $name, 5, "ROOMMATE $name: archiving last known location";
  1043. readingsBulkUpdate( $hash, "lastLocationLat", $currLat );
  1044. readingsBulkUpdate( $hash, "lastLocationLong", $currLong );
  1045. readingsBulkUpdate( $hash, "lastLocationAddr", $currAddr )
  1046. if ( $currAddr ne "" );
  1047. readingsBulkUpdate( $hash, "lastLocation", $currLocation );
  1048. }
  1049. readingsBulkUpdate( $hash, "locationLat", $lat );
  1050. readingsBulkUpdate( $hash, "locationLong", $long );
  1051. if ( $address ne "" ) {
  1052. readingsBulkUpdate( $hash, "locationAddr", $address );
  1053. }
  1054. elsif ( $currAddr ne "" ) {
  1055. readingsBulkUpdate( $hash, "locationAddr", "-" );
  1056. }
  1057. readingsBulkUpdate( $hash, "location", $location );
  1058. readingsEndUpdate( $hash, 1 );
  1059. # trigger state change
  1060. if ( $stateChange > 0 ) {
  1061. Log3 $name, 4,
  1062. "ROOMMATE $name: implicit state change caused by location "
  1063. . $location;
  1064. ROOMMATE_Set( $hash, $name, "silentSet", "state", "home" )
  1065. if $stateChange == 1;
  1066. ROOMMATE_Set( $hash, $name, "silentSet", "state", "absent" )
  1067. if $stateChange == 2;
  1068. }
  1069. }
  1070. ###################################
  1071. sub ROOMMATE_StartInternalTimers($$) {
  1072. my ($hash) = @_;
  1073. ROOMMATE_AutoGone($hash);
  1074. ROOMMATE_DurationTimer($hash);
  1075. }
  1076. 1;
  1077. =pod
  1078. =item helper
  1079. =item summary special virtual device to represent a resident of your home
  1080. =item summary_DE spezielles virtuelles Device, welches einen Mitbewohner repr&auml;sentiert
  1081. =begin html
  1082. <a name="ROOMMATE" id="ROOMMATE"></a>
  1083. <h3>ROOMMATE</h3>
  1084. <ul>
  1085. <a name="ROOMMATEdefine" id="ROOMMATEdefine"></a> <b>Define</b>
  1086. <ul>
  1087. <code>define &lt;rr_FirstName&gt; ROOMMATE [&lt;device name(s) of resident group(s)&gt;]</code><br>
  1088. <br>
  1089. Provides a special virtual device to represent a resident of your home.<br>
  1090. Based on the current state and other readings, you may trigger other actions within FHEM.<br>
  1091. <br>
  1092. Used by superior module <a href="#RESIDENTS">RESIDENTS</a> but may also be used stand-alone.<br>
  1093. <br />
  1094. Use comma separated list of resident device names for multi-membership (see example below).<br />
  1095. <br>
  1096. Example:<br>
  1097. <ul>
  1098. <code># Standalone<br>
  1099. define rr_Manfred ROOMMATE<br>
  1100. <br>
  1101. # Typical group member<br>
  1102. define rr_Manfred ROOMMATE rgr_Residents # to be member of resident group rgr_Residents<br>
  1103. <br>
  1104. # Member of multiple groups<br>
  1105. define rr_Manfred ROOMMATE rgr_Residents,rgr_Parents # to be member of resident group rgr_Residents and rgr_Parents<br>
  1106. <br>
  1107. # Complex family structure<br>
  1108. define rr_Manfred ROOMMATE rgr_Residents,rgr_Parents # Parent<br>
  1109. define rr_Lisa ROOMMATE rgr_Residents,rgr_Parents # Parent<br>
  1110. define rr_Rick ROOMMATE rgr_Residents,rgr_Children # Child1<br>
  1111. define rr_Alex ROOMMATE rgr_Residents,rgr_Children # Child2</code>
  1112. </ul>
  1113. </ul><br>
  1114. <ul>
  1115. Please note the RESIDENTS group device needs to be existing before a ROOMMATE device can become a member of it.
  1116. </ul><br>
  1117. <br>
  1118. <br>
  1119. <a name="ROOMMATEset" id="ROOMMATEset"></a> <b>Set</b>
  1120. <ul>
  1121. <code>set &lt;rr_FirstName&gt; &lt;command&gt; [&lt;parameter&gt;]</code><br>
  1122. <br>
  1123. Currently, the following commands are defined.<br>
  1124. <ul>
  1125. <li>
  1126. <b>location</b> &nbsp;&nbsp;-&nbsp;&nbsp; sets reading 'location'; see attribute rr_locations to adjust list shown in FHEMWEB
  1127. </li>
  1128. <li>
  1129. <b>mood</b> &nbsp;&nbsp;-&nbsp;&nbsp; sets reading 'mood'; see attribute rr_moods to adjust list shown in FHEMWEB
  1130. </li>
  1131. <li>
  1132. <b>state</b> &nbsp;&nbsp;home,gotosleep,asleep,awoken,absent,gone&nbsp;&nbsp; switch between states; see attribute rr_states to adjust list shown in FHEMWEB
  1133. </li>
  1134. <li>
  1135. <b>create</b>
  1136. <li><i>locationMap</i>&nbsp;&nbsp; add a pre-configured weblink device using showing a Google Map if readings locationLat+locationLong are present.</li>
  1137. <li><i>wakeuptimer</i>&nbsp;&nbsp; add several pre-configurations provided by RESIDENTS Toolkit. See separate section in <a href="#RESIDENTS">RESIDENTS module commandref</a> for details.</li>
  1138. </li>
  1139. </ul>
  1140. <ul>
  1141. <u>Note:</u> If you would like to restrict access to admin set-commands (-> create) you may set your FHEMWEB instance's attribute allowedCommands like 'set,set-user'.
  1142. The string 'set-user' will ensure only non-admin set-commands can be executed when accessing FHEM using this FHEMWEB instance.
  1143. </ul>
  1144. </ul><br>
  1145. <br>
  1146. <ul>
  1147. <u>Possible states and their meaning</u><br>
  1148. <br>
  1149. <ul>
  1150. This module differs between 6 states:<br>
  1151. <br>
  1152. <ul>
  1153. <li>
  1154. <b>home</b> - individual is present at home and awake
  1155. </li>
  1156. <li>
  1157. <b>gotosleep</b> - individual is on it's way to bed
  1158. </li>
  1159. <li>
  1160. <b>asleep</b> - individual is currently sleeping
  1161. </li>
  1162. <li>
  1163. <b>awoken</b> - individual just woke up from sleep
  1164. </li>
  1165. <li>
  1166. <b>absent</b> - individual is not present at home but will be back shortly
  1167. </li>
  1168. <li>
  1169. <b>gone</b> - individual is away from home for longer period
  1170. </li>
  1171. </ul>
  1172. </ul>
  1173. </ul><br>
  1174. <br>
  1175. <ul>
  1176. <u>Presence correlation to location</u><br>
  1177. <br>
  1178. <ul>
  1179. Under specific circumstances, changing state will automatically change reading 'location' as well.<br>
  1180. <br>
  1181. Whenever presence state changes from 'absent' to 'present', the location is set to 'home'. If attribute rr_locationHome was defined, first location from it will be used as home location.<br>
  1182. <br>
  1183. Whenever presence state changes from 'present' to 'absent', the location is set to 'underway'. If attribute rr_locationUnderway was defined, first location from it will be used as underway location.
  1184. </ul>
  1185. </ul><br>
  1186. <br>
  1187. <ul>
  1188. <u>Auto Gone</u><br>
  1189. <br>
  1190. <ul>
  1191. Whenever an individual is set to 'absent', a trigger is started to automatically change state to 'gone' after a specific timeframe.<br>
  1192. Default value is 36 hours.<br>
  1193. <br>
  1194. This behaviour can be customized by attribute rr_autoGoneAfter.
  1195. </ul>
  1196. </ul><br>
  1197. <br>
  1198. <ul>
  1199. <u>Synchronizing presence with other ROOMMATE or GUEST devices</u><br>
  1200. <br>
  1201. <ul>
  1202. If you always leave or arrive at your house together with other roommates or guests, you may enable a synchronization of your presence state for certain individuals.<br>
  1203. By setting attribute rr_passPresenceTo, those individuals will follow your presence state changes to 'home', 'absent' or 'gone' as you do them with your own device.<br>
  1204. <br>
  1205. Please note that individuals with current state 'gone' or 'none' (in case of guests) will not be touched.
  1206. </ul>
  1207. </ul><br>
  1208. <br>
  1209. <ul>
  1210. <u>Location correlation to state</u><br>
  1211. <br>
  1212. <ul>
  1213. Under specific circumstances, changing location will have an effect on the actual state as well.<br>
  1214. <br>
  1215. Whenever location is set to 'home', the state is set to 'home' if prior presence state was 'absent'. If attribute rr_locationHome was defined, all of those locations will trigger state change to 'home' as well.<br>
  1216. <br>
  1217. Whenever location is set to 'underway', the state is set to 'absent' if prior presence state was 'present'. If attribute rr_locationUnderway was defined, all of those locations will trigger state change to 'absent' as well. Those locations won't appear in reading 'lastLocation'.<br>
  1218. <br>
  1219. Whenever location is set to 'wayhome', the reading 'wayhome' is set to '1' if current presence state is 'absent'. If attribute rr_locationWayhome was defined, LEAVING one of those locations will set reading 'wayhome' to '1' as well. So you actually have implicit and explicit options to trigger wayhome.<br>
  1220. Arriving at home will reset the value of 'wayhome' to '0'.<br>
  1221. <br>
  1222. If you are using the <a href="#GEOFANCY">GEOFANCY</a> module, you can easily have your location updated with GEOFANCY events by defining a simple NOTIFY-trigger like this:<br>
  1223. <br>
  1224. <code>define n_rr_Manfred.location notify geofancy:currLoc_Manfred.* set rr_Manfred:FILTER=location!=$EVTPART1 location $EVTPART1</code><br>
  1225. <br>
  1226. By defining geofencing zones called 'home' and 'wayhome' in the iOS app, you automatically get all the features of automatic state changes described above.
  1227. </ul>
  1228. </ul><br>
  1229. <br>
  1230. <a name="ROOMMATEattr" id="ROOMMATEattr"></a> <b>Attributes</b><br>
  1231. <ul>
  1232. <ul>
  1233. <li>
  1234. <b>rr_autoGoneAfter</b> - hours after which state should be auto-set to 'gone' when current state is 'absent'; defaults to 36 hours
  1235. </li>
  1236. <li>
  1237. <b>rr_geofenceUUIDs</b> - comma separated list of device UUIDs updating their location via <a href="#GEOFANCY">GEOFANCY</a>. Avoids necessity for additional notify/DOIF/watchdog devices and can make GEOFANCY attribute <i>devAlias</i> obsolete. (using more than one UUID/device might not be a good idea as location my leap)
  1238. </li>
  1239. <li>
  1240. <b>rr_locationHome</b> - locations matching these will be treated as being at home; first entry reflects default value to be used with state correlation; separate entries by space; defaults to 'home'
  1241. </li>
  1242. <li>
  1243. <b>rr_locationUnderway</b> - locations matching these will be treated as being underway; first entry reflects default value to be used with state correlation; separate entries by comma or space; defaults to "underway"
  1244. </li>
  1245. <li>
  1246. <b>rr_locationWayhome</b> - leaving a location matching these will set reading wayhome to 1; separate entries by space; defaults to "wayhome"
  1247. </li>
  1248. <li>
  1249. <b>rr_locations</b> - list of locations to be shown in FHEMWEB; separate entries by comma only and do NOT use spaces
  1250. </li>
  1251. <li>
  1252. <b>rr_moodDefault</b> - the mood that should be set after arriving at home or changing state from awoken to home
  1253. </li>
  1254. <li>
  1255. <b>rr_moodSleepy</b> - the mood that should be set if state was changed to gotosleep or awoken
  1256. </li>
  1257. <li>
  1258. <b>rr_moods</b> - list of moods to be shown in FHEMWEB; separate entries by comma only and do NOT use spaces
  1259. </li>
  1260. <li>
  1261. <b>rr_noDuration</b> - may be used to disable duration timer calculation (see readings durTimer*)
  1262. </li>
  1263. <li>
  1264. <b>rr_passPresenceTo</b> - synchronize presence state with other ROOMMATE or GUEST devices; separte devices by space
  1265. </li>
  1266. <li>
  1267. <b>rr_presenceDevices</b> - take over presence state from any other FHEM device. Separate more than one device with comma meaning ALL of them need to be either present or absent to trigger update of this ROOMMATE device.
  1268. </li>
  1269. <li>
  1270. <b>rr_realname</b> - whenever ROOMMATE wants to use the realname it uses the value of attribute alias or group; defaults to group
  1271. </li>
  1272. <li>
  1273. <b>rr_showAllStates</b> - states 'asleep' and 'awoken' are hidden by default to allow simple gotosleep process via devStateIcon; defaults to 0
  1274. </li>
  1275. <li>
  1276. <b>rr_states</b> - list of states to be shown in FHEMWEB; separate entries by comma only and do NOT use spaces; unsupported states will lead to errors though
  1277. </li>
  1278. <li>
  1279. <b>rr_wakeupDevice</b> - reference to enslaved DUMMY devices used as a wake-up timer (part of RESIDENTS Toolkit's wakeuptimer)
  1280. </li>
  1281. </ul>
  1282. </ul><br>
  1283. <br>
  1284. <br>
  1285. <b>Generated Readings/Events:</b><br>
  1286. <ul>
  1287. <ul>
  1288. <li>
  1289. <b>durTimerAbsence</b> - timer to show the duration of absence from home in human readable format (hours:minutes:seconds)
  1290. </li>
  1291. <li>
  1292. <b>durTimerAbsence_cr</b> - timer to show the duration of absence from home in computer readable format (minutes)
  1293. </li>
  1294. <li>
  1295. <b>durTimerPresence</b> - timer to show the duration of presence at home in human readable format (hours:minutes:seconds)
  1296. </li>
  1297. <li>
  1298. <b>durTimerPresence_cr</b> - timer to show the duration of presence at home in computer readable format (minutes)
  1299. </li>
  1300. <li>
  1301. <b>durTimerSleep</b> - timer to show the duration of sleep in human readable format (hours:minutes:seconds)
  1302. </li>
  1303. <li>
  1304. <b>durTimerSleep_cr</b> - timer to show the duration of sleep in computer readable format (minutes)
  1305. </li>
  1306. <li>
  1307. <b>lastArrival</b> - timestamp of last arrival at home
  1308. </li>
  1309. <li>
  1310. <b>lastAwake</b> - timestamp of last sleep cycle end
  1311. </li>
  1312. <li>
  1313. <b>lastDeparture</b> - timestamp of last departure from home
  1314. </li>
  1315. <li>
  1316. <b>lastDurAbsence</b> - duration of last absence from home in human readable format (hours:minutes:seconds)
  1317. </li>
  1318. <li>
  1319. <b>lastDurAbsence_cr</b> - duration of last absence from home in computer readable format (minutes)
  1320. </li>
  1321. <li>
  1322. <b>lastDurPresence</b> - duration of last presence at home in human readable format (hours:minutes:seconds)
  1323. </li>
  1324. <li>
  1325. <b>lastDurPresence_cr</b> - duration of last presence at home in computer readable format (minutes)
  1326. </li>
  1327. <li>
  1328. <b>lastDurSleep</b> - duration of last sleep in human readable format (hours:minutes:seconds)
  1329. </li>
  1330. <li>
  1331. <b>lastDurSleep_cr</b> - duration of last sleep in computer readable format (minutes)
  1332. </li>
  1333. <li>
  1334. <b>lastLocation</b> - the prior location
  1335. </li>
  1336. <li>
  1337. <b>lastMood</b> - the prior mood
  1338. </li>
  1339. <li>
  1340. <b>lastSleep</b> - timestamp of last sleep cycle begin
  1341. </li>
  1342. <li>
  1343. <b>lastState</b> - the prior state
  1344. </li>
  1345. <li>
  1346. <b>lastWakeup</b> - time of last wake-up timer run
  1347. </li>
  1348. <li>
  1349. <b>lastWakeupDev</b> - device name of last wake-up timer
  1350. </li>
  1351. <li>
  1352. <b>location</b> - the current location
  1353. </li>
  1354. <li>
  1355. <b>mood</b> - the current mood
  1356. </li>
  1357. <li>
  1358. <b>nextWakeup</b> - time of next wake-up program run
  1359. </li>
  1360. <li>
  1361. <b>nextWakeupDev</b> - device name for next wake-up program run
  1362. </li>
  1363. <li>
  1364. <b>presence</b> - reflects the home presence state, depending on value of reading 'state' (can be 'present' or 'absent')
  1365. </li>
  1366. <li>
  1367. <b>state</b> - reflects the current state
  1368. </li>
  1369. <li>
  1370. <b>wakeup</b> - becomes '1' while a wake-up program of this resident is being executed
  1371. </li>
  1372. <li>
  1373. <b>wayhome</b> - depending on current location, it can become '1' if individual is on his/her way back home
  1374. </li>
  1375. </ul>
  1376. </ul>
  1377. </ul>
  1378. =end html
  1379. =begin html_DE
  1380. <a name="ROOMMATE" id="ROOMMATE"></a>
  1381. <h3>ROOMMATE</h3>
  1382. <ul>
  1383. <a name="ROOMMATEdefine" id="ROOMMATEdefine"></a> <b>Define</b>
  1384. <ul>
  1385. <code>define &lt;rr_FirstName&gt; ROOMMATE [&lt;Device Name(n) der Bewohnergruppe(n)&gt;]</code><br />
  1386. <br />
  1387. Stellt ein spezielles virtuelles Device bereit, welches einen Mitbewohner repr&auml;sentiert.<br />
  1388. Basierend auf dem aktuellen Status und anderen Readings k&ouml;nnen andere Aktionen innerhalb von FHEM angestoßen werden.<br />
  1389. <br />
  1390. Wird vom &uuml;bergeordneten Modul <a href="#RESIDENTS">RESIDENTS</a> verwendet, kann aber auch einzeln benutzt werden.<br />
  1391. <br />
  1392. Bei Mitgliedschaft mehrerer Bewohnergruppen werden diese durch Komma getrennt angegeben (siehe Beispiel unten).<br />
  1393. <br />
  1394. Beispiele:<br />
  1395. <ul>
  1396. <code># Einzeln<br />
  1397. define rr_Manfred ROOMMATE<br />
  1398. <br />
  1399. # Typisches Gruppenmitglied<br />
  1400. define rr_Manfred ROOMMATE rgr_Residents # um Mitglied der Gruppe rgr_Residents zu sein<br />
  1401. <br />
  1402. # Mitglied in mehreren Gruppen<br />
  1403. define rr_Manfred ROOMMATE rgr_Residents,rgr_Parents # um Mitglied der Gruppen rgr_Residents und rgr_Parents zu sein<br />
  1404. <br />
  1405. # Komplexe Familien Struktur<br />
  1406. define rr_Manfred ROOMMATE rgr_Residents,rgr_Parents # Elternteil<br />
  1407. define rr_Lisa ROOMMATE rgr_Residents,rgr_Parents # Elternteil<br />
  1408. define rr_Rick ROOMMATE rgr_Residents,rgr_Children # Kind1<br />
  1409. define rr_Alex ROOMMATE rgr_Residents,rgr_Children # Kind2</code>
  1410. </ul>
  1411. </ul><br />
  1412. <ul>
  1413. Bitte beachten, dass das RESIDENTS Gruppen Device zun&auml;chst angelegt werden muss, bevor ein ROOMMATE Objekt dort Mitglied werden kann.
  1414. </ul><br />
  1415. <br />
  1416. <br />
  1417. <a name="ROOMMATEset" id="ROOMMATEset"></a> <b>Set</b>
  1418. <ul>
  1419. <code>set &lt;rr_FirstName&gt; &lt;command&gt; [&lt;parameter&gt;]</code><br />
  1420. <br />
  1421. Momentan sind die folgenden Kommandos definiert.<br />
  1422. <ul>
  1423. <li>
  1424. <b>location</b> &nbsp;&nbsp;-&nbsp;&nbsp; setzt das Reading 'location'; siehe auch Attribut rr_locations, um die in FHEMWEB angezeigte Liste anzupassen
  1425. </li>
  1426. <li>
  1427. <b>mood</b> &nbsp;&nbsp;-&nbsp;&nbsp; setzt das Reading 'mood'; siehe auch Attribut rr_moods, um die in FHEMWEB angezeigte Liste anzupassen
  1428. </li>
  1429. <li>
  1430. <b>state</b> &nbsp;&nbsp;home,gotosleep,asleep,awoken,absent,gone&nbsp;&nbsp; wechselt den Status; siehe auch Attribut rr_states, um die in FHEMWEB angezeigte Liste anzupassen
  1431. </li>
  1432. <li>
  1433. <b>create</b>
  1434. <li><i>locationMap</i>&nbsp;&nbsp; f&uuml;gt ein vorkonfiguriertes weblink Device hinzu, welches eine Google Map anzeigt, sofern die Readings locationLat+locationLong vorhanden sind.</li>
  1435. <li><i>wakeuptimer</i>&nbsp;&nbsp; f&uuml;gt diverse Vorkonfigurationen auf Basis von RESIDENTS Toolkit hinzu. Siehe separate Sektion in der <a href="#RESIDENTS">RESIDENTS Modul Kommandoreferenz</a>.</li>
  1436. </li>
  1437. </ul>
  1438. <ul>
  1439. <u>Hinweis:</u> Sofern der Zugriff auf administrative set-Kommandos (-> create) eingeschr&auml;nkt werden soll, kann in einer FHEMWEB Instanz das Attribut allowedCommands &auml;hnlich wie 'set,set-user' erweitert werden.
  1440. Die Zeichenfolge 'set-user' stellt dabei sicher, dass beim Zugriff auf FHEM &uuml;ber diese FHEMWEB Instanz nur nicht-administrative set-Kommandos ausgef&uuml;hrt werden k&ouml;nnen.
  1441. </ul>
  1442. </ul><br />
  1443. <br />
  1444. <ul>
  1445. <u>M&ouml;gliche Status und ihre Bedeutung</u><br />
  1446. <br />
  1447. <ul>
  1448. Dieses Modul unterscheidet 6 verschiedene Status:<br />
  1449. <br />
  1450. <ul>
  1451. <li>
  1452. <b>home</b> - Mitbewohner ist zu Hause und wach
  1453. </li>
  1454. <li>
  1455. <b>gotosleep</b> - Mitbewohner ist auf dem Weg ins Bett
  1456. </li>
  1457. <li>
  1458. <b>asleep</b> - Mitbewohner schl&auml;ft
  1459. </li>
  1460. <li>
  1461. <b>awoken</b> - Mitbewohner ist gerade aufgewacht
  1462. </li>
  1463. <li>
  1464. <b>absent</b> - Mitbewohner ist momentan nicht zu Hause, wird aber bald zur&uuml;ck sein
  1465. </li>
  1466. <li>
  1467. <b>gone</b> - Mitbewohner ist f&uuml;r l&auml;ngere Zeit verreist
  1468. </li>
  1469. </ul>
  1470. </ul>
  1471. </ul><br />
  1472. <br />
  1473. <ul>
  1474. <u>Zusammenhang zwischen Anwesenheit/Presence und Aufenthaltsort/Location</u><br />
  1475. <br />
  1476. <ul>
  1477. Unter bestimmten Umst&auml;nden f&uuml;hrt der Wechsel des Status auch zu einer Änderung des Readings 'location'.<br />
  1478. <br />
  1479. Wannimmer die Anwesenheit (bzw. das Reading 'presence') von 'absent' auf 'present' wechselt, wird 'location' auf 'home' gesetzt. Sofern das Attribut rr_locationHome gesetzt ist, wird die erste Lokation daraus anstelle von 'home' verwendet.<br />
  1480. <br />
  1481. Wannimmer die Anwesenheit (bzw. das Reading 'presence') von 'present' auf 'absent' wechselt, wird 'location' auf 'underway' gesetzt. Sofern das Attribut rr_locationUnderway gesetzt ist, wird die erste Lokation daraus anstelle von 'underway' verwendet.
  1482. </ul>
  1483. </ul><br />
  1484. <br />
  1485. <ul>
  1486. <u>Auto-Status 'gone'</u><br />
  1487. <br />
  1488. <ul>
  1489. Immer wenn ein Mitbewohner auf 'absent' gesetzt wird, wird ein Z&auml;hler gestartet, der nach einer bestimmten Zeit den Status automatisch auf 'gone' setzt.<br />
  1490. Der Standard ist nach 36 Stunden.<br />
  1491. <br />
  1492. Dieses Verhalten kann &uuml;ber das Attribut rr_autoGoneAfter angepasst werden.
  1493. </ul>
  1494. </ul><br />
  1495. <br />
  1496. <ul>
  1497. <u>Anwesenheit mit anderen ROOMMATE oder GUEST Devices synchronisieren</u><br />
  1498. <br />
  1499. <ul>
  1500. Wenn Sie immer zusammen mit anderen Mitbewohnern oder G&auml;sten das Haus verlassen oder erreichen, k&ouml;nnen Sie ihren Status ganz einfach auf andere Mitbewohner &uuml;bertragen.<br />
  1501. Durch das Setzen des Attributs rr_PassPresenceTo folgen die dort aufgef&uuml;hrten Mitbewohner ihren eigenen Status&auml;nderungen nach 'home', 'absent' oder 'gone'.<br />
  1502. <br />
  1503. Bitte beachten, dass Mitbewohner mit dem aktuellen Status 'gone' oder 'none' (im Falle von G&auml;sten) nicht beachtet werden.
  1504. </ul>
  1505. </ul><br />
  1506. <br />
  1507. <ul>
  1508. <u>Zusammenhang zwischen Aufenthaltsort/Location und Anwesenheit/Presence</u><br />
  1509. <br />
  1510. <ul>
  1511. Unter bestimmten Umst&auml;nden hat der Wechsel des Readings 'location' auch einen Einfluss auf den tats&auml;chlichen Status.<br />
  1512. <br />
  1513. Immer wenn eine Lokation mit dem Namen 'home' gesetzt wird, wird auch der Status auf 'home' gesetzt, sofern die Anwesenheit bis dahin noch auf 'absent' stand. Sofern das Attribut rr_locationHome gesetzt wurde, so l&ouml;sen alle dort angegebenen Lokationen einen Statuswechsel nach 'home' aus.<br />
  1514. <br />
  1515. Immer wenn eine Lokation mit dem Namen 'underway' gesetzt wird, wird auch der Status auf 'absent' gesetzt, sofern die Anwesenheit bis dahin noch auf 'present' stand. Sofern das Attribut rr_locationUnderway gesetzt wurde, so l&ouml;sen alle dort angegebenen Lokationen einen Statuswechsel nach 'underway' aus. Diese Lokationen werden auch nicht in das Reading 'lastLocation' &uuml;bertragen.<br />
  1516. <br />
  1517. Immer wenn eine Lokation mit dem Namen 'wayhome' gesetzt wird, wird das Reading 'wayhome' auf '1' gesetzt, sofern die Anwesenheit zu diesem Zeitpunkt 'absent' ist. Sofern das Attribut rr_locationWayhome gesetzt wurde, so f&uuml;hrt das VERLASSEN einer dort aufgef&uuml;hrten Lokation ebenfalls dazu, dass das Reading 'wayhome' auf '1' gesetzt wird. Es gibt also 2 M&ouml;glichkeiten den Nach-Hause-Weg-Indikator zu beeinflussen (implizit und explizit).<br />
  1518. Die Ankunft zu Hause setzt den Wert von 'wayhome' zur&uuml;ck auf '0'.<br />
  1519. <br />
  1520. Wenn Sie auch das <a href="#GEOFANCY">GEOFANCY</a> Modul verwenden, k&ouml;nnen Sie das Reading 'location' ganz einfach &uuml;ber GEOFANCY Ereignisse aktualisieren lassen. Definieren Sie dazu einen NOTIFY-Trigger wie diesen:<br />
  1521. <br />
  1522. <code>define n_rr_Manfred.location notify geofancy:currLoc_Manfred.* set rr_Manfred:FILTER=location!=$EVTPART1 location $EVTPART1</code><br />
  1523. <br />
  1524. Durch das Anlegen von Geofencing-Zonen mit den Namen 'home' und 'wayhome' in der iOS App werden zuk&uuml;nftig automatisch alle Status&auml;nderungen wie oben beschrieben durchgef&uuml;hrt.
  1525. </ul>
  1526. </ul><br />
  1527. <br />
  1528. <a name="ROOMMATEattr" id="ROOMMATEattr"></a> <b>Attribute</b><br />
  1529. <ul>
  1530. <ul>
  1531. <li>
  1532. <b>rr_autoGoneAfter</b> - Anzahl der Stunden, nach denen sich der Status automatisch auf 'gone' &auml;ndert, wenn der aktuellen Status 'absent' ist; Standard ist 36 Stunden
  1533. </li>
  1534. <li>
  1535. <b>rr_geofenceUUIDs</b> - Mit Komma getrennte Liste von Ger&auml;te UUIDs, die ihren Standort &uuml;ber <a href="#GEOFANCY">GEOFANCY</a> aktualisieren. Vermeidet zus&auml;tzliche notify/DOIF/watchdog Ger&auml;te und kann als Ersatz f&uuml;r das GEOFANCY attribute <i>devAlias</i> dienen. (hier ehr als eine UUID/Device zu hinterlegen ist eher keine gute Idee da die Lokation dann wom&ouml;glich anf&auml;ngt zu springen)
  1536. </li>
  1537. <li>
  1538. <b>rr_locationHome</b> - hiermit &uuml;bereinstimmende Lokationen werden als zu Hause gewertet; der erste Eintrag wird f&uuml;r das Zusammenspiel bei Status&auml;nderungen benutzt; mehrere Eintr&auml;ge durch Leerzeichen trennen; Standard ist 'home'
  1539. </li>
  1540. <li>
  1541. <b>rr_locationUnderway</b> - hiermit &uuml;bereinstimmende Lokationen werden als unterwegs gewertet; der erste Eintrag wird f&uuml;r das Zusammenspiel bei Status&auml;nderungen benutzt; mehrere Eintr&auml;ge durch Leerzeichen trennen; Standard ist 'underway'
  1542. </li>
  1543. <li>
  1544. <b>rr_locationWayhome</b> - das Verlassen einer Lokation, die hier aufgef&uuml;hrt ist, l&auml;sst das Reading 'wayhome' auf '1' setzen; mehrere Eintr&auml;ge durch Leerzeichen trennen; Standard ist "wayhome"
  1545. </li>
  1546. <li>
  1547. <b>rr_locations</b> - Liste der in FHEMWEB anzuzeigenden Lokationsauswahlliste in FHEMWEB; mehrere Eintr&auml;ge nur durch Komma trennen und KEINE Leerzeichen verwenden
  1548. </li>
  1549. <li>
  1550. <b>rr_moodDefault</b> - die Stimmung, die nach Ankunft zu Hause oder nach dem Statuswechsel von 'awoken' auf 'home' gesetzt werden soll
  1551. </li>
  1552. <li>
  1553. <b>rr_moodSleepy</b> - die Stimmung, die nach Statuswechsel zu 'gotosleep' oder 'awoken' gesetzt werden soll
  1554. </li>
  1555. <li>
  1556. <b>rr_moods</b> - Liste von Stimmungen, wie sie in FHEMWEB angezeigt werden sollen; mehrere Eintr&auml;ge nur durch Komma trennen und KEINE Leerzeichen verwenden
  1557. </li>
  1558. <li>
  1559. <b>rr_noDuration</b> - deaktiviert die Berechnung der Zeitspannen (siehe Readings durTimer*)
  1560. </li>
  1561. <li>
  1562. <b>rr_passPresenceTo</b> - synchronisiere die Anwesenheit mit anderen ROOMMATE oder GUEST Devices; mehrere Devices durch Leerzeichen trennen
  1563. </li>
  1564. <li>
  1565. <b>rr_presenceDevices</b> - &uuml;bernehmen des presence Status von einem anderen FHEM Device. Bei mehreren Devices diese mit Komma trennen, um ein Update des ROOMMATE Devices auszul&ouml;sen, sobald ALLE Devices entweder absent oder present sind.
  1566. </li>
  1567. <li>
  1568. <b>rr_realname</b> - wo immer ROOMMATE den richtigen Namen verwenden m&ouml;chte nutzt es den Wert des Attributs alias oder group; Standard ist group
  1569. </li>
  1570. <li>
  1571. <b>rr_showAllStates</b> - die Status 'asleep' und 'awoken' sind normalerweise nicht immer sichtbar, um einen einfachen Zubettgeh-Prozess &uuml;ber das devStateIcon Attribut zu erm&ouml;glichen; Standard ist 0
  1572. </li>
  1573. <li>
  1574. <b>rr_states</b> - Liste aller in FHEMWEB angezeigter Status; Eintrage nur mit Komma trennen und KEINE Leerzeichen benutzen; nicht unterst&uuml;tzte Status f&uuml;hren zu Fehlern
  1575. </li>
  1576. <li>
  1577. <b>rr_wakeupDevice</b> - Referenz zu versklavten DUMMY Ger&auml;ten, welche als Wecker benutzt werden (Teil von RESIDENTS Toolkit's wakeuptimer)
  1578. </li>
  1579. </ul>
  1580. </ul><br />
  1581. <br />
  1582. <br />
  1583. <b>Generierte Readings/Events:</b><br />
  1584. <ul>
  1585. <ul>
  1586. <li>
  1587. <b>durTimerAbsence</b> - Timer, der die Dauer der Abwesenheit in normal lesbarem Format anzeigt (Stunden:Minuten:Sekunden)
  1588. </li>
  1589. <li>
  1590. <b>durTimerAbsence_cr</b> - Timer, der die Dauer der Abwesenheit in Computer lesbarem Format anzeigt (Minuten)
  1591. </li>
  1592. <li>
  1593. <b>durTimerPresence</b> - Timer, der die Dauer der Anwesenheit in normal lesbarem Format anzeigt (Stunden:Minuten:Sekunden)
  1594. </li>
  1595. <li>
  1596. <b>durTimerPresence_cr</b> - Timer, der die Dauer der Anwesenheit in Computer lesbarem Format anzeigt (Minuten)
  1597. </li>
  1598. <li>
  1599. <b>durTimerSleep</b> - Timer, der die Schlafdauer in normal lesbarem Format anzeigt (Stunden:Minuten:Sekunden)
  1600. </li>
  1601. <li>
  1602. <b>durTimerSleep_cr</b> - Timer, der die Schlafdauer in Computer lesbarem Format anzeigt (Minuten)
  1603. </li>
  1604. <li>
  1605. <b>lastArrival</b> - Zeitstempel der letzten Ankunft zu Hause
  1606. </li>
  1607. <li>
  1608. <b>lastAwake</b> - Zeitstempel des Endes des letzten Schlafzyklus
  1609. </li>
  1610. <li>
  1611. <b>lastDeparture</b> - Zeitstempel des letzten Verlassens des Zuhauses
  1612. </li>
  1613. <li>
  1614. <b>lastDurAbsence</b> - Dauer der letzten Abwesenheit in normal lesbarem Format (Stunden:Minuten:Sekunden)
  1615. </li>
  1616. <li>
  1617. <b>lastDurAbsence_cr</b> - Dauer der letzten Abwesenheit in Computer lesbarem Format (Minuten)
  1618. </li>
  1619. <li>
  1620. <b>lastDurPresence</b> - Dauer der letzten Anwesenheit in normal lesbarem Format (Stunden:Minuten:Sekunden)
  1621. </li>
  1622. <li>
  1623. <b>lastDurPresence_cr</b> - Dauer der letzten Anwesenheit in Computer lesbarem Format (Minuten)
  1624. </li>
  1625. <li>
  1626. <b>lastDurSleep</b> - Dauer des letzten Schlafzyklus in normal lesbarem Format (Stunden:Minuten:Sekunden)
  1627. </li>
  1628. <li>
  1629. <b>lastDurSleep_cr</b> - Dauer des letzten Schlafzyklus in Computer lesbarem Format (Minuten)
  1630. </li>
  1631. <li>
  1632. <b>lastLocation</b> - der vorherige Aufenthaltsort
  1633. </li>
  1634. <li>
  1635. <b>lastMood</b> - die vorherige Stimmung
  1636. </li>
  1637. <li>
  1638. <b>lastSleep</b> - Zeitstempel des Beginns des letzten Schlafzyklus
  1639. </li>
  1640. <li>
  1641. <b>lastState</b> - der vorherige Status
  1642. </li>
  1643. <li>
  1644. <b>lastWakeup</b> - Zeit der letzten Wake-up Timer Ausf&uuml;hring
  1645. </li>
  1646. <li>
  1647. <b>lastWakeupDev</b> - Device Name des zuletzt verwendeten Wake-up Timers
  1648. </li>
  1649. <li>
  1650. <b>location</b> - der aktuelle Aufenthaltsort
  1651. </li>
  1652. <li>
  1653. <b>mood</b> - die aktuelle Stimmung
  1654. </li>
  1655. <li>
  1656. <b>nextWakeup</b> - Zeit der n&auml;chsten Wake-up Timer Ausf&uuml;hrung
  1657. </li>
  1658. <li>
  1659. <b>nextWakeupDev</b> - Device Name des als n&auml;chstes ausgef&auml;hrten Wake-up Timer
  1660. </li>
  1661. <li>
  1662. <b>presence</b> - gibt den zu Hause Status in Abh&auml;ngigkeit des Readings 'state' wieder (kann 'present' oder 'absent' sein)
  1663. </li>
  1664. <li>
  1665. <b>state</b> - gibt den aktuellen Status wieder
  1666. </li>
  1667. <li>
  1668. <b>wakeup</b> - hat den Wert '1' w&auml;hrend ein Weckprogramm dieses Bewohners ausgef&uuml;hrt wird
  1669. </li>
  1670. <li>
  1671. <b>wayhome</b> - abh&auml;ngig vom aktullen Aufenthaltsort, kann der Wert '1' werden, wenn die Person auf dem weg zur&uuml;ck nach Hause ist
  1672. </li>
  1673. </ul>
  1674. </ul>
  1675. </ul>
  1676. =end html_DE
  1677. =cut