RESIDENTStk.pm 65 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719
  1. # $Id: RESIDENTStk.pm 12953 2017-01-04 14:49:44Z loredo $
  2. ##############################################################################
  3. #
  4. # RESIDENTStk.pm
  5. # Additional functions for 10_RESIDENTS.pm, 20_ROOMMATE.pm, 20_GUEST.pm
  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. no if $] >= 5.017011, warnings => 'experimental';
  27. sub RESIDENTStk_Initialize() {
  28. }
  29. #####################################
  30. # PRE-DEFINITION: wakeuptimer
  31. #------------------------------------
  32. #
  33. #
  34. # Enslave DUMMY device to be used for alarm clock
  35. #
  36. sub RESIDENTStk_wakeupSet($$) {
  37. my ( $NAME, $notifyValue ) = @_;
  38. my $VALUE;
  39. # filter non-registered notifies
  40. my @notify = split / /, $notifyValue;
  41. if (
  42. lc( $notify[0] ) !~
  43. /^(off|nextrun|trigger|start|stop|end|reset|auto|[\+\-][1-9]*[0-9]*|[\+\-]?[0-9]{2}:[0-9]{2})$/
  44. )
  45. {
  46. Log3 $NAME, 5,
  47. "RESIDENTStk $NAME: received unspecified notify '"
  48. . $notify[0]
  49. . "' - nothing to do";
  50. return;
  51. }
  52. elsif ( lc( $notify[0] ) eq "nextrun" ) {
  53. return if ( !defined( $notify[1] ) );
  54. $VALUE = $notify[1];
  55. }
  56. else {
  57. $VALUE = $notify[0];
  58. }
  59. my $wakeupMacro = AttrVal( $NAME, "wakeupMacro", 0 );
  60. my $wakeupDefaultTime = AttrVal( $NAME, "wakeupDefaultTime", 0 );
  61. my $wakeupAtdevice = AttrVal( $NAME, "wakeupAtdevice", 0 );
  62. my $wakeupUserdevice = AttrVal( $NAME, "wakeupUserdevice", 0 );
  63. my $wakeupDays = AttrVal( $NAME, "wakeupDays", "" );
  64. my $wakeupHolidays = AttrVal( $NAME, "wakeupHolidays", 0 );
  65. my $wakeupResetdays = AttrVal( $NAME, "wakeupResetdays", "" );
  66. my $wakeupOffset = AttrVal( $NAME, "wakeupOffset", 0 );
  67. my $wakeupEnforced = AttrVal( $NAME, "wakeupEnforced", 0 );
  68. my $wakeupResetSwitcher = AttrVal( $NAME, "wakeupResetSwitcher", 0 );
  69. my $holidayDevice = AttrVal( "global", "holiday2we", 0 );
  70. my $room = AttrVal( $NAME, "room", 0 );
  71. my $userattr = AttrVal( $NAME, "userattr", 0 );
  72. my $lastRun = ReadingsVal( $NAME, "lastRun", "07:00" );
  73. my $nextRun = ReadingsVal( $NAME, "nextRun", "07:00" );
  74. my $running = ReadingsVal( $NAME, "running", 0 );
  75. my $wakeupUserdeviceState = ReadingsVal( $wakeupUserdevice, "state", 0 );
  76. my $atName = "at_" . $NAME;
  77. my $wdNameGotosleep = "wd_" . $wakeupUserdevice . "_gotosleep";
  78. my $wdNameAsleep = "wd_" . $wakeupUserdevice . "_asleep";
  79. my $wdNameAwoken = "wd_" . $wakeupUserdevice . "_awoken";
  80. my $macroName = "Macro_" . $NAME;
  81. my $macroNameGotosleep = "Macro_" . $wakeupUserdevice . "_gotosleep";
  82. my $macroNameAsleep = "Macro_" . $wakeupUserdevice . "_asleep";
  83. my $macroNameAwoken = "Macro_" . $wakeupUserdevice . "_awoken";
  84. my $wakeupUserdeviceRealname = "Bewohner";
  85. if ( defined( $defs{$wakeupUserdevice} ) ) {
  86. if ( $defs{$wakeupUserdevice}{TYPE} eq "ROOMMATE" ) {
  87. $wakeupUserdeviceRealname = AttrVal(
  88. AttrVal( $NAME, "wakeupUserdevice", "" ),
  89. AttrVal(
  90. AttrVal( $NAME, "wakeupUserdevice", "" ), "rr_realname",
  91. "group"
  92. ),
  93. $wakeupUserdeviceRealname
  94. );
  95. }
  96. elsif ( $defs{$wakeupUserdevice}{TYPE} eq "GUEST" ) {
  97. $wakeupUserdeviceRealname = AttrVal(
  98. AttrVal( $NAME, "wakeupUserdevice", "" ),
  99. AttrVal(
  100. AttrVal( $NAME, "wakeupUserdevice", "" ), "rg_realname",
  101. "group"
  102. ),
  103. $wakeupUserdeviceRealname
  104. );
  105. }
  106. }
  107. # check for required userattr attribute
  108. my $userattributes =
  109. "wakeupOffset:slider,0,1,120 wakeupDefaultTime: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 wakeupMacro wakeupUserdevice wakeupAtdevice wakeupResetSwitcher wakeupResetdays:multiple-strict,0,1,2,3,4,5,6 wakeupDays:multiple-strict,0,1,2,3,4,5,6 wakeupHolidays:andHoliday,orHoliday,andNoHoliday,orNoHoliday wakeupEnforced:0,1,2 wakeupWaitPeriod:slider,0,1,360";
  110. if ( !$userattr || $userattr ne $userattributes ) {
  111. Log3 $NAME, 4,
  112. "RESIDENTStk $NAME: adjusting dummy device for required attribute userattr";
  113. fhem "attr $NAME userattr $userattributes";
  114. }
  115. # check for required userdevice attribute
  116. if ( !$wakeupUserdevice ) {
  117. Log3 $NAME, 3,
  118. "RESIDENTStk $NAME: WARNING - set attribute wakeupUserdevice before running wakeup function!";
  119. }
  120. elsif ( !defined( $defs{$wakeupUserdevice} ) ) {
  121. Log3 $NAME, 3,
  122. "RESIDENTStk $NAME: WARNING - user device $wakeupUserdevice does not exist!";
  123. }
  124. elsif ($defs{$wakeupUserdevice}{TYPE} ne "RESIDENTS"
  125. && $defs{$wakeupUserdevice}{TYPE} ne "ROOMMATE"
  126. && $defs{$wakeupUserdevice}{TYPE} ne "GUEST" )
  127. {
  128. Log3 $NAME, 3,
  129. "RESIDENTStk $NAME: WARNING - defined user device '$wakeupUserdevice' is not a RESIDENTS, ROOMMATE or GUEST device!";
  130. }
  131. # check for required wakeupMacro attribute
  132. if ( !$wakeupMacro ) {
  133. Log3 $NAME, 4,
  134. "RESIDENTStk $NAME: adjusting dummy device for required attribute wakeupMacro";
  135. fhem "attr $NAME wakeupMacro $macroName";
  136. $wakeupMacro = $macroName;
  137. }
  138. if ( !defined( $defs{$wakeupMacro} ) ) {
  139. my $wakeUpMacroTemplate = "{\
  140. ##=============================================================================\
  141. ## This is an example wake-up program running within a period of 30 minutes:\
  142. ## - drive shutters upwards slowly\
  143. ## - light up a HUE bulb from 2000K to 5600K\
  144. ## - have some voice notifications via SONOS\
  145. ## - have some wake-up chill music via SONOS during program run\
  146. ##\
  147. ## Actual FHEM commands are commented out by default as they would need\
  148. ## to be adapted to your configuration.\
  149. ##\
  150. ## Available wake-up variables:\
  151. ## 1. \$EVTPART0 -> start or stop\
  152. ## 2. \$EVTPART1 -> target wake-up time\
  153. ## 3. \$EVTPART2 -> wake-up begin time considering wakeupOffset attribute\
  154. ## 4. \$EVTPART3 -> enforced wakeup yes=1,no=0 from wakeupEnforced attribute\
  155. ## 5. \$EVTPART4 -> device name of the user which called this macro\
  156. ## 6. \$EVTPART5 -> current state of user\
  157. ##=============================================================================\
  158. \
  159. ##-----------------------------------------------------------------------------\
  160. ## DELETE TEMP. AT-COMMANDS POTENTIALLY CREATED EARLIER BY THIS SCRIPT\
  161. ## Executed for start to cleanup in case this wake-up automation is re-started.\
  162. ## Executed for stop to cleanup in case the user ends this automation earlier.\
  163. ##\
  164. for (my \$i=1;; \$i <= 10;; \$i++) {\
  165. if (defined(\$defs{\"atTmp_\".\$i.\"_\".\$NAME})) {\
  166. fhem \"delete atTmp_\".\$i.\"_\".\$NAME;;\
  167. }\
  168. }\
  169. \
  170. ##-----------------------------------------------------------------------------\
  171. ## BEGIN WAKE-UP PROGRAM\
  172. ## Run first automation commands and create temp. at-devices for lagging actions.\
  173. ##\
  174. if (\$EVTPART0 eq \"start\") {\
  175. Log3 \$NAME, 3, \"\$NAME: Wake-up program started for \$EVTPART4 with target time \$EVTPART1. Current state: \$EVTPART5\";;\
  176. \
  177. # fhem \"set BR_FloorLamp:FILTER=onoff=0 pct 1 : ct 2000 : transitiontime 0;; set BR_FloorLamp:FILTER=pct=1 pct 90 : ct 5600 : transitiontime 17700\";;\
  178. \
  179. # fhem \"define atTmp_1_\$NAME at +00:10:00 set BR_Shutter:FILTER=pct<20 pct 20\";;\
  180. # fhem \"define atTmp_2_\$NAME at +00:20:00 set BR_Shutter:FILTER=pct<40 pct 40\";;\
  181. # fhem \"define atTmp_4_\$NAME at +00:30:00 msg audio \\\@Sonos_Bedroom |Hint| Es ist \".\$EVTPART1.\" Uhr, Zeit zum aufstehen!;;;; set BR_FloorLamp:FILTER=pct<100 pct 100 60;;;; sleep 10;;;; set BR_Shutter:FILTER=pct<60 pct 60;;;; set Sonos_Bedroom:FILTER=Volume<10 Volume 10 10\";;\
  182. \
  183. # if wake-up should be enforced\
  184. if (\$EVTPART3) {\
  185. Log (4, \"\$NAME: planning enforced wake-up\");;\
  186. # fhem \"define atTmp_3_\$NAME at +00:25:00 set Sonos_Bedroom:FILTER=Volume>4 Volume 4;;;; sleep 0.5;;;; set Sonos_Bedroom:FILTER=Shuffle=0 Shuffle 1;;;; sleep 0.5;;;; set Sonos_Bedroom StartFavourite Morning%20Sounds\";;\
  187. # fhem \"define atTmp_4_\$NAME at +00:26:00 set Sonos_Bedroom:FILTER=Volume<5 Volume 5\";;\
  188. # fhem \"define atTmp_5_\$NAME at +00:27:00 set Sonos_Bedroom:FILTER=Volume<6 Volume 6\";;\
  189. # fhem \"define atTmp_6_\$NAME at +00:28:00 set Sonos_Bedroom:FILTER=Volume<7 Volume 7\";;\
  190. # fhem \"define atTmp_7_\$NAME at +00:29:00 set Sonos_Bedroom:FILTER=Volume<8 Volume 8\";;\
  191. }\
  192. }\
  193. \
  194. ##-----------------------------------------------------------------------------\
  195. ## END WAKE-UP PROGRAM (OPTIONAL)\
  196. ## Put some post wake-up tasks here like reminders after the actual wake-up period.\
  197. ##\
  198. ## Note: Will only be run when program ends normally after minutes specified in wakeupOffset.\
  199. ## If stop was user-forced by sending explicit set-command 'stop', this is not executed\
  200. ## assuming the user does not want any further automation activities.\
  201. ##\
  202. if (\$EVTPART0 eq \"stop\") {\
  203. Log3 \$NAME, 3, \"\$NAME: Wake-up program ended for \$EVTPART4 with target time \$EVTPART1. Current state: \$EVTPART5\";;\
  204. \
  205. # if wake-up should be enforced, auto-change user state from 'asleep' to 'awoken'\
  206. # after a small additional nap to kick you out of bed if user did not confirm to be awake :-)\
  207. # An additional notify for user state 'awoken' may take further actions\
  208. # and change to state 'home' afterwards.\
  209. if (\$EVTPART3) {\
  210. fhem \"define atTmp_9_\$NAME at +00:05:00 set \$EVTPART4:FILTER=STATE=asleep awoken\";;\
  211. \
  212. # Without enforced wake-up, be jentle and just set user state to 'home' after some\
  213. # additional long nap time\
  214. } else {\
  215. fhem \"define atTmp_9_\$NAME at +01:30:00 set \$EVTPART4:FILTER=STATE=asleep home\";;\
  216. }\
  217. }\
  218. \
  219. }\
  220. ";
  221. Log3 $NAME, 3,
  222. "RESIDENTStk $NAME: new notify macro device $wakeupMacro created";
  223. fhem "define $wakeupMacro notify $wakeupMacro $wakeUpMacroTemplate";
  224. fhem
  225. "attr $wakeupMacro comment Macro auto-created by RESIDENTS Toolkit";
  226. fhem "attr $wakeupMacro room $room"
  227. if ($room);
  228. }
  229. elsif ( $defs{$wakeupMacro}{TYPE} ne "notify" ) {
  230. Log3 $NAME, 3,
  231. "RESIDENTStk $NAME: WARNING - defined macro device '$wakeupMacro' is not a notify device!";
  232. }
  233. # check for required wakeupAtdevice attribute
  234. if ( !$wakeupAtdevice ) {
  235. Log3 $NAME, 4,
  236. "RESIDENTStk $NAME: adjusting dummy device for required attribute wakeupAtdevice";
  237. fhem "attr $NAME wakeupAtdevice $atName";
  238. $wakeupAtdevice = $atName;
  239. }
  240. if ( !defined( $defs{$wakeupAtdevice} ) ) {
  241. Log3 $NAME, 3,
  242. "RESIDENTStk $NAME: new at-device $wakeupAtdevice created";
  243. fhem
  244. "define $wakeupAtdevice at *{RESIDENTStk_wakeupGetBegin(\"$NAME\",\"$wakeupAtdevice\")} set $NAME trigger";
  245. fhem
  246. "attr $wakeupAtdevice comment Auto-created by RESIDENTS Toolkit: trigger wake-up timer at specific time";
  247. fhem "attr $wakeupAtdevice computeAfterInit 1";
  248. fhem "attr $wakeupAtdevice room $room"
  249. if ($room);
  250. ########
  251. # (re)create other notify and watchdog templates
  252. # for ROOMMATE or GUEST devices
  253. # macro: gotosleep
  254. if ( $defs{$wakeupUserdevice}{TYPE} ne "RESIDENTS"
  255. && !defined( $defs{$macroNameGotosleep} ) )
  256. {
  257. my $templateGotosleep = "{\
  258. ##=============================================================================\
  259. ## This is an example macro when gettin' ready for bed.\
  260. ##\
  261. ## Actual FHEM commands are commented out by default as they would need\
  262. ## to be adapted to your configuration.\
  263. ##=============================================================================\
  264. \
  265. ##-----------------------------------------------------------------------------\
  266. ## LIGHT SCENE\
  267. ##\
  268. \
  269. ## Dim up floor light\
  270. #fhem \"set FL_Light:FILTER=pct=0 pct 20\";;\
  271. \
  272. ## Dim down bright ceilling light in bedroom\
  273. #fhem \"set BR_Light:FILTER=pct!=0 pct 0 5\";;\
  274. \
  275. ## Dim up HUE floor lamp with very low color temperature\
  276. #fhem \"set BR_FloorLamp ct 2000 : pct 80 : transitiontime 30\";;\
  277. \
  278. \
  279. ##-----------------------------------------------------------------------------\
  280. ## ENVIRONMENT SCENE\
  281. ##\
  282. \
  283. ## Turn down shutter to 28%\
  284. #fhem \"set BR_Shutter:FILTER=pct>28 pct 28\";;\
  285. \
  286. \
  287. ##-----------------------------------------------------------------------------\
  288. ## PLAY CHILLOUT MUSIC\
  289. ## via SONOS at Bedroom and Bathroom\
  290. ##\
  291. \
  292. ## Stop playback bedroom's Sonos device might be involved in\
  293. #fhem \"set Sonos_Bedroom:transportState=PLAYING stop;;\";;\
  294. \
  295. ## Make Bedroom's and Bathroom's Sonos devices a single device\
  296. ## and do not touch other Sonos devices (this is why we use RemoveMember!)\
  297. #fhem \"sleep 0.5;; set Sonos_Bedroom RemoveMember Sonos_Bedroom\";;\
  298. #fhem \"sleep 1.0;; set Sonos_Bathroom RemoveMember Sonos_Bathroom\";;\
  299. \
  300. ## Group Bedroom's and Bathroom's Sonos devices with Bedroom as master\
  301. #fhem \"sleep 2.0;; set Sonos_Bedroom AddMember Sonos_Bathroom;; set Sonos_Bedroom:FILTER=Shuffle!=1 Shuffle 1;; set Sonos_Bedroom:FILTER=Volume!=12,Sonos_Bathroom:FILTER=Volume!=12 Volume 12\";;\
  302. \
  303. ## Start music from playlist\
  304. #fhem \"sleep 3.0;; set Sonos_Bedroom StartFavourite Evening%%20Chill\";;\
  305. \
  306. }";
  307. Log3 $NAME, 3,
  308. "RESIDENTStk $NAME: new notify macro device $macroNameGotosleep created";
  309. fhem
  310. "define $macroNameGotosleep notify $macroNameGotosleep $templateGotosleep";
  311. fhem
  312. "attr $macroNameGotosleep comment Auto-created by RESIDENTS Toolkit: FHEM commands to run when gettin' ready for bed";
  313. fhem "attr $macroNameGotosleep room $room"
  314. if ($room);
  315. }
  316. # wd: gotosleep
  317. if ( !defined( $defs{$wdNameGotosleep} ) ) {
  318. Log3 $NAME, 3,
  319. "RESIDENTStk $NAME: new watchdog device $wdNameGotosleep created";
  320. fhem
  321. "define $wdNameGotosleep watchdog $wakeupUserdevice:gotosleep 00:00:04 $wakeupUserdevice:(home|absent|gone|none|asleep|awoken) trigger $macroNameGotosleep;; setstate $wdNameGotosleep defined";
  322. fhem
  323. "attr $wdNameGotosleep comment Auto-created by RESIDENTS Toolkit: trigger macro after going to state gotosleep";
  324. fhem "attr $wdNameGotosleep room $room"
  325. if ($room);
  326. }
  327. # macro: asleep
  328. if ( $defs{$wakeupUserdevice}{TYPE} ne "RESIDENTS"
  329. && !defined( $defs{$macroNameAsleep} ) )
  330. {
  331. my $templateAsleep = "{\
  332. ##=============================================================================\
  333. ## This is an example macro when jumpin' into bed and start to sleep.\
  334. ##\
  335. ## Actual FHEM commands are commented out by default as they would need\
  336. ## to be adapted to your configuration.\
  337. ##=============================================================================\
  338. \
  339. ##-----------------------------------------------------------------------------\
  340. ## LIGHT SCENE\
  341. ##\
  342. \
  343. ## In 15 seconds, turn off all lights in Bedroom using a structure\
  344. #fhem \"sleep 15;; set g_BR_Lights [FILTER=state!=off] off\";;\
  345. \
  346. \
  347. ##-----------------------------------------------------------------------------\
  348. ## ENVIRONMENT SCENE\
  349. ##\
  350. \
  351. ## In 12 seconds, close shutter if window is closed\
  352. #if (ReadingsVal(\"BR_Window\",\"state\",0) eq \"closed\") {\
  353. # fhem \"sleep 12;; set BR_Shutter:FILTER=pct>0 close\";;\
  354. \
  355. ## In 12 seconds, if window is not closed just make sure shutter is at least\
  356. ## at 28% to allow some ventilation\
  357. #} else {\
  358. # fhem \"sleep 12;; set BR_Shutter:FILTER=pct>28 pct 28\";;\
  359. #}\
  360. \
  361. \
  362. ##-----------------------------------------------------------------------------\
  363. ## PLAY WAKE-UP ANNOUNCEMENT\
  364. ## via SONOS at Bedroom and stop playback elsewhere\
  365. ##\
  366. \
  367. #my \$nextWakeup = ReadingsVal(\"$wakeupUserdevice\",\"nextWakeup\",\"none\");;
  368. #my \$text = \"|Hint| $wakeupUserdeviceRealname, es ist kein Wecker gestellt. Du könntest verschlafen! Trotzdem eine gute Nacht.\";;
  369. #if (\$nextWakeup ne \"OFF\") {
  370. # \$text = \"|Hint| $wakeupUserdeviceRealname, dein Wecker ist auf \$nextWakeup Uhr gestellt. Gute Nacht und schlaf gut.\";;
  371. #}
  372. #if (\$nextWakeup ne \"none\") {
  373. # fhem \"set Sonos_Bedroom RemoveMember Sonos_Bedroom;; sleep 0.5;; msg audio \\\@Sonos_Bedroom \$text\";;\
  374. #}
  375. \
  376. }";
  377. Log3 $NAME, 3,
  378. "RESIDENTStk $NAME: new notify macro device $macroNameAsleep created";
  379. fhem
  380. "define $macroNameAsleep notify $macroNameAsleep $templateAsleep";
  381. fhem
  382. "attr $macroNameAsleep comment Auto-created by RESIDENTS Toolkit: FHEM commands to run when jumpin' into bed and start to sleep";
  383. fhem "attr $macroNameAsleep room $room"
  384. if ($room);
  385. }
  386. # wd: asleep
  387. if ( !defined( $defs{$wdNameAsleep} ) ) {
  388. Log3 $NAME, 3,
  389. "RESIDENTStk $NAME: new watchdog device $wdNameAsleep created";
  390. fhem
  391. "define $wdNameAsleep watchdog $wakeupUserdevice:asleep 00:00:04 $wakeupUserdevice:(home|absent|gone|none|gotosleep|awoken) trigger $macroNameAsleep;; setstate $wdNameAsleep defined";
  392. fhem
  393. "attr $wdNameAsleep comment Auto-created by RESIDENTS Toolkit: trigger macro after going to state asleep";
  394. fhem "attr $wdNameAsleep room $room"
  395. if ($room);
  396. }
  397. # macro: awoken
  398. if ( $defs{$wakeupUserdevice}{TYPE} ne "RESIDENTS"
  399. && !defined( $defs{$macroNameAwoken} ) )
  400. {
  401. my $templateAwoken = "{\
  402. ##=============================================================================\
  403. ## This is an example macro after confirming to be awake.\
  404. ##\
  405. ## Actual FHEM commands are commented out by default as they would need\
  406. ## to be adapted to your configuration.\
  407. ##=============================================================================\
  408. \
  409. ##-----------------------------------------------------------------------------\
  410. ## LIGHT SCENE\
  411. ##\
  412. \
  413. ## Dim up HUE floor lamp to maximum with cold color temperature\
  414. #fhem \"set BR_FloorLamp:FILTER=pct<100 pct 100 : ct 6500 : transitiontime 30\";;\
  415. \
  416. \
  417. ##-----------------------------------------------------------------------------\
  418. ## ENVIRONMENT SCENE\
  419. ##\
  420. \
  421. ## In 22 seconds, turn up shutter at least until 60%\
  422. #fhem \"sleep 22;; set BR_Shutter:FILTER=pct<60 60\";;\
  423. \
  424. \
  425. ##-----------------------------------------------------------------------------\
  426. ## RAMP-UP ALL MORNING STUFF\
  427. ##\
  428. \
  429. ## Play morning announcement via SONOS at Bedroom\
  430. #fhem \"set Sonos_Bedroom Stop;; msg audio \\\@Sonos_Bedroom |Hint| Guten Morgen, $wakeupUserdeviceRealname<.\";;\
  431. \
  432. ## In 10 seconds, start webradio playback in Bedroom\
  433. #fhem \"sleep 10;; set Sonos_Bedroom StartRadio /Charivari/;; sleep 2;; set Sonos_Bedroom Volume 15\";;\
  434. \
  435. ## Make webradio stream available at Bathroom and\
  436. ## Kitchen 5 seonds after it started\
  437. #fhem \"set Sonos_Bathroom,Sonos_Kitchen Volume 15;; sleep 15;; set Sonos_Bedroom AddMember Sonos_Bathroom;; set Sonos_Bedroom AddMember Sonos_Kitchen\";;\
  438. \
  439. }";
  440. Log3 $NAME, 3,
  441. "RESIDENTStk $NAME: new notify macro device $macroNameAwoken created";
  442. fhem
  443. "define $macroNameAwoken notify $macroNameAwoken $templateAwoken";
  444. fhem
  445. "attr $macroNameAwoken comment Auto-created by RESIDENTS Toolkit: FHEM commands to run after confirming to be awake";
  446. fhem "attr $macroNameAwoken room $room"
  447. if ($room);
  448. }
  449. # wd: awoken
  450. if ( !defined( $defs{$wdNameAwoken} ) ) {
  451. Log3 $NAME, 3,
  452. "RESIDENTStk $NAME: new watchdog device $wdNameAwoken created";
  453. fhem
  454. "define $wdNameAwoken watchdog $wakeupUserdevice:awoken 00:00:04 $wakeupUserdevice:(home|absent|gone|none|gotosleep|asleep) trigger $macroNameAwoken;; setstate $wdNameAwoken defined";
  455. fhem
  456. "attr $wdNameAwoken comment Auto-created by RESIDENTS Toolkit: trigger macro after going to state awoken";
  457. fhem "attr $wdNameAwoken room $room"
  458. if ($room);
  459. }
  460. ########
  461. # (re)create other notify and watchdog templates
  462. # for RESIDENT devices
  463. #
  464. my $RESIDENTGROUPS = "";
  465. if ( $defs{$wakeupUserdevice}{TYPE} eq "RESIDENTS" ) {
  466. $RESIDENTGROUPS = $wakeupUserdevice;
  467. }
  468. elsif (
  469. defined(
  470. $RESIDENTGROUPS = $defs{$wakeupUserdevice}{RESIDENTGROUPS}
  471. )
  472. )
  473. {
  474. $RESIDENTGROUPS = $defs{$wakeupUserdevice}{RESIDENTGROUPS};
  475. }
  476. for my $deviceName ( split /,/, $RESIDENTGROUPS ) {
  477. my $macroRNameGotosleep = "Macro_" . $deviceName . "_gotosleep";
  478. my $macroRNameAsleep = "Macro_" . $deviceName . "_asleep";
  479. my $macroRNameAwoken = "Macro_" . $deviceName . "_awoken";
  480. my $wdRNameGotosleep = "wd_" . $deviceName . "_gotosleep";
  481. my $wdRNameAsleep = "wd_" . $deviceName . "_asleep";
  482. my $wdRNameAwoken = "wd_" . $deviceName . "_awoken";
  483. # macro: gotosleep
  484. if ( !defined( $defs{$macroRNameGotosleep} ) ) {
  485. my $templateGotosleep = "{\
  486. ##=============================================================================\
  487. ## This is an example macro when all residents are gettin' ready for bed.\
  488. ##\
  489. ## Actual FHEM commands are commented out by default as they would need\
  490. ## to be adapted to your configuration.\
  491. ##=============================================================================\
  492. \
  493. ##-----------------------------------------------------------------------------\
  494. ## HOUSE MODE\
  495. ## Enforce evening mode if we are still in day mode\
  496. ##\
  497. \
  498. #fhem \"set HouseMode:FILTER=state=day evening\";;\
  499. \
  500. \
  501. ##-----------------------------------------------------------------------------\
  502. ## LIGHT SCENE\
  503. ##\
  504. \
  505. ## In 10 seconds, turn off lights in unused rooms using structures\
  506. #fhem \"sleep 10;; set g_LR_Lights,g_KT_Lights [FILTER=state!=off] off\";;\
  507. \
  508. \
  509. ##-----------------------------------------------------------------------------\
  510. ## ENVIRONMENT SCENE\
  511. ##\
  512. \
  513. ## Turn off all media devices in the Living Room\
  514. #fhem \"set g_HSE_Media [FILTER=state!=off] off\";;\
  515. \
  516. }";
  517. Log3 $NAME, 3,
  518. "RESIDENTStk $NAME: new notify macro device $macroRNameGotosleep created";
  519. fhem
  520. "define $macroRNameGotosleep notify $macroRNameGotosleep $templateGotosleep";
  521. fhem
  522. "attr $macroRNameGotosleep comment Auto-created by RESIDENTS Toolkit: FHEM commands to run when all residents are gettin' ready for bed";
  523. fhem "attr $macroRNameGotosleep room $room"
  524. if ($room);
  525. }
  526. # wd: gotosleep
  527. if ( !defined( $defs{$wdRNameGotosleep} ) ) {
  528. Log3 $NAME, 3,
  529. "RESIDENTStk $NAME: new watchdog device $wdRNameGotosleep created";
  530. fhem
  531. "define $wdRNameGotosleep watchdog $deviceName:gotosleep 00:00:03 $deviceName:(home|absent|gone|none|asleep|awoken) trigger $macroRNameGotosleep;; setstate $wdRNameGotosleep defined";
  532. fhem
  533. "attr $wdRNameGotosleep comment Auto-created by RESIDENTS Toolkit: trigger macro after going to state gotosleep";
  534. fhem "attr $wdRNameGotosleep room $room"
  535. if ($room);
  536. }
  537. # macro: asleep
  538. if ( !defined( $defs{$macroRNameAsleep} ) ) {
  539. my $templateAsleep = "{\
  540. ##=============================================================================\
  541. ## This is an example macro when all residents are in their beds.\
  542. ##\
  543. ## Actual FHEM commands are commented out by default as they would need\
  544. ## to be adapted to your configuration.\
  545. ##=============================================================================\
  546. \
  547. ##-----------------------------------------------------------------------------\
  548. ## HOUSE MODE\
  549. ## Enforce night mode if we are still in evening mode\
  550. ##\
  551. \
  552. #fhem \"set HouseMode:FILTER=state=evening night\";;\
  553. \
  554. \
  555. ##-----------------------------------------------------------------------------\
  556. ## LIGHT SCENE\
  557. ##\
  558. \
  559. ## In 20 seconds, turn off all lights in the house using structures\
  560. #fhem \"sleep 20;; set g_HSE_Lights [FILTER=state!=off] off\";;\
  561. \
  562. \
  563. ##-----------------------------------------------------------------------------\
  564. ## ENVIRONMENT SCENE\
  565. ##\
  566. \
  567. ## Stop playback at SONOS devices in shared rooms, e.g. Bathroom\
  568. #fhem \"set Sonos_Bathroom:FILTER=transportState=PLAYING Stop\";;\
  569. \
  570. }";
  571. Log3 $NAME, 3,
  572. "RESIDENTStk $NAME: new notify macro device $macroRNameAsleep created";
  573. fhem
  574. "define $macroRNameAsleep notify $macroRNameAsleep $templateAsleep";
  575. fhem
  576. "attr $macroRNameAsleep comment Auto-created by RESIDENTS Toolkit: FHEM commands to run when all residents are in their beds";
  577. fhem "attr $macroRNameAsleep room $room"
  578. if ($room);
  579. }
  580. # wd: asleep
  581. if ( !defined( $defs{$wdRNameAsleep} ) ) {
  582. Log3 $NAME, 3,
  583. "RESIDENTStk $NAME: new watchdog device $wdNameAsleep created";
  584. fhem
  585. "define $wdRNameAsleep watchdog $deviceName:asleep 00:00:03 $deviceName:(home|absent|gone|none|gotosleep|awoken) trigger $macroRNameAsleep;; setstate $wdRNameAsleep defined";
  586. fhem
  587. "attr $wdRNameAsleep comment Auto-created by RESIDENTS Toolkit: trigger macro after going to state asleep";
  588. fhem "attr $wdRNameAsleep room $room"
  589. if ($room);
  590. }
  591. # macro: awoken
  592. if ( !defined( $defs{$macroRNameAwoken} ) ) {
  593. my $templateAwoken = "{\
  594. ##=============================================================================\
  595. ## This is an example macro when the first resident has confirmed to be awake\
  596. ##\
  597. ## Actual FHEM commands are commented out by default as they would need\
  598. ## to be adapted to your configuration.\
  599. ##=============================================================================\
  600. \
  601. ##-----------------------------------------------------------------------------\
  602. ## HOUSE MODE\
  603. ## Enforce morning mode if we are still in night mode\
  604. ##\
  605. \
  606. #fhem \"set HouseMode:FILTER=state=night morning\";;\
  607. \
  608. \
  609. ##-----------------------------------------------------------------------------\
  610. ## LIGHT SCENE\
  611. ##\
  612. \
  613. ## Turn on lights in the Kitchen already but set a timer to turn it off again\
  614. #fhem \"set KT_CounterLight on-for-timer 6300\";;\
  615. \
  616. \
  617. ##-----------------------------------------------------------------------------\
  618. ## PREPARATIONS\
  619. ##\
  620. \
  621. ## In 90 minutes, switch House Mode to 'day' and\
  622. ## play voice announcement via SONOS\
  623. #if (!defined($defs{\"atTmp_HouseMode_day\"})) {\
  624. # fhem \"define atTmp_HouseMode_day at +01:30:00 {if (ReadingsVal(\\\"HouseMode\\\", \\\"state\\\", 0) ne \\\"day\\\") {fhem \\\"msg audio \\\@Sonos_Kitchen Tagesmodus wird etabliert.;;;; sleep 10;;;; set HouseMode day\\\"}}\";;\
  625. #}\
  626. \
  627. }";
  628. Log3 $NAME, 3,
  629. "RESIDENTStk $NAME: new notify macro device $macroRNameAwoken created";
  630. fhem
  631. "define $macroRNameAwoken notify $macroRNameAwoken $templateAwoken";
  632. fhem
  633. "attr $macroRNameAwoken comment Auto-created by RESIDENTS Toolkit: FHEM commands to run after first resident confirmed to be awake";
  634. fhem "attr $macroRNameAwoken room $room"
  635. if ($room);
  636. }
  637. # wd: awoken
  638. if ( !defined( $defs{$wdRNameAwoken} ) ) {
  639. Log3 $NAME, 3,
  640. "RESIDENTStk $NAME: new watchdog device $wdNameAwoken created";
  641. fhem
  642. "define $wdRNameAwoken watchdog $deviceName:awoken 00:00:04 $deviceName:(home|absent|gone|none|gotosleep|asleep) trigger $macroRNameAwoken;; setstate $wdRNameAwoken defined";
  643. fhem
  644. "attr $wdRNameAwoken comment Auto-created by RESIDENTS Toolkit: trigger macro after going to state awoken";
  645. fhem "attr $wdRNameAwoken room $room"
  646. if ($room);
  647. }
  648. }
  649. }
  650. elsif ( $defs{$wakeupAtdevice}{TYPE} ne "at" ) {
  651. Log3 $NAME, 3,
  652. "RESIDENTStk $NAME: WARNING - defined at-device '$wakeupAtdevice' is not an at-device!";
  653. }
  654. elsif ( AttrVal( $wakeupAtdevice, "computeAfterInit", 0 ) ne "1" ) {
  655. Log3 $NAME, 3,
  656. "RESIDENTStk $NAME: Correcting '$wakeupAtdevice' attribute computeAfterInit required for correct recalculation after reboot";
  657. fhem "attr $wakeupAtdevice computeAfterInit 1";
  658. }
  659. # verify holiday2we attribute
  660. if ($wakeupHolidays) {
  661. if ( !$holidayDevice ) {
  662. Log3 $NAME, 3,
  663. "RESIDENTStk $NAME: ERROR - wakeupHolidays set in this alarm clock but global attribute holiday2we not set!";
  664. return
  665. "ERROR: wakeupHolidays set in this alarm clock but global attribute holiday2we not set!";
  666. }
  667. elsif ( !defined( $defs{$holidayDevice} ) ) {
  668. Log3 $NAME, 3,
  669. "RESIDENTStk $NAME: ERROR - global attribute holiday2we has reference to non-existing device $holidayDevice";
  670. return
  671. "ERROR: global attribute holiday2we has reference to non-existing device $holidayDevice";
  672. }
  673. elsif ( $defs{$holidayDevice}{TYPE} ne "holiday" ) {
  674. Log3 $NAME, 3,
  675. "RESIDENTStk $NAME: ERROR - global attribute holiday2we seems to have invalid device reference - $holidayDevice is not of type 'holiday'";
  676. return
  677. "ERROR: global attribute holiday2we seems to have invalid device reference - $holidayDevice is not of type 'holiday'";
  678. }
  679. }
  680. # start
  681. #
  682. if ( $VALUE eq "start" ) {
  683. RESIDENTStk_wakeupRun( $NAME, 1 );
  684. }
  685. # trigger
  686. #
  687. elsif ( $VALUE eq "trigger" ) {
  688. RESIDENTStk_wakeupRun($NAME);
  689. }
  690. # stop | end
  691. #
  692. elsif ( ( $VALUE eq "stop" || $VALUE eq "end" ) && $running ) {
  693. Log3 $NAME, 4, "RESIDENTStk $NAME: stopping wake-up program";
  694. fhem "setreading $NAME running 0";
  695. fhem "set $NAME nextRun $nextRun";
  696. # trigger macro again so it may clean up it's stuff.
  697. # use $EVTPART1 to check
  698. if ( !$wakeupMacro ) {
  699. Log3 $NAME, 2, "RESIDENTStk $NAME: missing attribute wakeupMacro";
  700. }
  701. elsif ( !defined( $defs{$wakeupMacro} ) ) {
  702. Log3 $NAME, 2,
  703. "RESIDENTStk $NAME: notify macro $wakeupMacro not found - no wakeup actions defined!";
  704. }
  705. elsif ( $defs{$wakeupMacro}{TYPE} ne "notify" ) {
  706. Log3 $NAME, 2,
  707. "RESIDENTStk $NAME: device $wakeupMacro is not of type notify";
  708. }
  709. else {
  710. # conditional enforced wake-up:
  711. # only if actual wake-up time is not wakeupDefaultTime
  712. if ( $wakeupEnforced == 2
  713. && $wakeupDefaultTime
  714. && $wakeupDefaultTime ne $lastRun )
  715. {
  716. $wakeupEnforced = 1;
  717. }
  718. elsif ( $wakeupEnforced == 2 ) {
  719. $wakeupEnforced = 0;
  720. }
  721. if ( defined( $notify[1] ) || $VALUE eq "end" ) {
  722. Log3 $NAME, 4,
  723. "RESIDENTStk $NAME: trigger $wakeupMacro stop $lastRun $wakeupOffset $wakeupEnforced $wakeupUserdevice $wakeupUserdeviceState";
  724. fhem
  725. "trigger $wakeupMacro stop $lastRun $wakeupOffset $wakeupEnforced $wakeupUserdevice $wakeupUserdeviceState";
  726. }
  727. else {
  728. Log3 $NAME, 4,
  729. "RESIDENTStk $NAME: trigger $wakeupMacro forced-stop $lastRun $wakeupOffset $wakeupEnforced $wakeupUserdevice $wakeupUserdeviceState";
  730. fhem
  731. "trigger $wakeupMacro forced-stop $lastRun $wakeupOffset $wakeupEnforced $wakeupUserdevice $wakeupUserdeviceState";
  732. fhem "set $wakeupUserdevice:FILTER=state=asleep awoken";
  733. }
  734. fhem "setreading $wakeupUserdevice:FILTER=wakeup=1 wakeup 0";
  735. my $wakeupStopAtdevice = $wakeupAtdevice . "_stop";
  736. if ( defined( $defs{$wakeupStopAtdevice} ) ) {
  737. fhem "delete $wakeupStopAtdevice";
  738. }
  739. }
  740. return;
  741. }
  742. # auto or reset
  743. #
  744. elsif ( $VALUE eq "auto" || $VALUE eq "reset" || $VALUE =~ /^NaN:|:NaN$/ ) {
  745. my $resetTime = ReadingsVal( $NAME, "lastRun", 0 );
  746. if ($wakeupDefaultTime) {
  747. $resetTime = $wakeupDefaultTime;
  748. }
  749. if ( $resetTime
  750. && !( $VALUE eq "auto" && lc($resetTime) eq "off" ) )
  751. {
  752. fhem "set $NAME:FILTER=state!=$resetTime nextRun $resetTime";
  753. }
  754. elsif ( $VALUE eq "reset" ) {
  755. Log3 $NAME, 4,
  756. "RESIDENTStk $NAME: no default value specified in attribute wakeupDefaultTime, just keeping setting OFF";
  757. fhem "set $NAME:FILTER=state!=OFF nextRun OFF";
  758. }
  759. return;
  760. }
  761. # set new wakeup value
  762. elsif (
  763. (
  764. lc($VALUE) eq "off"
  765. || $VALUE =~ /^[\+\-][1-9]*[0-9]*$/
  766. || $VALUE =~ /^[\+\-]?([0-9]{2}):([0-9]{2})$/
  767. )
  768. && defined( $defs{$wakeupAtdevice} )
  769. && $defs{$wakeupAtdevice}{TYPE} eq "at"
  770. )
  771. {
  772. if ( $VALUE =~ /^[\+\-]/ ) {
  773. $VALUE =
  774. RESIDENTStk_TimeSum( ReadingsVal( $NAME, "nextRun", 0 ), $VALUE );
  775. }
  776. # Update wakeuptimer device
  777. #
  778. readingsBeginUpdate( $defs{$NAME} );
  779. if ( ReadingsVal( $NAME, "nextRun", 0 ) ne $VALUE ) {
  780. Log3 $NAME, 4, "RESIDENTStk $NAME: New wake-up time: $VALUE";
  781. readingsBulkUpdate( $defs{$NAME}, "nextRun", $VALUE );
  782. # Update at-device
  783. fhem
  784. "set $wakeupAtdevice modifyTimeSpec {RESIDENTStk_wakeupGetBegin(\"$NAME\",\"$wakeupAtdevice\")}";
  785. }
  786. if ( ReadingsVal( $NAME, "state", 0 ) ne $VALUE && !$running ) {
  787. readingsBulkUpdate( $defs{$NAME}, "state", $VALUE );
  788. }
  789. elsif ( ReadingsVal( $NAME, "state", 0 ) ne "running" && $running ) {
  790. readingsBulkUpdate( $defs{$NAME}, "state", "running" );
  791. }
  792. readingsEndUpdate( $defs{$NAME}, 1 );
  793. # Update user device
  794. #
  795. readingsBeginUpdate( $defs{$wakeupUserdevice} );
  796. my ( $nextWakeupDev, $nextWakeup ) =
  797. RESIDENTStk_wakeupGetNext($wakeupUserdevice);
  798. if ( !$nextWakeupDev || !$nextWakeup ) {
  799. $nextWakeupDev = "none";
  800. $nextWakeup = "OFF";
  801. }
  802. readingsBulkUpdate( $defs{$wakeupUserdevice},
  803. "nextWakeupDev", $nextWakeupDev )
  804. if ( ReadingsVal( $defs{$wakeupUserdevice}, "nextWakeupDev", 0 ) ne
  805. $nextWakeupDev );
  806. readingsBulkUpdate( $defs{$wakeupUserdevice},
  807. "nextWakeup", $nextWakeup )
  808. if ( ReadingsVal( $defs{$wakeupUserdevice}, "nextWakeup", 0 ) ne
  809. $nextWakeup );
  810. readingsEndUpdate( $defs{$wakeupUserdevice}, 1 );
  811. }
  812. return undef;
  813. }
  814. #
  815. # Get current wakeup begin
  816. #
  817. sub RESIDENTStk_wakeupGetBegin($;$) {
  818. my ( $NAME, $wakeupAtdevice ) = @_;
  819. my $nextRun = ReadingsVal( $NAME, "nextRun", 0 );
  820. my $wakeupDefaultTime = AttrVal( $NAME, "wakeupDefaultTime", 0 );
  821. my $wakeupOffset = AttrVal( $NAME, "wakeupOffset", 0 );
  822. my $wakeupInitTime = ( $wakeupDefaultTime
  823. && lc($wakeupDefaultTime) ne "off" ? $wakeupDefaultTime : "05:00" );
  824. my $wakeupTime;
  825. if ($wakeupAtdevice) {
  826. Log3 $NAME, 4,
  827. "RESIDENTStk $NAME: Wakeuptime recalculation triggered by at-device $wakeupAtdevice";
  828. }
  829. # just give any valuable return to at-device
  830. # if wakeuptimer device does not exit anymore
  831. # and run self-destruction to clean up
  832. if ( !defined( $defs{$NAME} ) ) {
  833. Log3 $NAME, 3,
  834. "RESIDENTStk $NAME: this wake-up timer device does not exist anymore";
  835. my $atName = "at_" . $NAME;
  836. if ( $wakeupAtdevice
  837. && defined( $defs{$wakeupAtdevice} )
  838. && $defs{$wakeupAtdevice}{TYPE} eq "at" )
  839. {
  840. Log3 $NAME, 3,
  841. "RESIDENTStk $NAME: Cleaning up at-device $wakeupAtdevice (self-destruction)";
  842. fhem "sleep 1; delete $wakeupAtdevice";
  843. }
  844. elsif ( defined( $defs{$atName} )
  845. && $defs{$atName}{TYPE} eq "at" )
  846. {
  847. Log3 $NAME, 3, "RESIDENTStk $NAME: Cleaning up at-device $atName";
  848. fhem "sleep 1; delete $atName";
  849. }
  850. else {
  851. Log3 $NAME, 3,
  852. "RESIDENTStk $NAME: Could not automatically clean up at-device, please perform manual cleanup.";
  853. }
  854. return $wakeupInitTime;
  855. }
  856. # use nextRun value if not OFF
  857. if ( $nextRun && lc($nextRun) ne "off" ) {
  858. $wakeupTime = $nextRun;
  859. Log3 $NAME, 4, "RESIDENTStk $NAME: wakeupGetBegin source: nextRun";
  860. }
  861. # use wakeupDefaultTime if present and not OFF
  862. elsif ( $wakeupDefaultTime && lc($wakeupDefaultTime) ne "off" ) {
  863. $wakeupTime = $wakeupDefaultTime;
  864. Log3 $NAME, 4,
  865. "RESIDENTStk $NAME: wakeupGetBegin source: wakeupDefaultTime";
  866. }
  867. # Use a default value to ensure auto-reset at least once a day
  868. else {
  869. $wakeupTime = $wakeupInitTime;
  870. Log3 $NAME, 4, "RESIDENTStk $NAME: wakeupGetBegin source: defaultValue";
  871. }
  872. # Recalculate new wake-up value
  873. my $seconds = RESIDENTStk_time2sec($wakeupTime) - $wakeupOffset * 60;
  874. if ( $seconds < 0 ) { $seconds = 86400 + $seconds }
  875. Log3 $NAME, 4,
  876. "RESIDENTStk $NAME: wakeupGetBegin result: $wakeupTime = $seconds s - $wakeupOffset m = "
  877. . RESIDENTStk_sec2time($seconds);
  878. return RESIDENTStk_sec2time($seconds);
  879. }
  880. #
  881. # Use DUMMY device to run wakup event
  882. #
  883. sub RESIDENTStk_wakeupRun($;$) {
  884. my ( $NAME, $forceRun ) = @_;
  885. my $wakeupMacro = AttrVal( $NAME, "wakeupMacro", 0 );
  886. my $wakeupDefaultTime = AttrVal( $NAME, "wakeupDefaultTime", 0 );
  887. my $wakeupAtdevice = AttrVal( $NAME, "wakeupAtdevice", 0 );
  888. my $wakeupUserdevice = AttrVal( $NAME, "wakeupUserdevice", 0 );
  889. my $wakeupDays = AttrVal( $NAME, "wakeupDays", "" );
  890. my $wakeupHolidays = AttrVal( $NAME, "wakeupHolidays", 0 );
  891. my $wakeupResetdays = AttrVal( $NAME, "wakeupResetdays", "" );
  892. my $wakeupOffset = AttrVal( $NAME, "wakeupOffset", 0 );
  893. my $wakeupEnforced = AttrVal( $NAME, "wakeupEnforced", 0 );
  894. my $wakeupResetSwitcher = AttrVal( $NAME, "wakeupResetSwitcher", 0 );
  895. my $wakeupWaitPeriod = AttrVal( $NAME, "wakeupWaitPeriod", 360 );
  896. my $holidayDevice = AttrVal( "global", "holiday2we", 0 );
  897. my $lastRun = ReadingsVal( $NAME, "lastRun", "06:00" );
  898. my $nextRun = ReadingsVal( $NAME, "nextRun", "06:00" );
  899. my $wakeupUserdeviceState = ReadingsVal( $wakeupUserdevice, "state", 0 );
  900. my $wakeupUserdeviceWakeup = ReadingsVal( $wakeupUserdevice, "wakeup", 0 );
  901. my $room = AttrVal( $NAME, "room", 0 );
  902. my $running = 0;
  903. my $preventRun = 0;
  904. my $holidayToday = "";
  905. if ( $wakeupHolidays
  906. && $holidayDevice
  907. && defined( $defs{$holidayDevice} )
  908. && $defs{$holidayDevice}{TYPE} eq "holiday" )
  909. {
  910. my $hdayTod = ReadingsVal( $holidayDevice, "state", "" );
  911. if ( $hdayTod ne "none" && $hdayTod ne "" ) { $holidayToday = 1 }
  912. else { $holidayToday = 0 }
  913. }
  914. else {
  915. $wakeupHolidays = 0;
  916. }
  917. my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
  918. localtime( time() + $wakeupOffset * 60 );
  919. $year += 1900;
  920. $mon++;
  921. $mon = "0" . $mon if ( $mon < 10 );
  922. $mday = "0" . $mday if ( $mday < 10 );
  923. $hour = "0" . $hour if ( $hour < 10 );
  924. $min = "0" . $min if ( $min < 10 );
  925. $sec = "0" . $sec if ( $sec < 10 );
  926. my $nowRun = $hour . ":" . $min;
  927. my $nowRunSec =
  928. time_str2num( $year . "-"
  929. . $mon . "-"
  930. . $mday . " "
  931. . $hour . ":"
  932. . $min . ":"
  933. . $sec );
  934. if ( $nextRun ne $nowRun ) {
  935. $lastRun = $nowRun;
  936. Log3 $NAME, 4, "RESIDENTStk $NAME: lastRun != nextRun = $lastRun";
  937. }
  938. else {
  939. $lastRun = $nextRun;
  940. Log3 $NAME, 4, "RESIDENTStk $NAME: lastRun = nextRun = $lastRun";
  941. }
  942. my @days = ($wday);
  943. if ( $wakeupDays ne "" ) {
  944. @days = split /,/, $wakeupDays;
  945. }
  946. my @rdays = ($wday);
  947. if ( $wakeupResetdays ne "" ) {
  948. @rdays = split /,/, $wakeupResetdays;
  949. }
  950. if ( !defined( $defs{$NAME} ) ) {
  951. return "$NAME: Non existing device";
  952. }
  953. elsif ( lc($nextRun) eq "off" && !$forceRun ) {
  954. Log3 $NAME, 4,
  955. "RESIDENTStk $NAME: alarm set to OFF - not triggering wake-up program";
  956. }
  957. elsif ( !$wakeupUserdevice ) {
  958. return "$NAME: missing attribute wakeupUserdevice";
  959. }
  960. elsif ( !defined( $defs{$wakeupUserdevice} ) ) {
  961. return "$NAME: Non existing wakeupUserdevice $wakeupUserdevice";
  962. }
  963. elsif ($defs{$wakeupUserdevice}{TYPE} ne "RESIDENTS"
  964. && $defs{$wakeupUserdevice}{TYPE} ne "ROOMMATE"
  965. && $defs{$wakeupUserdevice}{TYPE} ne "GUEST" )
  966. {
  967. return
  968. "$NAME: device $wakeupUserdevice is not of type RESIDENTS, ROOMMATE or GUEST";
  969. }
  970. elsif ($defs{$wakeupUserdevice}{TYPE} eq "GUEST"
  971. && $wakeupUserdeviceState eq "none" )
  972. {
  973. Log3 $NAME, 4,
  974. "RESIDENTStk $NAME: GUEST device $wakeupUserdevice has status value 'none' so let's disable this alarm timer";
  975. fhem "set $NAME nextRun OFF";
  976. return;
  977. }
  978. elsif ( !$wakeupHolidays && !( $wday ~~ @days ) && !$forceRun ) {
  979. Log3 $NAME, 4,
  980. "RESIDENTStk $NAME: weekday restriction in use - not triggering wake-up program this time";
  981. }
  982. elsif (
  983. $wakeupHolidays
  984. && !$forceRun
  985. && ( $wakeupHolidays eq "orHoliday"
  986. || $wakeupHolidays eq "orNoHoliday" )
  987. && (
  988. !( $wday ~~ @days )
  989. && ( ( $wakeupHolidays eq "orHoliday" && !$holidayToday )
  990. || ( $wakeupHolidays eq "orNoHoliday" && $holidayToday ) )
  991. )
  992. )
  993. {
  994. Log3 $NAME, 4,
  995. "RESIDENTStk $NAME: neither weekday nor holiday restriction matched - not triggering wake-up program this time";
  996. }
  997. elsif (
  998. $wakeupHolidays
  999. && !$forceRun
  1000. && ( $wakeupHolidays eq "andHoliday"
  1001. || $wakeupHolidays eq "andNoHoliday" )
  1002. && (
  1003. !( $wday ~~ @days )
  1004. || ( ( $wakeupHolidays eq "andHoliday" && !$holidayToday )
  1005. || ( $wakeupHolidays eq "andNoHoliday" && $holidayToday ) )
  1006. )
  1007. )
  1008. {
  1009. Log3 $NAME, 4,
  1010. "RESIDENTStk $NAME: weekday restriction in conjunction with $wakeupHolidays in use - not triggering wake-up program this time";
  1011. }
  1012. elsif ($wakeupUserdeviceState eq "absent"
  1013. || $wakeupUserdeviceState eq "gone"
  1014. || $wakeupUserdeviceState eq "gotosleep"
  1015. || $wakeupUserdeviceState eq "awoken" )
  1016. {
  1017. Log3 $NAME, 4,
  1018. "RESIDENTStk $NAME: we should not start any wake-up program for resident device $wakeupUserdevice being in state '"
  1019. . $wakeupUserdeviceState
  1020. . "' - not triggering wake-up program this time";
  1021. }
  1022. # general conditions to trigger program fulfilled
  1023. else {
  1024. my $expLastWakeup = time_str2num(
  1025. ReadingsTimestamp(
  1026. $wakeupUserdevice, "lastWakeup", "1970-01-01 00:00:00"
  1027. )
  1028. ) - 1 +
  1029. $wakeupOffset * 60 +
  1030. $wakeupWaitPeriod * 60;
  1031. my $expLastAwake = time_str2num(
  1032. ReadingsTimestamp(
  1033. $wakeupUserdevice, "lastAwake", "1970-01-01 00:00:00"
  1034. )
  1035. ) - 1 +
  1036. $wakeupWaitPeriod * 60;
  1037. if ( !$wakeupMacro ) {
  1038. return "$NAME: missing attribute wakeupMacro";
  1039. }
  1040. elsif ( !defined( $defs{$wakeupMacro} ) ) {
  1041. return
  1042. "$NAME: notify macro $wakeupMacro not found - no wakeup actions defined!";
  1043. }
  1044. elsif ( $defs{$wakeupMacro}{TYPE} ne "notify" ) {
  1045. return "$NAME: device $wakeupMacro is not of type notify";
  1046. }
  1047. elsif ($wakeupUserdeviceWakeup) {
  1048. Log3 $NAME, 3,
  1049. "RESIDENTStk $NAME: Another wake-up program is already being executed for device $wakeupUserdevice, won't trigger $wakeupMacro";
  1050. }
  1051. elsif ( $expLastWakeup > $nowRunSec && !$forceRun ) {
  1052. Log3 $NAME, 3,
  1053. "RESIDENTStk $NAME: won't trigger wake-up program due to non-expired wakeupWaitPeriod threshold since lastWakeup (expLastWakeup=$expLastWakeup > nowRunSec=$nowRunSec)";
  1054. }
  1055. elsif ( $expLastAwake > $nowRunSec && !$forceRun ) {
  1056. Log3 $NAME, 3,
  1057. "RESIDENTStk $NAME: won't trigger wake-up program due to non-expired wakeupWaitPeriod threshold since lastAwake (expLastAwake=$expLastAwake > nowRunSec=$nowRunSec)";
  1058. }
  1059. else {
  1060. # conditional enforced wake-up:
  1061. # only if actual wake-up time is not wakeupDefaultTime
  1062. if ( $wakeupEnforced == 2
  1063. && $wakeupDefaultTime
  1064. && $wakeupDefaultTime ne $lastRun )
  1065. {
  1066. $wakeupEnforced = 1;
  1067. }
  1068. elsif ( $wakeupEnforced == 2 ) {
  1069. $wakeupEnforced = 0;
  1070. }
  1071. Log3 $NAME, 4,
  1072. "RESIDENTStk $NAME: trigger $wakeupMacro (running=1)";
  1073. fhem
  1074. "trigger $wakeupMacro start $lastRun $wakeupOffset $wakeupEnforced $wakeupUserdevice $wakeupUserdeviceState";
  1075. # Update user device with last wakeup details
  1076. #
  1077. readingsBeginUpdate( $defs{$wakeupUserdevice} );
  1078. readingsBulkUpdate( $defs{$wakeupUserdevice},
  1079. "lastWakeup", $lastRun );
  1080. readingsBulkUpdate( $defs{$wakeupUserdevice},
  1081. "lastWakeupDev", $NAME );
  1082. readingsBulkUpdate( $defs{$wakeupUserdevice}, "wakeup", "1" );
  1083. readingsEndUpdate( $defs{$wakeupUserdevice}, 1 );
  1084. fhem "setreading $wakeupUserdevice wakeup 0"
  1085. if ( !$wakeupOffset );
  1086. fhem "setreading $NAME lastRun $lastRun";
  1087. if ( $wakeupOffset > 0 ) {
  1088. my $wakeupStopAtdevice = $wakeupAtdevice . "_stop";
  1089. if ( defined( $defs{$wakeupStopAtdevice} ) ) {
  1090. fhem "delete $wakeupStopAtdevice";
  1091. }
  1092. Log3 $NAME, 4,
  1093. "RESIDENTStk $NAME: created at-device $wakeupStopAtdevice to stop wake-up program in $wakeupOffset minutes";
  1094. fhem "define $wakeupStopAtdevice at +"
  1095. . RESIDENTStk_sec2time( $wakeupOffset * 60 + 1 )
  1096. . " set $NAME:FILTER=running=1 stop triggerpost";
  1097. fhem
  1098. "attr $wakeupStopAtdevice comment Auto-created by RESIDENTS Toolkit: temp. at-device to stop wake-up program of timer $NAME when wake-up time is reached";
  1099. $running = 1;
  1100. }
  1101. }
  1102. }
  1103. # Update user device with next wakeup details
  1104. #
  1105. readingsBeginUpdate( $defs{$wakeupUserdevice} );
  1106. my ( $nextWakeupDev, $nextWakeup ) =
  1107. RESIDENTStk_wakeupGetNext($wakeupUserdevice);
  1108. if ( !$nextWakeupDev || !$nextWakeup ) {
  1109. $nextWakeupDev = "none";
  1110. $nextWakeup = "OFF";
  1111. }
  1112. readingsBulkUpdate( $defs{$wakeupUserdevice},
  1113. "nextWakeupDev", $nextWakeupDev )
  1114. if ( ReadingsVal( $defs{$wakeupUserdevice}, "nextWakeupDev", 0 ) ne
  1115. $nextWakeupDev );
  1116. readingsBulkUpdate( $defs{$wakeupUserdevice}, "nextWakeup", $nextWakeup )
  1117. if ( ReadingsVal( $defs{$wakeupUserdevice}, "nextWakeup", 0 ) ne
  1118. $nextWakeup );
  1119. readingsEndUpdate( $defs{$wakeupUserdevice}, 1 );
  1120. if ( $running && $wakeupOffset > 0 ) {
  1121. readingsBeginUpdate( $defs{$NAME} );
  1122. readingsBulkUpdate( $defs{$NAME}, "running", "1" )
  1123. if ( ReadingsVal( $NAME, "running", 0 ) ne "1" );
  1124. readingsBulkUpdate( $defs{$NAME}, "state", "running" )
  1125. if ( ReadingsVal( $NAME, "state", 0 ) ne "running" );
  1126. readingsEndUpdate( $defs{$NAME}, 1 );
  1127. }
  1128. my $doReset = 1;
  1129. if ( $wakeupResetSwitcher
  1130. && defined( $defs{$wakeupResetSwitcher} )
  1131. && $defs{$wakeupResetSwitcher}{TYPE} eq "dummy"
  1132. && ReadingsVal( $wakeupResetSwitcher, "state", 0 ) eq "off" )
  1133. {
  1134. $doReset = 0;
  1135. }
  1136. if ( $wakeupDefaultTime && $wday ~~ @rdays && $doReset ) {
  1137. Log3 $NAME, 4,
  1138. "RESIDENTStk $NAME: Resetting based on wakeupDefaultTime";
  1139. fhem
  1140. "set $NAME:FILTER=state!=$wakeupDefaultTime nextRun $wakeupDefaultTime";
  1141. }
  1142. elsif ( !$running ) {
  1143. fhem "setreading $NAME:FILTER=state!=$nextRun state $nextRun";
  1144. }
  1145. return undef;
  1146. }
  1147. #####################################
  1148. # FHEM CODE INJECTION
  1149. #------------------------------------
  1150. #
  1151. #
  1152. # AttFn for enslaved dummy devices
  1153. #
  1154. sub RESIDENTStk_AttrFnDummy(@) {
  1155. my ( $cmd, $name, $aName, $aVal ) = @_;
  1156. # set attribute
  1157. if ( $init_done && $cmd eq "set" ) {
  1158. # wakeupResetSwitcher
  1159. if ( $aName eq "wakeupResetSwitcher" ) {
  1160. if ( !defined( $defs{$aVal} ) ) {
  1161. my $alias = AttrVal( $name, "alias", 0 );
  1162. my $group = AttrVal( $name, "group", 0 );
  1163. my $room = AttrVal( $name, "room", 0 );
  1164. fhem "define $aVal dummy";
  1165. fhem
  1166. "attr $aVal comment Auto-created by RESIDENTS Toolkit: easy between on/off for auto time reset of wake-up timer $NAME";
  1167. if ($alias) {
  1168. fhem "attr $aVal alias $alias Reset";
  1169. }
  1170. else {
  1171. fhem "attr $aVal alias Wake-up Timer Reset";
  1172. }
  1173. fhem
  1174. "attr $aVal devStateIcon auto:time_automatic:off off:time_manual_mode:auto";
  1175. fhem "attr $aVal group $group"
  1176. if ($group);
  1177. fhem "attr $aVal icon refresh";
  1178. fhem "attr $aVal room $room"
  1179. if ($room);
  1180. fhem "attr $aVal setList state:auto,off";
  1181. fhem "attr $aVal webCmd state";
  1182. fhem "set $aVal auto";
  1183. Log3 $name, 3,
  1184. "RESIDENTStk $name: new slave dummy device $aVal created";
  1185. }
  1186. elsif ( $defs{$aVal}{TYPE} ne "dummy" ) {
  1187. Log3 $name, 3,
  1188. "RESIDENTStk $name: Defined device name in attr $aName is not a dummy device";
  1189. return "Existing device $aVal is not a dummy!";
  1190. }
  1191. }
  1192. }
  1193. return undef;
  1194. }
  1195. #####################################
  1196. # GENERAL USER AUTOMATION FUNCTIONS
  1197. #------------------------------------
  1198. #
  1199. sub RESIDENTStk_wakeupGetNext($) {
  1200. my ($name) = @_;
  1201. my $wakeupDeviceAttrName = "";
  1202. $wakeupDeviceAttrName = "rgr_wakeupDevice"
  1203. if ( defined( $attr{$name}{"rgr_wakeupDevice"} ) );
  1204. $wakeupDeviceAttrName = "rr_wakeupDevice"
  1205. if ( defined( $attr{$name}{"rr_wakeupDevice"} ) );
  1206. $wakeupDeviceAttrName = "rg_wakeupDevice"
  1207. if ( defined( $attr{$name}{"rg_wakeupDevice"} ) );
  1208. my $wakeupDeviceList = AttrVal( $name, $wakeupDeviceAttrName, 0 );
  1209. my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
  1210. localtime(time);
  1211. $hour = "0" . $hours if ( $hour < 10 );
  1212. $min = "0" . $min if ( $min < 10 );
  1213. my $wdayTomorrow = $wday + 1;
  1214. $wdayTomorrow = 0 if ( $wdayTomorrow == 7 );
  1215. my $secNow = RESIDENTStk_time2sec( $hour . ":" . $min ) + $sec;
  1216. my $definitiveNextToday = 0;
  1217. my $definitiveNextTomorrow = 0;
  1218. my $definitiveNextTodayDev = 0;
  1219. my $definitiveNextTomorrowDev = 0;
  1220. my $holidayDevice = AttrVal( "global", "holiday2we", 0 );
  1221. my $hdayTod = ReadingsVal( $holidayDevice, "state", "" );
  1222. my $hdayTom = ReadingsVal( $holidayDevice, "tomorrow", "" );
  1223. # check for each registered wake-up device
  1224. for my $wakeupDevice ( split /,/, $wakeupDeviceList ) {
  1225. next if !$wakeupDevice;
  1226. if ( !defined( $defs{$wakeupDevice} ) ) {
  1227. Log3 $name, 4,
  1228. "RESIDENTStk $name: 00 - ignoring reference to non-existing wakeupDevice $wakeupDevice";
  1229. my $wakeupDeviceListNew = $wakeupDeviceList;
  1230. $wakeupDeviceListNew =~ s/,$wakeupDevice,/,/g;
  1231. $wakeupDeviceListNew =~ s/$wakeupDevice,//g;
  1232. $wakeupDeviceListNew =~ s/,$wakeupDevice//g;
  1233. if ( $wakeupDeviceListNew ne $wakeupDeviceList ) {
  1234. Log3 $name, 3,
  1235. "RESIDENTStk $name: reference to non-existing wakeupDevice '$wakeupDevice' was removed";
  1236. fhem "attr $name $wakeupDeviceAttrName $wakeupDeviceListNew";
  1237. }
  1238. next;
  1239. }
  1240. Log3 $name, 4,
  1241. "RESIDENTStk $name: 00 - checking for next wake-up candidate $wakeupDevice";
  1242. my $nextRun = ReadingsVal( $wakeupDevice, "nextRun", 0 );
  1243. my $wakeupAtdevice = AttrVal( $wakeupDevice, "wakeupAtdevice", 0 );
  1244. my $wakeupOffset = AttrVal( $wakeupDevice, "wakeupOffset", 0 );
  1245. my $wakeupAtNTM = (
  1246. defined( $defs{$wakeupAtdevice} )
  1247. && defined( $defs{$wakeupAtdevice}{NTM} )
  1248. ? substr( $defs{$wakeupAtdevice}{NTM}, 0, -3 )
  1249. : 0
  1250. );
  1251. my $wakeupDays = AttrVal( $wakeupDevice, "wakeupDays", "" );
  1252. my $wakeupHolidays = AttrVal( $wakeupDevice, "wakeupHolidays", 0 );
  1253. my $holidayToday = 0;
  1254. my $holidayTomorrow = 0;
  1255. # get holiday status for today and tomorrow
  1256. if ( $wakeupHolidays
  1257. && $holidayDevice
  1258. && defined( $defs{$holidayDevice} )
  1259. && $defs{$holidayDevice}{TYPE} eq "holiday" )
  1260. {
  1261. if ( $hdayTod ne "none" && $hdayTod ne "" ) { $holidayToday = 1 }
  1262. if ( $hdayTom ne "none" && $hdayTom ne "" ) { $holidayTomorrow = 1 }
  1263. Log3 $name, 4,
  1264. "RESIDENTStk $wakeupDevice: 01 - Holidays to be considered - today=$holidayToday tomorrow=$holidayTomorrow";
  1265. }
  1266. else {
  1267. Log3 $name, 4,
  1268. "RESIDENTStk $wakeupDevice: 01 - Not considering any holidays";
  1269. }
  1270. # set day scope for today
  1271. my @days = ($wday);
  1272. if ( $wakeupDays ne "" ) {
  1273. @days = split /,/, $wakeupDays;
  1274. }
  1275. # set day scope for tomorrow
  1276. my @daysTomorrow = ($wdayTomorrow);
  1277. if ( $wakeupDays ne "" ) {
  1278. @daysTomorrow = split /,/, $wakeupDays;
  1279. }
  1280. if ( lc($nextRun) ne "off" && $nextRun =~ /^([0-9]{2}:[0-9]{2})$/ ) {
  1281. Log3 $name, 4,
  1282. "RESIDENTStk $wakeupDevice: 02 - possible candidate found";
  1283. my $nextRunSec;
  1284. # Use direct information from at-device if possible
  1285. if ( $wakeupAtNTM && $wakeupAtNTM =~ /^([0-9]{2}:[0-9]{2})$/ ) {
  1286. $nextRunSec =
  1287. RESIDENTStk_time2sec($wakeupAtNTM) + $wakeupOffset * 60;
  1288. Log3 $name, 4,
  1289. "RESIDENTStk $wakeupDevice: 03 - considering at-device value wakeupAtNTM=$wakeupAtNTM wakeupOffset=$wakeupOffset";
  1290. }
  1291. else {
  1292. $nextRunSec = RESIDENTStk_time2sec($nextRun);
  1293. Log3 $name, 4,
  1294. "RESIDENTStk $wakeupDevice: 03 - considering dummy-device value nextRun=$nextRun wakeupOffset=$wakeupOffset (wakeupAtNTM=$wakeupAtNTM)";
  1295. }
  1296. # still running today
  1297. if ( $nextRunSec > $secNow ) {
  1298. Log3 $name, 4,
  1299. "RESIDENTStk $wakeupDevice: 04 - this is a candidate for today";
  1300. # if today is in scope
  1301. if ( $wday ~~ @days ) {
  1302. # if we need to consider holidays in addition
  1303. if (
  1304. $wakeupHolidays && ( $wakeupHolidays eq "andHoliday"
  1305. && !$holidayToday )
  1306. || ( $wakeupHolidays eq "andNoHoliday"
  1307. && $holidayToday )
  1308. )
  1309. {
  1310. Log3 $name, 4,
  1311. "RESIDENTStk $wakeupDevice: 05 - no run today due to holiday based on combined weekday and holiday decision";
  1312. next;
  1313. }
  1314. # easy if there is no holiday dependency
  1315. elsif ( !$definitiveNextToday
  1316. || $nextRunSec < $definitiveNextToday )
  1317. {
  1318. Log3 $name, 4,
  1319. "RESIDENTStk $wakeupDevice: 05 - until now, will be NEXT WAKE-UP RUN today based on weekday decision";
  1320. $definitiveNextToday = $nextRunSec;
  1321. $definitiveNextTodayDev = $wakeupDevice;
  1322. }
  1323. }
  1324. else {
  1325. Log3 $name, 4,
  1326. "RESIDENTStk $wakeupDevice: 05 - won't be running today anymore based on weekday decision";
  1327. }
  1328. # if we need to consider holidays in parallel to weekdays
  1329. if (
  1330. $wakeupHolidays
  1331. && (
  1332. ( $wakeupHolidays eq "orHoliday" && $holidayToday )
  1333. || ( $wakeupHolidays eq "orNoHoliday"
  1334. && !$holidayToday )
  1335. )
  1336. )
  1337. {
  1338. if ( !$definitiveNextToday
  1339. || $nextRunSec < $definitiveNextToday )
  1340. {
  1341. Log3 $name, 4,
  1342. "RESIDENTStk $wakeupDevice: 06 - until now, will be NEXT WAKE-UP RUN today based on holiday decision";
  1343. $definitiveNextToday = $nextRunSec;
  1344. $definitiveNextTodayDev = $wakeupDevice;
  1345. }
  1346. }
  1347. elsif ($wakeupHolidays) {
  1348. Log3 $name, 4,
  1349. "RESIDENTStk $wakeupDevice: 06 - won't be running today based on holiday decision (wakeupHolidays=$wakeupHolidays)";
  1350. }
  1351. }
  1352. # running tomorrow
  1353. else {
  1354. Log3 $name, 4,
  1355. "RESIDENTStk $wakeupDevice: 04 - this is a candidate for tomorrow";
  1356. # if tomorrow is in scope
  1357. if ( $wdayTomorrow ~~ @daysTomorrow ) {
  1358. # if we need to consider holidays in addition
  1359. if (
  1360. $wakeupHolidays && ( $wakeupHolidays eq "andHoliday"
  1361. && !$holidayTomorrow )
  1362. || ( $wakeupHolidays eq "andNoHoliday"
  1363. && $holidayTomorrow )
  1364. )
  1365. {
  1366. Log3 $name, 4,
  1367. "RESIDENTStk $wakeupDevice: 05 - no run tomorrow due to holiday based on combined weekday and holiday decision (wakeupHolidays=$wakeupHolidays)";
  1368. next;
  1369. }
  1370. # easy if there is no holiday dependency
  1371. elsif ( !$definitiveNextTomorrow
  1372. || $nextRunSec < $definitiveNextTomorrow )
  1373. {
  1374. Log3 $name, 4,
  1375. "RESIDENTStk $wakeupDevice: 05 - until now, will be NEXT WAKE-UP RUN tomorrow based on weekday decision";
  1376. $definitiveNextTomorrow = $nextRunSec;
  1377. $definitiveNextTomorrowDev = $wakeupDevice;
  1378. }
  1379. }
  1380. else {
  1381. Log3 $name, 4,
  1382. "RESIDENTStk $wakeupDevice: 05 - won't be running tomorrow based on weekday decision";
  1383. }
  1384. # if we need to consider holidays in parallel to weekdays
  1385. if (
  1386. $wakeupHolidays
  1387. && (
  1388. ( $wakeupHolidays eq "orHoliday" && $holidayTomorrow )
  1389. || ( $wakeupHolidays eq "orNoHoliday"
  1390. && !$holidayTomorrow )
  1391. )
  1392. )
  1393. {
  1394. if ( !$definitiveNextTomorrow
  1395. || $nextRunSec < $definitiveNextTomorrow )
  1396. {
  1397. Log3 $name, 4,
  1398. "RESIDENTStk $wakeupDevice: 06 - until now, will be NEXT WAKE-UP RUN tomorrow based on holiday decision (wakeupHolidays=$wakeupHolidays)";
  1399. $definitiveNextTomorrow = $nextRunSec;
  1400. $definitiveNextTomorrowDev = $wakeupDevice;
  1401. }
  1402. }
  1403. elsif ($wakeupHolidays) {
  1404. Log3 $name, 4,
  1405. "RESIDENTStk $wakeupDevice: 06 - won't be running tomorrow based on holiday decision (wakeupHolidays=$wakeupHolidays)";
  1406. }
  1407. }
  1408. }
  1409. else {
  1410. Log3 $name, 4,
  1411. "RESIDENTStk $wakeupDevice: 02 - set to OFF so no candidate";
  1412. }
  1413. }
  1414. if ( $definitiveNextTodayDev && $definitiveNextToday ) {
  1415. Log3 $name, 4,
  1416. "RESIDENTStk $name: 07 - next wake-up result: today at "
  1417. . RESIDENTStk_sec2time($definitiveNextToday)
  1418. . ", wakeupDevice="
  1419. . $definitiveNextTodayDev;
  1420. return ( $definitiveNextTodayDev,
  1421. substr( RESIDENTStk_sec2time($definitiveNextToday), 0, -3 ) );
  1422. }
  1423. elsif ( $definitiveNextTomorrowDev && $definitiveNextTomorrow ) {
  1424. Log3 $name, 4,
  1425. "RESIDENTStk $name: 07 - next wake-up result: tomorrow at "
  1426. . RESIDENTStk_sec2time($definitiveNextTomorrow)
  1427. . ", wakeupDevice="
  1428. . $definitiveNextTomorrowDev;
  1429. return ( $definitiveNextTomorrowDev,
  1430. substr( RESIDENTStk_sec2time($definitiveNextTomorrow), 0, -3 ) );
  1431. }
  1432. return ( 0, 0 );
  1433. }
  1434. #####################################
  1435. # GENERAL FUNCTIONS USED IN RESIDENTS, ROOMMATE, GUEST
  1436. #------------------------------------
  1437. #
  1438. #
  1439. # Make a summary of two time designations
  1440. #
  1441. sub RESIDENTStk_TimeSum($$) {
  1442. my ( $val1, $val2 ) = @_;
  1443. my ( $timestamp1, $timestamp2, $math );
  1444. if ( $val1 !~ /^([0-9]{2}):([0-9]{2})$/ ) {
  1445. return $val1;
  1446. }
  1447. else {
  1448. $timestamp1 = RESIDENTStk_time2sec($val1);
  1449. }
  1450. if ( $val2 =~ /^([\+\-])([0-9]{2}):([0-9]{2})$/ ) {
  1451. $math = $1;
  1452. $timestamp2 = RESIDENTStk_time2sec("$2:$3");
  1453. }
  1454. elsif ( $val2 =~ /^([\+\-])([0-9]*)$/ ) {
  1455. $math = $1;
  1456. $timestamp2 = $2 * 60;
  1457. }
  1458. else {
  1459. return $val1;
  1460. }
  1461. if ( $math eq "-" ) {
  1462. return
  1463. substr( RESIDENTStk_sec2time( $timestamp1 - $timestamp2 ), 0, -3 );
  1464. }
  1465. else {
  1466. return
  1467. substr( RESIDENTStk_sec2time( $timestamp1 + $timestamp2 ), 0, -3 );
  1468. }
  1469. }
  1470. sub RESIDENTStk_TimeDiff ($$;$) {
  1471. my ( $datetimeNow, $datetimeOld, $format ) = @_;
  1472. if ( $datetimeNow eq "" || $datetimeOld eq "" ) {
  1473. Log3 $name, 5,
  1474. "RESIDENTStk $name: empty data: datetimeNow='$datetimeNow' datetimeOld='$datetimeOld'";
  1475. $datetimeNow = "1970-01-01 00:00:00";
  1476. $datetimeOld = "1970-01-01 00:00:00";
  1477. }
  1478. my $timestampNow = time_str2num($datetimeNow);
  1479. my $timestampOld = time_str2num($datetimeOld);
  1480. my $timeDiff = $timestampNow - $timestampOld;
  1481. # return seconds
  1482. return int( $timeDiff + 0.5 )
  1483. if ( defined($format) && $format eq "sec" );
  1484. # return minutes
  1485. return int( $timeDiff / 60 + 0.5 )
  1486. if ( defined($format) && $format eq "min" );
  1487. # return human readable format
  1488. return RESIDENTStk_sec2time( int( $timeDiff + 0.5 ) );
  1489. }
  1490. sub RESIDENTStk_sec2time($) {
  1491. my ($sec) = @_;
  1492. # return human readable format
  1493. my $hours = ( abs($sec) < 3600 ? 0 : int( abs($sec) / 3600 ) );
  1494. $sec -= ( $hours == 0 ? 0 : ( $hours * 3600 ) );
  1495. my $minutes = ( abs($sec) < 60 ? 0 : int( abs($sec) / 60 ) );
  1496. my $seconds = abs($sec) % 60;
  1497. $hours = "0" . $hours if ( $hours < 10 );
  1498. $minutes = "0" . $minutes if ( $minutes < 10 );
  1499. $seconds = "0" . $seconds if ( $seconds < 10 );
  1500. return "$hours:$minutes:$seconds";
  1501. }
  1502. sub RESIDENTStk_time2sec($) {
  1503. my ($timeString) = @_;
  1504. my @time = split /:/, $timeString;
  1505. return $time[0] * 3600 + $time[1] * 60;
  1506. }
  1507. sub RESIDENTStk_InternalTimer($$$$$) {
  1508. my ( $modifier, $tim, $callback, $hash, $waitIfInitNotDone ) = @_;
  1509. my $mHash;
  1510. if ( $modifier eq "" ) {
  1511. $mHash = $hash;
  1512. }
  1513. else {
  1514. my $timerName = $hash->{NAME} . "_" . $modifier;
  1515. if ( exists( $hash->{TIMER}{$timerName} ) ) {
  1516. $mHash = $hash->{TIMER}{$timerName};
  1517. }
  1518. else {
  1519. $mHash = {
  1520. HASH => $hash,
  1521. NAME => $hash->{NAME} . "_" . $modifier,
  1522. MODIFIER => $modifier
  1523. };
  1524. $hash->{TIMER}{$timerName} = $mHash;
  1525. }
  1526. }
  1527. InternalTimer( $tim, $callback, $mHash, $waitIfInitNotDone );
  1528. }
  1529. sub RESIDENTStk_RemoveInternalTimer($$) {
  1530. my ( $modifier, $hash ) = @_;
  1531. my $timerName = $hash->{NAME} . "_" . $modifier;
  1532. if ( $modifier eq "" ) {
  1533. RemoveInternalTimer($hash);
  1534. }
  1535. else {
  1536. my $mHash = $hash->{TIMER}{$timerName};
  1537. if ( defined($mHash) ) {
  1538. delete $hash->{TIMER}{$timerName};
  1539. RemoveInternalTimer($mHash);
  1540. }
  1541. }
  1542. }
  1543. 1;