95_YAAHM.pm 165 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862
  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 16371 2018-03-10 09:28:10Z 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($FW_ME);
  34. use vars qw($FW_inform);
  35. use vars qw($FW_headerlines);
  36. use vars qw($FW_id);
  37. use Data::Dumper;
  38. use Math::Trig;
  39. use JSON; # imports encode_json, decode_json, to_json and from_json.
  40. #########################
  41. # Global variables
  42. my $yaahmname;
  43. my $yaahmlinkname = "Profile"; # link text
  44. my $yaahmhiddenroom = "ProfileRoom"; # hidden room
  45. my $yaahmpublicroom = "Unsorted"; # public room
  46. my $yaahmversion = "1.50";
  47. my $firstcall = 1;
  48. my %yaahm_transtable_EN = (
  49. "ok" => "OK",
  50. "notok" => "Not OK",
  51. "start" => "Start",
  52. "status" => "Status",
  53. "notstarted" => "Not started",
  54. "next" => "Next",
  55. "manual" => "Manual Time",
  56. "exceptly" => "exceptionally",
  57. "undecid" => "not decidable",
  58. "off" => "off",
  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. "transition" => "Transition to",
  82. "onlposfrm" => "only possible from",
  83. "notposfrm" => "not possible from",
  84. #--
  85. "aftermidnight" => "After Midnight",
  86. "beforesunrise" => "Before Sunrise",
  87. "sunrise" => "Sunrise",
  88. "aftersunrise" => "After Sunrise",
  89. "wakeup" => "WakeUp",
  90. "morning" => "Morning",
  91. "noon" => "Noon",
  92. "afternoon" => "Afternoon",
  93. "evening" => "Evening",
  94. "beforesunset" => "Before Sunset",
  95. "sunset" => "Sunset",
  96. "aftersunset" => "After Sunset",
  97. "sleep" => "Sleep",
  98. "night" => "Night",
  99. "beforemidnight" => "Before Midnight",
  100. #--
  101. "date" => "Date",
  102. "today" => "Today",
  103. "tomorrow" => "Tomorrow",
  104. "workday" => "Workday",
  105. "weekend" => "Weekend",
  106. "vacation" => "Vacation",
  107. "holiday" => "Holiday",
  108. "weekday" => "Day of Week",
  109. #--
  110. "mode" => "Mode",
  111. "normal" => "Normal",
  112. "party" => "Party",
  113. "absence" => "Absence",
  114. "donotdisturb" => "DoNotDisturb",
  115. #--
  116. "state" => "Security",
  117. "secstate" => "Device states",
  118. "unlocked" => "Unlocked",
  119. "locked" => "Locked",
  120. "unsecured" => "Not Secured",
  121. "secured" => "Secured",
  122. "protected" => "Protected",
  123. "guarded" => "Guarded",
  124. #--
  125. "monday" => ["Monday","Mon"],
  126. "tuesday" => ["Tuesday","Tue"],
  127. "wednesday" => ["Wednesday","Wed"],
  128. "thursday" => ["Thursday","Thu"],
  129. "friday" => ["Friday","Fri"],
  130. "saturday" => ["Saturday","Sat"],
  131. "sunday" => ["Sunday","Sun"],
  132. #--
  133. "spring" => "Spring",
  134. "summer" => "Summer",
  135. "fall" => "Fall",
  136. "winter" => "Winter"
  137. );
  138. my %yaahm_transtable_DE = (
  139. "ok" => "OK",
  140. "notok" => "Nicht OK",
  141. "start" => "Start",
  142. "status" => "Status",
  143. "notstarted" => "Nicht gestartet",
  144. "next" => "Nächste",
  145. "manual" => "Manuelle Zeit",
  146. "clock" => "Uhr",
  147. "exceptly" => "ausnahmsweise",
  148. "undecid" => "nicht bestimmbar",
  149. "off" => "Aus",
  150. "swoff" => "ausgeschaltet",
  151. "and" => "und",
  152. "active" => "Aktiv",
  153. "inactive" => "Inaktiv",
  154. "overview" => "Zusammenfassung",
  155. "name" => "Name",
  156. "event" => "Event",
  157. "time" => "Zeit",
  158. "timer" => "Timer",
  159. "action" => "Aktion",
  160. "weekly" => "Wochen-",
  161. "day" => "Tag",
  162. "daytime" => "Tageszeit",
  163. "nighttime" => "Nachtzeit",
  164. "daylight" => "Tageslicht",
  165. "daytype" => "Tagestyp",
  166. "daily" => "Tages-",
  167. "type" => "Typ",
  168. "description" => "Beschreibung",
  169. "profile" => "Profil",
  170. "profiles" => "Profile",
  171. "transition" => "Übergang zu",
  172. "onlposfrm" => "nur möglich aus",
  173. "notposfrm" => "nicht möglich aus",
  174. #--
  175. "aftermidnight" => "Nach Mitternacht",
  176. "beforesunrise" => "Vor Sonnenaufgang",
  177. "sunrise" => "Sonnenaufgang",
  178. "aftersunrise" => "Nach Sonnenaufgang",
  179. "wakeup" => "Wecken",
  180. "morning" => "Morgen",
  181. "noon" => "Mittag",
  182. "afternoon" => "Nachmittag",
  183. "evening" => "Abend",
  184. "beforesunset" => "Vor Sonnenuntergang",
  185. "sunset" => "Sonnenuntergang",
  186. "aftersunset" => "Nach Sonnenuntergang",
  187. "sleep" => "Schlafen",
  188. "night" => "Nacht",
  189. "beforemidnight" => "Vor Mitternacht",
  190. #--
  191. "date" => "Termin",
  192. "today" => "Heute",
  193. "tomorrow" => "Morgen",
  194. "workday" => "Arbeitstag",
  195. "weekend" => "Wochenende",
  196. "vacation" => "Ferientag",
  197. "holiday" => "Feiertag",
  198. "weekday" => "Wochentag",
  199. #--
  200. "mode" => "Modus",
  201. "normal" => "Normal",
  202. "party" => "Party",
  203. "absence" => "Abwesenheit",
  204. "donotdisturb" => "Nicht Stören",
  205. #--
  206. "state" => "Sicherheit",
  207. "secstate" => "Device States",
  208. "unlocked" => "Unverschlossen",
  209. "locked" => "Verschlossen",
  210. "unsecured" => "Nicht Gesichert",
  211. "secured" => "Gesichert",
  212. "protected" => "Geschützt",
  213. "guarded" => "Überwacht",
  214. #--
  215. "monday" => ["Montag","Mo"],
  216. "tuesday" => ["Dienstag","Di"],
  217. "wednesday" => ["Mittwoch","Mi"],
  218. "thursday" => ["Donnerstag","Do"],
  219. "friday" => ["Freitag","Fr"],
  220. "saturday" => ["Samstag","Sa"],
  221. "sunday" => ["Sonntag","So"],
  222. #--
  223. "spring" => "Frühling",
  224. "summer" => "Sommer",
  225. "fall" => "Herbst",
  226. "winter" => "Winter"
  227. );
  228. my $yaahm_tt;
  229. #-- default values, need to be overwritten from save file
  230. # first and second parameter
  231. # entries in the default table with no time entry are single-timers
  232. # entries in the default table with only first time are single-timers
  233. # entries in the default table with only second time are single-timer offsets
  234. # entries in the default table with first an second time are two-timer periods
  235. # third parameter
  236. # fourth parameter
  237. my %defaultdailytable = (
  238. "aftermidnight" => [undef,"00:01",undef,undef],
  239. "beforesunrise" => [undef,"01:00",undef,undef],
  240. "sunrise" => [undef,undef,undef,undef],
  241. "aftersunrise" => [undef,"01:00",undef,undef],
  242. "wakeup" => ["06:15",undef,undef,undef],
  243. "morning" => ["08:00",undef,undef,undef],
  244. "noon" => ["13:00",undef,undef,undef],
  245. "afternoon" => ["14:00",undef,undef,undef],
  246. "evening" => ["18:30",undef,undef,undef],
  247. "beforesunset" => [undef,"01:00",undef,undef],
  248. "sunset" => [undef,undef,undef,undef],
  249. "aftersunset" => [undef,"01:00",undef,undef],
  250. "sleep" => ["22:30",undef,undef,undef],
  251. "night" => ["22:00",undef,undef,undef],
  252. "beforemidnight" => [undef,"00:05",undef,undef]);
  253. my %dailytable = ();
  254. sub YAAHM_dsort {
  255. $dailytable{$a}[0] cmp $dailytable{$b}[0]
  256. }
  257. my @weeklytable = (
  258. "monday",
  259. "tuesday",
  260. "wednesday",
  261. "thursday",
  262. "friday",
  263. "saturday",
  264. "sunday");
  265. my %defaultwakeuptable = (
  266. "name" => "",
  267. "action" => "",
  268. "monday" => "06:15",
  269. "tuesday" => "06:15",
  270. "wednesday" => "06:15",
  271. "thursday" => "06:15",
  272. "friday" => "06:15",
  273. "saturday" => "off",
  274. "sunday" => "off");
  275. my %defaultsleeptable = (
  276. "name" => "",
  277. "action" => "",
  278. "monday" => "22:30",
  279. "tuesday" => "22:30",
  280. "wednesday" => "22:30",
  281. "thursday" => "22:30",
  282. "friday" => "23:00",
  283. "saturday" => "23:00",
  284. "sunday" => "22:30");
  285. my @daytype = (
  286. "workday",
  287. "vacation",
  288. "weekend",
  289. "holiday");
  290. my %defaultdayproperties = (
  291. "date" => "",
  292. "weekday" => "",
  293. "daytype" => 0,
  294. "desc" => "",
  295. "season" => "");
  296. my @times = (keys %defaultdailytable);
  297. my @modes = (
  298. "normal","party","absence","donotdisturb");
  299. my @states = (
  300. "unsecured","secured","protected","guarded");
  301. my @seasons = (
  302. "winter","spring","summer","fall");
  303. #-- modes or day types that affect the profile
  304. my @profmode = ("party","absence","donotdisturb");
  305. my @profday = ("vacation","holiday");
  306. #-- color schemes
  307. my @csmode;
  308. my @csmode1 = ("#53f3c7","#8bfa56","#ff9458","#fd5777");
  309. my @csstate;
  310. my @csstate1 = ("#53f3c7","#ff9458","#f554e2","#fd5777");
  311. #-- temporary fix for update purpose
  312. sub YAAHM_restore($$){};
  313. sub YAAHM_setWeeklyTime($){};
  314. #########################################################################################
  315. #
  316. # YAAHM_Initialize
  317. #
  318. # Parameter hash = hash of device addressed
  319. #
  320. #########################################################################################
  321. sub YAAHM_Initialize ($) {
  322. my ($hash) = @_;
  323. $hash->{DefFn} = "YAAHM_Define";
  324. $hash->{SetFn} = "YAAHM_Set";
  325. $hash->{GetFn} = "YAAHM_Get";
  326. $hash->{UndefFn} = "YAAHM_Undef";
  327. $hash->{AttrFn} = "YAAHM_Attr";
  328. my $attst = "linkname publicroom hiddenroom lockstate:locked,unlocked simulation:0,1 ".
  329. "modecolor0 modecolor1 modecolor2 modecolor3 statecolor0 statecolor1 statecolor2 statecolor3 ".
  330. "timeHelper modeHelper modeAuto:0,1 stateDevices:textField-long stateInterval noicons:0,1 stateWarning stateHelper stateAuto:0,1 ".
  331. "holidayDevices:textField-long vacationDevices:textField-long specialDevices:textField-long";
  332. $hash->{AttrList} = $attst;
  333. if( !defined($yaahm_tt) ){
  334. #-- in any attribute redefinition readjust language
  335. my $lang = AttrVal("global","language","EN");
  336. if( $lang eq "DE"){
  337. $yaahm_tt = \%yaahm_transtable_DE;
  338. }else{
  339. $yaahm_tt = \%yaahm_transtable_EN;
  340. }
  341. }
  342. $yaahmlinkname = $yaahm_tt->{"profiles"};
  343. #-- default colors
  344. @csmode = @csmode1;
  345. @csstate = @csstate1;
  346. $data{FWEXT}{YAAHMx}{LINK} = "?room=".$yaahmhiddenroom;
  347. $data{FWEXT}{YAAHMx}{NAME} = $yaahmlinkname;
  348. $data{FWEXT}{"/YAAHM_timewidget"}{FUNC} = "YAAHM_timewidget";
  349. $data{FWEXT}{"/YAAHM_timewidget"}{FORKABLE} = 0;
  350. return undef;
  351. }
  352. #########################################################################################
  353. #
  354. # YAAHM_Define - Implements DefFn function
  355. #
  356. # Parameter hash = hash of device addressed, def = definition string
  357. #
  358. #########################################################################################
  359. sub YAAHM_Define ($$) {
  360. my ($hash, $def) = @_;
  361. my $now = time();
  362. my $name = $hash->{NAME};
  363. my $TYPE = $hash->{TYPE};
  364. $hash->{VERSION} = $yaahmversion;
  365. $yaahmname = $name;
  366. #-- readjust language
  367. my $lang = AttrVal("global","language","EN");
  368. if( $lang eq "DE"){
  369. $yaahm_tt = \%yaahm_transtable_DE;
  370. }else{
  371. $yaahm_tt = \%yaahm_transtable_EN;
  372. }
  373. #-- default colors
  374. @csmode = @csmode1;
  375. @csstate = @csstate1;
  376. # NOTIFYDEV
  377. my $NOTIFYDEV = "global,$name";
  378. unless ( defined( $hash->{NOTIFYDEV} ) && $hash->{NOTIFYDEV} eq $NOTIFYDEV )
  379. {
  380. $hash->{NOTIFYDEV} = $NOTIFYDEV;
  381. #$changed = 1;
  382. }
  383. readingsSingleUpdate( $hash, "state", "Initialized", 1 );
  384. $yaahmlinkname = defined($attr{$name}{"linkname"}) ? $attr{$name}{"linkname"} : $yaahmlinkname;
  385. $yaahmhiddenroom = defined($attr{$name}{"hiddenroom"}) ? $attr{$name}{"hiddenroom"} : $yaahmhiddenroom;
  386. $data{FWEXT}{YAAHMx}{LINK} = "?room=".$yaahmhiddenroom;
  387. $data{FWEXT}{YAAHMx}{NAME} = $yaahmlinkname;
  388. $attr{$name}{"room"} = $yaahmhiddenroom;
  389. my $date = YAAHM_restore($hash,0);
  390. #-- data seems to be ok, restore
  391. if( defined($date) ){
  392. YAAHM_restore($hash,1);
  393. Log3 $name,1,"[YAAHM_Define] data hash restored from save file with date $date";
  394. #-- intialization
  395. }else{
  396. Log3 $name,1,"[YAAHM_Define] data hash is initialized";
  397. #-- clone daily default profile
  398. $hash->{DATA}{"DT"} = {%defaultdailytable};
  399. #-- clone weekly default profile
  400. $hash->{DATA}{"WT"} = ();
  401. push(@{$hash->{DATA}{"WT"}},{%defaultwakeuptable});
  402. $hash->{DATA}{"WT"}[0]{"name"} = $yaahm_tt->{"wakeup"};
  403. push(@{$hash->{DATA}{"WT"}},{%defaultsleeptable});
  404. $hash->{DATA}{"WT"}[1]{"name"} = $yaahm_tt->{"sleep"};
  405. #-- clone days for today and tomorrow
  406. $hash->{DATA}{"DD"} = ();
  407. push(@{$hash->{DATA}{"DD"}},{%defaultdayproperties});
  408. push(@{$hash->{DATA}{"DD"}},{%defaultdayproperties});
  409. }
  410. #-- determine Astro device
  411. if( !exists($modules{Astro}{defptr}) ){
  412. Log3 $name,1,"[YAAHM] does not find an Astro device, loading module Astro separately";
  413. require "95_Astro.pm";
  414. }else{
  415. my @keys = sort keys %{$modules{Astro}{defptr}};
  416. Log3 $name,1,"[YAAHM] finds ".int(@keys)." Astro devices, module not loaded separately";
  417. }
  418. #--
  419. $modules{YAAHM}{defptr}{$name} = $hash;
  420. RemoveInternalTimer($hash);
  421. InternalTimer ($now + 5, 'YAAHM_CreateEntry', $hash, 0);
  422. return;
  423. }
  424. #########################################################################################
  425. #
  426. # YAAHM_Undef - Implements Undef function
  427. #
  428. # Parameter hash = hash of device addressed, def = definition string
  429. #
  430. #########################################################################################
  431. sub YAAHM_Undef ($$) {
  432. my ($hash,$arg) = @_;
  433. my $name = $hash->{NAME};
  434. RemoveInternalTimer($hash);
  435. delete $data{FWEXT}{YAAHMx};
  436. if (defined $defs{$name."_weblink"}) {
  437. FW_fC("delete ".$name."_weblink");
  438. Log3 $hash, 3, "[".$name. " V".$yaahmversion."]"." Weblink ".$name."_weblink deleted";
  439. }
  440. if (defined $defs{$name."_shortlink"}) {
  441. FW_fC("delete ".$name."_shortlink");
  442. Log3 $hash, 3, "[".$name. " V".$yaahmversion."]"." Weblink ".$name."_shortlink deleted";
  443. }
  444. delete($modules{YAAHM}{defptr});
  445. return undef;
  446. }
  447. #########################################################################################
  448. #
  449. # YAAHM_Attr - Implements Attr function
  450. #
  451. # Parameter hash = hash of device addressed, ???
  452. #
  453. #########################################################################################
  454. sub YAAHM_Attr($$$) {
  455. my ($cmd, $name, $attrName, $attrVal) = @_;
  456. my $hash = $defs{"$name"};
  457. #-- in any attribute redefinition readjust language
  458. my $lang = AttrVal("global","language","EN");
  459. if( $lang eq "DE"){
  460. $yaahm_tt = \%yaahm_transtable_DE;
  461. }else{
  462. $yaahm_tt = \%yaahm_transtable_EN;
  463. }
  464. #---------------------------------------
  465. if ( $attrName eq "timeHelper" ) {
  466. my $dh = (defined($attr{$name}{"timeHelper"})) ? $attr{$name}{"timeHelper"} : undef;
  467. #-- remove this function from all entries
  468. if( $cmd eq "del" ){
  469. foreach my $key (keys %defaultdailytable){
  470. my $xval = $hash->{DATA}{"DT"}{$key}[2];
  471. if( $xval =~ /^{$dh/){
  472. my @cmds = split(',',$xval);
  473. shift(@cmds);
  474. $xval = join(',',@cmds);
  475. $hash->{DATA}{"DT"}{$key}[2] = $xval;
  476. }
  477. }
  478. }
  479. #---------------------------------------
  480. }elsif ( ($cmd eq "set") && ($attrName =~ /modecolor(\d)/) ) {
  481. my $ci = $1;
  482. if( $ci >= 0 && $ci <= 3 ){
  483. $csmode[$ci] = $attrVal;
  484. }
  485. #---------------------------------------
  486. }elsif ( ($cmd eq "del") && ($attrName =~ /modecolor(\d)/) ) {
  487. my $ci = $1;
  488. if( $ci >= 0 && $ci <= 3 ){
  489. $csmode[$ci] = $csmode1[$ci];
  490. }
  491. #---------------------------------------
  492. }elsif ( ($cmd eq "set") && ($attrName =~ /statecolor(\d)/) ) {
  493. my $ci = $1;
  494. if( $ci >= 0 && $ci <= 3 ){
  495. $csstate[$ci] = $attrVal;
  496. }
  497. #---------------------------------------
  498. }elsif ( ($cmd eq "del") && ($attrName =~ /statecolor(\d)/) ) {
  499. my $ci = $1;
  500. if( $ci >= 0 && $ci <= 3 ){
  501. $csstate[$ci] = $csstate1[$ci];
  502. }
  503. #---------------------------------------
  504. }elsif ( ($cmd eq "set") && ($attrName eq "linkname") ) {
  505. $yaahmlinkname = $attrVal;
  506. $data{FWEXT}{YAAHMx}{NAME} = $yaahmlinkname;
  507. #---------------------------------------
  508. }elsif ( ($cmd eq "set") && ($attrName eq "publicroom") ) {
  509. $yaahmpublicroom = $attrVal;
  510. FW_fC("attr ".$name."_shortlink room ".$yaahmpublicroom);
  511. #---------------------------------------
  512. }elsif ( ($cmd eq "set") && ($attrName eq "hiddenroom") ){
  513. #-- remove old hiddenroom from FHEMWEB instances
  514. foreach my $dn (sort keys %defs) {
  515. if ($defs{$dn}{TYPE} eq "FHEMWEB" && $defs{$dn}{NAME} !~ /FHEMWEB:/) {
  516. my $hr = AttrVal($defs{$dn}{NAME}, "hiddenroom", "");
  517. $hr =~ s/$yaahmhiddenroom//;
  518. $hr =~ s/,,//;
  519. $hr =~ s/,$//;
  520. FW_fC("attr ".$defs{$dn}{NAME}." hiddenroom ".$hr);
  521. }
  522. }
  523. #-- new value
  524. $yaahmhiddenroom = $attrVal;
  525. $data{FWEXT}{YAAHMx}{LINK} = "?room=".$yaahmhiddenroom;
  526. FW_fC("attr ".$name."_weblink room ".$yaahmhiddenroom);
  527. #-- place into FHEMWEB instances
  528. foreach my $dn (sort keys %defs) {
  529. if ($defs{$dn}{TYPE} eq "FHEMWEB" && $defs{$dn}{NAME} !~ /FHEMWEB:/) {
  530. my $hr = AttrVal($defs{$dn}{NAME}, "hiddenroom", "");
  531. if (index($hr,$yaahmhiddenroom) == -1){
  532. if ($hr eq "") {
  533. FW_fC("attr ".$defs{$dn}{NAME}." hiddenroom ".$yaahmhiddenroom);
  534. }else {
  535. FW_fC("attr ".$defs{$dn}{NAME}." hiddenroom ".$hr.",".$yaahmhiddenroom);
  536. }
  537. Log3 $hash, 3, "[".$name. " V".$yaahmversion."]"." Added hidden room '".$yaahmhiddenroom."' to ".$defs{$dn}{NAME};
  538. }
  539. }
  540. }
  541. #---------------------------------------
  542. }elsif ( ($cmd eq "delete") && ($attrName eq "stateDevices") ) {
  543. fhem("deletereading $name sdev_housestate");
  544. fhem("deletereading $name sec_housestate");
  545. fhem("deletereading $name sym_housestate");
  546. YAAHM_RemoveInternalTimer("check",$hash);
  547. #---------------------------------------
  548. }elsif ( ($cmd eq "set") && ($attrName eq "stateInterval") ) {
  549. my $next = gettimeofday()+AttrVal($name,"stateInterval",60)*60;
  550. YAAHM_RemoveInternalTimer("check",$hash);
  551. YAAHM_InternalTimer("check",$next, "YAAHM_checkstate", $hash, 0);
  552. #---------------------------------------
  553. }elsif ( ($cmd eq "delete") && ($attrName eq "stateInterval") ) {
  554. my $next = gettimeofday()+3600;
  555. YAAHM_RemoveInternalTimer("check",$hash);
  556. YAAHM_InternalTimer("check",$next, "YAAHM_checkstate", $hash, 0);
  557. #---------------------------------------
  558. }elsif ( $attrName eq "holidayDevices" ) {
  559. return "Value for $attrName has invalid format"
  560. unless ( $cmd eq "del" || $attrVal =~ m/^[A-Za-z\d._]+(?:,[A-Za-z\d._]*)*$/ );
  561. #---------------------------------------
  562. }elsif ( $attrName eq "vacationDevices" ) {
  563. return "Value for $attrName has invalid format"
  564. unless ( $cmd eq "del" || $attrVal =~ m/^[A-Za-z\d._]+(?:,[A-Za-z\d._]*)*$/ );
  565. }
  566. return;
  567. }
  568. #########################################################################################
  569. #
  570. # YAAHM_CreateEntry - Puts the YAAHM entry into the FHEM menu
  571. #
  572. # Parameter hash = hash of device addressed
  573. #
  574. #########################################################################################
  575. sub YAAHM_CreateEntry($) {
  576. my ($hash) = @_;
  577. my $name = $hash->{NAME};
  578. $yaahmlinkname = defined($attr{$name}{"linkname"}) ? $attr{$name}{"linkname"} : $yaahmlinkname;
  579. $yaahmpublicroom = defined($attr{$name}{"publicroom"}) ? $attr{$name}{"publicroom"} : $yaahmpublicroom;
  580. $yaahmhiddenroom = defined($attr{$name}{"hiddenroom"}) ? $attr{$name}{"hiddenroom"} : $yaahmhiddenroom;
  581. #-- this is the long YAAHM entry
  582. FW_fC("defmod ".$name."_weblink weblink htmlCode {YAAHM_Longtable(\"".$name."\")}");
  583. Log3 $hash, 3, "[".$name. " V".$yaahmversion."]"." Weblink ".$name."_weblink created";
  584. FW_fC("attr ".$name."_weblink room ".$yaahmhiddenroom)
  585. if(!defined($attr{$name."_weblink"}{"room"}));
  586. #-- this is the short YAAHM entry
  587. FW_fC("defmod ".$name."_shortlink weblink htmlCode {YAAHM_Shorttable(\"".$name."\")}");
  588. Log3 $hash, 3, "[".$name. " V".$yaahmversion."]"." Weblink ".$name."_shortlink created";
  589. FW_fC("attr ".$name."_shortlink room ".$yaahmpublicroom)
  590. if(!defined($attr{$name."_shortlink"}{"room"}));
  591. foreach my $dn (sort keys %defs) {
  592. if ($defs{$dn}{TYPE} eq "FHEMWEB" && $defs{$dn}{NAME} !~ /FHEMWEB:/) {
  593. my $hr = AttrVal($defs{$dn}{NAME}, "hiddenroom", "");
  594. if (index($hr,$yaahmhiddenroom) == -1){
  595. if ($hr eq "") {
  596. FW_fC("attr ".$defs{$dn}{NAME}." hiddenroom ".$yaahmhiddenroom);
  597. }else {
  598. FW_fC("attr ".$defs{$dn}{NAME}." hiddenroom ".$hr.",".$yaahmhiddenroom);
  599. }
  600. Log3 $hash, 3, "[".$name. " V".$yaahmversion."]"." Added hidden room '".$yaahmhiddenroom."' to ".$defs{$dn}{NAME};
  601. }
  602. }
  603. }
  604. #-- Start updater
  605. InternalTimer(gettimeofday()+ 3, "YAAHM_updater",$hash,0);
  606. YAAHM_InternalTimer("check",time()+ 5, "YAAHM_checkstate", $hash, 0);
  607. }
  608. #########################################################################################
  609. #
  610. # YAAHM_Set - Implements the Set function
  611. #
  612. # Parameter hash = hash of device addressed
  613. #
  614. #########################################################################################
  615. sub YAAHM_Set($@) {
  616. my ( $hash, $name, $cmd, @args ) = @_;
  617. my $imax;
  618. my $if;
  619. my $msg;
  620. my $exec = ( defined($attr{$name}{"simulation"})&&$attr{$name}{"simulation"}==1 ) ? 0 : 1;
  621. #-----------------------------------------------------------
  622. if ( $cmd =~ /^manualnext.*/ ) {
  623. #--timer address
  624. if( $args[0] =~ /^\d+/ ) {
  625. #-- check if valid
  626. if( $args[0] >= int(@{$hash->{DATA}{"WT"}}) ){
  627. $msg = "Error, timer number ".$args[0]." does not exist, number must be smaller than ".int( @{$hash->{DATA}{"WT"}});
  628. Log3 $name,1,"[YAAHM_Set] ".$msg;
  629. return $msg;
  630. }
  631. $cmd = "next_".$args[0];
  632. }else{
  633. my $if = undef;
  634. for( my $i=0;$i<int(@{$hash->{DATA}{"WT"}});$i++){
  635. $if = $i
  636. if ($hash->{DATA}{"WT"}[$i]{"name"} eq $args[0] );
  637. };
  638. #-- check if valid
  639. if( !defined($if) ){
  640. $msg = "Error: timer name ".$args[0]." not found";
  641. Log3 $name,1,"[YAAHM_Set] ".$msg;
  642. return $msg;
  643. }
  644. $cmd = "next_".$if;
  645. }
  646. return YAAHM_nextWeeklyTime($name,$cmd,$args[1],$exec);
  647. #-----------------------------------------------------------
  648. }elsif ( $cmd =~ /^checkstate.*/ ) {
  649. YAAHM_InternalTimer("check",time()+ $args[0], "YAAHM_checkstate", $hash, 0);
  650. #-----------------------------------------------------------
  651. }elsif ( $cmd =~ /^correctstate.*/ ) {
  652. YAAHM_correctstate($hash);
  653. #-----------------------------------------------------------
  654. }elsif ( $cmd =~ /^time.*/ ) {
  655. return YAAHM_time($name,$args[0],$exec);
  656. #-----------------------------------------------------------
  657. }elsif ( $cmd =~ /^mode.*/ ) {
  658. return YAAHM_mode($name,$args[0],$exec);
  659. #-----------------------------------------------------------
  660. }elsif ( $cmd =~ /^state.*/ ) {
  661. return YAAHM_state($name,$args[0],$exec);
  662. #-----------------------------------------------------------
  663. }elsif ( $cmd =~ /^lock(ed)?$/ ) {
  664. readingsSingleUpdate( $hash, "lockstate", "locked", 0 );
  665. return;
  666. #-----------------------------------------------------------
  667. } elsif ( $cmd =~ /^unlock(ed)?$/ ) {
  668. readingsSingleUpdate( $hash, "lockstate", "unlocked", 0 );
  669. return;
  670. #-----------------------------------------------------------
  671. } elsif ( $cmd =~ /^save/ ) {
  672. return YAAHM_save($hash);
  673. #-----------------------------------------------------------
  674. } elsif ( $cmd =~ /^restore/ ) {
  675. return YAAHM_restore($hash,1);
  676. #-----------------------------------------------------------
  677. } elsif ( $cmd =~ /^initialize/ ) {
  678. $firstcall = 1;
  679. YAAHM_updater($hash);
  680. YAAHM_InternalTimer("check",time()+ 5, "YAAHM_checkstate", $hash, 0);
  681. #-----------------------------------------------------------
  682. } elsif ( $cmd eq "createWeekly" ){
  683. return "[YAAHM] missing name for new weekly profile"
  684. if( !defined($args[0]) );
  685. #-- find index
  686. $imax = int(@{$hash->{DATA}{"WT"}});
  687. $if= undef;
  688. for( my $j=0;$j<$imax;$j++){
  689. if($hash->{DATA}{"WT"}[$j]{"name"} eq $args[0]){
  690. $if = $j;
  691. last;
  692. }
  693. }
  694. return "[YAAHM] name $args[0] for weekly profile to be created is already in use"
  695. if( defined($if) );
  696. #-- clone wakeuptable
  697. push(@{$hash->{DATA}{"WT"}},{%defaultwakeuptable});
  698. $hash->{DATA}{"WT"}[$imax]{"name"} = $args[0];
  699. #-- save everything
  700. YAAHM_save($hash);
  701. fhem("save");
  702. return "[YAAHM] weekly profile $args[0] created successfully";
  703. #-----------------------------------------------------------
  704. } elsif ( $cmd eq "deleteWeekly" ){
  705. return "[YAAHM] missing name for weekly profile to be deleted"
  706. if( !defined($args[0]) );
  707. return "[YAAHM] Default weekly profile cannot be deleted"
  708. if( ($args[0] eq $yaahm_tt->{"wakeup"}) || ($args[0] eq $yaahm_tt->{"sleep"}) );
  709. #-- find index
  710. $imax = int(@{$hash->{DATA}{"WT"}});
  711. $if= undef;
  712. for( my $j=2;$j<$imax;$j++){
  713. if($hash->{DATA}{"WT"}[$j]{"name"} eq $args[0]){
  714. $if = $j;
  715. last;
  716. }
  717. }
  718. return "[YAAHM] name $args[0] for weekly profile to be deleted is not known"
  719. if( !defined($if) );
  720. splice(@{$hash->{DATA}{"WT"}},$if,1);
  721. #-- delete timer
  722. fhem("delete ".$name.".wtimer_".$if.".IF");
  723. #-- delete readings
  724. for( my $j=$if;$j<$imax-1;$j++){
  725. $hash->{READINGS}{"ring_".$j}{VAL} = $hash->{READINGS}{"ring_".($j+1)}{VAL};
  726. $hash->{READINGS}{"ring_".$j."_1"}{VAL} = $hash->{READINGS}{"ring_".($j+1)."_1"}{VAL};
  727. $hash->{READINGS}{"next_".$j}{VAL} = $hash->{READINGS}{"next_".($j+1)}{VAL};
  728. $hash->{READINGS}{"today_".$j}{VAL} = $hash->{READINGS}{"today_".($j+1)}{VAL};
  729. $hash->{READINGS}{"today_".$j."_e"}{VAL} = $hash->{READINGS}{"today_".($j+1)."_e"}{VAL};
  730. $hash->{READINGS}{"tomorrow_".$j}{VAL} = $hash->{READINGS}{"tomorrow_".($j+1)}{VAL};
  731. $hash->{READINGS}{"tomorrow_".$j."_e"}{VAL} = $hash->{READINGS}{"tomorrow_".($j+1)."_e"}{VAL};
  732. $hash->{READINGS}{"tr_wake_".$j}{VAL} = $hash->{READINGS}{"tr_wake".($j+1)}{VAL};
  733. }
  734. fhem("deletereading ".$name." ring_".($imax-1));
  735. fhem("deletereading ".$name." ring_".($imax-1)."_1");
  736. fhem("deletereading ".$name." next_".($imax-1));
  737. fhem("deletereading ".$name." today_".($imax-1));
  738. fhem("deletereading ".$name." today_".($imax-1)."_e");
  739. fhem("deletereading ".$name." tomorrow_".($imax-1));
  740. fhem("deletereading ".$name." tomorrow_".($imax-1)."_e");
  741. fhem("deletereading ".$name." tr_wake_".($imax-1));
  742. #-- save everything
  743. YAAHM_save($hash);
  744. fhem("save");
  745. return "[YAAHM] weekly profile $args[0] deleted successfully";
  746. #-----------------------------------------------------------
  747. } else {
  748. my $str = "";
  749. return "[YAAHM] Unknown argument " . $cmd . ", choose one of".
  750. " manualnext time:".join(',',@times)." mode:".join(',',@modes).
  751. " state:".join(',',@states)." locked:noArg unlocked:noArg save:noArg correctstate:noArg checkstate:0,5,10 restore:noArg initialize:noArg createWeekly deleteWeekly";
  752. }
  753. }
  754. #########################################################################################
  755. #
  756. # YAAHM_Get - Implements the Get function
  757. #
  758. # Parameter hash = hash of device addressed
  759. #
  760. #########################################################################################
  761. sub YAAHM_Get($@) {
  762. my ($hash, @args) = @_;
  763. my $res = "";
  764. my $msg;
  765. my $name = $args[0];
  766. my $arg = (defined($args[1]) ? $args[1] : "");
  767. if ($arg eq "version") {
  768. return "YAAHM.version => $yaahmversion";
  769. }elsif ($arg eq "test") {
  770. YAAHM_testWeeklyTime($hash);
  771. return "ok";
  772. }elsif ( $arg eq "next" || $arg eq "sayNext" ){
  773. my $if;
  774. #--timer address
  775. if( $args[2] =~ /^\d+/ ) {
  776. #-- check if valid
  777. if( $args[2] >= int(@{$hash->{DATA}{"WT"}}) ){
  778. $msg = "Error, timer number ".$args[2]." does not exist, number musst be smaller than ".int( @{$hash->{DATA}{"WT"}});
  779. Log3 $name,1,"[YAAHM_Get] ".$msg;
  780. return $msg;
  781. }
  782. $if=$args[2];
  783. }else{
  784. $if = undef;
  785. for( my $i=0;$i<int(@{$hash->{DATA}{"WT"}});$i++){
  786. $if = $i
  787. if ($hash->{DATA}{"WT"}[$i]{"name"} eq $args[1] );
  788. };
  789. #-- check if valid
  790. if( !defined($if) ){
  791. $msg = "Error: timer name ".$args[2]." not found";
  792. Log3 $name,1,"[YAAHM_Get] ".$msg;
  793. return $msg;
  794. }
  795. }
  796. if( $arg eq "next" ){
  797. return YAAHM_sayWeeklyTime($hash,$if,0);
  798. }else{
  799. return YAAHM_sayWeeklyTime($hash,$if,1);
  800. }
  801. }elsif ($arg eq "template") {
  802. $res = "sub HouseTimeHelper(\@){\n".
  803. " my (\$event,\$param1,\$param2) = \@_;\n\n".
  804. " Log 1,\"[HouseTimeHelper] event=\$event\";\n\n".
  805. " my \$time = ReadingsVal(\"".$name."\",\"housetime\",\"\");\n".
  806. " my \$phase = ReadingsVal(\"".$name."\",\"housephase\",\"\");\n".
  807. " my \$state = ReadingsVal(\"".$name."\",\"housestate\",\"\");\n".
  808. " my \$party = (ReadingsVal(\"".$name."\",\"housemode\",\"\") eq \"party\") ? 1 : 0;\n".
  809. " my \$absence = (ReadingsVal(\"".$name."\",\"housemode\",\"\") eq \"absence\") ? 1 : 0;\n".
  810. " my \$dndist = (ReadingsVal(\"".$name."\",\"housemode\",\"\") eq \"donotdisturb\") ? 1 : 0;\n".
  811. " my \$todaytype = ReadingsVal(\"".$name."\",\"tr_todayType\",\"\");\n".
  812. " my \$todaydesc = ReadingsVal(\"".$name."\",\"todayDesc\",\"\");\n".
  813. " my \$tomorrowtype = ReadingsVal(\"".$name."\",\"tr_tomorrowType\",\"\");\n".
  814. " my \$tomorrowdesc = ReadingsVal(\"".$name."\",\"tomorrowDesc\",\"\");\n";
  815. #-- iterate through table
  816. foreach my $key (sort YAAHM_dsort keys %dailytable){
  817. $res .= " #---------------------------------------------------------------------\n";
  818. my $if = ($key eq "aftermidnight") ? "if" : "}elsif";
  819. $res .= " ".$if."( \$event eq \"".$key."\" ){\n\n";
  820. }
  821. $res .= " }\n}\n";
  822. $res .= "sub HouseStateHelper(\@){\n".
  823. " my (\$event,\$param1,\$param2) = \@_;\n\n".
  824. " Log 1,\"[HouseStateHelper] event=\$event\";\n\n".
  825. " my \$time = ReadingsVal(\"".$name."\",\"housetime\",\"\");\n".
  826. " my \$phase = ReadingsVal(\"".$name."\",\"housephase\",\"\");\n".
  827. " my \$state = ReadingsVal(\"".$name."\",\"housestate\",\"\");\n".
  828. " my \$party = (ReadingsVal(\"".$name."\",\"housemode\",\"\") eq \"party\") ? 1 : 0;\n".
  829. " my \$absence = (ReadingsVal(\"".$name."\",\"housemode\",\"\") eq \"absence\") ? 1 : 0;\n".
  830. " my \$dndist = (ReadingsVal(\"".$name."\",\"housemode\",\"\") eq \"donotdisturb\") ? 1 : 0;\n";
  831. #-- iterate through table
  832. for( my $i=0;$i<int(@states);$i++) {
  833. $res .= " #---------------------------------------------------------------------\n";
  834. my $if = ($i == 0) ? "if" : "}elsif";
  835. $res .= " ".$if."( \$event eq \"".$states[$i]."\" ){\n\n";
  836. }
  837. $res .= " }\n}\n";
  838. return $res;
  839. } else {
  840. $res = "0,1";
  841. for(my $i = 2; $i<int( @{$hash->{DATA}{"WT"}});$i++){
  842. $res .= ",".$i;
  843. }
  844. return "Unknown argument $arg choose one of next:".$res." sayNext:".$res." version:noArg template:noArg";
  845. }
  846. }
  847. #########################################################################################
  848. #
  849. # YAAHM_save
  850. #
  851. # Parameter hash = hash of the YAAHM device
  852. #
  853. #########################################################################################
  854. sub YAAHM_save($) {
  855. my ($hash) = @_;
  856. my $date = TimeNow();
  857. $hash->{DATA}{"savedate"} = $date;
  858. readingsSingleUpdate( $hash, "savedate", $date, 1 );
  859. my $jhash0 = toJSON($hash->{DATA});
  860. my $error = FileWrite("YAAHMFILE",$jhash0);
  861. #Log 1,"[YAAHM_save] error=$error";
  862. return;
  863. }
  864. #########################################################################################
  865. #
  866. # YAAHM_restore
  867. #
  868. # Parameter hash = hash of the YAAHM device
  869. #
  870. #########################################################################################
  871. sub YAAHM_restore($$) {
  872. my ($hash,$doit) = @_;
  873. my $name = $hash->{NAME};
  874. my ($error,$jhash0) = FileRead("YAAHMFILE");
  875. if( defined($error) && $error ne "" ){
  876. Log3 $name,1,"[YAAHM_restore] read error=$error";
  877. return undef;
  878. }
  879. my $json = JSON->new->utf8;
  880. my $jhash1 = eval{ $json->decode( $jhash0 ) };
  881. my $date = $jhash1->{"savedate"};
  882. #-- just for the first time, reading an old savefile
  883. $date = localtime(time)
  884. if( !defined($date));
  885. readingsSingleUpdate( $hash, "savedate", $date, 0 );
  886. if( $doit==1 ){
  887. $hash->{DATA} = {%{$jhash1}};
  888. Log3 $name,5,"[YAAHM_restore] Data hash restored from save file with date ".$date;
  889. return 1;
  890. }else{
  891. return $date;
  892. }
  893. }
  894. #########################################################################################
  895. #
  896. # YAAHM_setParm - Receives parameter values from the javascript FE
  897. #
  898. # Parameter name = name of the YAAHM device
  899. #
  900. #########################################################################################
  901. sub YAAHM_setParm($@) {
  902. my ($name, @a) = @_;
  903. my $hash = $defs{$name};
  904. my $cmd = $a[0];
  905. my $key = $a[1];
  906. my $msg = "";
  907. my $val;
  908. #-- daily profile
  909. # start, end/offset, execution, active in mode / daytype
  910. if ($cmd eq "dt") {
  911. for( my $i=1;$i<5;$i++){
  912. $val = $a[$i+1];
  913. if( ($val eq "undef")||($val eq "") ){
  914. $val = undef;
  915. }elsif( ($i<3) && ($val !~ /\d?\d:\d\d/)){
  916. $msg = "wrong time specification $val for key $key, must be hh:mm";
  917. Log 1,"[YAAHM_setParm] ".$msg;
  918. $val = "00:00";
  919. }elsif( $i<3 ){
  920. my ($hour,$min) = split(':',$val);
  921. if( $hour>23 || $min>59 ){
  922. $msg = "wrong time specification $val for key $key > 23:59";
  923. Log 1,"[YAAHM_setParm] ".$msg;
  924. $val = "00:00";
  925. }
  926. }
  927. $hash->{DATA}{"DT"}{$key}[$i-1]=$val;
  928. }
  929. return $msg;
  930. #-- weekly profile
  931. }elsif ($cmd eq "wt") {
  932. #-- action
  933. $hash->{DATA}{"WT"}[$a[1]]{"action"} = $a[2];
  934. #-- next time
  935. $val = $a[3];
  936. if( ($val eq "undef")||($val eq "") ){
  937. $val = undef;
  938. }elsif( $val =~/^off/ ){
  939. #-- ok
  940. }elsif( $val =~ /\d?\d:\d\d/ ){
  941. #-- ok
  942. my ($hour,$min) = split(':',$val);
  943. if( $hour>23 || $min>59 ){
  944. $msg = "wrong time specification next=$val for weekly timer > 23:59".$a[1];
  945. Log 1,"[YAAHM_setParm] ".$msg;
  946. $val = "off";
  947. }
  948. }else{
  949. $msg = "wrong time specification next=$val for weekly timer ".$a[1].", must be hh:mm of 'off'";
  950. Log 1,"[YAAHM_setParm] ".$msg;
  951. $val = "off";
  952. }
  953. #-- next waketime
  954. $hash->{DATA}{"WT"}[$a[1]]{"next"} = $val;
  955. #-- activity party/absence
  956. $hash->{DATA}{"WT"}[$a[1]]{"acti_m"} = $a[4];
  957. #-- activity vacation/holiday
  958. $hash->{DATA}{"WT"}[$a[1]]{"acti_d"} = $a[5];
  959. #-- weekdays
  960. for( my $i=0;$i<7;$i++){
  961. $val = $a[$i+6];
  962. if( ($val eq "undef")||($val eq "") ){
  963. $val = undef;
  964. }elsif( $val =~/^off/ ){
  965. #-- ok
  966. }elsif( $val =~ /\d?\d:\d\d/ ){
  967. #-- ok
  968. my ($hour,$min) = split(':',$val);
  969. if( $hour>23 || $min>59 ){
  970. $msg = "wrong time specification $val for weekly timer > 23:59 ".$a[1];
  971. Log 1,"[YAAHM_setParm] ".$msg;
  972. $val = "off";
  973. }
  974. }else{
  975. $msg = "wrong time specification $val for weekly timer ".$a[1].", must be hh:mm or 'off'";
  976. Log 1,"[YAAHM_setParm] ".$msg;
  977. $val = "off";
  978. }
  979. $hash->{DATA}{"WT"}[$a[1]]{$weeklytable[$i]} = $val;
  980. }
  981. return $msg;
  982. }
  983. }
  984. #########################################################################################
  985. #
  986. # YAAHM_time - Change the house time (aftermidnight .. beforemidnight)
  987. #
  988. # Parameter name = name of the YAAHM device
  989. #
  990. # Careful: $exec=0 is needed in case we want to prevent execution of helper here
  991. #
  992. #########################################################################################
  993. sub YAAHM_time {
  994. my ($name,$targettime,$exec) = @_;
  995. my $hash = $defs{$name};
  996. my $prevtime = defined($hash->{DATA}{"HSM"}{"time"}) ? $hash->{DATA}{"HSM"}{"time"} : "";
  997. my $currmode = defined($hash->{DATA}{"HSM"}{"mode"}) ? $hash->{DATA}{"HSM"}{"mode"} : "normal";
  998. my $currstate = defined($hash->{DATA}{"HSM"}{"state"}) ? $hash->{DATA}{"HSM"}{"state"} : "unsecured";
  999. my $msg = "";
  1000. #-- local checks
  1001. #-- double change
  1002. #if( $prevtime eq $targettime ){
  1003. # $msg = "Transition skipped, the time event $targettime has been executed already";
  1004. #}
  1005. #-- don't
  1006. if( $msg ne "" ){
  1007. Log3 $name,1,"[YAAHM_time] ".$msg;
  1008. return $msg;
  1009. }
  1010. #-- doit
  1011. my ($sec, $min, $hour, $day, $month, $year, $wday,$yday,$isdst) = localtime(time);
  1012. my $lval = sprintf("%02d%02d",$hour,$min);
  1013. my $mval = $dailytable{"morning"}[0];
  1014. my $nval = $dailytable{"night"}[0];
  1015. my $tval = $dailytable{$targettime}[0];
  1016. $mval =~ s/://;
  1017. $nval =~ s/://;
  1018. $tval =~ s/://;
  1019. #-- targetphase always according to real time, not to command time
  1020. my $targetphase = ( ($lval >= $mval) && ( $nval > $lval ) ) ? "daytime" : "nighttime";
  1021. #-- iterate through table to find next event
  1022. my $nexttime;
  1023. my $sval;
  1024. my $oval="0000";
  1025. foreach my $key (sort YAAHM_dsort keys %dailytable){
  1026. $nexttime = $key;
  1027. $sval = $dailytable{$key}[0];
  1028. next
  1029. if (!defined($sval));
  1030. $sval =~ s/://;
  1031. last
  1032. if ( ($lval <= $sval) && ( $lval > $oval ) );
  1033. $oval = $sval;
  1034. }
  1035. my $ma = defined($attr{$name}{"modeAuto"}) && ($attr{$name}{"modeAuto"} == 1);
  1036. my $sa = defined($attr{$name}{"stateAuto"}) && ($attr{$name}{"stateAuto"} == 1);
  1037. #-- automatically leave party mode at morning time or when going to bed
  1038. if( $currmode eq "party" && $targettime =~ /(morning)|(sleep)/ && $ma ){
  1039. $msg = YAAHM_mode($name,"normal",$exec)."\n";
  1040. $msg .= YAAHM_state($name,"secured",$exec)."\n"
  1041. if( $currstate eq "unsecured" && $targettime eq "sleep" && $sa );
  1042. #-- automatically leave absence mode at wakeup time
  1043. }elsif( $currmode eq "absence" && $targettime =~ /(wakeup)/ && $ma ){
  1044. $msg = YAAHM_mode($name,"normal",$exec)."\n";
  1045. #-- automatically leave donotdisturb mode at any time event
  1046. }elsif( $currmode eq "donotdisturb" && $ma ){
  1047. $msg = YAAHM_mode($name,"normal",$exec)."\n";
  1048. #-- automatically secure the house at night time or when going to bed (if not absence, and if not party)
  1049. }elsif( $currmode eq "normal" && $currstate eq "unsecured" && $targettime =~ /(night)|(sleep)/ && $sa ){
  1050. $msg = YAAHM_state($name,"secured",$exec)."\n";
  1051. }
  1052. $hash->{DATA}{"HSM"}{"time"} = $targettime;
  1053. YAAHM_checkMonthly($hash,'event',$targettime);
  1054. readingsBeginUpdate($hash);
  1055. readingsBulkUpdate($hash,"prev_housetime",$prevtime);
  1056. readingsBulkUpdate($hash,"next_housetime",$nexttime);
  1057. readingsBulkUpdate($hash,"housetime",$targettime);
  1058. readingsBulkUpdate($hash,"tr_housetime",$yaahm_tt->{$targettime});
  1059. readingsBulkUpdate($hash,"housephase",$targetphase);
  1060. readingsBulkUpdate($hash,"tr_housephase",$yaahm_tt->{$targetphase});
  1061. readingsEndUpdate($hash,1);
  1062. #-- helper function not executed, e.g. by call from external timer
  1063. return
  1064. if( !defined($exec) || $exec==0);
  1065. #-- execute the helper function
  1066. my $xval;
  1067. my $ival;
  1068. my $wupn;
  1069. #-- todo here: what should we do, if the timer is NOT enabled and we get up or go to bed anyhow ???
  1070. if( $targettime eq "wakeup" ){
  1071. $wupn = $hash->{DATA}{"WT"}[0]{"name"};
  1072. $ival = (ReadingsVal($name.".wtimer_0.IF","mode","") ne "disabled");
  1073. $xval = $ival ? $hash->{DATA}{"WT"}[0]{"action"} : "";
  1074. $msg .= "Simulation ".$xval." from weekly profile ".$wupn;
  1075. $msg .= " (disabled)"
  1076. if !$ival;
  1077. }elsif( $targettime eq "sleep" ){
  1078. $wupn = $hash->{DATA}{"WT"}[1]{"name"};
  1079. $ival = (ReadingsVal($name.".wtimer_1.IF","mode","") ne "disabled");
  1080. $xval = $ival ? $hash->{DATA}{"WT"}[1]{"action"} : "";
  1081. $msg .= "Simulation ".$xval." from weekly profile ".$wupn;
  1082. $msg .= " (disabled)"
  1083. if !$ival;
  1084. }else{
  1085. $xval = $dailytable{$targettime}[2];
  1086. $msg .= "Simulation ".$xval;
  1087. }
  1088. if( $exec==1 ){
  1089. Log3 $name,1,"[YAAHM_time] ecxecuting $xval";
  1090. fhem($xval);
  1091. }elsif( $exec==0 ){
  1092. Log3 $name,1,"[YAAHM_time] ".$msg;
  1093. return $msg;
  1094. }
  1095. }
  1096. #########################################################################################
  1097. #
  1098. # YAAHM_nextWeeklyTime - set the next weekly time
  1099. #
  1100. # Parameter name = name of device addressed
  1101. #
  1102. #########################################################################################
  1103. sub YAAHM_nextWeeklyTime {
  1104. my ($name,$cmd,$time,$exec) = @_;
  1105. my $hash = $defs{$name};
  1106. my $msg;
  1107. #--determine which timer (duplicate check when coming from set)
  1108. $cmd =~ /.*next_([0-9]+)$/;
  1109. my $i = $1;
  1110. if( $i >= int( @{$hash->{DATA}{"WT"}}) ){
  1111. $msg = "Error, timer number $i does not exist, number musst be smaller than ".int( @{$hash->{DATA}{"WT"}});
  1112. Log3 $name,1,"[YAAHM_nextWeeklyTime] ".$msg;
  1113. return $msg;
  1114. }
  1115. $time = lc($time);
  1116. #-- check value - may be empty
  1117. if( $time eq "" || $time eq "default" ){
  1118. $time = "";
  1119. #-- nontrivial
  1120. }else{
  1121. #-- off=ok, do nothing
  1122. if( $time eq "off"){
  1123. #-- time=ok, check
  1124. }elsif( $time =~ /(\d?\d):(\d\d)(:(\d\d))?/ ){
  1125. if( $1 >= 24 || $2 >= 60){
  1126. $msg = "Error, time specification $time for timer ".$cmd." > 23:59 ";
  1127. Log3 $name,1,"[YAAHM_nextWeeklyTime] ".$msg;
  1128. return $msg;
  1129. }
  1130. $time = sprintf("%02d:\%02d",$1,$2);
  1131. }else{
  1132. $msg = "Error, time specification $time invalid for timer ".$cmd.", must be hh:mm";;
  1133. Log3 $name,1,"[YAAHM_nextWeeklyTime] ".$msg;
  1134. return $msg;
  1135. }
  1136. }
  1137. #-- all logic in setweeklytime
  1138. $hash->{DATA}{"WT"}[$i]{"next"} = $time;
  1139. YAAHM_setWeeklyTime($hash);
  1140. }
  1141. #########################################################################################
  1142. #
  1143. # YAAHM_mode - Change the house mode (normal, party, absence)
  1144. #
  1145. # Parameter name = name of the YAAHM device
  1146. #
  1147. #########################################################################################
  1148. sub YAAHM_mode {
  1149. my ($name,$targetmode,$exec) = @_;
  1150. my $hash = $defs{$name};
  1151. my $prevmode = defined($hash->{DATA}{"HSM"}{"mode"}) ? $hash->{DATA}{"HSM"}{"mode"} : "normal";
  1152. my $currstate = defined($hash->{DATA}{"HSM"}{"state"}) ? $hash->{DATA}{"HSM"}{"state"} : "unsecured";
  1153. my $msg = "";
  1154. my $tr_msg = "";
  1155. #-- local checks
  1156. #-- double change
  1157. if( $prevmode eq $targetmode ){
  1158. $msg = "transition skipped, we are already in $targetmode mode";
  1159. #-- transition into party and absence is only possible from normal mode
  1160. }elsif( $prevmode ne "normal" && $targetmode ne "normal"){
  1161. $msg = "Transition into $targetmode mode is only possible from normal mode";
  1162. $tr_msg = $yaahm_tt->{transition}.' "'.$yaahm_tt->{$targetmode}.'" '.$yaahm_tt->{"onlposfrm"}.' '.$yaahm_tt->{"mode"}.'="'.$yaahm_tt->{"normal"}.'"';
  1163. #-- global checks
  1164. #-- transition into party mode only possible in unlocked state
  1165. }elsif( $targetmode eq "party" && $currstate ne "unsecured" ){
  1166. $msg = "Transition into party mode is only possible from unsecured state";
  1167. $tr_msg = $yaahm_tt->{transition}.' "'.$yaahm_tt->{$targetmode}.'" '.$yaahm_tt->{"onlposfrm"}.' '.$yaahm_tt->{"state"}.'="'.$yaahm_tt->{"unsecured"}.'"';
  1168. }
  1169. #-- don't
  1170. if( $msg ne "" ){
  1171. Log3 $name,1,"[YAAHM_mode] ".$msg;
  1172. readingsSingleUpdate($hash,"tr_errmsg",$tr_msg,1);
  1173. return $msg;
  1174. }
  1175. $hash->{DATA}{"HSM"}{"mode"} = $targetmode;
  1176. readingsBeginUpdate($hash);
  1177. readingsBulkUpdate($hash,"tr_errmsg","");
  1178. readingsBulkUpdate($hash,"prev_housemode",$prevmode);
  1179. readingsBulkUpdate($hash,"housemode",$targetmode);
  1180. readingsBulkUpdate($hash,"tr_housemode",$yaahm_tt->{$targetmode});
  1181. readingsEndUpdate($hash,1);
  1182. #-- doit, if not simulation
  1183. if (defined($attr{$name}{"modeHelper"})){
  1184. if( !defined($exec) || $exec==1 ){
  1185. fhem("{".$attr{$name}{"modeHelper"}."('".$targetmode."')}");
  1186. }else{
  1187. $msg = "Simulation {".$attr{$name}{"modeHelper"}."('".$targetmode."')}";
  1188. Log3 $name,1,"[YAAHM_mode] ".$msg;
  1189. return $msg;
  1190. }
  1191. }
  1192. }
  1193. #########################################################################################
  1194. #
  1195. # YAAHM_state - Change the house state (unscured, secured, guarded)
  1196. #
  1197. # Parameter name = name of the YAAHM device
  1198. #
  1199. #########################################################################################
  1200. sub YAAHM_state {
  1201. my ($name,$targetstate,$exec) = @_;
  1202. my $hash = $defs{$name};
  1203. my $prevstate = defined($hash->{DATA}{"HSM"}{"state"}) ? $hash->{DATA}{"HSM"}{"state"} : "unsecured";
  1204. my $currmode = defined($hash->{DATA}{"HSM"}{"mode"}) ? $hash->{DATA}{"HSM"}{"mode"} : "normal";
  1205. my $msg = "";
  1206. my $tr_msg = "";
  1207. #-- local checks
  1208. #-- double change
  1209. #if( $prevstate eq $targetstate ){
  1210. # $msg = "transition skipped, we are already in $targetstate state";
  1211. #-- global checks
  1212. #-- changing away from unlocked in party mode is not possible
  1213. if( $targetstate ne "unlocked" && $currmode eq "party" ){
  1214. $msg = "Not possible in party mode";
  1215. $tr_msg = $yaahm_tt->{transition}.' "'.$yaahm_tt->{$targetstate}.'" '.$yaahm_tt->{"notposfrm"}.' '.$yaahm_tt->{"mode"}.'="'.$yaahm_tt->{"party"}.'"';
  1216. }
  1217. #-- don't
  1218. if( $msg ne "" ){
  1219. Log3 $name,1,"[YAAHM_state] ".$msg;
  1220. readingsSingleUpdate($hash,"tr_errmsg",$tr_msg,1);
  1221. return $msg;
  1222. }
  1223. $hash->{DATA}{"HSM"}{"state"} = $targetstate;
  1224. readingsBeginUpdate($hash);
  1225. readingsBulkUpdate($hash,"tr_errmsg","");
  1226. readingsBulkUpdate($hash,"prev_housestate",$prevstate);
  1227. readingsBulkUpdate($hash,"housestate",$targetstate);
  1228. readingsBulkUpdate($hash,"tr_housestate",$yaahm_tt->{$targetstate});
  1229. readingsEndUpdate($hash,1);
  1230. #-- doit, if not simulation
  1231. if (defined($attr{$name}{"stateHelper"})){
  1232. if( !defined($exec) || $exec==1 ){
  1233. fhem("{".$attr{$name}{"stateHelper"}."('".$targetstate."')}");
  1234. }else{
  1235. $msg = "Simulation {".$attr{$name}{"stateHelper"}."('".$targetstate."')}";
  1236. Log3 $name,1,"[YAAHM_state] ".$msg;
  1237. return $msg;
  1238. }
  1239. }
  1240. YAAHM_InternalTimer("check",time()+ 30, "YAAHM_checkstate", $hash, 0);
  1241. }
  1242. #########################################################################################
  1243. #
  1244. # YAAHM_checkstate - check state devices
  1245. #
  1246. # Parameter someHash = either internal hash of timer
  1247. # => need to dereference it for getting device hash
  1248. # or device hash
  1249. #
  1250. #########################################################################################
  1251. sub YAAHM_checkstate($) {
  1252. my ($someHash) = @_;
  1253. my $hash;
  1254. if( defined($someHash->{HASH}) ){
  1255. $hash = $someHash->{HASH};
  1256. }else{
  1257. $hash = $someHash;
  1258. }
  1259. my $name = $hash->{NAME};
  1260. my $next;
  1261. $next = gettimeofday()+AttrVal($name,"stateInterval",60)*60;
  1262. YAAHM_RemoveInternalTimer("check",$hash);
  1263. YAAHM_InternalTimer("check",$next, "YAAHM_checkstate", $hash, 0);
  1264. Log3 $name, 5,"[YAAHM_checkstate] on device ".$hash->{NAME}." called";
  1265. my $istate;
  1266. my $cstate = defined($hash->{DATA}{"HSM"}{"state"}) ? $hash->{DATA}{"HSM"}{"state"} : "";
  1267. return undef
  1268. if( !defined($attr{$name}{"stateDevices"}) );
  1269. for($istate=0;$istate<int(@states);$istate++){
  1270. last
  1271. if($states[$istate] eq $cstate);
  1272. }
  1273. my (@devlist,@devl);
  1274. my ($dev,$devs,$devh,);
  1275. my @devf = ();
  1276. my $isf = 0;
  1277. @devlist = split(',',$attr{$name}{"stateDevices"});
  1278. foreach my $devc (@devlist) {
  1279. @devl = split(':',$devc);
  1280. $dev = $devl[0];
  1281. $devs = $devl[$istate+1];
  1282. if( defined($devs) && ($devs ne "") ){
  1283. $devh = Value($dev);
  1284. if( $devs ne $devh ){
  1285. $isf = 1;
  1286. 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'}.
  1287. "</div></td><td style=\"text-align:left;padding:5px\">".$devh."</td></tr>");
  1288. if( defined(AttrVal($name,"stateWarning",undef)) ){
  1289. fhem("{".AttrVal($name,"stateWarning",undef)."($dev,$devs,$devh)}");
  1290. }
  1291. }else{
  1292. 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>");
  1293. }
  1294. }
  1295. }
  1296. readingsBeginUpdate($hash);
  1297. readingsBulkUpdate($hash,"sdev_housestate","<html><table>".join("<br/>",@devf)."</table></html>");
  1298. readingsBulkUpdate($hash,"sec_housestate",(($isf==0)?"secure":"insecure"));
  1299. readingsBulkUpdate($hash,"sym_housestate",(($isf==0)?"<html><div style=\"color:green\">&#x2713;</div></html>":"<html><div style=\"color:red\">&#x274c;</div></html>"));
  1300. readingsEndUpdate($hash,1);
  1301. return undef
  1302. }
  1303. #########################################################################################
  1304. #
  1305. # YAAHM_correctstate - correct state devices
  1306. #
  1307. # Parameter someHash = either internal hash of timer
  1308. # => need to dereference it for getting device hash
  1309. # or device hash
  1310. #
  1311. #########################################################################################
  1312. sub YAAHM_correctstate($) {
  1313. my ($someHash) = @_;
  1314. my $hash;
  1315. if( defined($someHash->{HASH}) ){
  1316. $hash = $someHash->{HASH};
  1317. }else{
  1318. $hash = $someHash;
  1319. }
  1320. my $name = $hash->{NAME};
  1321. Log3 $name, 1,"[YAAHM_correctstate] on device ".$hash->{NAME}." called";
  1322. my $istate;
  1323. my $cstate = defined($hash->{DATA}{"HSM"}{"state"}) ? $hash->{DATA}{"HSM"}{"state"} : "";
  1324. return undef
  1325. if( !defined($attr{$name}{"stateDevices"}) );
  1326. for($istate=0;$istate<int(@states);$istate++){
  1327. last
  1328. if($states[$istate] eq $cstate);
  1329. }
  1330. my (@devlist,@devl);
  1331. my ($dev,$devs,$devh,);
  1332. my @devf = ();
  1333. my $isf = 0;
  1334. @devlist = split(',',$attr{$name}{"stateDevices"});
  1335. foreach my $devc (@devlist) {
  1336. @devl = split(':',$devc);
  1337. $dev = $devl[0];
  1338. $devs = $devl[$istate+1];
  1339. if( defined($devs) && ($devs ne "") ){
  1340. $devh = Value($dev);
  1341. if( $devs ne $devh ){
  1342. Log3 $name, 1,"[YAAHM_correctstate] calling set ".$dev." ".$devs;
  1343. fhem("set ".$dev." ".$devs);
  1344. $isf = 1;
  1345. }
  1346. }
  1347. }
  1348. YAAHM_checkstate($hash)
  1349. if($isf == 1);
  1350. return undef
  1351. }
  1352. #########################################################################################
  1353. #
  1354. # YAAHM_informer - Tell FHEMWEB to inform this page
  1355. #
  1356. # Parameter me = hash of FHEMWEB instance
  1357. #
  1358. #########################################################################################
  1359. sub YAAHM_informer($) {
  1360. my ($me) = @_;
  1361. $me->{inform}{type} = "status";
  1362. $me->{inform}{filter} = "YYY";
  1363. #$me->{inform}{since} = time()-5;
  1364. $me->{inform}{fmt} = "JSON";
  1365. my $filter = $me->{inform}{filter};
  1366. my %h = map { $_ => 1 } devspec2array($filter);
  1367. $h{global} = 1 if( $me->{inform}{addglobal} );
  1368. $h{"#FHEMWEB:$FW_wname"} = 1;
  1369. $me->{inform}{devices} = \%h;
  1370. %FW_visibleDeviceHash = FW_visibleDevices();
  1371. $me->{NTFY_ORDER} = $FW_cname; # else notifyfn won't be called
  1372. %ntfyHash = ();
  1373. }
  1374. #########################################################################################
  1375. #
  1376. # YAAHM_startDayTimer - start the daily timer function
  1377. #
  1378. # Parameter name = name of the YAAHM device
  1379. #
  1380. #########################################################################################
  1381. sub YAAHM_startDayTimer($) {
  1382. my ($name) = @_;
  1383. my $hash = $defs{$name};
  1384. my $res = "defmod $name.dtimer.IF DOIF ";
  1385. my $msg;
  1386. #--cleanup after definition fault
  1387. fhem("deletereading $name t_aftermidnight")
  1388. if( ReadingsVal($name,"t_aftermidnight",undef) );
  1389. fhem("deletereading $name t_aftersunrise")
  1390. if( ReadingsVal($name,"t_aftersunrise",undef) );
  1391. fhem("deletereading $name t_aftersunset")
  1392. if( ReadingsVal($name,"t_aftersunset",undef) );
  1393. fhem("deletereading $name t_beforemidnight")
  1394. if( ReadingsVal($name,"t_beforemidnight",undef) );
  1395. fhem("deletereading $name t_beforesunrise")
  1396. if( ReadingsVal($name,"t_beforesunrise",undef) );
  1397. fhem("deletereading $name t_beforesunset")
  1398. if( ReadingsVal($name,"t_beforesunset",undef) );
  1399. delete $hash->{DATA}{"DT"}{"daytime"};
  1400. delete $hash->{DATA}{"DT"}{"nighttime"};
  1401. #-- TODO check for plausibility
  1402. #--aftermidnight must be >= 00:01
  1403. my $check=$hash->{DATA}{"DT"}{"aftermidnight"}[0];
  1404. $check =~ s/://;
  1405. if( $check <= 0 ){
  1406. Log3 $name,1,"[YAAHM_startDayTimer] aftermidnight is minimum 00:01, used this value";
  1407. $hash->{DATA}{"DT"}{"aftermidnight"}[0] = "00:01";
  1408. }
  1409. #-- Internal timer for night time
  1410. my ($sec, $min, $hour, $day, $month, $year, $wday,$yday,$isdst) = localtime(time);
  1411. my $nval = $hash->{DATA}{"DT"}{"night"}[0];
  1412. if( $nval !~ /\d\d:\d\d/ ){
  1413. $msg = "Error in night time specification";
  1414. Log3 1,$name,"[YAAHM_startDayTimer] ".$msg;
  1415. return $msg;
  1416. }
  1417. my ($hourn,$minn) = split(':',$nval);
  1418. my $deltan = ($hourn-$hour)*3600+($minn-$min)*60-$sec;
  1419. my $delta = $deltan;
  1420. $delta += 86400
  1421. if( $delta<0 );
  1422. YAAHM_RemoveInternalTimer ("nighttime", $hash);
  1423. YAAHM_InternalTimer ("nighttime", gettimeofday()+$delta, "YAAHM_tonight", $hash, 0);
  1424. #-- Internal timer for daytime
  1425. my $mval = $hash->{DATA}{"DT"}{"morning"}[0];
  1426. if( $mval !~ /\d\d:\d\d/ ){
  1427. $msg = "Error in morning time specification";
  1428. Log3 1,$name,"[YAAHM_startDayTimer] ".$msg;
  1429. return $msg;
  1430. }
  1431. ($hourn,$minn) = split(':',$mval);
  1432. my $deltam = ($hourn-$hour)*3600+($minn-$min)*60-$sec;
  1433. $delta = $deltam;
  1434. $delta += 86400
  1435. if( $delta<0 );
  1436. YAAHM_RemoveInternalTimer ("daytime", $hash);
  1437. YAAHM_InternalTimer ("daytime", gettimeofday()+$delta, "YAAHM_today", $hash, 0);
  1438. #-- currently day or night ?
  1439. my $currtime = (($deltam < 0) && ($deltan > 0)) ? "daytime" : "nighttime";
  1440. #-- put data into readings
  1441. readingsBeginUpdate($hash);
  1442. readingsBulkUpdate($hash,"housephase",$currtime);
  1443. readingsBulkUpdate($hash,"tr_housephase",$yaahm_tt->{$currtime});
  1444. #-- compose external timer
  1445. foreach my $key (sort YAAHM_dsort keys %defaultdailytable){
  1446. next if( !defined($hash->{DATA}{"DT"}{$key}[2]) );
  1447. my $f1 = defined($defaultdailytable{$key}[0]);
  1448. my $f2 = defined($defaultdailytable{$key}[1]);
  1449. my $f3 = defined($hash->{DATA}{"DT"}{$key}[2]) && $hash->{DATA}{"DT"}{$key}[2] ne "";
  1450. #-- uh oh, double execution of functions !!! Do this in YAAHM_time, NOT in timer
  1451. #my $xval = "{YAAHM_time('".$name."','".$key."')},".$hash->{DATA}{"DT"}{$key}[2];
  1452. my $xval = "{YAAHM_time('".$name."','".$key."',1)}";
  1453. #-- entries in the default table with no entry are single-timers
  1454. if( !$f1 and !$f2 ){
  1455. $res .= "([[".$name.":s_".$key."]])\n(".$xval.")\nDOELSEIF"
  1456. if( $f3 );
  1457. #-- entries in the default table with only first time are single-timers
  1458. }elsif( $f1 and !$f2 ){
  1459. $res .= "([[".$name.":s_".$key."]])\n(".$xval.")\nDOELSEIF"
  1460. if( $f3 );
  1461. #-- entries in the default table with only second time are single-timer offsets
  1462. }elsif( !$f1 and $f2 ){
  1463. $res .= "([[".$name.":s_".$key."]])\n(".$xval.")\nDOELSEIF"
  1464. if( $f3 );
  1465. #-- entries in the default table with first and second time are two-timer periods
  1466. }elsif( $f1 and $f2 ){
  1467. $res .= "([[".$name.":s_".$key."]-[".$name.":t_".$key."]])\n(".$xval.")\nDOELSEIF"
  1468. if( $f3 );
  1469. #-- something wrong
  1470. }else{
  1471. $msg = "Daily timer $name.dtimer.IF NOT started, something wrong with entry ".$key;
  1472. Log 1,"[YAAHM_startDayTimer] ".$msg;
  1473. return $msg;
  1474. }
  1475. }
  1476. readingsEndUpdate($hash,1);
  1477. #-- take out last DOELSEIF
  1478. $res =~ s/\nDOELSEIF$//;
  1479. fhem($res);
  1480. fhem("attr $name.dtimer.IF do always");
  1481. fhem("set $name.dtimer.IF enable");
  1482. #-- save everything
  1483. YAAHM_save($hash);
  1484. fhem("save");
  1485. return "Daily timer $name.dtimer.IF started";
  1486. }
  1487. #########################################################################################
  1488. #
  1489. # YAAHM_startWeeklyTimer - start the Weekly timer function
  1490. #
  1491. # Parameter name = name of the YAAHM device
  1492. #
  1493. #########################################################################################
  1494. sub YAAHM_startWeeklyTimer($) {
  1495. my ($name) = @_;
  1496. my $hash = $defs{$name};
  1497. my $res;
  1498. my $wupn;
  1499. YAAHM_setWeeklyTime($hash);
  1500. #-- start timer
  1501. for( my $i=0;$i < int( @{$hash->{DATA}{"WT"}} );$i++){
  1502. $wupn = $hash->{DATA}{"WT"}[$i]{"name"};
  1503. $res = "defmod ".$name.".wtimer_".$i.".IF DOIF ([".$name.":ring_".$i."] eq \"off\")\n()\nDOELSEIF\n(([[".$name.":ring_".$i."]])";
  1504. #-- check for activity description
  1505. my $g4a = defined($hash->{DATA}{"WT"}[$i]{"acti_m"}) ? $hash->{DATA}{"WT"}[$i]{"acti_m"} : "";
  1506. my $g4b = defined($hash->{DATA}{"WT"}[$i]{"acti_d"}) ? $hash->{DATA}{"WT"}[$i]{"acti_d"} : "";
  1507. my $v4a = ($g4a ne "") ? "(normal)|(".join(')|(',split(',',$g4a)).")" : "(normal)";
  1508. my $v4b = ($g4b ne "") ? "(workday)|(weekend)|(".join(')|(',split(',',$g4b)).")" : "(workday)|(weekend)";
  1509. $res .= "\nand ([" .$name. ":housemode] =~ \"".$v4a."\")";
  1510. $res .= "\nand ([" .$name. ":todayType] =~ \"".$v4b."\")";
  1511. #-- action - explicitly in timer, not in YAAHM_time
  1512. my $xval = "";
  1513. if( $i==0 ){
  1514. $xval = "{YAAHM_time('".$name."','wakeup',0)},".$hash->{DATA}{"WT"}[$i]{"action"};
  1515. }elsif( $i==1 ){
  1516. $xval = "{YAAHM_time('".$name."','sleep',0)},".$hash->{DATA}{"WT"}[$i]{"action"};
  1517. }else{
  1518. $xval = $hash->{DATA}{"WT"}[$i]{"action"};
  1519. }
  1520. #-- action
  1521. $res .= ")\n(".$xval.")";
  1522. #-- doit
  1523. fhem($res);
  1524. fhem("attr ".$name.".wtimer_".$i.".IF do always");
  1525. fhem("set ".$name.".wtimer_".$i.".IF enable");
  1526. }
  1527. #-- save everything
  1528. YAAHM_save($hash);
  1529. fhem("save");
  1530. return "Weekly timers started";
  1531. }
  1532. #########################################################################################
  1533. #
  1534. # YAAHM_setWeeklyTime - set the Weekly times into readings
  1535. #
  1536. # Parameter hash = hash of device addressed
  1537. #
  1538. #########################################################################################
  1539. sub YAAHM_setWeeklyTime($) {
  1540. my ($hash) = @_;
  1541. my $name = $hash->{NAME};
  1542. #-- weekly profile times
  1543. my ($sg0,$sg1,$sg0mod,$sg1mod,$sg0en,$sg1en,$ring_0,$ring_1,$ng);
  1544. #-- iterate over timers
  1545. for( my $i=0;$i<int( @{$hash->{DATA}{"WT"}} );$i++){
  1546. #-- obtain next time spec => will override all
  1547. $ng = $hash->{DATA}{"WT"}[$i]{ "next" };
  1548. #-- highest priority is a disabled timer - no wakeup at all
  1549. if( ReadingsVal($name.".wtimer_".$i.".IF","mode","") eq "disabled" ){
  1550. $sg0 = "off";
  1551. $sg1 = "off";
  1552. $sg0en = "disabled (timer)";
  1553. $sg1en = "disabled (timer)";
  1554. #-- if the timer is enabled, we'll use its timing values
  1555. }else{
  1556. $sg0 = $hash->{DATA}{"WT"}[$i]{ $weeklytable[$hash->{DATA}{"DD"}[0]{"weekday"}] } ;
  1557. $sg1 = $hash->{DATA}{"WT"}[$i]{ $weeklytable[$hash->{DATA}{"DD"}[1]{"weekday"}] };
  1558. $sg0en = "enabled";
  1559. $sg1en = "enabled";
  1560. #-- next higher priority for "off" is daytype
  1561. my $wupad = $hash->{DATA}{"WT"}[$i]{"acti_d"}.",workday,weekend";
  1562. #-- start with tomorrow
  1563. if( index($wupad, $hash->{DATA}{"DD"}[1]{"daytype"}) == -1 ){
  1564. $sg1mod = "off (".substr(ReadingsVal($name,"tr_tomorrowType",""),0,3).")";
  1565. $sg1en = "disabled (".ReadingsVal($name,"tomorrowType","").")";
  1566. }elsif( ($hash->{DATA}{"DD"}[1]{"vacflag"} == 1 ) && index($wupad,"vacation") == -1 ){
  1567. $sg1mod = "off (".substr($yaahm_tt->{"vacation"},0,3).")";
  1568. $sg1en = "disabled (vacation)";
  1569. }else{
  1570. $sg1mod = $sg1;
  1571. }
  1572. #-- because today we might also have an influence of housemode
  1573. if( index($wupad, $hash->{DATA}{"DD"}[0]{"daytype"}) == -1 ){
  1574. $sg0mod = "off (".substr(ReadingsVal($name,"tr_todayType",""),0,3).")";
  1575. $sg0en = "disabled (".ReadingsVal($name,"todayType","").")";
  1576. }elsif( ($hash->{DATA}{"DD"}[0]{"vacflag"} == 1 ) && index($wupad,"vacation") == -1 ){
  1577. $sg0mod = "off (".substr($yaahm_tt->{"vacation"},0,3).")";
  1578. $sg0en = "disabled (vacation)";
  1579. }else{
  1580. #-- next higher priority for "off" (only today !) is housemode
  1581. my $wupam = $hash->{DATA}{"WT"}[$i]{"acti_m"}.",normal";
  1582. if( index($wupam, ReadingsVal($name,"housemode","")) == -1 ){
  1583. $sg0mod = "off (".substr(ReadingsVal($name,"tr_housemode",""),0,3).")";
  1584. $sg0en = "disabled (".ReadingsVal($name,"housemode","").")";
  1585. }else{
  1586. $sg0mod = $sg0;
  1587. }
  1588. }
  1589. }
  1590. #Log 1,"====> AFTER INITIAL CHECK TIMER $i sg0=$sg0 sg0mod=$sg0mod sg1=$sg1 sg1mod=$sg1mod ng=$ng";
  1591. #-- no "next" time specification
  1592. if( !defined($ng) || $ng eq "" ){
  1593. $ring_0 = $sg0;
  1594. $ring_1 = $sg1;
  1595. #-- highest priority is a "next" time specification
  1596. }else{
  1597. #-- current time
  1598. my ($sec, $min, $hour, $day, $month, $year, $wday,$yday,$isdst) = localtime(time);
  1599. my $lga = sprintf("%02d%02d",$hour,$min);
  1600. #-- today's waketime
  1601. my $tga = $sg0;
  1602. $tga =~ s/://;
  1603. #-- tomorrow's waketime
  1604. my $tgm = $sg1;
  1605. $tgm =~ s/://;
  1606. #-- "next" input
  1607. my $nga = (defined $ng)?$ng:"";
  1608. $nga =~ s/://;
  1609. #-- "next" is the same as todays waketime and todays waketime not over => restore !
  1610. if( ($nga eq $tga) && ($tga > $lga)){
  1611. $ring_0 = $sg0;
  1612. $ring_1 = $sg1;
  1613. $ng = "";
  1614. $hash->{DATA}{"WT"}[$i]{ "next" }="";
  1615. #-- "next" is the same as tomorrows waketime and todays waketime over => restore !
  1616. }elsif( ($nga eq $tgm) && ($tga < $lga)){
  1617. $ring_0 = $sg0;
  1618. $ring_1 = $sg1;
  1619. $ng = "";
  1620. $hash->{DATA}{"WT"}[$i]{ "next" }="";
  1621. #-- "next" is off
  1622. }elsif( $nga eq "off" ){
  1623. #-- today's waketime not over => we mean today
  1624. if( $tga ne "off" && ($tga > $lga)){
  1625. if( $sg0mod !~ /^off/ ){
  1626. $sg0mod = "off (man)";
  1627. $ring_0 = "off";
  1628. $ring_1 = $sg1;
  1629. }
  1630. #-- today's waketime over => we mean tomorrow
  1631. }else{
  1632. if( $sg1mod !~ /^off/ ){
  1633. $sg1mod = "off (man)";
  1634. $ring_0 = $sg0;
  1635. $ring_1 = "$sg1 (off)";
  1636. }
  1637. }
  1638. #-- "next" is nontrivial timespec
  1639. }else{
  1640. #-- "next" after current time => we mean today
  1641. if( $nga > $lga ){
  1642. #-- the same as original waketime => restore ! (do we come here at all ?)
  1643. #if( $ng eq $sg0 ){
  1644. # $sg0mod = $sg0;
  1645. # $ring_0 = $sg0;
  1646. # $ng = "";
  1647. # $hash->{DATA}{"WT"}[$i]{ "next" } = "";
  1648. #-- new manual waketime tomorrow
  1649. #}else{
  1650. $sg0mod = "$ng (man)";
  1651. $ring_0 = $ng;
  1652. #}
  1653. $ring_1 = $sg1;
  1654. #-- "next" before current time => we mean tomorrow
  1655. }else{
  1656. #-- the same as original waketime => restore ! (do we come here at all ?)
  1657. #if( $ng eq $sg1 ){
  1658. # $sg0mod = $sg1;
  1659. # $ring_1 = $sg1;
  1660. # $ng = "";
  1661. # $hash->{DATA}{"WT"}[$i]{ "next" } = "";
  1662. #}else{
  1663. $sg1mod = "$ng (man)";
  1664. $ring_1 = "$sg1 ($ng)";
  1665. #}
  1666. $ring_0 = $sg0;
  1667. }
  1668. }
  1669. }
  1670. $hash->{DATA}{"WT"}[$i]{"ring_0"} = $ring_0;
  1671. $hash->{DATA}{"WT"}[$i]{"ring_1"} = $ring_1;
  1672. $hash->{DATA}{"WT"}[$i]{"ring_0x"} = $sg0mod;
  1673. $hash->{DATA}{"WT"}[$i]{"ring_1x"} = $sg1mod;
  1674. $hash->{DATA}{"WT"}[$i]{"ring_0e"} = $sg0en;
  1675. $hash->{DATA}{"WT"}[$i]{"ring_1e"} = $sg1en;
  1676. #Log 1,"====> AFTER FINAL CHECK TIMER $i sg0=$sg0 sg0mod=$sg0mod sg1=$sg1 sg1mod=$sg1mod ng=$ng";
  1677. #Log 1," ".$hash->{DATA}{"WT"}[$i]{"ring_0x"}." ".$hash->{DATA}{"WT"}[$i]{"ring_1x"};
  1678. #-- notation:
  1679. # today_i is today's waketime of timer i
  1680. # tomorrow_i is tomorrow's waketime of timer i
  1681. # timers have additional conditions for activation according
  1682. # to housemode and daytype, these conditions are checked in the timer device
  1683. # devices and are not part of the table. But we have a reading:
  1684. # today_i_e is a copy of the condition checked in the timer device
  1685. # (housemode and daytype)
  1686. # tomorrow_i_e is not a complete copy of the condition checked in the timer device,
  1687. # (daytype only, because housemode of tomorrow is not known)
  1688. # ring_[i]_1 is tomorrow's ring time of timer i
  1689. readingsBeginUpdate($hash);
  1690. readingsBulkUpdate( $hash, "today_".$i,$sg0 );
  1691. readingsBulkUpdate( $hash, "tomorrow_".$i,$sg1 );
  1692. readingsBulkUpdate( $hash, "today_".$i."_e",$sg0en );
  1693. readingsBulkUpdate( $hash, "tomorrow_".$i."_e",$sg1en );
  1694. readingsBulkUpdate( $hash, "ring_".$i,$ring_0 );
  1695. readingsBulkUpdate( $hash, "ring_".$i."_1",$ring_1 );
  1696. readingsBulkUpdate( $hash, "ring_".$i."x",$sg0mod );
  1697. readingsBulkUpdate( $hash, "ring_".$i."_1x",$sg1mod );
  1698. readingsBulkUpdate( $hash, "next_".$i,$ng );
  1699. readingsEndUpdate($hash,1);
  1700. YAAHM_sayWeeklyTime($hash,$i,0);
  1701. }
  1702. }
  1703. #########################################################################################
  1704. #
  1705. # YAAHM_sayWeeklyTime - say the next weekly time
  1706. #
  1707. # Parameter name = name of device addressed
  1708. #
  1709. #########################################################################################
  1710. sub YAAHM_sayWeeklyTime($$$) {
  1711. my ($hash,$timer,$sp) = @_;
  1712. my $name = $hash->{NAME};
  1713. my ($tod,$tom,$ton,$hl,$ml,$tl,$ht,$mt,$tt,$tsay,$chg,$msg,$hw,$mw,$pt,$rea);
  1714. #--determine which timer (duplicate check when coming from set)
  1715. if( $timer >= int( @{$hash->{DATA}{"WT"}}) ){
  1716. $msg = "Error, timer number $timer does not exist, number musst be smaller than ".int( @{$hash->{DATA}{"WT"}});
  1717. Log3 $name,1,"[YAAHM_sayNextTime] ".$msg;
  1718. return $msg;
  1719. }
  1720. #-- init message
  1721. $msg = $hash->{DATA}{"WT"}[$timer]{"name"};
  1722. #-- get timer values from readings, because these include vacation settings and special time
  1723. $tod = $hash->{DATA}{"WT"}[$timer]{"ring_0x"};
  1724. $tom = $hash->{DATA}{"WT"}[$timer]{"ring_1x"};
  1725. #-- current local time
  1726. ($hl,$ml) = split(':',strftime('%H:%M', localtime(time)));
  1727. $tl = 60*$hl+$ml;
  1728. #-- today off AND tomorrow any time or off => compare this time with current time
  1729. if( $tod =~ /^off.*/ ){
  1730. #-- tomorrow any time
  1731. if( $tom =~ /(\d?\d):(\d\d)(:(\d\d))?/ && $tom !~ /.*\(off\)$/ ){
  1732. #Log 1,"===========> |$1|$2|$3|$4";
  1733. ($ht,$mt) = split('[\s:]',$tom);
  1734. $tt=60*$ht+$mt;
  1735. #-- wakeup tomorrow later than now
  1736. if( $tt < $tl ){
  1737. $hw = $1*1;
  1738. $mw = $2*1;
  1739. $pt = sprintf("%d:%02d",$hw,$mw)." ".lc($yaahm_tt->{"tomorrow"});
  1740. $msg .= " ".lc($yaahm_tt->{"tomorrow"})." $hw ".$yaahm_tt->{"clock"};
  1741. $msg .=" $mw"
  1742. if( $mw != 0 );
  1743. }else{
  1744. $pt = "off ".lc($yaahm_tt->{"today"});
  1745. $msg .= " ".lc($yaahm_tt->{"today"})." ".$yaahm_tt->{"swoff"};
  1746. }
  1747. }elsif( $tom =~ /^off/ || $tom =~ /.*\(off\)$/ ){
  1748. $pt = "off ".lc($yaahm_tt->{"today"})." ".$yaahm_tt->{"and"}." ".lc($yaahm_tt->{"tomorrow"});
  1749. $msg .= " ".lc($yaahm_tt->{"today"})." ".$yaahm_tt->{"and"}." ".lc($yaahm_tt->{"tomorrow"})." ".$yaahm_tt->{"swoff"};
  1750. }else{
  1751. $pt = $yaahm_tt->{"undecid"};
  1752. $msg .= " ".$yaahm_tt->{"undecid"};
  1753. }
  1754. #-- today nontrivial => compare this time with current time
  1755. }elsif( $tod =~ /(\d?\d):(\d\d)(:(\d\d))?/ ){
  1756. #Log 1,"===========> |$1|$2|$3|$4";
  1757. ($ht,$mt) = split('[\s:]',$tod);
  1758. $tt=60*$ht+$mt;
  1759. #-- wakeup later today
  1760. if( $tt >= $tl ){
  1761. $hw = $1*1;
  1762. $mw = $2*1;
  1763. $pt = sprintf("%d:%02d",$hw,$mw)." ".lc($yaahm_tt->{"today"});
  1764. $msg .= " ".lc($yaahm_tt->{"today"})." $hw ".$yaahm_tt->{"clock"};
  1765. $msg .=" $mw"
  1766. if( $mw != 0 );
  1767. #-- todays time already past => tomorrow - but this may be off
  1768. }elsif( ($tom eq "off") || ($tom =~ /.*\(off\)/) ){
  1769. $pt = "off ".lc($yaahm_tt->{"tomorrow"});
  1770. $msg .= " ".lc($yaahm_tt->{"tomorrow"})." ".$yaahm_tt->{"swoff"};
  1771. }elsif( $tom =~ /(\d?\d):(\d\d)(:(\d\d))?( \((\d?\d):(\d\d)(:(\d\d))?\))?/ ){
  1772. #Log 1,"===========> |$1|$2|$3|$4|$5|$6";
  1773. if( defined($5) && $5 ne ""){
  1774. $hw = $6*1;
  1775. $mw = $7*1;
  1776. }else{
  1777. $hw = $1*1;
  1778. $mw = $2*1;
  1779. }
  1780. $pt = sprintf("%d:%02d",$hw,$mw)." ".lc($yaahm_tt->{"tomorrow"});
  1781. $msg .= " ".lc($yaahm_tt->{"tomorrow"})." $hw ".$yaahm_tt->{"clock"};
  1782. $msg .=" $mw"
  1783. if( $mw != 0 );
  1784. }elsif( $tom =~ /^off/ || $tom =~ /.*\(off\)$/ ){
  1785. $pt = "off ".lc($yaahm_tt->{"tomorrow"});
  1786. $msg .= " ".lc($yaahm_tt->{"tomorrow"})." ".$yaahm_tt->{"swoff"};
  1787. }else{
  1788. $pt = $yaahm_tt->{"undecid"};
  1789. $msg .= " ".$yaahm_tt->{"undecid"};
  1790. }
  1791. }else{
  1792. $pt = $yaahm_tt->{"undecid"};
  1793. $msg .= " ".$yaahm_tt->{"undecid"};
  1794. }
  1795. $hash->{DATA}{"WT"}[$timer]{"wake"} = $pt;
  1796. readingsSingleUpdate($hash,"tr_wake_".$timer,$pt,1);
  1797. if( $sp==0 ){
  1798. return $pt
  1799. }else{
  1800. return $msg;
  1801. }
  1802. }
  1803. #########################################################################################
  1804. #
  1805. # YAAHM_checkMonthly - check Monthly calendar at each
  1806. #
  1807. # Parameter hash = hash of device addressed
  1808. #
  1809. #########################################################################################
  1810. sub YAAHM_checkMonthly($$$) {
  1811. my ($hash,$event,$param) = @_;
  1812. my $name = $hash->{NAME};
  1813. my ($ret,$line,$fline,$date);
  1814. my (@lines,@chunks,@tday,@eday,@sday,@tmor,@two);
  1815. my ($stoday,$stom,$stwom,$tod);
  1816. my $todaylong = "";
  1817. my $tomlong = "";
  1818. my $twodaylong= "";
  1819. #-- hourly call
  1820. #if( ($event eq 'hour') || ($event eq '') ){
  1821. # my $text;
  1822. # $text = fhem("get Muell text modeAlarmOrStart");
  1823. # $text = "--" if (!$text);
  1824. # $text = "--" if ($text eq "");
  1825. # #fhem("set Termin ".$text);
  1826. # return $text;
  1827. #-- Vorschau täglich oder wenn neu gestartet
  1828. if( ($event eq "test") || (($event eq 'event') && ($param eq 'aftermidnight')) ){
  1829. my $specialDevs = AttrVal( $name, "specialDevices", "" );
  1830. foreach my $specialDev ( split( /,/, $specialDevs ) ) {
  1831. my ($todaydesc,$tomdesc,$twomdesc);
  1832. #-- device of type holiday
  1833. if( IsDevice( $specialDev, "holiday" )){
  1834. $stoday = strftime('%m-%d', localtime(time));
  1835. $stom = strftime('%m-%d', localtime(time+86400));
  1836. $stwom = strftime('%m-%d', localtime(time+2*86400));
  1837. $tod = holiday_refresh( $specialDev, $stoday );
  1838. if ( $tod ne "none" ) {
  1839. $todaydesc .= $tod.",";
  1840. Log3 $name, 5,"[YAAHM] found today=special date \"$tod\" in holiday $specialDev";
  1841. }
  1842. $tod = holiday_refresh( $specialDev, $stom );
  1843. if ( $tod ne "none" ) {
  1844. $tomdesc .= $tod.",";
  1845. Log3 $name, 5,"[YAAHM] found tomorrow=special date \"$tod\" in holiday $specialDev";
  1846. }
  1847. $tod = holiday_refresh( $specialDev, $stwom );
  1848. if ( $tod ne "none" ) {
  1849. $twomdesc .= $tod.",";
  1850. Log3 $name, 5,"[YAAHM] found twodays=special date \"$tod\" in holiday $specialDev";
  1851. }
  1852. #-- device of type calendar
  1853. }elsif( IsDevice($specialDev, "Calendar" )){
  1854. $stoday = strftime('%d.%m.%Y', localtime(time));
  1855. $stom = strftime('%d.%m.%Y', localtime(time+86400));
  1856. $stwom = strftime('%d.%m.%Y', localtime(time+2*86400));
  1857. @tday = split('\.',$stoday);
  1858. @tmor = split('\.',$stom);
  1859. @two = split('\.',$stwom);
  1860. $fline=Calendar_Get($defs{$specialDev},"get","full","mode=alarm|start|upcoming");
  1861. #-- more complicated to check here,
  1862. # format is '<id> upcoming [<datetoannounce> <timetoannouce>] <datestart> <timestart>-<dateend> <timeend> [<description>]
  1863. my ($cstart,$cdesc);
  1864. if($fline){
  1865. #chomp($fline);
  1866. @lines = split('\n',$fline);
  1867. foreach $fline (@lines){
  1868. chomp($fline);
  1869. @chunks = split(' ',$fline);
  1870. if( int(@chunks)>=7 ){
  1871. $cstart = 4;
  1872. $cdesc = 7;
  1873. }else{
  1874. $cstart = 2;
  1875. $cdesc = 5,
  1876. }
  1877. @sday = split('\.',$chunks[$cstart]);
  1878. $tod = ($chunks[$cdesc]) ? $chunks[$cdesc] : "???";
  1879. #-- today
  1880. my $rets = ($sday[2]-$tday[2])*365+($sday[1]-$tday[1])*31+($sday[0]-$tday[0]);
  1881. if( $rets==0 ){
  1882. $todaydesc .= $tod.",";
  1883. Log3 $name, 5,"[YAAHM] found today=special date \"$tod\" in calendar $specialDev";
  1884. }
  1885. $rets = ($sday[2]-$tmor[2])*365+($sday[1]-$tmor[1])*31+($sday[0]-$tmor[0]);
  1886. if( $rets==0 ){
  1887. $tomdesc .= $tod.",";
  1888. Log3 $name, 5,"[YAAHM] found tomorrow=special date \"$tod\" in calendar $specialDev";
  1889. }
  1890. $rets = ($sday[2]-$two[2])*365+($sday[1]-$two[1])*31+($sday[0]-$two[0]);
  1891. if( $rets==0 ){
  1892. $twomdesc .= $tod.",";
  1893. Log3 $name, 5,"[YAAHM] found twodays=special date \"$tod\" in calendar $specialDev";
  1894. }
  1895. }
  1896. }
  1897. }else{
  1898. Log3 $name, 1,"[YAAHM] unknown special device $specialDev";
  1899. }
  1900. #-- accumulate descriptions
  1901. $todaylong .= $todaydesc
  1902. if($todaydesc);
  1903. $tomlong .= $tomdesc
  1904. if($tomdesc);
  1905. $twodaylong .= $twomdesc
  1906. if($twomdesc);
  1907. }
  1908. $todaylong =~ s/,$//;
  1909. $tomlong =~ s/,$//;
  1910. $twodaylong =~ s/,$//;
  1911. $hash->{DATA}{"DD"}[0]{"special"} = $todaylong;
  1912. $hash->{DATA}{"DD"}[1]{"special"} = $tomlong;
  1913. #-- put into readings
  1914. readingsBeginUpdate($hash);
  1915. readingsBulkUpdateIfChanged( $hash, "todaySpecial",$todaylong );
  1916. readingsBulkUpdateIfChanged( $hash, "tomorrowSpecial",$tomlong);
  1917. readingsEndUpdate($hash,1);
  1918. }
  1919. }
  1920. #########################################################################################
  1921. #
  1922. # YAAHM_today - internal function to switch into daytime
  1923. #
  1924. # Parameter timerHash = internal hash of timer
  1925. # => need to dereference it for getting device hash
  1926. #
  1927. #########################################################################################
  1928. sub YAAHM_today($) {
  1929. my ($timerHash) = @_;
  1930. my $next;
  1931. my $hash = $timerHash->{HASH};
  1932. my $name = $hash->{NAME};
  1933. $next = gettimeofday()+86400;
  1934. YAAHM_RemoveInternalTimer("today",$hash);
  1935. YAAHM_InternalTimer("today",$next, "YAAHM_today", $hash, 0);
  1936. Log 1,"[YAAHM_today] on device ".$hash->{NAME}." called for this day";
  1937. readingsBeginUpdate($hash);
  1938. readingsBulkUpdate($hash,"housephase","daytime");
  1939. readingsBulkUpdate($hash,"tr_housephase",$yaahm_tt->{"daytime"});
  1940. readingsEndUpdate($hash,1);
  1941. return undef;
  1942. }
  1943. #########################################################################################
  1944. #d
  1945. # YAAHM_tonight - internal function to switch into nighttime
  1946. #
  1947. # Parameter timerHash = internal hash of timer
  1948. # => need to dereference it for getting device hash
  1949. #
  1950. #########################################################################################
  1951. sub YAAHM_tonight($) {
  1952. my ($timerHash) = @_;
  1953. my $next;
  1954. my $hash = $timerHash->{HASH};
  1955. my $name = $hash->{NAME};
  1956. $next = gettimeofday()+86400;
  1957. YAAHM_RemoveInternalTimer("tonight",$hash);
  1958. YAAHM_InternalTimer("tonight",$next, "YAAHM_tonight", $hash, 0);
  1959. Log 1,"[YAAHM_tonight] on device ".$hash->{NAME}." called for this day";
  1960. readingsBeginUpdate($hash);
  1961. readingsBulkUpdate($hash,"housephase","nighttime");
  1962. readingsBulkUpdate($hash,"tr_housephase",$yaahm_tt->{"nighttime"});
  1963. readingsEndUpdate($hash,1);
  1964. return undef;
  1965. }
  1966. #########################################################################################
  1967. #
  1968. # YAAHM_updater - internal update function 1 minute after midnight
  1969. #
  1970. # Parameter timerHash = on first call, device hash.
  1971. # = on later calls: internal hash of timer
  1972. # => need to dereference it for getting device hash
  1973. #
  1974. #########################################################################################
  1975. sub YAAHM_updater($) {
  1976. my ($timerHash) = @_;
  1977. my $hash;
  1978. my $next;
  1979. #-- start timer for updates - when device is reloaded
  1980. if( defined($firstcall) && ($firstcall==1) ){
  1981. #-- timerHash is device hash
  1982. $hash = $timerHash;
  1983. my ($sec, $min, $hour, $day, $month, $year, $wday,$yday,$isdst) = localtime(time);
  1984. $next = gettimeofday()+(23-$hour)*3600+(59-$min)*60+(59-$sec)+34;
  1985. $firstcall=0;
  1986. #-- continue timer for updates
  1987. }else{
  1988. #-- timerHash is internal hash
  1989. $hash = $timerHash->{HASH};
  1990. $next = gettimeofday()+86400;
  1991. }
  1992. #-- safeguard if hash is not properly indirected
  1993. if( defined($hash->{HASH}) ){
  1994. #Log 1,"WARNING ! HASH indirection not ok. firstcall=$firstcall";
  1995. $hash = $hash->{HASH};
  1996. }
  1997. YAAHM_RemoveInternalTimer("aftermidnight",$hash);
  1998. YAAHM_InternalTimer("aftermidnight",$next, "YAAHM_updater", $hash, 0);
  1999. Log 1,"[YAAHM_updater] on device ".$hash->{NAME}." called for this day";
  2000. YAAHM_GetDayStatus($hash);
  2001. return undef;
  2002. }
  2003. #########################################################################################
  2004. #
  2005. # YAAHM_InternalTimer - start named internal timer
  2006. #
  2007. # Parameter modifier = name suffix
  2008. # tim = time
  2009. # callback = callback function
  2010. #
  2011. #
  2012. #########################################################################################
  2013. sub YAAHM_InternalTimer($$$$$) {
  2014. my ($modifier, $tim, $callback, $hash, $waitIfInitNotDone) = @_;
  2015. my $mHash;
  2016. if ($modifier eq "") {
  2017. $mHash = $hash;
  2018. } else {
  2019. my $timerName = "$hash->{NAME}_$modifier";
  2020. if (exists ($hash->{TIMER}{$timerName})) {
  2021. $mHash = $hash->{TIMER}{$timerName};
  2022. } else {
  2023. $mHash = { HASH=>$hash, NAME=>"$hash->{NAME}_$modifier", MODIFIER=>$modifier};
  2024. $hash->{TIMER}{$timerName} = $mHash;
  2025. }
  2026. }
  2027. InternalTimer($tim, $callback, $mHash, $waitIfInitNotDone);
  2028. }
  2029. #########################################################################################
  2030. #
  2031. # YAAHM_RemoveInternalTimer - kill named internal timer
  2032. #
  2033. # Parameter
  2034. #
  2035. #########################################################################################
  2036. sub YAAHM_RemoveInternalTimer($$) {
  2037. my ($modifier, $hash) = @_;
  2038. my $timerName = "$hash->{NAME}_$modifier";
  2039. if ($modifier eq "") {
  2040. RemoveInternalTimer($hash);
  2041. } else {
  2042. my $myHash = $hash->{TIMER}{$timerName};
  2043. if (defined($myHash)) {
  2044. delete $hash->{TIMER}{$timerName};
  2045. RemoveInternalTimer($myHash);
  2046. }
  2047. }
  2048. }
  2049. #########################################################################################
  2050. #
  2051. # YAAHM_GetDayStatus
  2052. #
  2053. # Parameter hash = hash of device addressed
  2054. #
  2055. #########################################################################################
  2056. sub YAAHM_GetDayStatus($) {
  2057. my ($hash) = @_;
  2058. my $name = $hash->{NAME};
  2059. #-- readjust language
  2060. my $lang = AttrVal("global","language","EN");
  2061. if( $lang eq "DE"){
  2062. $yaahm_tt = \%yaahm_transtable_DE;
  2063. }else{
  2064. $yaahm_tt = \%yaahm_transtable_EN;
  2065. }
  2066. my ($ret,$line,$fline,$date);
  2067. my (@lines,@chunks,@tday,@eday,@sday,@tmor);
  2068. my ($todaydesc,$todaytype,$tomdesc,$tomtype);
  2069. my $stoday = strftime('%d.%m.%Y', localtime(time));
  2070. my $stom = strftime('%d.%m.%Y', localtime(time+86400));
  2071. #-- workday has lowest priority
  2072. $todaytype = "workday";
  2073. $hash->{DATA}{"DD"}[0]{"date"} = $stoday;
  2074. $hash->{DATA}{"DD"}[0]{"weekday"} = (strftime('%w', localtime(time))+6)%7;
  2075. $hash->{DATA}{"DD"}[0]{"daytype"} = "workday";
  2076. $hash->{DATA}{"DD"}[0]{"desc"} = $yaahm_tt->{"workday"};
  2077. $hash->{DATA}{"DD"}[0]{"vacflag"} = 0;
  2078. $tomtype = "workday";
  2079. $hash->{DATA}{"DD"}[1]{"date"} = $stom;
  2080. $hash->{DATA}{"DD"}[1]{"weekday"} = (strftime('%w', localtime(time+86400))+6)%7;
  2081. $hash->{DATA}{"DD"}[1]{"daytype"} = "workday";
  2082. $hash->{DATA}{"DD"}[1]{"desc"} = $yaahm_tt->{"workday"};
  2083. $hash->{DATA}{"DD"}[1]{"vacflag"} = 0;
  2084. #-- vacation = vacdays has higher priority
  2085. my $vacdayDevs = AttrVal( $name, "vacationDevices", "" );
  2086. foreach my $vacdayDev ( split( /,/, $vacdayDevs ) ) {
  2087. #-- device of type holiday
  2088. if( IsDevice( $vacdayDev, "holiday" )){
  2089. $stoday = strftime('%m-%d', localtime(time));
  2090. $stom = strftime('%m-%d', localtime(time+86400));
  2091. my $tod = holiday_refresh( $vacdayDev, $stoday );
  2092. if ( $tod ne "none" ) {
  2093. $todaydesc = $tod;
  2094. $todaytype = "vacday";
  2095. Log3 $name, 5,"[YAAHM] found today=vacation \"$todaydesc\" in holiday $vacdayDev";
  2096. }
  2097. $tod = holiday_refresh( $vacdayDev, $stom );
  2098. if ( $tod ne "none" ) {
  2099. $tomdesc = $tod;
  2100. $tomtype = "vacday";
  2101. Log3 $name, 5,"[YAAHM] found tomorrow=vacation \"$tomdesc\" in holiday $vacdayDev";
  2102. }
  2103. #-- device of type calendar
  2104. }elsif( IsDevice($vacdayDev, "Calendar" )){
  2105. $stoday = strftime('%d.%m.%Y', localtime(time));
  2106. $stom = strftime('%d.%m.%Y', localtime(time+86400));
  2107. @tday = split('\.',$stoday);
  2108. @tmor = split('\.',$stom);
  2109. #-- more complicated to check here
  2110. $fline=Calendar_Get($defs{$vacdayDev},"get","full","mode=alarm|start|upcoming");
  2111. if($fline){
  2112. #chomp($fline);
  2113. @lines = split('\n',$fline);
  2114. foreach $fline (@lines){
  2115. chomp($fline);
  2116. @chunks = split(' ',$fline);
  2117. @sday = split('\.',$chunks[2]);
  2118. @eday = split('\.',substr($chunks[3],9,10));
  2119. #-- today
  2120. my $rets = ($sday[2]-$tday[2])*365+($sday[1]-$tday[1])*31+($sday[0]-$tday[0]);
  2121. my $rete = ($eday[2]-$tday[2])*365+($eday[1]-$tday[1])*31+($eday[0]-$tday[0]);
  2122. if( ($rete>=0) && ($rets<=0) ){
  2123. $todaydesc = $chunks[5];
  2124. $todaytype = "vacation";
  2125. Log3 $name, 5,"[YAAHM] found today=vacation \"$todaydesc\" in calendar $vacdayDev";
  2126. }
  2127. $rets = ($sday[2]-$tmor[2])*365+($sday[1]-$tmor[1])*31+($sday[0]-$tmor[0]);
  2128. $rete = ($eday[2]-$tmor[2])*365+($eday[1]-$tmor[1])*31+($eday[0]-$tmor[0]);
  2129. if( ($rete>=0) && ($rets<=0) ){
  2130. $tomdesc = $chunks[5];
  2131. $tomtype = "vacation";
  2132. Log3 $name, 5,"[YAAHM] found tomorrow=vacation \"$tomdesc\" in calendar $vacdayDev";
  2133. }
  2134. }
  2135. }
  2136. }else{
  2137. Log3 $name, 1,"[YAAHM] unknown vacation device $vacdayDev";
  2138. }
  2139. }
  2140. #-- put into readings
  2141. if( $todaytype eq "vacation" ){
  2142. $hash->{DATA}{"DD"}[0]{"daytype"} = "vacation";
  2143. $hash->{DATA}{"DD"}[0]{"desc"} = $todaydesc;
  2144. $hash->{DATA}{"DD"}[0]{"vacflag"} = 1;
  2145. }
  2146. if( $tomtype eq "vacation" ){
  2147. $hash->{DATA}{"DD"}[1]{"daytype"} = "vacation";
  2148. $hash->{DATA}{"DD"}[1]{"desc"} = $tomdesc;
  2149. $hash->{DATA}{"DD"}[1]{"vacflag"} = 1;
  2150. }
  2151. #-- weekend has higher priority
  2152. if( strftime('%u', localtime(time)) > 5){
  2153. $todaytype = "weekend";
  2154. if( $hash->{DATA}{"DD"}[0]{"daytype"} ne "workday" ){
  2155. $hash->{DATA}{"DD"}[0]{"desc"} = $yaahm_tt->{"weekend"}.", ".$hash->{DATA}{"DD"}[0]{"desc"};
  2156. }else{
  2157. $hash->{DATA}{"DD"}[0]{"desc"} = $yaahm_tt->{"weekend"};
  2158. }
  2159. $hash->{DATA}{"DD"}[0]{"daytype"} = "weekend";
  2160. }
  2161. if( strftime('%u', localtime(time+86400)) > 5){
  2162. $tomtype = "weekend";
  2163. if( $hash->{DATA}{"DD"}[1]{"daytype"} ne "workday" ){
  2164. $hash->{DATA}{"DD"}[1]{"desc"} = $yaahm_tt->{"weekend"}.", ".$hash->{DATA}{"DD"}[1]{"desc"};
  2165. }else{
  2166. $hash->{DATA}{"DD"}[1]{"desc"} = $yaahm_tt->{"weekend"};
  2167. }
  2168. $hash->{DATA}{"DD"}[1]{"daytype"} = "weekend";
  2169. }
  2170. #-- holidays have the highest priority
  2171. my $holidayDevs = AttrVal( $name, "holidayDevices", "" );
  2172. foreach my $holidayDev ( split( /,/, $holidayDevs ) ) {
  2173. #-- device of type holiday
  2174. if( IsDevice( $holidayDev, "holiday" )){
  2175. $stoday = strftime('%m-%d', localtime(time));
  2176. $stom = strftime('%m-%d', localtime(time+86400));
  2177. my $tod = holiday_refresh( $holidayDev, $stoday );
  2178. if ( $tod ne "none" ) {
  2179. $todaydesc = $tod;
  2180. $todaytype = "holiday";
  2181. Log3 $name, 1,"[YAAHM] found today=holiday \"$todaydesc\" in holiday $holidayDev";
  2182. }
  2183. $tod = holiday_refresh( $holidayDev, $stom );
  2184. if ( $tod ne "none" ) {
  2185. $tomdesc = $tod;
  2186. $tomtype = "holiday";
  2187. Log3 $name, 1,"[YAAHM] found tomorrow=holiday \"$tomdesc\" in holiday $holidayDev";
  2188. }
  2189. #-- device of type calendar
  2190. }elsif( IsDevice($holidayDev, "Calendar" )){
  2191. $stoday = strftime('%d.%m.%Y', localtime(time));
  2192. $stom = strftime('%d.%m.%Y', localtime(time+86400));
  2193. $line=Calendar_Get($defs{$holidayDev},"get","text","mode=alarm|start|upcoming");
  2194. if($line){
  2195. chomp($line);
  2196. @lines = split('\n',$line);
  2197. foreach $line (@lines){
  2198. chomp($line);
  2199. $date = substr($line,0,8);
  2200. if( $date eq $stoday ){
  2201. $todaydesc = substr($line,15);
  2202. $todaytype = "holiday";
  2203. Log3 $name, 1,"[YAAHM] found today=holiday \"$todaydesc\" in calendar $holidayDev";
  2204. }
  2205. if( $date eq $stom ){
  2206. $tomdesc = substr($line,15);
  2207. $tomtype = "holiday";
  2208. Log3 $name, 1,"[YAAHM] found tomorrow=holiday \"$tomdesc\" in calendar $holidayDev";
  2209. }
  2210. }
  2211. }
  2212. }else{
  2213. Log3 $name, 1,"[YAAHM] unknown holiday device $holidayDev";
  2214. }
  2215. }
  2216. #-- put into store
  2217. if( $todaytype eq "holiday" ){
  2218. if( $hash->{DATA}{"DD"}[0]{"daytype"} ne "workday" ){
  2219. $hash->{DATA}{"DD"}[0]{"desc"} = $todaydesc.", ".$hash->{DATA}{"DD"}[0]{"desc"};
  2220. }else{
  2221. $hash->{DATA}{"DD"}[0]{"desc"} = $todaydesc;
  2222. }
  2223. $hash->{DATA}{"DD"}[0]{"daytype"} = "holiday";
  2224. }
  2225. if( $tomtype eq "holiday" ){
  2226. if( $hash->{DATA}{"DD"}[1]{"daytype"} ne "workday" ){
  2227. $hash->{DATA}{"DD"}[1]{"desc"} = $tomdesc.", ".$hash->{DATA}{"DD"}[1]{"desc"};
  2228. }else{
  2229. $hash->{DATA}{"DD"}[1]{"desc"} = $tomdesc;
  2230. }
  2231. $hash->{DATA}{"DD"}[1]{"daytype"} = "holiday";
  2232. }
  2233. #-- sunrise, sunset and the offsets
  2234. YAAHM_sun($hash);
  2235. YAAHM_sunoffsets($hash);
  2236. readingsBeginUpdate($hash);
  2237. #-- and do not forget to put them into readings, because these are read by the timer
  2238. foreach my $key (sort YAAHM_dsort keys %defaultdailytable){
  2239. my $f1 = defined($defaultdailytable{$key}[0]);
  2240. my $f2 = defined($defaultdailytable{$key}[1]);
  2241. #-- entries in the default table with no entry are single-timers
  2242. if( !$f1 and !$f2 ){
  2243. readingsBulkUpdate( $hash, "s_".$key, $hash->{DATA}{"DT"}{$key}[0] );
  2244. #-- entries in the default table with only first time are single-timers
  2245. }elsif( $f1 and !$f2 ){
  2246. readingsBulkUpdate( $hash, "s_".$key, $hash->{DATA}{"DT"}{$key}[0] );
  2247. #-- entries in the default table with only second time are single-timer offsets
  2248. }elsif( !$f1 and $f2 ){
  2249. readingsBulkUpdate( $hash, "s_".$key, $hash->{DATA}{"DT"}{$key}[0] );
  2250. #-- entries in the default table with first and second time are two-timer periods
  2251. }elsif( $f1 and $f2 ){
  2252. readingsBulkUpdate( $hash, "s_".$key, $hash->{DATA}{"DT"}{$key}[0] );
  2253. readingsBulkUpdate( $hash, "t_".$key, $hash->{DATA}{"DT"}{$key}[1] );
  2254. #-- something wrong
  2255. }else{
  2256. my $msg = "Readings update failed, something wrong with entry ".$key;
  2257. Log 1,"[YAAHM_GetDayStatus] ".$msg;
  2258. return $msg;
  2259. }
  2260. }
  2261. readingsBulkUpdateIfChanged( $hash, "todayType",$todaytype );
  2262. readingsBulkUpdateIfChanged( $hash, "tr_todayType",$yaahm_tt->{$hash->{DATA}{"DD"}[0]{"daytype"}} );
  2263. if( $todaytype eq "workday"){
  2264. readingsBulkUpdateIfChanged( $hash, "todayDesc","--" )
  2265. }elsif( $todaytype eq "vacation"){
  2266. readingsBulkUpdateIfChanged( $hash, "todayDesc",$hash->{DATA}{"DD"}[0]{"desc"} )
  2267. }elsif( $todaytype eq "weekend"){
  2268. readingsBulkUpdateIfChanged( $hash, "todayDesc","--" )
  2269. }else{
  2270. readingsBulkUpdateIfChanged( $hash, "todayDesc",$hash->{DATA}{"DD"}[0]{"desc"} )
  2271. }
  2272. readingsBulkUpdateIfChanged( $hash, "tomorrowType",$tomtype );
  2273. readingsBulkUpdateIfChanged( $hash, "tr_tomorrowType",$yaahm_tt->{$hash->{DATA}{"DD"}[1]{"daytype"}} );
  2274. if( $tomtype eq "workday"){
  2275. readingsBulkUpdateIfChanged( $hash, "tomorrowDesc","--" )
  2276. }elsif( $tomtype eq "vacation"){
  2277. readingsBulkUpdateIfChanged( $hash, "tomorrowDesc",$hash->{DATA}{"DD"}[1]{"desc"} )
  2278. }elsif( $tomtype eq "weekend"){
  2279. readingsBulkUpdateIfChanged( $hash, "tomorrowDesc","--" )
  2280. }else{
  2281. readingsBulkUpdateIfChanged( $hash, "tomorrowDesc",$hash->{DATA}{"DD"}[1]{"desc"} )
  2282. }
  2283. YAAHM_setWeeklyTime($hash);
  2284. readingsEndUpdate($hash,1);
  2285. return undef;
  2286. }
  2287. #########################################################################################
  2288. #
  2289. # YAAHM_sun - obtain time offsets for midnight etc. sunrise and sunset
  2290. #
  2291. # Parameter hash = hash of device addressed
  2292. #
  2293. #########################################################################################
  2294. sub YAAHM_sun($) {
  2295. my ($hash) = @_;
  2296. my $name = $hash->{NAME};
  2297. #-- sunrise and sunset today and tomorrow
  2298. my ($sttoday,$sttom);
  2299. my $count = 0;
  2300. my $strise0 = "";
  2301. my $strise1 = "";
  2302. my ($msg,$stset0,$stseas0,$stset1,$stseas1);
  2303. $sttoday = strftime('%Y-%m-%d', localtime(time));
  2304. #-- since the Astro module sometimes gives us strange results, we need to do this more than once
  2305. while( $strise0 !~ /^\d\d:\d\d:\d\d/ && $count < 5){
  2306. $strise0 = Astro_Get($hash,"dummy","text", "SunRise",$sttoday).":00";
  2307. $count++;
  2308. select(undef,undef,undef,0.01);
  2309. }
  2310. if( $count == 5 ){
  2311. $msg = "Error, no proper sunrise today return from Astro module in 5 attempts";
  2312. Log3 $name,1,"[YAAHM_sun] ".$msg;
  2313. $strise0 = "06:00:00";
  2314. }
  2315. my ($hour,$min) = split(":",$strise0);
  2316. $hash->{DATA}{"DD"}[0]{"sunrise"} = sprintf("%02d:%02d",$hour,$min);
  2317. $stset0 = Astro_Get($hash,"dummy","text", "SunSet",$sttoday).":00";
  2318. ($hour,$min) = split(":",$stset0);
  2319. $hash->{DATA}{"DD"}[0]{"sunset"} = sprintf("%02d:%02d",$hour,$min);
  2320. $stseas0 = Astro_Get($hash,"dummy","text", "ObsSeasonN",$sttoday);
  2321. $hash->{DATA}{"DD"}[0]{"season"} = $seasons[$stseas0];
  2322. $sttom = strftime('%Y-%m-%d', localtime(time+86400));
  2323. $count = 0;
  2324. $msg = "";
  2325. #-- since the Astro module sometimes gives us strange results, we need to do this more than once
  2326. while( $strise1 !~ /^\d\d:\d\d:\d\d/ && $count < 5){
  2327. $strise1 = Astro_Get($hash,"dummy","text", "SunRise",$sttom).":00";
  2328. $count++;
  2329. select(undef,undef,undef,0.01);
  2330. }
  2331. if( $count == 5 ){
  2332. $msg = "Error, no proper sunrise tomorrow return from Astro module in 5 attempts";
  2333. Log3 $name,1,"[YAAHM_sun] ".$msg;
  2334. $strise1 = "06:00:00";
  2335. }
  2336. ($hour,$min) = split(":",$strise1);
  2337. $hash->{DATA}{"DD"}[1]{"sunrise"} = sprintf("%02d:%02d",$hour,$min);
  2338. $stset1 = Astro_Get($hash,"dummy","text", "SunSet",$sttom).":00";
  2339. ($hour,$min) = split(":",$stset1);
  2340. $hash->{DATA}{"DD"}[1]{"sunset"} = sprintf("%02d:%02d",$hour,$min);
  2341. $stseas1 = Astro_Get($hash,"dummy","text", "ObsSeasonN",$sttom);
  2342. $hash->{DATA}{"DD"}[1]{"season"} = $seasons[$stseas1];
  2343. }
  2344. #########################################################################################
  2345. #
  2346. # YAAHM_sunoffsets - obtain time offsets for midnight etc. sunrise and sunset
  2347. #
  2348. # Parameter hash = hash of device addressed
  2349. #
  2350. #########################################################################################
  2351. sub YAAHM_sunoffsets($) {
  2352. my ($hash) = @_;
  2353. #-- sunrise
  2354. my $st = $hash->{DATA}{"DD"}[0]{"sunrise"};
  2355. my ($hour,$min) = split(":",$st);
  2356. $hash->{DATA}{"DT"}{"sunrise"}[0] = sprintf("%02d:%02d",$hour,$min);
  2357. #-- before sunrise
  2358. my ($ofh,$ofm);
  2359. my $of = $hash->{DATA}{"DT"}{"beforesunrise"}[1];
  2360. if( $of !~ /\d\d:\d\d/){
  2361. Log 1,"[YAAHM] Offset before sunrise not in format hh:mm, using 00:00";
  2362. $ofh = 0;
  2363. $ofm = 0;
  2364. }else{
  2365. ($ofh,$ofm) = split(":",$of);
  2366. }
  2367. $ofm = $min-$ofm;
  2368. $ofh = $hour-$ofh;
  2369. if( $ofm < 0 ){
  2370. $ofh--;
  2371. $ofm +=60;
  2372. }
  2373. $hash->{DATA}{"DT"}{"beforesunrise"}[0] = sprintf("%02d:%02d",$ofh,$ofm);
  2374. #-- after sunrise
  2375. $of = $hash->{DATA}{"DT"}{"aftersunrise"}[1];
  2376. if( $of !~ /\d\d:\d\d/){
  2377. Log 1,"[YAAHM] Offset after sunrise not in format hh:mm, using 00:00";
  2378. $ofh = 0;
  2379. $ofm = 0;
  2380. }else{
  2381. ($ofh,$ofm) = split(":",$of);
  2382. }
  2383. $ofm = $min+$ofm;
  2384. $ofh = $hour+$ofh;
  2385. if( $ofm > 59 ){
  2386. $ofh++;
  2387. $ofm -=60;
  2388. }
  2389. $hash->{DATA}{"DT"}{"aftersunrise"}[0] = sprintf("%02d:%02d",$ofh,$ofm);
  2390. #-- sunset
  2391. $st = $hash->{DATA}{"DD"}[0]{"sunset"};
  2392. ($hour,$min) = split(":",$st);
  2393. $hash->{DATA}{"DT"}{"sunset"}[0] = sprintf("%02d:%02d",$hour,$min);
  2394. #-- before sunset
  2395. $of = $hash->{DATA}{"DT"}{"beforesunset"}[1];
  2396. if( $of !~ /\d\d:\d\d/){
  2397. Log 1,"[YAAHM] Offset before sunset not in format hh:mm, using 00:00";
  2398. $ofh = 0;
  2399. $ofm = 0;
  2400. }else{
  2401. ($ofh,$ofm) = split(":",$of);
  2402. }
  2403. $ofm = $min-$ofm;
  2404. $ofh = $hour-$ofh;
  2405. if( $ofm < 0 ){
  2406. $ofh--;
  2407. $ofm +=60;
  2408. }
  2409. $hash->{DATA}{"DT"}{"beforesunset"}[0] = sprintf("%02d:%02d",$ofh,$ofm);
  2410. #-- after sunset
  2411. $of = $hash->{DATA}{"DT"}{"aftersunset"}[1];
  2412. if( $of !~ /\d\d:\d\d/){
  2413. Log 1,"[YAAHM] Offset after sunset not in format hh:mm, using 00:00";
  2414. $ofh = 0;
  2415. $ofm = 0;
  2416. }else{
  2417. ($ofh,$ofm) = split(":",$of);
  2418. }
  2419. $ofm = $min+$ofm;
  2420. $ofh = $hour+$ofh;
  2421. if( $ofm > 59 ){
  2422. $ofh++;
  2423. $ofm -=60;
  2424. }
  2425. $hash->{DATA}{"DT"}{"aftersunset"}[0] = sprintf("%02d:%02d",$ofh,$ofm);
  2426. #-- before midnight
  2427. $hour = 24;
  2428. $min = 0;
  2429. $of = $hash->{DATA}{"DT"}{"beforemidnight"}[1];
  2430. if( $of !~ /\d\d:\d\d/){
  2431. Log 1,"[YAAHM] Offset before midnight not in format hh:mm, using 00:05";
  2432. $ofh = 0;
  2433. $ofm = 5;
  2434. }else{
  2435. ($ofh,$ofm) = split(":",$of);
  2436. }
  2437. $ofm = $min-$ofm;
  2438. $ofh = $hour-$ofh;
  2439. if( $ofm < 0 ){
  2440. $ofh--;
  2441. $ofm +=60;
  2442. }
  2443. $hash->{DATA}{"DT"}{"beforemidnight"}[0] = sprintf("%02d:%02d",$ofh,$ofm);
  2444. #-- after midnight
  2445. $hour = 0;
  2446. $min = 0;
  2447. $of = $hash->{DATA}{"DT"}{"aftermidnight"}[1];
  2448. if( $of !~ /\d\d:\d\d/){
  2449. Log 1,"[YAAHM] Offset after midnight not in format hh:mm, using 00:05";
  2450. $ofh = 0;
  2451. $ofm = 5;
  2452. }else{
  2453. ($ofh,$ofm) = split(":",$of);
  2454. }
  2455. $ofm = $min+$ofm;
  2456. $ofh = $hour+$ofh;
  2457. if( $min > 59 ){
  2458. $ofh++;
  2459. $ofm -=60;
  2460. }
  2461. $hash->{DATA}{"DT"}{"aftermidnight"}[0] = sprintf("%02d:%02d",$ofh,$ofm);
  2462. }
  2463. #########################################################################################
  2464. #
  2465. # YAAHM_statewidget - returns SVG code for inclusion into page
  2466. #
  2467. # Parameter hash = hash of device addressed
  2468. #
  2469. #########################################################################################
  2470. sub YAAHM_statewidget($){
  2471. my ($hash) = @_;
  2472. my $name = $hash->{NAME};
  2473. return ""
  2474. if( AttrVal($name,"noicons",0) == 1);
  2475. my $state = $hash->{DATA}{"HSM"}{"state"};
  2476. my ($color,$locks,$unlocks,$bells,$eyes);
  2477. if( $state eq "unsecured" ){
  2478. $color=$csstate[0];
  2479. $locks="hidden";
  2480. $unlocks="visible";
  2481. $bells="hidden";
  2482. $eyes="hidden";
  2483. }elsif( $state eq "secured" ){
  2484. $color=$csstate[1];
  2485. $locks="visible";
  2486. $unlocks="hidden";
  2487. $bells="hidden";
  2488. $eyes="hidden";
  2489. }elsif( $state eq "protected" ){
  2490. $color=$csstate[2];
  2491. $locks="visible";
  2492. $unlocks="hidden";
  2493. $bells="visible";
  2494. $eyes="hidden";
  2495. }elsif( $state eq "guarded" ){
  2496. $color=$csstate[3];
  2497. $locks="visible";
  2498. $unlocks="hidden";
  2499. $bells="visible";
  2500. $eyes="visible";
  2501. }else{
  2502. Log 1,"[YAAHM_statewidget] Error, housestate $state not defined";
  2503. return;
  2504. }
  2505. my $ret = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40" width="40px" height="40px">';
  2506. $ret .= '<g transform="translate(5,0)">\n';
  2507. #-- shield
  2508. $ret .='<g transform="scale(0.1,0.1)">\n'.
  2509. '<path class="hs_is" fill="'.$color.'" d="M179.6,13c0,0-50.1,44.5-146.1,48.5c0,0,13.5,158,50.5,197s95.6,80,95.6,80s78.9-55.5,98.9-81.5 s37.5-75,47-196.5C325.5,60.5,251.8,64,179.6,13z"/>'.
  2510. '<path style="fill:none;stroke:black;stroke-width:10" d="M179.6,13c0,0-50.1,44.5-146.1,48.5c0,0,13.5,158,50.5,197s95.6,80,95.6,80s78.9-55.5,98.9-81.5 s37.5-75,47-196.5C325.5,60.5,251.8,64,179.6,13z"/></g>';
  2511. #-- small bell
  2512. $ret .= '<g class="hs_smb" transform="translate(0,23) scale(0.28,0.28)" visibility="'.$bells.'">\n';
  2513. $ret .= '<path d="M 25 6 C 23.354545 6 22 7.3545455 22 9 L 22 10.365234 C 17.172775 11.551105 14.001117 15.612755 14.001953 21.0625 '.
  2514. 'L 14.001953 28.863281 C 14.001953 31.035281 12.718469 33.494563 11.980469 34.726562 L 10.167969 37.445312 C 9.9629687 37.751312 9.9431875 '.
  2515. '38.147656 10.117188 38.472656 C 10.291188 38.797656 10.631 39 11 39 L 39 39 C 39.369 39 39.708813 38.797656 39.882812 38.472656 C 40.056813 '.
  2516. '38.147656 40.037031 37.752313 39.832031 37.445312 L 38.044922 34.767578 C 36.668922 32.473578 36 30.587 36 29 L 36 21.199219 C 36 15.68167 '.
  2517. '32.827303 11.569596 28 10.369141 L 28 9 C 28 7.3545455 26.645455 6 25 6 z M 25 8 C 25.554545 8 26 8.4454545 26 9 L 26 10.044922 C 25.671339 '.
  2518. '10.019952 25.339787 10 25 10 C 24.660213 10 24.328661 10.020256 24 10.044922 L 24 9 C 24 8.4454545 24.445455 8 25 8 z " fill="black" stroke="black"/>'.
  2519. '.<path d="M 20.423828 41 C 21.197828 42.763 22.955 44 25 44 C 27.045 44 28.802172 42.763 29.576172 41 L 20.423828 41 z" fill="black"/></g>\n';
  2520. #-- lock
  2521. $ret .= '<g transform="translate(1,-2) scale(0.1,0.1)" visibility="visible" style="fill:black">\n';
  2522. $ret .= '<path d="M 130.57422 130.58594 C 129.00209 130.6591 127.9626 130.81157 126.67383 131.14062 C 121.6497 132.42339 117.28863 136.02241 114.93164 140.83398 '.
  2523. 'C 114.02534 142.68411 113.52297 144.27216 113.15234 146.45508 C 113.00288 147.33557 112.9931 149.48213 112.9668 187.17578 C 112.9473 215.14019 '.
  2524. '112.96786 227.3203 113.03516 228.16016 C 113.33148 231.85916 114.6641 235.33647 116.91797 238.29492 C 117.57258 239.15416 119.29708 240.86998 '.
  2525. '120.20508 241.56445 C 122.81931 243.56391 125.66412 244.76433 128.79688 245.18945 C 130.22882 245.38381 208.07219 245.39507 209.54102 245.20117 '.
  2526. 'C 217.01971 244.21392 223.13119 238.65732 224.92969 231.21289 C 225.49437 228.87561 225.45703 232.00604 225.45703 187.94336 C 225.45703 161.07427 '.
  2527. '225.42603 147.57463 225.36523 147.07227 C 225.02812 144.2871 223.98281 141.29331 222.58203 139.10742 C 219.44374 134.21016 214.3933 131.11459 '.
  2528. '208.72656 130.61328 C 208.60764 130.60277 208.27712 130.59537 208.06445 130.58594 L 191.28711 130.58594 L 191.28711 130.60938 L 169.33203 130.60938 '.
  2529. 'L 147.37695 130.60938 L 147.37695 130.58594 L 130.57422 130.58594 z M 168.84766 141.69531 C 196.24408 141.67681 207.96833 141.69697 208.37305 141.76367 '.
  2530. 'C 209.88322 142.01259 211.23437 142.69523 212.37109 143.78125 C 213.56262 144.91963 214.28722 146.23107 214.60742 147.82812 C 214.73627 148.47097 '.
  2531. '214.74661 152.13826 214.72461 188.36914 L 214.70117 228.20312 L 214.5 228.85352 C 213.61281 231.71117 211.36106 233.77649 208.56055 234.30469 C '.
  2532. '207.89347 234.43005 204.00466 234.44545 169.125 234.43945 C 131.56507 234.4334 130.40611 234.42765 129.63281 234.26562 C 128.63219 234.05598 127.28912 '.
  2533. '233.40183 126.47852 232.73047 C 124.95438 231.46815 123.97606 229.64008 123.74609 227.625 C 123.67879 227.03543 123.65738 215.18032 123.67578 187.48438 '.
  2534. 'L 123.70312 148.18164 L 123.90234 147.47266 C 124.36618 145.82739 125.37129 144.27333 126.59375 143.31055 C 127.28146 142.76892 128.48475 142.14943 '.
  2535. '129.36719 141.88281 C 129.87599 141.72912 131.53564 141.72051 168.84766 141.69531 z "/>\n'.
  2536. '<path d="m 168.43291,207.79319 c -2.82175,-0.41393 -5.20902,-2.19673 -6.45085,-4.81746 -0.18456,-0.38949 -0.42714,-1.02683 -0.53907,-1.41632 -0.20211,-0.70332 '.
  2537. '-0.20369,-0.74985 -0.23134,-6.79219 l -0.0278,-6.08403 -0.4938,-0.46648 c -1.94776,-1.84005 -3.29729,-4.28865 -3.89177,-7.06126 -0.22834,-1.06501 '.
  2538. '-0.23065,-3.78941 -0.004,-4.90967 0.5504,-2.72228 1.69072,-4.87295 3.59555,-6.78128 1.86685,-1.87027 3.93834,-2.98308 6.53099,-3.50847 1.14974,-0.23298 '.
  2539. '3.66048,-0.21247 4.804,0.0393 5.18176,1.14065 9.02113,5.15685 9.96535,10.42433 0.21336,1.19023 0.21526,3.4008 0.004,4.5588 -0.4602,2.5216 -1.68503,4.94293 '.
  2540. '-3.41861,6.75821 -0.69412,0.72682 -0.76553,0.83257 -0.69333,1.02659 0.0464,0.1247 0.0688,2.7301 0.0528,6.15217 -0.027,5.78112 -0.0327,5.95363 -0.22,6.61733 '.
  2541. '-0.66956,2.3726 -2.29646,4.38412 -4.40661,5.44839 -0.87624,0.44194 -1.4689,0.63145 -2.43431,0.77844 -0.91656,0.13954 -1.37046,0.14668 -2.14096,0.0337 l 0,0 z"/></g>\n';
  2542. $ret .= '<g class="hs_unlocked" transform="translate(1,-2) scale(0.1,0.1)" visibility="'.$unlocks.'" style="fill:black">\n';
  2543. $ret .= '<path d="M 170.82812 73.273438 C 169.18654 73.276141 168.63971 73.313421 167.69531 73.492188 C 165.61131 73.886674 163.21555 74.79455 161.46875 75.849609 '.
  2544. 'C 159.08962 77.286589 156.84235 79.476504 155.35742 81.806641 C 154.71611 82.812977 143.83472 103.12552 143.69141 103.58398 C 143.32362 104.76045 143.9985 '.
  2545. '106.3 145.13867 106.88867 C 145.89649 107.27994 146.17527 107.28572 149.68359 106.98633 C 153.46023 106.66405 153.66989 106.60755 154.45508 105.72266 C '.
  2546. '154.74018 105.40138 156.38998 102.44246 159.67383 96.359375 C 164.73578 86.982504 164.89996 86.708871 166.08984 85.746094 C 168.20634 84.033559 171.35111 '.
  2547. '83.652548 173.67188 84.828125 C 175.01748 85.50974 200.19425 99.256274 200.56836 99.513672 C 201.52002 100.16843 202.5089 101.37978 203 102.49023 C 203.13162 '.
  2548. '102.78785 203.32969 103.40656 203.43945 103.86523 C 203.70946 104.99358 203.65191 106.59046 203.30273 107.66992 C 203.08072 108.35626 193.2629 127.83267 '.
  2549. '192.83203 128.44141 L 192.7168 128.60352 L 204.32227 128.60352 C 204.34489 128.54135 206.0865 125.32409 208.24219 121.36133 C 210.41184 117.37291 212.41514 '.
  2550. '113.62986 212.69531 113.04297 C 216.53579 104.99821 213.90717 95.127045 206.625 90.25 C 205.84937 89.730545 181.06362 76.260203 178.6582 75.050781 C 177.5495 '.
  2551. '74.493339 176.37806 74.054405 175.24219 73.771484 C 173.49309 73.335818 172.90843 73.270038 170.82812 73.273438 z"/></g>\n';
  2552. $ret .= '<g class="hs_locked" transform="translate(1,-2) scale(0.1,0.1)" visibility="'.$locks.'" style="fill:black">\n';
  2553. $ret .= '<path d="M 169.33398 90.152344 C 161.36399 90.152344 153.39548 90.185221 152.80078 90.25 C 145.74301 91.018795 139.82758 95.841827 137.60547 102.63867 C '.
  2554. '137.17355 103.9598 136.90608 105.2186 136.76367 106.60938 C 136.70267 107.20469 136.66992 111.55655 136.66992 119.02344 L 136.66992 130.49805 L 147.37695 '.
  2555. '130.49805 L 147.40234 118.94727 L 147.42773 107.28516 L 147.62109 106.60352 C 148.34477 104.05797 150.12767 102.25431 152.73633 101.42969 L 153.5332 101.17773 '.
  2556. 'L 169.24609 101.17773 L 184.95898 101.17773 L 185.70703 101.37695 C 186.11913 101.48679 186.71238 101.69727 187.02539 101.84375 C 189.21729 102.86946 190.84344 '.
  2557. '105.12709 191.19141 107.62891 C 191.25571 108.09078 191.28711 111.96821 191.28711 119.46289 L 191.28711 130.49805 L 201.99805 130.49805 L 201.99805 119.11719 C '.
  2558. '201.99805 111.72973 201.9653 107.37447 201.9043 106.74805 C 201.77551 105.42445 201.44824 103.83653 201.06445 102.67773 C 200.64348 101.40669 199.67547 99.408912 '.
  2559. '198.95703 98.326172 C 195.91418 93.74044 191.20002 90.830687 185.86914 90.25 C 185.27445 90.185221 177.30398 90.152344 169.33398 90.152344 z"/></g>\n';
  2560. #-- eye
  2561. $ret .= '<g class="hs_eye" transform="translate(18,24) scale(0.014,0.014)" visibility="'.$eyes.'" style="fill:black">\n';
  2562. $ret .= '<path d="M493.6,794C196.9,794,10,576.3,10,518.6C10,460.8,196.9,206,493.6,206C790.2,206,990,444,990,518.6C990,593.1,790.2,794,493.6,794z M480.2,368.2c-43.3,0-78.4,'.
  2563. '36.3-78.4,81c0,44.7,35.1,81,78.4,81c43.3,0,78.4-36.3,78.4-81C558.6,404.4,523.5,368.2,480.2,368.2z M715.6,327.7c28.6,39.3,48,79.2,48,132.1c0,129.9-102.1,235.2-228.1,'.
  2564. '235.2c-125.9,0-228-105.3-228-235.2c0-80.3,15.4-130.2,74.9-172.7C194.4,316,76,472.2,76,514.9c0,46.8,155.7,217.2,414.2,217.2c258.5,0,427.8-156.8,427.8-217.2C918,475.6,'.
  2565. '873.8,393.3,715.6,327.7z"/></g>\n';
  2566. $ret .= '</g></svg>';
  2567. return $ret
  2568. }
  2569. #########################################################################################
  2570. #
  2571. # YAAHM_modewidget - returns SVG code for inclusion into page
  2572. #
  2573. # Parameter hash = hash of device addressed
  2574. #
  2575. #########################################################################################
  2576. sub YAAHM_modewidget($){
  2577. my ($hash) = @_;
  2578. my $name = $hash->{NAME};
  2579. return ""
  2580. if( AttrVal($name,"noicons",0) == 1);
  2581. my $mode = $hash->{DATA}{"HSM"}{"mode"};
  2582. my ($color,$normals,$absents,$partys,$dnds);
  2583. if( $mode eq "normal" ){
  2584. $color=$csmode[0];
  2585. $normals="visible";
  2586. $partys="hidden";
  2587. $absents="hidden";
  2588. $dnds="hidden";
  2589. }elsif( $mode eq "party" ){
  2590. $color=$csmode[1];
  2591. $normals="hidden";
  2592. $partys="visible";
  2593. $absents="hidden";
  2594. $dnds="hidden";
  2595. }elsif( $mode eq "absence" ){
  2596. $color=$csmode[2];
  2597. $normals="hidden";
  2598. $partys="hidden";
  2599. $absents="visible";
  2600. $dnds="hidden";
  2601. }elsif( $mode eq "donotdisturb" ){
  2602. $color=$csmode[3];
  2603. $normals="hidden";
  2604. $partys="hidden";
  2605. $absents="hidden";
  2606. $dnds="visible";
  2607. }else{
  2608. Log 1,"[YAAHM_modewidget] Error, housemode $mode not defined";
  2609. return;
  2610. }
  2611. my $ret = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40" width="40px" height="40px">';
  2612. $ret .= ' <g transform="translate(6,5) scale(0.8,0.8)">';
  2613. $ret .= ' <circle class="hm_is" r="20" cx="20" cy="20" fill="'.$color.'" stroke="none"/>'.
  2614. ' <circle r="20" cx="20" cy="20" style="fill:none;stroke:black"/>';
  2615. $ret .= ' <g class="hm_n" transform="translate(0,-6) scale(0.08,0.08)" style="fill:black" visibility="'.$normals.'">'.
  2616. '<path d="m 313.166,240.194 c 16.213,0 29.35,-13.139 29.35,-29.351 0,-16.213 -13.137,-29.351 -29.35,-29.351 -16.214,0 -29.351,13.138 -29.351,29.351 0,16.212 13.137,29.351 29.351,29.351 z"/>'.
  2617. '<path d="m 346.165,252.835 c -1.794,-4.361 -6.008,-6.992 -10.449,-6.989 -0.034,-0.002 -0.069,-0.005 -0.103,-0.005 l -44.897,0 c -0.05,0 -0.099,0.005 -0.148,0.007 -4.462,-0.026 -8.704,2.607 '.
  2618. ' -10.506,6.987 l -34.027,82.713 -34.013,-82.677 c -1.156,-2.809 -4.349,-7.03 -10.858,-7.03 0,0 -33.673,0 -44.897,0 -0.041,0 -0.081,0.005 -0.121,0.006 -4.446,0.057 -8.666,2.623 -10.463,6.989 '.
  2619. ' l -41.854,101.739 c -2.371,5.764 0.379,12.359 6.143,14.73 1.405,0.578 2.859,0.852 4.289,0.852 4.438,0 8.647,-2.636 10.441,-6.995 l 18.433,-44.807 0,7.792 -21.053,65.773 c -3.541,11.061 '.
  2620. ' 4.711,22.368 16.326,22.368 l 4.727,0 0,52.187 c 0,8.382 6.944,15.141 15.393,14.841 8.047,-0.287 14.308,-7.145 14.308,-15.197 l 0,-51.831 11.758,0 0,52.187 c 0,8.381 6.943,15.141 '.
  2621. ' 15.392,14.841 8.048,-0.286 14.309,-7.145 14.309,-15.197 l 0,-51.83 4.727,0 c 11.614,0 19.866,-11.307 16.326,-22.368 l -21.053,-65.773 0,-8.423 18.707,45.473 c 1.793,4.359 '.
  2622. ' 6.003,6.995 10.441,6.995 0.887,0 1.782,-0.119 2.671,-0.337 0.84,0.194 1.687,0.301 2.525,0.301 4.438,0 8.647,-2.636 10.441,-6.995 l 18.504,-44.979 0,148.292 c 0,8.382 6.944,15.141 '.
  2623. ' 15.393,14.841 8.047,-0.287 14.308,-7.145 14.308,-15.197 l 0,-80.144 c 0,-3.132 2.363,-5.866 5.488,-6.068 3.424,-0.221 6.27,2.49 6.27,5.866 l 0,80.701 c 0,8.381 6.944,15.141 15.392,14.841 '.
  2624. ' 8.048,-0.286 14.309,-7.145 14.309,-15.197 l 0,-147.689 18.402,44.732 c 1.793,4.359 6.003,6.995 10.441,6.995 1.43,0 2.885,-0.274 4.29,-0.852 5.764,-2.371 8.514,-8.966 6.143,-14.73 L '.
  2625. ' 346.165,252.835 Z"/>'.
  2626. '<path d="m 178.716,240.194 c 16.213,0 29.35,-13.139 29.35,-29.351 0,-16.213 -13.137,-29.351 -29.35,-29.351 -16.214,0 -29.351,13.138 -29.351,29.351 0,16.212 13.138,29.351 29.351,29.351 z"/></g>';
  2627. $ret .= '<g class="hm_a" transform="translate(2.5,0) scale(0.125,0.125)" style="fill:black" visibility="'.$absents.'">'.
  2628. '<path d="M59.717,110.045L53.353,98.45c-6.704,3.681-13.257,7.81-19.482,12.274l7.711,10.746 C47.376,117.315,53.476,113.47,59.717,110.045z" />'.
  2629. '<path d="M99.411,94.105l-3.415-12.779c-7.389,1.975-14.738,4.424-21.841,7.277l4.929,12.274 C85.699,98.22,92.535,95.943,99.411,94.105z" />'.
  2630. '<path d="M230.536,95.09c-6.834-3.415-13.958-6.452-21.186-9.029l-4.44,12.459c6.726,2.396,13.356,5.222,19.714,8.401 L230.536,95.09z" />'.
  2631. '<path d="M285.464,136.504l-9.739,8.943c4.823,5.251,9.373,10.85,13.528,16.632L300,154.368 C295.538,148.152,290.649,142.14,285.464,136.504z" />'.
  2632. '<path d="M243.18,117.654c5.932,3.935,11.694,8.28,17.115,12.909l8.588-10.059c-5.826-4.977-12.016-9.646-18.398-13.874 L243.18,117.654z" />'.
  2633. '<path d="M0,141.823l10.054,8.593c4.629-5.416,9.64-10.605,14.888-15.422l-8.943-9.741C10.358,130.426,4.977,136.003,0,141.823z" />'.
  2634. '<path d="M106.286,100.191l6.644,0.004l8.061-12.223l25.91,0.181l-11.593,39.963c0,1.166,0.948,2.116,2.114,2.116h10.66 l22.266-41.295l20.437,0.679c5.817,0,10.524-4.455,'.
  2635. ' 10.524-9.951c0.004-5.491-4.711-9.946-10.519-9.946l-20.589,0.688 l-22.117-41.023l-10.665-0.002c-1.166,0.002-2.114,0.952-2.114,2.118l11.513,39.685l-25.97,'.
  2636. ' 0.225l-7.923-11.987l-6.644,0.002 c-0.884,0-1.598,0.712-1.598,1.594v37.582C104.688,99.479,105.404,100.196,106.286,100.191z" />'.
  2637. '<path d="M171.31,150.616c-8.657-1.973-17.503-2.974-26.307-2.974c-55.361,0-102.631,37.757-114.949,91.814 c-2.361,10.361-3.28,20.82-2.863,31.161h13.237v-0.003c-0.425-9.353,'.
  2638. ' 0.379-18.823,2.515-28.201 c4.329,1.122,23.682,6.492,23.067,12.719c-0.518,5.222-2.198,11.17-0.8,15.481h12.166c1.671-1.217,3.282-1.797,4.858-0.139 c0.042,0.046,0.097,'.
  2639. ' 0.093,0.141,0.139h96.071c-22.612-14.403-25.811-39.848-25.811-39.848c-1.596-0.694,2.969-18.768-4.14-20.939 c-7.12-2.169-11.608-0.43-21.691-4.929'.
  2640. ' c-10.096-4.499-6.316-10.786-4.658-25.789c0.708-6.402,2.337-12.133,4.413-16.636 c5.998-1.056,12.159-1.607,18.442-1.607c7.812,0,15.678,0.888,23.373,2.641c27.292,6.216,'.
  2641. ' 50.529,22.69,65.43,46.38 c11.233,17.864,16.705,38.217,16.059,58.848c-0.864,0.628-1.792,1.254-2.762,1.883h15.907 c1.049-23.743-5.088-47.224-18.01-67.771C228.221,'.
  2642. ' 176.164,202.05,157.617,171.31,150.616z" /></g>';
  2643. $ret .= '<g class="hm_p" transform="translate(7,4) scale(0.027,0.027)" style="fill:black" visibility="'.$partys.'">'.
  2644. '<path d="M64.6,951.8l114.8,36.8c16.1,5.1,33.2-3.7,38.5-19.8l0,0c5.1-16.1-3.7-33.2-19.8-38.5l-26.2-8.4l47.9-147.1c-10.3-1.8-20.5-4.3-30.5-7.5c-10.9-3.5-21.3-7.7-31.3-12.7l-48,'.
  2645. ' 147.4l-26.5-8.5c-16.1-5.1-33.2,3.7-38.5,19.8l0,0C39.7,929.4,48.5,946.6,64.6,951.8z" />'.
  2646. '<path d="M167.2,725.7c26.3,13.8,61,22.3,90.8,22.3c58.8,0,115.7-27.5,152.3-73.6c33.4-42,43.9-96,52.9-147.6c4.7-27.1,20.8-142.7,21.4-152.9c0.5-8.4,'.
  2647. ' 1.3-19.4-3-26.9c-3.7-6.7-10-11.8-17.3-14.1l-246.9-79c-3.1-1-6.2-1.5-9.3-1.5c-10.9,0-21.2,5.8-26.7,15.7c-31.4,56.6-62.7,113.8-88.9,173C78,473.2,64.9,506.3,63,'.
  2648. ' 541.9c-1.7,29.8,3.7,60,15.4,87.4C96,670.5,127.5,704.9,167.2,725.7z M222.6,319.7l199,63.7c-9.8,82.7-19.7,145.2-28.9,183.5l-95.5-30.5l-62-19.8l-95.5-30.5C154.4,449.3,'.
  2649. ' 182.6,392.8,222.6,319.7z" />'.
  2650. '<path d="M916.8,893.5l-26.5,8.5l-48-147.4c-10,4.9-20.4,9.2-31.3,12.7c-10,3.2-20.2,5.7-30.5,7.5l47.9,147l-26.2,8.4c-16.1,5.1-24.9,22.3-19.8,38.5l0,0c5.1,16.1,22.3,24.9,38.5,'.
  2651. ' 19.8l114.8-36.8c16.1-5.1,24.9-22.3,19.8-38.5l0,0C950.1,897.2,932.9,888.4,916.8,893.5z" />'.
  2652. '<path d="M515.2,355.5c-0.7,3.1-0.9,6.4-0.5,9.8c0.1,1.2,15.4,132,28.5,195.1c7,34,15.8,67.9,34.7,97.4c15.9,24.9,37.5,46.1,62.7,61.4c30.4,18.6,65.8,28.6,101.5,28.6c20.2,0,'.
  2653. ' 40.5-3.2,59.7-9.3c10.9-3.5,21.2-7.8,31-13c52.9-27.6,90.7-79.6,101.4-138.2c11.3-62.4-13.8-120.8-40.2-175.8c-23.4-48.8-49.2-96.3-75.4-143.6c-5.5-9.9-15.8-15.7-26.7-15.7c-3.1,'.
  2654. ' 0-6.2,0.5-9.3,1.5l-246.9,79C525.1,336.2,517.5,345,515.2,355.5z M777.4,319.7c40.1,73.1,68.3,129.6,83,166.1l-95.5,30.5l-62,19.8l-95.5,30.5c-9.2-38.3-19.1-100.6-28.9-183.5L777.4,319.7z" /></g>';
  2655. $ret .= '<g class="hm_dnd" transform="translate(8,8) scale(0.05,0.05)" style="fill:white" visibility="'.$dnds.'">'.
  2656. '<path d="M471,330v82h41V289H41V121c0-5.336-2-10.169-6-14.5c-4-4.333-8.833-6.5-14.5-6.5 S10,102.167,6,106.5s-6,9.167-6,14.5v291h41v-82H471z"/>'.
  2657. '<path d="M105.5,205c10.333,0,19.333-3.667,27-11c7.667-7.334,11.5-16.168,11.5-26.5 c0-10.335-3.833-19.168-11.5-26.5c-7.667-7.333-16.667-11-27-11s-19.167,3.667-26.5,11s-11,'.
  2658. ' 16.167-11,26.5s3.667,19.167,11,26.5 C86.333,201.333,95.167,205,105.5,205z"/>'.
  2659. '<path d="M512,258v-50c0-8-2.833-14.5-8.5-19.5c-5.67-5-12.837-8.167-21.5-9.5l-297-29h-2 c-5.333,0-9.833,1.833-13.5,5.5s-5.5,8.167-5.5,13.5v58H88c-12.667,0-19,5.167-19,'.
  2660. ' 15.5S75.333,258,88,258H512z"/></g></g>';
  2661. $ret .= '</svg>';
  2662. return $ret
  2663. }
  2664. #########################################################################################
  2665. #
  2666. # YAAHM_timewidget - returns SVG code for inclusion into any room page
  2667. #
  2668. # Parameter name = name of the YAAHM definition
  2669. #
  2670. #########################################################################################
  2671. sub YAAHM_timewidget($){
  2672. my ($arg) = @_;
  2673. my $name = $FW_webArgs{name};
  2674. $name =~ s/'//g;
  2675. my $sz = ($FW_webArgs{size} ? $FW_webArgs{size} : '400x400');
  2676. $sz =~ s/'//g;
  2677. my @size=split('x',$sz);
  2678. $FW_RETTYPE = "image/svg+xml";
  2679. $FW_RET="";
  2680. FW_pO '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" width="'.$size[0].'px" height="'.$size[1].'px">';
  2681. my $hash = $defs{$name};
  2682. my $radius = 250;
  2683. my ($sec, $min, $hour, $day, $month, $year, $wday,$yday,$isdst) = localtime(time);
  2684. my $t_now = sprintf("%02d:%02d",$hour,$min);
  2685. my $a_now = (60*$hour + $min)/1440 * 2 * pi;
  2686. my $x_now = -int(sin($a_now)*$radius*100)/100;
  2687. my $y_now = int(cos($a_now)*$radius*100)/100;
  2688. my $t_sunrise = defined($hash->{DATA}{"DD"}[0]{"sunrise"}) ? $hash->{DATA}{"DD"}[0]{"sunrise"} : "06:00";
  2689. $t_sunrise =~ s/^0//;
  2690. ($hour,$min) = split(":",$t_sunrise);
  2691. my $sr = $hour + $min*60;
  2692. my $a_sunrise = (60*$hour + $min)/1440 * 2 * pi;
  2693. my $x_sunrise = -int(sin($a_sunrise)*$radius*100)/100;
  2694. my $y_sunrise = int(cos($a_sunrise)*$radius*100)/100;
  2695. my $t_morning = defined($hash->{DATA}{"DT"}{"morning"}[0]) ? $hash->{DATA}{"DT"}{"morning"}[0] : "08:00";
  2696. $t_morning =~ s/^0//;
  2697. ($hour,$min) = split(":",$t_morning);
  2698. my $mo = $hour + $min*60;
  2699. my $a_morning = (60*$hour + $min)/1440 * 2 * pi;
  2700. my $x_morning = -int(sin($a_morning)*$radius*100)/100;
  2701. my $y_morning = int(cos($a_morning)*$radius*100)/100;
  2702. my $t_noon = defined($hash->{DATA}{"DT"}{"noon"}[0]) ? $hash->{DATA}{"DT"}{"noon"}[0] : "12:00";
  2703. $t_noon =~ s/^0//;
  2704. ($hour,$min) = split(":",$t_noon);
  2705. my $a_noon = (60*$hour + $min)/1440 * 2 * pi;
  2706. my $x_noon = -int(sin($a_noon)*$radius*100)/100;
  2707. my $y_noon = int(cos($a_noon)*$radius*100)/100;
  2708. my $t_afternoon = defined($hash->{DATA}{"DT"}{"afternoon"}[0]) ? $hash->{DATA}{"DT"}{"afternoon"}[0] : "14:00";
  2709. $t_afternoon =~ s/^0//;
  2710. ($hour,$min) = split(":",$t_afternoon);
  2711. my $a_afternoon = (60*$hour + $min)/1440 * 2 * pi;
  2712. my $x_afternoon = -int(sin($a_afternoon)*$radius*100)/100;
  2713. my $y_afternoon = int(cos($a_afternoon)*$radius*100)/100;
  2714. my $t_sunset = defined($hash->{DATA}{"DD"}[0]{"sunset"}) ? $hash->{DATA}{"DD"}[0]{"sunset"} : "18:00";
  2715. $t_sunset =~ s/^0//;
  2716. ($hour,$min) = split(":",$t_sunset);
  2717. my $ss = $hour + $min*60;
  2718. my $a_sunset = (60*$hour + $min)/1440 * 2 * pi;
  2719. my $x_sunset = -int(sin($a_sunset)*$radius*100)/100;
  2720. my $y_sunset = int(cos($a_sunset)*$radius*100)/100;
  2721. my $t_evening = defined($hash->{DATA}{"DT"}{"evening"}[0]) ? $hash->{DATA}{"DT"}{"evening"}[0] : "19:00";
  2722. $t_evening =~ s/^0//;
  2723. ($hour,$min) = split(":",$t_evening);
  2724. my $ev = $hour + $min*60;
  2725. my $a_evening = (60*$hour + $min)/1440 * 2 * pi;
  2726. my $x_evening = -int(sin($a_evening)*$radius*100)/100;
  2727. my $y_evening = int(cos($a_evening)*$radius*100)/100;
  2728. my $t_night = defined($hash->{DATA}{"DT"}{"night"}[0]) ? $hash->{DATA}{"DT"}{"night"}[0] : "22:00";
  2729. $t_night =~ s/^0//;
  2730. ($hour,$min) = split(":",$t_night);
  2731. my $a_night = (60*$hour + $min)/1440 * 2 * pi;
  2732. my $x_night = -int(sin($a_night)*$radius*100)/100;
  2733. my $y_night = int(cos($a_night)*$radius*100)/100;
  2734. my $t_midnight = "0:00";
  2735. $t_midnight =~ s/^0//;
  2736. ($hour,$min) = split(":",$t_midnight);
  2737. my $a_midnight = 0.0;
  2738. my $x_midnight = 0.0;
  2739. my $y_midnight = $radius;
  2740. FW_pO '<defs>'.
  2741. sprintf('<linearGradient id="grad1" x1="0%%" y1="0%%" x2="%d%%" y2="%d%%">',int(-$x_noon/$radius*100),int(-$y_noon/$radius*100)).
  2742. '<stop offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1" />'.
  2743. '<stop offset="100%" style="stop-color:rgb(255,100,0);stop-opacity:1" />'.
  2744. '</linearGradient>'.
  2745. sprintf('<linearGradient id="grad2" x1="%d%%" y1="%d%%" x2="0%%" y2="0%%">',int(-$x_noon/$radius*100),int(-$y_noon/$radius*100)).
  2746. '<stop offset="0%" style="stop-color:rgb(70,70,100);stop-opacity:1" />'.
  2747. '<stop offset="100%" style="stop-color:rgb(255,150,0);stop-opacity:1" />'.
  2748. '</linearGradient>'.
  2749. sprintf('<linearGradient id="grad3" x1="%d%%" y1="%d%%" x2="0%%" y2="0%%">',int(-$x_noon/$radius*100),int(-$y_noon/$radius*100)).
  2750. '<stop offset="0%" style="stop-color:rgb(80,80,80);stop-opacity:1" />'.
  2751. '<stop offset="100%" style="stop-color:rgb(120,120,100);stop-opacity:1" />'.
  2752. '</linearGradient>'.
  2753. '</defs>';
  2754. FW_pO '<g id="Ebene_1" transform="translate(400,400)">';
  2755. #-- daytime arc
  2756. 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" />';
  2757. #-- sunset to sunrise sector. We need a workaround here for the broken SVG engine of Firefox, splitting this in two arcs
  2758. 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)"/>';
  2759. 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)"/>';
  2760. #-- sunrise to morning sector
  2761. my $dir = ( $sr < $mo ) ? 0 : 1;
  2762. FW_pO '<path d="M 0 0 '.$x_sunrise.' '.$y_sunrise.' A '.$radius.' '.$radius.' 0 0 '.$dir.' '.$x_morning.' '.$y_morning.' Z" fill="url(#grad2)"/>';
  2763. #-- morning to evening sector
  2764. 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)"/>';
  2765. #-- evening to sunset sector
  2766. $dir = ( $ss < $ev ) ? 1 : 0;
  2767. FW_pO '<path d="M 0 0 '.$x_evening.' '.$y_evening.' A '.$radius.' '.$radius.' 0 0 '.$dir.' '.$x_sunset.' '.$y_sunset.' Z" fill="url(#grad2)"/>';
  2768. #-- midnight line
  2769. FW_pO '<line x1="0" y1="0" x2="0" y2="'.($radius*1.2).'" style="stroke:rgb(75, 75, 75);stroke-width:2" />';
  2770. 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>';
  2771. #--sunrise line
  2772. 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" />';
  2773. 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>';
  2774. #--morning line
  2775. 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" />';
  2776. 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>';
  2777. #--noon line
  2778. 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" />';
  2779. 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>';
  2780. #--afternoon line
  2781. 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" />';
  2782. 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>';
  2783. #--sunset line
  2784. 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" />';
  2785. 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>';
  2786. #--evening line
  2787. 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" />';
  2788. 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>';
  2789. #--night line
  2790. 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" />';
  2791. 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>';
  2792. #--now line
  2793. 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" />';
  2794. 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>';
  2795. FW_pO '</g>';
  2796. FW_pO '</svg>';
  2797. return ($FW_RETTYPE, $FW_RET);
  2798. }
  2799. #########################################################################################
  2800. #
  2801. # YAAHM_toptable - returns incomplete HTML code for inclusion into any room page
  2802. # (action and overview fields)
  2803. #
  2804. # Parameter name = name of the YAAHM definition
  2805. #
  2806. #########################################################################################
  2807. sub YAAHM_toptable($){
  2808. my ($name) = @_;
  2809. my $hash = $defs{$name};
  2810. if( !defined($yaahm_tt) ){
  2811. #-- readjust language
  2812. my $lang = AttrVal("global","language","EN");
  2813. if( $lang eq "DE"){
  2814. $yaahm_tt = \%yaahm_transtable_DE;
  2815. }else{
  2816. $yaahm_tt = \%yaahm_transtable_EN;
  2817. }
  2818. }
  2819. #-- something's rotten in the state of denmark
  2820. my $st = $hash->{DATA}{"DD"}[0]{"sunrise"};
  2821. my $ts;
  2822. my ($styl,$stym,$styr);
  2823. my $ret = "";
  2824. YAAHM_GetDayStatus($hash);
  2825. #--
  2826. my $lockstate = ($hash->{READINGS}{lockstate}{VAL}) ? $hash->{READINGS}{lockstate}{VAL} : "unlocked";
  2827. my $showhelper = ($lockstate eq "unlocked") ? 1 : 0;
  2828. %dailytable = %{$hash->{DATA}{"DT"}};
  2829. my $dailyno = scalar keys %dailytable;
  2830. my $weeklyno = int( @{$hash->{DATA}{"WT"}} );
  2831. #--
  2832. $ret .= "<script type=\"text/javascript\" src=\"$FW_ME/pgm2/yaahm.js\"></script><script type=\"text/javascript\">\n";
  2833. $ret .= "var csmode = [\"".$csmode[0]."\",\"".$csmode[1]."\",\"".$csmode[2]."\",\"".$csmode[3]."\"];";
  2834. $ret .= "var csstate = [\"".$csstate[0]."\",\"".$csstate[1]."\",\"".$csstate[2]."\",\"".$csstate[3]."\"];";
  2835. $ret .= "var blinking = 0;\n";
  2836. $ret .= "var hscolor = \"".$csstate[0]."\";\n";
  2837. $ret .= "var dailyno = ".$dailyno.";\n";
  2838. $ret .= "var dailykeys = [\"".join("\",\"",(sort YAAHM_dsort keys %dailytable))."\"];\n";
  2839. $ret .= "var weeklyno = ".$weeklyno.";\n";
  2840. $ret .= "var weeklykeys = [\"".join("\",\"",@weeklytable)."\"];\n"; # Day names !!!
  2841. $ret .= "var weeklynames = [";
  2842. for( my $i=0;$i<$weeklyno;$i++){
  2843. $ret .= ","
  2844. if( $i!=0 );
  2845. $ret .= "\"".$hash->{DATA}{"WT"}[$i]{"name"}."\"";
  2846. }
  2847. $ret .= "];\n";
  2848. #-- watcher for next hidden divisions
  2849. for( my $i=2;$i<$weeklyno;$i++){
  2850. $ret .= "\$(\"body\").on('DOMSubtreeModified', \"#wt".$i."_o\",function () {nval = document.getElementById(\"wt".$i."_o\").innerHTML;document.getElementById(\"wt".$i."_n\").value = nval;})\n";
  2851. }
  2852. $ret .= "</script>\n";
  2853. $ret .= "<div informId=\"$name-housestate\" style=\"display:none\" id=\"hid_hs\">".ReadingsVal($name,"housestate",undef)."</div>".
  2854. "<div informId=\"$name-housemode\" style=\"display:none\" id=\"hid_hm\">".ReadingsVal($name,"housemode",undef)."</div>";
  2855. $ret .= "<table class=\"roomoverview\">\n";
  2856. $ret .= "<tr><td colspan=\"3\"><div class=\"devType\" style=\"font-weight:bold\">".$yaahm_tt->{"action"}.
  2857. "</div> <div informId=\"$name-tr_errmsg\" class=\"devType\" style=\"font-weight:normal\">".ReadingsVal($name,"tr_errmsg",undef)."</div></td></tr>\n";
  2858. ### action ################################################################################################
  2859. #-- determine columns
  2860. my $cols = max(max(int(@modes),int(@states)),$weeklyno);
  2861. $ret .= "<tr><td colspan=\"3\" style=\"align:left\"><table class=\"readings\" style=\"border:1px solid gray;border-radius:10px\">".
  2862. "<tr><td rowspan=\"2\" height=\"40\" valign=\"bottom\" id=\"wid_hm\">".YAAHM_modewidget($hash)."</td><td colspan=\"5\" height=\"20\"></td></tr>".
  2863. "<tr class=\"odd\"><td width=\"100px\" class=\"dname\" style=\"padding:5px;\">".$yaahm_tt->{"mode"}."</td>".
  2864. "<td width=\"120px\"><div class=\"dval\" informId=\"$name-tr_housemode\">".ReadingsVal($name,"tr_housemode",undef)."</div></td><td></td>";
  2865. for( my $i=0; $i<$cols; $i++){
  2866. if( $i < int(@modes)){
  2867. $ret .= "<td width=\"120px\"><input type=\"button\" id=\"b_".$modes[$i]."\" value=\"".$yaahm_tt->{$modes[$i]}.
  2868. "\" style=\"width:120px;\" onclick=\"javascript:yaahm_mode('$name','".$modes[$i]."')\"/></td>";
  2869. }else{
  2870. $ret .= "<td width=\"120px\"></td>";
  2871. }
  2872. }
  2873. $ret .= "</tr>";
  2874. $ret .= "<tr class=\"even\"><td rowspan=\"2\" height=\"40\" id=\"wid_hs\">".YAAHM_statewidget($hash)."</td>".
  2875. "<td class=\"dname\" style=\"padding:5px;\">".$yaahm_tt->{"state"}."</td>".
  2876. "<td><div informId=\"$name-tr_housestate\">".ReadingsVal($name,"tr_housestate",undef).
  2877. "</div></td><td style=\"width:20px\"><div id=\"sym_hs\" informId=\"$name-sym_housestate\" style=\"align:center\">".ReadingsVal($name,"sym_housestate",undef)."</div></td>";
  2878. for( my $i=0; $i<$cols; $i++){
  2879. if( $i < int(@states)){
  2880. $ret .= "<td width=\"120px\"><input type=\"button\" id=\"b_".$states[$i]."\" value=\"".$yaahm_tt->{$states[$i]}.
  2881. "\" style=\"width:120px;\" onclick=\"javascript:yaahm_state('$name','".$states[$i]."')\"/></td>";
  2882. }else{
  2883. $ret .= "<td width=\"120px\"></td>";
  2884. }
  2885. }
  2886. #style=\"height:20px;border-bottom: 10px solid #333333;background-image: linear-gradient(#e5e5e5,#ababab);\"
  2887. $ret .= "</tr><td colspan=\"5\" height=\"20\"></td></tr>";
  2888. $ret .= "</table><br/><table class=\"readings\">";
  2889. #-- repeat manual next for every weekly table
  2890. my $nval = "";
  2891. my $wupn;
  2892. $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>\n";
  2893. for (my $i=0;$i<$weeklyno;$i++){
  2894. if($i<$weeklyno-1){
  2895. $styl= "border-bottom:1px solid gray;border-top:1px solid gray";
  2896. }else{
  2897. $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";
  2898. }
  2899. $wupn = $hash->{DATA}{"WT"}[$i]{"name"};
  2900. $nval = ( defined($hash->{DATA}{"WT"}[$i]{"next"}) ) ? $hash->{DATA}{"WT"}[$i]{"next"} : "";
  2901. $ret .= sprintf("<td class=\"col2\" style=\"text-align:left;padding:5px;padding-left:10px;padding-right:10px;$styl\">$wupn<br/>".
  2902. "<div style=\"display:none\" id=\"wt%d_o\" informId=\"$name-next_$i\">$nval</div><input type=\"text\" id=\"wt%d_n\" size=\"4\" maxlength=\"120\" value=\"$nval\" onchange=\"javascript:yaahm_setnext('$name',%d)\"/></td>\n",$i,$i,$i);
  2903. }
  2904. $ret .= "</tr>\n";
  2905. $ret .= "</table><br/></td></tr>";
  2906. $ret .= "<tr><td colspan=\"3\"><div class=\"devType\" style=\"font-weight:bold\">".$yaahm_tt->{"overview"}."</div></td></tr>\n";
  2907. ### daily overview ################################################################################################
  2908. $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;";
  2909. $stym="border-top:1px solid gray;border-radius:0px;";
  2910. $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;";
  2911. $ret .= "<tr><td colspan=\"3\"><table>";
  2912. #-- time widget
  2913. $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>";
  2914. #-- continue summary with headers
  2915. $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>";
  2916. #-- device states
  2917. $ret .= "<td rowspan=\"8\" style=\"padding:5px;vertical-align:top;border:1px solid gray;border-radius:10px\">".
  2918. "<div class=\"dval\" informId=\"$name-tr_housestate\">".ReadingsVal($name,"tr_housestate",undef)."</div>&#x2192;".
  2919. $yaahm_tt->{"secstate"}."<div class=\"dval\" informId=\"$name-sdev_housestate\">".ReadingsVal($name,"sdev_housestate","")."</div></td></tr>\n";
  2920. $styl="border-left:1px solid gray;border-radius:0px;";
  2921. $stym="border:none";
  2922. $styr="border-right:1px solid gray;border-radius:0px;";
  2923. $ret .= "<tr><td colspan=\"2\" style=\"$styl\"></td><td style=\"padding:5px;$stym\">".$yaahm_tt->{$weeklytable[$hash->{DATA}{"DD"}[0]{"weekday"}]}[0] .
  2924. "</td><td style=\"padding:5px;$styr\">".$yaahm_tt->{$weeklytable[$hash->{DATA}{"DD"}[1]{"weekday"}]}[0]."</td></tr>";
  2925. #-- continue summary with entries
  2926. $ret .= "<tr><td colspan=\"2\" style=\"$styl\"></td><td style=\"padding:5px;$stym\">".$hash->{DATA}{"DD"}[0]{"date"}.
  2927. "</td><td style=\"padding:5px;$styr\">".$hash->{DATA}{"DD"}[1]{"date"}."</td></tr>\n";
  2928. $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"}.
  2929. "</td><td style=\"padding:5px;$styr\">".$hash->{DATA}{"DD"}[1]{"sunrise"}."-".$hash->{DATA}{"DD"}[1]{"sunset"}."</td></tr>\n";
  2930. $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]."-".
  2931. $hash->{DATA}{"DT"}{"night"}[0]."</td><td style=\"$styr\"></td></tr>\n";
  2932. $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"}}.
  2933. "</td><td style=\"padding:5px;$styr\">".$yaahm_tt->{$hash->{DATA}{"DD"}[1]{"daytype"}}."</td></tr>\n";
  2934. $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"}.
  2935. "</td><td style=\"padding:5px;width:100px;$styr\">".$hash->{DATA}{"DD"}[1]{"desc"}."</td></tr>\n";
  2936. $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;";
  2937. $stym="border-bottom:1px solid gray;border-radius:0px;";
  2938. $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;";
  2939. $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"}.
  2940. "</td><td style=\"padding:5px;width:100px;$styr\">".$hash->{DATA}{"DD"}[1]{"special"}."</td></tr>\n";
  2941. #-- housetime/phase
  2942. $ret .= "<tr><td rowspan=\"".$weeklyno."\" style=\"text-align:center; white-space:nowrap;max-height:20px\">".
  2943. "<label><div class=\"dval\" informId=\"$name-tr_housetime\">".ReadingsVal($name,"tr_housetime","").
  2944. "</div>&nbsp;<div class=\"dval\" informId=\"$name-tr_housephase\">".ReadingsVal($name,"tr_housephase","")."</div></label></td>";
  2945. #-- weekly timers
  2946. for( my $i=0;$i<$weeklyno;$i++ ){
  2947. $wupn = $hash->{DATA}{"WT"}[$i]{"name"};
  2948. #-- timer status
  2949. if( defined($defs{$name.".wtimer_".$i.".IF"})){
  2950. if( ReadingsVal($name.".wtimer_".$i.".IF","mode","") ne "disabled" ){
  2951. $ts = "<div style=\"color:green\">&#x2713;</div>";
  2952. }else{
  2953. $ts = "<div style=\"color:red\">&#x274c;</div>";
  2954. }
  2955. }else{
  2956. $ts = "";
  2957. }
  2958. #-- ring times
  2959. my $ring_0x = $hash->{DATA}{"WT"}[$i]{"ring_0x"};
  2960. my $ring_1x = $hash->{DATA}{"WT"}[$i]{"ring_1x"};
  2961. my $wake = $hash->{DATA}{"WT"}[$i]{"wake"};
  2962. #-- border styles
  2963. if( $i==0 ){
  2964. $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;";
  2965. $stym="border-top:1px solid gray;border-radius:0px;";
  2966. $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;";
  2967. }elsif( $i == $weeklyno-1 ){
  2968. $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;";
  2969. $stym="border-bottom:1px solid gray;border-radius:0px;";
  2970. $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;";
  2971. }else{
  2972. $styl="border-left:1px solid gray;border-radius:0px;";
  2973. $stym="border:none";
  2974. $styr="border-right:1px solid gray;border-radius:0px;";
  2975. }
  2976. $ret.="<td style=\"padding:5px;$styl\">".$wupn.
  2977. "</td><td style=\"text-align:center;width:30px;padding:5px;$stym\">$ts</td>".
  2978. "<td style=\"padding:5px;$stym\"><div class=\"dval\" informId=\"$name-ring_".$i."x\">".$ring_0x."</div></td>".
  2979. "<td style=\"padding:5px;$stym\"><div class=\"dval\" informId=\"$name-ring_".$i."_1x\">".$ring_1x."</div></td>".
  2980. "<td style=\"padding:5px;$styr\"><div class=\"dval\" informId=\"$name-tr_wake_$i\">".$wake."</div></td></tr>\n";
  2981. }
  2982. $ret .= "</table></td></tr>\n";
  2983. return $ret;
  2984. }
  2985. #########################################################################################
  2986. #
  2987. # YAAHM_Shorttable - returns complete HTML code for inclusion into any room page
  2988. # (action and overview fields)
  2989. #
  2990. # Parameter name = name of the YAAHM definition
  2991. #
  2992. #########################################################################################
  2993. sub YAAHM_Shorttable($){
  2994. my ($name) = @_;
  2995. my $ret = YAAHM_toptable($name);
  2996. #-- complete the code of the page
  2997. $ret .= "</table>";
  2998. InternalTimer(gettimeofday()+ 1, "YAAHM_informer", $defs{$FW_cname},0);
  2999. return $ret;
  3000. }
  3001. #########################################################################################
  3002. #
  3003. # YAAHM_Longtable - returns complete HTML code for the full YAAHM page
  3004. # (action, overview, daily and weekly profile)
  3005. #
  3006. # Parameter name = name of the YAAHM definition
  3007. #
  3008. #########################################################################################
  3009. sub YAAHM_Longtable($){
  3010. my ($name) = @_;
  3011. my $ret = "";
  3012. my $hash = $defs{$name};
  3013. my $id = $defs{$name}{NR};
  3014. #--
  3015. my $lockstate = ($hash->{READINGS}{lockstate}{VAL}) ? $hash->{READINGS}{lockstate}{VAL} : "unlocked";
  3016. my $showhelper = ($lockstate eq "unlocked") ? 1 : 0;
  3017. %dailytable = %{$hash->{DATA}{"DT"}};
  3018. my $dailyno = scalar keys %dailytable;
  3019. my $weeklyno = int( @{$hash->{DATA}{"WT"}} );
  3020. #--
  3021. $ret = YAAHM_toptable($name);
  3022. ### daily profile table ################################################################################################
  3023. my $row = 1;
  3024. my $event = "";
  3025. my $sval = "";
  3026. my $eval = "";
  3027. my $xval = "";
  3028. my $dh = (defined($attr{$name}{"timeHelper"})) ? $attr{$name}{"timeHelper"} : undef;
  3029. #-- global status of timer
  3030. my ($tl,$ts);
  3031. if( defined($defs{$name.".dtimer.IF"})){
  3032. $tl = "<a href=\"/fhem?detail=$name.dtimer.IF\">$name.dtimer.IF</a>";
  3033. #-- green hook
  3034. if( ReadingsVal($name.".dtimer.IF","mode","") ne "disabled" ){
  3035. $ts = "<td style=\"color:green;padding-left:5px\">&#x2713;</td>\n";
  3036. #-- red cross
  3037. }else{
  3038. $ts = "<td style=\"color:red;padding-left:5px\">&#x274c;</td>\n";
  3039. }
  3040. }else{
  3041. $tl= $yaahm_tt->{"notstarted"};
  3042. $ts ="<td></td>";
  3043. }
  3044. #-- name link button status
  3045. $ret .= "<tr><td colspan=\"3\"><div class=\"devType\" style=\"font-weight:bold; white-space:nowrap;\">".$yaahm_tt->{"daily"}.$yaahm_tt->{"profile"}."\n".
  3046. "<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";
  3047. $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";
  3048. $ret .= "</tr></table></div></td></tr>";
  3049. #-- header line
  3050. $ret .= "<tr><td colspan=\"3\"><table class=\"block wide\" id=\"dailytable\">\n";
  3051. $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;\">".
  3052. $yaahm_tt->{"time"}." [hh:mm]&nbsp;&nbsp;&nbsp;</td>".
  3053. "<td rowspan=\"2\" class=\"devType col3\" style=\"min-width:200px;\">".$yaahm_tt->{"action"}."</td>".
  3054. "</tr>".
  3055. "<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";
  3056. #-- iterate through table
  3057. foreach my $key (sort YAAHM_dsort keys %dailytable){
  3058. $row++;
  3059. $event= $yaahm_tt->{$key};
  3060. $sval = $dailytable{$key}[0];
  3061. $eval = $dailytable{$key}[1];
  3062. $xval = $dailytable{$key}[2];
  3063. $xval = "" if( !defined($xval) );
  3064. #--
  3065. if( $dh ){
  3066. #-- timeHelper not in command list
  3067. if( $xval !~ /^{$dh/ ){
  3068. if( defined($xval) && length($xval)>0 ){
  3069. $xval ="{".$dh."('".$key."')},".$xval;
  3070. }else{
  3071. $xval ="{".$dh."('".$key."')}";
  3072. }
  3073. }
  3074. }
  3075. $ret .= sprintf("<tr class=\"%s\"><td class=\"col1\">$event</td>\n", ($row&1)?"odd":"even");
  3076. #-- First field
  3077. #-- Only reference for wakeup
  3078. if( $key =~ /^wakeup.*/ ){
  3079. $ret .= "<td class=\"col2\" style=\"text-align:left\">".$yaahm_tt->{"weekly"}.$yaahm_tt->{"profile"}."</td><td></td><td></td></tr>\n";
  3080. next;
  3081. #-- Only reference for sleep
  3082. }elsif( $key =~ /^sleep.*/ ){
  3083. $ret .= "<td class=\"col2\" style=\"text-align:left\">".$yaahm_tt->{"weekly"}.$yaahm_tt->{"profile"}."</td><td></td><td></td></tr>\n";
  3084. next;
  3085. #-- calculated value for sunrise/sunset
  3086. }elsif( $key =~ /.*((sunrise)|(sunset)|(midnight)).*/ ){
  3087. my $pre;
  3088. if( $key =~ /.*sunrise.*/ ){
  3089. $pre = $hash->{DATA}{"DD"}[0]{"sunrise"}
  3090. }elsif( $key =~ /.*sunset.*/ ){
  3091. $pre = $hash->{DATA}{"DD"}[0]{"sunset"}
  3092. }else{
  3093. $pre = "00:00";
  3094. }
  3095. $ret .= "<td class=\"col2\" style=\"text-align:right\">";
  3096. if( $key =~ /.*before.*/ ){
  3097. $ret .= "$pre - &nbsp;<input type=\"text\" id=\"dt".$key."_e\" size=\"4\" maxlength=\"120\" value=\"$eval\"/>&nbsp;=&nbsp;$sval&nbsp;&nbsp;&nbsp;&nbsp;</td>"
  3098. }elsif( $key =~ /.*after.*/ ){
  3099. $ret .= "$pre + &nbsp;<input type=\"text\" id=\"dt".$key."_e\" size=\"4\" maxlength=\"120\" value=\"$eval\"/>&nbsp;=&nbsp;$sval&nbsp;&nbsp;&nbsp;&nbsp;</td>"
  3100. }else{
  3101. $ret .= "$pre&nbsp;&nbsp;&nbsp;&nbsp;</td>"
  3102. }
  3103. #-- normal input of one time
  3104. }else{
  3105. $ret .= "<td class=\"col2\" style=\"text-align:right\"><input type=\"text\" id=\"dt".$key."_s\" size=\"4\" maxlength=\"120\" value=\"$sval\"/></td>";
  3106. }
  3107. #-- Second field
  3108. $ret .= "<td class=\"col3\"><input type=\"text\" id=\"dt".$key."_x\" size=\"28\" maxlength=\"512\" value=\"$xval\"/></td></tr>\n";
  3109. }
  3110. $ret .= "</table></td></tr></tr>";
  3111. ### weekly profile table ################################################################################################
  3112. $row = 1;
  3113. $event = "";
  3114. $sval = "";
  3115. my $wupn;
  3116. my $wt = ( $weeklyno == 1) ? $yaahm_tt->{"profile"} :$yaahm_tt->{"profiles"};
  3117. $ret .= "<tr><td colspan=\"3\"><div class=\"devType\" style=\"font-weight:bold; white-space: nowrap;\">".$yaahm_tt->{"weekly"}.$wt.
  3118. "&nbsp;&nbsp;<input type=\"button\" value=\"".$yaahm_tt->{"start"}." ".$yaahm_tt->{"weekly"}.$yaahm_tt->{"timer"}."\" onclick=\"javascript:yaahm_startWeeklyTimer('$name')\"/></div></td></tr>";
  3119. $ret .= "<tr><td><table class=\"readings\" id=\"weeklytable\">\n";
  3120. #-- repeat name for every weekly table
  3121. $ret .= "<tr class=\"odd\"><td class=\"col1\" style=\"font-weight:bold;text-align:left;padding:5px\">".$yaahm_tt->{"name"}."</td>";
  3122. for (my $i=0;$i<$weeklyno;$i++){
  3123. $wupn = $hash->{DATA}{"WT"}[$i]{"name"};
  3124. $ret .= "<td class=\"col2\" style=\"text-align:left;padding:5px\">$wupn</td>";
  3125. }
  3126. $ret .= "</tr>\n";
  3127. #-- repeat link for every weekly table
  3128. $ret .= "<tr class=\"even\"><td class=\"col1\" style=\"font-weight:bold;text-align:left;padding:5px\">".$yaahm_tt->{"timer"}."</td>";
  3129. #-- array with activity status
  3130. my @tss;
  3131. for (my $i=0;$i<$weeklyno;$i++){
  3132. $wupn = $hash->{DATA}{"WT"}[$i]{"name"};
  3133. #-- timer status
  3134. if( defined($defs{$name.".wtimer_".$i.".IF"})){
  3135. $tl = "<a href=\"/fhem?detail=".$name.".wtimer_".$i.".IF\">".$name.".wtimer_".$i.".IF</a>";
  3136. if( ReadingsVal($name.".wtimer_".$i.".IF","mode","") ne "disabled" ){
  3137. push(@tss,"<div style=\"color:green\">&#x2713;</div>");
  3138. }else{
  3139. push(@tss,"<div style=\"color:red\">&#x274c;</div>");
  3140. }
  3141. }else{
  3142. $tl = $yaahm_tt->{"notstarted"};
  3143. push(@tss,"");
  3144. }
  3145. $ret .= sprintf("<td class=\"col2\" style=\"text-align:left;padding:5px\"><div id=\"wt%dlink\">%s</div></td>",$i,$tl);
  3146. }
  3147. $ret .= "</tr>\n";
  3148. #-- repeat active status for every weekly table
  3149. my $asg = "";
  3150. my $ast = "";
  3151. my $ass;
  3152. my $acc;
  3153. #--header
  3154. for(my $i=0;$i<int(@profmode);$i++){
  3155. $asg .= substr($yaahm_tt->{$profmode[$i]},0,3)."&nbsp;";
  3156. $ast .= $yaahm_tt->{$profmode[$i]}." ";
  3157. }
  3158. for(my $i=0;$i<int(@profday);$i++){
  3159. $asg .= substr($yaahm_tt->{$profday[$i]},0,3)."&nbsp;";
  3160. $ast .= $yaahm_tt->{$profday[$i]}." ";
  3161. }
  3162. $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>";
  3163. for (my $i=0;$i<$weeklyno;$i++){
  3164. $wupn = $hash->{DATA}{"WT"}[$i]{"name"};
  3165. $ret .= "<td class=\"col2\" style=\"text-align:center;padding:5px\">".$tss[$i]."</br>";
  3166. $asg = "";
  3167. $ass = ( defined($hash->{DATA}{"WT"}[$i]{"acti_m"}) ) ? $hash->{DATA}{"WT"}[$i]{"acti_m"} : "";
  3168. for( my $j=0;$j<int(@profmode);$j++ ){
  3169. $acc = $profmode[$j];
  3170. $acc = ( $ass =~ /.*$acc.*/ ) ? " checked=\"checked\"" : "";
  3171. $asg .= sprintf("<input type=\"checkbox\" name=\"acti_%d_m\" value=\"".$profmode[$j]."\" $acc/>&nbsp;",$i);
  3172. }
  3173. $ass = ( defined($hash->{DATA}{"WT"}[$i]{"acti_d"}) ) ? $hash->{DATA}{"WT"}[$i]{"acti_d"} : "";
  3174. for( my $j=0;$j<int(@profday);$j++ ){
  3175. $acc = $profday[$j];
  3176. $acc = ( $ass =~ /.*$acc.*/ ) ? " checked=\"checked\"" : "";
  3177. $asg .= sprintf("<input type=\"checkbox\" name=\"acti_%d_d\" value=\"".$profday[$j]."\" $acc/>&nbsp;",$i);
  3178. }
  3179. $ret .= "$asg</td>";
  3180. }
  3181. $ret .= "</tr>\n";
  3182. #-- repeat action for every weekly table
  3183. $ret .= "<tr class=\"odd\"><td class=\"col1\" style=\"font-weight:bold;text-align:left;padding:5px\">".$yaahm_tt->{"action"}."</td>";
  3184. for (my $i=0;$i<$weeklyno;$i++){
  3185. $xval = $hash->{DATA}{"WT"}[$i]{"action"};
  3186. #--
  3187. if( $dh && $i<2 ){
  3188. #-- timeHelper not in command list
  3189. $wupn = ($i==0) ? "wakeup" : "sleep";
  3190. if( $xval !~ /^{$dh/ ){
  3191. if( defined($xval) && length($xval)>0 ){
  3192. $xval ="{".$dh."('".$wupn."')},".$xval;
  3193. }else{
  3194. $xval ="{".$dh."('".$wupn."')}";
  3195. }
  3196. }
  3197. }
  3198. $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);
  3199. }
  3200. $ret .= "</tr>\n";
  3201. #-- repeat unit for every weekly table
  3202. $ret .= "<tr class=\"even\"><td></td>";
  3203. for (my $i=0;$i<$weeklyno;$i++){
  3204. $ret .= "<td class=\"col2\" style=\"text-align:left;padding:5px\">".$yaahm_tt->{"time"}." [hh:mm]</td>";
  3205. }
  3206. $ret .= "</tr>\n";
  3207. #-- weekday header
  3208. $ret .= "<tr class=\"even\"><td class=\"col1\" style=\"font-weight:bold;text-align:left;padding:5px\">".$yaahm_tt->{"weekday"}."</td>";
  3209. for (my $i=0;$i<$weeklyno;$i++){
  3210. $ret .= "<td></td>";
  3211. }
  3212. $ret .= "</tr>\n";
  3213. for (my $j=0;$j<7;$j++){
  3214. my $key = $weeklytable[$j];
  3215. $row++;
  3216. $event = $yaahm_tt->{$key}[0];
  3217. $ret .= sprintf("<tr class=\"%s\"><td class=\"col1\" style=\"text-align:left;padding-left:5px\">$event</td>\n", ($row&1)?"odd":"even");
  3218. for (my $i=0;$i<$weeklyno;$i++){
  3219. $sval = $hash->{DATA}{"WT"}[$i]{$key};
  3220. $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);
  3221. }
  3222. $ret .= "</tr>\n";
  3223. }
  3224. $ret .= "</table></td></tr>";
  3225. #-- complete the code of the page
  3226. $ret .= "</table>";
  3227. #InternalTimer(gettimeofday()+ 3, "YAAHM_informer", $defs{$FW_cname},0);
  3228. return $ret;
  3229. }
  3230. 1;
  3231. =pod
  3232. =item helper
  3233. =item summary admimistration of profiles for daily, weekly and monthly processes
  3234. =item summary_DE Verwaltung von Profilen für tägliche, wöchentliche und monatliche Abläufe
  3235. =begin html
  3236. <a name="YAAHM"></a>
  3237. <h3>YAAHM</h3>
  3238. <ul>
  3239. <p> Yet Another Auto Home Module to set up a cyclic processing of commands (daily, weekly, monthly, yearly profile)</p>
  3240. <a name="YAAHMusage"></a>
  3241. <h4>Usage</h4>
  3242. See <a href="http://www.fhemwiki.de/wiki/Modul_YAAHM">German Wiki page</a>
  3243. <br/>
  3244. <a name="YAAHMdefine"></a>
  3245. <h4>Define</h4>
  3246. <p>
  3247. <code>define &lt;name&gt; YAAHM</code>
  3248. <br />Defines the YAAHM system. </p>
  3249. <a name="YAAHMset"></a>
  3250. Notes: <ul>
  3251. <li>This module uses the global attribute <code>language</code> to determine its output data<br/>
  3252. (default: EN=english). For German output set <code>attr global language DE</code>.</li>
  3253. <li>This module needs the JSON package</li>
  3254. </ul>
  3255. <h4>Set</h4>
  3256. <ul>
  3257. <li><a name="yaahm_time">
  3258. <code>set &lt;name&gt; time &lt;timeevent&gt;</code></a><br/>
  3259. Set the current house time (event), i.e. one of several values:
  3260. <ul>
  3261. <li>(after|before) midnight | [before|after] sunrise | [before|after] sunset are calculated from astronomical data (&pm;offset).
  3262. These values vary from day to day, only the offset can be specified in the daily profile. </li>
  3263. <li>morning | noon | afternoon | evening | night are fixed time events specified in the daily profile.
  3264. The time phase between events morning and night is called <i>daytime</i>, the
  3265. time phase between events night and morning is called <i>nighttime</i></li>
  3266. <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>
  3267. </ul>
  3268. 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>.
  3269. </li>
  3270. <li><a name="yaahm_manualnext"></a>
  3271. <code>set &lt;name&gt; manualnext &lt;timernumber&gt; &lt;time&gt;</code><br/>
  3272. <code>set &lt;name&gt; manualnext &lt;timername&gt; &lt;time&gt;</code><br/>
  3273. 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", or "default".
  3274. <ul>
  3275. <li>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.</li>
  3276. <li>If the time specification is "off", the next pending waketime will be ignored.</li>
  3277. <li>If the time specification id "default", the manual waketime is removed and the value from the weekly schedule will be used.</li>
  3278. </ul>
  3279. </li>
  3280. <li><a name="yaahm_mode">
  3281. <code>set &lt;name&gt; mode normal | party | absence | donotdisturb</code>
  3282. </a>
  3283. <br />Set the current house mode, i.e. one of several values:
  3284. <ul>
  3285. <li>normal - normal daily and weekly time profiles apply</li>
  3286. <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>
  3287. <li>absence - can be used in the timeHelper function to suppress certain actions. Valid until manual mode change</li>
  3288. <li>donotodisturb - can be used in the timeHelper function to suppress certain actions. Valid until manual mode change</li>
  3289. </ul>
  3290. 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.
  3291. The actual changes to certain devices are made by an external <a href="#yaahm_modehelper">modeHelper function</a>.
  3292. </li>
  3293. <li><a name="yaahm_state">
  3294. <code>set &lt;name&gt; state unsecured | secured | protected | guarded</code>
  3295. </a>
  3296. <br/>Set house (security) state, i.e. one of several values:
  3297. <ul>
  3298. <li> unsecured - Example: doors etc.
  3299. </li>
  3300. <li> secured - Example: doors etc. are locked, windows may not be open
  3301. </li>
  3302. <li> protected - Example: doors etc. are locked, windows may not be open, alarm system is armed
  3303. </li>
  3304. <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
  3305. </li>
  3306. </ul>
  3307. 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.
  3308. 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
  3309. for a particular house (security) state can be checked automatically, see the attribute <a href="#yaahm_statedevices">stateDevices</a>
  3310. </li>
  3311. <li><a name="yaahm_checkstate">
  3312. <code>set &lt;name&gt; checkstate 0|5|10</code>
  3313. </a>
  3314. <br/>Check the house (security) state for each of the devices defined in the stateDevices attribute in 0, 5 or 10 seconds from now</li>
  3315. <li><a name="yaahm_correctstate">
  3316. <code>set &lt;name&gt; correctstate</code>
  3317. </a>
  3318. <br/>Try to correct the house (security) state, by issueing a FHEM command <i>set &lt;device&gt; &lt;targetstate&gt;</i>
  3319. for each of the devices defined in the stateDevices attribute and not in the proper state for the given house (security) state</li>
  3320. <li><a name="yaahm_createweekly">
  3321. <code>set &lt;name&gt; createWeekly &lt;string&gt;</code>
  3322. </a>
  3323. <br/>Create a new weekly profile &lt;string&gt;</li>
  3324. <li><a name="yaahm_deleteweekly">
  3325. <code>set &lt;name&gt; deleteWeekly &lt;string&gt;</code>
  3326. </a>
  3327. <br/>Delete the weekly profile &lt;string&gt;</li>
  3328. <li><a name="yaahm_initialize">
  3329. <code>set &lt;name&gt; initialize</code>
  3330. </a>
  3331. <br/>Restart the internal timers</li>
  3332. <li><a name="yaahm_lock">
  3333. <code>set &lt;name&gt; locked|unlocked</code>
  3334. </a>
  3335. <br />Set the lockstate of the yaahm module to <i>locked</i> (i.e., yaahm setups
  3336. may not be changed) resp. <i>unlocked</i> (i.e., yaahm setups may be changed>)</li>
  3337. <li><a name="yaahm_save">
  3338. <code>set &lt;name&gt; save|restore</code>
  3339. </a>
  3340. <br />Manually save/restore the complete profile data to/from the external file YAAHMFILE (save done automatically at each timer start, restore at FHEM start)</li>
  3341. </ul>
  3342. <a name="YAAHMget"></a>
  3343. <h4>Get</h4>
  3344. <ul>
  3345. <li><a name="yaahm_next"></a>
  3346. <code>get &lt;name&gt; next &lt;timernumber&gt;</code><br/>
  3347. <code>get &lt;name&gt; next &lt;timername&gt;</code><br/>
  3348. 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>
  3349. <li><a name="yaahm_saynext"></a>
  3350. <code>get &lt;name&gt; sayNext &lt;timernumber&gt;</code><br/>
  3351. <code>get &lt;name&gt; sayNext &lt;timername&gt;</code><br/>
  3352. 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>
  3353. <li><a name="yaahm_version"></a>
  3354. <code>get &lt;name&gt; version</code>
  3355. <br />Display the version of the module</li>
  3356. <li><a name="yaahm_template"></a>
  3357. <code>get &lt;name&gt; template</code>
  3358. <br />Return an (empty) perl subroutine for the helper functions</li>
  3359. </ul>
  3360. <a name="YAAHMattr"></a>
  3361. <h4>Attributes</h4>
  3362. <ul>
  3363. <li><a name="yaahm_linkname"><code>attr &lt;name&gt; linkname
  3364. &lt;string&gt;</code></a>
  3365. <br />Name for yaahm web link, default:
  3366. Profile</li>
  3367. <li><a name="yaahm_hiddenroom"><code>attr &lt;name&gt; hiddenroom
  3368. &lt;string&gt;</code></a>
  3369. <br />Room name for hidden yaahm room (containing only the YAAHM device), default:
  3370. ProfileRoom</li>
  3371. <li><a name="yaahm_lockstate"><code>attr &lt;name&gt; lockstate
  3372. locked|unlocked</code></a>
  3373. <br /><i>locked</i> means that yaahm setups may not be changed, <i>unlocked</i>
  3374. means that yaahm setups may be changed</li>
  3375. <li><a name="yaahm_simulation"><code>attr &lt;name&gt; simulation
  3376. 0|1</code></a>
  3377. <br />a value of 1 means that commands issued directly on the device as "set ... " will not be executed, but only simulated. Does <i>not</i> prevent the button
  3378. click commands from the interactive web page to be executed.</li>
  3379. <li><a name="yaahm_noicons"><code>attr &lt;name&gt; noicons
  3380. 0|1</code></a>
  3381. <br />when set to 1, animated icons are suppressed</li>
  3382. <li><a name="yaahm_modecolor"><code>attr &lt;name&gt; modecolor[0|1|2|3] <i>color</i></code></a>
  3383. <br />four color specifications to signal the modes normal (default <span style="color:#53f3c7">#53f3c7</span>),
  3384. party (default <span style="color:#8bfa56">#8bfa56</span>), absence (default <span style="color:#ff9458">#ff9458</span>),
  3385. donotodisturb (default <span style="color:#fd5777">#fd5777</span>), </li>
  3386. <li><a name="yaahm_statecolor"><code>attr &lt;name&gt; statecolor[0|1|2|3] <i>color</i></code></a>
  3387. <br />four color specifications to signal the states unsecured (default <span style="color:#53f3c7">#53f3c7</span>),
  3388. secured (default <span style="color:#ff9458">#ff9458</span>),
  3389. protected (default <span style="color:#f554e2">#f554e2</span>) and guarded (default <span style="color:#fd5777">#fd5777</span>)</li>
  3390. <li><a name="yaahm_timehelper"><code>attr &lt;name&gt; timeHelper &lt;name of perl program&gt;</code></a>
  3391. <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>
  3392. <li><a name="yaahm_modehelper"><code>attr &lt;name&gt; modeHelper &lt;name of perl program&gt;</code></a>
  3393. <br />name of a perl function that is called at every change of the house mode</li>
  3394. <li><a name="yaahm_modeauto"><code>attr &lt;name&gt; modeAuto 0|1</code></a>
  3395. <br />If this attribute is set to 1, the house mode changes automatically at certain time events.
  3396. <ul>
  3397. <li>On time (event) <i>sleep</i> or <i>morning</i>, <i>party</i> mode will be reset to <i>normal</i> mode.</li>
  3398. <li>On time (event) <i>wakeup</i>, <i>absence</i> mode will be reset to <i>normal</i> mode.</li>
  3399. <li>On <i>any</i> time (event), <i>donotdisturb</i> mode will be reset to <i>normal</i> mode.</li>
  3400. </ul>
  3401. </li>
  3402. <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>
  3403. <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
  3404. for its proper state, and a <i>stateWarning</i> function will be called if it is not in the proper state.</li>
  3405. <li><a name="yaahm_stateinterval"><code>attr &lt;name&gt; stateInterval &lt;integer&gt;</code></a>
  3406. <br />interval in minutes for checking all <i>stateDevices</i> for their proper state according of the house (security) state. Default 60 minutes.</li>
  3407. <li><a name="yaahm_statewarning"><code>attr &lt;name&gt; stateWarning &lt;name of perl program&gt;</code></a>
  3408. <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>
  3409. <li><a name="yaahm_statehelper"><code>attr &lt;name&gt; stateHelper &lt;name of perl program&gt;</code></a>
  3410. <br />name of a perl function that is called as <i>stateHelper('event')</i> at every change of the house (security) state</li>
  3411. <li><a name="yaahm_stateauto"><code>attr &lt;name&gt; stateAuto 0|1</code></a>
  3412. <br />If this attribute is set to 1, the house state changes automatically if certain modes are set or at certain time events
  3413. <ul>
  3414. <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>
  3415. <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>
  3416. </ul>
  3417. </li>
  3418. <li><a name="yaahm_holidaydevices"><code>attr &lt;name&gt; &lt;comma-separated list of devices&gt; </code></a>
  3419. <br />list of devices that provide holiday information. The devices may be
  3420. <a href="#holiday">holiday devices</a> or <a href="#Calendar">Calendar devices</a></li>
  3421. <li><a name="yaahm_vacationdevices"><code>attr &lt;comma-separated list of devices&gt; </code></a>
  3422. <br />list of devices that provide vacation information. The devices may be
  3423. <a href="#holiday">holiday devices</a> or <a href="#Calendar">Calendar devices</a></li>
  3424. <li><a name="yaahm_specialdevices"><code>attr &lt;comma-separated list of devices&gt; </code></a>
  3425. <br />list of devices that provide special date information (like e.g. garbage collection). The devices may be
  3426. <a href="#holiday">holiday devices</a> or <a href="#Calendar">Calendar devices</a></li>
  3427. </ul>
  3428. </ul>
  3429. =end html
  3430. =begin html_DE
  3431. <a name="YAAHM"></a>
  3432. <h3>YAAHM</h3>
  3433. <ul>
  3434. <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>
  3435. </ul>
  3436. =end html_DE
  3437. =cut