######################################################################################################################## # $Id: $ ######################################################################################################################### # 60_Watches.pm # # (c) 2018 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 <http://www.gnu.org/licenses/>. # # 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 main; use strict; use warnings; # Versions History intern our %Watches_vNotesIntern = ( "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" ); sub Watches_modern($); ################################################################ sub Watches_Initialize($) { my ($hash) = @_; $hash->{DefFn} = "Watches_Define"; $hash->{AttrList} = "digitalColorBackground:colorpicker ". "digitalColorDigits:colorpicker ". "digitalDisplayPattern:text,watch ". "digitalDisplayText ". "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 ". "disable:1,0 ". "htmlattr ". "hideDisplayName:1,0 " ; $hash->{FW_summaryFn} = "Watches_FwFn"; $hash->{FW_detailFn} = "Watches_FwFn"; $hash->{AttrFn} = "Watches_Attr"; $hash->{FW_hideDisplayName} = 1; # Forum 88667 # $hash->{FW_addDetailToSummary} = 1; $hash->{FW_atPageEnd} = 1; # wenn 1 -> kein Longpoll ohne informid in HTML-Tag } ################################################################ sub Watches_Define($$) { my ($hash, $def) = @_; my $name = $hash->{NAME}; my @a = split("[ \t][ \t]*", $def); if(!$a[2]) { return "You need to specify more parameters.\n". "Format: define <name> Watches [Modern | Station | Digital]"; } $hash->{MODEL} = $a[2]; $hash->{VERSION} = $hash->{VERSION} = (reverse sort(keys %Watches_vNotesIntern))[0]; readingsSingleUpdate($hash,"state", "initialized", 1); # Init für "state" return undef; } ################################################################ sub Watches_Attr($$$$) { 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/i && $aName =~ /^modern.*/) { return "\"$aName\" is only valid for Watches model \"Modern\""; } if ($cmd eq "set" && $hash->{MODEL} !~ /station/i && $aName =~ /^station.*/) { return "\"$aName\" is only valid for Watches model \"Station\""; } if ($cmd eq "set" && $hash->{MODEL} !~ /digital/i && $aName =~ /^digital.*/) { return "\"$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); } return undef; } ################################################################ sub Watches_FwFn($$$$) { 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 = "<a href=\"/fhem?detail=$d\">$alias</a>"; my $ret = ""; $ret .= "<span>$dlink </span><br>" if(!AttrVal($d,"hideDisplayName",0)); if(IsDisabled($d)) { if(AttrVal($d,"hideDisplayName",0)) { $ret .= "Watch <a href=\"/fhem?detail=$d\">$d</a> is disabled"; } else { $ret .= "<html>Watch is disabled</html>"; } } else { $ret .= Watches_modern($d) if($hash->{MODEL} =~ /modern/i); $ret .= Watches_station($d) if($hash->{MODEL} =~ /station/i); $ret .= Watches_digital($d) if($hash->{MODEL} =~ /digital/i); } return $ret; } ################################################################ sub Watches_digital($) { my ($d) = @_; my $hash = $defs{$d}; my $hattr = AttrVal($d,"htmlattr","width='150' height='50'"); my $bgc = AttrVal($d,"digitalColorBackground","C4C4C4"); my $dcd = AttrVal($d,"digitalColorDigits","000000"); my $ddp = AttrVal($d,"digitalDisplayPattern","watch"); my $ddt = AttrVal($d,"digitalDisplayText","Play"); if($ddp eq "watch") { $ddp = "##:##:##"; $ddt = " "."((hours < 10) ? ' ' : '') + hours + ':' + ((minutes < 10) ? '0' : '') + minutes + ':' + ((seconds < 10) ? '0' : '') + seconds"; } elsif($ddp eq "text") { $ddp = "##########"; my $txtc = length($ddt); $ddp = ""; for(my $i = 0; $i <= $txtc; $i++) { $ddp .= "#"; } $ddt = "' ".$ddt."'"; } # Segmentanzeige aus: http://www.3quarks.com/de/Segmentanzeige/index.html return " <html> <body> <canvas id='display_$d' $hattr style='background-color:#$bgc'></canvas> <script> // Segment display types SegmentDisplay_$d.SevenSegment = 7; SegmentDisplay_$d.FourteenSegment = 14; SegmentDisplay_$d.SixteenSegment = 16; // Segment corner types SegmentDisplay_$d.SymmetricCorner = 0; SegmentDisplay_$d.SquaredCorner = 1; SegmentDisplay_$d.RoundedCorner = 2; function SegmentDisplay_$d(displayId_$d) { this.displayId_$d = displayId_$d; this.pattern = '##:##:##'; this.value = '12:34:56'; this.digitHeight = 20; this.digitWidth = 10; this.digitDistance = 2.5; this.displayAngle = 12; this.segmentWidth = 2.5; this.segmentDistance = 0.2; this.segmentCount = SegmentDisplay_$d.SevenSegment; this.cornerType = SegmentDisplay_$d.RoundedCorner; this.colorOn = 'rgb(233, 93, 15)'; this.colorOff = 'rgb(75, 30, 5)'; }; SegmentDisplay_$d.prototype.setValue = function(value) { this.value = value; this.draw(); }; SegmentDisplay_$d.prototype.draw = function() { var display_$d = document.getElementById(this.displayId_$d); if (display_$d) { var context = display_$d.getContext('2d'); if (context) { // clear canvas context.clearRect(0, 0, display_$d.width, display_$d.height); // compute and check display width var width = 0; var first = true; if (this.pattern) { for (var i = 0; i < this.pattern.length; i++) { var c = this.pattern.charAt(i).toLowerCase(); if (c == '#') { width += this.digitWidth; } else if (c == '.' || c == ':') { width += this.segmentWidth; } else if (c != ' ') { return; } width += first ? 0 : this.digitDistance; first = false; } } if (width <= 0) { return; } // compute skew factor var angle = -1.0 * Math.max(-45.0, Math.min(45.0, this.displayAngle)); var skew = Math.tan((angle * Math.PI) / 180.0); // compute scale factor var scale = Math.min(display_$d.width / (width + Math.abs(skew * this.digitHeight)), display_$d.height / this.digitHeight); // compute display offset var offsetX = (display_$d.width - (width + skew * this.digitHeight) * scale) / 2.0; var offsetY = (display_$d.height - this.digitHeight * scale) / 2.0; // context transformation context.save(); context.translate(offsetX, offsetY); context.scale(scale, scale); context.transform(1, 0, skew, 1, 0, 0); // draw segments var xPos = 0; var size = (this.value) ? this.value.length : 0; for (var i = 0; i < this.pattern.length; i++) { var mask = this.pattern.charAt(i); var value = (i < size) ? this.value.charAt(i).toLowerCase() : ' '; xPos += this.drawDigit(context, xPos, mask, value); } // finish drawing context.restore(); } } }; SegmentDisplay_$d.prototype.drawDigit = function(context, xPos, mask, c) { switch (mask) { case '#': var r = Math.sqrt(this.segmentWidth * this.segmentWidth / 2.0); var d = Math.sqrt(this.segmentDistance * this.segmentDistance / 2.0); var e = d / 2.0; var f = (this.segmentWidth - d) * Math.sin((45.0 * Math.PI) / 180.0); var g = f / 2.0; var h = (this.digitHeight - 3.0 * this.segmentWidth) / 2.0; var w = (this.digitWidth - 3.0 * this.segmentWidth) / 2.0; var s = this.segmentWidth / 2.0; var t = this.digitWidth / 2.0; // draw segment a (a1 and a2 for 16 segments) if (this.segmentCount == 16) { var x = xPos; var y = 0; context.fillStyle = this.getSegmentColor_$d(c, null, '02356789abcdefgiopqrstz@%'); context.beginPath(); switch (this.cornerType) { case SegmentDisplay_$d.SymmetricCorner: context.moveTo(x + s + d, y + s); context.lineTo(x + this.segmentWidth + d, y); break; case SegmentDisplay_$d.SquaredCorner: context.moveTo(x + s + e, y + s - e); context.lineTo(x + this.segmentWidth, y); break; default: context.moveTo(x + this.segmentWidth - f, y + this.segmentWidth - f - d); context.quadraticCurveTo(x + this.segmentWidth - g, y, x + this.segmentWidth, y); } context.lineTo(x + t - d - s, y); context.lineTo(x + t - d, y + s); context.lineTo(x + t - d - s, y + this.segmentWidth); context.lineTo(x + this.segmentWidth + d, y + this.segmentWidth); context.fill(); var x = xPos; var y = 0; context.fillStyle = this.getSegmentColor_$d(c, null, '02356789abcdefgiopqrstz\@'); context.beginPath(); context.moveTo(x + this.digitWidth - this.segmentWidth - d, y + this.segmentWidth); context.lineTo(x + t + d + s, y + this.segmentWidth); context.lineTo(x + t + d, y + s); context.lineTo(x + t + d + s, y); switch (this.cornerType) { case SegmentDisplay_$d.SymmetricCorner: context.lineTo(x + this.digitWidth - this.segmentWidth - d, y); context.lineTo(x + this.digitWidth - s - d, y + s); break; case SegmentDisplay_$d.SquaredCorner: context.lineTo(x + this.digitWidth - this.segmentWidth, y); context.lineTo(x + this.digitWidth - s - e, y + s - e); break; default: context.lineTo(x + this.digitWidth - this.segmentWidth, y); context.quadraticCurveTo(x + this.digitWidth - this.segmentWidth + g, y, x + this.digitWidth - this.segmentWidth + f, y + this.segmentWidth - f - d); } context.fill(); } else { var x = xPos; var y = 0; context.fillStyle = this.getSegmentColor_$d(c, '02356789acefp', '02356789abcdefgiopqrstz\@'); context.beginPath(); switch (this.cornerType) { case SegmentDisplay_$d.SymmetricCorner: context.moveTo(x + s + d, y + s); context.lineTo(x + this.segmentWidth + d, y); context.lineTo(x + this.digitWidth - this.segmentWidth - d, y); context.lineTo(x + this.digitWidth - s - d, y + s); break; case SegmentDisplay_$d.SquaredCorner: context.moveTo(x + s + e, y + s - e); context.lineTo(x + this.segmentWidth, y); context.lineTo(x + this.digitWidth - this.segmentWidth, y); context.lineTo(x + this.digitWidth - s - e, y + s - e); break; default: context.moveTo(x + this.segmentWidth - f, y + this.segmentWidth - f - d); context.quadraticCurveTo(x + this.segmentWidth - g, y, x + this.segmentWidth, y); context.lineTo(x + this.digitWidth - this.segmentWidth, y); context.quadraticCurveTo(x + this.digitWidth - this.segmentWidth + g, y, x + this.digitWidth - this.segmentWidth + f, y + this.segmentWidth - f - d); } context.lineTo(x + this.digitWidth - this.segmentWidth - d, y + this.segmentWidth); context.lineTo(x + this.segmentWidth + d, y + this.segmentWidth); context.fill(); } // draw segment b x = xPos + this.digitWidth - this.segmentWidth; y = 0; context.fillStyle = this.getSegmentColor_$d(c, '01234789adhpy', '01234789abdhjmnopqruwy'); context.beginPath(); switch (this.cornerType) { case SegmentDisplay_$d.SymmetricCorner: context.moveTo(x + s, y + s + d); context.lineTo(x + this.segmentWidth, y + this.segmentWidth + d); break; case SegmentDisplay_$d.SquaredCorner: context.moveTo(x + s + e, y + s + e); context.lineTo(x + this.segmentWidth, y + this.segmentWidth); break; default: context.moveTo(x + f + d, y + this.segmentWidth - f); context.quadraticCurveTo(x + this.segmentWidth, y + this.segmentWidth - g, x + this.segmentWidth, y + this.segmentWidth); } context.lineTo(x + this.segmentWidth, y + h + this.segmentWidth - d); context.lineTo(x + s, y + h + this.segmentWidth + s - d); context.lineTo(x, y + h + this.segmentWidth - d); context.lineTo(x, y + this.segmentWidth + d); context.fill(); // draw segment c x = xPos + this.digitWidth - this.segmentWidth; y = h + this.segmentWidth; context.fillStyle = this.getSegmentColor_$d(c, '013456789abdhnouy', '01346789abdghjmnoqsuw\@', '%'); context.beginPath(); context.moveTo(x, y + this.segmentWidth + d); context.lineTo(x + s, y + s + d); context.lineTo(x + this.segmentWidth, y + this.segmentWidth + d); context.lineTo(x + this.segmentWidth, y + h + this.segmentWidth - d); switch (this.cornerType) { case SegmentDisplay_$d.SymmetricCorner: context.lineTo(x + s, y + h + this.segmentWidth + s - d); context.lineTo(x, y + h + this.segmentWidth - d); break; case SegmentDisplay_$d.SquaredCorner: context.lineTo(x + s + e, y + h + this.segmentWidth + s - e); context.lineTo(x, y + h + this.segmentWidth - d); break; default: context.quadraticCurveTo(x + this.segmentWidth, y + h + this.segmentWidth + g, x + f + d, y + h + this.segmentWidth + f); context.lineTo(x, y + h + this.segmentWidth - d); } context.fill(); // draw segment d (d1 and d2 for 16 segments) if (this.segmentCount == 16) { x = xPos; y = this.digitHeight - this.segmentWidth; context.fillStyle = this.getSegmentColor_$d(c, null, '0235689bcdegijloqsuz_=\@'); context.beginPath(); context.moveTo(x + this.segmentWidth + d, y); context.lineTo(x + t - d - s, y); context.lineTo(x + t - d, y + s); context.lineTo(x + t - d - s, y + this.segmentWidth); switch (this.cornerType) { case SegmentDisplay_$d.SymmetricCorner: context.lineTo(x + this.segmentWidth + d, y + this.segmentWidth); context.lineTo(x + s + d, y + s); break; case SegmentDisplay_$d.SquaredCorner: context.lineTo(x + this.segmentWidth, y + this.segmentWidth); context.lineTo(x + s + e, y + s + e); break; default: context.lineTo(x + this.segmentWidth, y + this.segmentWidth); context.quadraticCurveTo(x + this.segmentWidth - g, y + this.segmentWidth, x + this.segmentWidth - f, y + f + d); context.lineTo(x + this.segmentWidth - f, y + f + d); } context.fill(); x = xPos; y = this.digitHeight - this.segmentWidth; context.fillStyle = this.getSegmentColor_$d(c, null, '0235689bcdegijloqsuz_=\@', '%'); context.beginPath(); context.moveTo(x + t + d + s, y + this.segmentWidth); context.lineTo(x + t + d, y + s); context.lineTo(x + t + d + s, y); context.lineTo(x + this.digitWidth - this.segmentWidth - d, y); switch (this.cornerType) { case SegmentDisplay_$d.SymmetricCorner: context.lineTo(x + this.digitWidth - s - d, y + s); context.lineTo(x + this.digitWidth - this.segmentWidth - d, y + this.segmentWidth); break; case SegmentDisplay_$d.SquaredCorner: context.lineTo(x + this.digitWidth - s - e, y + s + e); context.lineTo(x + this.digitWidth - this.segmentWidth, y + this.segmentWidth); break; default: context.lineTo(x + this.digitWidth - this.segmentWidth + f, y + f + d); context.quadraticCurveTo(x + this.digitWidth - this.segmentWidth + g, y + this.segmentWidth, x + this.digitWidth - this.segmentWidth, y + this.segmentWidth); } context.fill(); } else { x = xPos; y = this.digitHeight - this.segmentWidth; context.fillStyle = this.getSegmentColor_$d(c, '0235689bcdelotuy_', '0235689bcdegijloqsuz_=\@'); context.beginPath(); context.moveTo(x + this.segmentWidth + d, y); context.lineTo(x + this.digitWidth - this.segmentWidth - d, y); switch (this.cornerType) { case SegmentDisplay_$d.SymmetricCorner: context.lineTo(x + this.digitWidth - s - d, y + s); context.lineTo(x + this.digitWidth - this.segmentWidth - d, y + this.segmentWidth); context.lineTo(x + this.segmentWidth + d, y + this.segmentWidth); context.lineTo(x + s + d, y + s); break; case SegmentDisplay_$d.SquaredCorner: context.lineTo(x + this.digitWidth - s - e, y + s + e); context.lineTo(x + this.digitWidth - this.segmentWidth, y + this.segmentWidth); context.lineTo(x + this.segmentWidth, y + this.segmentWidth); context.lineTo(x + s + e, y + s + e); break; default: context.lineTo(x + this.digitWidth - this.segmentWidth + f, y + f + d); context.quadraticCurveTo(x + this.digitWidth - this.segmentWidth + g, y + this.segmentWidth, x + this.digitWidth - this.segmentWidth, y + this.segmentWidth); context.lineTo(x + this.segmentWidth, y + this.segmentWidth); context.quadraticCurveTo(x + this.segmentWidth - g, y + this.segmentWidth, x + this.segmentWidth - f, y + f + d); context.lineTo(x + this.segmentWidth - f, y + f + d); } context.fill(); } // draw segment e x = xPos; y = h + this.segmentWidth; context.fillStyle = this.getSegmentColor_$d(c, '0268abcdefhlnoprtu', '0268acefghjklmnopqruvw\@'); context.beginPath(); context.moveTo(x, y + this.segmentWidth + d); context.lineTo(x + s, y + s + d); context.lineTo(x + this.segmentWidth, y + this.segmentWidth + d); context.lineTo(x + this.segmentWidth, y + h + this.segmentWidth - d); switch (this.cornerType) { case SegmentDisplay_$d.SymmetricCorner: context.lineTo(x + s, y + h + this.segmentWidth + s - d); context.lineTo(x, y + h + this.segmentWidth - d); break; case SegmentDisplay_$d.SquaredCorner: context.lineTo(x + s - e, y + h + this.segmentWidth + s - d + e); context.lineTo(x, y + h + this.segmentWidth); break; default: context.lineTo(x + this.segmentWidth - f - d, y + h + this.segmentWidth + f); context.quadraticCurveTo(x, y + h + this.segmentWidth + g, x, y + h + this.segmentWidth); } context.fill(); // draw segment f x = xPos; y = 0; context.fillStyle = this.getSegmentColor_$d(c, '045689abcefhlpty', '045689acefghklmnopqrsuvwy\@', '%'); context.beginPath(); context.moveTo(x + this.segmentWidth, y + this.segmentWidth + d); context.lineTo(x + this.segmentWidth, y + h + this.segmentWidth - d); context.lineTo(x + s, y + h + this.segmentWidth + s - d); context.lineTo(x, y + h + this.segmentWidth - d); switch (this.cornerType) { case SegmentDisplay_$d.SymmetricCorner: context.lineTo(x, y + this.segmentWidth + d); context.lineTo(x + s, y + s + d); break; case SegmentDisplay_$d.SquaredCorner: context.lineTo(x, y + this.segmentWidth); context.lineTo(x + s - e, y + s + e); break; default: context.lineTo(x, y + this.segmentWidth); context.quadraticCurveTo(x, y + this.segmentWidth - g, x + this.segmentWidth - f - d, y + this.segmentWidth - f); context.lineTo(x + this.segmentWidth - f - d, y + this.segmentWidth - f); } context.fill(); // draw segment g for 7 segments if (this.segmentCount == 7) { x = xPos; y = (this.digitHeight - this.segmentWidth) / 2.0; context.fillStyle = this.getSegmentColor_$d(c, '2345689abdefhnoprty-='); context.beginPath(); context.moveTo(x + s + d, y + s); context.lineTo(x + this.segmentWidth + d, y); context.lineTo(x + this.digitWidth - this.segmentWidth - d, y); context.lineTo(x + this.digitWidth - s - d, y + s); context.lineTo(x + this.digitWidth - this.segmentWidth - d, y + this.segmentWidth); context.lineTo(x + this.segmentWidth + d, y + this.segmentWidth); context.fill(); } // draw inner segments for the fourteen- and sixteen-segment-display if (this.segmentCount != 7) { // draw segment g1 x = xPos; y = (this.digitHeight - this.segmentWidth) / 2.0; context.fillStyle = this.getSegmentColor_$d(c, null, '2345689aefhkprsy-+*=', '%'); context.beginPath(); context.moveTo(x + s + d, y + s); context.lineTo(x + this.segmentWidth + d, y); context.lineTo(x + t - d - s, y); context.lineTo(x + t - d, y + s); context.lineTo(x + t - d - s, y + this.segmentWidth); context.lineTo(x + this.segmentWidth + d, y + this.segmentWidth); context.fill(); // draw segment g2 x = xPos; y = (this.digitHeight - this.segmentWidth) / 2.0; context.fillStyle = this.getSegmentColor_$d(c, null, '234689abefghprsy-+*=\@', '%'); context.beginPath(); context.moveTo(x + t + d, y + s); context.lineTo(x + t + d + s, y); context.lineTo(x + this.digitWidth - this.segmentWidth - d, y); context.lineTo(x + this.digitWidth - s - d, y + s); context.lineTo(x + this.digitWidth - this.segmentWidth - d, y + this.segmentWidth); context.lineTo(x + t + d + s, y + this.segmentWidth); context.fill(); // draw segment j x = xPos + t - s; y = 0; context.fillStyle = this.getSegmentColor_$d(c, null, 'bdit+*', '%'); context.beginPath(); if (this.segmentCount == 14) { context.moveTo(x, y + this.segmentWidth + this.segmentDistance); context.lineTo(x + this.segmentWidth, y + this.segmentWidth + this.segmentDistance); } else { context.moveTo(x, y + this.segmentWidth + d); context.lineTo(x + s, y + s + d); context.lineTo(x + this.segmentWidth, y + this.segmentWidth + d); } context.lineTo(x + this.segmentWidth, y + h + this.segmentWidth - d); context.lineTo(x + s, y + h + this.segmentWidth + s - d); context.lineTo(x, y + h + this.segmentWidth - d); context.fill(); // draw segment m x = xPos + t - s; y = this.digitHeight; context.fillStyle = this.getSegmentColor_$d(c, null, 'bdity+*\@', '%'); context.beginPath(); if (this.segmentCount == 14) { context.moveTo(x, y - this.segmentWidth - this.segmentDistance); context.lineTo(x + this.segmentWidth, y - this.segmentWidth - this.segmentDistance); } else { context.moveTo(x, y - this.segmentWidth - d); context.lineTo(x + s, y - s - d); context.lineTo(x + this.segmentWidth, y - this.segmentWidth - d); } context.lineTo(x + this.segmentWidth, y - h - this.segmentWidth + d); context.lineTo(x + s, y - h - this.segmentWidth - s + d); context.lineTo(x, y - h - this.segmentWidth + d); context.fill(); // draw segment h x = xPos + this.segmentWidth; y = this.segmentWidth; context.fillStyle = this.getSegmentColor_$d(c, null, 'mnx\\\\*'); context.beginPath(); context.moveTo(x + this.segmentDistance, y + this.segmentDistance); context.lineTo(x + this.segmentDistance + r, y + this.segmentDistance); context.lineTo(x + w - this.segmentDistance , y + h - this.segmentDistance - r); context.lineTo(x + w - this.segmentDistance , y + h - this.segmentDistance); context.lineTo(x + w - this.segmentDistance - r , y + h - this.segmentDistance); context.lineTo(x + this.segmentDistance, y + this.segmentDistance + r); context.fill(); // draw segment k x = xPos + w + 2.0 * this.segmentWidth; y = this.segmentWidth; context.fillStyle = this.getSegmentColor_$d(c, null, '0kmvxz/*', '%'); context.beginPath(); context.moveTo(x + w - this.segmentDistance, y + this.segmentDistance); context.lineTo(x + w - this.segmentDistance, y + this.segmentDistance + r); context.lineTo(x + this.segmentDistance + r, y + h - this.segmentDistance); context.lineTo(x + this.segmentDistance, y + h - this.segmentDistance); context.lineTo(x + this.segmentDistance, y + h - this.segmentDistance - r); context.lineTo(x + w - this.segmentDistance - r, y + this.segmentDistance); context.fill(); // draw segment l x = xPos + w + 2.0 * this.segmentWidth; y = h + 2.0 * this.segmentWidth; context.fillStyle = this.getSegmentColor_$d(c, null, '5knqrwx\\\\*'); context.beginPath(); context.moveTo(x + this.segmentDistance, y + this.segmentDistance); context.lineTo(x + this.segmentDistance + r, y + this.segmentDistance); context.lineTo(x + w - this.segmentDistance , y + h - this.segmentDistance - r); context.lineTo(x + w - this.segmentDistance , y + h - this.segmentDistance); context.lineTo(x + w - this.segmentDistance - r , y + h - this.segmentDistance); context.lineTo(x + this.segmentDistance, y + this.segmentDistance + r); context.fill(); // draw segment n x = xPos + this.segmentWidth; y = h + 2.0 * this.segmentWidth; context.fillStyle = this.getSegmentColor_$d(c, null, '0vwxz/*', '%'); context.beginPath(); context.moveTo(x + w - this.segmentDistance, y + this.segmentDistance); context.lineTo(x + w - this.segmentDistance, y + this.segmentDistance + r); context.lineTo(x + this.segmentDistance + r, y + h - this.segmentDistance); context.lineTo(x + this.segmentDistance, y + h - this.segmentDistance); context.lineTo(x + this.segmentDistance, y + h - this.segmentDistance - r); context.lineTo(x + w - this.segmentDistance - r, y + this.segmentDistance); context.fill(); } return this.digitDistance + this.digitWidth; case '.': context.fillStyle = (c == '#') || (c == '.') ? this.colorOn : this.colorOff; this.drawPoint(context, xPos, this.digitHeight - this.segmentWidth, this.segmentWidth); return this.digitDistance + this.segmentWidth; case ':': context.fillStyle = (c == '#') || (c == ':') ? this.colorOn : this.colorOff; var y = (this.digitHeight - this.segmentWidth) / 2.0 - this.segmentWidth; this.drawPoint(context, xPos, y, this.segmentWidth); this.drawPoint(context, xPos, y + 2.0 * this.segmentWidth, this.segmentWidth); return this.digitDistance + this.segmentWidth; default: return this.digitDistance; } }; SegmentDisplay_$d.prototype.drawPoint = function(context, x1, y1, size) { var x2 = x1 + size; var y2 = y1 + size; var d = size / 4.0; context.beginPath(); context.moveTo(x2 - d, y1); context.quadraticCurveTo(x2, y1, x2, y1 + d); context.lineTo(x2, y2 - d); context.quadraticCurveTo(x2, y2, x2 - d, y2); context.lineTo(x1 + d, y2); context.quadraticCurveTo(x1, y2, x1, y2 - d); context.lineTo(x1, y1 + d); context.quadraticCurveTo(x1, y1, x1 + d, y1); context.fill(); }; SegmentDisplay_$d.prototype.getSegmentColor_$d = function(c, charSet7, charSet14, charSet16) { if (c == '#') { return this.colorOn; } else { switch (this.segmentCount) { case 7: return (charSet7.indexOf(c) == -1) ? this.colorOff : this.colorOn; case 14: return (charSet14.indexOf(c) == -1) ? this.colorOff : this.colorOn; case 16: var pattern = charSet14 + (charSet16 === undefined ? '' : charSet16); return (pattern.indexOf(c) == -1) ? this.colorOff : this.colorOn; default: return this.colorOff; } } }; var display_$d = new SegmentDisplay_$d('display_$d'); display_$d.pattern = '$ddp '; display_$d.cornerType = 2; display_$d.displayType = 7; display_$d.displayAngle = 9; display_$d.digitHeight = 20; display_$d.digitWidth = 12; display_$d.digitDistance = 2; display_$d.segmentWidth = 3; display_$d.segmentDistance = 0.5; //display_$d.colorOn = 'rgba(0, 0, 0, 0.9)'; display_$d.colorOn = '#$dcd'; display_$d.colorOff = 'rgba(0, 0, 0, 0.1)'; animate_$d(); function animate_$d() { var time = new Date(); var hours = time.getHours(); var minutes = time.getMinutes(); var seconds = time.getSeconds(); var value = $ddt; display_$d.setValue(value); window.setTimeout('animate_$d()', 100); } </script> </body> </html> "; } ################################################################ sub Watches_station($) { 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'"); # Bahnhofsuhr aus http://www.3quarks.com/de/Bahnhofsuhr/ return " <html> <body> <canvas id='clock_$d' $hattr> </canvas> <script> // clock body (Uhrgehäuse) StationClock_$d.NoBody = 0; StationClock_$d.SmallWhiteBody = 1; StationClock_$d.RoundBody = 2; StationClock_$d.RoundGreenBody = 3; StationClock_$d.SquareBody = 4; StationClock_$d.ViennaBody = 5; // stroke dial (Zifferblatt) StationClock_$d.NoDial = 0; StationClock_$d.GermanHourStrokeDial = 1; StationClock_$d.GermanStrokeDial = 2; StationClock_$d.AustriaStrokeDial = 3; StationClock_$d.SwissStrokeDial = 4; StationClock_$d.ViennaStrokeDial = 5; //clock hour hand (Stundenzeiger) StationClock_$d.PointedHourHand = 1; StationClock_$d.BarHourHand = 2; StationClock_$d.SwissHourHand = 3; StationClock_$d.ViennaHourHand = 4; //clock minute hand (Minutenzeiger) StationClock_$d.PointedMinuteHand = 1; StationClock_$d.BarMinuteHand = 2; StationClock_$d.SwissMinuteHand = 3; StationClock_$d.ViennaMinuteHand = 4; //clock second hand (Sekundenzeiger) StationClock_$d.NoSecondHand = 0; StationClock_$d.BarSecondHand = 1; StationClock_$d.HoleShapedSecondHand = 2; StationClock_$d.NewHoleShapedSecondHand = 3; StationClock_$d.SwissSecondHand = 4; // clock boss (Zeigerabdeckung) StationClock_$d.NoBoss = 0; StationClock_$d.BlackBoss = 1; StationClock_$d.RedBoss = 2; StationClock_$d.ViennaBoss = 3; // minute hand behavoir StationClock_$d.CreepingMinuteHand = 0; StationClock_$d.BouncingMinuteHand = 1; StationClock_$d.ElasticBouncingMinuteHand = 2; // second hand behavoir StationClock_$d.CreepingSecondHand = 0; StationClock_$d.BouncingSecondHand = 1; StationClock_$d.ElasticBouncingSecondHand = 2; StationClock_$d.OverhastySecondHand = 3; function StationClock_$d(clockId_$d) { this.clockId_$d = clockId_$d; this.radius = 0; // hour offset this.hourOffset = 0; // clock body this.body = StationClock_$d.RoundBody; this.bodyShadowColor = 'rgba(0,0,0,0.5)'; this.bodyShadowOffsetX = 0.03; this.bodyShadowOffsetY = 0.03; this.bodyShadowBlur = 0.06; // body dial this.dial = StationClock_$d.GermanStrokeDial; this.dialColor = 'rgb(60,60,60)'; // clock hands this.hourHand = StationClock_$d.PointedHourHand; this.minuteHand = StationClock_$d.PointedMinuteHand; this.secondHand = StationClock_$d.HoleShapedSecondHand; this.handShadowColor = 'rgba(0,0,0,0.3)'; this.handShadowOffsetX = 0.03; this.handShadowOffsetY = 0.03; this.handShadowBlur = 0.04; // clock colors this.hourHandColor = 'rgb(0,0,0)'; this.minuteHandColor = 'rgb(0,0,0)'; this.secondHandColor = 'rgb(200,0,0)'; // clock boss this.boss = StationClock_$d.NoBoss; this.bossShadowColor = 'rgba(0,0,0,0.2)'; this.bossShadowOffsetX = 0.02; this.bossShadowOffsetY = 0.02; this.bossShadowBlur = 0.03; // hand behavoir this.minuteHandBehavoir = StationClock_$d.CreepingMinuteHand; this.secondHandBehavoir = StationClock_$d.OverhastySecondHand; // hand animation this.minuteHandAnimationStep = 0; this.secondHandAnimationStep = 0; this.lastMinute = 0; this.lastSecond = 0; }; StationClock_$d.prototype.draw = function() { var clock_$d = document.getElementById(this.clockId_$d); if (clock_$d) { var context = clock_$d.getContext('2d'); if (context) { this.radius = 0.75 * (Math.min(clock_$d.width, clock_$d.height) / 2); // clear canvas and set new origin context.clearRect(0, 0, clock_$d.width, clock_$d.height); context.save(); context.translate(clock_$d.width / 2, clock_$d.height / 2); // draw body if (this.body != StationClock_$d.NoStrokeBody) { context.save(); switch (this.body) { case StationClock_$d.SmallWhiteBody: this.fillCircle(context, 'rgb(255,255,255)', 0, 0, 1); break; case StationClock_$d.RoundBody: this.fillCircle(context, 'rgb(255,255,255)', 0, 0, 1.1); context.save(); this.setShadow(context, this.bodyShadowColor, this.bodyShadowOffsetX, this.bodyShadowOffsetY, this.bodyShadowBlur); this.strokeCircle(context, 'rgb(0,0,0)', 0, 0, 1.1, 0.07); context.restore(); break; case StationClock_$d.RoundGreenBody: this.fillCircle(context, 'rgb(235,236,212)', 0, 0, 1.1); context.save(); this.setShadow(context, this.bodyShadowColor, this.bodyShadowOffsetX, this.bodyShadowOffsetY, this.bodyShadowBlur); this.strokeCircle(context, 'rgb(180,180,180)', 0, 0, 1.1, 0.2); context.restore(); this.strokeCircle(context, 'rgb(29,84,31)', 0, 0, 1.15, 0.1); context.save(); this.setShadow(context, 'rgba(235,236,212,100)', -0.02, -0.02, 0.09); this.strokeCircle(context, 'rgb(76,128,110)', 0, 0, 1.1, 0.08); context.restore(); break; case StationClock_$d.SquareBody: context.save(); this.setShadow(context, this.bodyShadowColor, this.bodyShadowOffsetX, this.bodyShadowOffsetY, this.bodyShadowBlur); this.fillSquare(context, 'rgb(237,235,226)', 0, 0, 2.4); this.strokeSquare(context, 'rgb(38,106,186)', 0, 0, 2.32, 0.16); context.restore(); context.save(); this.setShadow(context, this.bodyShadowColor, this.bodyShadowOffsetX, this.bodyShadowOffsetY, this.bodyShadowBlur); this.strokeSquare(context, 'rgb(42,119,208)', 0, 0, 2.24, 0.08); context.restore(); break; case StationClock_$d.ViennaBody: context.save(); this.fillSymmetricPolygon(context, 'rgb(156,156,156)', [[-1.2,1.2],[-1.2,-1.2]],0.1); this.fillPolygon(context, 'rgb(156,156,156)', 0,1.2 , 1.2,1.2 , 1.2,0); this.fillCircle(context, 'rgb(255,255,255)', 0, 0, 1.05, 0.08); this.strokeCircle(context, 'rgb(0,0,0)', 0, 0, 1.05, 0.01); this.strokeCircle(context, 'rgb(100,100,100)', 0, 0, 1.1, 0.01); this.fillPolygon(context, 'rgb(100,100,100)', 0.45,1.2 , 1.2,1.2 , 1.2,0.45); this.fillPolygon(context, 'rgb(170,170,170)', 0.45,-1.2 , 1.2,-1.2 , 1.2,-0.45); this.fillPolygon(context, 'rgb(120,120,120)', -0.45,1.2 , -1.2,1.2 , -1.2,0.45); this.fillPolygon(context, 'rgb(200,200,200)', -0.45,-1.2 , -1.2,-1.2 , -1.2,-0.45); this.strokeSymmetricPolygon(context, 'rgb(156,156,156)', [[-1.2,1.2],[-1.2,-1.2]],0.01); this.fillPolygon(context, 'rgb(255,0,0)', 0.05,-0.6 , 0.15,-0.6 , 0.15,-0.45 , 0.05,-0.45); this.fillPolygon(context, 'rgb(255,0,0)', -0.05,-0.6 , -0.15,-0.6 , -0.15,-0.45 , -0.05,-0.45); this.fillPolygon(context, 'rgb(255,0,0)', 0.05,-0.35 , 0.15,-0.35 , 0.15,-0.30 , 0.10,-0.20 , 0.05,-0.20); this.fillPolygon(context, 'rgb(255,0,0)', -0.05,-0.35 , -0.15,-0.35 , -0.15,-0.30 , -0.10,-0.20 , -0.05,-0.20); context.restore(); break; } context.restore(); } // draw dial for (var i = 0; i < 60; i++) { context.save(); context.rotate(i * Math.PI / 30); switch (this.dial) { case StationClock_$d.SwissStrokeDial: if ((i % 5) == 0) { this.strokeLine(context, this.dialColor, 0.0, -1.0, 0.0, -0.75, 0.07); } else { this.strokeLine(context, this.dialColor, 0.0, -1.0, 0.0, -0.92, 0.026); } break; case StationClock_$d.AustriaStrokeDial: if ((i % 5) == 0) { this.fillPolygon(context, this.dialColor, -0.04, -1.0, 0.04, -1.0, 0.03, -0.78, -0.03, -0.78); } else { this.strokeLine(context, this.dialColor, 0.0, -1.0, 0.0, -0.94, 0.02); } break; case StationClock_$d.GermanStrokeDial: if ((i % 15) == 0) { this.strokeLine(context, this.dialColor, 0.0, -1.0, 0.0, -0.70, 0.08); } else if ((i % 5) == 0) { this.strokeLine(context, this.dialColor, 0.0, -1.0, 0.0, -0.76, 0.08); } else { this.strokeLine(context, this.dialColor, 0.0, -1.0, 0.0, -0.92, 0.036); } break; case StationClock_$d.GermanHourStrokeDial: if ((i % 15) == 0) { this.strokeLine(context, this.dialColor, 0.0, -1.0, 0.0, -0.70, 0.10); } else if ((i % 5) == 0) { this.strokeLine(context, this.dialColor, 0.0, -1.0, 0.0, -0.74, 0.08); } break; case StationClock_$d.ViennaStrokeDial: if ((i % 15) == 0) { this.fillPolygon(context, this.dialColor, 0.7,-0.1, 0.6,0, 0.7,0.1, 1,0.03, 1,-0.03); } else if ((i % 5) == 0) { this.fillPolygon(context, this.dialColor, 0.85,-0.06, 0.78,0, 0.85,0.06, 1,0.03, 1,-0.03); } this.fillCircle(context, this.dialColor, 0.0, -1.0, 0.03); break; } context.restore(); } // get current time var time = new Date(); var millis = time.getMilliseconds() / 1000.0; var seconds = time.getSeconds(); var minutes = time.getMinutes(); var hours = time.getHours() + this.hourOffset; // draw hour hand context.save(); context.rotate(hours * Math.PI / 6 + minutes * Math.PI / 360); this.setShadow(context, this.handShadowColor, this.handShadowOffsetX, this.handShadowOffsetY, this.handShadowBlur); switch (this.hourHand) { case StationClock_$d.BarHourHand: this.fillPolygon(context, this.hourHandColor, -0.05, -0.6, 0.05, -0.6, 0.05, 0.15, -0.05, 0.15); break; case StationClock_$d.PointedHourHand: this.fillPolygon(context, this.hourHandColor, 0.0, -0.6, 0.065, -0.53, 0.065, 0.19, -0.065, 0.19, -0.065, -0.53); break; case StationClock_$d.SwissHourHand: this.fillPolygon(context, this.hourHandColor, -0.05, -0.6, 0.05, -0.6, 0.065, 0.26, -0.065, 0.26); break; case StationClock_$d.ViennaHourHand: this.fillSymmetricPolygon(context, this.hourHandColor, [[-0.02,-0.72],[-0.08,-0.56],[-0.15,-0.45],[-0.06,-0.30],[-0.03,0],[-0.1,0.2],[-0.05,0.23],[-0.03,0.2]]); } context.restore(); // draw minute hand context.save(); switch (this.minuteHandBehavoir) { case StationClock_$d.CreepingMinuteHand: context.rotate((minutes + seconds / 60) * Math.PI / 30); break; case StationClock_$d.BouncingMinuteHand: context.rotate(minutes * Math.PI / 30); break; case StationClock_$d.ElasticBouncingMinuteHand: if (this.lastMinute != minutes) { this.minuteHandAnimationStep = 3; this.lastMinute = minutes; } context.rotate((minutes + this.getAnimationOffset(this.minuteHandAnimationStep)) * Math.PI / 30); this.minuteHandAnimationStep--; break; } this.setShadow(context, this.handShadowColor, this.handShadowOffsetX, this.handShadowOffsetY, this.handShadowBlur); switch (this.minuteHand) { case StationClock_$d.BarMinuteHand: this.fillPolygon(context, this.minuteHandColor, -0.05, -0.9, 0.035, -0.9, 0.035, 0.23, -0.05, 0.23); break; case StationClock_$d.PointedMinuteHand: this.fillPolygon(context, this.minuteHandColor, 0.0, -0.93, 0.045, -0.885, 0.045, 0.23, -0.045, 0.23, -0.045, -0.885); break; case StationClock_$d.SwissMinuteHand: this.fillPolygon(context, this.minuteHandColor, -0.035, -0.93, 0.035, -0.93, 0.05, 0.25, -0.05, 0.25); break; case StationClock_$d.ViennaMinuteHand: this.fillSymmetricPolygon(context, this.minuteHandColor, [[-0.02,-0.98],[-0.09,-0.7],[-0.03,0],[-0.05,0.2],[-0.01,0.4]]); } context.restore(); // draw second hand context.save(); switch (this.secondHandBehavoir) { case StationClock_$d.OverhastySecondHand: context.rotate(Math.min((seconds + millis) * (60.0 / 58.5), 60.0) * Math.PI / 30); break; case StationClock_$d.CreepingSecondHand: context.rotate((seconds + millis) * Math.PI / 30); break; case StationClock_$d.BouncingSecondHand: context.rotate(seconds * Math.PI / 30); break; case StationClock_$d.ElasticBouncingSecondHand: if (this.lastSecond != seconds) { this.secondHandAnimationStep = 3; this.lastSecond = seconds; } context.rotate((seconds + this.getAnimationOffset(this.secondHandAnimationStep)) * Math.PI / 30); this.secondHandAnimationStep--; break; } this.setShadow(context, this.handShadowColor, this.handShadowOffsetX, this.handShadowOffsetY, this.handShadowBlur); switch (this.secondHand) { case StationClock_$d.BarSecondHand: this.fillPolygon(context, this.secondHandColor, -0.006, -0.92, 0.006, -0.92, 0.028, 0.23, -0.028, 0.23); break; case StationClock_$d.HoleShapedSecondHand: this.fillPolygon(context, this.secondHandColor, 0.0, -0.9, 0.011, -0.889, 0.01875, -0.6, -0.01875, -0.6, -0.011, -0.889); this.fillPolygon(context, this.secondHandColor, 0.02, -0.4, 0.025, 0.22, -0.025, 0.22, -0.02, -0.4); this.strokeCircle(context, this.secondHandColor, 0, -0.5, 0.083, 0.066); break; case StationClock_$d.NewHoleShapedSecondHand: this.fillPolygon(context, this.secondHandColor, 0.0, -0.95, 0.015, -0.935, 0.0187, -0.65, -0.0187, -0.65, -0.015, -0.935); this.fillPolygon(context, this.secondHandColor, 0.022, -0.45, 0.03, 0.27, -0.03, 0.27, -0.022, -0.45); this.strokeCircle(context, this.secondHandColor, 0, -0.55, 0.085, 0.07); break; case StationClock_$d.SwissSecondHand: this.strokeLine(context, this.secondHandColor, 0.0, -0.6, 0.0, 0.35, 0.026); this.fillCircle(context, this.secondHandColor, 0, -0.64, 0.1); break; case StationClock_$d.ViennaSecondHand: this.strokeLine(context, this.secondHandColor, 0.0, -0.6, 0.0, 0.35, 0.026); this.fillCircle(context, this.secondHandColor, 0, -0.64, 0.1); break; } context.restore(); // draw clock boss if (this.boss != StationClock_$d.NoBoss) { context.save(); this.setShadow(context, this.bossShadowColor, this.bossShadowOffsetX, this.bossShadowOffsetY, this.bossShadowBlur); switch (this.boss) { case StationClock_$d.BlackBoss: this.fillCircle(context, 'rgb(0,0,0)', 0, 0, 0.1); break; case StationClock_$d.RedBoss: this.fillCircle(context, 'rgb(220,0,0)', 0, 0, 0.06); break; case StationClock_$d.ViennaBoss: this.fillCircle(context, 'rgb(0,0,0)', 0, 0, 0.07); break; } context.restore(); } context.restore(); } } }; StationClock_$d.prototype.getAnimationOffset = function(animationStep) { switch (animationStep) { case 3: return 0.2; case 2: return -0.1; case 1: return 0.05; } return 0; }; StationClock_$d.prototype.setShadow = function(context, color, offsetX, offsetY, blur) { if (color) { context.shadowColor = color; context.shadowOffsetX = this.radius * offsetX; context.shadowOffsetY = this.radius * offsetY; context.shadowBlur = this.radius * blur; } }; StationClock_$d.prototype.fillCircle = function(context, color, x, y, radius) { if (color) { context.beginPath(); context.fillStyle = color; context.arc(x * this.radius, y * this.radius, radius * this.radius, 0, 2 * Math.PI, true); context.fill(); } }; StationClock_$d.prototype.strokeCircle = function(context, color, x, y, radius, lineWidth) { if (color) { context.beginPath(); context.strokeStyle = color; context.lineWidth = lineWidth * this.radius; context.arc(x * this.radius, y * this.radius, radius * this.radius, 0, 2 * Math.PI, true); context.stroke(); } }; StationClock_$d.prototype.fillSquare = function(context, color, x, y, size) { if (color) { context.fillStyle = color; context.fillRect((x - size / 2) * this.radius, (y -size / 2) * this.radius, size * this.radius, size * this.radius); } }; StationClock_$d.prototype.strokeSquare = function(context, color, x, y, size, lineWidth) { if (color) { context.strokeStyle = color; context.lineWidth = lineWidth * this.radius; context.strokeRect((x - size / 2) * this.radius, (y -size / 2) * this.radius, size * this.radius, size * this.radius); } }; StationClock_$d.prototype.strokeLine = function(context, color, x1, y1, x2, y2, width) { if (color) { context.beginPath(); context.strokeStyle = color; context.moveTo(x1 * this.radius, y1 * this.radius); context.lineTo(x2 * this.radius, y2 * this.radius); context.lineWidth = width * this.radius; context.stroke(); } }; StationClock_$d.prototype.fillPolygon = function(context, color, x1, y1, x2, y2, x3, y3, x4, y4, x5, y5) { if (color) { context.beginPath(); context.fillStyle = color; context.moveTo(x1 * this.radius, y1 * this.radius); context.lineTo(x2 * this.radius, y2 * this.radius); context.lineTo(x3 * this.radius, y3 * this.radius); context.lineTo(x4 * this.radius, y4 * this.radius); if ((x5 != undefined) && (y5 != undefined)) { context.lineTo(x5 * this.radius, y5 * this.radius); } context.lineTo(x1 * this.radius, y1 * this.radius); context.fill(); } }; StationClock_$d.prototype.fillSymmetricPolygon = function(context, color, points) { context.beginPath(); context.fillStyle = color; context.moveTo(points[0][0] * this.radius, points[0][1] * this.radius); for (var i = 1; i < points.length; i++) { context.lineTo(points[i][0] * this.radius, points[i][1] * this.radius); } for (var i = points.length - 1; i >= 0; i--) { context.lineTo(0 - points[i][0] * this.radius, points[i][1] * this.radius); } context.lineTo(points[0][0] * this.radius, points[0][1] * this.radius); context.fill(); }; StationClock_$d.prototype.strokeSymmetricPolygon = function(context, color, points, width) { context.beginPath(); context.strokeStyle = color; context.moveTo(points[0][0] * this.radius, points[0][1] * this.radius); for (var i = 1; i < points.length; i++) { context.lineTo(points[i][0] * this.radius, points[i][1] * this.radius); } for (var i = points.length - 1; i >= 0; i--) { context.lineTo(0 - points[i][0] * this.radius, points[i][1] * this.radius); } context.lineTo(points[0][0] * this.radius, points[0][1] * this.radius); context.lineWidth = width * this.radius; context.stroke(); }; var clock_$d = new StationClock_$d('clock_$d'); clock_$d.body = StationClock_$d.$sbody; clock_$d.dial = StationClock_$d.$ssd; clock_$d.hourHand = StationClock_$d.$shh; clock_$d.minuteHand = StationClock_$d.$smh; clock_$d.secondHand = StationClock_$d.$ssh; clock_$d.boss = StationClock_$d.$sb; clock_$d.minuteHandBehavoir = StationClock_$d.$mhb; clock_$d.secondHandBehavoir = StationClock_$d.$shb; function animate(clock_$d) { clock_$d.draw(); window.setTimeout(function(){animate(clock_$d)}, 50); } animate(clock_$d); </script> </body> </html> "; } ################################################################ sub Watches_modern($) { 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'"); # moderne Uhr aus https://www.w3schools.com/graphics/canvas_clock_start.asp return " <html> <body> <canvas id='canvas_$d' $hattr style='background-color:#$bgc'> </canvas> <script> var canvas_$d = document.getElementById('canvas_$d'); var ctx_$d = canvas_$d.getContext('2d'); var radius_$d = canvas_$d.height / 2; ctx_$d.translate(radius_$d, radius_$d); radius_$d = radius_$d * 0.90 setInterval(drawClock_$d, 1000); function drawClock_$d() { drawFace_$d(ctx_$d, radius_$d); drawnumbers_$d(ctx_$d, radius_$d); drawTime_$d(ctx_$d, radius_$d); } function drawFace_$d(ctx_$d, radius_$d) { var grad_$d; ctx_$d.beginPath(); ctx_$d.arc(0, 0, radius_$d, 0, 2*Math.PI); ctx_$d.fillStyle = '#$facec'; // Füllung Uhr ctx_$d.fill(); grad_$d = ctx_$d.createRadialGradient(0,0,radius_$d*0.95, 0,0,radius_$d*1.05); grad_$d.addColorStop(0, '#$hc'); // Farbe Zeiger und innere Ringgrenze grad_$d.addColorStop(0.5, '#$fr'); // Farbe Ziffernblattring grad_$d.addColorStop(1, '#$fre'); // Farbe äußere Ringgrenze ctx_$d.strokeStyle = grad_$d; ctx_$d.lineWidth = radius_$d*0.1; ctx_$d.stroke(); ctx_$d.beginPath(); ctx_$d.arc(0, 0, radius_$d*0.1, 0, 2*Math.PI); ctx_$d.fillStyle = '#$fc'; // Farbe Ziffern und Zeigerwelle ctx_$d.fill(); } function drawnumbers_$d(ctx_$d, radius_$d) { var ang_$d; var num_$d; ctx_$d.font = radius_$d*0.15 + 'px arial'; ctx_$d.textBaseline='middle'; ctx_$d.textAlign='center'; for(num_$d = 1; num_$d < 13; num_$d++){ ang_$d = num_$d * Math.PI / 6; ctx_$d.rotate(ang_$d); ctx_$d.translate(0, -radius_$d*0.85); ctx_$d.rotate(-ang_$d); ctx_$d.fillText(num_$d.toString(), 0, 0); ctx_$d.rotate(ang_$d); ctx_$d.translate(0, radius_$d*0.85); ctx_$d.rotate(-ang_$d); } } function drawTime_$d(ctx_$d, radius_$d){ var now_$d = new Date(); var hour_$d = now_$d.getHours(); var minute_$d = now_$d.getMinutes(); var second_$d = now_$d.getSeconds(); //hour_$d hour_$d=hour_$d%12; hour_$d=(hour_$d*Math.PI/6)+ (minute_$d*Math.PI/(6*60))+ (second_$d*Math.PI/(360*60)); drawHand_$d(ctx_$d, hour_$d, radius_$d*0.5, radius_$d*0.07); //minute_$d minute_$d=(minute_$d*Math.PI/30)+(second_$d*Math.PI/(30*60)); drawHand_$d(ctx_$d, minute_$d, radius_$d*0.8, radius_$d*0.07); // second_$d second_$d=(second_$d*Math.PI/30); drawHand_$d(ctx_$d, second_$d, radius_$d*0.9, radius_$d*0.02); } function drawHand_$d(ctx_$d, pos, length, width) { ctx_$d.beginPath(); ctx_$d.lineWidth = width; ctx_$d.lineCap = 'round'; ctx_$d.moveTo(0,0); ctx_$d.rotate(pos); ctx_$d.lineTo(0, -length); ctx_$d.stroke(); ctx_$d.rotate(-pos); } </script> </body> </html> "; } 1; =pod =item helper =item summary create a watch in modern, station or digital style =item summary_DE erstellt eine Uhr: Modern, Bahnhofsuhr oder Digital =begin html <a name="Watches"></a> <h3>Watches</h3> <br> Das Modul Watches stellt eine Modern-, Bahnhofs- oder Digitalanzeige als Device zur Verfügung. <br> Die Uhren basieren auf Skripten dieser Seiten: <br> <a href='https://www.w3schools.com/graphics/canvas_clock_start.asp'>moderne Uhr</a>, <a href='http://www.3quarks.com/de/Bahnhofsuhr/'>Bahnhofsuhr</a>, <a href='http://www.3quarks.com/de/Segmentanzeige/'>Digitalanzeige</a> <br> <br> <ul> <a name="WatchesDefine"></a> <b>Define</b> <ul> define <name> Watches [Modern | Station | Digital] <br><br> <table> <colgroup> <col width=5%> <col width=95%> </colgroup> <tr><td> <b>Modern</b> </td><td>: erstellt eine analoge Uhr im modernen Design </td></tr> <tr><td> <b>Station</b> </td><td>: erstellt eine Bahnhofsuhr </td></tr> <tr><td> <b>Digital</b> </td><td>: erstellt eine Digitalanzeige (Uhr oder Text) </td></tr> </table> <br> <br> </ul> <a name="WatchesSet"></a> <b>Set</b> <ul>N/A</ul><br> <a name="WatchesGet"></a> <b>Get</b> <ul>N/A</ul><br> <a name="WatchesAttr"></a> <b>Attribute</b> <br><br> <ul> <ul> <a name="disable"></a> <li><b>disable</b><br> Aktiviert/deaktiviert das Device. </li> <br> <a name="hideDisplayName"></a> <li><b>hideDisplayName</b><br> Verbirgt den Device/Alias-Namen (Link zur Detailansicht). </li> <br> <a name="htmlattr"></a> <li><b>htmlattr</b><br> Zusätzliche HTML Tags zur Größenänderung de Uhr. <br><br> <ul> <b>Beispiel: </b><br> attr <name> htmlattr width="125" height="125" <br> </ul> </li> <br> </ul> Die nachfolgenden Attribute sind spezifisch für einen Uhrentyp zu setzen. <br> <br> <b>Model: Modern</b> <br><br> <ul> <a name="modernColorBackground"></a> <li><b>modernColorBackground</b><br> Hintergrundfarbe der Uhr. </li> <br> <a name="modernColorFace"></a> <li><b>modernColorFace</b><br> Einfärbung des Ziffernblattes. </li> <br> <a name="modernColorFigure"></a> <li><b>modernColorFigure</b><br> Farbe der Ziffern im Ziffernblatt und der Zeigerachsabdeckung. </li> <br> <a name="modernColorHand"></a> <li><b>modernColorHand</b><br> Farbe der UhrenZeiger. </li> <br> <a name="modernColorRing"></a> <li><b>modernColorRing</b><br> Farbe des Ziffernblattrahmens. </li> <br> <a name="modernColorRingEdge"></a> <li><b>modernColorRingEdge</b><br> Farbe des Außenringes vom Ziffernblattrahmen. </li> <br> </ul> <br> <b>Model: Station</b> <br><br> <ul> <a name="stationBody"></a> <li><b>stationBody</b><br> Art des Uhrengehäuses. </li> <br> <a name="stationBoss"></a> <li><b>stationBoss</b><br> Art und Farbe der Zeigerachsabdeckung. </li> <br> <a name="stationHourHand"></a> <li><b>stationHourHand</b><br> Art des Stundenzeigers. </li> <br> <a name="stationMinuteHand"></a> <li><b>stationMinuteHand</b><br> Art des Minutenzeigers. </li> <br> <a name="stationMinuteHandBehavoir"></a> <li><b>stationMinuteHandBehavoir</b><br> Verhalten des Minutenzeigers. </li> <br> <a name="stationSecondHand"></a> <li><b>stationSecondHand</b><br> Art des Sekundenzeigers. </li> <br> <a name="stationSecondHandBehavoir"></a> <li><b>stationSecondHandBehavoir</b><br> Verhalten des Sekundenzeigers. </li> <br> <a name="stationStrokeDial"></a> <li><b>stationStrokeDial</b><br> Auswahl des Ziffernblattes. </li> <br> </ul> <br> <b>Model: Digital</b> <br><br> <ul> <a name="digitalColorBackground"></a> <li><b>digitalColorBackground</b><br> Digitaluhr Hintergrundfarbe. </li> <br> <a name="digitalColorDigits"></a> <li><b>digitalColorDigits</b><br> Farbe der Balkenanzeige in einer Digitaluhr. </li> <br> <a name="digitalDisplayPattern"></a> <li><b>digitalDisplayPattern [text | watch]</b><br> Umschaltung der Digitalanzeige zwischen Uhrenmodus (default) und Textanzeige. Der anzuzeigende Text kann mit dem Attribut "digitalDisplayText" definiert werden. <br><br> <ul> <table> <colgroup> <col width=5%> <col width=95%> </colgroup> <tr><td> <b>watch</b> </td><td>: Anzeige einer Uhr </td></tr> <tr><td> <b>text</b> </td><td>: Anzeige eines definierbaren Textes </td></tr> </table> </ul> <br> </li> <a name="digitalDisplayText"></a> <li><b>digitalDisplayText</b><br> Ist das Attribut "digitalDisplayPattern = text" gesetzt, kann mit "digitalDisplayText" der anzuzeigende Text eingestellt werden. Im Default wird "Play" anzgezeigt. <br> Mit der Siebensegmentanzeige können Ziffern, Bindestrich, Unterstrich und die Buchstaben A, b, C, d, E, F, H, L, n, o, P, r, t, U und Y angezeigt werden. So lassen sich außer Zahlen auch kurze Texte wie „Error“, „HELP“, „run“ oder „PLAY“ anzeigen. </li> <br> </ul> </ul> </ul> =end html =begin html_DE <a name="Watches"></a> <h3>Watches</h3> <br> Das Modul Watches stellt eine Modern-, Bahnhofs- oder Digitalanzeige als Device zur Verfügung. <br> Die Uhren basieren auf Skripten dieser Seiten: <br> <a href='https://www.w3schools.com/graphics/canvas_clock_start.asp'>moderne Uhr</a>, <a href='http://www.3quarks.com/de/Bahnhofsuhr/'>Bahnhofsuhr</a>, <a href='http://www.3quarks.com/de/Segmentanzeige/'>Digitalanzeige</a> <br> <br> <ul> <a name="WatchesDefine"></a> <b>Define</b> <ul> define <name> Watches [Modern | Station | Digital] <br><br> <table> <colgroup> <col width=5%> <col width=95%> </colgroup> <tr><td> <b>Modern</b> </td><td>: erstellt eine analoge Uhr im modernen Design </td></tr> <tr><td> <b>Station</b> </td><td>: erstellt eine Bahnhofsuhr </td></tr> <tr><td> <b>Digital</b> </td><td>: erstellt eine Digitalanzeige (Uhr oder Text) </td></tr> </table> <br> <br> </ul> <a name="WatchesSet"></a> <b>Set</b> <ul>N/A</ul><br> <a name="WatchesGet"></a> <b>Get</b> <ul>N/A</ul><br> <a name="WatchesAttr"></a> <b>Attribute</b> <br><br> <ul> <ul> <a name="disable"></a> <li><b>disable</b><br> Aktiviert/deaktiviert das Device. </li> <br> <a name="hideDisplayName"></a> <li><b>hideDisplayName</b><br> Verbirgt den Device/Alias-Namen (Link zur Detailansicht). </li> <br> <a name="htmlattr"></a> <li><b>htmlattr</b><br> Zusätzliche HTML Tags zur Größenänderung de Uhr. <br><br> <ul> <b>Beispiel: </b><br> attr <name> htmlattr width="125" height="125" <br> </ul> </li> <br> </ul> Die nachfolgenden Attribute sind spezifisch für einen Uhrentyp zu setzen. <br> <br> <b>Model: Modern</b> <br><br> <ul> <a name="modernColorBackground"></a> <li><b>modernColorBackground</b><br> Hintergrundfarbe der Uhr. </li> <br> <a name="modernColorFace"></a> <li><b>modernColorFace</b><br> Einfärbung des Ziffernblattes. </li> <br> <a name="modernColorFigure"></a> <li><b>modernColorFigure</b><br> Farbe der Ziffern im Ziffernblatt und der Zeigerachsabdeckung. </li> <br> <a name="modernColorHand"></a> <li><b>modernColorHand</b><br> Farbe der UhrenZeiger. </li> <br> <a name="modernColorRing"></a> <li><b>modernColorRing</b><br> Farbe des Ziffernblattrahmens. </li> <br> <a name="modernColorRingEdge"></a> <li><b>modernColorRingEdge</b><br> Farbe des Außenringes vom Ziffernblattrahmen. </li> <br> </ul> <br> <b>Model: Station</b> <br><br> <ul> <a name="stationBody"></a> <li><b>stationBody</b><br> Art des Uhrengehäuses. </li> <br> <a name="stationBoss"></a> <li><b>stationBoss</b><br> Art und Farbe der Zeigerachsabdeckung. </li> <br> <a name="stationHourHand"></a> <li><b>stationHourHand</b><br> Art des Stundenzeigers. </li> <br> <a name="stationMinuteHand"></a> <li><b>stationMinuteHand</b><br> Art des Minutenzeigers. </li> <br> <a name="stationMinuteHandBehavoir"></a> <li><b>stationMinuteHandBehavoir</b><br> Verhalten des Minutenzeigers. </li> <br> <a name="stationSecondHand"></a> <li><b>stationSecondHand</b><br> Art des Sekundenzeigers. </li> <br> <a name="stationSecondHandBehavoir"></a> <li><b>stationSecondHandBehavoir</b><br> Verhalten des Sekundenzeigers. </li> <br> <a name="stationStrokeDial"></a> <li><b>stationStrokeDial</b><br> Auswahl des Ziffernblattes. </li> <br> </ul> <br> <b>Model: Digital</b> <br><br> <ul> <a name="digitalColorBackground"></a> <li><b>digitalColorBackground</b><br> Digitaluhr Hintergrundfarbe. </li> <br> <a name="digitalColorDigits"></a> <li><b>digitalColorDigits</b><br> Farbe der Balkenanzeige in einer Digitaluhr. </li> <br> <a name="digitalDisplayPattern"></a> <li><b>digitalDisplayPattern [text | watch]</b><br> Umschaltung der Digitalanzeige zwischen Uhrenmodus (default) und Textanzeige. Der anzuzeigende Text kann mit dem Attribut "digitalDisplayText" definiert werden. <br><br> <ul> <table> <colgroup> <col width=5%> <col width=95%> </colgroup> <tr><td> <b>watch</b> </td><td>: Anzeige einer Uhr </td></tr> <tr><td> <b>text</b> </td><td>: Anzeige eines definierbaren Textes </td></tr> </table> </ul> <br> </li> <a name="digitalDisplayText"></a> <li><b>digitalDisplayText</b><br> Ist das Attribut "digitalDisplayPattern = text" gesetzt, kann mit "digitalDisplayText" der anzuzeigende Text eingestellt werden. Im Default wird "Play" anzgezeigt. <br> Mit der Siebensegmentanzeige können Ziffern, Bindestrich, Unterstrich und die Buchstaben A, b, C, d, E, F, H, L, n, o, P, r, t, U und Y angezeigt werden. So lassen sich außer Zahlen auch kurze Texte wie „Error“, „HELP“, „run“ oder „PLAY“ anzeigen. </li> <br> </ul> </ul> </ul> =end html_DE =cut