| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787 |
- ###############################################################################
- # $Id: RESIDENTStk.pm 17593 2018-10-22 15:35:04Z loredo $
- package main;
- use strict;
- use warnings;
- use Data::Dumper;
- use Unit;
- our (@RESIDENTStk_attr);
- # module variables ############################################################
- @RESIDENTStk_attr = (
- "autoGoneAfter:0,12,16,24,26,28,30,36,48,60",
- "geofenceUUIDs",
- "lang:EN,DE",
- "locationHome",
- "locations",
- "locationUnderway",
- "locationWayhome",
- "moodDefault",
- "moods",
- "moodSleepy",
- "noDuration:0,1",
- "passPresenceTo",
- "presenceDevices",
- "realname:group,alias",
- "showAllStates:0,1",
- "wakeupDevice",
- );
- ## initialize #################################################################
- sub RESIDENTStk_Initialize() { }
- # regular Fn ##################################################################
- sub RESIDENTStk_InitializeDev($) {
- my ($hash) = @_;
- $hash = $defs{$hash} unless ( ref($hash) );
- my $NOTIFYDEV = "global";
- if ( $hash->{TYPE} eq "RESIDENTS" ) {
- $NOTIFYDEV .= "," . RESIDENTStk_findResidentSlaves($hash);
- }
- else {
- $NOTIFYDEV .= "," . RESIDENTStk_findDummySlaves($hash);
- }
- return 0
- if ( defined( $hash->{NOTIFYDEV} ) && $hash->{NOTIFYDEV} eq $NOTIFYDEV );
- $hash->{NOTIFYDEV} = $NOTIFYDEV;
- return undef;
- }
- sub RESIDENTStk_Define($$) {
- my ( $hash, $def ) = @_;
- my @a = split( "[ \t][ \t]*", $def );
- my $name = $hash->{NAME};
- my $name_attr;
- my $TYPE = GetType($name);
- my $prefix = RESIDENTStk_GetPrefixFromType($name);
- if ( $TYPE ne "RESIDENTS" && int(@a) < 2 ) {
- my $msg = "Wrong syntax: define <name> $TYPE [RESIDENTS-DEVICE-NAMES]";
- Log3 $name, 4, $msg;
- return $msg;
- }
- return "Invalid parameter for RESIDENTS-DEVICE-NAMES"
- unless ( $TYPE ne "RESIDENTS"
- || !defined( $a[2] )
- || $a[2] =~ /^[A-Za-z\d._]+(?:,[A-Za-z\d._]*)*$/ );
- $hash->{MOD_INIT} = 1;
- $hash->{NOTIFYDEV} = "global";
- delete $hash->{RESIDENTGROUPS} if ( $hash->{RESIDENTGROUPS} );
- $hash->{RESIDENTGROUPS} = $a[2] if ( defined( $a[2] ) );
- # set default settings on first define
- if ( $init_done && !defined( $hash->{OLDDEF} ) ) {
- RESIDENTStk_Attr( "init", $name, $prefix . "lang" );
- $attr{$name}{webCmd} = "state";
- if ( $TYPE eq "RESIDENTS" ) {
- $attr{$name}{icon} = "control_building_filled";
- $attr{$name}{room} = "Residents";
- }
- elsif ( $TYPE ne "RESIDENTS" ) {
- my $fname = $name;
- $fname =~ s/^$prefix//;
- $attr{$name}{group} = $fname if ( $prefix eq "rr_" );
- $attr{$name}{group} = "Guests" if ( $prefix eq "rg_" );
- $attr{$name}{alias} = "Status" if ( $prefix eq "rr_" );
- $attr{$name}{alias} = $fname if ( $prefix eq "rg_" );
- $attr{$name}{icon} = "people_sensor" if ( $prefix eq "rr_" );
- $attr{$name}{icon} = "scene_visit_guests" if ( $prefix eq "rg_" );
- $attr{$name}{rr_realname} = "group" if ( $prefix eq "rr_" );
- $attr{$name}{rg_realname} = "alias" if ( $prefix eq "rg_" );
- $attr{$name}{sortby} = "1";
- if ( $hash->{RESIDENTGROUPS} ) {
- my $firstRgr = $hash->{RESIDENTGROUPS};
- $firstRgr =~ s/,[\s\S]*$//gmi;
- $attr{$name}{room} = $attr{$firstRgr}{room}
- if ( $attr{$firstRgr} && $attr{$firstRgr}{room} );
- }
- }
- }
- # trigger for modified objects
- if ( $TYPE ne "RESIDENTS" ) {
- readingsBeginUpdate($hash);
- readingsBulkUpdateIfChanged( $hash, "state",
- ReadingsVal( $name, "state", "" ) );
- readingsEndUpdate( $hash, 1 );
- }
- # run timers
- InternalTimer(
- gettimeofday() + 15,
- "RESIDENTStk_StartInternalTimers",
- $hash, 0
- );
- return undef;
- }
- sub RESIDENTStk_Undefine($$) {
- my ( $hash, $name ) = @_;
- RESIDENTStk_StopInternalTimers($hash);
- return undef unless ($init_done);
- # delete child roommates
- if ( defined( $hash->{ROOMMATES} )
- && $hash->{ROOMMATES} ne "" )
- {
- my @registeredRoommates =
- split( /,/, $hash->{ROOMMATES} );
- foreach my $child (@registeredRoommates) {
- fhem( "delete " . $child );
- Log3 $name, 3, "RESIDENTS $name: deleted device $child";
- }
- }
- # delete child guests
- if ( defined( $hash->{GUESTS} )
- && $hash->{GUESTS} ne "" )
- {
- my @registeredGuests =
- split( /,/, $hash->{GUESTS} );
- foreach my $child (@registeredGuests) {
- fhem( "delete " . $child );
- Log3 $name, 3, "RESIDENTS $name: deleted device $child";
- }
- }
- return undef;
- }
- sub RESIDENTStk_Set($@);
- sub RESIDENTStk_Set($@) {
- my ( $hash, @a ) = @_;
- my $name = $hash->{NAME};
- my $TYPE = GetType($name);
- my $prefix = RESIDENTStk_GetPrefixFromType($name);
- my $state = ReadingsVal( $name, "state", "initialized" );
- my $presence = ReadingsVal( $name, "presence", "undefined" );
- my $mood = ReadingsVal( $name, "mood", "-" );
- my $location = ReadingsVal( $name, "location", "undefined" );
- my $roommates = ( $hash->{ROOMMATES} ? $hash->{ROOMMATES} : undef );
- my $guests = ( $hash->{GUESTS} ? $hash->{GUESTS} : undef );
- my $silent = 0;
- return undef if ( IsDisabled($name) );
- return "No Argument given" unless ( defined( $a[1] ) );
- # depending on current FHEMWEB instance's allowedCommands,
- # restrict set commands if there is "set-user" in it
- my $adminMode = 1;
- my $FWallowedCommands = 0;
- $FWallowedCommands = AttrVal( $FW_wname, "allowedCommands", 0 )
- if ( defined($FW_wname) );
- if ( $FWallowedCommands && $FWallowedCommands =~ m/\bset-user\b/ ) {
- $adminMode = 0;
- return "Forbidden command: set " . $a[1]
- if ( $TYPE ne "RESIDENTS" && lc( $a[1] ) eq "create" );
- return "Forbidden command: set "
- . $a[1]
- if (
- $TYPE eq "RESIDENTS"
- && ( lc( $a[1] ) eq "addroommate"
- || lc( $a[1] ) eq "addguest"
- || lc( $a[1] ) eq "removeroommate"
- || lc( $a[1] ) eq "removeguest"
- || lc( $a[1] ) eq "create" )
- );
- }
- # states
- my $states = (
- defined( $attr{$name}{ $prefix . 'states' } )
- ? $attr{$name}{ $prefix . 'states' }
- : (
- defined( $attr{$name}{ $prefix . 'showAllStates' } )
- && $attr{$name}{ $prefix . 'showAllStates' } == 1
- ? "home,gotosleep,asleep,awoken,absent"
- : "home,gotosleep,absent"
- )
- );
- $states .= ",gone" if ( $TYPE ne "GUEST" );
- $states .= ",none" if ( $TYPE eq "GUEST" );
- $states = $state . "," . $states
- if ( $state ne "initialized" && $states !~ /$state/ );
- $states =~ s/ /,/g;
- # moods
- my $moods = (
- defined( $attr{$name}{ $prefix . 'moods' } )
- ? $attr{$name}{ $prefix . 'moods' } . ",toggle"
- : "calm,relaxed,happy,excited,lonely,sad,bored,stressed,uncomfortable,sleepy,angry,toggle"
- );
- $moods = $mood . "," . $moods if ( $moods !~ /$mood/ );
- $moods =~ s/ /,/g;
- # locations
- my $locations = (
- defined( $attr{$name}{ $prefix . 'locations' } )
- ? $attr{$name}{ $prefix . 'locations' }
- : ""
- );
- if ( $locations !~ /$location/
- && $locations ne "" )
- {
- $locations = ":" . $location . "," . $locations;
- }
- elsif ( $locations ne "" ) {
- $locations = ":" . $locations;
- }
- $locations =~ s/ /,/g;
- my $usage = "Unknown argument " . $a[1] . ", choose one of state:$states";
- if ( $TYPE ne "RESIDENTS" ) {
- $usage .= " mood:$moods";
- $usage .= " location$locations";
- if ($adminMode) {
- $usage .= " create:wakeuptimer";
- $usage .= ",locationMap"
- if ( ReadingsVal( $name, "locationLat", "-" ) ne "-"
- && ReadingsVal( $name, "locationLong", "-" ) ne "-" );
- }
- }
- else {
- if ($adminMode) {
- $usage .= " addRoommate addGuest";
- $usage .= " removeRoommate:" . $roommates if ($roommates);
- $usage .= " removeGuest:" . $guests if ($guests);
- $usage .= " create:wakeuptimer";
- }
- }
- # silentSet
- if ( $a[1] eq "silentSet" ) {
- $silent = 1;
- my $first = shift @a;
- $a[0] = $first;
- }
- # states
- if ( $a[1] eq "state"
- || $a[1] eq "home"
- || $a[1] eq "gotosleep"
- || $a[1] eq "asleep"
- || $a[1] eq "awoken"
- || $a[1] eq "absent"
- || $a[1] eq "none"
- || $a[1] eq "gone" )
- {
- my $newstate;
- # if not direct
- if (
- $a[1] eq "state"
- && defined( $a[2] )
- && ( $a[2] eq "home"
- || $a[2] eq "gotosleep"
- || $a[2] eq "asleep"
- || $a[2] eq "awoken"
- || $a[2] eq "absent"
- || $a[2] eq "none"
- || $a[2] eq "gone" )
- )
- {
- $newstate = $a[2];
- }
- elsif ( defined( $a[2] ) ) {
- return
- "Invalid 2nd argument, choose one of home gotosleep asleep awoken absent gone ";
- }
- else {
- $newstate = $a[1];
- }
- $newstate = "none" if ( $newstate eq "gone" && $TYPE eq "GUEST" );
- $newstate = "gone" if ( $newstate eq "none" && $TYPE ne "GUEST" );
- Log3 $name, 2, "$TYPE set $name " . $newstate if ( !$silent );
- # RESIDENTS only
- if ( $TYPE eq "RESIDENTS" ) {
- $presence = "absent";
- # loop through every roommate
- if ($roommates) {
- my @registeredRoommates =
- split( /,/, $roommates );
- foreach my $roommate (@registeredRoommates) {
- fhem "set $roommate silentSet state $newstate"
- if ( ReadingsVal( $roommate, "state", "initialized" ) ne
- $newstate );
- }
- }
- # loop through every guest
- if ($guests) {
- $newstate = "none" if ( $newstate eq "gone" );
- my @registeredGuests =
- split( /,/, $guests );
- foreach my $guest (@registeredGuests) {
- fhem "set $guest silentSet state $newstate"
- if ( ReadingsVal( $guest, "state", "initialized" ) ne
- $newstate );
- }
- }
- }
- # ROOMMATE+GUEST: if state changed
- elsif ( $state ne $newstate ) {
- readingsBeginUpdate($hash);
- readingsBulkUpdate( $hash, "lastState", $state );
- readingsBulkUpdate( $hash, "state", $newstate );
- my $datetime = TimeNow();
- # reset mood
- my $mood_default =
- ( defined( $attr{$name}{ $prefix . 'moodDefault' } ) )
- ? $attr{$name}{ $prefix . 'moodDefault' }
- : "calm";
- my $mood_sleepy =
- ( defined( $attr{$name}{ $prefix . 'moodSleepy' } ) )
- ? $attr{$name}{ $prefix . 'moodSleepy' }
- : "sleepy";
- if (
- $mood ne "-"
- && ( $newstate eq "gone"
- || $newstate eq "none"
- || $newstate eq "absent"
- || $newstate eq "asleep" )
- )
- {
- Log3 $name, 4,
- "$TYPE $name: implicit mood change caused by state "
- . $newstate;
- RESIDENTStk_Set( $hash, $name, "silentSet", "mood", "-" );
- }
- elsif ( $mood ne $mood_sleepy
- && ( $newstate eq "gotosleep" || $newstate eq "awoken" ) )
- {
- Log3 $name, 4,
- "$TYPE $name: implicit mood change caused by state "
- . $newstate;
- RESIDENTStk_Set( $hash, $name, "silentSet", "mood",
- $mood_sleepy );
- }
- elsif ( ( $mood eq "-" || $mood eq $mood_sleepy )
- && $newstate eq "home" )
- {
- Log3 $name, 4,
- "$TYPE $name: implicit mood change caused by state "
- . $newstate;
- RESIDENTStk_Set( $hash, $name, "silentSet", "mood",
- $mood_default );
- }
- # if state is asleep, start sleep timer
- readingsBulkUpdate( $hash, "lastSleep", $datetime )
- if ( $newstate eq "asleep" );
- # if prior state was asleep, update sleep statistics
- if ( $state eq "asleep"
- && ReadingsVal( $name, "lastSleep", "" ) ne "" )
- {
- readingsBulkUpdate( $hash, "lastAwake", $datetime );
- readingsBulkUpdate(
- $hash,
- "lastDurSleep",
- UConv::duration(
- $datetime, ReadingsVal( $name, "lastSleep", "" )
- )
- );
- readingsBulkUpdate(
- $hash,
- "lastDurSleep_cr",
- UConv::duration(
- $datetime, ReadingsVal( $name, "lastSleep", "" ),
- "min"
- )
- );
- }
- # calculate presence state
- my $newpresence =
- ( $newstate ne "none"
- && $newstate ne "gone"
- && $newstate ne "absent" )
- ? "present"
- : "absent";
- # stop any running wakeup-timers in case state changed
- my $wakeupState = ReadingsVal( $name, "wakeup", 0 );
- if ( $wakeupState > 0 ) {
- my $wakeupDeviceList =
- AttrVal( $name, $prefix . 'wakeupDevice', 0 );
- for my $wakeupDevice ( split /,/, $wakeupDeviceList ) {
- next if !$wakeupDevice;
- if ( IsDevice( $wakeupDevice, "dummy" ) ) {
- # forced-stop only if resident is not present anymore
- if ( $newpresence eq "present" ) {
- Log3 $name, 4,
- "$TYPE $name: ending wakeup-timer $wakeupDevice";
- fhem "set $wakeupDevice:FILTER=running!=0 end";
- }
- else {
- Log3 $name, 4,
- "$TYPE $name: stopping wakeup-timer $wakeupDevice";
- fhem "set $wakeupDevice:FILTER=running!=0 stop";
- }
- }
- }
- }
- # if presence changed
- if ( $newpresence ne $presence ) {
- readingsBulkUpdate( $hash, "presence", $newpresence );
- # update location
- my @location_home =
- split( ' ',
- AttrVal( $name, $prefix . 'locationHome', "home" ) );
- my @location_underway =
- split(
- ' ',
- AttrVal( $name, $prefix . 'locationUnderway', "underway" )
- );
- my @location_wayhome =
- split( ' ',
- AttrVal( $name, $prefix . 'locationWayhome', "wayhome" ) );
- my $searchstring = quotemeta($location);
- if ( !$silent && $newpresence eq "present" ) {
- if ( !grep( m/^$searchstring$/, @location_home )
- && $location ne $location_home[0] )
- {
- Log3 $name, 4,
- "$TYPE $name: implicit location change caused by state "
- . $newstate;
- RESIDENTStk_Set( $hash, $name, "silentSet", "location",
- $location_home[0] );
- }
- }
- else {
- if ( !$silent
- && !grep( m/^$searchstring$/, @location_underway )
- && $location ne $location_underway[0] )
- {
- Log3 $name, 4,
- "$TYPE $name: implicit location change caused by state "
- . $newstate;
- RESIDENTStk_Set( $hash, $name, "silentSet", "location",
- $location_underway[0] );
- }
- }
- # reset wayhome
- readingsBulkUpdateIfChanged( $hash, "wayhome", "0" );
- # update statistics
- if ( $newpresence eq "present" ) {
- readingsBulkUpdate( $hash, "lastArrival", $datetime );
- # absence duration
- if ( ReadingsVal( $name, "lastDeparture", "-" ) ne "-" ) {
- readingsBulkUpdate(
- $hash,
- "lastDurAbsence",
- UConv::duration(
- $datetime,
- ReadingsVal( $name, "lastDeparture", "-" )
- )
- );
- readingsBulkUpdate(
- $hash,
- "lastDurAbsence_cr",
- UConv::duration(
- $datetime,
- ReadingsVal( $name, "lastDeparture", "-" ),
- "min"
- )
- );
- }
- }
- else {
- readingsBulkUpdate( $hash, "lastDeparture", $datetime );
- # presence duration
- if ( ReadingsVal( $name, "lastArrival", "-" ) ne "-" ) {
- readingsBulkUpdate(
- $hash,
- "lastDurPresence",
- UConv::duration(
- $datetime,
- ReadingsVal( $name, "lastArrival", "-" )
- )
- );
- readingsBulkUpdate(
- $hash,
- "lastDurPresence_cr",
- UConv::duration(
- $datetime,
- ReadingsVal( $name, "lastArrival", "-" ), "min"
- )
- );
- }
- }
- # adjust linked objects
- if ( defined( $attr{$name}{ $prefix . 'passPresenceTo' } )
- && $attr{$name}{ $prefix . 'passPresenceTo' } ne "" )
- {
- my @linkedObjects =
- split( ' ', $attr{$name}{ $prefix . 'passPresenceTo' } );
- foreach my $object (@linkedObjects) {
- if ( IsDevice( $object, "ROOMMATE|GUEST" )
- && $defs{$object} ne $name
- && ReadingsVal( $object, "state", "" ) ne "gone"
- && ReadingsVal( $object, "state", "" ) ne "none" )
- {
- fhem("set $object $newstate");
- }
- }
- }
- }
- # clear readings if guest is gone
- if ( $newstate eq "none" ) {
- readingsBulkUpdate( $hash, "lastArrival", "-" )
- if ( ReadingsVal( $name, "lastArrival", "-" ) ne "-" );
- readingsBulkUpdate( $hash, "lastAwake", "-" )
- if ( ReadingsVal( $name, "lastAwake", "-" ) ne "-" );
- readingsBulkUpdate( $hash, "lastDurAbsence", "-" )
- if ( ReadingsVal( $name, "lastDurAbsence", "-" ) ne "-" );
- readingsBulkUpdate( $hash, "lastDurSleep", "-" )
- if ( ReadingsVal( $name, "lastDurSleep", "-" ) ne "-" );
- readingsBulkUpdate( $hash, "lastLocation", "-" )
- if ( ReadingsVal( $name, "lastLocation", "-" ) ne "-" );
- readingsBulkUpdate( $hash, "lastSleep", "-" )
- if ( ReadingsVal( $name, "lastSleep", "-" ) ne "-" );
- readingsBulkUpdate( $hash, "lastMood", "-" )
- if ( ReadingsVal( $name, "lastMood", "-" ) ne "-" );
- readingsBulkUpdate( $hash, "location", "-" )
- if ( ReadingsVal( $name, "location", "-" ) ne "-" );
- readingsBulkUpdate( $hash, "mood", "-" )
- if ( ReadingsVal( $name, "mood", "-" ) ne "-" );
- }
- # calculate duration timers
- RESIDENTStk_DurationTimer( $hash, $silent );
- readingsEndUpdate( $hash, 1 );
- # enable or disable AutoGone timer
- if ( $newstate eq "absent" ) {
- RESIDENTStk_AutoGone($hash);
- }
- elsif ( $state eq "absent" ) {
- delete $hash->{AUTOGONE} if ( $hash->{AUTOGONE} );
- RESIDENTStk_RemoveInternalTimer( "AutoGone", $hash );
- }
- }
- }
- # RESIDENTS only: addRoommate
- elsif ( $TYPE eq "RESIDENTS" && lc( $a[1] ) eq "addroommate" ) {
- Log3 $name, 2, "$TYPE set $name " . $a[1] . " " . $a[2]
- if ( defined( $a[2] ) );
- my $rr_name;
- my $rr_name_attr;
- if ( $a[2] ne "" ) {
- $rr_name = "rr_" unless ( $a[2] =~ /^rr_/ );
- $rr_name .= $a[2];
- # define roommate
- fhem( "define " . $rr_name . " ROOMMATE " . $name )
- unless ( IsDevice($rr_name) );
- if ( IsDevice($rr_name) ) {
- return "Can't create, device $rr_name already existing."
- unless ( IsDevice( $rr_name, "ROOMMATE" ) );
- my $lang =
- $a[3]
- ? uc( $a[3] )
- : AttrVal( $rr_name, "rr_lang",
- AttrVal( $name, "rgr_lang", undef ) );
- fhem( "attr " . $rr_name . " rr_lang " . $lang )
- if ($lang);
- $attr{$rr_name}{comment} = "Auto-created by $name"
- unless ( defined( $attr{$rr_name}{comment} )
- && $attr{$rr_name}{comment} eq "Auto-created by $name" );
- fhem "sleep 1;set $rr_name silentSet state home";
- Log3 $name, 3, "$TYPE $name: created new device $rr_name";
- }
- }
- else {
- return "No Argument given, choose one of name ";
- }
- }
- # RESIDENTS only: removeRoommate
- elsif ( $TYPE eq "RESIDENTS" && lc( $a[1] ) eq "removeroommate" ) {
- Log3 $name, 2, "$TYPE set $name " . $a[1] . " " . $a[2]
- if ( defined( $a[2] ) );
- if ( $a[2] ne "" ) {
- my $rr_name = $a[2];
- # delete roommate
- if ( IsDevice($rr_name) ) {
- Log3 $name, 3, "$TYPE $name: deleted device $rr_name"
- if fhem( "delete " . $rr_name );
- }
- }
- else {
- return "No Argument given, choose one of name ";
- }
- }
- # RESIDENTS only: addGuest
- elsif ( $TYPE eq "RESIDENTS" && lc( $a[1] ) eq "addguest" ) {
- Log3 $name, 2, "$TYPE set $name " . $a[1] . " " . $a[2]
- if ( defined( $a[2] ) );
- my $rg_name;
- my $rg_name_attr;
- if ( $a[2] ne "" ) {
- $rg_name = "rg_" unless ( $a[2] =~ /^rg_/ );
- $rg_name .= $a[2];
- # define guest
- fhem( "define " . $rg_name . " GUEST " . $name )
- unless ( IsDevice($rg_name) );
- if ( IsDevice($rg_name) ) {
- return "Can't create, device $rg_name already existing."
- unless ( IsDevice( $rg_name, "GUEST" ) );
- my $lang =
- $a[3]
- ? uc( $a[3] )
- : AttrVal( $rg_name, "rg_lang",
- AttrVal( $name, "rgr_lang", undef ) );
- fhem( "attr " . $rg_name . " rg_lang " . $lang )
- if ($lang);
- $attr{$rg_name}{comment} = "Auto-created by $name"
- unless ( defined( $attr{$rg_name}{comment} )
- && $attr{$rg_name}{comment} eq "Auto-created by $name" );
- fhem "sleep 1;set $rg_name silentSet state home";
- Log3 $name, 3, "$TYPE $name: created new device $rg_name";
- }
- }
- else {
- return "No Argument given, choose one of name ";
- }
- }
- # RESIDENTS only: removeGuest
- elsif ( $TYPE eq "RESIDENTS" && lc( $a[1] ) eq "removeguest" ) {
- Log3 $name, 2, "$TYPE set $name " . $a[1] . " " . $a[2]
- if ( defined( $a[2] ) );
- if ( $a[2] ne "" ) {
- my $rg_name = $a[2];
- # delete guest
- if ( IsDevice($rg_name) ) {
- Log3 $name, 3, "$TYPE $name: deleted device $rg_name"
- if fhem( "delete " . $rg_name );
- }
- }
- else {
- return "No Argument given, choose one of name ";
- }
- }
- # mood
- elsif ( $TYPE ne "RESIDENTS" && $a[1] eq "mood" ) {
- if ( defined( $a[2] ) && $a[2] ne "" ) {
- Log3 $name, 2, "$TYPE set $name mood " . $a[2] if ( !$silent );
- readingsBeginUpdate($hash) if ( !$silent );
- if ( $a[2] eq "toggle"
- && ReadingsVal( $name, "lastMood", "" ) ne "" )
- {
- readingsBulkUpdate( $hash, "mood",
- ReadingsVal( $name, "lastMood", "" ) );
- readingsBulkUpdate( $hash, "lastMood", $mood );
- }
- elsif ( $mood ne $a[2] ) {
- readingsBulkUpdate( $hash, "lastMood", $mood )
- if ( $mood ne "-" );
- readingsBulkUpdate( $hash, "mood", $a[2] );
- }
- readingsEndUpdate( $hash, 1 ) if ( !$silent );
- }
- else {
- return "Invalid 2nd argument, choose one of mood toggle";
- }
- }
- # location
- elsif ( $TYPE ne "RESIDENTS" && $a[1] eq "location" ) {
- if ( defined( $a[2] ) && $a[2] ne "" ) {
- Log3 $name, 2, "$TYPE set $name location " . $a[2]
- if ( !$silent );
- if ( $location ne $a[2] ) {
- my $searchstring;
- readingsBeginUpdate($hash) if ( !$silent );
- # read attributes
- my @location_home =
- split( ' ',
- AttrVal( $name, $prefix . 'locationHome', "home" ) );
- my @location_underway =
- split(
- ' ',
- AttrVal( $name, $prefix . 'locationUnderway', "underway" )
- );
- my @location_wayhome =
- split( ' ',
- AttrVal( $name, $prefix . 'locationWayhome', "wayhome" ) );
- $searchstring = quotemeta($location);
- readingsBulkUpdate( $hash, "lastLocation", $location )
- if ( $location ne "wayhome"
- && !grep( m/^$searchstring$/, @location_underway ) );
- readingsBulkUpdate( $hash, "location", $a[2] )
- if ( $a[2] ne "wayhome" );
- # wayhome detection
- $searchstring = quotemeta($location);
- if (
- (
- $a[2] eq "wayhome"
- || grep( m/^$searchstring$/, @location_wayhome )
- )
- && ( $presence eq "absent" )
- )
- {
- Log3 $name, 3,
- "$TYPE $name: on way back home from $location";
- readingsBulkUpdateIfChanged( $hash, "wayhome", "1" );
- }
- readingsEndUpdate( $hash, 1 ) if ( !$silent );
- # auto-updates
- $searchstring = quotemeta( $a[2] );
- if (
- (
- $a[2] eq "home"
- || grep( m/^$searchstring$/, @location_home )
- )
- && $state ne "home"
- && $state ne "gotosleep"
- && $state ne "asleep"
- && $state ne "awoken"
- && $state ne "initialized"
- )
- {
- Log3 $name, 4,
- "$TYPE $name: implicit state change caused by location "
- . $a[2];
- RESIDENTStk_Set( $hash, $name, "silentSet", "state",
- "home" );
- }
- elsif (
- (
- $a[2] eq "underway"
- || grep( m/^$searchstring$/, @location_underway )
- )
- && $state ne "gone"
- && $state ne "none"
- && $state ne "absent"
- && $state ne "initialized"
- )
- {
- Log3 $name, 4,
- "$TYPE $name: implicit state change caused by location "
- . $a[2];
- RESIDENTStk_Set( $hash, $name, "silentSet", "state",
- "absent" );
- }
- }
- }
- else {
- return "Invalid 2nd argument, choose one of location ";
- }
- }
- # create
- elsif ( lc( $a[1] ) eq "create" ) {
- if ( $TYPE eq "RESIDENTS"
- && ( !defined( $a[2] ) || lc( $a[2] ) ne "wakeuptimer" ) )
- {
- return "Invalid 2nd argument, choose one of wakeuptimer ";
- }
- elsif ( !defined( $a[2] ) || $a[2] !~ /^(wakeuptimer|locationMap)$/i ) {
- return
- "Invalid 2nd argument, choose one of wakeuptimer locationMap ";
- }
- elsif ( lc( $a[2] ) eq "wakeuptimer" ) {
- my $i = "1";
- my $wakeuptimerName = $name . "_wakeuptimer" . $i;
- my $created = 0;
- until ($created) {
- if ( IsDevice($wakeuptimerName) ) {
- $i++;
- $wakeuptimerName = $name . "_wakeuptimer" . $i;
- }
- else {
- my $sortby = AttrVal( $name, "sortby", -1 );
- $sortby++;
- # create new dummy device
- fhem "define $wakeuptimerName dummy";
- fhem "attr $wakeuptimerName alias Wake-up Timer $i";
- fhem
- "attr $wakeuptimerName comment Auto-created by $TYPE module for use with RESIDENTS Toolkit";
- fhem
- "attr $wakeuptimerName devStateIcon OFF:general_aus\@red:reset running:general_an\@green:stop .*:general_an\@orange:nextRun%20OFF";
- fhem "attr $wakeuptimerName group " . $attr{$name}{group}
- if ( defined( $attr{$name}{group} ) );
- fhem "attr $wakeuptimerName icon time_timer";
- fhem "attr $wakeuptimerName room " . $attr{$name}{room}
- if ( defined( $attr{$name}{room} ) );
- fhem
- "attr $wakeuptimerName setList nextRun:OFF,00:00,00:15,00:30,00:45,01:00,01:15,01:30,01:45,02:00,02:15,02:30,02:45,03:00,03:15,03:30,03:45,04:00,04:15,04:30,04:45,05:00,05:15,05:30,05:45,06:00,06:15,06:30,06:45,07:00,07:15,07:30,07:45,08:00,08:15,08:30,08:45,09:00,09:15,09:30,09:45,10:00,10:15,10:30,10:45,11:00,11:15,11:30,11:45,12:00,12:15,12:30,12:45,13:00,13:15,13:30,13:45,14:00,14:15,14:30,14:45,15:00,15:15,15:30,15:45,16:00,16:15,16:30,16:45,17:00,17:15,17:30,17:45,18:00,18:15,18:30,18:45,19:00,19:15,19:30,19:45,20:00,20:15,20:30,20:45,21:00,21:15,21:30,21:45,22:00,22:15,22:30,22:45,23:00,23:15,23:30,23:45 reset:noArg trigger:noArg start:noArg stop:noArg end:noArg wakeupOffset:slider,0,1,120 wakeupDefaultTime:OFF,00:00,00:15,00:30,00:45,01:00,01:15,01:30,01:45,02:00,02:15,02:30,02:45,03:00,03:15,03:30,03:45,04:00,04:15,04:30,04:45,05:00,05:15,05:30,05:45,06:00,06:15,06:30,06:45,07:00,07:15,07:30,07:45,08:00,08:15,08:30,08:45,09:00,09:15,09:30,09:45,10:00,10:15,10:30,10:45,11:00,11:15,11:30,11:45,12:00,12:15,12:30,12:45,13:00,13:15,13:30,13:45,14:00,14:15,14:30,14:45,15:00,15:15,15:30,15:45,16:00,16:15,16:30,16:45,17:00,17:15,17:30,17:45,18:00,18:15,18:30,18:45,19:00,19:15,19:30,19:45,20:00,20:15,20:30,20:45,21:00,21:15,21:30,21:45,22:00,22:15,22:30,22:45,23:00,23:15,23:30,23:45 wakeupResetdays:multiple-strict,0,1,2,3,4,5,6 wakeupDays:multiple-strict,0,1,2,3,4,5,6 wakeupHolidays:,andHoliday,orHoliday,andNoHoliday,orNoHoliday wakeupEnforced:0,1,2,3";
- fhem "attr $wakeuptimerName userattr wakeupUserdevice";
- fhem "attr $wakeuptimerName sortby " . $sortby
- if ($sortby);
- fhem "attr $wakeuptimerName wakeupUserdevice $name";
- fhem "attr $wakeuptimerName webCmd nextRun";
- # register slave device
- my $wakeupDevice =
- AttrVal( $name, $prefix . 'wakeupDevice', 0 );
- if ( !$wakeupDevice ) {
- fhem "attr $name $prefix"
- . "wakeupDevice $wakeuptimerName";
- }
- elsif ( $wakeupDevice !~ /(.*,?)($wakeuptimerName)(.*,?)/ )
- {
- fhem "attr $name $prefix"
- . "wakeupDevice "
- . $wakeupDevice
- . ",$wakeuptimerName";
- }
- # trigger first update
- fhem "sleep 1;set $wakeuptimerName nextRun OFF";
- $created = 1;
- }
- }
- return
- "Dummy $wakeuptimerName and other pending devices created and pre-configured.\nYou may edit Macro_$wakeuptimerName to define your wake-up actions\nand at_$wakeuptimerName for optional at-device adjustments.";
- }
- elsif ( $TYPE ne "RESIDENTS" && lc( $a[2] ) eq "locationmap" ) {
- my $locationmapName = $name . "_map";
- if ( IsDevice($locationmapName) ) {
- return
- "Device $locationmapName existing already, delete it first to have it re-created.";
- }
- else {
- my $sortby = AttrVal( $name, "sortby", -1 );
- $sortby++;
- # create new weblink device
- fhem "define $locationmapName weblink htmlCode {
- '<ul style=\"width: 400px;; overflow: hidden;; height: 300px;;\">
- <iframe name=\"$locationmapName\" src=\"https://www.google.com/maps/embed/v1/place?key=AIzaSyB66DvcpbXJ5eWgIkzxpUN2s_9l3_6fegM&q='
- .ReadingsVal('$name','locationLat','')
- .','
- .ReadingsVal('$name','locationLong','')
- .'&zoom=13\" width=\"480\" height=\"480\" frameborder=\"0\" style=\"border:0;; margin-top: -165px;; margin-left: -135px;;\">
- </iframe>
- </ul>'
- }";
- fhem "attr $locationmapName alias Current Location";
- fhem
- "attr $locationmapName comment Auto-created by $TYPE module";
- fhem "attr $locationmapName group " . $attr{$name}{group}
- if ( defined( $attr{$name}{group} ) );
- fhem "attr $locationmapName room " . $attr{$name}{room}
- if ( defined( $attr{$name}{room} ) );
- }
- return "Weblink device $locationmapName was created.";
- }
- }
- # return usage hint
- else {
- return $usage;
- }
- return undef;
- }
- sub RESIDENTStk_Attr(@) {
- my ( $cmd, $name, $attribute, $value ) = @_;
- my $hash = $defs{$name};
- my $prefix = RESIDENTStk_GetPrefixFromType($name);
- if ( $attribute eq $prefix . "wakeupDevice"
- || $attribute eq $prefix . "presenceDevices" )
- {
- return "Value for $attribute has invalid format"
- unless ( $cmd eq "del"
- || $value =~
- m/^([a-zA-Z\d._]+(:[A-Za-z\d_\.\-\/]+)?,?)([a-zA-Z\d._]+(:[A-Za-z\d_\.\-\/]+)?,?)*$/
- );
- }
- elsif ( !$init_done ) {
- return undef;
- }
- elsif ( $attribute eq "disable" ) {
- if ( $value and $value == 1 ) {
- $hash->{STATE} = "disabled";
- RESIDENTStk_StopInternalTimers($hash);
- }
- elsif ( $cmd eq "del" or !$value ) {
- evalStateFormat($hash);
- RESIDENTStk_StartInternalTimers( $hash, 1 );
- }
- }
- elsif ( $prefix ne "rgr_" && $attribute eq $prefix . "autoGoneAfter" ) {
- if ($value) {
- RESIDENTStk_AutoGone($hash);
- }
- elsif ( !$value ) {
- delete $hash->{AUTOGONE} if ( $hash->{AUTOGONE} );
- RESIDENTStk_RemoveInternalTimer( "AutoGone", $hash );
- }
- }
- elsif ( $attribute eq $prefix . "noDuration" ) {
- if ($value) {
- delete $hash->{DURATIONTIMER} if ( $hash->{DURATIONTIMER} );
- RESIDENTStk_RemoveInternalTimer( "DurationTimer", $hash );
- }
- elsif ( !$value ) {
- RESIDENTStk_DurationTimer($hash);
- }
- }
- elsif ( $attribute eq $prefix . "lang" ) {
- my $lang =
- $cmd eq "set" ? uc($value) : AttrVal( "global", "language", "EN" );
- # for initial define, ensure fallback to EN
- $lang = "EN"
- if ( $cmd eq "init" && $lang !~ /^EN|DE$/i );
- if ( $lang eq "DE" ) {
- if ( $prefix eq "rgr_" ) {
- $attr{$name}{alias} = "Bewohner"
- if ( !defined( $attr{$name}{alias} )
- || $attr{$name}{alias} eq "Residents" );
- $attr{$name}{group} = "Zuhause Status"
- if ( !defined( $attr{$name}{group} )
- || $attr{$name}{group} eq "Home State" );
- }
- $attr{$name}{devStateIcon} =
- '.*zuhause:user_available:absent '
- . '.*anwesend:user_available:absent .*abwesend:user_away:home .*verreist:user_ext_away:home .*bettfertig:scene_toilet:asleep .*schlaeft:scene_sleeping:awoken .*schläft:scene_sleeping:awoken .*aufgestanden:scene_sleeping_alternat:home .*:user_unknown:home';
- $attr{$name}{eventMap} =
- "home:zuhause absent:abwesend gone:verreist "
- . "gotosleep:bettfertig asleep:schläft awoken:aufgestanden";
- $attr{$name}{widgetOverride} =
- "state:zuhause,bettfertig,schläft,"
- . "aufgestanden,abwesend,verreist";
- }
- elsif ( $lang eq "EN" ) {
- if ( $prefix eq "rgr_" ) {
- $attr{$name}{alias} = "Residents"
- if ( !defined( $attr{$name}{alias} )
- || $attr{$name}{alias} eq "Bewohner" );
- $attr{$name}{group} = "Home State"
- if ( !defined( $attr{$name}{group} )
- || $attr{$name}{group} eq "Zuhause Status" );
- }
- $attr{$name}{devStateIcon} =
- '.*home:user_available:absent .*absent:user_away:home '
- . '.*gone:user_ext_away:home .*gotosleep:scene_toilet:asleep .*asleep:scene_sleeping:awoken .*awoken:scene_sleeping_alternat:home .*:user_unknown:home';
- delete $attr{$name}{eventMap}
- if ( defined( $attr{$name}{eventMap} ) );
- delete $attr{$name}{widgetOverride}
- if ( defined( $attr{$name}{widgetOverride} ) );
- }
- else {
- return "Unsupported language $lang";
- }
- evalStateFormat($hash);
- }
- return undef;
- }
- sub RESIDENTStk_Notify($$) {
- my ( $hash, $dev ) = @_;
- my $name = $hash->{NAME};
- my $TYPE = GetType($name);
- my $prefix = RESIDENTStk_GetPrefixFromType($name);
- my $devName = $dev->{NAME};
- my $devPrefix = RESIDENTStk_GetPrefixFromType($devName);
- my $devType = GetType($devName);
- if ( $devName eq "global" ) {
- my $events = deviceEvents( $dev, 1 );
- return "" unless ($events);
- foreach ( @{$events} ) {
- next if ( $_ =~ m/^[A-Za-z\d_-]+:/ );
- # module and device initialization
- if ( $_ =~ m/^INITIALIZED|REREADCFG$/ ) {
- if ( !defined( &{'DoInitDev'} ) ) {
- if ( $_ eq "REREADCFG" ) {
- delete $modules{$TYPE}{READY};
- delete $modules{$TYPE}{INIT};
- }
- RESIDENTStk_DoInitDev(
- devspec2array("TYPE=$TYPE:FILTER=MOD_INIT=.+") );
- }
- }
- # if any of our monitored devices was modified,
- # recalculate monitoring status
- elsif ( $_ =~
- m/^(DEFINED|MODIFIED|RENAMED|DELETED)\s+([A-Za-z\d_-]+)$/ )
- {
- if ( defined( &{'DoInitDev'} ) ) {
- # DELETED would normally be handled by fhem.pl and imply
- # DoModuleTrigger instead of DoInitDev to update module
- # init state
- next if ( $_ =~ /^DELETED/ );
- DoInitDev($name);
- }
- else {
- # for DELETED, we normally would want to use
- # DoModuleTrigger() but we miss the deleted
- # device's TYPE at this state :-(
- RESIDENTStk_DoInitDev($name);
- }
- }
- # only process attribute events
- next
- unless ( $_ =~
- m/^((?:DELETE)?ATTR)\s+([A-Za-z\d._]+)\s+([A-Za-z\d_\.\-\/]+)(?:\s+(.*)\s*)?$/
- );
- my $cmd = $1;
- my $d = $2;
- my $attr = $3;
- my $val = $4;
- my $type = GetType($d);
- # filter attributes to be processed
- next
- unless ( $attr eq $prefix . "wakeupDevice"
- || $attr eq $prefix . "presenceDevices"
- || $attr eq $prefix . "wakeupResetSwitcher" );
- # when attributes of RESIDENTS, ROOMMATE or GUEST were changed
- if ( $d eq $name ) {
- if ( defined( &{'DoInitDev'} ) ) {
- DoInitDev($name)
- if ( $TYPE ne "dummy" );
- }
- else {
- RESIDENTStk_DoInitDev($name)
- if ( $TYPE ne "dummy" );
- }
- return "";
- }
- # when slave attributes where changed
- elsif ( $hash->{NOTIFYDEV} ) {
- my @ndlarr = devspec2array( $hash->{NOTIFYDEV} );
- return "" unless ( grep( $_ eq $d, @ndlarr ) );
- # wakeupResetSwitcher
- if ( $attr eq "wakeupResetSwitcher" ) {
- if ( $cmd eq "ATTR" ) {
- if ( !IsDevice($val) ) {
- my $alias = AttrVal( $d, "alias", undef );
- my $group = AttrVal( $d, "group", undef );
- my $room = AttrVal( $d, "room", undef );
- fhem "define $val dummy";
- $attr{$val}{comment} =
- "Auto-created by RESIDENTS Toolkit:"
- . " easy switch between on/off for auto time reset of wake-up timer $d";
- if ($alias) {
- $attr{$val}{alias} = "$alias Reset";
- }
- else {
- $attr{$val}{alias} = "Wake-up Timer Reset";
- }
- $attr{$val}{devStateIcon} =
- "auto:time_automatic:off "
- . "off:time_manual_mode:auto";
- $attr{$val}{group} = "$group"
- if ($group);
- $attr{$val}{icon} = "refresh";
- $attr{$val}{room} = "$room"
- if ($room);
- $attr{$val}{setList} = "state:auto,off";
- $attr{$val}{webCmd} = "state";
- fhem "set $val auto";
- Log3 $d, 3,
- "RESIDENTStk $d: "
- . "new slave dummy device $val created";
- }
- }
- if ( defined( &{'DoInitDev'} ) ) {
- DoInitDev($name);
- }
- else {
- RESIDENTStk_DoInitDev($name);
- }
- }
- }
- }
- return "";
- }
- return "" if ( IsDisabled($name) or IsDisabled($devName) );
- # process events from ROOMMATE or GUEST devices
- # only when they hit RESIDENTS devices
- if ( $TYPE eq "RESIDENTS" && $devType =~ /^ROOMMATE|GUEST$/ ) {
- my $events = deviceEvents( $dev, 1 );
- return "" unless ($events);
- readingsBeginUpdate($hash);
- foreach my $event ( @{$events} ) {
- next unless ( defined($event) );
- # state changed
- if ( $event !~ /^[a-zA-Z\d._]+:/
- || $event =~ /^state:/
- || $event =~ /^wayhome:/
- || $event =~ /^wakeup:/ )
- {
- RESIDENTS_UpdateReadings($hash);
- }
- # activity
- if ( $event !~ /^[a-zA-Z\d._]+:/ || $event =~ /^state:/ ) {
- # get user realname
- my $realname =
- AttrVal( $devName,
- AttrVal( $devName, $devPrefix . "realname", "group" ),
- $devName );
- # update statistics
- readingsBulkUpdate( $hash, "lastActivity",
- ReadingsVal( $devName, "state", $event ) );
- readingsBulkUpdate( $hash, "lastActivityBy", $realname );
- readingsBulkUpdate( $hash, "lastActivityByDev", $devName );
- }
- }
- readingsEndUpdate( $hash, 1 );
- return "";
- }
- delete $dev->{CHANGEDWITHSTATE};
- my $events = deviceEvents( $dev, 1 );
- return "" unless ($events);
- my @registeredWakeupdevs =
- split( ',', AttrVal( $name, $prefix . "wakeupDevice", "" ) );
- my @presenceDevices =
- split( ',', AttrVal( $name, $prefix . "presenceDevices", "" ) );
- foreach my $event ( @{$events} ) {
- next unless ( defined($event) );
- my $found = 0;
- # process wakeup devices
- if (@registeredWakeupdevs) {
- # if this is an event of a registered wakeup device
- if ( grep { m/^$devName$/ } @registeredWakeupdevs ) {
- RESIDENTStk_wakeupSet( $devName, $event );
- next;
- }
- # process sub-child events: *_wakeupDevice
- foreach my $wakeupDev (@registeredWakeupdevs) {
- # if this is an event of a registered sub dummy device
- # of one of our wakeup devices
- if ( AttrVal( $wakeupDev, "wakeupResetSwitcher", "" ) eq
- $devName
- && IsDevice( $devName, "dummy" ) )
- {
- RESIDENTStk_wakeupSet( $wakeupDev, $event )
- unless ( $event =~ /^(?:state:\s*)?off$/i );
- $found = 1;
- last;
- }
- }
- next if ($found);
- }
- # process PRESENCE
- if ( @presenceDevices
- && ( grep { /^$devName(:[A-Za-z\d_\.\-\/]+)?$/ } @presenceDevices )
- && $event =~ /^(?:([A-Za-z\d_\.\-\/]+): )?(.+)$/ )
- {
- my $reading = $1;
- my $value = $2;
- # early exit if unexpected event value
- next
- unless ( $value =~
- m/^0|false|absent|disappeared|unavailable|unreachable|disconnected|1|true|present|appeared|available|reachable|connected$/i
- );
- my $counter = {
- absent => 0,
- present => 0,
- };
- for (@presenceDevices) {
- my $r = "presence";
- my $d = $_;
- if ( $d =~ m/^([a-zA-Z\d._]+):(?:([A-Za-z\d_\.\-\/]*))?$/ ) {
- $d = $1;
- $r = $2 if ( $2 && $2 ne "" );
- }
- my $presenceState =
- ReadingsVal( $d, $r, ReadingsVal( $d, "state", "" ) );
- # ignore device if it has unexpected state
- next
- unless ( $presenceState =~
- m/^(0|false|absent|disappeared|unavailable|unreachable|disconnected)|(1|true|present|appeared|available|reachable|connected|)$/i
- );
- $counter->{absent}++ if ( defined($1) );
- $counter->{present}++ if ( defined($2) );
- }
- if ( $counter->{absent} && !$counter->{present} ) {
- Log3 $name, 4,
- "$TYPE $name: " . "Syncing status with $devName = absent";
- fhem "set $name:FILTER=presence=present absent";
- }
- elsif ( $counter->{present} ) {
- Log3 $name, 4,
- "$TYPE $name: " . "Syncing status with $devName = present";
- fhem "set $name:FILTER=presence=absent home";
- }
- }
- }
- return "";
- }
- # module Fn ####################################################################
- sub RESIDENTStk_AutoGone($;$) {
- my ( $mHash, @a ) = @_;
- my $hash = ( $mHash->{HASH} ) ? $mHash->{HASH} : $mHash;
- my $name = $hash->{NAME};
- my $TYPE = GetType($name);
- my $prefix = RESIDENTStk_GetPrefixFromType($name);
- my $autoGoneAfter = AttrVal(
- $hash->{NAME},
- $prefix . "autoGoneAfter",
- ( $prefix eq "rr_" ? 36 : 16 )
- );
- delete $hash->{AUTOGONE} if ( $hash->{AUTOGONE} );
- RESIDENTStk_RemoveInternalTimer( "AutoGone", $hash );
- return if ( IsDisabled($name) || !$autoGoneAfter );
- if ( ReadingsVal( $name, "state", "home" ) eq "absent" ) {
- my ( $date, $time, $y, $m, $d,
- $hour, $min, $sec, $timestamp, $timeDiff );
- my $timestampNow = gettimeofday();
- ( $date, $time ) = split( ' ', $hash->{READINGS}{state}{TIME} );
- ( $y, $m, $d ) = split( '-', $date );
- ( $hour, $min, $sec ) = split( ':', $time );
- $m -= 01;
- $timestamp = timelocal( $sec, $min, $hour, $d, $m, $y );
- $timeDiff = $timestampNow - $timestamp;
- if ( $timeDiff >= $autoGoneAfter * 3600 ) {
- Log3 $name, 3,
- "$TYPE $name: AutoGone timer changed state to 'gone'";
- RESIDENTStk_Set( $hash, $name, "silentSet", "state", "gone" );
- }
- else {
- my $runtime = $timestamp + $autoGoneAfter * 3600;
- $hash->{AUTOGONE} = $runtime;
- Log3 $name, 4, "$TYPE $name: AutoGone timer scheduled: $runtime";
- RESIDENTStk_InternalTimer( "AutoGone", $runtime,
- "RESIDENTStk_AutoGone", $hash, 1 );
- }
- }
- return undef;
- }
- sub RESIDENTStk_DurationTimer($;$) {
- my ( $mHash, @a ) = @_;
- my $hash = ( $mHash->{HASH} ) ? $mHash->{HASH} : $mHash;
- my $name = $hash->{NAME};
- my $TYPE = GetType($name);
- my $prefix = RESIDENTStk_GetPrefixFromType($name);
- my $silent = ( defined( $a[0] ) && $a[0] eq "1" ) ? 1 : 0;
- my $timestampNow = gettimeofday();
- my $diff;
- my $durPresence = "0";
- my $durAbsence = "0";
- my $durSleep = "0";
- my $noDuration = AttrVal( $name, $prefix . "noDuration", 0 );
- delete $hash->{DURATIONTIMER} if ( $hash->{DURATIONTIMER} );
- RESIDENTStk_RemoveInternalTimer( "DurationTimer", $hash );
- return if ( IsDisabled($name) || $noDuration );
- # presence timer
- if ( ReadingsVal( $name, "presence", "absent" ) eq "present"
- && ReadingsVal( $name, "lastArrival", "-" ) ne "-" )
- {
- $durPresence =
- $timestampNow -
- time_str2num( ReadingsVal( $name, "lastArrival", "" ) );
- }
- # absence timer
- if ( ReadingsVal( $name, "presence", "present" ) eq "absent"
- && ReadingsVal( $name, "lastDeparture", "-" ) ne "-" )
- {
- $durAbsence =
- $timestampNow -
- time_str2num( ReadingsVal( $name, "lastDeparture", "" ) );
- }
- # sleep timer
- if ( ReadingsVal( $name, "state", "home" ) eq "asleep"
- && ReadingsVal( $name, "lastSleep", "-" ) ne "-" )
- {
- $durSleep =
- $timestampNow - time_str2num( ReadingsVal( $name, "lastSleep", "" ) );
- }
- my $durPresence_hr =
- ( $durPresence > 0 )
- ? UConv::s2hms($durPresence)
- : "00:00:00";
- my $durPresence_cr =
- ( $durPresence > 60 ) ? int( $durPresence / 60 + 0.5 ) : 0;
- my $durAbsence_hr =
- ( $durAbsence > 0 ) ? UConv::s2hms($durAbsence) : "00:00:00";
- my $durAbsence_cr =
- ( $durAbsence > 60 ) ? int( $durAbsence / 60 + 0.5 ) : 0;
- my $durSleep_hr = ( $durSleep > 0 ) ? UConv::s2hms($durSleep) : "00:00:00";
- my $durSleep_cr = ( $durSleep > 60 ) ? int( $durSleep / 60 + 0.5 ) : 0;
- readingsBeginUpdate($hash) if ( !$silent );
- readingsBulkUpdateIfChanged( $hash, "durTimerPresence_cr",
- $durPresence_cr );
- readingsBulkUpdateIfChanged( $hash, "durTimerPresence", $durPresence_hr );
- readingsBulkUpdateIfChanged( $hash, "durTimerAbsence_cr", $durAbsence_cr );
- readingsBulkUpdateIfChanged( $hash, "durTimerAbsence", $durAbsence_hr );
- readingsBulkUpdateIfChanged( $hash, "durTimerSleep_cr", $durSleep_cr );
- readingsBulkUpdateIfChanged( $hash, "durTimerSleep", $durSleep_hr );
- readingsEndUpdate( $hash, 1 ) if ( !$silent );
- $hash->{DURATIONTIMER} = $timestampNow + 60;
- RESIDENTStk_InternalTimer( "DurationTimer", $hash->{DURATIONTIMER},
- "RESIDENTStk_DurationTimer", $hash, 1 );
- return undef;
- }
- sub RESIDENTStk_SetLocation(@) {
- my (
- $name, $location, $trigger, $id,
- $time, $lat, $long, $address,
- $device, $radius, $posLat, $posLong,
- $posAddress, $posBeaconUUID, $posDistHome, $posDistLoc,
- $motion, $wifiSSID, $wifiBSSID
- ) = @_;
- my $hash = $defs{$name};
- my $TYPE = GetType($name);
- my $prefix = RESIDENTStk_GetPrefixFromType($name);
- my $state = ReadingsVal( $name, "state", "initialized" );
- my $presence = ReadingsVal( $name, "presence", "present" );
- my $currLocation = ReadingsVal( $name, "location", "-" );
- my $currWayhome = ReadingsVal( $name, "wayhome", "0" );
- my $currLat = ReadingsVal( $name, "locationLat", "-" );
- my $currLong = ReadingsVal( $name, "locationLong", "-" );
- my $currRadius = ReadingsVal( $name, "locationRadius", "" );
- my $currAddr = ReadingsVal( $name, "locationAddr", "" );
- my $currPosLat = ReadingsVal( $name, "positionLat", "" );
- my $currPosLong = ReadingsVal( $name, "positionLong", "" );
- my $currPosAddr = ReadingsVal( $name, "positionAddr", "" );
- my $currPosBeaconUUID = ReadingsVal( $name, "positionBeaconUUID", "" );
- my $currPosDistHome = ReadingsVal( $name, "positionDistHome", "" );
- my $currPosDistLoc = ReadingsVal( $name, "positionDistLocation", "" );
- my $currPosMotion = ReadingsVal( $name, "positionMotion", "" );
- my $currPosSSID = ReadingsVal( $name, "positionSSID", "" );
- my $currPosBSSID = ReadingsVal( $name, "positionBSSID", "" );
- $id = "-" if ( !$id || $id eq "" );
- $lat = "-" if ( !$lat || $lat eq "" );
- $long = "-" if ( !$long || $long eq "" );
- $address = "" if ( !$address );
- $time = "" if ( !$time );
- $device = "" if ( !$device );
- $posLat = "" if ( !$posLat || $posLat eq "-" );
- $posLong = "" if ( !$posLong || $posLong eq "-" );
- $posAddress = "-" if ( !$posAddress || $posAddress eq "" );
- $posBeaconUUID = "" if ( !$posBeaconUUID || $posBeaconUUID eq "-" );
- Log3 $name, 5,
- "$TYPE $name: received location information: id=$id name=$location trig=$trigger date=$time lat=$lat long=$long posLat=$posLat posLong=$posLong address=$address device=$device";
- my $searchstring;
- readingsBeginUpdate($hash);
- # read attributes
- my @location_home =
- split( ' ', AttrVal( $name, $prefix . "locationHome", "home" ) );
- my @location_underway =
- split( ' ', AttrVal( $name, $prefix . "locationUnderway", "underway" ) );
- my @location_wayhome =
- split( ' ', AttrVal( $name, $prefix . "locationWayhome", "wayhome" ) );
- $searchstring = quotemeta($location);
- # update locationPresence
- # if ( $posBeaconUUID eq "" ) {
- readingsBulkUpdate( $hash, "locationPresence", "present" )
- if ( $trigger == 1 );
- readingsBulkUpdate( $hash, "locationPresence", "absent" )
- if ( $trigger == 0 );
- # }
- # # update positionPresence
- # readingsBulkUpdate( $hash, "positionPresence", "present" )
- # if ( $trigger == 1 );
- # readingsBulkUpdate( $hash, "positionPresence", "absent" )
- # if ( $trigger == 0 );
- # travelled distance for location
- my $locTravDist = "";
- if ( $lat ne "" && $long ne "" ) {
- my $locLatVal = ReadingsVal( $name, "locationLat", "-" );
- $locLatVal = ReadingsVal( $name, "lastLocationLat", "-" )
- if ( $locLatVal eq "-" );
- my $locLongVal = ReadingsVal( $name, "locationLong", "-" );
- $locLongVal = ReadingsVal( $name, "lastLocationLong", "-" )
- if ( $locLongVal eq "-" );
- if ( $locLatVal ne "-" && $locLongVal ne "-" ) {
- $locTravDist =
- UConv::distance( $lat, $long, $locLatVal, $locLongVal, 2 );
- }
- }
- # travelled distance for position
- my $posTravDist = "";
- if ( $posLat ne "" && $posLong ne "" ) {
- my $currPosLatVal = ReadingsVal( $name, "positionLat", "" );
- my $currPosLongVal = ReadingsVal( $name, "positionLong", "" );
- if ( $currPosLatVal ne "" && $currPosLongVal ne "" ) {
- $posTravDist = UConv::distance( $posLat, $posLong, $currPosLatVal,
- $currPosLongVal, 2 );
- }
- }
- # backup last known position
- foreach (
- 'positionLat', 'positionLong',
- 'positionAddr', 'positionBeaconUUID',
- 'positionDistHome', 'positionDistLocation',
- 'positionMotion', 'positionSSID',
- 'positionBSSID', 'positionTravDistance',
- 'locationTravDistance'
- )
- {
- my $currReading = $_;
- my $lastReading = "last" . ucfirst($_);
- my $currVal = ReadingsVal( $name, $currReading, undef );
- readingsBulkUpdate( $hash, $lastReading, $currVal )
- if ( defined($currVal) );
- }
- # update position based readings
- readingsBulkUpdate( $hash, "positionLat", $posLat );
- readingsBulkUpdate( $hash, "positionLong", $posLong );
- readingsBulkUpdate( $hash, "positionAddr", $posAddress );
- readingsBulkUpdate( $hash, "positionBeaconUUID", $posBeaconUUID );
- readingsBulkUpdate( $hash, "positionDistHome", $posDistHome );
- readingsBulkUpdate( $hash, "positionDistLocation", $posDistLoc );
- readingsBulkUpdate( $hash, "positionMotion", $motion );
- readingsBulkUpdate( $hash, "positionSSID", $wifiSSID );
- readingsBulkUpdate( $hash, "positionBSSID", $wifiBSSID );
- readingsBulkUpdate( $hash, "positionTravDistance", $posTravDist );
- readingsBulkUpdate( $hash, "locationTravDistance", $locTravDist );
- # check for implicit state change
- #
- my $stateChange = 0;
- # home
- if ( $location eq "home" || grep( m/^$searchstring$/, @location_home ) ) {
- Log3 $name, 5, "$TYPE $name: received signal from home location";
- # home
- if ( $state ne "home"
- && $state ne "gotosleep"
- && $state ne "asleep"
- && $state ne "awoken"
- && $trigger eq "1" )
- {
- $stateChange = 1;
- }
- # absent
- elsif ($state ne "gone"
- && $state ne "none"
- && $state ne "absent"
- && $trigger eq "0" )
- {
- $stateChange = 2;
- }
- }
- # underway
- elsif ($location eq "underway"
- || $location eq "wayhome"
- || grep( m/^$searchstring$/, @location_underway )
- || grep( m/^$searchstring$/, @location_wayhome ) )
- {
- Log3 $name, 5, "$TYPE $name: received signal from underway location";
- # absent
- $stateChange = 2
- if ( $state ne "gone"
- && $state ne "none"
- && $state ne "absent" );
- }
- # wayhome
- if (
- $location eq "wayhome"
- || ( grep( m/^$searchstring$/, @location_wayhome )
- && $trigger eq "0" )
- )
- {
- Log3 $name, 5, "$TYPE $name: wayhome signal received";
- # wayhome=true
- if (
- (
- ( $location eq "wayhome" && $trigger eq "1" )
- || ( $location ne "wayhome" && $trigger eq "0" )
- )
- && ReadingsVal( $name, "wayhome", "0" ) ne "1"
- )
- {
- Log3 $name, 3, "$TYPE $name: on way back home from $location";
- readingsBulkUpdate( $hash, "wayhome", "1" );
- }
- # wayhome=false
- elsif ($location eq "wayhome"
- && $trigger eq "0"
- && ReadingsVal( $name, "wayhome", "0" ) ne "0" )
- {
- Log3 $name, 3,
- "$TYPE $name: seems not to be on way back home anymore";
- readingsBulkUpdate( $hash, "wayhome", "0" );
- }
- }
- # activate wayhome tracing when reaching another location while wayhome=1
- elsif ( $stateChange == 0 && $trigger == 1 && $currWayhome == 1 ) {
- Log3 $name, 3,
- "$TYPE $name: seems to stay at $location before coming home";
- readingsBulkUpdate( $hash, "wayhome", "2" );
- }
- # revert wayhome during active wayhome tracing
- elsif ( $stateChange == 0 && $trigger == 0 && $currWayhome == 2 ) {
- Log3 $name, 3, "$TYPE $name: finally on way back home from $location";
- readingsBulkUpdate( $hash, "wayhome", "1" );
- }
- my $currLongDiff = 0;
- my $currLatDiff = 0;
- $currLongDiff =
- maxNum( ReadingsVal( $name, "lastLocationLong", 0 ), $currLong ) -
- minNum( ReadingsVal( $name, "lastLocationLong", 0 ), $currLong )
- if ( $currLong ne "-" );
- $currLatDiff =
- maxNum( ReadingsVal( $name, "lastLocationLat", 0 ), $currLat ) -
- minNum( ReadingsVal( $name, "lastLocationLat", 0 ), $currLat )
- if ( $currLat ne "-" );
- if (
- $trigger == 1
- && ( $stateChange > 0
- || ReadingsVal( $name, "lastLocation", "-" ) ne $currLocation
- || $currLongDiff > 0.00002
- || $currLatDiff > 0.00002 )
- )
- {
- Log3 $name, 5, "$TYPE $name: archiving last known location";
- readingsBulkUpdate( $hash, "lastLocationLat", $currLat );
- readingsBulkUpdate( $hash, "lastLocationLong", $currLong );
- readingsBulkUpdate( $hash, "lastLocationRadius", $currRadius );
- readingsBulkUpdate( $hash, "lastLocationAddr", $currAddr )
- if ( $currAddr ne "" );
- readingsBulkUpdate( $hash, "lastLocation", $currLocation );
- }
- readingsBulkUpdate( $hash, "locationLat", $lat );
- readingsBulkUpdate( $hash, "locationLong", $long );
- readingsBulkUpdate( $hash, "locationRadius", $radius );
- if ( $address ne "" ) {
- readingsBulkUpdate( $hash, "locationAddr", $address );
- }
- elsif ( $currAddr ne "" ) {
- readingsBulkUpdate( $hash, "locationAddr", "-" );
- }
- readingsBulkUpdate( $hash, "location", $location );
- readingsEndUpdate( $hash, 1 );
- # trigger state change
- if ( $stateChange > 0 ) {
- Log3 $name, 4,
- "$TYPE $name: implicit state change caused by location " . $location;
- RESIDENTStk_Set( $hash, $name, "silentSet", "state", "home" )
- if $stateChange == 1;
- RESIDENTStk_Set( $hash, $name, "silentSet", "state", "absent" )
- if $stateChange == 2;
- }
- }
- sub RESIDENTStk_wakeupSet($$) {
- my ( $NAME, $n ) = @_;
- my ( $a, $h ) = parseParams($n);
- my $cmd = shift @$a;
- my $VALUE = join( " ", @$a );
- $cmd =~ s/^state:\s*(.*)$/$1/;
- return if ( $cmd =~ /^[A-Za-z]+:/ );
- # filter non-registered events
- if ( $cmd !~
- m/^((?:next[rR]un)?\s*(off|OFF|([\+\-])?(([0-9]{2}):([0-9]{2})|([1-9]+[0-9]*)))?|trigger|start|stop|end|reset|auto|wakeupResetdays|wakeupDays|wakeupHolidays|wakeupEnforced|wakeupDefaultTime|wakeupOffset)$/i
- )
- {
- Log3 $NAME, 6,
- "RESIDENTStk $NAME: "
- . "received unspecified event '$cmd' - nothing to do";
- return;
- }
- my $wakeupMacro = AttrVal( $NAME, "wakeupMacro", 0 );
- my $wakeupDefaultTime =
- ReadingsVal( $NAME, "wakeupDefaultTime",
- AttrVal( $NAME, "wakeupDefaultTime", 0 ) );
- my $wakeupAtdevice = AttrVal( $NAME, "wakeupAtdevice", 0 );
- my $wakeupUserdevice = AttrVal( $NAME, "wakeupUserdevice", 0 );
- my $wakeupDays =
- ReadingsVal( $NAME, "wakeupDays", AttrVal( $NAME, "wakeupDays", "" ) );
- my $wakeupHolidays =
- ReadingsVal( $NAME, "wakeupHolidays",
- AttrVal( $NAME, "wakeupHolidays", "" ) );
- my $wakeupResetdays =
- ReadingsVal( $NAME, "wakeupResetdays",
- AttrVal( $NAME, "wakeupResetdays", "" ) );
- my $wakeupOffset =
- ReadingsVal( $NAME, "wakeupOffset", AttrVal( $NAME, "wakeupOffset", 0 ) );
- my $wakeupEnforced =
- ReadingsVal( $NAME, "wakeupEnforced",
- AttrVal( $NAME, "wakeupEnforced", 0 ) );
- my $wakeupResetSwitcher = AttrVal( $NAME, "wakeupResetSwitcher", 0 );
- my $holidayDevice = AttrVal( "global", "holiday2we", 0 );
- my $room = AttrVal( $NAME, "room", 0 );
- my $userattr = AttrVal( $NAME, "userattr", 0 );
- my $lastRun = ReadingsVal( $NAME, "lastRun", "07:00" );
- my $nextRun = ReadingsVal( $NAME, "nextRun", "07:00" );
- my $running = ReadingsVal( $NAME, "running", 0 );
- my $wakeupUserdeviceState = ReadingsVal( $wakeupUserdevice, "state", 0 );
- my $atName = "at_" . $NAME;
- my $wdNameGotosleep = "wd_" . $wakeupUserdevice . "_gotosleep";
- my $wdNameAsleep = "wd_" . $wakeupUserdevice . "_asleep";
- my $wdNameAwoken = "wd_" . $wakeupUserdevice . "_awoken";
- my $macroName = "Macro_" . $NAME;
- my $macroNameGotosleep = "Macro_" . $wakeupUserdevice . "_gotosleep";
- my $macroNameAsleep = "Macro_" . $wakeupUserdevice . "_asleep";
- my $macroNameAwoken = "Macro_" . $wakeupUserdevice . "_awoken";
- my $TYPE = GetType($wakeupUserdevice);
- my $prefix = RESIDENTStk_GetPrefixFromType($wakeupUserdevice);
- my $wakeupUserdeviceRealname = "Bewohner";
- if ( $TYPE eq "RESIDENTS" ) {
- $wakeupUserdeviceRealname = AttrVal(
- AttrVal( $NAME, "wakeupUserdevice", "" ),
- AttrVal(
- AttrVal( $NAME, "wakeupUserdevice", "" ),
- $prefix . "realname", "alias"
- ),
- $wakeupUserdeviceRealname
- );
- }
- else {
- $wakeupUserdeviceRealname = AttrVal(
- AttrVal( $NAME, "wakeupUserdevice", "" ),
- AttrVal(
- AttrVal( $NAME, "wakeupUserdevice", "" ),
- $prefix . "realname", "group"
- ),
- $wakeupUserdeviceRealname
- );
- }
- # check for required userattr attribute
- my $userattributes =
- "wakeupOffset:slider,0,1,120 wakeupDefaultTime:OFF,00:00,00:15,00:30,00:45,01:00,01:15,01:30,01:45,02:00,02:15,02:30,02:45,03:00,03:15,03:30,03:45,04:00,04:15,04:30,04:45,05:00,05:15,05:30,05:45,06:00,06:15,06:30,06:45,07:00,07:15,07:30,07:45,08:00,08:15,08:30,08:45,09:00,09:15,09:30,09:45,10:00,10:15,10:30,10:45,11:00,11:15,11:30,11:45,12:00,12:15,12:30,12:45,13:00,13:15,13:30,13:45,14:00,14:15,14:30,14:45,15:00,15:15,15:30,15:45,16:00,16:15,16:30,16:45,17:00,17:15,17:30,17:45,18:00,18:15,18:30,18:45,19:00,19:15,19:30,19:45,20:00,20:15,20:30,20:45,21:00,21:15,21:30,21:45,22:00,22:15,22:30,22:45,23:00,23:15,23:30,23:45 wakeupMacro wakeupUserdevice wakeupAtdevice wakeupResetSwitcher wakeupResetdays:multiple-strict,0,1,2,3,4,5,6 wakeupDays:multiple-strict,0,1,2,3,4,5,6 wakeupHolidays:andHoliday,orHoliday,andNoHoliday,orNoHoliday wakeupEnforced:0,1,2,3 wakeupWaitPeriod:slider,0,1,360";
- if ( !$userattr || $userattr ne $userattributes ) {
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: "
- . "adjusting dummy device for required attribute userattr";
- fhem "attr $NAME userattr $userattributes";
- }
- # check for required userdevice attribute
- if ( !$wakeupUserdevice ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "WARNING - set attribute wakeupUserdevice before running wakeup function!";
- }
- elsif ( !IsDevice($wakeupUserdevice) ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "WARNING - user device $wakeupUserdevice does not exist!";
- }
- elsif ( !IsDevice( $wakeupUserdevice, "RESIDENTS|ROOMMATE|GUEST" ) ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "WARNING - defined user device '$wakeupUserdevice' is not a RESIDENTS, ROOMMATE or GUEST device!";
- }
- # check for required wakeupMacro attribute
- if ( !$wakeupMacro ) {
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: "
- . "adjusting dummy device for required attribute wakeupMacro";
- fhem "attr $NAME wakeupMacro $macroName";
- $wakeupMacro = $macroName;
- }
- if ( !IsDevice($wakeupMacro) ) {
- my $wakeUpMacroTemplate = "{\
- ##=============================================================================\
- ## This is an example wake-up program running within a period of 30 minutes:\
- ## - drive shutters upwards slowly\
- ## - light up a HUE bulb from 2000K to 5600K\
- ## - have some voice notifications via SONOS\
- ## - have some wake-up chill music via SONOS during program run\
- ##\
- ## Actual FHEM commands are commented out by default as they would need\
- ## to be adapted to your configuration.\
- ##\
- ## Available wake-up variables:\
- ## 1. \$EVTPART0 -> start or stop\
- ## 2. \$EVTPART1 -> target wake-up time\
- ## 3. \$EVTPART2 -> wake-up begin time considering wakeupOffset attribute\
- ## 4. \$EVTPART3 -> enforced wakeup yes=1,no=0 from wakeupEnforced attribute\
- ## 5. \$EVTPART4 -> device name of the user which called this macro\
- ## 6. \$EVTPART5 -> current state of user\
- ##=============================================================================\
- \
- ##-----------------------------------------------------------------------------\
- ## DELETE TEMP. AT-COMMANDS POTENTIALLY CREATED EARLIER BY THIS SCRIPT\
- ## Executed for start to cleanup in case this wake-up automation is re-started.\
- ## Executed for stop to cleanup in case the user ends this automation earlier.\
- ##\
- fhem \"delete atTmp_.*_\".\$NAME.\":FILTER=TYPE=at\";;\
- \
- ##-----------------------------------------------------------------------------\
- ## BEGIN WAKE-UP PROGRAM\
- ## Run first automation commands and create temp. at-devices for lagging actions.\
- ##\
- if (\$EVTPART0 eq \"start\") {\
- Log3 \$NAME, 3, \"\$NAME: Wake-up program started for \$EVTPART4 with target time \$EVTPART1. Current state: \$EVTPART5\";;\
- \
- # fhem \"set BR_FloorLamp:FILTER=onoff=0 pct 1 : ct 2000 : transitiontime 0;; set BR_FloorLamp:FILTER=pct=1 pct 90 : ct 5600 : transitiontime 17700\";;\
- \
- # fhem \"define atTmp_1_\$NAME at +00:10:00 set BR_Shutter:FILTER=pct<20 pct 20\";;\
- # fhem \"define atTmp_2_\$NAME at +00:20:00 set BR_Shutter:FILTER=pct<40 pct 40\";;\
- # fhem \"define atTmp_4_\$NAME at +00:30:00 msg audio \\\@Sonos_Bedroom |Hint| Es ist \".\$EVTPART1.\" Uhr, Zeit zum aufstehen!;;;; set BR_FloorLamp:FILTER=pct<100 pct 100 60;;;; sleep 10;;;; set BR_Shutter:FILTER=pct<60 pct 60;;;; set Sonos_Bedroom:FILTER=Volume<10 Volume 10 10\";;\
- \
- # if wake-up should be enforced\
- if (\$EVTPART3) {\
- Log3 \$NAME, 3, \"\$NAME: planning enforced wake-up\";;\
- # fhem \"define atTmp_3_\$NAME at +00:25:00 set Sonos_Bedroom:FILTER=Volume>4 Volume 4;;;; sleep 0.5;;;; set Sonos_Bedroom:FILTER=Shuffle=0 Shuffle 1;;;; sleep 0.5;;;; set Sonos_Bedroom StartFavourite Morning%20Sounds\";;\
- # fhem \"define atTmp_4_\$NAME at +00:26:00 set Sonos_Bedroom:FILTER=Volume<5 Volume 5\";;\
- # fhem \"define atTmp_5_\$NAME at +00:27:00 set Sonos_Bedroom:FILTER=Volume<6 Volume 6\";;\
- # fhem \"define atTmp_6_\$NAME at +00:28:00 set Sonos_Bedroom:FILTER=Volume<7 Volume 7\";;\
- # fhem \"define atTmp_7_\$NAME at +00:29:00 set Sonos_Bedroom:FILTER=Volume<8 Volume 8\";;\
- }\
- }\
- \
- ##-----------------------------------------------------------------------------\
- ## END WAKE-UP PROGRAM (OPTIONAL)\
- ## Put some post wake-up tasks here like reminders after the actual wake-up period.\
- ##\
- ## Note: Will only be run when program ends normally after minutes specified in wakeupOffset.\
- ## If stop was user-forced by sending explicit set-command 'stop', this is not executed\
- ## assuming the user does not want any further automation activities.\
- ##\
- if (\$EVTPART0 eq \"stop\") {\
- Log3 \$NAME, 3, \"\$NAME: Wake-up program ended for \$EVTPART4 with target time \$EVTPART1. Current state: \$EVTPART5\";;\
- \
- # if wake-up should be enforced, auto-change user state from 'asleep' to 'awoken'\
- # after a small additional nap to kick you out of bed if user did not confirm to be awake :-)\
- # An additional notify for user state 'awoken' may take further actions\
- # and change to state 'home' afterwards.\
- if (\$EVTPART3) {\
- fhem \"define atTmp_9_\$NAME at +00:05:00 set \$EVTPART4:FILTER=state=asleep awoken\";;\
- \
- # Without enforced wake-up, be jentle and just set user state to 'home' after some\
- # additional long nap time\
- } else {\
- fhem \"define atTmp_9_\$NAME at +01:30:00 set \$EVTPART4:FILTER=state=asleep home\";;\
- }\
- }\
- \
- }\
- ";
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "new notify macro device $wakeupMacro created";
- fhem "define $wakeupMacro notify $wakeupMacro $wakeUpMacroTemplate";
- fhem
- "attr $wakeupMacro comment Macro auto-created by RESIDENTS Toolkit";
- fhem "attr $wakeupMacro room $room"
- if ($room);
- }
- elsif ( !IsDevice( $wakeupMacro, "notify" ) ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "WARNING - defined macro device '$wakeupMacro' is not a notify device!";
- }
- # check for required wakeupAtdevice attribute
- if ( !$wakeupAtdevice ) {
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: "
- . "adjusting dummy device for required attribute wakeupAtdevice";
- fhem "attr $NAME wakeupAtdevice $atName";
- $wakeupAtdevice = $atName;
- }
- if ( !IsDevice($wakeupAtdevice) ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: " . "new at-device $wakeupAtdevice created";
- fhem "define $wakeupAtdevice "
- . "at *{RESIDENTStk_wakeupGetBegin(\"$NAME\",\"$wakeupAtdevice\")} set $NAME trigger";
- fhem "attr $wakeupAtdevice "
- . "comment Auto-created by RESIDENTS Toolkit: trigger wake-up timer at specific time";
- fhem "attr $wakeupAtdevice computeAfterInit 1";
- fhem "attr $wakeupAtdevice room $room"
- if ($room);
- ########
- # (re)create other notify and watchdog templates
- # for ROOMMATE or GUEST devices
- # macro: gotosleep
- if ( !IsDevice( $wakeupUserdevice, "RESIDENTS" )
- && !IsDevice($macroNameGotosleep) )
- {
- my $templateGotosleep = "{\
- ##=============================================================================\
- ## This is an example macro when gettin' ready for bed.\
- ##\
- ## Actual FHEM commands are commented out by default as they would need\
- ## to be adapted to your configuration.\
- ##=============================================================================\
- \
- ##-----------------------------------------------------------------------------\
- ## LIGHT SCENE\
- ##\
- \
- ## Dim up floor light\
- #fhem \"set FL_Light:FILTER=pct=0 pct 20\";;\
- \
- ## Dim down bright ceilling light in bedroom\
- #fhem \"set BR_Light:FILTER=pct!=0 pct 0 5\";;\
- \
- ## Dim up HUE floor lamp with very low color temperature\
- #fhem \"set BR_FloorLamp ct 2000 : pct 80 : transitiontime 30\";;\
- \
- \
- ##-----------------------------------------------------------------------------\
- ## ENVIRONMENT SCENE\
- ##\
- \
- ## Turn down shutter to 28%\
- #fhem \"set BR_Shutter:FILTER=pct>28 pct 28\";;\
- \
- \
- ##-----------------------------------------------------------------------------\
- ## PLAY CHILLOUT MUSIC\
- ## via SONOS at Bedroom and Bathroom\
- ##\
- \
- ## Stop playback bedroom's Sonos device might be involved in\
- #fhem \"set Sonos_Bedroom:transportState=PLAYING stop;;\";;\
- \
- ## Make Bedroom's and Bathroom's Sonos devices a single device\
- ## and do not touch other Sonos devices (this is why we use RemoveMember!)\
- #fhem \"sleep 0.5;; set Sonos_Bedroom RemoveMember Sonos_Bedroom\";;\
- #fhem \"sleep 1.0;; set Sonos_Bathroom RemoveMember Sonos_Bathroom\";;\
- \
- ## Group Bedroom's and Bathroom's Sonos devices with Bedroom as master\
- #fhem \"sleep 2.0;; set Sonos_Bedroom AddMember Sonos_Bathroom;; set Sonos_Bedroom:FILTER=Shuffle!=1 Shuffle 1;; set Sonos_Bedroom:FILTER=Volume!=12,Sonos_Bathroom:FILTER=Volume!=12 Volume 12\";;\
- \
- ## Start music from playlist\
- #fhem \"sleep 3.0;; set Sonos_Bedroom StartFavourite Evening%%20Chill\";;\
- \
- return;;\
- }";
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "new notify macro device $macroNameGotosleep created";
- fhem "define $macroNameGotosleep "
- . "notify $macroNameGotosleep $templateGotosleep";
- fhem "attr $macroNameGotosleep "
- . "comment Auto-created by RESIDENTS Toolkit: FHEM commands to run when gettin' ready for bed";
- fhem "attr $macroNameGotosleep room $room"
- if ($room);
- }
- # wd: gotosleep
- if ( !IsDevice($wdNameGotosleep) ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "new watchdog device $wdNameGotosleep created";
- fhem "define $wdNameGotosleep "
- . "watchdog $wakeupUserdevice:(gotosleep|bettfertig) 00:00:04 $wakeupUserdevice:(home|anwesend|zuhause|absent|abwesend|gone|verreist|asleep|schlaeft|schläft|awoken|aufgestanden) trigger $macroNameGotosleep";
- fhem "attr $wdNameGotosleep autoRestart 1";
- fhem "attr $wdNameGotosleep "
- . "comment Auto-created by RESIDENTS Toolkit: trigger macro after going to state gotosleep";
- fhem "attr $wdNameGotosleep room $room"
- if ($room);
- }
- # macro: asleep
- if ( !IsDevice( $wakeupUserdevice, "RESIDENTS" )
- && !IsDevice($macroNameAsleep) )
- {
- my $templateAsleep = "{\
- ##=============================================================================\
- ## This is an example macro when jumpin' into bed and start to sleep.\
- ##\
- ## Actual FHEM commands are commented out by default as they would need\
- ## to be adapted to your configuration.\
- ##=============================================================================\
- \
- ##-----------------------------------------------------------------------------\
- ## LIGHT SCENE\
- ##\
- \
- ## In 15 seconds, turn off all lights in Bedroom using a structure\
- #fhem \"sleep 15;; set g_BR_Lights [FILTER=state!=off] off\";;\
- \
- \
- ##-----------------------------------------------------------------------------\
- ## ENVIRONMENT SCENE\
- ##\
- \
- ## In 12 seconds, close shutter if window is closed\
- #if (ReadingsVal(\"BR_Window\",\"state\",0) eq \"closed\") {\
- # fhem \"sleep 12;; set BR_Shutter:FILTER=pct>0 close\";;\
- \
- ## In 12 seconds, if window is not closed just make sure shutter is at least\
- ## at 28% to allow some ventilation\
- #} else {\
- # fhem \"sleep 12;; set BR_Shutter:FILTER=pct>28 pct 28\";;\
- #}\
- \
- \
- ##-----------------------------------------------------------------------------\
- ## PLAY WAKE-UP ANNOUNCEMENT\
- ## via SONOS at Bedroom and stop playback elsewhere\
- ##\
- \
- #my \$nextWakeup = ReadingsVal(\"$wakeupUserdevice\",\"nextWakeup\",\"none\");;
- #my \$text = \"|Hint| $wakeupUserdeviceRealname, es ist kein Wecker gestellt. Du könntest verschlafen! Trotzdem eine gute Nacht.\";;
- #if (\$nextWakeup ne \"OFF\") {
- # \$text = \"|Hint| $wakeupUserdeviceRealname, dein Wecker ist auf \$nextWakeup Uhr gestellt. Gute Nacht und schlaf gut.\";;
- #}
- #if (\$nextWakeup ne \"none\") {
- # fhem \"set Sonos_Bedroom RemoveMember Sonos_Bedroom;; sleep 0.5;; msg audio \\\@Sonos_Bedroom \$text\";;\
- #}
- \
- return;;\
- }";
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "new notify macro device $macroNameAsleep created";
- fhem
- "define $macroNameAsleep notify $macroNameAsleep $templateAsleep";
- fhem "attr $macroNameAsleep "
- . "comment Auto-created by RESIDENTS Toolkit: FHEM commands to run when jumpin' into bed and start to sleep";
- fhem "attr $macroNameAsleep room $room"
- if ($room);
- }
- # wd: asleep
- if ( !IsDevice($wdNameAsleep) ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "new watchdog device $wdNameAsleep created";
- fhem "define $wdNameAsleep "
- . "watchdog $wakeupUserdevice:(asleep|schlaeft|schläft) 00:00:04 $wakeupUserdevice:(home|anwesend|zuhause|absent|abwesend|gone|verreist|gotosleep|bettfertig|awoken|aufgestanden) trigger $macroNameAsleep";
- fhem "attr $wdNameAsleep autoRestart 1";
- fhem "attr $wdNameAsleep "
- . "comment Auto-created by RESIDENTS Toolkit: trigger macro after going to state asleep";
- fhem "attr $wdNameAsleep room $room"
- if ($room);
- }
- # macro: awoken
- if ( !IsDevice( $wakeupUserdevice, "RESIDENTS" )
- && !IsDevice($macroNameAwoken) )
- {
- my $templateAwoken = "{\
- ##=============================================================================\
- ## This is an example macro after confirming to be awake.\
- ##\
- ## Actual FHEM commands are commented out by default as they would need\
- ## to be adapted to your configuration.\
- ##=============================================================================\
- \
- ##-----------------------------------------------------------------------------\
- ## LIGHT SCENE\
- ##\
- \
- ## Dim up HUE floor lamp to maximum with cold color temperature\
- #fhem \"set BR_FloorLamp:FILTER=pct<100 pct 100 : ct 6500 : transitiontime 30\";;\
- \
- \
- ##-----------------------------------------------------------------------------\
- ## ENVIRONMENT SCENE\
- ##\
- \
- ## In 22 seconds, turn up shutter at least until 60%\
- #fhem \"sleep 22;; set BR_Shutter:FILTER=pct<60 60\";;\
- \
- \
- ##-----------------------------------------------------------------------------\
- ## RAMP-UP ALL MORNING STUFF\
- ##\
- \
- ## Play morning announcement via SONOS at Bedroom\
- #fhem \"set Sonos_Bedroom Stop;; msg audio \\\@Sonos_Bedroom |Hint| Guten Morgen, $wakeupUserdeviceRealname.\";;\
- \
- ## In 10 seconds, start webradio playback in Bedroom\
- #fhem \"sleep 10;; set Sonos_Bedroom StartRadio /Charivari/;; sleep 2;; set Sonos_Bedroom Volume 15\";;\
- \
- ## Make webradio stream available at Bathroom and\
- ## Kitchen 5 seonds after it started\
- #fhem \"set Sonos_Bathroom,Sonos_Kitchen Volume 15;; sleep 15;; set Sonos_Bedroom AddMember Sonos_Bathroom;; set Sonos_Bedroom AddMember Sonos_Kitchen\";;\
- \
- ## change user state to home after 60 seconds\
- fhem \"sleep 60;; set $wakeupUserdevice:FILTER=state!=home home\";;\
- \
- return;;\
- }";
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "new notify macro device $macroNameAwoken created";
- fhem
- "define $macroNameAwoken notify $macroNameAwoken $templateAwoken";
- fhem "attr $macroNameAwoken "
- . "comment Auto-created by RESIDENTS Toolkit: FHEM commands to run after confirming to be awake";
- fhem "attr $macroNameAwoken room $room"
- if ($room);
- }
- # wd: awoken
- if ( !IsDevice($wdNameAwoken) ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "new watchdog device $wdNameAwoken created";
- fhem "define $wdNameAwoken "
- . "watchdog $wakeupUserdevice:(awoken|aufgestanden) 00:00:04 $wakeupUserdevice:(home|anwesend|zuhause|absent|abwesend|gone|verreist|gotosleep|bettfertig|asleep|schlaeft|schläft) trigger $macroNameAwoken";
- fhem "attr $wdNameAwoken autoRestart 1";
- fhem "attr $wdNameAwoken "
- . "comment Auto-created by RESIDENTS Toolkit: trigger macro after going to state awoken";
- fhem "attr $wdNameAwoken room $room"
- if ($room);
- }
- ########
- # (re)create other notify and watchdog templates
- # for RESIDENT devices
- #
- my $RESIDENTGROUPS;
- if ( IsDevice( $wakeupUserdevice, "RESIDENTS" ) ) {
- $RESIDENTGROUPS = $wakeupUserdevice;
- }
- elsif ( IsDevice($wakeupUserdevice)
- && defined( $defs{$wakeupUserdevice}{RESIDENTGROUPS} ) )
- {
- $RESIDENTGROUPS = $defs{$wakeupUserdevice}{RESIDENTGROUPS};
- }
- for my $deviceName ( split /,/, $RESIDENTGROUPS ) {
- my $macroRNameGotosleep = "Macro_" . $deviceName . "_gotosleep";
- my $macroRNameAsleep = "Macro_" . $deviceName . "_asleep";
- my $macroRNameAwoken = "Macro_" . $deviceName . "_awoken";
- my $wdRNameGotosleep = "wd_" . $deviceName . "_gotosleep";
- my $wdRNameAsleep = "wd_" . $deviceName . "_asleep";
- my $wdRNameAwoken = "wd_" . $deviceName . "_awoken";
- # macro: gotosleep
- if ( !IsDevice($macroRNameGotosleep) ) {
- my $templateGotosleep = "{\
- ##=============================================================================\
- ## This is an example macro when all residents are gettin' ready for bed.\
- ##\
- ## Actual FHEM commands are commented out by default as they would need\
- ## to be adapted to your configuration.\
- ##=============================================================================\
- \
- ##-----------------------------------------------------------------------------\
- ## HOUSE MODE\
- ## Enforce evening mode if we are still in day mode\
- ##\
- \
- #fhem \"set HouseMode:FILTER=state=day evening\";;\
- \
- \
- ##-----------------------------------------------------------------------------\
- ## LIGHT SCENE\
- ##\
- \
- ## In 10 seconds, turn off lights in unused rooms using structures\
- #fhem \"sleep 10;; set g_LR_Lights,g_KT_Lights [FILTER=state!=off] off\";;\
- \
- \
- ##-----------------------------------------------------------------------------\
- ## ENVIRONMENT SCENE\
- ##\
- \
- ## Turn off all media devices in the Living Room\
- #fhem \"set g_HSE_Media [FILTER=state!=off] off\";;\
- \
- return;;\
- }";
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "new notify macro device $macroRNameGotosleep created";
- fhem "define $macroRNameGotosleep "
- . "notify $macroRNameGotosleep $templateGotosleep";
- fhem "attr $macroRNameGotosleep "
- . "comment Auto-created by RESIDENTS Toolkit: FHEM commands to run when all residents are gettin' ready for bed";
- fhem "attr $macroRNameGotosleep room $room"
- if ($room);
- }
- # wd: gotosleep
- if ( !IsDevice($wdRNameGotosleep) ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "new watchdog device $wdRNameGotosleep created";
- fhem "define $wdRNameGotosleep "
- . "watchdog $deviceName:(gotosleep|bettfertig) 00:00:03 $deviceName:(home|anwesend|zuhause|absent|abwesend|gone|verreist|asleep|schlaeft|schläft|awoken|aufgestanden) trigger $macroRNameGotosleep";
- fhem "attr $wdRNameGotosleep autoRestart 1";
- fhem "attr $wdRNameGotosleep "
- . "comment Auto-created by RESIDENTS Toolkit: trigger macro after going to state gotosleep";
- fhem "attr $wdRNameGotosleep room $room"
- if ($room);
- }
- # macro: asleep
- if ( !IsDevice($macroRNameAsleep) ) {
- my $templateAsleep = "{\
- ##=============================================================================\
- ## This is an example macro when all residents are in their beds.\
- ##\
- ## Actual FHEM commands are commented out by default as they would need\
- ## to be adapted to your configuration.\
- ##=============================================================================\
- \
- ##-----------------------------------------------------------------------------\
- ## HOUSE MODE\
- ## Enforce night mode if we are still in evening mode\
- ##\
- \
- #fhem \"set HouseMode:FILTER=state=evening night\";;\
- \
- \
- ##-----------------------------------------------------------------------------\
- ## LIGHT SCENE\
- ##\
- \
- ## In 20 seconds, turn off all lights in the house using structures\
- #fhem \"sleep 20;; set g_HSE_Lights [FILTER=state!=off] off\";;\
- \
- \
- ##-----------------------------------------------------------------------------\
- ## ENVIRONMENT SCENE\
- ##\
- \
- ## Stop playback at SONOS devices in shared rooms, e.g. Bathroom\
- #fhem \"set Sonos_Bathroom:FILTER=transportState=PLAYING Stop\";;\
- \
- return;;\
- }";
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "new notify macro device $macroRNameAsleep created";
- fhem "define $macroRNameAsleep "
- . "notify $macroRNameAsleep $templateAsleep";
- fhem "attr $macroRNameAsleep "
- . "comment Auto-created by RESIDENTS Toolkit: FHEM commands to run when all residents are in their beds";
- fhem "attr $macroRNameAsleep room $room"
- if ($room);
- }
- # wd: asleep
- if ( !IsDevice($wdRNameAsleep) ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "new watchdog device $wdNameAsleep created";
- fhem "define $wdRNameAsleep "
- . "watchdog $deviceName:(asleep|schlaeft|schläft) 00:00:03 $deviceName:(home|anwesend|zuhause|absent|abwesend|gone|verreist|gotosleep|bettfertig|awoken|aufgestanden) trigger $macroRNameAsleep";
- fhem "attr $wdRNameAsleep autoRestart 1";
- fhem "attr $wdRNameAsleep "
- . "comment Auto-created by RESIDENTS Toolkit: trigger macro after going to state asleep";
- fhem "attr $wdRNameAsleep room $room"
- if ($room);
- }
- # macro: awoken
- if ( !IsDevice($macroRNameAwoken) ) {
- my $templateAwoken = "{\
- ##=============================================================================\
- ## This is an example macro when the first resident has confirmed to be awake\
- ##\
- ## Actual FHEM commands are commented out by default as they would need\
- ## to be adapted to your configuration.\
- ##=============================================================================\
- \
- ##-----------------------------------------------------------------------------\
- ## HOUSE MODE\
- ## Enforce morning mode if we are still in night mode\
- ##\
- \
- #fhem \"set HouseMode:FILTER=state=night morning\";;\
- \
- \
- ##-----------------------------------------------------------------------------\
- ## LIGHT SCENE\
- ##\
- \
- ## Turn on lights in the Kitchen already but set a timer to turn it off again\
- #fhem \"set KT_CounterLight on-for-timer 6300\";;\
- \
- \
- ##-----------------------------------------------------------------------------\
- ## PREPARATIONS\
- ##\
- \
- ## In 90 minutes, switch House Mode to 'day' and\
- ## play voice announcement via SONOS\
- #unless (IsDevice(\"atTmpHouseMode_day\")) {\
- # fhem \"define atTmpHouseMode_day at +01:30:00 {if (ReadingsVal(\\\"HouseMode\\\", \\\"state\\\", 0) ne \\\"day\\\") {fhem \\\"msg audio \\\@Sonos_Kitchen Tagesmodus wird etabliert.;;;; sleep 10;;;; set HouseMode day\\\"}}\";;\
- #}\
- \
- return;;\
- }";
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "new notify macro device $macroRNameAwoken created";
- fhem "define $macroRNameAwoken "
- . "notify $macroRNameAwoken $templateAwoken";
- fhem "attr $macroRNameAwoken "
- . "comment Auto-created by RESIDENTS Toolkit: FHEM commands to run after first resident confirmed to be awake";
- fhem "attr $macroRNameAwoken room $room"
- if ($room);
- }
- # wd: awoken
- if ( !IsDevice($wdRNameAwoken) ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "new watchdog device $wdNameAwoken created";
- fhem "define $wdRNameAwoken "
- . "watchdog $deviceName:(awoken|aufgestanden) 00:00:04 $deviceName:(home|anwesend|zuhause|absent|abwesend|gone|verreist|gotosleep|bettfertig|asleep|schlaeft|schläft) trigger $macroRNameAwoken";
- fhem "attr $wdRNameAwoken autoRestart 1";
- fhem "attr $wdRNameAwoken "
- . "comment Auto-created by RESIDENTS Toolkit: trigger macro after going to state awoken";
- fhem "attr $wdRNameAwoken room $room"
- if ($room);
- }
- }
- }
- elsif ( !IsDevice( $wakeupAtdevice, "at" ) ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "WARNING - defined at-device '$wakeupAtdevice' is not an at-device!";
- }
- elsif ( AttrVal( $wakeupAtdevice, "computeAfterInit", 0 ) ne "1" ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "Correcting '$wakeupAtdevice' attribute computeAfterInit required for correct recalculation after reboot";
- fhem "attr $wakeupAtdevice computeAfterInit 1";
- }
- # verify holiday2we attribute
- if ( $wakeupHolidays ne "" ) {
- if ( !$holidayDevice ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "ERROR - wakeupHolidays set in this alarm clock but global attribute holiday2we not set!";
- return "ERROR: "
- . "wakeupHolidays set in this alarm clock but global attribute holiday2we not set!";
- }
- elsif ( !IsDevice($holidayDevice) ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "ERROR - global attribute holiday2we has reference to non-existing device $holidayDevice";
- return "ERROR: "
- . "global attribute holiday2we has reference to non-existing device $holidayDevice";
- }
- elsif ( !IsDevice( $holidayDevice, "holiday" ) ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "ERROR - global attribute holiday2we seems to have invalid device reference - $holidayDevice is not of type 'holiday'";
- return "ERROR: "
- . "global attribute holiday2we seems to have invalid device reference - $holidayDevice is not of type 'holiday'";
- }
- }
- # start
- #
- if ( $cmd eq "start" ) {
- RESIDENTStk_wakeupRun( $NAME, 1 );
- }
- # trigger
- #
- elsif ( $cmd eq "trigger" ) {
- RESIDENTStk_wakeupRun($NAME);
- }
- # stop | end
- #
- elsif ( $cmd eq "stop" || $cmd eq "end" ) {
- if ($running) {
- Log3 $NAME, 4, "RESIDENTStk $NAME: " . "stopping wake-up program";
- readingsSingleUpdate( $defs{$NAME}, "running", "0", 1 );
- }
- fhem "set $NAME nextRun $nextRun";
- return unless ($running);
- # trigger macro again so it may clean up it's stuff.
- # use $EVTPART1 to check
- if ( !$wakeupMacro ) {
- Log3 $NAME, 2,
- "RESIDENTStk $NAME: " . "missing attribute wakeupMacro";
- }
- elsif ( !IsDevice($wakeupMacro) ) {
- Log3 $NAME, 2,
- "RESIDENTStk $NAME: "
- . "notify macro $wakeupMacro not found - no wakeup actions defined!";
- }
- elsif ( !IsDevice( $wakeupMacro, "notify" ) ) {
- Log3 $NAME, 2,
- "RESIDENTStk $NAME: "
- . "device $wakeupMacro is not of type notify";
- }
- else {
- # conditional enforced wake-up:
- # only if actual wake-up time is
- # earlier than wakeupDefaultTime
- if ( $wakeupEnforced == 3
- && $wakeupDefaultTime
- && UConv::hms2s($wakeupDefaultTime) > UConv::hms2s($lastRun) )
- {
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: "
- . "Enforcing wake-up because wake-up time is earlier than normal (wakeupDefaultTime=$wakeupDefaultTime > lastRun=$lastRun)";
- $wakeupEnforced = 1;
- }
- # conditional enforced wake-up:
- # only if actual wake-up time is not wakeupDefaultTime
- elsif ($wakeupEnforced == 2
- && $wakeupDefaultTime
- && $wakeupDefaultTime ne $lastRun )
- {
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: "
- . "Enforcing wake-up because wake-up is different from normal (wakeupDefaultTime=$wakeupDefaultTime =! lastRun=$lastRun)";
- $wakeupEnforced = 1;
- }
- elsif ( $wakeupEnforced > 1 ) {
- $wakeupEnforced = 0;
- }
- if ( $VALUE ne "" || $cmd eq "end" ) {
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: "
- . "trigger $wakeupMacro stop $lastRun $wakeupOffset $wakeupEnforced $wakeupUserdevice $wakeupUserdeviceState";
- fhem "trigger $wakeupMacro "
- . "stop $lastRun $wakeupOffset $wakeupEnforced $wakeupUserdevice $wakeupUserdeviceState";
- }
- else {
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: "
- . "trigger $wakeupMacro forced-stop $lastRun $wakeupOffset $wakeupEnforced $wakeupUserdevice $wakeupUserdeviceState";
- fhem "trigger $wakeupMacro "
- . "forced-stop $lastRun $wakeupOffset $wakeupEnforced $wakeupUserdevice $wakeupUserdeviceState";
- fhem "set $wakeupUserdevice:FILTER=state=asleep awoken";
- }
- my $wakeupStopAtdevice = $wakeupAtdevice . "_stop";
- fhem "delete $wakeupStopAtdevice"
- if ( IsDevice($wakeupStopAtdevice) );
- }
- readingsSingleUpdate( $defs{$wakeupUserdevice}, "wakeup", "0", 1 );
- return;
- }
- # auto or reset
- #
- elsif ( $cmd eq "auto" || $cmd eq "reset" ) {
- my $resetTime = ReadingsVal( $NAME, "lastRun", 0 );
- if ($wakeupDefaultTime) {
- $resetTime = $wakeupDefaultTime;
- }
- if ( $resetTime
- && !( $cmd eq "auto" && lc($resetTime) eq "off" ) )
- {
- fhem "set $NAME:FILTER=state!=$resetTime nextRun $resetTime";
- }
- elsif ( $cmd eq "reset" ) {
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: "
- . "no default value specified in attribute wakeupDefaultTime, just keeping setting OFF";
- fhem "set $NAME:FILTER=state!=OFF nextRun OFF";
- }
- return;
- }
- # wakeup attributes
- #
- elsif ( $cmd =~
- m/^(wakeupResetdays|wakeupDays|wakeupHolidays|wakeupEnforced|wakeupDefaultTime|wakeupOffset)$/
- )
- {
- Log3 $NAME, 4, "RESIDENTStk $NAME: " . "setting $1 to '$VALUE'";
- readingsBeginUpdate( $defs{$NAME} );
- readingsBulkUpdate( $defs{$NAME}, $1, $VALUE );
- readingsEndUpdate( $defs{$NAME}, 1 );
- fhem "set $NAME nextRun $nextRun";
- return;
- }
- # set new wakeup value
- elsif ( IsDevice( $wakeupAtdevice, "at" )
- && $cmd =~
- m/^(?:nextRun)?\s*(OFF|([\+\-])?(([0-9]{2}):([0-9]{2})|([1-9]+[0-9]*)))?$/i
- )
- {
- $VALUE = $1 if ( $1 && $1 ne "" );
- $VALUE =
- RESIDENTStk_TimeSum( ReadingsVal( $NAME, "nextRun", 0 ), $VALUE )
- if ($2);
- # Update wakeuptimer device
- #
- readingsBeginUpdate( $defs{$NAME} );
- if ( ReadingsVal( $NAME, "nextRun", 0 ) ne $VALUE ) {
- Log3 $NAME, 4, "RESIDENTStk $NAME: " . "New wake-up time: $VALUE";
- readingsBulkUpdate( $defs{$NAME}, "nextRun", $VALUE );
- # Update at-device
- fhem "set $wakeupAtdevice "
- . "modifyTimeSpec {RESIDENTStk_wakeupGetBegin(\"$NAME\",\"$wakeupAtdevice\")}";
- }
- if ( ReadingsVal( $NAME, "state", 0 ) ne $VALUE
- && !$running )
- {
- readingsBulkUpdate( $defs{$NAME}, "state", $VALUE );
- }
- elsif ( ReadingsVal( $NAME, "state", 0 ) ne "running"
- && $running )
- {
- readingsBulkUpdate( $defs{$NAME}, "state", "running" );
- }
- readingsEndUpdate( $defs{$NAME}, 1 );
- # Update user device
- #
- readingsBeginUpdate( $defs{$wakeupUserdevice} );
- my ( $nextWakeupDev, $nextWakeup ) =
- RESIDENTStk_wakeupGetNext($wakeupUserdevice);
- if ( !$nextWakeupDev || !$nextWakeup ) {
- $nextWakeupDev = "";
- $nextWakeup = "OFF";
- }
- readingsBulkUpdateIfChanged( $defs{$wakeupUserdevice},
- "nextWakeupDev", $nextWakeupDev );
- readingsBulkUpdateIfChanged( $defs{$wakeupUserdevice},
- "nextWakeup", $nextWakeup );
- readingsEndUpdate( $defs{$wakeupUserdevice}, 1 );
- }
- return undef;
- }
- sub RESIDENTStk_wakeupGetBegin($;$) {
- my ( $NAME, $wakeupAtdevice ) = @_;
- unless ( IsDevice($NAME) ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "Run function RESIDENTStk_wakeupGetBegin() for non-existing device!";
- return "$NAME: Non-existing device";
- }
- my $nextRun = ReadingsVal( $NAME, "nextRun", 0 );
- my $wakeupDefaultTime =
- ReadingsVal( $NAME, "wakeupDefaultTime",
- AttrVal( $NAME, "wakeupDefaultTime", 0 ) );
- my $wakeupOffset =
- ReadingsVal( $NAME, "wakeupOffset", AttrVal( $NAME, "wakeupOffset", 0 ) );
- my $wakeupInitTime = (
- $wakeupDefaultTime && lc($wakeupDefaultTime) ne "off"
- ? $wakeupDefaultTime
- : "05:00"
- );
- my $wakeupTime;
- if ($wakeupAtdevice) {
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: "
- . "Wakeuptime recalculation triggered by at-device $wakeupAtdevice";
- }
- # just give any valuable return to at-device
- # if wakeuptimer device does not exit anymore
- # and run self-destruction to clean up
- if ( !IsDevice($NAME) ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "this wake-up timer device does not exist anymore";
- my $atName = "at_" . $NAME;
- if ( IsDevice( $wakeupAtdevice, "at" ) ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "Cleaning up at-device $wakeupAtdevice (self-destruction)";
- fhem "sleep 1; delete $wakeupAtdevice";
- }
- elsif ( IsDevice( $atName, "at" ) ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: " . "Cleaning up at-device $atName";
- fhem "sleep 1; delete $atName";
- }
- else {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "Could not automatically clean up at-device, please perform manual cleanup.";
- }
- return $wakeupInitTime;
- }
- # use nextRun value if not OFF
- if ( $nextRun && lc($nextRun) ne "off" ) {
- $wakeupTime = $nextRun;
- Log3 $NAME, 4, "RESIDENTStk $NAME: " . "wakeupGetBegin source: nextRun";
- }
- # use wakeupDefaultTime if present and not OFF
- elsif ( $wakeupDefaultTime
- && lc($wakeupDefaultTime) ne "off" )
- {
- $wakeupTime = $wakeupDefaultTime;
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: " . "wakeupGetBegin source: wakeupDefaultTime";
- }
- # Use a default value to ensure auto-reset at least once a day
- else {
- $wakeupTime = $wakeupInitTime;
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: " . "wakeupGetBegin source: defaultValue";
- }
- # Recalculate new wake-up value
- my $seconds = UConv::hms2s($wakeupTime) - $wakeupOffset * 60;
- if ( $seconds < 0 ) { $seconds = 86400 + $seconds }
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: "
- . "wakeupGetBegin result: $wakeupTime = $seconds s - $wakeupOffset m = "
- . UConv::s2hms($seconds);
- return UConv::s2hms($seconds);
- }
- sub RESIDENTStk_wakeupRun($;$) {
- my ( $NAME, $forceRun ) = @_;
- unless ( IsDevice($NAME) ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "Run function RESIDENTStk_wakeupRun() for non-existing device!";
- return "$NAME: Non-existing device";
- }
- my $wakeupMacro = AttrVal( $NAME, "wakeupMacro", 0 );
- my $wakeupDefaultTime =
- ReadingsVal( $NAME, "wakeupDefaultTime",
- AttrVal( $NAME, "wakeupDefaultTime", 0 ) );
- my $wakeupAtdevice = AttrVal( $NAME, "wakeupAtdevice", 0 );
- my $wakeupUserdevice = AttrVal( $NAME, "wakeupUserdevice", 0 );
- my $wakeupDays =
- ReadingsVal( $NAME, "wakeupDays", AttrVal( $NAME, "wakeupDays", "" ) );
- my $wakeupHolidays =
- ReadingsVal( $NAME, "wakeupHolidays",
- AttrVal( $NAME, "wakeupHolidays", "" ) );
- my $wakeupResetdays =
- ReadingsVal( $NAME, "wakeupResetdays",
- AttrVal( $NAME, "wakeupResetdays", "" ) );
- my $wakeupOffset =
- ReadingsVal( $NAME, "wakeupOffset", AttrVal( $NAME, "wakeupOffset", 0 ) );
- my $wakeupEnforced =
- ReadingsVal( $NAME, "wakeupEnforced",
- AttrVal( $NAME, "wakeupEnforced", 0 ) );
- my $wakeupResetSwitcher = AttrVal( $NAME, "wakeupResetSwitcher", 0 );
- my $wakeupWaitPeriod = AttrVal( $NAME, "wakeupWaitPeriod", 360 );
- my $holidayDevice = AttrVal( "global", "holiday2we", 0 );
- my $lastRun = ReadingsVal( $NAME, "lastRun", "06:00" );
- my $nextRun = ReadingsVal( $NAME, "nextRun", "06:00" );
- my $wakeupUserdeviceState = ReadingsVal( $wakeupUserdevice, "state", 0 );
- my $wakeupUserdeviceWakeup = ReadingsVal( $wakeupUserdevice, "wakeup", 0 );
- my $room = AttrVal( $NAME, "room", 0 );
- my $running = 0;
- my $preventRun = 0;
- my $holidayToday = 0;
- if ( $wakeupHolidays ne ""
- && IsDevice( $holidayDevice, "holiday" ) )
- {
- $holidayToday = 1
- unless ( ReadingsVal( $holidayDevice, "state", "none" ) eq "none" );
- }
- else {
- $wakeupHolidays = "";
- }
- my ( $sec, $min, $hour, $mday, $mon, $year, $today, $yday, $isdst ) =
- localtime( time() + $wakeupOffset * 60 );
- $year += 1900;
- $mon++;
- $mon = "0" . $mon if ( $mon < 10 );
- $mday = "0" . $mday if ( $mday < 10 );
- $hour = "0" . $hour if ( $hour < 10 );
- $min = "0" . $min if ( $min < 10 );
- $sec = "0" . $sec if ( $sec < 10 );
- my $nowRun = $hour . ":" . $min;
- my $nowRunSec =
- time_str2num( $year . "-"
- . $mon . "-"
- . $mday . " "
- . $hour . ":"
- . $min . ":"
- . $sec );
- if ( $nextRun ne $nowRun ) {
- $lastRun = $nowRun;
- Log3 $NAME, 4, "RESIDENTStk $NAME: " . "lastRun != nextRun = $lastRun";
- }
- else {
- $lastRun = $nextRun;
- Log3 $NAME, 4, "RESIDENTStk $NAME: " . "lastRun = nextRun = $lastRun";
- }
- my @days = ($today);
- @days = split /,/, $wakeupDays
- if ( $wakeupDays ne "" );
- my %days = map { $_ => 1 } @days;
- my @rdays = ($today);
- @rdays = split /,/, $wakeupResetdays
- if ( $wakeupResetdays ne "" );
- my %rdays = map { $_ => 1 } @rdays;
- if ( IsDisabled($NAME) ) {
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: "
- . "wakeupDevice disabled - not triggering wake-up program";
- }
- elsif ( IsDisabled($wakeupUserdevice) ) {
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: "
- . "wakeupUserdevice disabled - not triggering wake-up program";
- }
- elsif ( !$wakeupUserdevice ) {
- return "$NAME: missing attribute wakeupUserdevice";
- }
- elsif ( !IsDevice($wakeupUserdevice) ) {
- return "$NAME: Non existing wakeupUserdevice $wakeupUserdevice";
- }
- elsif ( !IsDevice( $wakeupUserdevice, "RESIDENTS|ROOMMATE|GUEST" ) ) {
- return "$NAME: "
- . "wakeupUserdevice $wakeupUserdevice is not of type RESIDENTS, ROOMMATE or GUEST";
- }
- elsif ( IsDevice( $wakeupUserdevice, "GUEST" )
- && $wakeupUserdeviceState eq "none" )
- {
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: "
- . "GUEST device $wakeupUserdevice has status value 'none' so let's disable this wake-up timer";
- fhem "set $NAME nextRun OFF";
- return;
- }
- elsif ( IsDisabled($wakeupUserdevice) ) {
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: "
- . "wakeupUserdevice disabled - not triggering wake-up program";
- }
- elsif ( lc($nextRun) eq "off" && !$forceRun ) {
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: "
- . "wakeup timer set to OFF - not triggering wake-up program";
- }
- elsif (
- !$forceRun
- && !$days{$today}
- && ( $wakeupHolidays eq ""
- || $wakeupHolidays eq "andHoliday"
- || $wakeupHolidays eq "andNoHoliday" )
- )
- {
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: "
- . "weekday restriction in use - not triggering wake-up program this time";
- }
- elsif (
- !$forceRun
- # && !$days{$today}
- # && (
- # ( $wakeupHolidays eq "andHoliday" && !$holidayToday )
- # || ( $wakeupHolidays eq "andNoHoliday"
- # && $holidayToday )
- # || ( $wakeupHolidays eq "orHoliday" && !$holidayToday )
- # || ( $wakeupHolidays eq "orNoHoliday"
- # && $holidayToday )
- # )
- && (
- (
- $days{$today}
- && ( ( $wakeupHolidays eq "andHoliday" && !$holidayToday )
- || ( $wakeupHolidays eq "andNoHoliday" && $holidayToday ) )
- )
- || (
- !$days{$today}
- && ( ( $wakeupHolidays eq "orHoliday" && !$holidayToday )
- || ( $wakeupHolidays eq "orNoHoliday" && $holidayToday ) )
- )
- )
- )
- {
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: "
- . "holiday restriction $wakeupHolidays in use - not triggering wake-up program this time";
- }
- elsif ($wakeupUserdeviceState eq "absent"
- || $wakeupUserdeviceState eq "gone"
- || $wakeupUserdeviceState eq "gotosleep"
- || $wakeupUserdeviceState eq "awoken" )
- {
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: "
- . "we should not start any wake-up program for resident device $wakeupUserdevice being in state '"
- . $wakeupUserdeviceState
- . "' - not triggering wake-up program this time";
- }
- # general conditions to trigger program fulfilled
- else {
- my $expLastWakeup = time_str2num(
- ReadingsTimestamp(
- $wakeupUserdevice, "lastWakeup", "1970-01-01 00:00:00"
- )
- ) - 1 +
- $wakeupOffset * 60 +
- $wakeupWaitPeriod * 60;
- my $expLastAwake = time_str2num(
- ReadingsTimestamp(
- $wakeupUserdevice, "lastAwake", "1970-01-01 00:00:00"
- )
- ) - 1 +
- $wakeupWaitPeriod * 60;
- if ( !$wakeupMacro ) {
- return "$NAME: missing attribute wakeupMacro";
- }
- elsif ( !IsDevice($wakeupMacro) ) {
- return "$NAME: "
- . "notify macro $wakeupMacro not found - no wakeup actions defined!";
- }
- elsif ( !IsDevice( $wakeupMacro, "notify" ) ) {
- return "$NAME: device $wakeupMacro is not of type notify";
- }
- elsif ($wakeupUserdeviceWakeup) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "Another wake-up program is already being executed for device $wakeupUserdevice, won't trigger $wakeupMacro";
- }
- elsif ( $expLastWakeup > $nowRunSec && !$forceRun ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "won't trigger wake-up program due to non-expired wakeupWaitPeriod threshold since lastWakeup (expLastWakeup=$expLastWakeup > nowRunSec=$nowRunSec)";
- }
- elsif ( $expLastAwake > $nowRunSec && !$forceRun ) {
- Log3 $NAME, 3,
- "RESIDENTStk $NAME: "
- . "won't trigger wake-up program due to non-expired wakeupWaitPeriod threshold since lastAwake (expLastAwake=$expLastAwake > nowRunSec=$nowRunSec)";
- }
- else {
- # conditional enforced wake-up:
- # only if actual wake-up time is
- # earlier than wakeupDefaultTime
- if ( $wakeupEnforced == 3
- && $wakeupDefaultTime
- && UConv::hms2s($wakeupDefaultTime) > UConv::hms2s($lastRun) )
- {
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: "
- . "Enforcing wake-up because wake-up time is earlier than normal (wakeupDefaultTime=$wakeupDefaultTime > lastRun=$lastRun)";
- $wakeupEnforced = 1;
- }
- # conditional enforced wake-up:
- # only if actual wake-up time is not wakeupDefaultTime
- elsif ($wakeupEnforced == 2
- && $wakeupDefaultTime
- && $wakeupDefaultTime ne $lastRun )
- {
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: "
- . "Enforcing wake-up because wake-up is different from normal (wakeupDefaultTime=$wakeupDefaultTime =! lastRun=$lastRun)";
- $wakeupEnforced = 1;
- }
- elsif ( $wakeupEnforced > 1 ) {
- $wakeupEnforced = 0;
- }
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: " . "trigger $wakeupMacro (running=1)";
- fhem "trigger $wakeupMacro "
- . "start $lastRun $wakeupOffset $wakeupEnforced $wakeupUserdevice $wakeupUserdeviceState";
- # Update user device with last wakeup details
- #
- readingsBeginUpdate( $defs{$wakeupUserdevice} );
- readingsBulkUpdate( $defs{$wakeupUserdevice},
- "lastWakeup", $lastRun );
- readingsBulkUpdate( $defs{$wakeupUserdevice},
- "lastWakeupDev", $NAME );
- readingsBulkUpdate( $defs{$wakeupUserdevice}, "wakeup", "1" );
- readingsEndUpdate( $defs{$wakeupUserdevice}, 1 );
- readingsSingleUpdate( $defs{$wakeupUserdevice}, "wakeup", "0", 1 )
- unless ($wakeupOffset);
- readingsSingleUpdate( $defs{$NAME}, "lastRun", $lastRun, 1 );
- if ($wakeupOffset) {
- my $wakeupStopAtdevice = $wakeupAtdevice . "_stop";
- fhem "delete $wakeupStopAtdevice"
- if ( IsDevice($wakeupStopAtdevice) );
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: "
- . "created at-device $wakeupStopAtdevice to stop wake-up program in $wakeupOffset minutes";
- fhem "define $wakeupStopAtdevice at +"
- . UConv::s2hms( $wakeupOffset * 60 + 1 )
- . " set $NAME:FILTER=running=1 stop triggerpost";
- fhem "attr $wakeupStopAtdevice "
- . "comment Auto-created by RESIDENTS Toolkit: temp. at-device to stop wake-up program of timer $NAME when wake-up time is reached";
- $running = 1;
- }
- }
- }
- if ( $running && $wakeupOffset ) {
- readingsBeginUpdate( $defs{$NAME} );
- readingsBulkUpdate( $defs{$NAME}, "running", "1" );
- readingsBulkUpdate( $defs{$NAME}, "state", "running" );
- readingsEndUpdate( $defs{$NAME}, 1 );
- }
- # Update user device with next wakeup details
- #
- readingsBeginUpdate( $defs{$wakeupUserdevice} );
- my ( $nextWakeupDev, $nextWakeup ) =
- RESIDENTStk_wakeupGetNext( $wakeupUserdevice, $NAME );
- if ( !$nextWakeupDev || !$nextWakeup ) {
- $nextWakeupDev = "";
- $nextWakeup = "OFF";
- }
- readingsBulkUpdateIfChanged( $defs{$wakeupUserdevice},
- "nextWakeupDev", $nextWakeupDev );
- readingsBulkUpdateIfChanged( $defs{$wakeupUserdevice},
- "nextWakeup", $nextWakeup );
- readingsEndUpdate( $defs{$wakeupUserdevice}, 1 );
- my $doReset = 1;
- if ( IsDevice( $wakeupResetSwitcher, "dummy" )
- && ReadingsVal( $wakeupResetSwitcher, "state", 0 ) eq "off" )
- {
- $doReset = 0;
- }
- if ( $wakeupDefaultTime && $rdays{$today} && $doReset ) {
- Log3 $NAME, 4,
- "RESIDENTStk $NAME: " . "Resetting based on wakeupDefaultTime";
- fhem "set $NAME:FILTER=state!=$wakeupDefaultTime "
- . "nextRun $wakeupDefaultTime";
- }
- elsif ( !$running ) {
- readingsBeginUpdate( $defs{$NAME} );
- readingsBulkUpdateIfChanged( $defs{$NAME}, "state", $nextRun );
- readingsEndUpdate( $defs{$NAME}, 1 );
- }
- return undef;
- }
- sub RESIDENTStk_wakeupGetNext($;$) {
- my ( $name, $wakeupDeviceRunning ) = @_;
- my $wakeupDeviceAttrName = "";
- $wakeupDeviceAttrName = "rgr_wakeupDevice"
- if ( defined( $attr{$name}{"rgr_wakeupDevice"} ) );
- $wakeupDeviceAttrName = "rr_wakeupDevice"
- if ( defined( $attr{$name}{"rr_wakeupDevice"} ) );
- $wakeupDeviceAttrName = "rg_wakeupDevice"
- if ( defined( $attr{$name}{"rg_wakeupDevice"} ) );
- my $wakeupDeviceList = AttrVal( $name, $wakeupDeviceAttrName, 0 );
- my ( $sec, $min, $hour, $mday, $mon, $year, $today, $yday, $isdst ) =
- localtime(time);
- $hour = "0" . $hour if ( $hour < 10 );
- $min = "0" . $min if ( $min < 10 );
- my $tomorrow = $today + 1;
- $tomorrow = 0 if ( $tomorrow == 7 );
- my $secNow = UConv::hms2s( $hour . ":" . $min ) + $sec;
- my $definitiveNextToday;
- my $definitiveNextTomorrow;
- my $definitiveNextTodayDev;
- my $definitiveNextTomorrowDev;
- my $holidayDevice = AttrVal( "global", "holiday2we", 0 );
- # check for each registered wake-up device
- for my $wakeupDevice ( split /,/, $wakeupDeviceList ) {
- if ( !IsDevice($wakeupDevice) ) {
- Log3 $name, 4,
- "RESIDENTStk $name: "
- . "00 - ignoring reference to non-existing wakeupDevice $wakeupDevice";
- my $wakeupDeviceListNew = $wakeupDeviceList;
- $wakeupDeviceListNew =~ s/,$wakeupDevice,/,/g;
- $wakeupDeviceListNew =~ s/$wakeupDevice,//g;
- $wakeupDeviceListNew =~ s/,$wakeupDevice//g;
- if ( $wakeupDeviceListNew ne $wakeupDeviceList ) {
- Log3 $name, 3,
- "RESIDENTStk $name: "
- . "reference to non-existing wakeupDevice '$wakeupDevice' was removed";
- fhem "attr $name $wakeupDeviceAttrName $wakeupDeviceListNew";
- }
- next;
- }
- my $wakeupAtdevice = AttrVal( $wakeupDevice, "wakeupAtdevice", undef );
- my $wakeupOffset =
- ReadingsVal( $wakeupDevice, "wakeupOffset",
- AttrVal( $wakeupDevice, "wakeupOffset", 0 ) );
- my $wakeupAtNTM = (
- IsDevice($wakeupAtdevice)
- && defined( $defs{$wakeupAtdevice}{NTM} )
- ? substr( $defs{$wakeupAtdevice}{NTM}, 0, -3 )
- : undef
- );
- my $wakeupDays =
- ReadingsVal( $wakeupDevice, "wakeupDays",
- AttrVal( $wakeupDevice, "wakeupDays", "" ) );
- my $wakeupHolidays =
- ReadingsVal( $wakeupDevice, "wakeupHolidays",
- AttrVal( $wakeupDevice, "wakeupHolidays", "" ) );
- my $holidayToday = 0;
- my $holidayTomorrow = 0;
- my $nextRunSrc;
- my $nextRun = ReadingsVal( $wakeupDevice, "nextRun", undef );
- my $ltoday = $today;
- my $ltomorrow = $tomorrow;
- if ( IsDisabled($wakeupDevice)
- || !$nextRun
- || lc($nextRun) eq "off"
- || $nextRun !~ /^([0-9]{2}:[0-9]{2})$/ )
- {
- Log3 $name, 4,
- "RESIDENTStk $name: "
- . "00 - ignoring disabled wakeupDevice $wakeupDevice";
- next;
- }
- Log3 $name, 4,
- "RESIDENTStk $name: "
- . "00 - checking for next wake-up candidate $wakeupDevice";
- # get holiday status for today and tomorrow
- if ( $wakeupHolidays eq "" ) {
- Log3 $name, 4,
- "RESIDENTStk $wakeupDevice: 01 - Not considering any holidays";
- }
- elsif ( IsDevice( $holidayDevice, "holiday" ) ) {
- $holidayToday = 1
- unless (
- ReadingsVal( $holidayDevice, "state", "none" ) eq "none" );
- $holidayTomorrow = 1
- unless (
- ReadingsVal( $holidayDevice, "tomorrow", "none" ) eq "none" );
- Log3 $name, 4,
- "RESIDENTStk $wakeupDevice: "
- . "01 - Holidays to be considered ($wakeupHolidays) - holidayToday=$holidayToday holidayTomorrow=$holidayTomorrow";
- }
- # set day scope for today
- my @days = ($ltoday);
- @days = split /,/, $wakeupDays
- if ( $wakeupDays ne "" );
- my %days = map { $_ => 1 } @days;
- # set day scope for tomorrow
- my @daysTomorrow = ($ltomorrow);
- @daysTomorrow = split /,/, $wakeupDays
- if ( $wakeupDays ne "" );
- my %daysTomorrow = map { $_ => 1 } @daysTomorrow;
- Log3 $name, 4,
- "RESIDENTStk $wakeupDevice: "
- . "02 - possible candidate found - weekdayToday=$ltoday weekdayTomorrow=$ltomorrow";
- my $nextRunSec;
- my $nextRunSecTarget;
- # Use direct information from at-device if possible
- if ( $wakeupAtNTM
- && $wakeupAtNTM =~ /^([0-9]{2}:[0-9]{2})$/ )
- {
- $nextRunSrc = "at";
- $nextRunSec = UConv::hms2s($wakeupAtNTM);
- $nextRunSecTarget = $nextRunSec + $wakeupOffset * 60;
- if ( $wakeupOffset && $nextRunSecTarget >= 86400 ) {
- $nextRunSecTarget -= 86400;
- $ltoday++;
- $ltoday = $ltoday - 7
- if ( $ltoday > 6 );
- $ltomorrow++;
- $ltomorrow = $ltomorrow - 7
- if ( $ltomorrow > 6 );
- }
- Log3 $name, 4,
- "RESIDENTStk $wakeupDevice: "
- . "03 - considering at-device value wakeupAtNTM=$wakeupAtNTM wakeupOffset=$wakeupOffset nextRunSec=$nextRunSec nextRunSecTarget=$nextRunSecTarget";
- }
- else {
- $nextRunSrc = "dummy";
- $nextRunSecTarget = UConv::hms2s($nextRun);
- $nextRunSec = $nextRunSecTarget - $wakeupOffset * 60;
- if ( $wakeupOffset && $nextRunSec < 0 ) {
- $nextRunSec += 86400;
- $ltoday--;
- $ltoday = $ltoday + 7
- if ( $ltoday < 0 );
- $ltomorrow--;
- $ltomorrow = $ltomorrow + 7
- if ( $ltomorrow < 0 );
- }
- Log3 $name, 4,
- "RESIDENTStk $wakeupDevice: "
- . "03 - considering dummy-device value nextRun=$nextRun wakeupOffset=$wakeupOffset nextRunSec=$nextRunSec nextRunSecTarget=$nextRunSecTarget (wakeupAtNTM=$wakeupAtNTM)";
- }
- # still running today
- if ( $nextRunSec > $secNow ) {
- Log3 $name, 4,
- "RESIDENTStk $wakeupDevice: "
- . "04 - this is a candidate for today - weekdayToday=$ltoday";
- # if today is in scope
- if ( $days{$ltoday} ) {
- # if we need to consider holidays in addition
- if (
- ( $wakeupHolidays eq "andHoliday" && !$holidayToday )
- || ( $wakeupHolidays eq "andNoHoliday"
- && $holidayToday )
- )
- {
- Log3 $name, 4,
- "RESIDENTStk $wakeupDevice: "
- . "05 - no run today due to holiday based on combined weekday and holiday decision";
- next;
- }
- # easy if there is no holiday dependency
- elsif ( !$definitiveNextToday
- || $nextRunSec < $definitiveNextToday )
- {
- Log3 $name, 4,
- "RESIDENTStk $wakeupDevice: "
- . "05 - until now, will be NEXT WAKE-UP RUN today based on weekday decision";
- $definitiveNextToday = $nextRunSec;
- $definitiveNextTodayDev = $wakeupDevice;
- }
- }
- elsif ($wakeupHolidays eq ""
- || $wakeupHolidays eq "andHoliday"
- || $wakeupHolidays eq "andNoHoliday" )
- {
- Log3 $name, 4,
- "RESIDENTStk $wakeupDevice: "
- . "05 - won't be running today anymore based on weekday decision";
- next;
- }
- # if we need to consider holidays in parallel to weekdays
- elsif (( $wakeupHolidays eq "orHoliday" && !$holidayToday )
- || ( $wakeupHolidays eq "orNoHoliday" && $holidayToday ) )
- {
- Log3 $name, 4,
- "RESIDENTStk $wakeupDevice: "
- . "06 - won't be running today based on holiday decision";
- next;
- }
- # easy if there is no holiday dependency
- elsif ( !$definitiveNextToday
- || $nextRunSec < $definitiveNextToday )
- {
- Log3 $name, 4,
- "RESIDENTStk $wakeupDevice: "
- . "06 - until now, will be NEXT WAKE-UP RUN today based on holiday decision";
- $definitiveNextToday = $nextRunSec;
- $definitiveNextTodayDev = $wakeupDevice;
- }
- }
- # running later
- else {
- Log3 $name, 4,
- "RESIDENTStk $wakeupDevice: "
- . "04 - this is a candidate for tomorrow or later - weekdayTomorrow=$ltomorrow";
- # if tomorrow is in scope
- if ( $daysTomorrow{$ltomorrow} ) {
- # if we need to consider holidays in addition
- if (
- ( $wakeupHolidays eq "andHoliday" && !$holidayTomorrow )
- || ( $wakeupHolidays eq "andNoHoliday"
- && $holidayTomorrow )
- )
- {
- Log3 $name, 4,
- "RESIDENTStk $wakeupDevice: "
- . "05 - no run tomorrow due to holiday based on combined weekday and holiday decision";
- next;
- }
- # easy if there is no holiday dependency
- elsif ( !$definitiveNextTomorrow
- || $nextRunSec < $definitiveNextTomorrow )
- {
- Log3 $name, 4,
- "RESIDENTStk $wakeupDevice: "
- . "05 - until now, will be NEXT WAKE-UP RUN tomorrow based on weekday decision";
- $definitiveNextTomorrow = $nextRunSec;
- $definitiveNextTomorrowDev = $wakeupDevice;
- }
- }
- elsif ($wakeupHolidays eq ""
- || $wakeupHolidays eq "andHoliday"
- || $wakeupHolidays eq "andNoHoliday" )
- {
- Log3 $name, 4,
- "RESIDENTStk $wakeupDevice: "
- . "05 - won't be running tomorrow based on weekday decision";
- next;
- }
- # if we need to consider holidays in parallel to weekdays
- elsif (
- ( $wakeupHolidays eq "orHoliday" && !$holidayTomorrow )
- || ( $wakeupHolidays eq "orNoHoliday"
- && $holidayTomorrow )
- )
- {
- Log3 $name, 4,
- "RESIDENTStk $wakeupDevice: "
- . "06 - won't be running tomorrow based on holiday decision";
- next;
- }
- # easy if there is no holiday dependency
- elsif ( !$definitiveNextTomorrow
- || $nextRunSec < $definitiveNextTomorrow )
- {
- Log3 $name, 4,
- "RESIDENTStk $wakeupDevice: "
- . "06 - until now, will be NEXT WAKE-UP RUN tomorrow based on holiday decision";
- $definitiveNextTomorrow = $nextRunSec;
- $definitiveNextTomorrowDev = $wakeupDevice;
- }
- }
- if ($wakeupOffset) {
- # add Offset
- $definitiveNextToday += $wakeupOffset * 60
- if ( defined($definitiveNextToday) );
- $definitiveNextTomorrow += $wakeupOffset * 60
- if ( defined($definitiveNextTomorrow) );
- # correct change over midnight
- if ( defined($definitiveNextToday) ) {
- if ( $definitiveNextToday >= 86400 ) {
- $definitiveNextToday -= 86400;
- }
- elsif ( $definitiveNextToday < 0 ) {
- $definitiveNextToday += 86400;
- }
- }
- if ( defined($definitiveNextTomorrow) ) {
- if ( $definitiveNextTomorrow >= 86400 ) {
- $definitiveNextTomorrow -= 86400;
- }
- elsif ( $definitiveNextTomorrow < 0 ) {
- $definitiveNextTomorrow += 86400;
- }
- }
- }
- }
- if ( defined($definitiveNextTodayDev)
- && defined($definitiveNextToday) )
- {
- Log3 $name, 4,
- "RESIDENTStk $name: 07 - next wake-up result: today at "
- . UConv::s2hms($definitiveNextToday)
- . ", wakeupDevice="
- . $definitiveNextTodayDev;
- return ( $definitiveNextTodayDev,
- substr( UConv::s2hms($definitiveNextToday), 0, -3 ) );
- }
- elsif (defined($definitiveNextTomorrowDev)
- && defined($definitiveNextTomorrow) )
- {
- Log3 $name, 4,
- "RESIDENTStk $name: 07 - next wake-up result: tomorrow at "
- . UConv::s2hms($definitiveNextTomorrow)
- . ", wakeupDevice="
- . $definitiveNextTomorrowDev;
- return ( $definitiveNextTomorrowDev,
- substr( UConv::s2hms($definitiveNextTomorrow), 0, -3 ) );
- }
- return ( undef, undef );
- }
- sub RESIDENTStk_TimeSum($$) {
- my ( $val1, $val2 ) = @_;
- my ( $timestamp1, $timestamp2, $math );
- if ( $val1 !~ /^([0-9]{2}):([0-9]{2})$/ ) {
- return $val1;
- }
- else {
- $timestamp1 = UConv::hms2s($val1);
- }
- if ( $val2 =~ /^([\+\-])([0-9]{2}):([0-9]{2})$/ ) {
- $math = $1;
- $timestamp2 = UConv::hms2s("$2:$3");
- }
- elsif ( $val2 =~ /^([\+\-])([0-9]*)$/ ) {
- $math = $1;
- $timestamp2 = $2 * 60;
- }
- else {
- return $val1;
- }
- if ( $math eq "-" ) {
- return substr( UConv::s2hms( $timestamp1 - $timestamp2 ), 0, -3 );
- }
- else {
- return substr( UConv::s2hms( $timestamp1 + $timestamp2 ), 0, -3 );
- }
- }
- sub RESIDENTStk_InternalTimer($$$$$) {
- my ( $modifier, $tim, $callback, $hash, $waitIfInitNotDone ) = @_;
- my $mHash;
- if ( $modifier eq "" ) {
- $mHash = $hash;
- }
- else {
- my $timerName = $hash->{NAME} . "_" . $modifier;
- if ( exists( $hash->{TIMER}{$timerName} ) ) {
- $mHash = $hash->{TIMER}{$timerName};
- }
- else {
- $mHash = {
- HASH => $hash,
- NAME => $hash->{NAME} . "_" . $modifier,
- MODIFIER => $modifier
- };
- $hash->{TIMER}{$timerName} = $mHash;
- }
- }
- InternalTimer( $tim, $callback, $mHash, $waitIfInitNotDone );
- }
- sub RESIDENTStk_RemoveInternalTimer($$) {
- my ( $modifier, $hash ) = @_;
- my $timerName = $hash->{NAME} . "_" . $modifier;
- if ( $modifier eq "" ) {
- RemoveInternalTimer($hash);
- }
- else {
- my $mHash = $hash->{TIMER}{$timerName};
- if ( defined($mHash) ) {
- delete $hash->{TIMER}{$timerName};
- RemoveInternalTimer($mHash);
- }
- }
- }
- sub RESIDENTStk_StartInternalTimers($$) {
- my ($hash) = @_;
- RESIDENTStk_AutoGone($hash);
- RESIDENTStk_DurationTimer($hash);
- }
- sub RESIDENTStk_StopInternalTimers($) {
- my ($hash) = @_;
- delete $hash->{AUTOGONE} if ( $hash->{AUTOGONE} );
- delete $hash->{DURATIONTIMER} if ( $hash->{DURATIONTIMER} );
- RESIDENTStk_RemoveInternalTimer( "AutoGone", $hash );
- RESIDENTStk_RemoveInternalTimer( "DurationTimer", $hash );
- }
- sub RESIDENTStk_findResidentSlaves($;$) {
- my ( $hash, $ret ) = @_;
- my @slaves;
- my @ROOMMATES;
- foreach ( devspec2array("TYPE=ROOMMATE") ) {
- next
- unless (
- defined( $defs{$_}{RESIDENTGROUPS} )
- && grep { $hash->{NAME} eq $_ }
- split( /,/, $defs{$_}{RESIDENTGROUPS} )
- );
- push @ROOMMATES, $_;
- }
- my @GUESTS;
- foreach ( devspec2array("TYPE=GUEST") ) {
- next
- unless (
- defined( $defs{$_}{RESIDENTGROUPS} )
- && grep { $hash->{NAME} eq $_ }
- split( /,/, $defs{$_}{RESIDENTGROUPS} )
- );
- push @GUESTS, $_;
- }
- if ( scalar @ROOMMATES ) {
- $hash->{ROOMMATES} = join( ",", @ROOMMATES );
- }
- elsif ( $hash->{ROOMMATES} ) {
- delete $hash->{ROOMMATES};
- }
- if ( scalar @GUESTS ) {
- $hash->{GUESTS} = join( ",", @GUESTS );
- }
- elsif ( $hash->{GUESTS} ) {
- delete $hash->{GUESTS};
- }
- if ( $hash->{ROOMMATES} ) {
- $ret .= "," if ($ret);
- $ret .= $hash->{ROOMMATES};
- }
- if ( $hash->{GUESTS} ) {
- $ret .= "," if ($ret);
- $ret .= $hash->{GUESTS};
- }
- return RESIDENTStk_findDummySlaves( $hash, $ret );
- }
- sub RESIDENTStk_findDummySlaves($;$);
- sub RESIDENTStk_findDummySlaves($;$) {
- my ( $hash, $ret ) = @_;
- my $prefix = RESIDENTStk_GetPrefixFromType( $hash->{NAME} );
- my $wakeupDevice =
- AttrVal( $hash->{NAME}, $prefix . "wakeupDevice", undef );
- my $presenceDevices =
- AttrVal( $hash->{NAME}, $prefix . "presenceDevices", undef );
- $ret = "" unless ($ret);
- # add r_*wakeupDevice
- if ($wakeupDevice) {
- $ret .= "," if ( $ret ne "" );
- $ret .= $wakeupDevice;
- # wakeupResetSwitcher
- foreach ( split( ',', $wakeupDevice ) ) {
- my $rsw = AttrVal( $_, "wakeupResetSwitcher", "" );
- if ( $rsw =~ /^[a-zA-Z\d._]+$/ ) {
- $ret .= "," if ( $ret ne "" );
- $ret .= $rsw;
- }
- }
- }
- # add r_*presenceDevices
- if ($presenceDevices) {
- foreach ($presenceDevices) {
- my $d = $_;
- $d =~ s/:.*$//g;
- $ret .= "," if ( $ret ne "" );
- $ret .= $d;
- }
- }
- return $ret;
- }
- sub RESIDENTStk_GetPrefixFromType($) {
- my ($name) = @_;
- return $modules{ $defs{$name}{TYPE} }{AttrPrefix}
- if ( $defs{$name}
- && $defs{$name}{TYPE}
- && $modules{ $defs{$name}{TYPE} }
- && $modules{ $defs{$name}{TYPE} }{AttrPrefix} );
- return "";
- }
- sub RESIDENTStk_DoModuleTrigger($$@) {
- my ( $hash, $newState, $noreplace, $TYPE ) = @_;
- $hash = $defs{$hash} unless ( ref($hash) );
- $TYPE = $hash->{TYPE} unless ( defined($TYPE) );
- return ""
- unless ( defined($TYPE)
- && defined( $modules{$TYPE} )
- && defined($newState)
- && $newState =~
- m/^([A-Za-z\d._]+)(?:\s+([A-Za-z\d._]+)(?:\s+(.*))?)?$/ );
- $noreplace = 1 unless ( defined($noreplace) );
- my $e = $1;
- my $d = $2;
- return "DoModuleTrigger() can only handle module related events"
- if ( ( $hash->{NAME} && $hash->{NAME} eq "global" )
- || $d eq "global" );
- return DoTrigger( "global", "$TYPE:$newState", $noreplace )
- unless ( $e =~ /^INITIALIZED|INITIALIZING|MODIFIED|DELETED$/ );
- return "$e: missing device name"
- if ( !defined($d) || $d eq "" );
- my $dret;
- my @rets;
- $hash = $defs{$d};
- if ( !$hash || !ref($hash) ) {
- $dret = "device was deleted"
- unless ( $e eq "DELETED" );
- $e = "DELETED";
- }
- elsif ($e eq "INITIALIZING"
- || ( $hash->{READY} && $hash->{MOD_INIT} )
- || ( $modules{$TYPE}{READY} && $hash->{MOD_INIT} ) )
- {
- $dret = "device initialization started";
- Log 4, "$TYPE $d: $dret";
- $hash->{MOD_INIT} = 1;
- $e = "INITIALIZING";
- }
- elsif ( $hash->{MOD_INIT} ) {
- $dret = "pending device initialization";
- Log 4, "$TYPE $d: $dret";
- }
- elsif ( !$hash->{READY} ) {
- $e = "INITIALIZED";
- $hash->{READY} = 1;
- Log 4, "$TYPE $d: device initialization completed";
- }
- else {
- $e = "MODIFIED";
- }
- my $ret = DoTrigger( "global", "$TYPE:$e $d", $noreplace )
- if ( $e ne "DELETED"
- && ( !$hash->{MOD_INIT} || $e eq "INITIALIZING" ) );
- push @rets, $ret if ( defined($ret) && $ret ne "" );
- ####
- ## track module readiness
- my $initcnt = scalar devspec2array("TYPE=$TYPE:FILTER=MOD_INIT=.+");
- # no more devices of that module
- if ( scalar devspec2array("TYPE=$TYPE") == 0 ) {
- delete $modules{$TYPE}{READY};
- delete $modules{$TYPE}{INIT};
- Log 4, "$TYPE: module is no more in use";
- my $ret = DoTrigger( "global", "$TYPE:DELETED", $noreplace );
- push @rets, $ret if ( defined($ret) && $ret ne "" );
- }
- # all devices completed initialization after bootup
- elsif ( !$modules{$TYPE}{READY} && !$initcnt ) {
- $modules{$TYPE}{READY} = 1;
- delete $modules{$TYPE}{INIT};
- Log 4, "$TYPE: module initialization completed";
- my $ret = DoTrigger( "global", "$TYPE:INITIALIZED", $noreplace );
- push @rets, $ret if ( defined($ret) && $ret ne "" );
- }
- # pending devices during bootup
- elsif ( !$modules{$TYPE}{READY} && !$modules{$TYPE}{INIT} && $initcnt ) {
- $modules{$TYPE}{INIT} = 1;
- }
- # pending devices during runtime
- elsif ( !$modules{$TYPE}{INIT} && $initcnt ) {
- $modules{$TYPE}{INIT} = 1;
- Log 4, "$TYPE: module re-initialization in progress";
- my $ret = DoTrigger( "global", "$TYPE:INITIALIZING", $noreplace );
- push @rets, $ret if ( defined($ret) && $ret ne "" );
- }
- # device constellation changed during runtime
- # with pending devices
- elsif ($modules{$TYPE}{READY}
- && $modules{$TYPE}{INIT}
- && ( !$initcnt || $e eq "DELETED" ) )
- {
- delete $modules{$TYPE}{INIT};
- Log 4, "$TYPE: module re-initialization completed";
- my $ret = DoTrigger( "global", "$TYPE:INITIALIZED", $noreplace );
- push @rets, $ret if ( defined($ret) && $ret ne "" );
- }
- # device constellation changed during runtime
- elsif ( $modules{$TYPE}{READY} && ( !$initcnt || $e eq "DELETED" ) ) {
- Log 4, "$TYPE: module constellation updated";
- my $ret = DoTrigger( "global", "$TYPE:MODIFIED", $noreplace );
- push @rets, $ret if ( defined($ret) && $ret ne "" );
- }
- unshift @rets, " $d: $dret" if ($dret);
- return join( "\n", @rets );
- }
- sub RESIDENTStk_DoInitDev(@) {
- my (@devices) = @_;
- my @rets;
- foreach my $d (@devices) {
- $d = $d->{NAME} if ( ref($d) eq "HASH" );
- next unless ( $defs{$d} );
- $defs{$d}{MOD_INIT} = 1;
- my $ret = CallFn( $d, "InitDevFn", $defs{$d} );
- if ( defined($ret) && $ret eq "0" ) {
- delete $defs{$d}{MOD_INIT};
- next;
- }
- if ( defined($ret) && $ret ne "" ) {
- $defs{$d}{MOD_INIT} = 2;
- $modules{ $defs{$d}{TYPE} }{INIT} = 2;
- push @rets, $ret;
- }
- else {
- delete $defs{$d}{MOD_INIT};
- }
- $ret = RESIDENTStk_DoModuleTrigger( $defs{$d}, "INITIALIZED $d" );
- push @rets, $ret if ( defined($ret) && $ret ne "" );
- }
- return join( "\n", @rets );
- }
- 1;
|