95_YAAHM.pm 135 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430
  1. ########################################################################################
  2. #
  3. # YAAHM.pm
  4. #
  5. # Yet Another Auto Home Module for FHEM
  6. #
  7. # Prof. Dr. Peter A. Henning
  8. #
  9. # $Id: 95_YAAHM.pm 15430 2017-11-13 20:30:11Z phenning $
  10. #
  11. ########################################################################################
  12. #
  13. # This programm is free software; you can redistribute it and/or modify
  14. # it under the terms of the GNU General Public License as published by
  15. # the Free Software Foundation; either version 2 of the License, or
  16. # (at your option) any later version.
  17. #
  18. # The GNU General Public License can be found at
  19. # http://www.gnu.org/copyleft/gpl.html.
  20. # A copy is found in the textfile GPL.txt and important notices to the license
  21. # from the author is found in LICENSE.txt distributed with these scripts.
  22. #
  23. # This script is distributed in the hope that it will be useful,
  24. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  26. # GNU General Public License for more details.
  27. #
  28. ########################################################################################
  29. package main;
  30. use strict;
  31. use warnings;
  32. use vars qw(%defs); # FHEM device/button definitions
  33. use vars qw(%intAt); # FHEM at definitions
  34. use vars qw($FW_ME);
  35. use vars qw($FW_inform);
  36. use vars qw($FW_headerlines);
  37. use vars qw($FW_id);
  38. use Data::Dumper;
  39. use Math::Trig;
  40. use JSON; # imports encode_json, decode_json, to_json and from_json.
  41. #########################
  42. # Global variables
  43. my $yaahmname;
  44. my $yaahmlinkname = "Profile"; # link text
  45. my $yaahmhiddenroom = "ProfileRoom"; # hidden room
  46. my $yaahmpublicroom = "Unsorted"; # public room
  47. my $yaahmversion = "1.17";
  48. my $firstcall = 1;
  49. my %yaahm_transtable_EN = (
  50. "ok" => "OK",
  51. "notok" => "Not OK",
  52. "start" => "Start",
  53. "status" => "Status",
  54. "notstarted" => "Not started",
  55. "next" => "Next",
  56. "manual" => "Manual Time",
  57. "exceptly" => "exceptionally",
  58. "undecid" => "not decidable",
  59. "swoff" => "switched off",
  60. "and" => "and",
  61. "clock" => "",
  62. "active" => "Active",
  63. "inactive" => "Inactive",
  64. "overview" => "Summary",
  65. "name" => "Name",
  66. "event" => "Event",
  67. "time" => "Time",
  68. "timer" => "Timer",
  69. "action" => "Action",
  70. "weekly" => "Weekly ",
  71. "day" => "Day",
  72. "daytime" => "Daytime",
  73. "nighttime" => "Nighttime",
  74. "daylight" => "Daylight",
  75. "daytype" => "Day Type",
  76. "daily" => "Daily ",
  77. "type" => "Type",
  78. "description" => "Description",
  79. "profile" => "Profile",
  80. "profiles" => "Profiles",
  81. #--
  82. "aftermidnight" => "After Midnight",
  83. "beforesunrise" => "Before Sunrise",
  84. "sunrise" => "Sunrise",
  85. "aftersunrise" => "After Sunrise",
  86. "wakeup" => "WakeUp",
  87. "morning" => "Morning",
  88. "noon" => "Noon",
  89. "afternoon" => "Afternoon",
  90. "evening" => "Evening",
  91. "beforesunset" => "Before Sunset",
  92. "sunset" => "Sunset",
  93. "aftersunset" => "After Sunset",
  94. "sleep" => "Sleep",
  95. "night" => "Night",
  96. "beforemidnight" => "Before Midnight",
  97. #--
  98. "date" => "Date",
  99. "today" => "Today",
  100. "tomorrow" => "Tomorrow",
  101. "workday" => "Workday",
  102. "weekend" => "Weekend",
  103. "vacation" => "Vacation",
  104. "holiday" => "Holiday",
  105. "weekday" => "Day of Week",
  106. #--
  107. "mode" => "Mode",
  108. "normal" => "Normal",
  109. "party" => "Party",
  110. "absence" => "Absence",
  111. "donotdisturb" => "DoNotDisturb",
  112. #--
  113. "state" => "Security",
  114. "secstate" => "Device states",
  115. "unlocked" => "Unlocked",
  116. "locked" => "Locked",
  117. "unsecured" => "Not Secured",
  118. "secured" => "Secured",
  119. "protected" => "Geschützt",
  120. "guarded" => "Guarded",
  121. #--
  122. "monday" => ["Monday","Mon"],
  123. "tuesday" => ["Tuesday","Tue"],
  124. "wednesday" => ["Wednesday","Wed"],
  125. "thursday" => ["Thursday","Thu"],
  126. "friday" => ["Friday","Fri"],
  127. "saturday" => ["Saturday","Sat"],
  128. "sunday" => ["Sunday","Sun"],
  129. #--
  130. "spring" => "Spring",
  131. "summer" => "Summer",
  132. "fall" => "Fall",
  133. "winter" => "Winter"
  134. );
  135. my %yaahm_transtable_DE = (
  136. "ok" => "OK",
  137. "notok" => "Nicht OK",
  138. "start" => "Start",
  139. "status" => "Status",
  140. "notstarted" => "Nicht gestartet",
  141. "next" => "Nächste",
  142. "manual" => "Manuelle Zeit",
  143. "clock" => "Uhr",
  144. "exceptly" => "ausnahmsweise",
  145. "undecid" => "nicht bestimmbar",
  146. "swoff" => "ausgeschaltet",
  147. "and" => "und",
  148. "active" => "Aktiv",
  149. "inactive" => "Inaktiv",
  150. "overview" => "Zusammenfassung",
  151. "name" => "Name",
  152. "event" => "Event",
  153. "time" => "Zeit",
  154. "timer" => "Timer",
  155. "action" => "Aktion",
  156. "weekly" => "Wochen-",
  157. "day" => "Tag",
  158. "daytime" => "Tageszeit",
  159. "nighttime" => "Nachtzeit",
  160. "daylight" => "Tageslicht",
  161. "daytype" => "Tagestyp",
  162. "daily" => "Tages-",
  163. "type" => "Typ",
  164. "description" => "Beschreibung",
  165. "profile" => "Profil",
  166. "profiles" => "Profile",
  167. #--
  168. "aftermidnight" => "Nach Mitternacht",
  169. "beforesunrise" => "Vor Sonnenaufgang",
  170. "sunrise" => "Sonnenaufgang",
  171. "aftersunrise" => "Nach Sonnenaufgang",
  172. "wakeup" => "Wecken",
  173. "morning" => "Morgen",
  174. "noon" => "Mittag",
  175. "afternoon" => "Nachmittag",
  176. "evening" => "Abend",
  177. "beforesunset" => "Vor Sonnenuntergang",
  178. "sunset" => "Sonnenuntergang",
  179. "aftersunset" => "Nach Sonnenuntergang",
  180. "sleep" => "Schlafen",
  181. "night" => "Nacht",
  182. "beforemidnight" => "Vor Mitternacht",
  183. #--
  184. "date" => "Termin",
  185. "today" => "Heute",
  186. "tomorrow" => "Morgen",
  187. "workday" => "Arbeitstag",
  188. "weekend" => "Wochenende",
  189. "vacation" => "Ferientag",
  190. "holiday" => "Feiertag",
  191. "weekday" => "Wochentag",
  192. #--
  193. "mode" => "Modus",
  194. "normal" => "Normal",
  195. "party" => "Party",
  196. "absence" => "Abwesenheit",
  197. "donotdisturb" => "Nicht Stören",
  198. #--
  199. "state" => "Sicherheit",
  200. "secstate" => "Device States",
  201. "unlocked" => "Unverschlossen",
  202. "locked" => "Verschlossen",
  203. "unsecured" => "Nicht Gesichert",
  204. "secured" => "Gesichert",
  205. "protected" => "Geschützt",
  206. "guarded" => "Überwacht",
  207. #--
  208. "monday" => ["Montag","Mo"],
  209. "tuesday" => ["Dienstag","Di"],
  210. "wednesday" => ["Mittwoch","Mi"],
  211. "thursday" => ["Donnerstag","Do"],
  212. "friday" => ["Freitag","Fr"],
  213. "saturday" => ["Samstag","Sa"],
  214. "sunday" => ["Sonntag","So"],
  215. #--
  216. "spring" => "Frühling",
  217. "summer" => "Sommer",
  218. "fall" => "Herbst",
  219. "winter" => "Winter"
  220. );
  221. my $yaahm_tt;
  222. #-- default values, need to be overwritten from save file
  223. # first and second parameter
  224. # entries in the default table with no time entry are single-timers
  225. # entries in the default table with only first time are single-timers
  226. # entries in the default table with only second time are single-timer offsets
  227. # entries in the default table with first an second time are two-timer periods
  228. # third parameter
  229. # fourth parameter
  230. my %defaultdailytable = (
  231. "aftermidnight" => [undef,"00:01",undef,undef],
  232. "beforesunrise" => [undef,"01:00",undef,undef],
  233. "sunrise" => [undef,undef,undef,undef],
  234. "aftersunrise" => [undef,"01:00",undef,undef],
  235. "wakeup" => ["06:15",undef,undef,undef],
  236. "morning" => ["08:00",undef,undef,undef],
  237. "noon" => ["13:00",undef,undef,undef],
  238. "afternoon" => ["14:00",undef,undef,undef],
  239. "evening" => ["18:30",undef,undef,undef],
  240. "beforesunset" => [undef,"01:00",undef,undef],
  241. "sunset" => [undef,undef,undef,undef],
  242. "aftersunset" => [undef,"01:00",undef,undef],
  243. "sleep" => ["22:30",undef,undef,undef],
  244. "night" => ["22:00",undef,undef,undef],
  245. "beforemidnight" => [undef,"00:05",undef,undef]);
  246. my %dailytable = ();
  247. sub YAAHM_dsort {
  248. $dailytable{$a}[0] cmp $dailytable{$b}[0]
  249. }
  250. my @weeklytable = (
  251. "monday",
  252. "tuesday",
  253. "wednesday",
  254. "thursday",
  255. "friday",
  256. "saturday",
  257. "sunday");
  258. my %defaultwakeuptable = (
  259. "name" => "",
  260. "action" => "",
  261. "monday" => "06:15",
  262. "tuesday" => "06:15",
  263. "wednesday" => "06:15",
  264. "thursday" => "06:15",
  265. "friday" => "06:15",
  266. "saturday" => "off",
  267. "sunday" => "off");
  268. my %defaultsleeptable = (
  269. "name" => "",
  270. "action" => "",
  271. "monday" => "22:30",
  272. "tuesday" => "22:30",
  273. "wednesday" => "22:30",
  274. "thursday" => "22:30",
  275. "friday" => "23:00",
  276. "saturday" => "23:00",
  277. "sunday" => "22:30");
  278. my @daytype = (
  279. "workday",
  280. "vacation",
  281. "weekend",
  282. "holiday");
  283. my %defaultdayproperties = (
  284. "date" => "",
  285. "weekday" => "",
  286. "daytype" => 0,
  287. "desc" => "",
  288. "season" => "");
  289. my @times = (keys %defaultdailytable);
  290. my @modes = (
  291. "normal","party","absence","donotdisturb");
  292. my @states = (
  293. "unsecured","secured","protected","guarded");
  294. my @seasons = (
  295. "winter","spring","summer","fall");
  296. #-- modes or day types that affect the profile
  297. my @profmode = ("party","absence");
  298. my @profday = ("vacation","holiday");
  299. #-- temporary fix for update purpose
  300. sub YAAHM_restore($$){};
  301. sub YAAHM_sayWeeklyTime($$$){};
  302. #########################################################################################
  303. #
  304. # YAAHM_Initialize
  305. #
  306. # Parameter hash = hash of device addressed
  307. #
  308. #########################################################################################
  309. sub YAAHM_Initialize ($) {
  310. my ($hash) = @_;
  311. $hash->{DefFn} = "YAAHM_Define";
  312. $hash->{SetFn} = "YAAHM_Set";
  313. $hash->{GetFn} = "YAAHM_Get";
  314. $hash->{UndefFn} = "YAAHM_Undef";
  315. $hash->{AttrFn} = "YAAHM_Attr";
  316. my $attst = "linkname publicroom hiddenroom lockstate:locked,unlocked simulation:0,1 ".
  317. "timeHelper modeHelper modeAuto:0,1 stateDevices:textField-long stateInterval stateWarning stateHelper stateAuto:0,1 ".
  318. "holidayDevices:textField-long vacationDevices:textField-long specialDevices:textField-long";
  319. $hash->{AttrList} = $attst;
  320. if( !defined($yaahm_tt) ){
  321. #-- in any attribute redefinition readjust language
  322. my $lang = AttrVal("global","language","EN");
  323. if( $lang eq "DE"){
  324. $yaahm_tt = \%yaahm_transtable_DE;
  325. }else{
  326. $yaahm_tt = \%yaahm_transtable_EN;
  327. }
  328. }
  329. $yaahmlinkname = $yaahm_tt->{"profiles"};
  330. $data{FWEXT}{YAAHMx}{LINK} = "?room=".$yaahmhiddenroom;
  331. $data{FWEXT}{YAAHMx}{NAME} = $yaahmlinkname;
  332. $data{FWEXT}{"/YAAHM_timewidget"}{FUNC} = "YAAHM_timewidget";
  333. $data{FWEXT}{"/YAAHM_timewidget"}{FORKABLE} = 0;
  334. return undef;
  335. }
  336. #########################################################################################
  337. #
  338. # YAAHM_Define - Implements DefFn function
  339. #
  340. # Parameter hash = hash of device addressed, def = definition string
  341. #
  342. #########################################################################################
  343. sub YAAHM_Define ($$) {
  344. my ($hash, $def) = @_;
  345. my $now = time();
  346. my $name = $hash->{NAME};
  347. my $TYPE = $hash->{TYPE};
  348. $hash->{VERSION} = $yaahmversion;
  349. $yaahmname = $name;
  350. #-- readjust language
  351. my $lang = AttrVal("global","language","EN");
  352. if( $lang eq "DE"){
  353. $yaahm_tt = \%yaahm_transtable_DE;
  354. }else{
  355. $yaahm_tt = \%yaahm_transtable_EN;
  356. }
  357. #$hash->{DATA}{"TT"}=$yaahm_tt;
  358. # NOTIFYDEV
  359. my $NOTIFYDEV = "global,$name";
  360. unless ( defined( $hash->{NOTIFYDEV} ) && $hash->{NOTIFYDEV} eq $NOTIFYDEV )
  361. {
  362. $hash->{NOTIFYDEV} = $NOTIFYDEV;
  363. #$changed = 1;
  364. }
  365. readingsSingleUpdate( $hash, "state", "Initialized", 1 );
  366. $yaahmlinkname = defined($attr{$name}{"linkname"}) ? $attr{$name}{"linkname"} : $yaahmlinkname;
  367. $yaahmhiddenroom = defined($attr{$name}{"hiddenroom"}) ? $attr{$name}{"hiddenroom"} : $yaahmhiddenroom;
  368. $data{FWEXT}{YAAHMx}{LINK} = "?room=".$yaahmhiddenroom;
  369. $data{FWEXT}{YAAHMx}{NAME} = $yaahmlinkname;
  370. $attr{$name}{"room"} = $yaahmhiddenroom;
  371. my $date = YAAHM_restore($hash,0);
  372. #-- data seems to be ok, restore
  373. if( defined($date) ){
  374. YAAHM_restore($hash,1);
  375. Log3 $name,1,"[YAAHM_Define] data hash restored from save file with date $date";
  376. #-- intialization
  377. }else{
  378. Log3 $name,1,"[YAAHM_Define] data hash is initialized";
  379. #-- clone daily default profile
  380. $hash->{DATA}{"DT"} = {%defaultdailytable};
  381. #-- clone weekly default profile
  382. $hash->{DATA}{"WT"} = ();
  383. push(@{$hash->{DATA}{"WT"}},{%defaultwakeuptable});
  384. $hash->{DATA}{"WT"}[0]{"name"} = $yaahm_tt->{"wakeup"};
  385. push(@{$hash->{DATA}{"WT"}},{%defaultsleeptable});
  386. $hash->{DATA}{"WT"}[1]{"name"} = $yaahm_tt->{"sleep"};
  387. #-- clone days for today and tomorrow
  388. $hash->{DATA}{"DD"} = ();
  389. push(@{$hash->{DATA}{"DD"}},{%defaultdayproperties});
  390. push(@{$hash->{DATA}{"DD"}},{%defaultdayproperties});
  391. }
  392. #-- determine Astro device
  393. if( !exists($modules{Astro}{defptr}) ){
  394. Log3 $name,1,"[YAAHM] does not find an Astro device, loading module Astro separately";
  395. require "95_Astro.pm";
  396. }else{
  397. my @keys = sort keys %{$modules{Astro}{defptr}};
  398. Log3 $name,1,"[YAAHM] finds ".int(@keys)." Astro devices, module not loaded separately";
  399. }
  400. #--
  401. $modules{YAAHM}{defptr}{$name} = $hash;
  402. RemoveInternalTimer($hash);
  403. InternalTimer ($now + 5, 'YAAHM_CreateEntry', $hash, 0);
  404. return;
  405. }
  406. #########################################################################################
  407. #
  408. # YAAHM_Undef - Implements Undef function
  409. #
  410. # Parameter hash = hash of device addressed, def = definition string
  411. #
  412. #########################################################################################
  413. sub YAAHM_Undef ($$) {
  414. my ($hash,$arg) = @_;
  415. my $name = $hash->{NAME};
  416. RemoveInternalTimer($hash);
  417. delete $data{FWEXT}{YAAHMx};
  418. if (defined $defs{$name."_weblink"}) {
  419. FW_fC("delete ".$name."_weblink");
  420. Log3 $hash, 3, "[".$name. " V".$yaahmversion."]"." Weblink ".$name."_weblink deleted";
  421. }
  422. if (defined $defs{$name."_shortlink"}) {
  423. FW_fC("delete ".$name."_shortlink");
  424. Log3 $hash, 3, "[".$name. " V".$yaahmversion."]"." Weblink ".$name."_shortlink deleted";
  425. }
  426. delete($modules{YAAHM}{defptr});
  427. return undef;
  428. }
  429. #########################################################################################
  430. #
  431. # YAAHM_Attr - Implements Attr function
  432. #
  433. # Parameter hash = hash of device addressed, ???
  434. #
  435. #########################################################################################
  436. sub YAAHM_Attr($$$) {
  437. my ($cmd, $name, $attrName, $attrVal) = @_;
  438. my $hash = $defs{"$name"};
  439. #-- in any attribute redefinition readjust language
  440. my $lang = AttrVal("global","language","EN");
  441. if( $lang eq "DE"){
  442. $yaahm_tt = \%yaahm_transtable_DE;
  443. }else{
  444. $yaahm_tt = \%yaahm_transtable_EN;
  445. }
  446. #---------------------------------------
  447. if ( $attrName eq "timeHelper" ) {
  448. my $dh = (defined($attr{$name}{"timeHelper"})) ? $attr{$name}{"timeHelper"} : undef;
  449. #-- remove this function from all entries
  450. if( $cmd eq "del" ){
  451. foreach my $key (keys %defaultdailytable){
  452. my $xval = $hash->{DATA}{"DT"}{$key}[2];
  453. if( $xval =~ /^{$dh/){
  454. my @cmds = split(',',$xval);
  455. shift(@cmds);
  456. $xval = join(',',@cmds);
  457. $hash->{DATA}{"DT"}{$key}[2] = $xval;
  458. }
  459. }
  460. }
  461. #---------------------------------------
  462. }elsif ( ($cmd eq "set") && ($attrName eq "linkname") ) {
  463. $yaahmlinkname = $attrVal;
  464. $data{FWEXT}{YAAHMx}{NAME} = $yaahmlinkname;
  465. #---------------------------------------
  466. }elsif ( ($cmd eq "set") && ($attrName eq "publicroom") ) {
  467. $yaahmpublicroom = $attrVal;
  468. FW_fC("attr ".$name."_shortlink room ".$yaahmpublicroom);
  469. #---------------------------------------
  470. }elsif ( ($cmd eq "set") && ($attrName eq "hiddenroom") ){
  471. #-- remove old hiddenroom from FHEMWEB instances
  472. foreach my $dn (sort keys %defs) {
  473. if ($defs{$dn}{TYPE} eq "FHEMWEB" && $defs{$dn}{NAME} !~ /FHEMWEB:/) {
  474. my $hr = AttrVal($defs{$dn}{NAME}, "hiddenroom", "");
  475. $hr =~ s/$yaahmhiddenroom//;
  476. $hr =~ s/,,//;
  477. $hr =~ s/,$//;
  478. FW_fC("attr ".$defs{$dn}{NAME}." hiddenroom ".$hr);
  479. }
  480. }
  481. #-- new value
  482. $yaahmhiddenroom = $attrVal;
  483. $data{FWEXT}{YAAHMx}{LINK} = "?room=".$yaahmhiddenroom;
  484. FW_fC("attr ".$name."_weblink room ".$yaahmhiddenroom);
  485. #-- place into FHEMWEB instances
  486. foreach my $dn (sort keys %defs) {
  487. if ($defs{$dn}{TYPE} eq "FHEMWEB" && $defs{$dn}{NAME} !~ /FHEMWEB:/) {
  488. my $hr = AttrVal($defs{$dn}{NAME}, "hiddenroom", "");
  489. if (index($hr,$yaahmhiddenroom) == -1){
  490. if ($hr eq "") {
  491. FW_fC("attr ".$defs{$dn}{NAME}." hiddenroom ".$yaahmhiddenroom);
  492. }else {
  493. FW_fC("attr ".$defs{$dn}{NAME}." hiddenroom ".$hr.",".$yaahmhiddenroom);
  494. }
  495. Log3 $hash, 3, "[".$name. " V".$yaahmversion."]"." Added hidden room '".$yaahmhiddenroom."' to ".$defs{$dn}{NAME};
  496. }
  497. }
  498. }
  499. #---------------------------------------
  500. }elsif ( ($cmd eq "delete") && ($attrName eq "stateDevices") ) {
  501. fhem("deletereading $name sdev_housestate");
  502. fhem("deletereading $name sec_housestate");
  503. fhem("deletereading $name sym_housestate");
  504. YAAHM_RemoveInternalTimer("check",$hash);
  505. #---------------------------------------
  506. }elsif ( ($cmd eq "set") && ($attrName eq "stateInterval") ) {
  507. my $next = gettimeofday()+AttrVal($name,"stateInterval",60)*60;
  508. YAAHM_RemoveInternalTimer("check",$hash);
  509. YAAHM_InternalTimer("check",$next, "YAAHM_checkstate", $hash, 0);
  510. #---------------------------------------
  511. }elsif ( ($cmd eq "delete") && ($attrName eq "stateInterval") ) {
  512. my $next = gettimeofday()+3600;
  513. YAAHM_RemoveInternalTimer("check",$hash);
  514. YAAHM_InternalTimer("check",$next, "YAAHM_checkstate", $hash, 0);
  515. #---------------------------------------
  516. }elsif ( $attrName eq "holidayDevices" ) {
  517. return "Value for $attrName has invalid format"
  518. unless ( $cmd eq "del" || $attrVal =~ m/^[A-Za-z\d._]+(?:,[A-Za-z\d._]*)*$/ );
  519. #---------------------------------------
  520. }elsif ( $attrName eq "vacationDevices" ) {
  521. return "Value for $attrName has invalid format"
  522. unless ( $cmd eq "del" || $attrVal =~ m/^[A-Za-z\d._]+(?:,[A-Za-z\d._]*)*$/ );
  523. }
  524. return;
  525. }
  526. #########################################################################################
  527. #
  528. # YAAHM_CreateEntry - Puts the YAAHM entry into the FHEM menu
  529. #
  530. # Parameter hash = hash of device addressed
  531. #
  532. #########################################################################################
  533. sub YAAHM_CreateEntry($) {
  534. my ($hash) = @_;
  535. my $name = $hash->{NAME};
  536. $yaahmlinkname = defined($attr{$name}{"linkname"}) ? $attr{$name}{"linkname"} : $yaahmlinkname;
  537. $yaahmpublicroom = defined($attr{$name}{"publicroom"}) ? $attr{$name}{"publicroom"} : $yaahmpublicroom;
  538. $yaahmhiddenroom = defined($attr{$name}{"hiddenroom"}) ? $attr{$name}{"hiddenroom"} : $yaahmhiddenroom;
  539. #-- this is the long YAAHM entry
  540. FW_fC("defmod ".$name."_weblink weblink htmlCode {YAAHM_Longtable(\"".$name."\")}");
  541. Log3 $hash, 3, "[".$name. " V".$yaahmversion."]"." Weblink ".$name."_weblink created";
  542. FW_fC("attr ".$name."_weblink room ".$yaahmhiddenroom)
  543. if(!defined($attr{$name."_weblink"}{"room"}));
  544. #-- this is the short YAAHM entry
  545. FW_fC("defmod ".$name."_shortlink weblink htmlCode {YAAHM_Shorttable(\"".$name."\")}");
  546. Log3 $hash, 3, "[".$name. " V".$yaahmversion."]"." Weblink ".$name."_shortlink created";
  547. FW_fC("attr ".$name."_shortlink room ".$yaahmpublicroom)
  548. if(!defined($attr{$name."_shortlink"}{"room"}));
  549. foreach my $dn (sort keys %defs) {
  550. if ($defs{$dn}{TYPE} eq "FHEMWEB" && $defs{$dn}{NAME} !~ /FHEMWEB:/) {
  551. my $hr = AttrVal($defs{$dn}{NAME}, "hiddenroom", "");
  552. if (index($hr,$yaahmhiddenroom) == -1){
  553. if ($hr eq "") {
  554. FW_fC("attr ".$defs{$dn}{NAME}." hiddenroom ".$yaahmhiddenroom);
  555. }else {
  556. FW_fC("attr ".$defs{$dn}{NAME}." hiddenroom ".$hr.",".$yaahmhiddenroom);
  557. }
  558. Log3 $hash, 3, "[".$name. " V".$yaahmversion."]"." Added hidden room '".$yaahmhiddenroom."' to ".$defs{$dn}{NAME};
  559. }
  560. }
  561. }
  562. #-- Start updater
  563. InternalTimer(gettimeofday()+ 3, "YAAHM_updater",$hash,0);
  564. YAAHM_InternalTimer("check",time()+ 5, "YAAHM_checkstate", $hash, 0);
  565. }
  566. #########################################################################################
  567. #
  568. # YAAHM_Set - Implements the Set function
  569. #
  570. # Parameter hash = hash of device addressed
  571. #
  572. #########################################################################################
  573. sub YAAHM_Set($@) {
  574. my ( $hash, $name, $cmd, @args ) = @_;
  575. my $imax;
  576. my $if;
  577. my $msg;
  578. my $exec = ( defined($attr{$name}{"simulation"})&&$attr{$name}{"simulation"}==1 ) ? 0 : 1;
  579. #-----------------------------------------------------------
  580. if ( $cmd =~ /^manualnext.*/ ) {
  581. #--timer address
  582. if( $args[0] =~ /^\d+/ ) {
  583. #-- check if valid
  584. if( $args[0] >= int(@{$hash->{DATA}{"WT"}}) ){
  585. $msg = "Error, timer number ".$args[0]." does not exist, number musst be smaller than ".int( @{$hash->{DATA}{"WT"}});
  586. Log3 $name,1,"[YAAHM_Set] ".$msg;
  587. return $msg;
  588. }
  589. $cmd = "next_".$args[0];
  590. }else{
  591. my $if = undef;
  592. for( my $i=0;$i<int(@{$hash->{DATA}{"WT"}});$i++){
  593. $if = $i
  594. if ($hash->{DATA}{"WT"}[$i]{"name"} eq $args[0] );
  595. };
  596. #-- check if valid
  597. if( !defined($if) ){
  598. $msg = "Error: timer name ".$args[0]." not found";
  599. Log3 $name,1,"[YAAHM_Set] ".$msg;
  600. return $msg;
  601. }
  602. $cmd = "next_".$if;
  603. }
  604. return YAAHM_nextWeeklyTime($name,$cmd,$args[1],$exec);
  605. #-----------------------------------------------------------
  606. }elsif ( $cmd =~ /^time.*/ ) {
  607. return YAAHM_time($name,$args[0],$exec);
  608. #-----------------------------------------------------------
  609. }elsif ( $cmd =~ /^mode.*/ ) {
  610. return YAAHM_mode($name,$args[0],$exec);
  611. #-----------------------------------------------------------
  612. }elsif ( $cmd =~ /^state.*/ ) {
  613. return YAAHM_state($name,$args[0],$exec);
  614. #-----------------------------------------------------------
  615. }elsif ( $cmd =~ /^lock(ed)?$/ ) {
  616. readingsSingleUpdate( $hash, "lockstate", "locked", 0 );
  617. return;
  618. #-----------------------------------------------------------
  619. } elsif ( $cmd =~ /^unlock(ed)?$/ ) {
  620. readingsSingleUpdate( $hash, "lockstate", "unlocked", 0 );
  621. return;
  622. #-----------------------------------------------------------
  623. } elsif ( $cmd =~ /^save/ ) {
  624. return YAAHM_save($hash);
  625. #-----------------------------------------------------------
  626. } elsif ( $cmd =~ /^restore/ ) {
  627. return YAAHM_restore($hash,1);
  628. #-----------------------------------------------------------
  629. } elsif ( $cmd =~ /^initialize/ ) {
  630. $firstcall = 1;
  631. YAAHM_updater($hash);
  632. YAAHM_InternalTimer("check",time()+ 5, "YAAHM_checkstate", $hash, 0);
  633. #-----------------------------------------------------------
  634. } elsif ( $cmd eq "createWeekly" ){
  635. return "[YAAHM] missing name for new weekly profile"
  636. if( !defined($args[0]) );
  637. #-- find index
  638. $imax = int(@{$hash->{DATA}{"WT"}});
  639. $if= undef;
  640. for( my $j=0;$j<$imax;$j++){
  641. if($hash->{DATA}{"WT"}[$j]{"name"} eq $args[0]){
  642. $if = $j;
  643. last;
  644. }
  645. }
  646. return "[YAAHM] name $args[0] for weekly profile to be created is already in use"
  647. if( defined($if) );
  648. #-- clone wakeuptable
  649. push(@{$hash->{DATA}{"WT"}},{%defaultwakeuptable});
  650. $hash->{DATA}{"WT"}[$imax]{"name"} = $args[0];
  651. #-- save everything
  652. YAAHM_save($hash);
  653. fhem("save");
  654. return "[YAAHM] weekly profile $args[0] created successfully";
  655. #-----------------------------------------------------------
  656. } elsif ( $cmd eq "deleteWeekly" ){
  657. return "[YAAHM] missing name for weekly profile to be deleted"
  658. if( !defined($args[0]) );
  659. return "[YAAHM] Default weekly profile cannot be deleted"
  660. if( ($args[0] eq $yaahm_tt->{"wakeup"}) || ($args[0] eq $yaahm_tt->{"sleep"}) );
  661. #-- find index
  662. $imax = int(@{$hash->{DATA}{"WT"}});
  663. $if= undef;
  664. for( my $j=0;$j<$imax;$j++){
  665. if($hash->{DATA}{"WT"}[$j]{"name"} eq $args[0]){
  666. $if = $j;
  667. last;
  668. }
  669. }
  670. return "[YAAHM] name $args[0] for weekly profile to be deleted is not known"
  671. if( !defined($if) );
  672. splice(@{$hash->{DATA}{"WT"}},$if,1);
  673. #-- delete timer
  674. fhem("delete ".$name.".wtimer_".$if.".IF");
  675. #-- save everything
  676. YAAHM_save($hash);
  677. fhem("save");
  678. return "[YAAHM] weekly profile $args[0] deleted successfully";
  679. #-----------------------------------------------------------
  680. } else {
  681. my $str = "";
  682. return "[YAAHM] Unknown argument " . $cmd . ", choose one of".
  683. " manualnext time:".join(',',@times)." mode:".join(',',@modes).
  684. " state:".join(',',@states)." locked:noArg unlocked:noArg save:noArg restore:noArg initialize:noArg createWeekly deleteWeekly";
  685. }
  686. }
  687. #########################################################################################
  688. #
  689. # YAAHM_Get - Implements the Get function
  690. #
  691. # Parameter hash = hash of device addressed
  692. #
  693. #########################################################################################
  694. sub YAAHM_Get($@) {
  695. my ($hash, @args) = @_;
  696. my $res = "";
  697. my $msg;
  698. my $name = $args[0];
  699. my $arg = (defined($args[1]) ? $args[1] : "");
  700. if ($arg eq "version") {
  701. return "YAAHM.version => $yaahmversion";
  702. }elsif ($arg eq "test") {
  703. YAAHM_testWeeklyTime($hash);
  704. return "ok";
  705. }elsif ( $arg eq "next" || $arg eq "sayNext" ){
  706. my $if;
  707. #--timer address
  708. if( $args[2] =~ /^\d+/ ) {
  709. #-- check if valid
  710. if( $args[2] >= int(@{$hash->{DATA}{"WT"}}) ){
  711. $msg = "Error, timer number ".$args[2]." does not exist, number musst be smaller than ".int( @{$hash->{DATA}{"WT"}});
  712. Log3 $name,1,"[YAAHM_Get] ".$msg;
  713. return $msg;
  714. }
  715. $if=$args[2];
  716. }else{
  717. $if = undef;
  718. for( my $i=0;$i<int(@{$hash->{DATA}{"WT"}});$i++){
  719. $if = $i
  720. if ($hash->{DATA}{"WT"}[$i]{"name"} eq $args[1] );
  721. };
  722. #-- check if valid
  723. if( !defined($if) ){
  724. $msg = "Error: timer name ".$args[2]." not found";
  725. Log3 $name,1,"[YAAHM_Get] ".$msg;
  726. return $msg;
  727. }
  728. }
  729. if( $arg eq "next" ){
  730. return YAAHM_sayWeeklyTime($hash,$if,0);
  731. }else{
  732. return YAAHM_sayWeeklyTime($hash,$if,1);
  733. }
  734. }elsif ($arg eq "template") {
  735. $res = "sub HouseTimeHelper(\@){\n".
  736. " my (\$event,\$param1,\$param2) = \@_;\n\n".
  737. " Log 1,\"[HouseTimeHelper] event=\$event\";\n\n".
  738. " my \$time = ReadingsVal(\"".$name."\",\"housetime\",\"\");\n".
  739. " my \$phase = ReadingsVal(\"".$name."\",\"housephase\",\"\");\n".
  740. " my \$state = ReadingsVal(\"".$name."\",\"housestate\",\"\");\n".
  741. " my \$party = (ReadingsVal(\"".$name."\",\"housemode\",\"\") eq \"party\") ? 1 : 0;\n".
  742. " my \$absence = (ReadingsVal(\"".$name."\",\"housemode\",\"\") eq \"absence\") ? 1 : 0;\n".
  743. " my \$dndist = (ReadingsVal(\"".$name."\",\"housemode\",\"\") eq \"donotdisturb\") ? 1 : 0;\n".
  744. " my \$todaytype = ReadingsVal(\"".$name."\",\"tr_todayType\",\"\");\n".
  745. " my \$todaydesc = ReadingsVal(\"".$name."\",\"todayDesc\",\"\");\n".
  746. " my \$tomorrowtype = ReadingsVal(\"".$name."\",\"tr_tomorrowType\",\"\");\n".
  747. " my \$tomorrowdesc = ReadingsVal(\"".$name."\",\"tomorrowDesc\",\"\");\n";
  748. #-- iterate through table
  749. foreach my $key (sort YAAHM_dsort keys %dailytable){
  750. $res .= " #---------------------------------------------------------------------\n";
  751. my $if = ($key eq "aftermidnight") ? "if" : "}elsif";
  752. $res .= " ".$if."( \$event eq \"".$key."\" ){\n\n";
  753. }
  754. $res .= " }\n}\n";
  755. $res .= "sub HouseStateHelper(\@){\n".
  756. " my (\$event,\$param1,\$param2) = \@_;\n\n".
  757. " Log 1,\"[HouseStateHelper] event=\$event\";\n\n".
  758. " my \$time = ReadingsVal(\"".$name."\",\"housetime\",\"\");\n".
  759. " my \$phase = ReadingsVal(\"".$name."\",\"housephase\",\"\");\n".
  760. " my \$state = ReadingsVal(\"".$name."\",\"housestate\",\"\");\n".
  761. " my \$party = (ReadingsVal(\"".$name."\",\"housemode\",\"\") eq \"party\") ? 1 : 0;\n".
  762. " my \$absence = (ReadingsVal(\"".$name."\",\"housemode\",\"\") eq \"absence\") ? 1 : 0;\n".
  763. " my \$dndist = (ReadingsVal(\"".$name."\",\"housemode\",\"\") eq \"donotdisturb\") ? 1 : 0;\n";
  764. #-- iterate through table
  765. for( my $i=0;$i<int(@states);$i++) {
  766. $res .= " #---------------------------------------------------------------------\n";
  767. my $if = ($i == 0) ? "if" : "}elsif";
  768. $res .= " ".$if."( \$event eq \"".$states[$i]."\" ){\n\n";
  769. }
  770. $res .= " }\n}\n";
  771. return $res;
  772. } else {
  773. $res = "0,1";
  774. for(my $i = 2; $i<int( @{$hash->{DATA}{"WT"}});$i++){
  775. $res .= ",".$i;
  776. }
  777. return "Unknown argument $arg choose one of next:".$res." sayNext:".$res." version:noArg template:noArg";
  778. }
  779. }
  780. #########################################################################################
  781. #
  782. # YAAHM_save
  783. #
  784. # Parameter hash = hash of the YAAHM device
  785. #
  786. #########################################################################################
  787. sub YAAHM_save($) {
  788. my ($hash) = @_;
  789. $hash->{DATA}{"savedate"} = localtime(time);
  790. readingsSingleUpdate( $hash, "savedate", $hash->{DATA}{"savedate"}, 1 );
  791. my $json = JSON->new->utf8;
  792. my $jhash0 = eval{ $json->encode( $hash->{DATA} ) };
  793. my $error = FileWrite("YAAHMFILE",$jhash0);
  794. #Log 1,"[YAAHM_save] error=$error";
  795. return;
  796. }
  797. #########################################################################################
  798. #
  799. # YAAHM_restore
  800. #
  801. # Parameter hash = hash of the YAAHM device
  802. #
  803. #########################################################################################
  804. sub YAAHM_restore($$) {
  805. my ($hash,$doit) = @_;
  806. my $name = $hash->{NAME};
  807. my ($error,$jhash0) = FileRead("YAAHMFILE");
  808. if( defined($error) && $error ne "" ){
  809. Log3 $name,1,"[YAAHM_restore] read error=$error";
  810. return undef;
  811. }
  812. my $json = JSON->new->utf8;
  813. my $jhash1 = eval{ $json->decode( $jhash0 ) };
  814. my $date = $jhash1->{"savedate"};
  815. #-- just for the first time, reading an old savefile
  816. $date = localtime(time)
  817. if( !defined($date));
  818. readingsSingleUpdate( $hash, "savedate", $date, 0 );
  819. if( $doit==1 ){
  820. $hash->{DATA} = {%{$jhash1}};
  821. Log3 $name,5,"[YAAHM_restore] Data hash restored from save file with date ".$date;
  822. return 1;
  823. }else{
  824. return $date;
  825. }
  826. }
  827. #########################################################################################
  828. #
  829. # YAAHM_setParm - Receives parameter values from the javascript FE
  830. #
  831. # Parameter name = name of the YAAHM device
  832. #
  833. #########################################################################################
  834. sub YAAHM_setParm($@) {
  835. my ($name, @a) = @_;
  836. my $hash = $defs{$name};
  837. my $cmd = $a[0];
  838. my $key = $a[1];
  839. my $msg = "";
  840. my $val;
  841. #-- daily profile
  842. # start, end/offset, execution, active in mode / daytype
  843. if ($cmd eq "dt") {
  844. for( my $i=1;$i<5;$i++){
  845. $val = $a[$i+1];
  846. if( ($val eq "undef")||($val eq "") ){
  847. $val = undef;
  848. }elsif( ($i<3) && ($val !~ /\d?\d:\d\d/)){
  849. $msg = "wrong time specification $val for key $key, must be hh:mm";
  850. Log 1,"[YAAHM_setParm] ".$msg;
  851. $val = "00:00";
  852. }elsif( $i<3 ){
  853. my ($hour,$min) = split(':',$val);
  854. if( $hour>23 || $min>59 ){
  855. $msg = "wrong time specification $val for key $key > 23:59";
  856. Log 1,"[YAAHM_setParm] ".$msg;
  857. $val = "00:00";
  858. }
  859. }
  860. $hash->{DATA}{"DT"}{$key}[$i-1]=$val;
  861. }
  862. return $msg;
  863. #-- weekly profile
  864. }elsif ($cmd eq "wt") {
  865. #-- action
  866. $hash->{DATA}{"WT"}[$a[1]]{"action"} = $a[2];
  867. #-- next time
  868. $val = $a[3];
  869. if( ($val eq "undef")||($val eq "") ){
  870. $val = undef;
  871. }elsif( $val =~/^off/ ){
  872. #-- ok
  873. }elsif( $val =~ /\d?\d:\d\d/ ){
  874. #-- ok
  875. my ($hour,$min) = split(':',$val);
  876. if( $hour>23 || $min>59 ){
  877. $msg = "wrong time specification next=$val for weekly timer > 23:59".$a[1];
  878. Log 1,"[YAAHM_setParm] ".$msg;
  879. $val = "off";
  880. }
  881. }else{
  882. $msg = "wrong time specification next=$val for weekly timer ".$a[1].", must be hh:mm of 'off'";
  883. Log 1,"[YAAHM_setParm] ".$msg;
  884. $val = "off";
  885. }
  886. #-- next waketime
  887. $hash->{DATA}{"WT"}[$a[1]]{"next"} = $val;
  888. #-- activity party/absence
  889. $hash->{DATA}{"WT"}[$a[1]]{"acti_m"} = $a[4];
  890. #-- activity vacation/holiday
  891. $hash->{DATA}{"WT"}[$a[1]]{"acti_d"} = $a[5];
  892. #-- weekdays
  893. for( my $i=0;$i<7;$i++){
  894. $val = $a[$i+6];
  895. if( ($val eq "undef")||($val eq "") ){
  896. $val = undef;
  897. }elsif( $val =~/^off/ ){
  898. #-- ok
  899. }elsif( $val =~ /\d?\d:\d\d/ ){
  900. #-- ok
  901. my ($hour,$min) = split(':',$val);
  902. if( $hour>23 || $min>59 ){
  903. $msg = "wrong time specification $val for weekly timer > 23:59 ".$a[1];
  904. Log 1,"[YAAHM_setParm] ".$msg;
  905. $val = "off";
  906. }
  907. }else{
  908. $msg = "wrong time specification $val for weekly timer ".$a[1].", must be hh:mm or 'off'";
  909. Log 1,"[YAAHM_setParm] ".$msg;
  910. $val = "off";
  911. }
  912. $hash->{DATA}{"WT"}[$a[1]]{$weeklytable[$i]} = $val;
  913. }
  914. return $msg;
  915. }
  916. }
  917. #########################################################################################
  918. #
  919. # YAAHM_time - Change the house time (aftermidnight .. beforemidnight)
  920. #
  921. # Parameter name = name of the YAAHM device
  922. #
  923. #########################################################################################
  924. sub YAAHM_time {
  925. my ($name,$targettime,$exec) = @_;
  926. my $hash = $defs{$name};
  927. my $prevtime = defined($hash->{DATA}{"HSM"}{"time"}) ? $hash->{DATA}{"HSM"}{"time"} : "";
  928. my $currmode = defined($hash->{DATA}{"HSM"}{"mode"}) ? $hash->{DATA}{"HSM"}{"mode"} : "normal";
  929. my $currstate = defined($hash->{DATA}{"HSM"}{"state"}) ? $hash->{DATA}{"HSM"}{"state"} : "unsecured";
  930. my $msg = "";
  931. #-- local checks
  932. #-- double change
  933. #if( $prevtime eq $targettime ){
  934. # $msg = "Transition skipped, the time event $targettime has been executed already";
  935. #}
  936. #-- don't
  937. if( $msg ne "" ){
  938. Log3 $name,1,"[YAAHM_time] ".$msg;
  939. return $msg;
  940. }
  941. #-- doit
  942. my ($sec, $min, $hour, $day, $month, $year, $wday,$yday,$isdst) = localtime(time);
  943. my $lval = sprintf("%02d%02d",$hour,$min);
  944. my $mval = $dailytable{"morning"}[0];
  945. my $nval = $dailytable{"night"}[0];
  946. my $tval = $dailytable{$targettime}[0];
  947. $mval =~ s/://;
  948. $nval =~ s/://;
  949. $tval =~ s/://;
  950. #-- targetphase always according to real time, not to command time
  951. my $targetphase = ( ($lval >= $mval) && ( $nval > $lval ) ) ? "daytime" : "nighttime";
  952. #-- iterate through table to find next event
  953. my $nexttime;
  954. my $sval;
  955. my $oval="0000";
  956. foreach my $key (sort YAAHM_dsort keys %dailytable){
  957. $nexttime = $key;
  958. $sval = $dailytable{$key}[0];
  959. next
  960. if (!defined($sval));
  961. $sval =~ s/://;
  962. last
  963. if ( ($lval <= $sval) && ( $lval > $oval ) );
  964. $oval = $sval;
  965. }
  966. my $ma = defined($attr{$name}{"modeAuto"}) && ($attr{$name}{"modeAuto"} == 1);
  967. my $sa = defined($attr{$name}{"stateAuto"}) && ($attr{$name}{"stateAuto"} == 1);
  968. #-- automatically leave party mode at morning time or when going to bed
  969. if( $currmode eq "party" && $targettime =~ /(morning)|(sleep)/ && $ma ){
  970. $msg = YAAHM_mode($name,"normal",$exec)."\n";
  971. $msg .= YAAHM_state($name,"secured",$exec)."\n"
  972. if( $currstate eq "unsecured" && $targettime eq "sleep" && $sa );
  973. #-- automatically leave absence mode at wakeup time
  974. }elsif( $currmode eq "absence" && $targettime =~ /(wakeup)/ && $ma ){
  975. $msg = YAAHM_mode($name,"normal",$exec)."\n";
  976. #-- automatically leave donotdisturb mode at any time event
  977. }elsif( $currmode eq "donotdisturb" && $ma ){
  978. $msg = YAAHM_mode($name,"normal",$exec)."\n";
  979. #-- automatically secure the house at night time or when going to bed (if not absence, and if not party)
  980. }elsif( $currmode eq "normal" && $currstate eq "unsecured" && $targettime =~ /(night)|(sleep)/ && $sa ){
  981. $msg = YAAHM_state($name,"secured",$exec)."\n";
  982. }
  983. $hash->{DATA}{"HSM"}{"time"} = $targettime;
  984. YAAHM_checkMonthly($hash,'event',$targettime);
  985. readingsBeginUpdate($hash);
  986. readingsBulkUpdate($hash,"prev_housetime",$prevtime);
  987. readingsBulkUpdate($hash,"next_housetime",$nexttime);
  988. readingsBulkUpdate($hash,"housetime",$targettime);
  989. readingsBulkUpdate($hash,"tr_housetime",$yaahm_tt->{$targettime});
  990. readingsBulkUpdate($hash,"housephase",$targetphase);
  991. readingsBulkUpdate($hash,"tr_housephase",$yaahm_tt->{$targetphase});
  992. readingsEndUpdate($hash,1);
  993. #-- helper function not executed, e.g. by call from external timer
  994. return
  995. if( !defined($exec) );
  996. #-- execute the helper function
  997. my $xval;
  998. my $ival;
  999. my $wupn;
  1000. #-- todo here: what should we do, if the timer is NOT enabled and we get up or go to bed anyhow ???
  1001. if( $targettime eq "wakeup" ){
  1002. $wupn = $hash->{DATA}{"WT"}[0]{"name"};
  1003. $ival = (ReadingsVal($name.".wtimer_0.IF","mode","") ne "disabled");
  1004. $xval = $ival ? $hash->{DATA}{"WT"}[0]{"action"} : "";
  1005. $msg .= "Simulation ".$xval." from weekly profile ".$wupn;
  1006. $msg .= " (disabled)"
  1007. if !$ival;
  1008. }elsif( $targettime eq "sleep" ){
  1009. $wupn = $hash->{DATA}{"WT"}[1]{"name"};
  1010. $ival = (ReadingsVal($name.".wtimer_1.IF","mode","") ne "disabled");
  1011. $xval = $ival ? $hash->{DATA}{"WT"}[1]{"action"} : "";
  1012. $msg .= "Simulation ".$xval." from weekly profile ".$wupn;
  1013. $msg .= " (disabled)"
  1014. if !$ival;
  1015. }else{
  1016. $xval = $dailytable{$targettime}[2];
  1017. $msg .= "Simulation ".$xval;
  1018. }
  1019. if( $exec==1 ){
  1020. fhem($xval);
  1021. }elsif( $exec==0 ){
  1022. Log3 $name,1,"[YAAHM_time] ".$msg;
  1023. return $msg;
  1024. }
  1025. }
  1026. #########################################################################################
  1027. #
  1028. # YAAHM_nextWeeklyTime - set the next weekly time
  1029. #
  1030. # Parameter name = name of device addressed
  1031. #
  1032. #########################################################################################
  1033. sub YAAHM_nextWeeklyTime {
  1034. my ($name,$cmd,$time,$exec) = @_;
  1035. my $hash = $defs{$name};
  1036. my $msg;
  1037. #--determine which timer (duplicate check when coming from set)
  1038. $cmd =~ /.*next_([0-9]+)$/;
  1039. my $i = $1;
  1040. if( $i >= int( @{$hash->{DATA}{"WT"}}) ){
  1041. $msg = "Error, timer number $i does not exist, number musst be smaller than ".int( @{$hash->{DATA}{"WT"}});
  1042. Log3 $name,1,"[YAAHM_nextWeeklyTime] ".$msg;
  1043. return $msg;
  1044. }
  1045. #-- check value - may be empty
  1046. if( $time ne ""){
  1047. #-- off=ok, do nothing
  1048. if( $time eq "off"){
  1049. #-- time=ok, check
  1050. }elsif( $time =~ /(\d?\d):(\d\d)(:(\d\d))?/ ){
  1051. if( $1 >= 24 || $2 >= 60){
  1052. $msg = "Error, time specification $time for timer ".$cmd." > 23:59 ";
  1053. Log3 $name,1,"[YAAHM_nextWeeklyTime] ".$msg;
  1054. return $msg;
  1055. }
  1056. $time = sprintf("%02d:\%02d",$1,$2);
  1057. }else{
  1058. $msg = "Error, time specification $time invalid for timer ".$cmd.", must be hh:mm";;
  1059. Log3 $name,1,"[YAAHM_nextWeeklyTime] ".$msg;
  1060. return $msg;
  1061. }
  1062. }
  1063. #-- weekly profile times
  1064. my $sg0;
  1065. if( ReadingsVal($name.".wtimer_".$i.".IF","mode","") ne "disabled" ){
  1066. $sg0 = $time;
  1067. }else{
  1068. $sg0 = "off";
  1069. }
  1070. $hash->{DATA}{"WT"}[$i]{"next"} = $sg0;
  1071. YAAHM_setWeeklyTime($hash);
  1072. readingsEndUpdate($hash,1);
  1073. }
  1074. #########################################################################################
  1075. #
  1076. # YAAHM_mode - Change the house mode (normal, party, absence)
  1077. #
  1078. # Parameter name = name of the YAAHM device
  1079. #
  1080. #########################################################################################
  1081. sub YAAHM_mode {
  1082. my ($name,$targetmode,$exec) = @_;
  1083. my $hash = $defs{$name};
  1084. my $prevmode = defined($hash->{DATA}{"HSM"}{"mode"}) ? $hash->{DATA}{"HSM"}{"mode"} : "normal";
  1085. my $currstate = defined($hash->{DATA}{"HSM"}{"state"}) ? $hash->{DATA}{"HSM"}{"state"} : "unsecured";
  1086. my $msg = "";
  1087. #-- local checks
  1088. #-- double change
  1089. if( $prevmode eq $targetmode ){
  1090. $msg = "transition skipped, we are already in $targetmode mode";
  1091. #-- transition into party and absence is only possible from normal mode
  1092. }elsif( $prevmode ne "normal" && $targetmode ne "normal"){
  1093. $msg = "transition into $targetmode mode is only possible from normal mode";
  1094. #-- global checks
  1095. #-- transition into party mode only possible in unlocked state
  1096. }elsif( $targetmode eq "party" && $currstate ne "unsecured" ){
  1097. $msg = "transition into party mode is only possible in unsecured state"
  1098. }
  1099. #-- don't
  1100. if( $msg ne "" ){
  1101. Log3 $name,1,"[YAAHM_mode] ".$msg;
  1102. return $msg;
  1103. }
  1104. $hash->{DATA}{"HSM"}{"mode"} = $targetmode;
  1105. #-- doit, if not simulation
  1106. if (defined($attr{$name}{"modeHelper"})){
  1107. if( !defined($exec) || $exec==1 ){
  1108. fhem("{".$attr{$name}{"modeHelper"}."('".$targetmode."')}");
  1109. }else{
  1110. $msg = "Simulation {".$attr{$name}{"modeHelper"}."('".$targetmode."')}";
  1111. Log3 $name,1,"[YAAHM_mode] ".$msg;
  1112. return $msg;
  1113. }
  1114. }
  1115. readingsBeginUpdate($hash);
  1116. readingsBulkUpdate($hash,"prev_housemode",$prevmode);
  1117. readingsBulkUpdate($hash,"housemode",$targetmode);
  1118. readingsBulkUpdate($hash,"tr_housemode",$yaahm_tt->{$targetmode});
  1119. readingsEndUpdate($hash,1);
  1120. }
  1121. #########################################################################################
  1122. #
  1123. # YAAHM_state - Change the house state (unscured, secured, guarded)
  1124. #
  1125. # Parameter name = name of the YAAHM device
  1126. #
  1127. #########################################################################################
  1128. sub YAAHM_state {
  1129. my ($name,$targetstate,$exec) = @_;
  1130. my $hash = $defs{$name};
  1131. my $prevstate = defined($hash->{DATA}{"HSM"}{"state"}) ? $hash->{DATA}{"HSM"}{"state"} : "unsecured";
  1132. my $currmode = defined($hash->{DATA}{"HSM"}{"mode"}) ? $hash->{DATA}{"HSM"}{"mode"} : "normal";
  1133. my $msg = "";
  1134. #-- local checks
  1135. #-- double change
  1136. #if( $prevstate eq $targetstate ){
  1137. # $msg = "transition skipped, we are already in $targetstate state";
  1138. #-- global checks
  1139. #-- changing away from unlocked in party mode is not possible
  1140. if( $targetstate ne "unlocked" && $currmode eq "party" ){
  1141. $msg = "not possible in party mode";
  1142. }
  1143. #-- don't
  1144. if( $msg ne "" ){
  1145. Log3 $name,1,"[YAAHM_state] ".$msg;
  1146. return $msg;
  1147. }
  1148. #-- doit, if not simulation
  1149. if (defined($attr{$name}{"stateHelper"})){
  1150. if( !defined($exec) || $exec==1 ){
  1151. fhem("{".$attr{$name}{"stateHelper"}."('".$targetstate."')}");
  1152. }else{
  1153. $msg = "Simulation {".$attr{$name}{"stateHelper"}."('".$targetstate."')}";
  1154. Log3 $name,1,"[YAAHM_state] ".$msg;
  1155. return $msg;
  1156. }
  1157. }
  1158. $hash->{DATA}{"HSM"}{"state"} = $targetstate;
  1159. readingsBeginUpdate($hash);
  1160. readingsBulkUpdate($hash,"prev_housestate",$prevstate);
  1161. readingsBulkUpdate($hash,"housestate",$targetstate);
  1162. readingsBulkUpdate($hash,"tr_housestate",$yaahm_tt->{$targetstate});
  1163. readingsEndUpdate($hash,1);
  1164. YAAHM_InternalTimer("check",time()+ 30, "YAAHM_checkstate", $hash, 0);
  1165. }
  1166. #########################################################################################
  1167. #
  1168. # YAAHM_checkstate - check state devices
  1169. #
  1170. # Parameter someHash = either internal hash of timer
  1171. # => need to dereference it for getting device hash
  1172. # or device hash
  1173. #
  1174. #########################################################################################
  1175. sub YAAHM_checkstate($) {
  1176. my ($someHash) = @_;
  1177. my $hash;
  1178. if( defined($someHash->{HASH}) ){
  1179. $hash = $someHash->{HASH};
  1180. }else{
  1181. $hash = $someHash;
  1182. }
  1183. my $name = $hash->{NAME};
  1184. my $next;
  1185. $next = gettimeofday()+AttrVal($name,"stateInterval",60)*60;
  1186. YAAHM_RemoveInternalTimer("check",$hash);
  1187. YAAHM_InternalTimer("check",$next, "YAAHM_checkstate", $hash, 0);
  1188. Log3 $name, 5,"[YAAHM_checkstate] on device ".$hash->{NAME}." called";
  1189. my $istate;
  1190. my $cstate = defined($hash->{DATA}{"HSM"}{"state"}) ? $hash->{DATA}{"HSM"}{"state"} : "";
  1191. return undef
  1192. if( !defined($attr{$name}{"stateDevices"}) );
  1193. for($istate=0;$istate<int(@states);$istate++){
  1194. last
  1195. if($states[$istate] eq $cstate);
  1196. }
  1197. my (@devlist,@devl);
  1198. my ($dev,$devs,$devh,);
  1199. my @devf = ();
  1200. my $isf = 0;
  1201. @devlist = split(',',$attr{$name}{"stateDevices"});
  1202. foreach my $devc (@devlist) {
  1203. @devl = split(':',$devc);
  1204. $dev = $devl[0];
  1205. $devs = $devl[$istate+1];
  1206. if( defined($devs) && ($devs ne "") ){
  1207. $devh = Value($dev);
  1208. if( $devs ne $devh ){
  1209. $isf = 1;
  1210. push(@devf,"<tr><td style=\"text-align:left;padding:5px\">".$dev."</td><td style=\"text-align:left;padding:5px\"><div style=\"color:red\">".$yaahm_tt->{'notok'}.
  1211. "</div></td><td style=\"text-align:left;padding:5px\">".$devh."</td></tr>");
  1212. if( defined(AttrVal($name,"stateWarning",undef)) ){
  1213. fhem("{".AttrVal($name,"stateWarning",undef)."($dev,$devs,$devh)}");
  1214. }
  1215. }else{
  1216. push(@devf,"<tr><td style=\"text-align:left;padding:5px\">".$dev."</td><td style=\"text-align:left;padding:5px\"><div style=\"color:green\">".$yaahm_tt->{'ok'}."</div></td><td></td></tr>");
  1217. }
  1218. }
  1219. }
  1220. readingsBeginUpdate($hash);
  1221. readingsBulkUpdate($hash,"sdev_housestate","<html><table>".join("<br/>",@devf)."</table></html>");
  1222. readingsBulkUpdate($hash,"sec_housestate",(($isf==0)?"secure":"insecure"));
  1223. readingsBulkUpdate($hash,"sym_housestate",(($isf==0)?"<html><div style=\"color:green\">&#x2713;</div></html>":"<html><div style=\"color:red\">&#x274c;</div></html>"));
  1224. readingsEndUpdate($hash,1);
  1225. return undef
  1226. }
  1227. #########################################################################################
  1228. #
  1229. # YAAHM_SM - State machine
  1230. #
  1231. # Parameter hash = hash of device addressed
  1232. #
  1233. #########################################################################################
  1234. sub YAAHM_SM($) {
  1235. my ($hash) = @_;
  1236. }
  1237. #########################################################################################
  1238. #
  1239. # YAAHM_informer - Tell FHEMWEB to inform this page
  1240. #
  1241. # Parameter me = hash of FHEMWEB instance
  1242. #
  1243. #########################################################################################
  1244. sub YAAHM_informer($) {
  1245. my ($me) = @_;
  1246. $me->{inform}{type} = "status";
  1247. $me->{inform}{filter} = "YYY";
  1248. #$me->{inform}{since} = time()-5;
  1249. $me->{inform}{fmt} = "JSON";
  1250. my $filter = $me->{inform}{filter};
  1251. my %h = map { $_ => 1 } devspec2array($filter);
  1252. $h{global} = 1 if( $me->{inform}{addglobal} );
  1253. $h{"#FHEMWEB:$FW_wname"} = 1;
  1254. $me->{inform}{devices} = \%h;
  1255. %FW_visibleDeviceHash = FW_visibleDevices();
  1256. $me->{NTFY_ORDER} = $FW_cname; # else notifyfn won't be called
  1257. %ntfyHash = ();
  1258. }
  1259. #########################################################################################
  1260. #
  1261. # YAAHM_startDayTimer - start the daily timer function
  1262. #
  1263. # Parameter name = name of the YAAHM device
  1264. #
  1265. #########################################################################################
  1266. sub YAAHM_startDayTimer($) {
  1267. my ($name) = @_;
  1268. my $hash = $defs{$name};
  1269. my $res = "defmod $name.dtimer.IF DOIF ";
  1270. my $msg;
  1271. #--cleanup after definition fault
  1272. fhem("deletereading $name t_aftermidnight")
  1273. if( ReadingsVal($name,"t_aftermidnight",undef) );
  1274. fhem("deletereading $name t_aftersunrise")
  1275. if( ReadingsVal($name,"t_aftersunrise",undef) );
  1276. fhem("deletereading $name t_aftersunset")
  1277. if( ReadingsVal($name,"t_aftersunset",undef) );
  1278. fhem("deletereading $name t_beforemidnight")
  1279. if( ReadingsVal($name,"t_beforemidnight",undef) );
  1280. fhem("deletereading $name t_beforesunrise")
  1281. if( ReadingsVal($name,"t_beforesunrise",undef) );
  1282. fhem("deletereading $name t_beforesunset")
  1283. if( ReadingsVal($name,"t_beforesunset",undef) );
  1284. delete $hash->{DATA}{"DT"}{"daytime"};
  1285. delete $hash->{DATA}{"DT"}{"nighttime"};
  1286. #-- TODO check for plausibility
  1287. #--aftermidnight must be >= 00:01
  1288. my $check=$hash->{DATA}{"DT"}{"aftermidnight"}[0];
  1289. $check =~ s/://;
  1290. if( $check <= 0 ){
  1291. Log3 $name,1,"[YAAHM_startDayTimer] aftermidnight is minimum 00:01, used this value";
  1292. $hash->{DATA}{"DT"}{"aftermidnight"}[0] = "00:01";
  1293. }
  1294. #-- Internal timer for night time
  1295. my ($sec, $min, $hour, $day, $month, $year, $wday,$yday,$isdst) = localtime(time);
  1296. my $nval = $hash->{DATA}{"DT"}{"night"}[0];
  1297. if( $nval !~ /\d\d:\d\d/ ){
  1298. $msg = "Error in night time specification";
  1299. Log3 1,$name,"[YAAHM_startDayTimer] ".$msg;
  1300. return $msg;
  1301. }
  1302. my ($hourn,$minn) = split(':',$nval);
  1303. my $deltan = ($hourn-$hour)*3600+($minn-$min)*60-$sec;
  1304. my $delta = $deltan;
  1305. $delta += 86400
  1306. if( $delta<0 );
  1307. YAAHM_RemoveInternalTimer ("nighttime", $hash);
  1308. YAAHM_InternalTimer ("nighttime", gettimeofday()+$delta, "YAAHM_tonight", $hash, 0);
  1309. #-- Internal timer for daytime
  1310. my $mval = $hash->{DATA}{"DT"}{"morning"}[0];
  1311. if( $mval !~ /\d\d:\d\d/ ){
  1312. $msg = "Error in morning time specification";
  1313. Log3 1,$name,"[YAAHM_startDayTimer] ".$msg;
  1314. return $msg;
  1315. }
  1316. ($hourn,$minn) = split(':',$mval);
  1317. my $deltam = ($hourn-$hour)*3600+($minn-$min)*60-$sec;
  1318. $delta = $deltam;
  1319. $delta += 86400
  1320. if( $delta<0 );
  1321. YAAHM_RemoveInternalTimer ("daytime", $hash);
  1322. YAAHM_InternalTimer ("daytime", gettimeofday()+$delta, "YAAHM_today", $hash, 0);
  1323. #-- currently day or night ?
  1324. my $currtime = (($deltam < 0) && ($deltan > 0)) ? "daytime" : "nighttime";
  1325. #-- put data into readings
  1326. readingsBeginUpdate($hash);
  1327. readingsBulkUpdate($hash,"housephase",$currtime);
  1328. readingsBulkUpdate($hash,"tr_housephase",$yaahm_tt->{$currtime});
  1329. #-- compose external timer
  1330. foreach my $key (sort YAAHM_dsort keys %defaultdailytable){
  1331. next if( !defined($hash->{DATA}{"DT"}{$key}[2]) );
  1332. my $f1 = defined($defaultdailytable{$key}[0]);
  1333. my $f2 = defined($defaultdailytable{$key}[1]);
  1334. my $f3 = defined($hash->{DATA}{"DT"}{$key}[2]) && $hash->{DATA}{"DT"}{$key}[2] ne "";
  1335. my $xval = "{YAAHM_time('".$name."','".$key."')},".$hash->{DATA}{"DT"}{$key}[2];
  1336. #-- entries in the default table with no entry are single-timers
  1337. if( !$f1 and !$f2 ){
  1338. $res .= "([[".$name.":s_".$key."]])\n(".$xval.")\nDOELSEIF"
  1339. if( $f3 );
  1340. #-- entries in the default table with only first time are single-timers
  1341. }elsif( $f1 and !$f2 ){
  1342. $res .= "([[".$name.":s_".$key."]])\n(".$xval.")\nDOELSEIF"
  1343. if( $f3 );
  1344. #-- entries in the default table with only second time are single-timer offsets
  1345. }elsif( !$f1 and $f2 ){
  1346. $res .= "([[".$name.":s_".$key."]])\n(".$xval.")\nDOELSEIF"
  1347. if( $f3 );
  1348. #-- entries in the default table with first and second time are two-timer periods
  1349. }elsif( $f1 and $f2 ){
  1350. $res .= "([[".$name.":s_".$key."]-[".$name.":t_".$key."]])\n(".$xval.")\nDOELSEIF"
  1351. if( $f3 );
  1352. #-- something wrong
  1353. }else{
  1354. $msg = "Daily timer $name.dtimer.IF NOT started, something wrong with entry ".$key;
  1355. Log 1,"[YAAHM_startDayTimer] ".$msg;
  1356. return $msg;
  1357. }
  1358. }
  1359. readingsEndUpdate($hash,1);
  1360. #-- take out last DOELSEIF
  1361. $res =~ s/\nDOELSEIF$//;
  1362. fhem($res);
  1363. fhem("attr $name.dtimer.IF do always");
  1364. fhem("set $name.dtimer.IF enable");
  1365. #-- save everything
  1366. YAAHM_save($hash);
  1367. fhem("save");
  1368. return "Daily timer $name.dtimer.IF started";
  1369. }
  1370. #########################################################################################
  1371. #
  1372. # YAAHM_startWeeklyTimer - start the Weekly timer function
  1373. #
  1374. # Parameter name = name of the YAAHM device
  1375. #
  1376. #########################################################################################
  1377. sub YAAHM_startWeeklyTimer($) {
  1378. my ($name) = @_;
  1379. my $hash = $defs{$name};
  1380. my $res;
  1381. my $wupn;
  1382. YAAHM_setWeeklyTime($hash);
  1383. #-- start timer
  1384. for( my $i=0;$i < int( @{$hash->{DATA}{"WT"}} );$i++){
  1385. $wupn = $hash->{DATA}{"WT"}[$i]{"name"};
  1386. $res = "defmod ".$name.".wtimer_".$i.".IF DOIF ([".$name.":ring_".$i."] eq \"off\")\n()\nDOELSEIF\n(([[".$name.":ring_".$i."]])";
  1387. #-- check for activity description
  1388. my $g4a = defined($hash->{DATA}{"WT"}[$i]{"acti_m"}) ? $hash->{DATA}{"WT"}[$i]{"acti_m"} : "";
  1389. my $g4b = defined($hash->{DATA}{"WT"}[$i]{"acti_d"}) ? $hash->{DATA}{"WT"}[$i]{"acti_d"} : "";
  1390. my $v4a = ($g4a ne "") ? "(normal)|(".join(')|(',split(',',$g4a)).")" : "(normal)";
  1391. my $v4b = ($g4b ne "") ? "(workday)|(weekend)|(".join(')|(',split(',',$g4b)).")" : "(workday)|(weekend)";
  1392. $res .= "\nand ([" .$name. ":housemode] =~ \"".$v4a."\")";
  1393. $res .= "\nand ([" .$name. ":todayType] =~ \"".$v4b."\")";
  1394. #-- action
  1395. my $xval = "";
  1396. if( $i==0 ){
  1397. $xval = "{YAAHM_time('".$name."','wakeup')},".$hash->{DATA}{"WT"}[$i]{"action"};
  1398. }elsif( $i==1 ){
  1399. $xval = "{YAAHM_time('".$name."','sleep')},".$hash->{DATA}{"WT"}[$i]{"action"};
  1400. }else{
  1401. $xval = $hash->{DATA}{"WT"}[$i]{"action"};
  1402. }
  1403. #-- action
  1404. $res .= ")\n(".$xval.")";
  1405. #-- doit
  1406. fhem($res);
  1407. fhem("attr ".$name.".wtimer_".$i.".IF do always");
  1408. fhem("set ".$name.".wtimer_".$i.".IF enable");
  1409. }
  1410. #-- save everything
  1411. YAAHM_save($hash);
  1412. fhem("save");
  1413. return "Weekly timers started";
  1414. }
  1415. #########################################################################################
  1416. #
  1417. # YAAHM_setWeeklyTime - set the Weekly times into readings
  1418. #
  1419. # Parameter hash = hash of device addressed
  1420. #
  1421. #########################################################################################
  1422. sub YAAHM_setWeeklyTime($) {
  1423. my ($hash) = @_;
  1424. my $name = $hash->{NAME};
  1425. #-- weekly profile times
  1426. my ($sg0,$sg1,$sg0mod,$sg1mod,$sg0en,$sg1en,$ring_0,$ring_1,$ng);
  1427. #-- iterate over timers
  1428. for( my $i=0;$i<int( @{$hash->{DATA}{"WT"}} );$i++){
  1429. #-- TODO: inconsistency, time is off although only timer disabled
  1430. #-- lowest priority is the waketable - provided, the timer device is enabled
  1431. if( ReadingsVal($name.".wtimer_".$i.".IF","mode","") ne "disabled" ){
  1432. $sg0 = $hash->{DATA}{"WT"}[$i]{ $weeklytable[$hash->{DATA}{"DD"}[0]{"weekday"}] } ;
  1433. $sg1 = $hash->{DATA}{"WT"}[$i]{ $weeklytable[$hash->{DATA}{"DD"}[1]{"weekday"}] };
  1434. $ng = $hash->{DATA}{"WT"}[$i]{ "next" };
  1435. $sg0en = "enabled";
  1436. $sg1en = "enabled";
  1437. }else{
  1438. $sg0 = "off";
  1439. $sg1 = "off";
  1440. $ng = "off";
  1441. $sg0en = "disabled (timer)";
  1442. $sg1en = "disabled (timer)";
  1443. }
  1444. #-- next higher priority is to check for daytype
  1445. my $wupad = $hash->{DATA}{"WT"}[$i]{"acti_d"}.",workday,weekend";
  1446. if( ($sg0 !~ /^off/) && (index($wupad, $hash->{DATA}{"DD"}[0]{"daytype"}) == -1) ){
  1447. $sg0mod = "off (".substr(ReadingsVal($name,"tr_todayType",""),0,3).")";
  1448. $sg0en = "disabled (".ReadingsVal($name,"todayType","").")";
  1449. }else{
  1450. $sg0mod = $sg0;
  1451. $sg0en = "enabled"
  1452. if( $sg0en !~ /^disabled/);
  1453. }
  1454. if( ($sg1 !~ /^off/) && (index($wupad, $hash->{DATA}{"DD"}[1]{"daytype"}) == -1) ){
  1455. $sg1mod = "off (".substr(ReadingsVal($name,"tr_tomorrowType",""),0,3).")";
  1456. $sg1en = "disabled (".ReadingsVal($name,"todayType","").")";
  1457. }else{
  1458. $sg1mod = $sg1;
  1459. $sg1en = "enabled"
  1460. if( $sg1en !~ /^disabled/);
  1461. }
  1462. #-- next higher priority is to check for housemode (only today !)
  1463. my $wupam = $hash->{DATA}{"WT"}[$i]{"acti_m"}.",normal";
  1464. if( ($sg0mod !~ /^off/) && (index($wupam, ReadingsVal($name,"housemode","")) == -1) ){
  1465. $sg0mod = "off (".substr(ReadingsVal($name,"tr_housemode",""),0,3).")";
  1466. $sg0en = "disabled (".ReadingsVal($name,"housemode","").")";
  1467. }
  1468. #-- highest priority is a "next" time specification
  1469. #-- current time
  1470. my ($sec, $min, $hour, $day, $month, $year, $wday,$yday,$isdst) = localtime(time);
  1471. my $lga = sprintf("%02d%02d",$hour,$min);
  1472. #-- today's waketime
  1473. my $tga = $sg0;
  1474. $tga =~ s/://;
  1475. #-- "next" input
  1476. my $nga = (defined $ng)?$ng:"";
  1477. $nga =~ s/://;
  1478. #-- "next" is empty
  1479. if( $nga eq "" ){
  1480. $ring_0 = $sg0;
  1481. $ring_1 = $sg1;
  1482. #-- "next" is off
  1483. }elsif( $nga eq "off" ){
  1484. #-- today's waketime not over => we mean today
  1485. if( $tga ne "off" && ($tga > $lga)){
  1486. if( $sg0mod !~ /^off/ ){
  1487. $sg0mod = "off (man)";
  1488. $ring_0 = "off";
  1489. $ring_1 = $sg1;
  1490. }
  1491. #-- today's waketime over => we mean tomorrow
  1492. }else{
  1493. if( $sg1mod !~ /^off/ ){
  1494. $sg1mod = "off (man)";
  1495. $ring_0 = $sg0;
  1496. $ring_1 = "$sg1 (off)";
  1497. }
  1498. }
  1499. #-- "next" is nontrivial timespec
  1500. }else{
  1501. #-- "next" after current time => we mean today
  1502. if( $nga > $lga ){
  1503. #-- only restore standard setting
  1504. if( $ng eq $sg0 ){
  1505. $sg0mod = $sg0;
  1506. $ring_0 = $sg0;
  1507. $hash->{DATA}{"WT"}[$i]{ "next" } = "";
  1508. }else{
  1509. $sg0mod = "$ng (man)";
  1510. $ring_0 = $ng;
  1511. }
  1512. $ring_1 = $sg1;
  1513. #-- "next" before current time => we mean tomorrow
  1514. }else{
  1515. #-- only restore standard setting
  1516. if( $ng eq $sg1 ){
  1517. $sg0mod = $sg1;
  1518. $ring_1 = $sg1;
  1519. $hash->{DATA}{"WT"}[$i]{ "next" } = "";
  1520. }else{
  1521. $sg1mod = "$ng (man)";
  1522. $ring_1 = "$sg1 ($ng)";
  1523. }
  1524. $ring_0 = $sg0;
  1525. }
  1526. }
  1527. #-- notation:
  1528. # today_i is today's waketime of timer i
  1529. # tomorrow_i is tomorrow's waketime of timer i
  1530. # timers have additional conditions for activation according
  1531. # to housemode and daytype, these conditions are checked in the timer device
  1532. # devices and are not part of the table. But we have a reading:
  1533. # today_i_e is a copy of the condition checked in the timer device
  1534. # (housemode and daytype)
  1535. # tomorrow_i_e is not a complete copy of the condition checked in the timer device,
  1536. # (daytype only, because housemode of tomorrow is not known)
  1537. # ring_[i]_1 is tomorrow's ring time of timer i
  1538. readingsBeginUpdate($hash);
  1539. readingsBulkUpdate( $hash, "today_".$i,$sg0 );
  1540. readingsBulkUpdate( $hash, "tomorrow_".$i,$sg1 );
  1541. readingsBulkUpdate( $hash, "today_".$i."_e",$sg0en );
  1542. readingsBulkUpdate( $hash, "tomorrow_".$i."_e",$sg1en );
  1543. readingsBulkUpdate( $hash, "ring_".$i,$ring_0 );
  1544. readingsBulkUpdate( $hash, "ring_".$i."_1",$ring_1 );
  1545. readingsBulkUpdate( $hash, "next_".$i,$ng );
  1546. $hash->{DATA}{"WT"}[$i]{"ring_0"} = $ring_0;
  1547. $hash->{DATA}{"WT"}[$i]{"ring_1"} = $ring_1;
  1548. $hash->{DATA}{"WT"}[$i]{"ring_0x"} = $sg0mod;
  1549. $hash->{DATA}{"WT"}[$i]{"ring_1x"} = $sg1mod;
  1550. readingsEndUpdate($hash,1);
  1551. YAAHM_sayWeeklyTime($hash,$i,0);
  1552. }
  1553. }
  1554. #########################################################################################
  1555. #
  1556. # YAAHM_sayWeeklyTime - say the next weekly time
  1557. #
  1558. # Parameter name = name of device addressed
  1559. #
  1560. #########################################################################################
  1561. sub YAAHM_sayWeeklyTime($$$) {
  1562. my ($hash,$timer,$sp) = @_;
  1563. my $name = $hash->{NAME};
  1564. my ($tod,$tom,$ton,$hl,$ml,$tl,$ht,$mt,$tt,$tsay,$chg,$msg,$hw,$mw,$pt);
  1565. #--determine which timer (duplicate check when coming from set)
  1566. if( $timer >= int( @{$hash->{DATA}{"WT"}}) ){
  1567. $msg = "Error, timer number $timer does not exist, number musst be smaller than ".int( @{$hash->{DATA}{"WT"}});
  1568. Log3 $name,1,"[YAAHM_sayNextTime] ".$msg;
  1569. return $msg;
  1570. }
  1571. $tod = $hash->{DATA}{"WT"}[$timer]{"ring_0"};
  1572. $tom = $hash->{DATA}{"WT"}[$timer]{"ring_1"};
  1573. $ton = $hash->{DATA}{"WT"}[$timer]{"next"};
  1574. $msg = $hash->{DATA}{"WT"}[$timer]{"name"};
  1575. ($hl,$ml) = split(':',strftime('%H:%M', localtime(time)));
  1576. $tl = 60*$hl+$ml;
  1577. #-- today off AND tomorrow any time, off or special time
  1578. if( $tod eq "off" ){
  1579. #-- special time
  1580. if( ($ton =~ /(\d?\d):(\d\d)(:(\d\d))?/) && ($tom ne $ton) ){
  1581. $hw = $1*1;
  1582. $mw = $2*1;
  1583. $pt = sprintf("%d:%02d",$hw,$mw)." ".tolower($yaahm_tt->{"today"});
  1584. $msg .= " ".tolower($yaahm_tt->{"tomorrow"})." ".$yaahm_tt->{"exceptly"}." $hw ".$yaahm_tt->{"clock"};
  1585. $msg .=" $mw"
  1586. if( $mw != 0 );
  1587. }elsif( $tom =~ /(\d?\d):(\d\d)(:(\d\d))?/ && $tom !~ /.*\(off\)$/ ){
  1588. $hw = $1*1;
  1589. $mw = $2*1;
  1590. $pt = sprintf("%d:%02d",$hw,$mw)." ".tolower($yaahm_tt->{"tomorrow"});
  1591. $msg .= " ".tolower($yaahm_tt->{"tomorrow"})." $hw ".$yaahm_tt->{"clock"};
  1592. $msg .=" $mw"
  1593. if( $mw != 0 );
  1594. }elsif( $tom eq "off" || $tom =~ /.*\(off\)$/ ){
  1595. $pt = "off ".tolower($yaahm_tt->{"today"})." ".$yaahm_tt->{"and"}." ".tolower($yaahm_tt->{"tomorrow"});
  1596. $msg .= " ".tolower($yaahm_tt->{"today"})." ".$yaahm_tt->{"and"}." ".tolower($yaahm_tt->{"tomorrow"})." ".$yaahm_tt->{"swoff"};
  1597. }else{
  1598. $pt = $yaahm_tt->{"undecid"};
  1599. $msg .= " ".$yaahm_tt->{"undecid"};
  1600. }
  1601. #-- today nontrivial => compare this time with current time
  1602. }elsif( $tod =~ /(\d?\d):(\d\d)(:(\d\d))?/ ){
  1603. #Log 1,"===========> |$1|$2|$3|$4";
  1604. ($ht,$mt) = split(':',$tod);
  1605. $tt=60*$ht+$mt;
  1606. #-- wakeup later today
  1607. if( $tt >= $tl ){
  1608. $hw = $1*1;
  1609. $mw = $2*1;
  1610. $pt = sprintf("%d:%02d",$hw,$mw)." ".tolower($yaahm_tt->{"today"});
  1611. $msg .= " ".tolower($yaahm_tt->{"today"})." $hw ".$yaahm_tt->{"clock"};
  1612. $msg .=" $mw"
  1613. if( $mw != 0 );
  1614. #-- todays time already past => tomorrow - but this may be off
  1615. }elsif( ($tom eq "off") || ($tom =~ /.*\(off\)/) ){
  1616. $pt = "off ".tolower($yaahm_tt->{"tomorrow"});
  1617. $msg .= " ".tolower($yaahm_tt->{"tomorrow"})." ".$yaahm_tt->{"swoff"};
  1618. }elsif( $tom =~ /(\d?\d):(\d\d)(:(\d\d))?( \((\d?\d):(\d\d)(:(\d\d))?\))?/ ){
  1619. #Log 1,"===========> |$1|$2|$3|$4|$5|$6";
  1620. if( defined($5) && $5 ne ""){
  1621. $hw = $6*1;
  1622. $mw = $7*1;
  1623. }else{
  1624. $hw = $1*1;
  1625. $mw = $2*1;
  1626. }
  1627. $pt = sprintf("%d:%02d",$hw,$mw)." ".tolower($yaahm_tt->{"tomorrow"});
  1628. $msg .= " ".tolower($yaahm_tt->{"tomorrow"})." $hw ".$yaahm_tt->{"clock"};
  1629. $msg .=" $mw"
  1630. if( $mw != 0 );
  1631. }else{
  1632. $pt = $yaahm_tt->{"undecid"};
  1633. $msg .= " ".$yaahm_tt->{"undecid"};
  1634. }
  1635. }else{
  1636. $pt = $yaahm_tt->{"undecid"};
  1637. $msg .= " ".$yaahm_tt->{"undecid"};
  1638. }
  1639. $hash->{DATA}{"WT"}[$timer]{"wake"} = $pt;
  1640. readingsSingleUpdate($hash,"tr_wake_".$timer,$pt,1);
  1641. if( $sp==0 ){
  1642. return $pt
  1643. }else{
  1644. return $msg;
  1645. }
  1646. }
  1647. #########################################################################################
  1648. #
  1649. # YAAHM_checkMonthly - check Monthly calendar at each
  1650. #
  1651. # Parameter hash = hash of device addressed
  1652. #
  1653. #########################################################################################
  1654. sub YAAHM_checkMonthly($$$) {
  1655. my ($hash,$event,$param) = @_;
  1656. my $name = $hash->{NAME};
  1657. my ($ret,$line,$fline,$date);
  1658. my (@lines,@chunks,@tday,@eday,@sday,@tmor,@two);
  1659. my ($stoday,$stom,$stwom);
  1660. my $todaylong = "";
  1661. my $tomlong = "";
  1662. my $twodaylong= "";
  1663. #-- hourly call
  1664. #if( ($event eq 'hour') || ($event eq '') ){
  1665. # my $text;
  1666. # $text = fhem("get Muell text modeAlarmOrStart");
  1667. # $text = "--" if (!$text);
  1668. # $text = "--" if ($text eq "");
  1669. # #fhem("set Termin ".$text);
  1670. # return $text;
  1671. #-- Vorschau täglich oder wenn neu gestartet
  1672. if( ($event eq "test") || (($event eq 'event') && ($param eq 'aftermidnight')) ){
  1673. my $specialDevs = AttrVal( $name, "specialDevices", "" );
  1674. foreach my $specialDev ( split( /,/, $specialDevs ) ) {
  1675. my ($todaydesc,$tomdesc,$twomdesc);
  1676. #-- device of type holiday
  1677. if( IsDevice( $specialDev, "holiday" )){
  1678. $stoday = strftime('%2m-%2d', localtime(time));
  1679. $stom = strftime('%2m-%2d', localtime(time+86400));
  1680. $stwom = strftime('%2m-%2d', localtime(time+2*86400));
  1681. my $tod = holiday_refresh( $specialDev, $stoday );
  1682. if ( $tod ne "none" ) {
  1683. $todaydesc = $tod;
  1684. Log3 $name, 5,"[YAAHM] found today=special date \"$todaydesc\" in holiday $specialDev";
  1685. }
  1686. $tod = holiday_refresh( $specialDev, $stom );
  1687. if ( $tod ne "none" ) {
  1688. $tomdesc = $tod;
  1689. Log3 $name, 5,"[YAAHM] found tomorrow=special date \"$tomdesc\" in holiday $specialDev";
  1690. }
  1691. $tod = holiday_refresh( $specialDev, $stwom );
  1692. if ( $tod ne "none" ) {
  1693. $twomdesc = $tod;
  1694. Log3 $name, 5,"[YAAHM] found twodays=special date \"$twomdesc\" in holiday $specialDev";
  1695. }
  1696. #-- device of type calendar
  1697. }elsif( IsDevice($specialDev, "Calendar" )){
  1698. $stoday = strftime('%2d.%2m.%2Y', localtime(time));
  1699. $stom = strftime('%2d.%2m.%2Y', localtime(time+86400));
  1700. $stwom = strftime('%2d.%2m.%2Y', localtime(time+2*86400));
  1701. @tday = split('\.',$stoday);
  1702. @tmor = split('\.',$stom);
  1703. @two = split('\.',$stwom);
  1704. #-- more complicated to check here
  1705. $fline=Calendar_Get($defs{$specialDev},"get","full","mode=alarm|start|upcoming");
  1706. if($fline){
  1707. #chomp($fline);
  1708. @lines = split('\n',$fline);
  1709. foreach $fline (@lines){
  1710. chomp($fline);
  1711. @chunks = split(' ',$fline);
  1712. @sday = split('\.',$chunks[4]);
  1713. #-- today
  1714. my $rets = ($sday[2]-$tday[2])*365+($sday[1]-$tday[1])*31+($sday[0]-$tday[0]);
  1715. if( $rets==0 ){
  1716. $todaydesc = $chunks[7];
  1717. Log3 $name, 5,"[YAAHM] found today=special date \"$todaydesc\" in calendar $specialDev";
  1718. }
  1719. $rets = ($sday[2]-$tmor[2])*365+($sday[1]-$tmor[1])*31+($sday[0]-$tmor[0]);
  1720. if( $rets==0 ){
  1721. $tomdesc = $chunks[7];
  1722. Log3 $name, 5,"[YAAHM] found tomorrow=special date \"$tomdesc\" in calendar $specialDev";
  1723. }
  1724. $rets = ($sday[2]-$two[2])*365+($sday[1]-$two[1])*31+($sday[0]-$two[0]);
  1725. if( $rets==0 ){
  1726. $twomdesc = $chunks[7];
  1727. Log3 $name, 5,"[YAAHM] found twodays=special date \"$twomdesc\" in calendar $specialDev";
  1728. }
  1729. }
  1730. }
  1731. }else{
  1732. Log3 $name, 1,"[YAAHM] unknown special device $specialDev";
  1733. }
  1734. #-- accumulate descriptions
  1735. $todaylong .= $todaydesc.','
  1736. if($todaydesc);
  1737. $todaylong =~ s/,$//;
  1738. $tomlong .= $tomdesc.','
  1739. if($tomdesc);
  1740. $tomlong =~ s/,$//;
  1741. $twodaylong .= $twomdesc.','
  1742. if($twomdesc);
  1743. $twodaylong =~ s/,$//;
  1744. }
  1745. $hash->{DATA}{"DD"}[0]{"special"} = $todaylong;
  1746. $hash->{DATA}{"DD"}[1]{"special"} = $tomlong;
  1747. #-- put into readings
  1748. readingsBeginUpdate($hash);
  1749. readingsBulkUpdateIfChanged( $hash, "todaySpecial",$todaylong );
  1750. readingsBulkUpdateIfChanged( $hash, "tomorrowSpecial",$tomlong);
  1751. readingsEndUpdate($hash,1);
  1752. }
  1753. }
  1754. #########################################################################################
  1755. #
  1756. # YAAHM_today - internal function to switch into daytime
  1757. #
  1758. # Parameter timerHash = internal hash of timer
  1759. # => need to dereference it for getting device hash
  1760. #
  1761. #########################################################################################
  1762. sub YAAHM_today($) {
  1763. my ($timerHash) = @_;
  1764. my $next;
  1765. my $hash = $timerHash->{HASH};
  1766. my $name = $hash->{NAME};
  1767. $next = gettimeofday()+86400;
  1768. YAAHM_RemoveInternalTimer("today",$hash);
  1769. YAAHM_InternalTimer("today",$next, "YAAHM_today", $hash, 0);
  1770. Log 1,"[YAAHM_today] on device ".$hash->{NAME}." called for this day";
  1771. readingsBeginUpdate($hash);
  1772. readingsBulkUpdate($hash,"housephase","daytime");
  1773. readingsBulkUpdate($hash,"tr_housephase",$yaahm_tt->{"daytime"});
  1774. readingsEndUpdate($hash,1);
  1775. return undef;
  1776. }
  1777. #########################################################################################
  1778. #d
  1779. # YAAHM_tonight - internal function to switch into nighttime
  1780. #
  1781. # Parameter timerHash = internal hash of timer
  1782. # => need to dereference it for getting device hash
  1783. #
  1784. #########################################################################################
  1785. sub YAAHM_tonight($) {
  1786. my ($timerHash) = @_;
  1787. my $next;
  1788. my $hash = $timerHash->{HASH};
  1789. my $name = $hash->{NAME};
  1790. $next = gettimeofday()+86400;
  1791. YAAHM_RemoveInternalTimer("tonight",$hash);
  1792. YAAHM_InternalTimer("tonight",$next, "YAAHM_tonight", $hash, 0);
  1793. Log 1,"[YAAHM_tonight] on device ".$hash->{NAME}." called for this day";
  1794. readingsBeginUpdate($hash);
  1795. readingsBulkUpdate($hash,"housephase","nighttime");
  1796. readingsBulkUpdate($hash,"tr_housephase",$yaahm_tt->{"nighttime"});
  1797. readingsEndUpdate($hash,1);
  1798. return undef;
  1799. }
  1800. #########################################################################################
  1801. #
  1802. # YAAHM_updater - internal update function 1 minute after midnight
  1803. #
  1804. # Parameter timerHash = on first call, device hash.
  1805. # = on later calls: internal hash of timer
  1806. # => need to dereference it for getting device hash
  1807. #
  1808. #########################################################################################
  1809. sub YAAHM_updater($) {
  1810. my ($timerHash) = @_;
  1811. my $hash;
  1812. my $next;
  1813. #-- start timer for updates - when device is reloaded
  1814. if( defined($firstcall) && ($firstcall==1) ){
  1815. #-- timerHash is device hash
  1816. $hash = $timerHash;
  1817. my ($sec, $min, $hour, $day, $month, $year, $wday,$yday,$isdst) = localtime(time);
  1818. $next = gettimeofday()+(23-$hour)*3600+(59-$min)*60+(59-$sec)+34;
  1819. $firstcall=0;
  1820. #-- continue timer for updates
  1821. }else{
  1822. #-- timerHash is internal hash
  1823. $hash = $timerHash->{HASH};
  1824. $next = gettimeofday()+86400;
  1825. }
  1826. #-- safeguard if hash is not properly indirected
  1827. if( defined($hash->{HASH}) ){
  1828. #Log 1,"WARNING ! HASH indirection not ok. firstcall=$firstcall";
  1829. $hash = $hash->{HASH};
  1830. }
  1831. YAAHM_RemoveInternalTimer("aftermidnight",$hash);
  1832. YAAHM_InternalTimer("aftermidnight",$next, "YAAHM_updater", $hash, 0);
  1833. Log 1,"[YAAHM_updater] on device ".$hash->{NAME}." called for this day";
  1834. YAAHM_GetDayStatus($hash);
  1835. return undef;
  1836. }
  1837. #########################################################################################
  1838. #
  1839. # YAAHM_InternalTimer - start named internal timer
  1840. #
  1841. # Parameter modifier = name suffix
  1842. # tim = time
  1843. # callback = callback function
  1844. #
  1845. #
  1846. #########################################################################################
  1847. sub YAAHM_InternalTimer($$$$$) {
  1848. my ($modifier, $tim, $callback, $hash, $waitIfInitNotDone) = @_;
  1849. my $mHash;
  1850. if ($modifier eq "") {
  1851. $mHash = $hash;
  1852. } else {
  1853. my $timerName = "$hash->{NAME}_$modifier";
  1854. if (exists ($hash->{TIMER}{$timerName})) {
  1855. $mHash = $hash->{TIMER}{$timerName};
  1856. } else {
  1857. $mHash = { HASH=>$hash, NAME=>"$hash->{NAME}_$modifier", MODIFIER=>$modifier};
  1858. $hash->{TIMER}{$timerName} = $mHash;
  1859. }
  1860. }
  1861. InternalTimer($tim, $callback, $mHash, $waitIfInitNotDone);
  1862. }
  1863. #########################################################################################
  1864. #
  1865. # YAAHM_RemoveInternalTimer - kill named internal timer
  1866. #
  1867. # Parameter
  1868. #
  1869. #########################################################################################
  1870. sub YAAHM_RemoveInternalTimer($$) {
  1871. my ($modifier, $hash) = @_;
  1872. my $timerName = "$hash->{NAME}_$modifier";
  1873. if ($modifier eq "") {
  1874. RemoveInternalTimer($hash);
  1875. } else {
  1876. my $myHash = $hash->{TIMER}{$timerName};
  1877. if (defined($myHash)) {
  1878. delete $hash->{TIMER}{$timerName};
  1879. RemoveInternalTimer($myHash);
  1880. }
  1881. }
  1882. }
  1883. #########################################################################################
  1884. #
  1885. # YAAHM_GetDayStatus
  1886. #
  1887. # Parameter hash = hash of device addressed
  1888. #
  1889. #########################################################################################
  1890. sub YAAHM_GetDayStatus($) {
  1891. my ($hash) = @_;
  1892. my $name = $hash->{NAME};
  1893. #-- readjust language
  1894. my $lang = AttrVal("global","language","EN");
  1895. if( $lang eq "DE"){
  1896. $yaahm_tt = \%yaahm_transtable_DE;
  1897. }else{
  1898. $yaahm_tt = \%yaahm_transtable_EN;
  1899. }
  1900. my ($ret,$line,$fline,$date);
  1901. my (@lines,@chunks,@tday,@eday,@sday,@tmor);
  1902. my ($todaydesc,$todaytype,$tomdesc,$tomtype);
  1903. my $stoday = strftime('%2d.%2m.%2Y', localtime(time));
  1904. my $stom = strftime('%2d.%2m.%2Y', localtime(time+86400));
  1905. #-- workday has lowest priority
  1906. $todaytype = "workday";
  1907. $hash->{DATA}{"DD"}[0]{"date"} = $stoday;
  1908. $hash->{DATA}{"DD"}[0]{"weekday"} = (strftime('%w', localtime(time))+6)%7;
  1909. $hash->{DATA}{"DD"}[0]{"daytype"} = "workday";
  1910. $hash->{DATA}{"DD"}[0]{"desc"} = $yaahm_tt->{"workday"};
  1911. $tomtype = "workday";
  1912. $hash->{DATA}{"DD"}[1]{"date"} = $stom;
  1913. $hash->{DATA}{"DD"}[1]{"weekday"} = (strftime('%w', localtime(time+86400))+6)%7;
  1914. $hash->{DATA}{"DD"}[1]{"daytype"} = "workday";
  1915. $hash->{DATA}{"DD"}[1]{"desc"} = $yaahm_tt->{"workday"};
  1916. #-- vacation = vacdays has higher priority
  1917. my $vacdayDevs = AttrVal( $name, "vacationDevices", "" );
  1918. foreach my $vacdayDev ( split( /,/, $vacdayDevs ) ) {
  1919. #-- device of type holiday
  1920. if( IsDevice( $vacdayDev, "holiday" )){
  1921. $stoday = strftime('%2m-%2d', localtime(time));
  1922. $stom = strftime('%2m-%2d', localtime(time+86400));
  1923. my $tod = holiday_refresh( $vacdayDev, $stoday );
  1924. if ( $tod ne "none" ) {
  1925. $todaydesc = $tod;
  1926. $todaytype = "vacday";
  1927. Log3 $name, 1,"[YAAHM] found today=vacation \"$todaydesc\" in holiday $vacdayDev";
  1928. }
  1929. $tod = holiday_refresh( $vacdayDev, $stom );
  1930. if ( $tod ne "none" ) {
  1931. $tomdesc = $tod;
  1932. $tomtype = "vacday";
  1933. Log3 $name, 1,"[YAAHM] found tomorrow=vacation \"$tomdesc\" in holiday $vacdayDev";
  1934. }
  1935. #-- device of type calendar
  1936. }elsif( IsDevice($vacdayDev, "Calendar" )){
  1937. $stoday = strftime('%2d.%2m.%2Y', localtime(time));
  1938. $stom = strftime('%2d.%2m.%2Y', localtime(time+86400));
  1939. @tday = split('\.',$stoday);
  1940. @tmor = split('\.',$stom);
  1941. #-- more complicated to check here
  1942. $fline=Calendar_Get($defs{$vacdayDev},"get","full","mode=alarm|start|upcoming");
  1943. if($fline){
  1944. #chomp($fline);
  1945. @lines = split('\n',$fline);
  1946. foreach $fline (@lines){
  1947. chomp($fline);
  1948. @chunks = split(' ',$fline);
  1949. @sday = split('\.',$chunks[2]);
  1950. @eday = split('\.',substr($chunks[3],9,10));
  1951. #-- today
  1952. my $rets = ($sday[2]-$tday[2])*365+($sday[1]-$tday[1])*31+($sday[0]-$tday[0]);
  1953. my $rete = ($eday[2]-$tday[2])*365+($eday[1]-$tday[1])*31+($eday[0]-$tday[0]);
  1954. if( ($rete>=0) && ($rets<=0) ){
  1955. $todaydesc = $chunks[5];
  1956. $todaytype = "vacation";
  1957. Log3 $name, 1,"[YAAHM] found today=vacation \"$todaydesc\" in calendar $vacdayDev";
  1958. }
  1959. $rets = ($sday[2]-$tmor[2])*365+($sday[1]-$tmor[1])*31+($sday[0]-$tmor[0]);
  1960. $rete = ($eday[2]-$tmor[2])*365+($eday[1]-$tmor[1])*31+($eday[0]-$tmor[0]);
  1961. if( ($rete>=0) && ($rets<=0) ){
  1962. $tomdesc = $chunks[5];
  1963. $tomtype = "vacation";
  1964. Log3 $name, 1,"[YAAHM] found tomorrow=vacation \"$tomdesc\" in calendar $vacdayDev";
  1965. }
  1966. }
  1967. }
  1968. }else{
  1969. Log3 $name, 1,"[YAAHM] unknown vacation device $vacdayDev";
  1970. }
  1971. }
  1972. #-- put into readings
  1973. if( $todaytype eq "vacation" ){
  1974. $hash->{DATA}{"DD"}[0]{"daytype"} = "vacation";
  1975. $hash->{DATA}{"DD"}[0]{"desc"} = $todaydesc;
  1976. }else{
  1977. }
  1978. if( $tomtype eq "vacation" ){
  1979. $hash->{DATA}{"DD"}[1]{"daytype"} = "vacation";
  1980. $hash->{DATA}{"DD"}[1]{"desc"} = $tomdesc;
  1981. }else{
  1982. }
  1983. #-- weekend has higher priority
  1984. if( strftime('%u', localtime(time)) > 5){
  1985. $todaytype = "weekend";
  1986. if( $hash->{DATA}{"DD"}[0]{"daytype"} ne "workday" ){
  1987. $hash->{DATA}{"DD"}[0]{"desc"} = $yaahm_tt->{"weekend"}.", ".$hash->{DATA}{"DD"}[0]{"desc"};
  1988. }else{
  1989. $hash->{DATA}{"DD"}[0]{"desc"} = $yaahm_tt->{"weekend"};
  1990. }
  1991. $hash->{DATA}{"DD"}[0]{"daytype"} = "weekend";
  1992. }
  1993. if( strftime('%u', localtime(time+86400)) > 5){
  1994. $tomtype = "weekend";
  1995. if( $hash->{DATA}{"DD"}[1]{"daytype"} ne "workday" ){
  1996. $hash->{DATA}{"DD"}[1]{"desc"} = $yaahm_tt->{"weekend"}.", ".$hash->{DATA}{"DD"}[1]{"desc"};
  1997. }else{
  1998. $hash->{DATA}{"DD"}[1]{"desc"} = $yaahm_tt->{"weekend"};
  1999. }
  2000. $hash->{DATA}{"DD"}[1]{"daytype"} = "weekend";
  2001. }
  2002. #-- holidays have the highest priority
  2003. my $holidayDevs = AttrVal( $name, "holidayDevices", "" );
  2004. foreach my $holidayDev ( split( /,/, $holidayDevs ) ) {
  2005. #-- device of type holiday
  2006. if( IsDevice( $holidayDev, "holiday" )){
  2007. $stoday = strftime('%2m-%2d', localtime(time));
  2008. $stom = strftime('%2m-%2d', localtime(time+86400));
  2009. my $tod = holiday_refresh( $holidayDev, $stoday );
  2010. if ( $tod ne "none" ) {
  2011. $todaydesc = $tod;
  2012. $todaytype = "holiday";
  2013. Log3 $name, 1,"[YAAHM] found today=holiday \"$todaydesc\" in holiday $holidayDev";
  2014. }
  2015. $tod = holiday_refresh( $holidayDev, $stom );
  2016. if ( $tod ne "none" ) {
  2017. $tomdesc = $tod;
  2018. $tomtype = "holiday";
  2019. Log3 $name, 1,"[YAAHM] found tomorrow=holiday \"$tomdesc\" in holiday $holidayDev";
  2020. }
  2021. #-- device of type calendar
  2022. }elsif( IsDevice($holidayDev, "Calendar" )){
  2023. $stoday = strftime('%2d.%2m.%2Y', localtime(time));
  2024. $stom = strftime('%2d.%2m.%2Y', localtime(time+86400));
  2025. $line=Calendar_Get($defs{$holidayDev},"get","text","mode=alarm|start|upcoming");
  2026. if($line){
  2027. chomp($line);
  2028. @lines = split('\n',$line);
  2029. foreach $line (@lines){
  2030. chomp($line);
  2031. $date = substr($line,0,8);
  2032. if( $date eq $stoday ){
  2033. $todaydesc = substr($line,15);
  2034. $todaytype = "holiday";
  2035. Log3 $name, 1,"[YAAHM] found today=holiday \"$todaydesc\" in calendar $holidayDev";
  2036. }
  2037. if( $date eq $stom ){
  2038. $tomdesc = substr($line,15);
  2039. $tomtype = "holiday";
  2040. Log3 $name, 1,"[YAAHM] found tomorrow=holiday \"$tomdesc\" in calendar $holidayDev";
  2041. }
  2042. }
  2043. }
  2044. }else{
  2045. Log3 $name, 1,"[YAAHM] unknown holiday device $holidayDev";
  2046. }
  2047. }
  2048. #-- put into store
  2049. if( $todaytype eq "holiday" ){
  2050. if( $hash->{DATA}{"DD"}[0]{"daytype"} ne "workday" ){
  2051. $hash->{DATA}{"DD"}[0]{"desc"} = $todaydesc.", ".$hash->{DATA}{"DD"}[0]{"desc"};
  2052. }else{
  2053. $hash->{DATA}{"DD"}[0]{"desc"} = $todaydesc;
  2054. }
  2055. $hash->{DATA}{"DD"}[0]{"daytype"} = "holiday";
  2056. }
  2057. if( $tomtype eq "holiday" ){
  2058. if( $hash->{DATA}{"DD"}[1]{"daytype"} ne "workday" ){
  2059. $hash->{DATA}{"DD"}[1]{"desc"} = $tomdesc.", ".$hash->{DATA}{"DD"}[1]{"desc"};
  2060. }else{
  2061. $hash->{DATA}{"DD"}[1]{"desc"} = $tomdesc;
  2062. }
  2063. $hash->{DATA}{"DD"}[1]{"daytype"} = "holiday";
  2064. }
  2065. #-- sunrise, sunset and the offsets
  2066. YAAHM_sun($hash);
  2067. YAAHM_sunoffsets($hash);
  2068. readingsBeginUpdate($hash);
  2069. #-- and do not forget to put them into readings, because these are read by the timer
  2070. foreach my $key (sort YAAHM_dsort keys %defaultdailytable){
  2071. my $f1 = defined($defaultdailytable{$key}[0]);
  2072. my $f2 = defined($defaultdailytable{$key}[1]);
  2073. #-- entries in the default table with no entry are single-timers
  2074. if( !$f1 and !$f2 ){
  2075. readingsBulkUpdate( $hash, "s_".$key, $hash->{DATA}{"DT"}{$key}[0] );
  2076. #-- entries in the default table with only first time are single-timers
  2077. }elsif( $f1 and !$f2 ){
  2078. readingsBulkUpdate( $hash, "s_".$key, $hash->{DATA}{"DT"}{$key}[0] );
  2079. #-- entries in the default table with only second time are single-timer offsets
  2080. }elsif( !$f1 and $f2 ){
  2081. readingsBulkUpdate( $hash, "s_".$key, $hash->{DATA}{"DT"}{$key}[0] );
  2082. #-- entries in the default table with first and second time are two-timer periods
  2083. }elsif( $f1 and $f2 ){
  2084. readingsBulkUpdate( $hash, "s_".$key, $hash->{DATA}{"DT"}{$key}[0] );
  2085. readingsBulkUpdate( $hash, "t_".$key, $hash->{DATA}{"DT"}{$key}[1] );
  2086. #-- something wrong
  2087. }else{
  2088. my $msg = "Readings update failed, something wrong with entry ".$key;
  2089. Log 1,"[YAAHM_GetDayStatus] ".$msg;
  2090. return $msg;
  2091. }
  2092. }
  2093. readingsBulkUpdateIfChanged( $hash, "todayType",$todaytype );
  2094. readingsBulkUpdateIfChanged( $hash, "tr_todayType",$yaahm_tt->{$hash->{DATA}{"DD"}[0]{"daytype"}} );
  2095. if( $todaytype eq "workday"){
  2096. readingsBulkUpdateIfChanged( $hash, "todayDesc","--" )
  2097. }elsif( $todaytype eq "vacation"){
  2098. readingsBulkUpdateIfChanged( $hash, "todayDesc",$hash->{DATA}{"DD"}[0]{"desc"} )
  2099. }elsif( $todaytype eq "weekend"){
  2100. readingsBulkUpdateIfChanged( $hash, "todayDesc","--" )
  2101. }else{
  2102. readingsBulkUpdateIfChanged( $hash, "todayDesc",$hash->{DATA}{"DD"}[0]{"desc"} )
  2103. }
  2104. readingsBulkUpdateIfChanged( $hash, "tomorrowType",$tomtype );
  2105. readingsBulkUpdateIfChanged( $hash, "tr_tomorrowType",$yaahm_tt->{$hash->{DATA}{"DD"}[1]{"daytype"}} );
  2106. if( $tomtype eq "workday"){
  2107. readingsBulkUpdateIfChanged( $hash, "tomorrowDesc","--" )
  2108. }elsif( $tomtype eq "vacation"){
  2109. readingsBulkUpdateIfChanged( $hash, "tomorrowDesc",$hash->{DATA}{"DD"}[1]{"desc"} )
  2110. }elsif( $tomtype eq "weekend"){
  2111. readingsBulkUpdateIfChanged( $hash, "tomorrowDesc","--" )
  2112. }else{
  2113. readingsBulkUpdateIfChanged( $hash, "tomorrowDesc",$hash->{DATA}{"DD"}[1]{"desc"} )
  2114. }
  2115. YAAHM_setWeeklyTime($hash);
  2116. readingsEndUpdate($hash,1);
  2117. return undef;
  2118. }
  2119. #########################################################################################
  2120. #
  2121. # YAAHM_sun - obtain time offsets for midnight etc. sunrise and sunset
  2122. #
  2123. # Parameter hash = hash of device addressed
  2124. #
  2125. #########################################################################################
  2126. sub YAAHM_sun($) {
  2127. my ($hash) = @_;
  2128. my $name = $hash->{NAME};
  2129. #-- sunrise and sunset today and tomorrow
  2130. my ($sttoday,$sttom);
  2131. my $count = 0;
  2132. my $strise0 = "";
  2133. my $strise1 = "";
  2134. my ($msg,$stset0,$stseas0,$stset1,$stseas1);
  2135. $sttoday = strftime('%4Y-%2m-%2d', localtime(time));
  2136. #-- since the Astro module sometimes gives us strange results, we need to do this more than once
  2137. while( $strise0 !~ /^\d\d:\d\d:\d\d/ && $count < 5){
  2138. $strise0 = Astro_Get($hash,"dummy","text", "SunRise",$sttoday).":00";
  2139. $count++;
  2140. select(undef,undef,undef,0.01);
  2141. }
  2142. if( $count == 5 ){
  2143. $msg = "Error, no proper sunrise today return from Astro module in 5 attempts";
  2144. Log3 $name,1,"[YAAHM_sun] ".$msg;
  2145. $strise0 = "06:00:00";
  2146. }
  2147. my ($hour,$min) = split(":",$strise0);
  2148. $hash->{DATA}{"DD"}[0]{"sunrise"} = sprintf("%02d:%02d",$hour,$min);
  2149. $stset0 = Astro_Get($hash,"dummy","text", "SunSet",$sttoday).":00";
  2150. ($hour,$min) = split(":",$stset0);
  2151. $hash->{DATA}{"DD"}[0]{"sunset"} = sprintf("%02d:%02d",$hour,$min);
  2152. $stseas0 = Astro_Get($hash,"dummy","text", "ObsSeasonN",$sttoday);
  2153. $hash->{DATA}{"DD"}[0]{"season"} = $seasons[$stseas0];
  2154. $sttom = strftime('%4Y-%2m-%2d', localtime(time+86400));
  2155. $count = 0;
  2156. $msg = "";
  2157. #-- since the Astro module sometimes gives us strange results, we need to do this more than once
  2158. while( $strise1 !~ /^\d\d:\d\d:\d\d/ && $count < 5){
  2159. $strise1 = Astro_Get($hash,"dummy","text", "SunRise",$sttom).":00";
  2160. $count++;
  2161. select(undef,undef,undef,0.01);
  2162. }
  2163. if( $count == 5 ){
  2164. $msg = "Error, no proper sunrise tomorrow return from Astro module in 5 attempts";
  2165. Log3 $name,1,"[YAAHM_sun] ".$msg;
  2166. $strise1 = "06:00:00";
  2167. }
  2168. ($hour,$min) = split(":",$strise1);
  2169. $hash->{DATA}{"DD"}[1]{"sunrise"} = sprintf("%02d:%02d",$hour,$min);
  2170. $stset1 = Astro_Get($hash,"dummy","text", "SunSet",$sttom).":00";
  2171. ($hour,$min) = split(":",$stset1);
  2172. $hash->{DATA}{"DD"}[1]{"sunset"} = sprintf("%02d:%02d",$hour,$min);
  2173. $stseas1 = Astro_Get($hash,"dummy","text", "ObsSeasonN",$sttom);
  2174. $hash->{DATA}{"DD"}[1]{"season"} = $seasons[$stseas1];
  2175. }
  2176. #########################################################################################
  2177. #
  2178. # YAAHM_sunoffsets - obtain time offsets for midnight etc. sunrise and sunset
  2179. #
  2180. # Parameter hash = hash of device addressed
  2181. #
  2182. #########################################################################################
  2183. sub YAAHM_sunoffsets($) {
  2184. my ($hash) = @_;
  2185. #-- sunrise
  2186. my $st = $hash->{DATA}{"DD"}[0]{"sunrise"};
  2187. my ($hour,$min) = split(":",$st);
  2188. $hash->{DATA}{"DT"}{"sunrise"}[0] = sprintf("%02d:%02d",$hour,$min);
  2189. #-- before sunrise
  2190. my ($ofh,$ofm);
  2191. my $of = $hash->{DATA}{"DT"}{"beforesunrise"}[1];
  2192. if( $of !~ /\d\d:\d\d/){
  2193. Log 1,"[YAAHM] Offset before sunrise not in format hh:mm, using 00:00";
  2194. $ofh = 0;
  2195. $ofm = 0;
  2196. }else{
  2197. ($ofh,$ofm) = split(":",$of);
  2198. }
  2199. $ofm = $min-$ofm;
  2200. $ofh = $hour-$ofh;
  2201. if( $ofm < 0 ){
  2202. $ofh--;
  2203. $ofm +=60;
  2204. }
  2205. $hash->{DATA}{"DT"}{"beforesunrise"}[0] = sprintf("%02d:%02d",$ofh,$ofm);
  2206. #-- after sunrise
  2207. $of = $hash->{DATA}{"DT"}{"aftersunrise"}[1];
  2208. if( $of !~ /\d\d:\d\d/){
  2209. Log 1,"[YAAHM] Offset after sunrise not in format hh:mm, using 00:00";
  2210. $ofh = 0;
  2211. $ofm = 0;
  2212. }else{
  2213. ($ofh,$ofm) = split(":",$of);
  2214. }
  2215. $ofm = $min+$ofm;
  2216. $ofh = $hour+$ofh;
  2217. if( $ofm > 59 ){
  2218. $ofh++;
  2219. $ofm -=60;
  2220. }
  2221. $hash->{DATA}{"DT"}{"aftersunrise"}[0] = sprintf("%02d:%02d",$ofh,$ofm);
  2222. #-- sunset
  2223. $st = $hash->{DATA}{"DD"}[0]{"sunset"};
  2224. ($hour,$min) = split(":",$st);
  2225. $hash->{DATA}{"DT"}{"sunset"}[0] = sprintf("%02d:%02d",$hour,$min);
  2226. #-- before sunset
  2227. $of = $hash->{DATA}{"DT"}{"beforesunset"}[1];
  2228. if( $of !~ /\d\d:\d\d/){
  2229. Log 1,"[YAAHM] Offset before sunset not in format hh:mm, using 00:00";
  2230. $ofh = 0;
  2231. $ofm = 0;
  2232. }else{
  2233. ($ofh,$ofm) = split(":",$of);
  2234. }
  2235. $ofm = $min-$ofm;
  2236. $ofh = $hour-$ofh;
  2237. if( $ofm < 0 ){
  2238. $ofh--;
  2239. $ofm +=60;
  2240. }
  2241. $hash->{DATA}{"DT"}{"beforesunset"}[0] = sprintf("%02d:%02d",$ofh,$ofm);
  2242. #-- after sunset
  2243. $of = $hash->{DATA}{"DT"}{"aftersunset"}[1];
  2244. if( $of !~ /\d\d:\d\d/){
  2245. Log 1,"[YAAHM] Offset after sunset not in format hh:mm, using 00:00";
  2246. $ofh = 0;
  2247. $ofm = 0;
  2248. }else{
  2249. ($ofh,$ofm) = split(":",$of);
  2250. }
  2251. $ofm = $min+$ofm;
  2252. $ofh = $hour+$ofh;
  2253. if( $ofm > 59 ){
  2254. $ofh++;
  2255. $ofm -=60;
  2256. }
  2257. $hash->{DATA}{"DT"}{"aftersunset"}[0] = sprintf("%02d:%02d",$ofh,$ofm);
  2258. #-- before midnight
  2259. $hour = 24;
  2260. $min = 0;
  2261. $of = $hash->{DATA}{"DT"}{"beforemidnight"}[1];
  2262. if( $of !~ /\d\d:\d\d/){
  2263. Log 1,"[YAAHM] Offset before midnight not in format hh:mm, using 00:05";
  2264. $ofh = 0;
  2265. $ofm = 5;
  2266. }else{
  2267. ($ofh,$ofm) = split(":",$of);
  2268. }
  2269. $ofm = $min-$ofm;
  2270. $ofh = $hour-$ofh;
  2271. if( $ofm < 0 ){
  2272. $ofh--;
  2273. $ofm +=60;
  2274. }
  2275. $hash->{DATA}{"DT"}{"beforemidnight"}[0] = sprintf("%02d:%02d",$ofh,$ofm);
  2276. #-- after midnight
  2277. $hour = 0;
  2278. $min = 0;
  2279. $of = $hash->{DATA}{"DT"}{"aftermidnight"}[1];
  2280. if( $of !~ /\d\d:\d\d/){
  2281. Log 1,"[YAAHM] Offset after midnight not in format hh:mm, using 00:05";
  2282. $ofh = 0;
  2283. $ofm = 5;
  2284. }else{
  2285. ($ofh,$ofm) = split(":",$of);
  2286. }
  2287. $ofm = $min+$ofm;
  2288. $ofh = $hour+$ofh;
  2289. if( $min > 59 ){
  2290. $ofh++;
  2291. $ofm -=60;
  2292. }
  2293. $hash->{DATA}{"DT"}{"aftermidnight"}[0] = sprintf("%02d:%02d",$ofh,$ofm);
  2294. }
  2295. #########################################################################################
  2296. #
  2297. # YAAHM_timewidget - returns SVG code for inclusion into any room page
  2298. #
  2299. # Parameter name = name of the YAAHM definition
  2300. #
  2301. #########################################################################################
  2302. sub YAAHM_timewidget($){
  2303. my ($arg) = @_;
  2304. my $name = $FW_webArgs{name};
  2305. $name =~ s/'//g;
  2306. my @size=split('x',($FW_webArgs{size} ? $FW_webArgs{size} : '400x400'));
  2307. $FW_RETTYPE = "image/svg+xml";
  2308. $FW_RET="";
  2309. FW_pO '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" width="'.$size[0].'px" height="'.$size[1].'px">';
  2310. my $hash = $defs{$name};
  2311. # Midnight = 0 200
  2312. # Noon = 0 -200
  2313. # hh:mm => a = (hh*60 + mm)/1140
  2314. my $radius = 250;
  2315. my ($sec, $min, $hour, $day, $month, $year, $wday,$yday,$isdst) = localtime(time);
  2316. my $t_now = sprintf("%02d:%02d",$hour,$min);
  2317. my $a_now = (60*$hour + $min)/1440 * 2 * pi;
  2318. my $x_now = -int(sin($a_now)*$radius*100)/100;
  2319. my $y_now = int(cos($a_now)*$radius*100)/100;
  2320. my $t_sunrise = defined($hash->{DATA}{"DD"}[0]{"sunrise"}) ? $hash->{DATA}{"DD"}[0]{"sunrise"} : "06:00";
  2321. $t_sunrise =~ s/^0//;
  2322. ($hour,$min) = split(":",$t_sunrise);
  2323. my $a_sunrise = (60*$hour + $min)/1440 * 2 * pi;
  2324. my $x_sunrise = -int(sin($a_sunrise)*$radius*100)/100;
  2325. my $y_sunrise = int(cos($a_sunrise)*$radius*100)/100;
  2326. my $t_morning = defined($hash->{DATA}{"DT"}{"morning"}[0]) ? $hash->{DATA}{"DT"}{"morning"}[0] : "08:00";
  2327. $t_morning =~ s/^0//;
  2328. ($hour,$min) = split(":",$t_morning);
  2329. my $a_morning = (60*$hour + $min)/1440 * 2 * pi;
  2330. my $x_morning = -int(sin($a_morning)*$radius*100)/100;
  2331. my $y_morning = int(cos($a_morning)*$radius*100)/100;
  2332. my $t_noon = defined($hash->{DATA}{"DT"}{"noon"}[0]) ? $hash->{DATA}{"DT"}{"noon"}[0] : "12:00";
  2333. $t_noon =~ s/^0//;
  2334. ($hour,$min) = split(":",$t_noon);
  2335. my $a_noon = (60*$hour + $min)/1440 * 2 * pi;
  2336. my $x_noon = -int(sin($a_noon)*$radius*100)/100;
  2337. my $y_noon = int(cos($a_noon)*$radius*100)/100;
  2338. my $t_afternoon = defined($hash->{DATA}{"DT"}{"afternoon"}[0]) ? $hash->{DATA}{"DT"}{"afternoon"}[0] : "14:00";
  2339. $t_afternoon =~ s/^0//;
  2340. ($hour,$min) = split(":",$t_afternoon);
  2341. my $a_afternoon = (60*$hour + $min)/1440 * 2 * pi;
  2342. my $x_afternoon = -int(sin($a_afternoon)*$radius*100)/100;
  2343. my $y_afternoon = int(cos($a_afternoon)*$radius*100)/100;
  2344. my $t_sunset = defined($hash->{DATA}{"DD"}[0]{"sunset"}) ? $hash->{DATA}{"DD"}[0]{"sunset"} : "18:00";
  2345. $t_sunset =~ s/^0//;
  2346. ($hour,$min) = split(":",$t_sunset);
  2347. my $a_sunset = (60*$hour + $min)/1440 * 2 * pi;
  2348. my $x_sunset = -int(sin($a_sunset)*$radius*100)/100;
  2349. my $y_sunset = int(cos($a_sunset)*$radius*100)/100;
  2350. my $t_evening = defined($hash->{DATA}{"DT"}{"evening"}[0]) ? $hash->{DATA}{"DT"}{"evening"}[0] : "19:00";
  2351. $t_evening =~ s/^0//;
  2352. ($hour,$min) = split(":",$t_evening);
  2353. my $a_evening = (60*$hour + $min)/1440 * 2 * pi;
  2354. my $x_evening = -int(sin($a_evening)*$radius*100)/100;
  2355. my $y_evening = int(cos($a_evening)*$radius*100)/100;
  2356. my $t_night = defined($hash->{DATA}{"DT"}{"night"}[0]) ? $hash->{DATA}{"DT"}{"night"}[0] : "22:00";
  2357. $t_night =~ s/^0//;
  2358. ($hour,$min) = split(":",$t_night);
  2359. my $a_night = (60*$hour + $min)/1440 * 2 * pi;
  2360. my $x_night = -int(sin($a_night)*$radius*100)/100;
  2361. my $y_night = int(cos($a_night)*$radius*100)/100;
  2362. my $t_midnight = "0:00";
  2363. $t_midnight =~ s/^0//;
  2364. ($hour,$min) = split(":",$t_midnight);
  2365. my $a_midnight = 0.0;
  2366. my $x_midnight = 0.0;
  2367. my $y_midnight = $radius;
  2368. FW_pO '<defs>'.
  2369. sprintf('<linearGradient id="grad1" x1="0%%" y1="0%%" x2="%d%%" y2="%d%%">',int(-$x_noon/$radius*100),int(-$y_noon/$radius*100)).
  2370. '<stop offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1" />'.
  2371. '<stop offset="100%" style="stop-color:rgb(255,100,0);stop-opacity:1" />'.
  2372. '</linearGradient>'.
  2373. sprintf('<linearGradient id="grad2" x1="%d%%" y1="%d%%" x2="0%%" y2="0%%">',int(-$x_noon/$radius*100),int(-$y_noon/$radius*100)).
  2374. '<stop offset="0%" style="stop-color:rgb(70,70,100);stop-opacity:1" />'.
  2375. '<stop offset="100%" style="stop-color:rgb(255,150,0);stop-opacity:1" />'.
  2376. '</linearGradient>'.
  2377. sprintf('<linearGradient id="grad3" x1="%d%%" y1="%d%%" x2="0%%" y2="0%%">',int(-$x_noon/$radius*100),int(-$y_noon/$radius*100)).
  2378. '<stop offset="0%" style="stop-color:rgb(80,80,80);stop-opacity:1" />'.
  2379. '<stop offset="100%" style="stop-color:rgb(120,120,100);stop-opacity:1" />'.
  2380. '</linearGradient>'.
  2381. '</defs>';
  2382. FW_pO '<g id="Ebene_1" transform="translate(400,400)">';
  2383. #-- daytime arc
  2384. FW_pO '<path d="M 0 0 '.($x_morning*1.1).' '.($y_morning*1.1). ' A '.($radius*1.1).' '.($radius*1.1).' 0 1 1 '.($x_night*1.1).' '.($y_night*1.1).' Z" fill="none" stroke="rgb(0,255,200)" stroke-width="15" />';
  2385. #-- sunset to sunrise sector. We need a workaround here for the broken SVG engine of Firefox, splitting this in two arcs
  2386. FW_pO '<path d="M 0 0 '.$x_sunset. ' '.$y_sunset. ' A '.$radius.' '.$radius.' 0 0 1 '.$x_midnight.' '.$y_midnight.' Z" fill="rgb(70,70,100)"/>';
  2387. FW_pO '<path d="M 0 0 '.$x_midnight.' '.$y_midnight. ' A '.$radius.' '.$radius.' 0 0 1 '.$x_sunrise.' '. $y_sunrise. ' Z" fill="rgb(70,70,100)"/>';
  2388. #-- sunrise to morning sector
  2389. FW_pO '<path d="M 0 0 '.$x_sunrise.' '.$y_sunrise.' A '.$radius.' '.$radius.' 0 0 1 '.$x_morning.' '.$y_morning.' Z" fill="url(#grad2)"/>';
  2390. #-- morning to evening sector
  2391. FW_pO '<path d="M 0 0 '.$x_morning.' '.$y_morning.' A '.$radius.' '.$radius.' 0 0 1 '.$x_evening.' '.$y_evening.' Z" fill="url(#grad1)"/>';
  2392. #-- evening to sunset sector
  2393. FW_pO '<path d="M 0 0 '.$x_evening.' '.$y_evening.' A '.$radius.' '.$radius.' 0 0 1 '.$x_sunset.' '.$y_sunset.' Z" fill="url(#grad2)"/>';
  2394. #-- midnight line
  2395. FW_pO '<line x1="0" y1="0" x2="0" y2="'.($radius*1.2).'" style="stroke:rgb(75, 75, 75);stroke-width:2" />';
  2396. FW_pO '<text x="-30" y="'.($radius*1.25).'" fill="rgb(75, 75, 75)" style="font-family:Helvetica;font-size:36px;font-weight:bold">0:00</text>';
  2397. #--sunrise line
  2398. FW_pO '<line x1="0" y1="0" x2="'.($x_sunrise*1.2).'" y2="'.($y_sunrise*1.2).'" style="stroke:rgb(75, 75, 75);stroke-width:2" />';
  2399. FW_pO '<text x="'.($x_sunrise*1.25-30).'" y="'.($y_sunrise*1.25).'" fill="rgb(75, 75, 75)" style="font-family:Helvetica;font-size:36px;font-weight:bold">'.$t_sunrise.'</text>';
  2400. #--morning line
  2401. FW_pO '<line x1="0" y1="0" x2="'.($x_morning*1.2).'" y2="'.($y_morning*1.2).'" style="stroke:rgb(75, 75, 75);stroke-width:2" />';
  2402. FW_pO '<text x="'.($x_morning*1.25-30).'" y="'.($y_morning*1.25).'" fill="rgb(75, 75, 75)" style="font-family:Helvetica;font-size:36px;font-weight:bold">'.$t_morning.'</text>';
  2403. #--noon line
  2404. FW_pO '<line x1="0" y1="0" x2="'.($x_noon*1.2) .'" y2="'.($y_noon*1.2) .'" style="stroke:rgb(75, 75, 75);stroke-width:2" />';
  2405. FW_pO '<text x="'.($x_noon*1.25).'" y="'.($y_noon*1.25).'" fill="rgb(75, 75, 75)" style="font-family:Helvetica;font-size:36px;font-weight:bold">'.$t_noon.'</text>';
  2406. #--afternoon line
  2407. FW_pO '<line x1="0" y1="0" x2="'.($x_afternoon*1.2) .'" y2="'.($y_afternoon*1.2) .'" style="stroke:rgb(75, 75, 75);stroke-width:2" />';
  2408. FW_pO '<text x="'.($x_afternoon*1.25).'" y="'.($y_afternoon*1.25).'" fill="rgb(75, 75, 75)" style="font-family:Helvetica;font-size:36px;font-weight:bold">'.$t_afternoon.'</text>';
  2409. #--sunset line
  2410. FW_pO '<line x1="0" y1="0" x2="'.($x_sunset*1.2) .'" y2="'.($y_sunset*1.2) .'" style="stroke:rgb(75, 75, 75);stroke-width:2" />';
  2411. FW_pO '<text x="'.($x_sunset*1.25).'" y="'.($y_sunset*1.25).'" fill="rgb(75, 75, 75)" style="font-family:Helvetica;font-size:36px;font-weight:bold">'.$t_sunset.'</text>';
  2412. #--evening line
  2413. FW_pO '<line x1="0" y1="0" x2="'.($x_evening*1.2) .'" y2="'.($y_evening*1.2) .'" style="stroke:rgb(75, 75, 75);stroke-width:2" />';
  2414. FW_pO '<text x="'.($x_evening*1.25).'" y="'.($y_evening*1.25).'" fill="rgb(75, 75, 75)" style="font-family:Helvetica;font-size:36px;font-weight:bold">'.$t_evening.'</text>';
  2415. #--night line
  2416. FW_pO '<line x1="0" y1="0" x2="'.($x_night*1.2) .'" y2="'.($y_night*1.2) .'" style="stroke:rgb(75, 75, 75);stroke-width:2" />';
  2417. FW_pO '<text x="'.($x_night*1.25).'" y="'.($y_night*1.25).'" fill="rgb(75, 75, 75)" style="font-family:Helvetica;font-size:36px;font-weight:bold">'.$t_night.'</text>';
  2418. #--now line
  2419. FW_pO '<line x1="0" y1="0" x2="'.($x_now*1.2) .'" y2="'.($y_now*1.2) .'" style="stroke:rgb(255,0,0);stroke-width:4" />';
  2420. FW_pO '<text x="'.($x_now*1.25).'" y="'.($y_now*1.25).'" fill="rgb(255,0,0)" style="font-family:Helvetica;font-size:36px;font-weight:bold">'.$t_now.'</text>';
  2421. FW_pO '</g>';
  2422. FW_pO '</svg>';
  2423. return ($FW_RETTYPE, $FW_RET);
  2424. }
  2425. #########################################################################################
  2426. #
  2427. # YAAHM_toptable - returns incomplete HTML code for inclusion into any room page
  2428. # (action and overview fields)
  2429. #
  2430. # Parameter name = name of the YAAHM definition
  2431. #
  2432. #########################################################################################
  2433. sub YAAHM_toptable($){
  2434. my ($name) = @_;
  2435. my $hash = $defs{$name};
  2436. if( !defined($yaahm_tt) ){
  2437. #-- readjust language
  2438. my $lang = AttrVal("global","language","EN");
  2439. if( $lang eq "DE"){
  2440. $yaahm_tt = \%yaahm_transtable_DE;
  2441. }else{
  2442. $yaahm_tt = \%yaahm_transtable_EN;
  2443. }
  2444. }
  2445. #-- something's rotten in the state of denmark
  2446. my $st = $hash->{DATA}{"DD"}[0]{"sunrise"};
  2447. my $ts;
  2448. my ($styl,$stym,$styr);
  2449. my $ret = "";
  2450. YAAHM_GetDayStatus($hash);
  2451. #--
  2452. my $lockstate = ($hash->{READINGS}{lockstate}{VAL}) ? $hash->{READINGS}{lockstate}{VAL} : "unlocked";
  2453. my $showhelper = ($lockstate eq "unlocked") ? 1 : 0;
  2454. %dailytable = %{$hash->{DATA}{"DT"}};
  2455. my $dailyno = scalar keys %dailytable;
  2456. my $weeklyno = int( @{$hash->{DATA}{"WT"}} );
  2457. #--
  2458. $ret .= "<script type=\"text/javascript\" src=\"$FW_ME/pgm2/yaahm.js\"></script><script type=\"text/javascript\">\n";
  2459. $ret .= "var dailyno = ".$dailyno.";\n";
  2460. $ret .= "var dailykeys = [\"".join("\",\"",(sort YAAHM_dsort keys %dailytable))."\"];\n";
  2461. $ret .= "var weeklyno = ".$weeklyno.";\n";
  2462. $ret .= "var weeklykeys = [\"".join("\",\"",@weeklytable)."\"];\n"; # Day names !!!
  2463. $ret .= "var weeklynames = [";
  2464. for( my $i=0;$i<$weeklyno;$i++){
  2465. $ret .= ","
  2466. if( $i!=0 );
  2467. $ret .= "\"".$hash->{DATA}{"WT"}[$i]{"name"}."\"";
  2468. }
  2469. $ret .= "];\n";
  2470. $ret .= "</script>\n";
  2471. $ret .= "<table class=\"roomoverview\">\n";
  2472. $ret .= "<tr><td colspan=\"3\"><div class=\"devType\" style=\"font-weight:bold\">".$yaahm_tt->{"action"}."</div></td></tr>\n";
  2473. ### action ################################################################################################
  2474. #-- determine columns
  2475. my $cols = max(int(@modes),int(@states),$weeklyno);
  2476. $ret .= "<tr><td colspan=\"3\" style=\"align:left\"><table class=\"readings\" style=\"border:1px solid gray;border-radius:10px>".
  2477. "<tr class=\"odd\"><td width=\"100px\" class=\"dname\" style=\"padding:5px;\">".$yaahm_tt->{"mode"}."</td>".
  2478. "<td width=\"120px\"><div class=\"dval\" informId=\"$name-tr_housemode\">".ReadingsVal($name,"tr_housemode",undef)."</div></td><td></td>";
  2479. for( my $i=0; $i<$cols; $i++){
  2480. if( $i < int(@modes)){
  2481. $ret .= "<td width=\"120px\"><input type=\"button\" id=\"b_".$modes[$i]."\" value=\"".$yaahm_tt->{$modes[$i]}.
  2482. "\" style=\"height:20px; width:120px;\" onclick=\"javascript:yaahm_mode('$name','".$modes[$i]."')\"/></td>";
  2483. }else{
  2484. $ret .= "<td width=\"120px\"></td>";
  2485. }
  2486. }
  2487. $ret .= "</tr>";
  2488. $ret .= "<tr class=\"even\"><td class=\"dname\" style=\"padding:5px;\">".$yaahm_tt->{"state"}."</td>".
  2489. "<td><div informId=\"$name-tr_housestate\">".ReadingsVal($name,"tr_housestate",undef).
  2490. "</div></td><td style=\"width:20px\"><div informId=\"$name-sym_housestate\" style=\"align:center\">".ReadingsVal($name,"sym_housestate",undef)."</div></td>";
  2491. for( my $i=0; $i<$cols; $i++){
  2492. if( $i < int(@states)){
  2493. $ret .= "<td width=\"120px\"><input type=\"button\" id=\"b_".$states[$i]."\" value=\"".$yaahm_tt->{$states[$i]}.
  2494. "\" style=\"height:20px; width:120px;\" onclick=\"javascript:yaahm_state('$name','".$states[$i]."')\"/></td>";
  2495. }else{
  2496. $ret .= "<td width=\"120px\"></td>";
  2497. }
  2498. }
  2499. #style=\"height:20px;border-bottom: 10px solid #333333;background-image: linear-gradient(#e5e5e5,#ababab);\"
  2500. #$ret .= "</tr><tr><td colspan=\"8\" class=\"devType\" style=\"height:5px;border-top: 1px solid #ababab;border-bottom: 1px solid #ababab;\"></td></tr>";
  2501. $ret .= "</table><br/><table class=\"readings\">";
  2502. #-- repeat manual next for every weekly table
  2503. my $nval = "";
  2504. my $wupn;
  2505. $ret .= "<tr class=\"odd\"><td class=\"col1\" style=\"padding:5px;border-left:1px solid gray;border-top:1px solid gray;border-bottom:1px solid gray;border-bottom-left-radius:10px;border-top-left-radius:10px\">".$yaahm_tt->{"manual"}."</td>";
  2506. for (my $i=0;$i<$weeklyno;$i++){
  2507. if($i<$weeklyno-1){
  2508. $styl= "border-bottom:1px solid gray;border-top:1px solid gray";
  2509. }else{
  2510. $styl= "border-bottom:1px solid gray;border-top:1px solid gray;border-right:1px solid gray;border-bottom-right-radius:10px;border-top-right-radius:10px";
  2511. }
  2512. $wupn = $hash->{DATA}{"WT"}[$i]{"name"};
  2513. $nval = ( defined($hash->{DATA}{"WT"}[$i]{"next"}) ) ? $hash->{DATA}{"WT"}[$i]{"next"} : "";
  2514. $ret .= sprintf("<td class=\"col2\" style=\"text-align:left;padding:5px;padding-left:10px;padding-right:10px;$styl\">$wupn<br/>".
  2515. "<input type=\"text\" id=\"wt%d_n\" informId=\"$name-next_$i\" size=\"4\" maxlength=\"120\" value=\"$nval\" onchange=\"javascript:yaahm_setnext('$name',%d)\"/></td>",$i,$i);
  2516. }
  2517. $ret .= "</tr>\n";
  2518. $ret .= "</table><br/></td></tr>";
  2519. $ret .= "<tr><td colspan=\"3\"><div class=\"devType\" style=\"font-weight:bold\">".$yaahm_tt->{"overview"}."</div></td></tr>\n";
  2520. ### daily overview ################################################################################################
  2521. $styl="border-left:1px solid gray;border-top:1px solid gray;border-top-left-radius:10px;border-top-right-radius:0px;border-bottom-left-radius:0px;";
  2522. $stym="border-top:1px solid gray;border-radius:0px;";
  2523. $styr="border-right:1px solid gray;border-top:1px solid gray;border-top-right-radius:10px;border-top-left-radius:0px;border-bottom-right-radius:0px;";
  2524. $ret .= "<tr><td colspan=\"3\"><table>";
  2525. #-- time widget
  2526. $ret .= "<tr><td rowspan=\"8\" width=\"200\" style=\"padding-right:50px\"><img src=\"/fhem/YAAHM_timewidget?name='".$name."'&amp;size='200x200'\" type=\"image/svg+xml\" ></td>";
  2527. #-- continue summary with headers
  2528. $ret .= "<td colspan=\"2\" width=\"150\" style=\"$styl\"></td><td width=\"120\" style=\"$stym\">".$yaahm_tt->{"today"}."</td><td width=\"120\" style=\"$styr\">".$yaahm_tt->{"tomorrow"}."</td>";
  2529. #-- device states
  2530. $ret .= "<td rowspan=\"8\" style=\"padding:5px;vertical-align:top;border:1px solid gray;border-radius:10px\">".
  2531. "<div class=\"dval\" informId=\"$name-tr_housestate\">".ReadingsVal($name,"tr_housestate",undef)."</div>&#x2192;".
  2532. $yaahm_tt->{"secstate"}."<div class=\"dval\" informId=\"$name-sdev_housestate\">".ReadingsVal($name,"sdev_housestate","")."</div></td></tr>\n";
  2533. $styl="border-left:1px solid gray;border-radius:0px;";
  2534. $stym="border:none";
  2535. $styr="border-right:1px solid gray;border-radius:0px;";
  2536. $ret .= "<tr><td colspan=\"2\" style=\"$styl\"></td><td style=\"padding:5px;$stym\">".$yaahm_tt->{$weeklytable[$hash->{DATA}{"DD"}[0]{"weekday"}]}[0] .
  2537. "</td><td style=\"padding:5px;$styr\">".$yaahm_tt->{$weeklytable[$hash->{DATA}{"DD"}[1]{"weekday"}]}[0]."</td></tr>";
  2538. #-- continue summary with entries
  2539. $ret .= "<tr><td colspan=\"2\" style=\"$styl\"></td><td style=\"padding:5px;$stym\">".$hash->{DATA}{"DD"}[0]{"date"}.
  2540. "</td><td style=\"padding:5px;$styr\">".$hash->{DATA}{"DD"}[1]{"date"}."</td></tr>\n";
  2541. $ret .= "<tr><td colspan=\"2\" class=\"dname\" style=\"padding:5px;$styl\">".$yaahm_tt->{"daylight"}."</td><td style=\"padding:5px;$stym\">".$hash->{DATA}{"DD"}[0]{"sunrise"}."-".$hash->{DATA}{"DD"}[0]{"sunset"}.
  2542. "</td><td style=\"padding:5px;$styr\">".$hash->{DATA}{"DD"}[1]{"sunrise"}."-".$hash->{DATA}{"DD"}[1]{"sunset"}."</td></tr>\n";
  2543. $ret .= "<tr><td colspan=\"2\" class=\"dname\" style=\"padding:5px;$styl\">".$yaahm_tt->{"daytime"}."</td><td style=\"padding:5px;$stym\">".$hash->{DATA}{"DT"}{"morning"}[0]."-".
  2544. $hash->{DATA}{"DT"}{"night"}[0]."</td><td style=\"$styr\"></td></tr>\n";
  2545. $ret .= "<tr><td colspan=\"2\" class=\"dname\" style=\"padding:5px;$styl\">".$yaahm_tt->{"daytype"}."</td><td style=\"padding:5px;$stym\">".$yaahm_tt->{$hash->{DATA}{"DD"}[0]{"daytype"}}.
  2546. "</td><td style=\"padding:5px;$styr\">".$yaahm_tt->{$hash->{DATA}{"DD"}[1]{"daytype"}}."</td></tr>\n";
  2547. $ret .= "<tr><td colspan=\"2\" class=\"dname\" style=\"padding:5px;$styl\">".$yaahm_tt->{"description"}."</td><td style=\"padding:5px;$stym\">".$hash->{DATA}{"DD"}[0]{"desc"}.
  2548. "</td><td style=\"padding:5px;width:100px;$styr\">".$hash->{DATA}{"DD"}[1]{"desc"}."</td></tr>\n";
  2549. $styl="border-left:1px solid gray;border-bottom:1px solid gray;border-bottom-left-radius:10px;border-bottom-right-radius:0px;border-top-left-radius:0px;";
  2550. $stym="border-bottom:1px solid gray;border-radius:0px;";
  2551. $styr="border-right:1px solid gray;border-bottom:1px solid gray;border-bottom-right-radius:10px;border-top-right-radius:0px;border-bottom-left-radius:0px;";
  2552. $ret .= "<tr><td colspan=\"2\" class=\"dname\" style=\"padding:5px;$styl\">".$yaahm_tt->{"date"}."</td><td style=\"padding:5px;width:100px;$stym\">".$hash->{DATA}{"DD"}[0]{"special"}.
  2553. "</td><td style=\"padding:5px;width:100px;$styr\">".$hash->{DATA}{"DD"}[1]{"special"}."</td></tr>\n";
  2554. #-- housetime/phase
  2555. $ret .= "<tr><td rowspan=\"".$weeklyno."\" style=\"text-align:center; white-space:nowrap;max-height:20px\">".
  2556. "<label><div class=\"dval\" informId=\"$name-tr_housetime\">".ReadingsVal($name,"tr_housetime","").
  2557. "</div>&nbsp;<div class=\"dval\" informId=\"$name-tr_housephase\">".ReadingsVal($name,"tr_housephase","")."</div></label></td>";
  2558. #-- weekly timers
  2559. for( my $i=0;$i<$weeklyno;$i++ ){
  2560. $wupn = $hash->{DATA}{"WT"}[$i]{"name"};
  2561. #-- timer status
  2562. if( defined($defs{$name.".wtimer_".$i.".IF"})){
  2563. if( ReadingsVal($name.".wtimer_".$i.".IF","mode","") ne "disabled" ){
  2564. $ts = "<div style=\"color:green\">&#x2713;</div>";
  2565. }else{
  2566. $ts = "<div style=\"color:red\">&#x274c;</div>";
  2567. }
  2568. }else{
  2569. $ts = "";
  2570. }
  2571. #-- ring times
  2572. my $ring_0 = $hash->{DATA}{"WT"}[$i]{"ring_0x"};
  2573. my $ring_0_e = ReadingsVal($name,"today_".$i."_e","");
  2574. if( $ring_0_e=~ /^disabled \((.*)\)/ ){
  2575. $ring_0 = 'off ('.substr($yaahm_tt->{$1},0,3).')';
  2576. }
  2577. my $ring_1 = $hash->{DATA}{"WT"}[$i]{"ring_1x"};
  2578. my $ring_1_e = ReadingsVal($name,"tomorrow_".$i."_e","");
  2579. if( $ring_1_e=~ /^disabled \((.*)\)/ ){
  2580. $ring_1 = 'off ('.substr($yaahm_tt->{$1},0,3).')';
  2581. }
  2582. my $wake = $hash->{DATA}{"WT"}[$i]{"wake"};
  2583. #-- border styles
  2584. if( $i==0 ){
  2585. $styl="border-left:1px solid gray;border-top:1px solid gray;border-top-left-radius:10px;border-top-right-radius:0px;border-bottom-left-radius:0px;";
  2586. $stym="border-top:1px solid gray;border-radius:0px;";
  2587. $styr="border-right:1px solid gray;border-top:1px solid gray;border-top-right-radius:10px;border-top-left-radius:0px;border-bottom-right-radius:0px;";
  2588. }elsif( $i == $weeklyno-1 ){
  2589. $styl="border-left:1px solid gray;border-bottom:1px solid gray;border-bottom-left-radius:10px;border-bottom-right-radius:0px;border-top-left-radius:0px;";
  2590. $stym="border-bottom:1px solid gray;border-radius:0px;";
  2591. $styr="border-right:1px solid gray;border-bottom:1px solid gray;border-bottom-right-radius:10px;border-top-right-radius:0px;border-bottom-left-radius:0px;";
  2592. }else{
  2593. $styl="border-left:1px solid gray;border-radius:0px;";
  2594. $stym="border:none";
  2595. $styr="border-right:1px solid gray;border-radius:0px;";
  2596. }
  2597. $ret.="<td style=\"padding:5px;$styl\">".$wupn.
  2598. "</td><td style=\"text-align:center;width:30px;padding:5px;$stym\">$ts</td>".
  2599. "<td style=\"padding:5px;$stym\"><div class=\"dval\" informId=\"$name-ring_$i\">".$ring_0."</div></td>".
  2600. "<td style=\"padding:5px;$stym\"><div class=\"dval\" informId=\"$name-ring_".$i."_1\">".$ring_1."</div></td>".
  2601. "<td style=\"padding:5px;$styr\"><div class=\"dval\" informId=\"$name-tr_wake_$i\">".$wake."</div></td></tr>\n";
  2602. }
  2603. $ret .= "</table></td></tr>\n";
  2604. return $ret;
  2605. }
  2606. #########################################################################################
  2607. #
  2608. # YAAHM_Shorttable - returns complete HTML code for inclusion into any room page
  2609. # (action and overview fields)
  2610. #
  2611. # Parameter name = name of the YAAHM definition
  2612. #
  2613. #########################################################################################
  2614. sub YAAHM_Shorttable($){
  2615. my ($name) = @_;
  2616. my $ret = YAAHM_toptable($name);
  2617. #-- complete the code of the page
  2618. $ret .= "</table>";
  2619. InternalTimer(gettimeofday()+ 1, "YAAHM_informer", $defs{$FW_cname},0);
  2620. return $ret;
  2621. }
  2622. #########################################################################################
  2623. #
  2624. # YAAHM_Longtable - returns complete HTML code for the full YAAHM page
  2625. # (action, overview, daily and weekly profile)
  2626. #
  2627. # Parameter name = name of the YAAHM definition
  2628. #
  2629. #########################################################################################
  2630. sub YAAHM_Longtable($){
  2631. my ($name) = @_;
  2632. my $ret = "";
  2633. my $hash = $defs{$name};
  2634. my $id = $defs{$name}{NR};
  2635. #--
  2636. my $lockstate = ($hash->{READINGS}{lockstate}{VAL}) ? $hash->{READINGS}{lockstate}{VAL} : "unlocked";
  2637. my $showhelper = ($lockstate eq "unlocked") ? 1 : 0;
  2638. %dailytable = %{$hash->{DATA}{"DT"}};
  2639. my $dailyno = scalar keys %dailytable;
  2640. my $weeklyno = int( @{$hash->{DATA}{"WT"}} );
  2641. #--
  2642. $ret = YAAHM_toptable($name);
  2643. ### daily profile table ################################################################################################
  2644. my $row = 1;
  2645. my $event = "";
  2646. my $sval = "";
  2647. my $eval = "";
  2648. my $xval = "";
  2649. my $dh = (defined($attr{$name}{"timeHelper"})) ? $attr{$name}{"timeHelper"} : undef;
  2650. #-- global status of timer
  2651. my ($tl,$ts);
  2652. if( defined($defs{$name.".dtimer.IF"})){
  2653. $tl = "<a href=\"/fhem?detail=$name.dtimer.IF\">$name.dtimer.IF</a>";
  2654. #-- green hook
  2655. if( ReadingsVal($name.".dtimer.IF","mode","") ne "disabled" ){
  2656. $ts = "<td style=\"color:green;padding-left:5px\">&#x2713;</td>\n";
  2657. #-- red cross
  2658. }else{
  2659. $ts = "<td style=\"color:red;padding-left:5px\">&#x274c;</td>\n";
  2660. }
  2661. }else{
  2662. $tl= $yaahm_tt->{"notstarted"};
  2663. $ts ="<td></td>";
  2664. }
  2665. #-- name link button status
  2666. $ret .= "<tr><td colspan=\"3\"><div class=\"devType\" style=\"font-weight:bold; white-space:nowrap;\">".$yaahm_tt->{"daily"}.$yaahm_tt->{"profile"}."\n".
  2667. "<table><tr><td style=\"text-align:center;vertical-align:middle;white-space:nowrap;padding-left:5px\"><div id=\"dtlink\" style=\"font-weight:normal\">$tl</div></td>$ts";
  2668. $ret .= "<td style=\"vertical-align:middle;;padding-left:5px\"><input type=\"button\" value=\"".$yaahm_tt->{"start"}." ".$yaahm_tt->{"daily"}.$yaahm_tt->{"timer"}."\" onclick=\"javascript:yaahm_startDayTimer('$name')\"/></td>\n";
  2669. $ret .= "</tr></table></div></td></tr>";
  2670. #-- header line
  2671. $ret .= "<tr><td colspan=\"3\"><table class=\"block wide\" id=\"dailytable\">\n";
  2672. $ret .= "<tr style=\"font-weight:bold\"><td rowspan=\"2\" class=\"devType col1\" style=\"min-width:120px;\">".$yaahm_tt->{"event"}."</td><td class=\"devType,col2\" style=\"text-align:right;min-width:180px;white-space:nowrap;\">".
  2673. $yaahm_tt->{"time"}." [hh:mm]&nbsp;&nbsp;&nbsp;</td>".
  2674. "<td rowspan=\"2\" class=\"devType col3\" style=\"min-width:200px;\">".$yaahm_tt->{"action"}."</td>".
  2675. "</tr>".
  2676. "<tr style=\"font-weight:bold\"><td class=\"devType col2\" style=\"text-align:right\">Start/Offset&nbsp;&nbsp;&nbsp;End&nbsp;&nbsp;&nbsp;</td></tr>\n";
  2677. #-- iterate through table
  2678. foreach my $key (sort YAAHM_dsort keys %dailytable){
  2679. $row++;
  2680. $event= $yaahm_tt->{$key};
  2681. $sval = $dailytable{$key}[0];
  2682. $eval = $dailytable{$key}[1];
  2683. $xval = $dailytable{$key}[2];
  2684. $xval = "" if( !defined($xval) );
  2685. #--
  2686. if( $dh ){
  2687. #-- timeHelper not in command list
  2688. if( $xval !~ /^{$dh/ ){
  2689. if( defined($xval) && length($xval)>0 ){
  2690. $xval ="{".$dh."('".$key."')},".$xval;
  2691. }else{
  2692. $xval ="{".$dh."('".$key."')}";
  2693. }
  2694. }
  2695. }
  2696. $ret .= sprintf("<tr class=\"%s\"><td class=\"col1\">$event</td>\n", ($row&1)?"odd":"even");
  2697. #-- First field
  2698. #-- Only reference for wakeup
  2699. if( $key =~ /^wakeup.*/ ){
  2700. $ret .= "<td class=\"col2\" style=\"text-align:left\">".$yaahm_tt->{"weekly"}.$yaahm_tt->{"profile"}."</td><td></td><td></td></tr>\n";
  2701. next;
  2702. #-- Only reference for sleep
  2703. }elsif( $key =~ /^sleep.*/ ){
  2704. $ret .= "<td class=\"col2\" style=\"text-align:left\">".$yaahm_tt->{"weekly"}.$yaahm_tt->{"profile"}."</td><td></td><td></td></tr>\n";
  2705. next;
  2706. #-- calculated value for sunrise/sunset
  2707. }elsif( $key =~ /.*((sunrise)|(sunset)|(midnight)).*/ ){
  2708. my $pre;
  2709. if( $key =~ /.*sunrise.*/ ){
  2710. $pre = $hash->{DATA}{"DD"}[0]{"sunrise"}
  2711. }elsif( $key =~ /.*sunset.*/ ){
  2712. $pre = $hash->{DATA}{"DD"}[0]{"sunset"}
  2713. }else{
  2714. $pre = "00:00";
  2715. }
  2716. $ret .= "<td class=\"col2\" style=\"text-align:right\">";
  2717. if( $key =~ /.*before.*/ ){
  2718. $ret .= "$pre - &nbsp;<input type=\"text\" id=\"dt".$key."_e\" size=\"4\" maxlength=\"120\" value=\"$eval\"/>&nbsp;=&nbsp;$sval&nbsp;&nbsp;&nbsp;&nbsp;</td>"
  2719. }elsif( $key =~ /.*after.*/ ){
  2720. $ret .= "$pre + &nbsp;<input type=\"text\" id=\"dt".$key."_e\" size=\"4\" maxlength=\"120\" value=\"$eval\"/>&nbsp;=&nbsp;$sval&nbsp;&nbsp;&nbsp;&nbsp;</td>"
  2721. }else{
  2722. $ret .= "$pre&nbsp;&nbsp;&nbsp;&nbsp;</td>"
  2723. }
  2724. #-- normal input of one time
  2725. }else{
  2726. $ret .= "<td class=\"col2\" style=\"text-align:right\"><input type=\"text\" id=\"dt".$key."_s\" size=\"4\" maxlength=\"120\" value=\"$sval\"/></td>";
  2727. }
  2728. #-- Second field
  2729. $ret .= "<td class=\"col3\"><input type=\"text\" id=\"dt".$key."_x\" size=\"28\" maxlength=\"512\" value=\"$xval\"/></td></tr>\n";
  2730. }
  2731. $ret .= "</table></td></tr></tr>";
  2732. ### weekly profile table ################################################################################################
  2733. $row = 1;
  2734. $event = "";
  2735. $sval = "";
  2736. my $wupn;
  2737. my $wt = ( $weeklyno == 1) ? $yaahm_tt->{"profile"} :$yaahm_tt->{"profiles"};
  2738. $ret .= "<tr><td colspan=\"3\"><div class=\"devType\" style=\"font-weight:bold; white-space: nowrap;\">".$yaahm_tt->{"weekly"}.$wt.
  2739. "&nbsp;&nbsp;<input type=\"button\" value=\"".$yaahm_tt->{"start"}." ".$yaahm_tt->{"weekly"}.$yaahm_tt->{"timer"}."\" onclick=\"javascript:yaahm_startWeeklyTimer('$name')\"/></div></td></tr>";
  2740. $ret .= "<tr><td><table class=\"readings\" id=\"weeklytable\">\n";
  2741. #-- repeat name for every weekly table
  2742. $ret .= "<tr class=\"odd\"><td class=\"col1\" style=\"font-weight:bold;text-align:left;padding:5px\">".$yaahm_tt->{"name"}."</td>";
  2743. for (my $i=0;$i<$weeklyno;$i++){
  2744. $wupn = $hash->{DATA}{"WT"}[$i]{"name"};
  2745. $ret .= "<td class=\"col2\" style=\"text-align:left;padding:5px\">$wupn</td>";
  2746. }
  2747. $ret .= "</tr>\n";
  2748. #-- repeat link for every weekly table
  2749. $ret .= "<tr class=\"even\"><td class=\"col1\" style=\"font-weight:bold;text-align:left;padding:5px\">".$yaahm_tt->{"timer"}."</td>";
  2750. #-- array with activity status
  2751. my @tss;
  2752. for (my $i=0;$i<$weeklyno;$i++){
  2753. $wupn = $hash->{DATA}{"WT"}[$i]{"name"};
  2754. #-- timer status
  2755. if( defined($defs{$name.".wtimer_".$i.".IF"})){
  2756. $tl = "<a href=\"/fhem?detail=".$name.".wtimer_".$i.".IF\">".$name.".wtimer_".$i.".IF</a>";
  2757. if( ReadingsVal($name.".wtimer_".$i.".IF","mode","") ne "disabled" ){
  2758. push(@tss,"<div style=\"color:green\">&#x2713;</div>");
  2759. }else{
  2760. push(@tss,"<div style=\"color:red\">&#x274c;</div>");
  2761. }
  2762. }else{
  2763. $tl = $yaahm_tt->{"notstarted"};
  2764. push(@tss,"");
  2765. }
  2766. $ret .= sprintf("<td class=\"col2\" style=\"text-align:left;padding:5px\"><div id=\"wt%dlink\">%s</div></td>",$i,$tl);
  2767. }
  2768. $ret .= "</tr>\n";
  2769. #-- repeat active status for every weekly table
  2770. my $asg = "";
  2771. my $ast = "";
  2772. my $ass;
  2773. my $acc;
  2774. #--header
  2775. for(my $i=0;$i<int(@profmode);$i++){
  2776. $asg .= substr($yaahm_tt->{$profmode[$i]},0,3)."&nbsp;";
  2777. $ast .= $yaahm_tt->{$profmode[$i]}." ";
  2778. }
  2779. for(my $i=0;$i<int(@profday);$i++){
  2780. $asg .= substr($yaahm_tt->{$profday[$i]},0,3)."&nbsp;";
  2781. $ast .= $yaahm_tt->{$profday[$i]}." ";
  2782. }
  2783. $ret .= "<tr class=\"odd\"><td class=\"col1\" style=\"font-weight:bold;text-align:left;padding:5px\">".$yaahm_tt->{"active"}."<br/><div title=\".$ast.\" style=\"font-weight:normal\">".$asg."</div></td>";
  2784. for (my $i=0;$i<$weeklyno;$i++){
  2785. $wupn = $hash->{DATA}{"WT"}[$i]{"name"};
  2786. $ret .= "<td class=\"col2\" style=\"text-align:center;padding:5px\">".$tss[$i]."</br>";
  2787. $asg = "";
  2788. $ass = ( defined($hash->{DATA}{"WT"}[$i]{"acti_m"}) ) ? $hash->{DATA}{"WT"}[$i]{"acti_m"} : "";
  2789. for( my $j=0;$j<int(@profmode);$j++ ){
  2790. $acc = $profmode[$j];
  2791. $acc = ( $ass =~ /.*$acc.*/ ) ? " checked=\"checked\"" : "";
  2792. $asg .= sprintf("<input type=\"checkbox\" name=\"acti_%d_m\" value=\"".$profmode[$j]."\" $acc/>&nbsp;",$i);
  2793. }
  2794. $ass = ( defined($hash->{DATA}{"WT"}[$i]{"acti_d"}) ) ? $hash->{DATA}{"WT"}[$i]{"acti_d"} : "";
  2795. for( my $j=0;$j<int(@profday);$j++ ){
  2796. $acc = $profday[$i];
  2797. $acc = ( $ass =~ /.*$acc.*/ ) ? " checked=\"checked\"" : "";
  2798. $asg .= sprintf("<input type=\"checkbox\" name=\"acti_%d_d\" value=\"".$profday[$j]."\" $acc/>&nbsp;",$i);
  2799. }
  2800. $ret .= "$asg</td>";
  2801. }
  2802. $ret .= "</tr>\n";
  2803. #-- repeat action for every weekly table
  2804. $ret .= "<tr class=\"odd\"><td class=\"col1\" style=\"font-weight:bold;text-align:left;padding:5px\">".$yaahm_tt->{"action"}."</td>";
  2805. for (my $i=0;$i<$weeklyno;$i++){
  2806. $xval = $hash->{DATA}{"WT"}[$i]{"action"};
  2807. #--
  2808. if( $dh && $i<2 ){
  2809. #-- timeHelper not in command list
  2810. $wupn = ($i==0) ? "wakeup" : "sleep";
  2811. if( $xval !~ /^{$dh/ ){
  2812. if( defined($xval) && length($xval)>0 ){
  2813. $xval ="{".$dh."('".$wupn."')},".$xval;
  2814. }else{
  2815. $xval ="{".$dh."('".$wupn."')}";
  2816. }
  2817. }
  2818. }
  2819. $ret .= sprintf("<td class=\"col2\" style=\"text-align:left;padding:5px\"><input class=\"expand\" type=\"text\" id=\"wt%d_x\" size=\"10\" maxlength=\"512\" value=\"$xval\"/></td>",$i);
  2820. }
  2821. $ret .= "</tr>\n";
  2822. #-- repeat unit for every weekly table
  2823. $ret .= "<tr class=\"even\"><td></td>";
  2824. for (my $i=0;$i<$weeklyno;$i++){
  2825. $ret .= "<td class=\"col2\" style=\"text-align:left;padding:5px\">".$yaahm_tt->{"time"}." [hh:mm]</td>";
  2826. }
  2827. $ret .= "</tr>\n";
  2828. #-- weekday header
  2829. $ret .= "<tr class=\"even\"><td class=\"col1\" style=\"font-weight:bold;text-align:left;padding:5px\">".$yaahm_tt->{"weekday"}."</td>";
  2830. for (my $i=0;$i<$weeklyno;$i++){
  2831. $ret .= "<td></td>";
  2832. }
  2833. $ret .= "</tr>\n";
  2834. for (my $j=0;$j<7;$j++){
  2835. my $key = $weeklytable[$j];
  2836. $row++;
  2837. $event = $yaahm_tt->{$key}[0];
  2838. $ret .= sprintf("<tr class=\"%s\"><td class=\"col1\" style=\"text-align:left;padding-left:5px\">$event</td>\n", ($row&1)?"odd":"even");
  2839. for (my $i=0;$i<$weeklyno;$i++){
  2840. $sval = $hash->{DATA}{"WT"}[$i]{$key};
  2841. $ret .= sprintf("<td class=\"col2\" style=\"text-align:left;padding-left:5px\"><input type=\"text\" id=\"wt%s%d_s\" size=\"4\" maxlength=\"120\" value=\"$sval\"/></td>",$key,$i);
  2842. }
  2843. $ret .= "</tr>\n";
  2844. }
  2845. $ret .= "</table></td></tr></tr>";
  2846. #-- complete the code of the page
  2847. $ret .= "</table>";
  2848. #InternalTimer(gettimeofday()+ 3, "YAAHM_informer", $defs{$FW_cname},0);
  2849. return $ret;
  2850. }
  2851. 1;
  2852. =pod
  2853. =item helper
  2854. =item summary admimistration of profiles for daily, weekly and monthly processes
  2855. =item summary_DE Verwaltung von Profilen für tägliche, wöchentliche und monatliche Abläufe
  2856. =begin html
  2857. <a name="YAAHM"></a>
  2858. <h3>YAAHM</h3>
  2859. <p> Yet Another Auto Home Module to set up a cyclic processing of commands (daily, weekly, monthly, yearly profile)</p>
  2860. <a name="YAAHMusage"></a>
  2861. <h4>Usage</h4>
  2862. See <a href="http://www.fhemwiki.de/wiki/Modul_YAAHM">German Wiki page</a>
  2863. <br/>
  2864. <a name="YAAHMdefine"></a>
  2865. <h4>Define</h4>
  2866. <p>
  2867. <code>define &lt;name&gt; YAAHM</code>
  2868. <br />Defines the YAAHM system. </p>
  2869. <a name="YAAHMset"></a>
  2870. Notes: <ul>
  2871. <li>This module uses the global attribute <code>language</code> to determine its output data<br/>
  2872. (default: EN=english). For German output set <code>attr global language DE</code>.</li>
  2873. </ul>
  2874. <h4>Set</h4>
  2875. <ul>
  2876. <li><a name="yaahm_time">
  2877. <code>set &lt;name&gt; time &lt;timeevent&gt;</code></a><br/>
  2878. Set the current house time (event), i.e. one of several values:
  2879. <ul>
  2880. <li>(after|before) midnight | [before|after] sunrise | [before|after] sunset are calculated from astronomical data (&pm;offset).
  2881. These values vary from day to day, only the offset can be specified in the daily profile. </li>
  2882. <li>morning | noon | afternoon | evening | night are fixed time events specified in the daily profile.
  2883. The time phase between events morning and night is called <i>daytime</i>, the
  2884. time phase between events night and morning is called <i>nighttime</i></li>
  2885. <li>wakeup|sleep are time events specified in the weekly default profiles <i>Wakeup</i> and <i>Sleep</i>, i.e. the value may change from day to day.</li>
  2886. </ul>
  2887. The actual changes to certain devices are made by the functions in the command field, or by an external <a href="#yaahm_timehelper">timeHelper function</a>.
  2888. </li>
  2889. <li><a name="yaahm_manualnext"></a>
  2890. <code>set &lt;name&gt; manualnext &lt;timernumber&gt; &lt;time&gt;</code><br/>
  2891. <code>set &lt;name&gt; manualnext &lt;timername&gt; &lt;time&gt;</code><br/>
  2892. For the weekly timer identified by its number (starting at 0) or its name, set the next ring time manually. The time specification &lt;time&gt;must be in the format hh:mm or "off"
  2893. If the time specification &lt;time&gt; is later than the current time, it will be used for today. If it is earlier than the current time, it will be used tomorrow.
  2894. </li>
  2895. <li><a name="yaahm_mode">
  2896. <code>set &lt;name&gt; mode normal | party | absence | donotdisturb</code>
  2897. </a>
  2898. <br />Set the current house mode, i.e. one of several values:
  2899. <ul>
  2900. <li>normal - normal daily and weekly time profiles apply</li>
  2901. <li>party - can be used in the timeHelper function to suppress certain actions, like e.g. those that set the house (security) state to <i>secured</i> or the house time event to <i>night</i>.</li>
  2902. <li>absence - can be used in the timeHelper function to suppress certain actions. Valid until manual mode change</li>
  2903. <li>donotodisturb - can be used in the timeHelper function to suppress certain actions. Valid until manual mode change</li>
  2904. </ul>
  2905. House modes are valid until manual mode change. If the attribute <i>modeAuto</i> is set (see below), mode will change automatically at certain time events.
  2906. The actual changes to certain devices are made by an external <a href="#yaahm_modehelper">modeHelper function</a>.
  2907. </li>
  2908. <li><a name="yaahm_state">
  2909. <code>set &lt;name&gt; state unsecured | secured | protected | guarded</code>
  2910. </a>
  2911. <br/>Set house (security) state, i.e. one of several values:
  2912. <ul>
  2913. <li> unsecured - Example: doors etc.
  2914. </li>
  2915. <li> secured - Example: doors etc. are locked, windows may not be open
  2916. </li>
  2917. <li> protected - Example: doors etc. are locked, windows may not be open, alarm system is armed
  2918. </li>
  2919. <li> guarded - Example: doors etc. are locked, windows may not be open, alarm is armed, a periodic house check is run and a simulation as well
  2920. </li>
  2921. </ul>
  2922. House (security) states are valid until manual change. If the attribute <i>stateAuto</i> is set (see below), state will change automatically at certain times.
  2923. The actual changes to certain devices are made by an external <a href="#yaahm_statehelper">stateHelper function</a>. If these external devices are in their proper state
  2924. for a particular house (security) state can be checked automatically, see the attribute <a href="#yaahm_statedevices">stateDevices</a>
  2925. </li>
  2926. <li><a name="yaahm_createweekly">
  2927. <code>set &lt;name&gt; createWeekly &lt;string&gt;</code>
  2928. </a>
  2929. <br/>Create a new weekly profile &lt;string&gt;</li>
  2930. <li><a name="yaahm_deleteweekly">
  2931. <code>set &lt;name&gt; deleteWeekly &lt;string&gt;</code>
  2932. </a>
  2933. <br/>Delete the weekly profile &lt;string&gt;</li>
  2934. <li><a name="yaahm_initialize">
  2935. <code>set &lt;name&gt; initialize</code>
  2936. </a>
  2937. <br/>Restart the internal timers</li>
  2938. <li><a name="yaahm_lock">
  2939. <code>set &lt;name&gt; locked|unlocked</code>
  2940. </a>
  2941. <br />Set the lockstate of the yaahm module to <i>locked</i> (i.e., yaahm setups
  2942. may not be changed) resp. <i>unlocked</i> (i.e., yaahm setups may be changed>)</li>
  2943. <li><a name="yaahm_save">
  2944. <code>set &lt;name&gt; save|restore</code>
  2945. </a>
  2946. <br />Manually save/restore the complete profile data to/from the external file YAAHMFILE (save done automatically at each timer starte, restore at FHEM start)</li>
  2947. </ul>
  2948. <a name="YAAHMget"></a>
  2949. <h4>Get</h4>
  2950. <ul>
  2951. <li><a name="yaahm_next"></a>
  2952. <code>get &lt;name&gt; next &lt;timernumber&gt;</code><br/>
  2953. <code>get &lt;name&gt; next &lt;timername&gt;</code><br/>
  2954. For the weekly timer identified by its number (starting at 0) or its name, get the next ring time in a format suitable for text devices.</li>
  2955. <li><a name="yaahm_saynext"></a>
  2956. <code>get &lt;name&gt; sayNext &lt;timernumber&gt;</code><br/>
  2957. <code>get &lt;name&gt; sayNext &lt;timername&gt;</code><br/>
  2958. For the weekly timer identified by its number (starting at 0) or its name, get the next ring time in a format suitable for speech devices.</li>
  2959. <li><a name="yaahm_version"></a>
  2960. <code>get &lt;name&gt; version</code>
  2961. <br />Display the version of the module</li>
  2962. <li><a name="yaahm_template"></a>
  2963. <code>get &lt;name&gt; template</code>
  2964. <br />Return an (empty) perl subroutine for the helper functions</li>
  2965. </ul>
  2966. <a name="YAAHMattr"></a>
  2967. <h4>Attributes</h4>
  2968. <ul>
  2969. <li><a name="yaahm_linkname"><code>attr &lt;name&gt; linkname
  2970. &lt;string&gt;</code></a>
  2971. <br />Name for yaahm web link, default:
  2972. Profile</li>
  2973. <li><a name="yaahm_hiddenroom"><code>attr &lt;name&gt; hiddenroom
  2974. &lt;string&gt;</code></a>
  2975. <br />Room name for hidden yaahm room (containing only the YAAHM device), default:
  2976. ProfileRoom</li>
  2977. <li><a name="yaahm_lockstate"><code>attr &lt;name&gt; lockstate
  2978. locked|unlocked</code></a>
  2979. <br /><i>locked</i> means that yaahm setups may not be changed, <i>unlocked</i>
  2980. means that yaahm setups may be changed</li>
  2981. <li><a name="yaahm_simulation"><code>attr &lt;name&gt; simulation
  2982. 0|1</code></a>
  2983. <br />a value of 1 means that commands will not be executed, but only simulated</li>
  2984. <li><a name="yaahm_timehelper"><code>attr &lt;name&gt; timeHelper &lt;name of perl program&gt;</code></a>
  2985. <br />name of a perl function that is called at each time step of the daily profile and for the two default weekly profiles</li>
  2986. <li><a name="yaahm_modehelper"><code>attr &lt;name&gt; modeHelper &lt;name of perl program&gt;</code></a>
  2987. <br />name of a perl function that is called at every change of the house mode</li>
  2988. <li><a name="yaahm_modeauto"><code>attr &lt;name&gt; modeAuto 0|1</code></a>
  2989. <br />If this attribute is set to 1, the house mode changes automatically at certain time events.
  2990. <ul>
  2991. <li>On time (event) <i>sleep</i> or <i>morning</i>, <i>party</i> mode will be reset to <i>normal</i> mode.</li>
  2992. <li>On time (event) <i>wakeup</i>, <i>absence</i> mode will be reset to <i>normal</i> mode.</li>
  2993. <li>On <i>any</i> time (event), <i>donotdisturb</i> mode will be reset to <i>normal</i> mode.</li>
  2994. </ul>
  2995. </li>
  2996. <li><a name="yaahm_statedevices"><code>attr &lt;name&gt; stateDevices (&lt;device&gt;:&lt;state-unsecured&gt;:&lt;state-secured&gt;:&lt;state-protected&gt;:&lt;state-guarded&gt;,)*</code></a>
  2997. <br />comma separated list of devices and their state in each of the house (security) states. Each of the listed devices will be checked in the interval given by the <i>stateInterval</i> attribute
  2998. for its proper state, and a <i>stateWarning</i> function will be called if it is not in the proper state.</li>
  2999. <li><a name="yaahm_stateinterval"><code>attr &lt;name&gt; stateInterval &lt;integer&gt;</code></a>
  3000. <br />interval in minutes for checking all <i>stateDevices</i> for their proper state according of the house (security) state. Default 60 minutes.</li>
  3001. <li><a name="yaahm_statewarning"><code>attr &lt;name&gt; stateWarning &lt;name of perl program&gt;</code></a>
  3002. <br />name of a perl function that is called as <i>stateWarning('device','desired state','actual state')</i>if a device is not in the desired state.</li>
  3003. <li><a name="yaahm_statehelper"><code>attr &lt;name&gt; stateHelper &lt;name of perl program&gt;</code></a>
  3004. <br />name of a perl function that is called as <i>stateHelper('event')</i> at every change of the house (security) state</li>
  3005. <li><a name="yaahm_stateauto"><code>attr &lt;name&gt; stateAuto 0|1</code></a>
  3006. <br />If this attribute is set to 1, the house state changes automatically if certain modes are set or at certain time events
  3007. <ul>
  3008. <li>If leaving <i>party</i> mode and time event <i>sleep</i>, and currently in (security) state <i>unsecured</i>, the state will change to <i>secured</i>.</li>
  3009. <li>If in <i>normal</i> mode and time event <i>sleep</i> or <i>night</i>, and currently in (security) state <i>unsecured</i>, the state will change to <i>secured</i>.</li>
  3010. </ul>
  3011. </li>
  3012. <li><a name="yaahm_holidaydevices"><code>attr &lt;name&gt; &lt;comma-separated list of devices&gt; </code></a>
  3013. <br />list of devices that provide holiday information. The devices may be
  3014. <a href="#holiday">holiday devices</a> or <a href="#Calendar">Calendar devices</a></li>
  3015. <li><a name="yaahm_vacationdevices"><code>attr &lt;comma-separated list of devices&gt; </code></a>
  3016. <br />list of devices that provide vacation information. The devices may be
  3017. <a href="#holiday">holiday devices</a> or <a href="#Calendar">Calendar devices</a></li>
  3018. <li><a name="yaahm_specialdevices"><code>attr &lt;comma-separated list of devices&gt; </code></a>
  3019. <br />list of devices that provide special date information (like e.g. garbage collection). The devices may be
  3020. <a href="#holiday">holiday devices</a> or <a href="#Calendar">Calendar devices</a></li>
  3021. </ul>
  3022. =end html
  3023. =begin html_DE
  3024. <a name="YAAHM"></a>
  3025. <h3>YAAHM</h3>
  3026. <a href="https://wiki.fhem.de/wiki/Modul_YAAHM">Deutsche Dokumentation im Wiki</a> vorhanden, die englische Version gibt es hier: <a href="/fhem/docs/commandref.html#YAAHM">YAAHM</a>
  3027. =end html_DE
  3028. =cut