######################################################################################################################## # $Id: 60_Watches.pm 22591 2020-08-12 21:13:44Z DS_Starter $ ######################################################################################################################### # 60_Watches.pm # # (c) 2018-2021 by Heiko Maaz # e-mail: Heiko dot Maaz at t-online dot de # # This script is part of fhem. # # Fhem is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # Fhem is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with fhem. If not, see . # # The script is based on sources from following sites: # # modern clock: https://www.w3schools.com/graphics/canvas_clock_start.asp # # station clock: http://www.3quarks.com/de/Bahnhofsuhr/ # # digital clock: http://www.3quarks.com/de/Segmentanzeige/index.html # ######################################################################################################################### package FHEM::Watches; ## no critic 'package' use strict; use warnings; use Time::HiRes qw(time gettimeofday tv_interval); use GPUtils qw(GP_Import GP_Export); # wird für den Import der FHEM Funktionen aus der fhem.pl benötigt eval "use FHEM::Meta;1" or my $modMetaAbsent = 1; ## no critic 'eval' # Run before module compilation BEGIN { # Import from main:: GP_Import( qw( AttrVal defs FW_makeImage FW_ME FW_subdir IsDisabled Log3 modules ReadingsVal readingsDelete readingsBeginUpdate readingsBulkUpdate readingsEndUpdate readingFnAttributes readingsSingleUpdate sortTopicNum ) ); # Export to main context with different name # my $pkg = caller(0); # my $main = $pkg; # $main =~ s/^(?:.+::)?([^:]+)$/main::$1\_/gx; # foreach (@_) { # *{ $main . $_ } = *{ $pkg . '::' . $_ }; # } GP_Export( qw( Initialize ) ); } # Versions History intern my %vNotesIntern = ( "0.27.1" => "09.01.2021 remove usage of sscam_tooltip.js in sub controlPanel ,Forum: https:/topic,93454.msg1119567.html#msg1119567", "0.27.0" => "12.08.2020 control buttons, new attr hideButtons, controlButtonSize, some more changes according PBP". "fix Random triggering of alarm or random time display at start / resume ", "0.26.0" => "01.08.2020 add attr timeAsReading -> write into reading 'currtime' if time is displayed, ". "add \$readingFnAttributes, set release_status to stable ", "0.25.0" => "03.06.2020 set reading 'stoptime' in type 'stopwatch' ", "0.24.0" => "26.05.2020 entry of countDownInit can be in format ", "0.23.2" => "20.05.2020 english commandref ", "0.23.1" => "10.05.2020 some more changes for PBP severity 3 ", "0.23.0" => "10.05.2020 attr 'digitalBorderDistance' now also valid for digital watches, some changes for PBP ", "0.22.0" => "09.05.2020 new attr 'digitalBorderDistance' for left and rigtht border distance of digital text ", "0.21.1" => "09.05.2020 fix calculate forerun of 'text' dynamically if digitalTextDigitNumber=0 ", "0.21.0" => "08.05.2020 support of alarm time of model digital 'watch' ", "0.20.1" => "08.05.2020 asynchron read digital text and much more fixes, set client as default timeSource ", "0.20.0" => "07.05.2020 asynchron read alarmTime reading, some fixes ", "0.19.0" => "06.05.2020 alarm event creation for watch models 'Station' and 'Station' ", "0.18.0" => "06.05.2020 attr 'digitalTextTicker' deleted and switched to setter 'textTicker', default text switched to blank ", "0.17.0" => "05.05.2020 new attr 'digitalTextTicker', 'digitalTextDigitNumber' ", "0.16.0" => "04.05.2020 delete attr 'digitalDisplayText', new setter 'displayText', 'displayTextDel' ", "0.15.1" => "04.05.2020 fix permanently events when no alarmTime is set in countdownwatch and countdown is finished ", "0.15.0" => "04.05.2020 new attribute 'digitalSegmentType' for different segement count, also new attributes ". "'digitalDigitAngle', 'digitalDigitDistance', 'digitalDigitHeight', 'digitalDigitWidth', 'digitalSegmentDistance' ". "'digitalSegmentWidth', stopwatches don't stop when alarm is triggered (use notify to do it) ", "0.14.0" => "03.05.2020 switch to packages, use setVersionInfo, support of Meta.pm ", "0.13.0" => "03.05.2020 set resume for countdownwatch, set 'continue' removed ", "0.12.0" => "03.05.2020 set resume for stopwatch, new 'alarmDel' command for stop watches, alarmHMS renamed to 'alarmHMSdelset' ", "0.11.0" => "02.05.2020 alarm event stabilized, reset command for 'countdownwatch', event alarmed contains alarm time ", "0.10.0" => "02.05.2020 renamed 'countDownDone' to 'alarmed', bug fix ", "0.9.0" => "02.05.2020 new attribute 'timeSource' for selection of client/server time ", "0.8.0" => "01.05.2020 new values 'countdownwatch' for attribute digitalDisplayPattern, switch all watches to server time ", "0.7.0" => "30.04.2020 new set 'continue' for stopwatch ", "0.6.0" => "29.04.2020 new set 'reset' for stopwatch, read 'state' and 'starttime' from readings, add csrf token support ", "0.5.0" => "28.04.2020 new values 'stopwatch', 'staticwatch' for attribute digitalDisplayPattern ", "0.4.0" => "20.11.2018 text display ", "0.3.0" => "19.11.2018 digital clock added ", "0.2.0" => "14.11.2018 station clock added ", "0.1.0" => "13.11.2018 initial Version with modern analog clock" ); my %hcb = ( # Hash der Steuertastendefinition 1 => {cmd => "start", img => "default/remotecontrol/black_btn_GREEN.png", }, 2 => {cmd => "stop", img => "default/remotecontrol/black_btn_RED.png", }, 3 => {cmd => "resume", img => "default/remotecontrol/black_btn_YELLOW.png", }, 4 => {cmd => "reset", img => "default/remotecontrol/black_btn_STOP.png", }, ); my %hset = ( # Hash der Set-Werte staticwatch => {set => "time" }, stopwatch => {set => "alarmSet alarmDel:noArg reset:noArg resume:noArg start:noArg stop:noArg" }, countdownwatch => {set => "alarmSet alarmDel:noArg reset:noArg resume:noArg start:noArg stop:noArg countDownInit" }, watch => {set => "alarmSet alarmDel:noArg" }, text => {set => "displayTextSet displayTextDel:noArg textTicker:on,off" }, time => {fn => \&_setTime }, reset => {fn => \&_setReset }, textTicker => {fn => \&_setTextTicker }, displayTextDel => {fn => \&_setDisplayTextDel }, displayTextSet => {fn => \&_setDisplayTextSet }, stop => {fn => \&_setStop }, resume => {fn => \&_setResume }, countDownInit => {fn => \&_setCountDownInit }, alarmDel => {fn => \&_setAlarmDel }, alarmSet => {fn => \&_setAlarmSet }, start => {fn => \&_setStart }, ); ############################################################################## # Initialize Funktion ############################################################################## sub Initialize { my ($hash) = @_; $hash->{DefFn} = \&Define; $hash->{SetFn} = \&Set; $hash->{FW_summaryFn} = \&FWebFn; $hash->{FW_detailFn} = \&FWebFn; $hash->{AttrFn} = \&Attr; $hash->{AttrList} = "controlButtonSize:selectnumbers,50,5,150,0,lin ". "digitalBorderDistance:slider,0,1,40 ". "digitalColorBackground:colorpicker ". "digitalColorDigits:colorpicker ". "digitalDisplayPattern:countdownwatch,staticwatch,stopwatch,text,watch ". "digitalDigitAngle:slider,-30,0.5,30,1 ". "digitalDigitDistance:slider,0.5,0.1,10,1 ". "digitalDigitHeight:slider,5,0.1,50,1 ". "digitalDigitWidth:slider,5,0.1,50,1 ". "digitalSegmentDistance:slider,0,0.1,5,1 ". "digitalSegmentType:7,14,16 ". "digitalSegmentWidth:slider,0.3,0.1,3.5,1 ". "digitalTextDigitNumber ". "disable:1,0 ". "hideButtons:1,0 ". "hideDisplayName:1,0 ". "htmlattr ". "modernColorBackground:colorpicker ". "modernColorHand:colorpicker ". "modernColorFigure:colorpicker ". "modernColorFace:colorpicker ". "modernColorRing:colorpicker ". "modernColorRingEdge:colorpicker ". "stationSecondHand:Bar,HoleShaped,NewHoleShaped,No ". "stationSecondHandBehavoir:Bouncing,Overhasty,Creeping,ElasticBouncing ". "stationMinuteHandBehavoir:Bouncing,Creeping,ElasticBouncing ". "stationBoss:Red,Black,Vienna,No ". "stationMinuteHand:Bar,Pointed,Swiss,Vienna ". "stationHourHand:Bar,Pointed,Swiss,Vienna ". "stationStrokeDial:GermanHour,German,Austria,Swiss,Vienna,No ". "stationBody:Round,SmallWhite,RoundGreen,Square,Vienna,No ". "timeAsReading:1,0 ". "timeSource:server,client ". $readingFnAttributes;; $hash->{FW_hideDisplayName} = 1; # Forum 88667 # $hash->{FW_addDetailToSummary} = 1; $hash->{FW_atPageEnd} = 1; # wenn 1 -> kein Longpoll ohne informid in HTML-Tag # $hash->{FW_deviceOverview} = 1; eval { FHEM::Meta::InitMod( __FILE__, $hash ) }; ## no critic 'eval' # für Meta.pm (https://forum.fhem.de/index.php/topic,97589.0.html) return; } ############################################################################## # Define Funktion ############################################################################## sub Define { my ($hash, $def) = @_; my $name = $hash->{NAME}; my @a = split m{\s+}x, $def; if(!$a[2]) { return "You need to specify more parameters.\n". "Format: define Watches [Modern | Station | Digital]"; } $hash->{HELPER}{MODMETAABSENT} = 1 if($modMetaAbsent); # Modul Meta.pm nicht vorhanden $hash->{MODEL} = uc($a[2]); setVersionInfo($hash); # Versionsinformationen setzen readingsSingleUpdate($hash,"state", "initialized", 1); # Init für "state" return; } ############################################################################## # Set Funktion ############################################################################## sub Set { my ($hash, @a) = @_; return qq{"set X" needs at least an argument} if ( @a < 2 ); my $name = $a[0]; my $opt = $a[1]; my $prop = $a[2]; my $prop1 = $a[3]; my $prop2 = $a[4]; return if(IsDisabled($name)); my $addp = AttrVal($name, "digitalDisplayPattern", "watch"); if (!$hset{$addp}) { Log3($name, 1, "$name - ERROR - The attribute 'digitalDisplayPattern' value '$addp' is not known by module '$hash->{TYPE}'"); return; } my $setlist = "Unknown argument $opt, choose one of "; $setlist .= "$hset{$addp}{set} "; my $params = { hash => $hash, name => $name, opt => $opt, prop => $prop, prop1 => $prop1, prop2 => $prop2, addp => $addp, aref => \@a, }; no strict "refs"; ## no critic 'NoStrict' if($hset{$opt}) { my $ret = ""; $ret = &{$hset{$opt}{fn}} ($params) if(defined &{$hset{$opt}{fn}}); return $ret; } use strict "refs"; return $setlist; } ################################################################ # Setter start ################################################################ sub _setStart { my $paref = shift; my $hash = $paref->{hash}; my $name = $paref->{name}; my $addp = $paref->{addp}; return qq{Please set "countDownInit" before !} if($addp =~ /countdownwatch/x && !ReadingsVal($name, "countInitVal", "")); my $ms = int(time*1000); readingsBeginUpdate ($hash); readingsBulkUpdate ($hash, "alarmed", 0) if($addp =~ /stopwatch|countdownwatch/x); readingsBulkUpdate ($hash, "starttime", $ms); readingsBulkUpdate ($hash, "state", "started"); readingsEndUpdate ($hash, 1); return; } ################################################################ # Setter alarmSet ################################################################ sub _setAlarmSet { my $paref = shift; my $hash = $paref->{hash}; my $name = $paref->{name}; my $opt = $paref->{opt}; my $prop = $paref->{prop} // 70; my $prop1 = $paref->{prop1} // 70; my $prop2 = $paref->{prop2} // 70; my $msg = qq{The value for "$opt" is invalid. Use parameter "hh mm ss" like "19 45 13".}; return $msg if($prop>23 || $prop1>59 || $prop2>59); my $at = sprintf("%02d",$prop).":".sprintf("%02d",$prop1).":".sprintf("%02d",$prop2); readingsSingleUpdate($hash, "alarmed", 0, 0); readingsSingleUpdate($hash, "alarmTime", $at, 1); return; } ################################################################ # Setter alarmDel ################################################################ sub _setAlarmDel { my $paref = shift; my $name = $paref->{name}; delReadings ($name, "alarmTime"); delReadings ($name, "alarmed"); return; } ################################################################ # Setter countDownInit ################################################################ sub _setCountDownInit { my $paref = shift; my $hash = $paref->{hash}; my $name = $paref->{name}; my $opt = $paref->{opt}; my $prop = $paref->{prop}; my $prop1 = $paref->{prop1}; my $prop2 = $paref->{prop2}; my $ct; my $msg = qq{The value for "$opt" is invalid. Use parameter "hh mm ss" like "19 45 13" \nor alternatively only one entry in seconds.}; if($prop && $prop1) { # Format: hh mm ss $prop2 = defined $prop2 ? $prop2 : 70; # Sekunden return $msg if($prop>23 || $prop1>59 || $prop2>59); $ct = $prop*3600 + $prop1*60 + $prop2; # in Sekunden umgewandelt ! } elsif ($prop && !$prop1) { # Format: Sekundenangabe $ct = $prop; } else { return $msg; } delReadings ($name, "countInitVal"); readingsBeginUpdate ($hash); readingsBulkUpdate ($hash, "countInitVal", $ct ); readingsBulkUpdate ($hash, "state", "initialized"); readingsEndUpdate ($hash, 1); return; } ################################################################ # Setter resume ################################################################ sub _setResume { my $paref = shift; my $hash = $paref->{hash}; my $name = $paref->{name}; my $addp = $paref->{addp}; return qq{Please set "countDownInit" before !} if($addp =~ /countdownwatch/x && !ReadingsVal($name, "countInitVal", "")); return if(ReadingsVal($name, "state", "") eq "started"); my $ms = int(time*1000); readingsSingleUpdate($hash, "starttime", $ms, 0); readingsSingleUpdate($hash, "state", "resumed", 1); return; } ################################################################ # Setter stop ################################################################ sub _setStop { my $paref = shift; my $hash = $paref->{hash}; readingsSingleUpdate($hash, "state", "stopped", 1); return; } ################################################################ # Setter displayTextSet ################################################################ sub _setDisplayTextSet { my $paref = shift; my $hash = $paref->{hash}; my $aref = $paref->{aref}; my @a = @$aref; shift @a; shift @a; my $txt = join (" ", @a); $txt =~ s/[\r\n]//gx; readingsSingleUpdate($hash, "displayText", $txt, 1); return; } ################################################################ # Setter displayTextDel ################################################################ sub _setDisplayTextDel { my $paref = shift; my $name = $paref->{name}; delReadings ($name, "displayText"); return; } ################################################################ # Setter textTicker ################################################################ sub _setTextTicker { my $paref = shift; my $hash = $paref->{hash}; my $prop = $paref->{prop}; if($prop eq "on") { readingsSingleUpdate($hash, "displayTextTicker", "on", 1); } else { readingsSingleUpdate($hash, "displayTextTicker", "off", 1); } return; } ################################################################ # Setter reset ################################################################ sub _setReset { my $paref = shift; my $hash = $paref->{hash}; my $name = $paref->{name}; delReadings ($name); readingsSingleUpdate($hash, "state", "initialized", 1); return; } ################################################################ # Setter time ################################################################ sub _setTime { my $paref = shift; my $hash = $paref->{hash}; my $opt = $paref->{opt}; my $prop = sprintf("%0d", $paref->{prop} // 0); my $prop1 = sprintf("%0d", $paref->{prop1} // 0); my $prop2 = sprintf("%0d", $paref->{prop2} // 0); my $msg = qq{The value for "$opt" is invalid. Use parameter "hh mm ss" like "19 45 13"}; return $msg if($prop>23 || $prop1>59 || $prop2>59); readingsBeginUpdate ($hash); readingsBulkUpdate ($hash, "hour", $prop); readingsBulkUpdate ($hash, "minute", $prop1); readingsBulkUpdate ($hash, "second", $prop2); readingsEndUpdate ($hash, 1); return; } ############################################################################## # Attributfunktion ############################################################################## sub Attr { ## no critic 'complexity' my ($cmd,$name,$aName,$aVal) = @_; my $hash = $defs{$name}; my ($do,$val); # $cmd can be "del" or "set" # $name is device name # aName and aVal are Attribute name and value if ($cmd eq "set" && $hash->{MODEL} !~ /modern/ix && $aName =~ /^modern/x) { return qq{"$aName" is only valid for Watches model "Modern"}; } if ($cmd eq "set" && $hash->{MODEL} !~ /station/ix && $aName =~ /^station/x) { return qq{"$aName" is only valid for Watches model "Station"}; } if ($cmd eq "set" && $hash->{MODEL} !~ /digital/ix && $aName =~ /^digital/x) { return qq{"$aName" is only valid for Watches model "Digital"}; } if ($aName eq "disable") { if($cmd eq "set") { $do = ($aVal) ? 1 : 0; } $do = 0 if($cmd eq "del"); $val = ($do == 1 ? "disabled" : "initialized"); readingsSingleUpdate($hash, "state", $val, 1); } if ($aName eq "timeAsReading") { if($cmd eq "set") { $do = $aVal; } $do = 0 if($cmd eq "del"); if(!$do) { delReadings ($name, "currtime"); } } if ($aName eq "digitalDisplayPattern") { if($cmd eq "set") { $do = $aVal; } $do = 0 if($cmd eq "del"); if($do ne "text") { delReadings ($name); } else { delReadings ($name,undef,"^display.*"); } readingsSingleUpdate($hash, "state", "initialized", 1); if($do =~ /\bstopwatch\b/x) { my $ms = int(time*1000); readingsSingleUpdate($hash, "starttime", $ms, 0); } } if ($cmd eq "set") { if ($aName =~ /digitalTextDigitNumber|digitalBorderDistance/x && $aVal !~ /^[0-9]+$/x) { return qq{The value of "$aName" is not valid. Only integers are allowed !}; } } return; } ############################################################################## # Webanzeige des Devices ############################################################################## sub FWebFn { my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn. my $hash = $defs{$d}; my $alias = AttrVal($d, "alias", $d); # Linktext als Aliasname oder Devicename setzen my $dlink = qq{$alias}; my $ret = ""; $ret .= "$dlink
" if(!AttrVal($d,"hideDisplayName",0)); if(IsDisabled($d)) { if(AttrVal($d,"hideDisplayName",0)) { $ret .= qq{Watch $d is disabled}; } else { $ret .= "Watch is disabled"; } } else { $ret .= modernWatch ($d) if($hash->{MODEL} =~ /modern/ix); $ret .= stationWatch($d) if($hash->{MODEL} =~ /station/ix); $ret .= digitalWatch($d) if($hash->{MODEL} =~ /digital/ix); } my $ddp = AttrVal($d, "digitalDisplayPattern", ""); # Steuertastenpaneel my $hb = AttrVal($d, "hideButtons", 0); if($ddp =~ /countdownwatch|stopwatch/x && !$hb) { $ret .= controlPanel ($d); } return $ret; } ############################################################################### # Paneel für Stoppuhr Steuertasten ############################################################################### sub controlPanel { my $name = shift; my $pbs = AttrVal($name,"controlButtonSize", 100); # Größe der Druckbuttons in % my $iconpath = "www/images"; my $ret = ""; $ret .= ""; $ret .= ""; $ret .= ""; $ret .= ''; $ret .= ""; for my $btn (sort keys %hcb) { my $cmd = $hcb{$btn}{cmd}; my $img = $hcb{$btn}{img}; next if(!$cmd || !$img); $ret .= ""; } $ret .= ""; $ret .= "
" if($pbs <= 100); $ret .= "" if($pbs >= 105); if ($img =~ m/\.svg/x) { # Verwendung für SVG's $img = FW_makeImage($img, $cmd, "rc-button"); } else { # $FW_ME = URL-Pfad unter dem der FHEMWEB-Server via HTTP erreichbar ist, z.B. /fhem $img = ""; } my $cmd1 = "FW_cmd('$FW_ME$FW_subdir?XHR=1&cmd=set $name $cmd')"; # $FW_subdir = Sub-path in URL, used by FLOORPLAN/weblink $ret .= "$img"; $ret .= "
"; return $ret; } ############################################################################## # löscht alle oder das spezifizierte Reading (außer state) # $todel = nur dieses Reading löschen # $supress = Reading (Regex) nicht löschen ############################################################################## sub delReadings { my ($name,$todel,$supress) = @_; my $hash = $defs{$name}; my $addp = AttrVal($name, "digitalDisplayPattern", "watch"); if($todel) { readingsDelete($hash,$todel); return; } my @allrds = keys%{$hash->{READINGS}}; for my $key(@allrds) { next if($key =~ /\bstate\b/x); next if(defined $supress && $key =~ /$supress/x); readingsDelete($hash,$key); } return; } ############################################################################## # Digitale Uhr / Anzeige aus: # http://www.3quarks.com/de/Segmentanzeige/index.html # ############################################################################## sub digitalWatch { my ($d) = @_; my $hash = $defs{$d}; my $alarmdef = "00:00:00"; my $bgc = AttrVal($d, "digitalColorBackground", "C4C4C4"); my $dcd = AttrVal($d, "digitalColorDigits", "000000"); my $addp = AttrVal($d, "digitalDisplayPattern", "watch"); my $adst = AttrVal($d, "digitalSegmentType", 7); my $adsw = AttrVal($d, "digitalSegmentWidth", 1.5); my $addh = AttrVal($d, "digitalDigitHeight", 20); my $addw = AttrVal($d, "digitalDigitWidth", 12); my $addd = AttrVal($d, "digitalDigitDistance", 2); my $adsd = AttrVal($d, "digitalSegmentDistance", 0.5); my $adda = AttrVal($d, "digitalDigitAngle", 9); my $adtdn = AttrVal($d, "digitalTextDigitNumber", 0); my $abdist = AttrVal($d, "digitalBorderDistance", 8); my $hattr = AttrVal($d, "htmlattr", "width='150' height='50'"); my $tsou = AttrVal($d, "timeSource", "client"); my $showct = AttrVal($d, "timeAsReading", 0); my $deftxt = " "; my $rdtt = ReadingsVal ($d, "displayTextTicker", "off"); my $ddt = ReadingsVal ($d, "displayText", $deftxt); my $alarm = ReadingsVal ($d, "alarmTime", "aa:bb:cc"); my ($h,$m,$s,$txtc) = (0,0,0,0); my $bdist = ""; # Abstand zum linken und rechten Rand for (my $i=0; $i<=$abdist; $i++ ) { $bdist .= " "; } if ($addp eq "stopwatch") { $alarmdef = "aa:bb:cc"; # Stoppuhr bei Start 00:00:00 nicht Alerm auslösen } if ($addp eq "staticwatch") { # statische Uhrzeitanzeige $h = ReadingsVal($d, "hour" , 0); $m = ReadingsVal($d, "minute", 0); $s = ReadingsVal($d, "second", 0); } my $back = << "END_JS"; END_JS return qq{$back}; } ############################################################################## # Bahnhofsuhr aus: # http://www.3quarks.com/de/Bahnhofsuhr # ############################################################################## sub stationWatch { my ($d) = @_; my $hash = $defs{$d}; my $ssh = AttrVal ($d, "stationSecondHand", "Bar"). "SecondHand"; my $shb = AttrVal ($d, "stationSecondHandBehavoir", "Bouncing"). "SecondHand"; my $smh = AttrVal ($d, "stationMinuteHand", "Pointed"). "MinuteHand"; my $mhb = AttrVal ($d, "stationMinuteHandBehavoir", "Bouncing"). "MinuteHand"; my $shh = AttrVal ($d, "stationHourHand", "Pointed"). "HourHand"; my $sb = AttrVal ($d, "stationBoss", "Red"). "Boss"; my $ssd = AttrVal ($d, "stationStrokeDial", "Swiss"). "StrokeDial"; my $sbody = AttrVal ($d, "stationBody", "Round"). "Body"; my $hattr = AttrVal ($d, "htmlattr", "width='150' height='150'"); my $tsou = AttrVal ($d, "timeSource", "client"); my $showct = AttrVal ($d, "timeAsReading", 0); my $alarm = ReadingsVal($d, "alarmTime", "aa:bb:cc"); my $back = << "END_JS"; END_JS return qq{$back}; } ############################################################################## # Moderne Uhr aus: # https://www.w3schools.com/graphics/canvas_clock_start.asp # ############################################################################## sub modernWatch { my ($d) = @_; my $hash = $defs{$d}; my $facec = AttrVal($d, "modernColorFace", "FFFEFA"); my $bgc = AttrVal($d, "modernColorBackground", "333"); my $fc = AttrVal($d, "modernColorFigure", "333"); my $hc = AttrVal($d, "modernColorHand", "333"); my $fr = AttrVal($d, "modernColorRing", "FFFFFF"); my $fre = AttrVal($d, "modernColorRingEdge", "333"); my $hattr = AttrVal($d, "htmlattr", "width='150' height='150'"); my $tsou = AttrVal($d, "timeSource", "client"); my $showct = AttrVal($d, "timeAsReading", 0); my $alarm = ReadingsVal($d, "alarmTime", "aa:bb:cc"); my $back = << "END_JS"; END_JS return qq{$back}; } ############################################################################## # Versionierungen des Moduls setzen # Die Verwendung von Meta.pm und Packages wird berücksichtigt # ############################################################################## sub setVersionInfo { my ($hash) = @_; my $name = $hash->{NAME}; my $v = (sortTopicNum("desc",keys %vNotesIntern))[0]; my $type = $hash->{TYPE}; $hash->{HELPER}{PACKAGE} = __PACKAGE__; $hash->{HELPER}{VERSION} = $v; if($modules{$type}{META}{x_prereqs_src} && !$hash->{HELPER}{MODMETAABSENT}) { # META-Daten sind vorhanden $modules{$type}{META}{version} = "v".$v; # Version aus META.json überschreiben, Anzeige mit {Dumper $modules{SMAPortal}{META}} if($modules{$type}{META}{x_version}) { $modules{$type}{META}{x_version} =~ s/1\.1\.1/$v/gx; } else { $modules{$type}{META}{x_version} = $v; } return $@ unless (FHEM::Meta::SetInternals($hash)); if(__PACKAGE__ eq "FHEM::$type" || __PACKAGE__ eq $type) { # es wird mit Packages gearbeitet -> Perl übliche Modulversion setzen # mit {->VERSION()} im FHEMWEB kann Modulversion abgefragt werden use version 0.77; our $VERSION = FHEM::Meta::Get( $hash, 'version' ); ## no critic 'VERSION' } } else { # herkömmliche Modulstruktur $hash->{VERSION} = $v; } return; } 1; =pod =item helper =item summary Clock display in different variants =item summary_DE Uhrenanzeige in verschiedenen Varianten =begin html

Watches


The module Watches provides watches in different styles as Device. The module is a JavaScript application that runs on a client (browser) and not on the FHEM server. Attributes and readings are read asynchronously from the server and possibly also written, but only if the application is currently running in the browser.
The user can influence the design of the watches via attributes.
The clocks are based on scripts of these pages:
modern watch, Station clock, Digital display

A device of the model Digital can also be used as stopwatch, countdown timer or universal text display (for sixteen segment mode see attribute digitalSegmentType).
As time source the client (browser time) as well as the FHEM server can be set (attribute timeSource).
    Define
      define <name> Watches [Modern | Station | Digital]

      Modern : creates an analog clock with a modern design
      Station : creates a station clock
      Digital : creates a digital display (clock, (CountDown)stop watch, static time display or text)


    Set
      • alarmSet <hh> <mm> <ss>
        Sets the alarm time in the format hh hours, mm minutes and ss seconds.
        If the time reaches the defined value, an event of the reading "alarmed" is triggered.
        This set command is only available for digital stopwatches.

          Example
          set <name> alarmSet 0 30 10


      • alarmDel
        Clears the set alarm time and its status.
        This set command is only available for digital stopwatches.

      • countDownInit <hh> <mm> <ss> | <seconds>
        Sets the start time of a countdown stopwatch. The format can be <hh> hours, <mm> minutes and <ss> seconds or alternatively only one entry in seconds.
        This set command is only available with a digital countdown stopwatch.

          Examples
          set <name> countDownInit 0 30 10
          set <name> countDownInit 3600


      • displayTextSet
        Sets the text to be displayed.
        This set command is only available for a digital segment display with "digitalDisplayPattern = text".
        (default: blank display)

        Note:
        The displayable characters depend on the attribute "digitalSegmentType".
        With the (default) seven-segment display, only numbers, hyphen, underscore and the letters A, b, C, d, E, F, H, L, n, o, P, r, t, U and Y are displayed. In addition to numbers, short texts such as "Error", "HELP", "run" or "PLAY" can also be displayed.
        For text display it is recommended to set the sixteen segment display with the attribute "digitalSegmentType" !

      • displayTextDel
        Deletes the display text.
        This set command is only available for a digital segment display with "digitalDisplayPattern = text".

      • reset
        Stops the stopwatch (if running) and clears all specific readings or resets it to initialized.
        This set command is only available for digital stopwatches.

      • resume
        Resumes counting a stopped stopwatch.
        This set command is only available for digital stopwatches.

      • start
        Starts the stopwatch.
        This set command is only available for digital stopwatches.

      • stop
        Stop the stopwatch. The achieved time is retained.
        This set command is only available for digital stopwatches.

      • textTicker on | off
        Switches the ticker mode of a text display (see attribute digitalDisplayPattern) on or off.
        (default: off)

      • time <hh> <mm> <ss>
        Sets a static time display with hh hours, mm minutes and ss seconds.
        This set command is only available for a digital clock with static time display.

          Example
          set <name> time 8 15 3


    Get
      N/A

    Attributes

      • controlButtonSize
        Changes the size of the control buttons if the clock type has control buttons.

      • disable
        Activates/deactivates the Device.

      • hideButtons
        Hides the control buttons if the watch type has control buttons.

      • hideDisplayName
        Hides the Device/Alias name (link to detail view).

      • htmlattr
        Additional HTML tags to resize the clock / display.

          Example:
          attr <name> htmlattr width="125" height="125"

      • timeAsReading
        If set, a displayed time is written to the reading currtime.

      • timeSource
        Selects the time source. The local client time (browser) or the FHEM server time can be displayed.
        This setting is not relevant for (countdown) stopwatches.
        [default: client]

      The following attributes must be set specifically for a clock type.

      Model: Modern

      • modernColorBackground
        Background color of the clock.

      • modernColorFace
        Colouring of the dial.

      • modernColorFigure
        Colour of the numbers on the dial and the pointer axle cover.

      • modernColorHand
        Colour of the watch hands.

      • modernColorRing
        Colour of the dial frame.

      • modernColorRingEdge
        Colour of the outer ring of the dial frame.


      Model: Station

      • stationBody
        Type of watch case.

      • stationBoss
        Type and colour of the pointer axle cover.

      • stationHourHand
        Type of hour hand.

      • stationMinuteHand
        Type of minute hand.

      • stationMinuteHandBehavoir
        Behavior of the minute hand.

      • stationSecondHand
        Type of second hand.

      • stationSecondHandBehavoir
        Behavior of the second hand.

      • stationStrokeDial
        Selection of the dial.


      Model: Digital

      • digitalBorderDistance
        Left and right distance of the digital text display from the background edge.
        (default: 8)

      • digitalColorBackground
        Digital clock background color.

      • digitalColorDigits
        Color of the bar display in a digital watch.

      • digitalDigitAngle
        Adjusts the tilt angle of the displayed characters.
        (default: 9)

      • digitalDigitDistance
        Adjusts the character spacing.
        (default: 2)

      • digitalDigitHeight
        Adjusts the character height.
        (default: 20)

      • digitalDigitWidth
        Adjusts the character width.
        (default: 12)

      • digitalDisplayPattern [countdownwatch | staticwatch | stopwatch | text | watch]
        Switching the digital display between a clock (default), a stopwatch, static time display or text display. The text to be displayed in text display mode can be defined with
        set <name> displayText.

        Note: For text display it is recommended to set the attribute "digitalSegmentType" to "16".

          countdownwatch : CountDown Stopwatch
          staticwatch : static time display
          stopwatch : Stopwatch
          text : Display of a definable text
          watch : Watch


      • digitalSegmentDistance
        Defines the distance between the segments.
        (default: 0.5)

      • digitalSegmentType
        Switches the segment number of the digital display.
        (default: 7)

      • digitalSegmentWidth
        Changes the width of the individual segments.
        (default: 1.5)

      • digitalTextDigitNumber <Quantity>
        If <Quantity> > 0, the number of digits of a text display (digitalDisplayPattern = text) is fixed. If <Quantity> = 0 or not set, the setting is made automatically. In this case an adaptation is made of the character size to the number depending on the set display size (see htmlattr).
        (default: 0)

=end html =begin html_DE

Watches


Das Modul Watches stellt Uhren in unterschiedlichen Stilen als Device zur Verfügung. Das Modul ist eine JavaScript Anwendung die auf einem Client (Browser) ausgeführt wird und nicht auf dem FHEM Server. Attribute und Readings werden asynchron vom Server gelesen und evtl. auch geschrieben, allerdings nur dann wenn die Anwendung aktuell im Browser ausgeführt wird.
Der Nutzer kann das Design der Uhren über Attribute beeinflussen.
Die Uhren basieren auf Skripten dieser Seiten:
moderne Uhr, Bahnhofsuhr, Digitalanzeige

Ein Device vom Model Digital kann ebenfalls als Stoppuhr, CountDown-Timer oder universelle Textanzeige (für Sechzehnsegment-Modus siehe Attribut digitalSegmentType) verwendet werden.
Als Zeitquelle können sowohl der Client (Browserzeit) als auch der FHEM-Server eingestellt werden (Attribut timeSource).
    Define
      define <name> Watches [Modern | Station | Digital]

      Modern : erstellt eine analoge Uhr im modernen Design
      Station : erstellt eine Bahnhofsuhr
      Digital : erstellt eine Digitalanzeige (Uhr, (CountDown)Stoppuhr, statische Zeitanzeige oder Text)


    Set
      • alarmSet <hh> <mm> <ss>
        Setzt die Alarmzeit im Format hh-Stunden, mm-Minuten und ss-Sekunden.
        Erreicht die Zeit den definierten Wert, wird ein Event des Readings "alarmed" ausgelöst.
        Dieses Set-Kommando ist nur bei digitalen Stoppuhren vorhanden.

          Beispiel
          set <name> alarmSet 0 30 10


      • alarmDel
        Löscht die gesetzte Alarmzeit und deren Status.
        Dieses Set-Kommando ist nur bei digitalen Stoppuhren vorhanden.

      • countDownInit <hh> <mm> <ss> | <Sekunden>
        Setzt die Startzeit einer CountDown-Stoppuhr. Das Format kann sein <hh> Stunden, <mm> Minuten und <ss> Sekunden oder alternativ nur eine Angabe in Sekunden.
        Dieses Set-Kommando ist nur bei einer digitalen CountDown-Stoppuhr vorhanden.

          Beispiel
          set <name> countDownInit 0 30 10
          set <name> countDownInit 3600


      • displayTextSet
        Stellt den anzuzeigenden Text ein.
        Dieses Set-Kommando ist nur bei einer digitalen Segmentanzeige mit "digitalDisplayPattern = text" vorhanden.
        (default: leere Anzeige)

        Hinweis:
        Die darstellbaren Zeichen sind vom Attribut "digitalSegmentType" abhängig.
        Mit der (default) Siebensegmentanzeige können lediglich Ziffern, Bindestrich, Unterstrich und die Buchstaben A, b, C, d, E, F, H, L, n, o, P, r, t, U und Y angezeigt werden. Damit lassen sich außer Zahlen auch kurze Texte wie „Error“, „HELP“, „run“ oder „PLAY“ anzeigen.
        Für Textdarstellung wird empfohlen die Sechzehnsegmentanzeige mit dem Attribut "digitalSegmentType" einzustellen !

      • displayTextDel
        Löscht den Anzeigetext.
        Dieses Set-Kommando ist nur bei einer digitalen Segmentanzeige mit "digitalDisplayPattern = text" vorhanden.

      • reset
        Stoppt die Stoppuhr (falls sie läuft) und löscht alle spezifischen Readings bzw. setzt sie auf initialized zurück.
        Dieses Set-Kommando ist nur bei digitalen Stoppuhren vorhanden.

      • resume
        Setzt die Zählung einer angehaltenen Stoppuhr fort.
        Dieses Set-Kommando ist nur bei digitalen Stoppuhren vorhanden.

      • start
        Startet die Stoppuhr.
        Dieses Set-Kommando ist nur bei digitalen Stoppuhren vorhanden.

      • stop
        Stoppt die Stoppuhr. Die erreichte Zeit bleibt erhalten.
        Dieses Set-Kommando ist nur bei digitalen Stoppuhren vorhanden.

      • textTicker on | off
        Schaltet den Laufschriftmodus einer Textanzeige (siehe Attribut digitalDisplayPattern) ein bzw. aus.
        (default: off)

      • time <hh> <mm> <ss>
        Setzt eine statische Zeitanzeige mit hh-Stunden, mm-Minuten und ss-Sekunden.
        Dieses Set-Kommando ist nur bei einer Digitaluhr mit statischer Zeitanzeige vorhanden.

          Beispiel
          set <name> time 8 15 3


    Get
      N/A

    Attribute

      • controlButtonSize
        Ändert die Größe der Steuerdrucktasten sofern der Uhrentyp über Steuerdrucktasten verfügt.

      • disable
        Aktiviert/deaktiviert das Device.

      • hideButtons
        Verbirgt die Steuerdrucktasten sofern der Uhrentyp über Steuerdrucktasten verfügt.

      • hideDisplayName
        Verbirgt den Device/Alias-Namen (Link zur Detailansicht).

      • htmlattr
        Zusätzliche HTML Tags zur Größenänderung der Uhr / Anzeige.

          Beispiel:
          attr <name> htmlattr width="125" height="125"

      • timeAsReading
        Wenn gesetzt, wird eine angezeigte Uhrzeit in das Reading currtime geschrieben.

      • timeSource
        Wählt die Zeitquelle aus. Es kann die lokale Clientzeit (Browser) oder die Zeit des FHEM-Servers angezeigt werden.
        Diese Einstellung ist bei (CountDown-)Stoppuhren nicht relevant.
        (default: client)

      Die nachfolgenden Attribute sind spezifisch für einen Uhrentyp zu setzen.

      Model: Modern

      • modernColorBackground
        Hintergrundfarbe der Uhr.

      • modernColorFace
        Einfärbung des Ziffernblattes.

      • modernColorFigure
        Farbe der Ziffern im Ziffernblatt und der Zeigerachsabdeckung.

      • modernColorHand
        Farbe der UhrenZeiger.

      • modernColorRing
        Farbe des Ziffernblattrahmens.

      • modernColorRingEdge
        Farbe des Außenringes vom Ziffernblattrahmen.


      Model: Station

      • stationBody
        Art des Uhrengehäuses.

      • stationBoss
        Art und Farbe der Zeigerachsabdeckung.

      • stationHourHand
        Art des Stundenzeigers.

      • stationMinuteHand
        Art des Minutenzeigers.

      • stationMinuteHandBehavoir
        Verhalten des Minutenzeigers.

      • stationSecondHand
        Art des Sekundenzeigers.

      • stationSecondHandBehavoir
        Verhalten des Sekundenzeigers.

      • stationStrokeDial
        Auswahl des Ziffernblattes.


      Model: Digital

      • digitalBorderDistance
        Linker und rechter Abstand der digitalen Textanzeige vom Hintergrundrand.
        (default: 8)

      • digitalColorBackground
        Digitaluhr Hintergrundfarbe.

      • digitalColorDigits
        Farbe der Balkenanzeige in einer Digitaluhr.

      • digitalDigitAngle
        Stellt den Neigungswinkel der dargestellten Zeichen ein.
        (default: 9)

      • digitalDigitDistance
        Stellt den Zeichenabstand ein.
        (default: 2)

      • digitalDigitHeight
        Stellt die Zeichenhöhe ein.
        (default: 20)

      • digitalDigitWidth
        Stellt die Zeichenbreite ein.
        (default: 12)

      • digitalDisplayPattern [countdownwatch | staticwatch | stopwatch | text | watch]
        Umschaltung der Digitalanzeige zwischen einer Uhr (default), einer Stoppuhr, statischen Zeitanzeige oder Textanzeige. Der anzuzeigende Text im Modus Textanzeige kann mit
        set <name> displayText.

        Hinweis: Bei Textanzeige wird empfohlen das Attribut "digitalSegmentType" auf "16" zu stellen.

          countdownwatch : CountDown Stoppuhr
          staticwatch : statische Zeitanzeige
          stopwatch : Stoppuhr
          text : Anzeige eines definierbaren Textes
          watch : Uhr


      • digitalSegmentDistance
        Legt den Abstand zwischen den Segmenten fest.
        (default: 0.5)

      • digitalSegmentType
        Schaltet die Segmentanzahl der Digitalanzeige um.
        (default: 7)

      • digitalSegmentWidth
        Verändert die Breite der einzelnen Segmente.
        (default: 1.5)

      • digitalTextDigitNumber <Anzahl>
        Wenn <Anzahl> > 0 wird die Anzahl der Stellen einer Textanzeige (digitalDisplayPattern = text) fest eingestellt. Wenn <Anzahl> = 0 oder nicht gesetzt erfolgt die Festlegung automatisch. In diesem Fall erfolgt eine Adaption der Zeichengröße an die Anzahl abhängig von der eingestellten Displaygröße (siehe htmlattr).
        (default: 0)

=end html_DE =for :application/json;q=META.json 60_Watches.pm { "abstract": "Clock display in different variants", "x_lang": { "de": { "abstract": "Uhrenanzeige in verschiedenen Varianten" } }, "keywords": [ "Watch", "Modern clock", "clock", "Station clock", "Digital display" ], "version": "v1.1.1", "release_status": "stable", "author": [ "Heiko Maaz ", null ], "x_fhem_maintainer": [ "DS_Starter" ], "x_fhem_maintainer_github": [ "nasseeder1" ], "prereqs": { "runtime": { "requires": { "FHEM": 5.00918799, "perl": 5.014, "Time::HiRes": 0, "GPUtils": 0 }, "recommends": { "FHEM::Meta": 0 }, "suggests": { } } } } =end :application/json;q=META.json =cut