diff --git a/fhem/contrib/DS_Starter/76_SMAPortal.pm b/fhem/contrib/DS_Starter/76_SMAPortal.pm index a9dba8b35..93aec6e47 100644 --- a/fhem/contrib/DS_Starter/76_SMAPortal.pm +++ b/fhem/contrib/DS_Starter/76_SMAPortal.pm @@ -135,6 +135,7 @@ BEGIN { readingsDelete readingsEndUpdate ReadingsNum + ReadingsTimestamp ReadingsVal RemoveInternalTimer setKeyValue @@ -142,12 +143,19 @@ BEGIN { TimeNow Value json2nameValue + FW_directNotify + FW_ME + FW_subdir + FW_room + FW_detail + FW_wname ) ); } # Versions History intern our %vNotesIntern = ( + "2.0.0" => "03.06.2019 designed for SMAPortalSPG graphics device", "1.8.0" => "27.05.2019 redesign of SMAPortal graphics by Wzut/XGuide ", "1.7.1" => "01.05.2019 PortalAsHtml: use of colored svg-icons possible ", "1.7.0" => "01.05.2019 code change of PortalAsHtml, new attributes \"portalGraphicColor\" and \"portalGraphicStyle\" ", @@ -167,6 +175,7 @@ our %vNotesIntern = ( "1.1.0" => "09.03.2019 make get data more stable, new attribute \"getDataRetries\" ", "1.0.0" => "03.03.2019 initial " ); + # Web instance ############################################################### # SMAPortal Define @@ -279,7 +288,7 @@ sub Set($@) { return "Invalid portal graphic devicetype ! Use one of \"Generation\", \"Consumption\", \"Generation_Consumption\", \"Differential\". " } - $ret = CommandDefine($hash->{CL},"$htmldev weblink htmlCode {FHEM::SMAPortal::PortalAsHtml (\"$name\",\"$htmldev\")}"); + $ret = CommandDefine($hash->{CL},"$htmldev SMAPortalSPG {FHEM::SMAPortal::PortalAsHtml ('$name','$htmldev')}"); return $ret if($ret); CommandAttr($hash->{CL},"$htmldev alias $c"); # Alias setzen @@ -287,33 +296,28 @@ sub Set($@) { $c = "This device provides a praphical output of SMA Sunny Portal values.\n". "The device needs to set attribute \"detailLevel\" in device \"$name\" to level \"4\""; CommandAttr($hash->{CL},"$htmldev comment $c"); - - $c = "color:colorpicker,RGB color2:colorpicker,RGB hours:slider,4,1,24 hour_style show_header:1,0 show_link:1,0 show_night:1,0 maxPV W/kW:W,kW "; - $c .= "height font_size html_start html_end consumers legend_style:none,icon_top,icon_bottom,text_top,text_bottom show_weather:1,0 type:pv,co,pvco,diff "; - $c .= "weather_color:colorpicker,RGB show_diff:no,top,bottom width"; - CommandAttr($hash->{CL},"$htmldev userattr $c"); # es muß nicht unbedingt jedes der möglichen userattr unbedingt vorbesetzt werden # bzw muß überhaupt hier etwas vorbesetzt werden ? # alle Werte enstprechen eh den AttrVal/ AttrNum default Werten - CommandAttr($hash->{CL},"$htmldev hours 24"); - CommandAttr($hash->{CL},"$htmldev icon on"); - CommandAttr($hash->{CL},"$htmldev show_header 1"); - CommandAttr($hash->{CL},"$htmldev show_link 1"); - CommandAttr($hash->{CL},"$htmldev font_size 24"); - CommandAttr($hash->{CL},"$htmldev show_weather 1"); - CommandAttr($hash->{CL},"$htmldev type $type"); # Anzeigetyp setzen + CommandAttr($hash->{CL},"$htmldev hourCount 24"); + CommandAttr($hash->{CL},"$htmldev suggestIcon on"); + CommandAttr($hash->{CL},"$htmldev showHeader 1"); + CommandAttr($hash->{CL},"$htmldev showLink 1"); + CommandAttr($hash->{CL},"$htmldev spaceSize 24"); + CommandAttr($hash->{CL},"$htmldev showWeather 1"); + CommandAttr($hash->{CL},"$htmldev layoutType $type"); # Anzeigetyp setzen # eine mögliche Startfarbe steht beim installiertem f18 Style direkt zur Verfügung # ohne vorhanden f18 Style bestimmt später tr.odd aus der Style css die Anfangsfarbe my $color = %{json2nameValue(AttrVal('WEB','styleData',undef))}{'f18_cols.header'}; if (defined($color)) { - CommandAttr($hash->{CL},"$htmldev color $color"); + CommandAttr($hash->{CL},"$htmldev beamColor $color"); } # zweite Farbe setzen - CommandAttr($hash->{CL},"$htmldev color2 $color2"); + CommandAttr($hash->{CL},"$htmldev beamColor2 $color2"); my $room = AttrVal($name,"room","SMAPortal"); CommandAttr($hash->{CL},"$htmldev room $room"); @@ -518,6 +522,8 @@ sub Attr($$$$) { readingsBeginUpdate($hash); readingsBulkUpdate($hash, "state", $val); readingsEndUpdate($hash, 1); + + InternalTimer(gettimeofday()+2.0, "FHEM::SMAPortal::SPGRefresh", "$name,0,1", 0); } if ($cmd eq "set") { @@ -815,6 +821,9 @@ sub ParseData($) { readingsEndUpdate($hash, 1); delete($hash->{HELPER}{RUNNING_PID}); + SPGRefresh($hash,0,1); + +return; } ################################################################ @@ -828,6 +837,8 @@ sub ParseAborted($) { Log3 ($name, 1, "$name -> BlockingCall $hash->{HELPER}{RUNNING_PID}{fn} pid:$hash->{HELPER}{RUNNING_PID}{pid} $cause"); delete($hash->{HELPER}{RUNNING_PID}); + +return; } ################################################################ @@ -1404,30 +1415,51 @@ return($txt); sub PortalAsHtml ($$) { my ($name,$wlname) = @_; my $hash = $defs{$name}; + my $ret = ""; - return "Device \"$name\" doesn't exist !" if(!$hash); - return "Graphic device \"$wlname\" doesn't exist !" if (!defined($defs{$wlname})); - - my $pv0 = ReadingsNum($name,"L2_ThisHour_PvMeanPower", undef); - if (AttrVal($name, "detailLevel", 1) != 4) { - return "The attribute \"detailLevel\" of device \"$name\" has to be set to level \"4\" !
"; - } elsif (!defined $pv0) { - # Vorschlag : Das und level 4 data weiter unten stehen so verloren auf der Seite - # man könnte die beiden Meldungen noch in eine mini packen - # und mit dem Wert aus attr height in etwa auf die spätere Ausǵabe vorformatieren - return "Awaiting level 2 data ...
"; - } - - my $ret = ""; - my ($i,$icon,$colorv,$colorc,$maxhours,$hourstyle,$wllink,$header,$legend,$legend_txt,$legend_style); + my ($i,$icon,$colorv,$colorc,$maxhours,$hourstyle,$header,$legend,$legend_txt,$legend_style); my ($val,$height,$fsize,$html_start,$html_end,$wlalias,$weather,$colorw,$maxVal,$show_night,$type,$kw); my ($maxDif,$minDif,$maxCon,$v,$z2,$z3,$z4,$show_diff,$width,$w); my $he; # Balkenhöhe my (%pv,%is,%t,%we,%di,%co); my @pgCDev; + + # Kontext des aufrufenden SMAPortalSPG-Devices speichern für Refresh + $hash->{HELPER}{SPGDEV} = $wlname; # Name des aufrufenden SMAPortalSPG-Devices + $hash->{HELPER}{SPGROOM} = $FW_room?$FW_room:""; # Raum aus dem das SMAPortalSPG-Device die Funktion aufrief + $hash->{HELPER}{SPGDETAIL} = $FW_detail?$FW_detail:""; # Name des SMAPortalSPG-Devices (wenn Detailansicht) + + my $dl = AttrVal($name, "detailLevel", 1); + my $pv0 = ReadingsNum($name,"L2_ThisHour_PvMeanPower", undef); + my $pv1 = ReadingsNum($name,"L4_NextHour01_PvMeanPower", undef); + if(!$hash || !defined($defs{$wlname}) || $dl != 4 || !defined $pv0 || !defined $pv1) { + $height = AttrNum($wlname, 'beamHeight', 200); + $ret .= "
"; + $ret .= ""; + $ret .= "
"; + if(!$hash) { + $ret .= "Device \"$name\" doesn't exist !"; + } elsif (!defined($defs{$wlname})) { + $ret .= "Graphic device \"$wlname\" doesn't exist !"; + } elsif ($dl != 4) { + $ret .= "The attribute \"detailLevel\" of device \"$name\" has to be set to level \"4\" !"; + } elsif (!defined $pv0) { + # Vorschlag : Das und level 4 data weiter unten stehen so verloren auf der Seite + # man könnte die beiden Meldungen noch in eine mini packen + # und mit dem Wert aus attr beamHeight in etwa auf die spätere Ausǵabe vorformatieren + $ret .= "Awaiting level 2 data ..."; + } elsif (!defined $pv1) { + $ret .= "Awaiting level 4 data ..."; + } - @pgCDev = split(',',AttrVal($wlname,"consumers","")); # definierte Verbraucher ermitteln - ($legend_style, $legend) = split('_',AttrVal($wlname,'legend_style','icon_top')); + $ret .= ""; + $ret .= ""; + $ret .= "
"; + return $ret; + } + + @pgCDev = split(',',AttrVal($wlname,"consumerList","")); # definierte Verbraucher ermitteln + ($legend_style, $legend) = split('_',AttrVal($wlname,'consumerLegend','icon_top')); $legend = '' if(($legend_style eq 'none') || (!int(@pgCDev))); @@ -1438,38 +1470,37 @@ sub PortalAsHtml ($$) { $legend_txt .= $txt.' '.FW_makeImage($im).' '; } else { my (undef,$co) = split('\@',$im); - $co = '#cccccc' if (!$co); # irgendeine Farbe per default + $co = '#cccccc' if (!$co); # Farbe per default $legend_txt .= ''.$txt.'   '; # hier auch Umbruch erlauben } } } # Parameter f. Anzeige extrahieren - $maxhours = AttrNum($wlname, 'hours', 24); - $hourstyle = AttrVal($wlname, 'hour_style', undef); - $colorv = AttrVal($wlname, 'color', undef); - $colorc = AttrVal($wlname, 'color2', '000000'); # schwarz wenn keine Userauswahl; - $icon = AttrVal($wlname, 'icon', undef); - $html_start = AttrVal($wlname, 'html_start', undef); # beliebige HTML Strings die vor dem Weblink ausgegeben werden - $html_end = AttrVal($wlname, 'html_end', undef); # beliebige HTML Strings die nach dem Weblink ausgegeben werden + $maxhours = AttrNum($wlname, 'hourCount', 24); + $hourstyle = AttrVal($wlname, 'hourStyle', undef); + $colorv = AttrVal($wlname, 'beamColor', undef); + $colorc = AttrVal($wlname, 'beamColor2', '000000'); # schwarz wenn keine Userauswahl; + $icon = AttrVal($wlname, 'suggestIcon', undef); + $html_start = AttrVal($wlname, 'htmlStart', undef); # beliebige HTML Strings die vor der Grafik ausgegeben werden + $html_end = AttrVal($wlname, 'htmlEnd', undef); # beliebige HTML Strings die nach der Grafik ausgegeben werden - $type = AttrVal($wlname, 'type', 'pv'); - $kw = AttrVal($wlname, 'W/kW', 'W'); + $type = AttrVal($wlname, 'layoutType', 'pv'); + $kw = AttrVal($wlname, 'W/kW', 'W'); - $height = AttrNum($wlname, 'height', 200); - $width = AttrNum($wlname, 'width', 6); # zu klein ist nicht problematisch - $w = $width*$maxhours; # gesammte Breite der Ausgabe , WetterIcon brauch ca.34px - $fsize = AttrNum($wlname, 'font_size', 24); - $maxVal = AttrNum($wlname, 'maxPV', 0); # dyn. Anpassung der Balkenhöhe oder statisch ? + $height = AttrNum($wlname, 'beamHeight', 200); + $width = AttrNum($wlname, 'beamWidth', 6); # zu klein ist nicht problematisch + $w = $width*$maxhours; # gesammte Breite der Ausgabe , WetterIcon braucht ca. 34px + $fsize = AttrNum($wlname, 'spaceSize', 24); + $maxVal = AttrNum($wlname, 'maxPV', 0); # dyn. Anpassung der Balkenhöhe oder statisch ? - $show_night = AttrNum($wlname, 'show_night', 0); # alle Balken (Spalten) anzeigen ? - $show_diff = AttrVal($wlname, 'show_diff', 'no'); # zusätzliche Anzeige $di{} in allen Typen - $weather = AttrNum($wlname, 'show_weather', 1); - $colorw = AttrVal($wlname, 'weather_color',undef); + $show_night = AttrNum($wlname, 'showNight', 0); # alle Balken (Spalten) anzeigen ? + $show_diff = AttrVal($wlname, 'showDiff', 'no'); # zusätzliche Anzeige $di{} in allen Typen + $weather = AttrNum($wlname, 'showWeather', 1); + $colorw = AttrVal($wlname, 'weatherColor', undef); - $wlalias = AttrVal($wlname, 'alias', $wlname); - $header = (AttrNum($wlname, 'show_header', 1)) ? 1 : undef; - $wlname = (AttrNum($wlname, 'show_link', 1) == 1) ? $wlname : ''; + $wlalias = AttrVal($wlname, 'alias', $wlname); + $header = (AttrNum($wlname, 'showHeader', 1)) ? 1 : undef; # Icon Erstellung, mit @ ergänzen falls einfärben # Beispiel mit Farbe: $icon = FW_makeImage('light_light_dim_100.svg@green'); @@ -1503,7 +1534,7 @@ sub PortalAsHtml ($$) { # Headerzeile generieren my $alias = AttrVal($name, "alias", "SMA Sunny Portal"); # Linktext als Aliasname oder "SMA Sunny Portal" my $dlink = "$alias"; - $wllink = ($wlname) ? "$wlalias" : ''; + my $lup = ReadingsTimestamp($name, "state", "0000-00-00 00:00:00"); # letzte Updatezeit # Da der Header relativ viele Zeichen hat, müssen Stellen erlaubt werden an denen automatisch umgebrochen werden kann. # Sonst sind schmale Ausgaben nicht von den Balken bzw. deren Anzahl abhängig, sondern allein durch die Breite des Headers bestimmt @@ -1511,19 +1542,22 @@ sub PortalAsHtml ($$) { if ($header) { my ($h1,$h2); if(AttrVal("global","language","EN") eq "DE") { - $h1 = "Prognosedaten[pv] - nächsten 4 Stunden: $pv4h/h / Rest des Tages: $pvRe/h / Morgen: $pvTo/h"; - $h2 = "Prognosedaten[co] - nächsten 4 Stunden: $co4h/h / Rest des Tages: $coRe/h / Morgen: $coTo/h"; + $h1 = "Prognose [pv] - nächste 4 Stunden: $pv4h/h / Rest des Tages: $pvRe/h / Morgen: $pvTo/h"; + $h2 = "Prognose [co] - nächste 4 Stunden: $co4h/h / Rest des Tages: $coRe/h / Morgen: $coTo/h"; + my ($year, $month, $day, $hour, $min, $sec) = $lup =~ /(\d+)-(\d\d)-(\d\d)\s+(.*)/; + $lup = "$3.$2.$1 $4"; } else { - $h1 = "forecast data[pv] - next 4 hours: $pv4h/h / rest of day: $pvRe / tomorrow: $pvTo/h"; - $h2 = "forecast data[co] - next 4 hours: $co4h/h / rest of day: $coRe / tomorrow: $coTo/h"; + $h1 = "forecast data [pv] - next 4 hours: $pv4h/h / rest of day: $pvRe / tomorrow: $pvTo/h"; + $h2 = "forecast data [co] - next 4 hours: $co4h/h / rest of day: $coRe / tomorrow: $coTo/h"; } + $lup = "     (last update: $lup)"; if ($type eq 'pv') { - $header = $dlink.'
'.$h1; + $header = $dlink.' '.$lup.'
'.$h1; } elsif ($type eq 'co') { - $header = $dlink.'
'.$h2; + $header = $dlink.' '.$lup.'
'.$h2; } else { - $header = $dlink.'
'.$h1.'
'.$h2; + $header = $dlink.' '.$lup.'
'.$h1.'
'.$h2; } } @@ -1539,7 +1573,7 @@ sub PortalAsHtml ($$) { if(AttrVal("global","language","EN") eq "DE") { (undef,undef,undef,$t{0}) = ReadingsVal($name,"L2_ThisHour_Time",'0') =~ m/(\d{2}).(\d{2}).(\d{4})\s(\d{2})/; } else { - (undef,undef,undef,$t{0}) = ReadingsVal($name,"L2_ThisHour_Time",'0') =~ m/(\d{4})-(\d{2})-(\d{2})\s(\d{2})/; + (undef,undef,undef,$t{0}) = ReadingsVal($name,"L2_ThisHour_Time",'0') =~ m/(\d{4})-(\d{2})-(\d{2})\s(\d{2})/; } $t{0} = int($t{0}); # zum Rechnen Integer ohne führende Null @@ -1568,13 +1602,13 @@ sub PortalAsHtml ($$) { $end = int($end); #correct the hour for accurate display - if ($start < $t{0}) { #consumption seems to be tomorrow + if ($start < $t{0}) { # consumption seems to be tomorrow $start = 23-$t{0}+$start; } else { $start -= $t{0}; } - if ($end < $t{0}) { #consumption seems to be tomorrow + if ($end < $t{0}) { # consumption seems to be tomorrow $end = 23-$t{0}+$end; } else { $end -= $t{0}; @@ -1590,8 +1624,8 @@ sub PortalAsHtml ($$) { $maxVal = (!$maxVal) ? $pv{0} : $maxVal; # Startwert wenn kein Wert bereits via attr vorgegeben ist $maxCon = $co{0}; # für Typ co - $maxDif = $di{0}; # für Typ dif - $minDif = $di{0}; # für Typ dif + $maxDif = $di{0}; # für Typ diff + $minDif = $di{0}; # für Typ diff foreach $i (1..$maxhours-1) { $pv{$i} = ReadingsNum($name,"L4_NextHour".sprintf("%02d",$i)."_PvMeanPower",0); # Erzeugung @@ -1617,8 +1651,6 @@ sub PortalAsHtml ($$) { $t{$i} = int($t{$i}); # keine führende 0 } - return "Awaiting level 4 data ... " if(!defined $pv{1}); - ###################################### # Tabellen Ausgabe erzeugen @@ -1626,11 +1658,12 @@ sub PortalAsHtml ($$) { # lässt sich durch einbetten in eine zusätzliche Table roomoverview eindämmen # Die Tabelle ist recht schmal angelegt, aber nur so lassen sich Umbrüche erzwingen - $ret = $html_start if (defined($html_start)); + $ret = ""; + $ret .= $html_start if (defined($html_start)); $ret .= ""; - $ret .= ""; + $ret .= "
$wllink
"; $ret .= "
"; - $ret .= "\n"; # das \n erleichtert das lesen der debug Quelltextausgabe + $ret .= "\n
"; # das \n erleichtert das Lesen der debug Quelltextausgabe if ($header) { # Header ausgeben $ret .= ""; @@ -1672,7 +1705,7 @@ sub PortalAsHtml ($$) { foreach $i (0..$maxhours-1) { $val = formatVal6($di{$i},$kw,$we{$i}); - $val = ($di{$i} < 0) ? ''.$val.'' : '+'.$val; # Geschmacksfrage, negativ Zahlen in Fettschrift + $val = ($di{$i} < 0) ? ''.$val.'' : '+'.$val; # negativ Zahlen in Fettschrift $ret .= ""; } $ret .= ""; # freier Platz am Ende @@ -1683,7 +1716,7 @@ sub PortalAsHtml ($$) { foreach $i (0..$maxhours-1) { # Achtung Falle, Division by Zero möglich, # maxVal kann gerade bei kleineren maxhours Ausgaben in der Nacht leicht auf 0 fallen - $height = 200 if (!$height); # Fallback, sollte eigentlich nicht vorkommen, ausser der User setzt das auf 0 + $height = 200 if (!$height); # Fallback, sollte eigentlich nicht vorkommen, außer der User setzt es auf 0 $maxVal = 1 if (!$maxVal); $maxCon = 1 if (!$maxCon); @@ -1736,14 +1769,14 @@ sub PortalAsHtml ($$) { $px_neg = $height - $px_pos; # Rundungsfehler vermeiden } else { # Dynamische hoch/runter Verschiebung der Null-Linie - if ($minDif >= 0 ) { # keine negativen Balken vorhanden, die positiven bekommen den gesammten Raum + if ($minDif >= 0 ) { # keine negativen Balken vorhanden, die Positiven bekommen den gesammten Raum $px_neg = 0; $px_pos = $height; } else { if ($maxDif > 0) { $px_neg = int($height * abs($minDif) / ($maxDif + abs($minDif))); # Wieviel % entfallen auf unten ? $px_pos = $height-$px_neg; # der Rest ist oben - } else { # keine positiven Balken vorhanden, die negativen bekommen den gesammten Raum + } else { # keine positiven Balken vorhanden, die Negativen bekommen den gesammten Raum $px_neg = $height; $px_pos = 0; } @@ -1788,7 +1821,7 @@ sub PortalAsHtml ($$) { if ($v || $show_night) { # Balken nur einfärben wenn der User via Attr eine Farbe vorgibt, sonst bestimmt class odd von TR alleine die Farbe my $style = "style=\"padding-bottom:0px; vertical-align:top; margin-left:auto; margin-right:auto;"; - $style .= (defined($colorv)) ? " background-color:#$colorv\"" : '"'; #" Syntaxhilight :) + $style .= (defined($colorv)) ? " background-color:#$colorv\"" : '"'; # Syntaxhilight $ret .= ""; $ret .= "
$val
"; @@ -1927,6 +1960,7 @@ sub PortalAsHtml ($$) { $ret .= "
"; $ret .= $html_end if (defined($html_end)); + $ret .= ""; return $ret; } @@ -2049,6 +2083,63 @@ return $weather_ids{$id} if(defined($weather_ids{$id})); return 'unknown'; } +###################################################################################################### +# Refresh eines Raumes aus $hash->{HELPER}{SPGROOM} +# bzw. Longpoll von SSCam bzw. eines SMAPortalSPG Devices wenn $hash->{HELPER}{SPGDEV} gefüllt +# $hash, $pload (1=Page reload), SMAPortalSPG-Event (1=Event) +###################################################################################################### +sub SPGRefresh($$$) { + my ($hash,$pload,$lpollspg) = @_; + my $name; + if (ref $hash ne "HASH") { + ($name,$pload,$lpollspg) = split ",",$hash; + $hash = $defs{$name}; + } else { + $name = $hash->{NAME}; + } + my $fpr = 0; + + # Kontext des SMAPortalSPG-Devices speichern für Refresh + my $sd = $hash->{HELPER}{SPGDEV}?$hash->{HELPER}{SPGDEV}:"\"n.a.\""; # Name des aufrufenden SMAPortalSPG-Devices + my $sr = $hash->{HELPER}{SPGROOM}?$hash->{HELPER}{SPGROOM}:"\"n.a.\""; # Raum aus dem das SMAPortalSPG-Device die Funktion aufrief + my $sl = $hash->{HELPER}{SPGDETAIL}?$hash->{HELPER}{SPGDETAIL}:"\"n.a.\""; # Name des SMAPortalSPG-Devices (wenn Detailansicht) + $fpr = AttrVal($hash->{HELPER}{SPGDEV},"forcePageRefresh",0) if($hash->{HELPER}{SPGDEV}); + Log3($name, 4, "$name - Refresh - caller: $sd, callerroom: $sr, detail: $sl, pload: $pload, forcePageRefresh: $fpr, event_Spgdev: $lpollspg"); + + # Page-Reload + if($pload && ($hash->{HELPER}{SPGROOM} && !$hash->{HELPER}{SPGDETAIL} && !$fpr)) { + # trifft zu wenn in einer Raumansicht + my @rooms = split(",",$hash->{HELPER}{SPGROOM}); + foreach (@rooms) { + my $room = $_; + { map { FW_directNotify("FILTER=room=$room", "#FHEMWEB:$_", "location.reload('true')", "") } devspec2array("TYPE=FHEMWEB") } + } + } elsif ($pload && (!$hash->{HELPER}{SPGROOM} || $hash->{HELPER}{SPGDETAIL})) { + # trifft zu bei Detailansicht oder im FLOORPLAN bzw. Dashboard oder wenn Seitenrefresh mit dem + # SMAPortalSPG-Attribut "forcePageRefresh" erzwungen wird + { map { FW_directNotify("#FHEMWEB:$_", "location.reload('true')", "") } devspec2array("TYPE=FHEMWEB") } + } else { + if($fpr) { + { map { FW_directNotify("#FHEMWEB:$_", "location.reload('true')", "") } devspec2array("TYPE=FHEMWEB") } + } + } + + # parentState des SMAPortalSPG-Device updaten + my @spgs = devspec2array("TYPE=SMAPortalSPG"); + my $st = ReadingsVal($name, "state", "initialized"); + foreach(@spgs) { + if($defs{$_}{PARENT} eq $name) { + next if(IsDisabled($defs{$_}{NAME})); + readingsBeginUpdate($defs{$_}); + readingsBulkUpdate($defs{$_},"parentState", $st); + readingsBulkUpdate($defs{$_},"state", "updated"); + readingsEndUpdate($defs{$_}, 1); + } + } + +return; +} + 1; =pod @@ -2076,7 +2167,7 @@ return 'unknown';

SMAPortal

    - Mit diesem Modul können Daten aus dem SMA-Portal abgerufen werden. + Mit diesem Modul können Daten aus dem SMA Sunny Portal abgerufen werden. Momentan sind es:

        @@ -2143,8 +2234,8 @@ return 'unknown'; Erstellt Devices zur grafischen Anzeige der SMA Sunny Portal Prognosedaten in verschiedenen Layouts. Das Attribut "detailLevel" muss auf den Level 4 gesetzt sein. Der Befehl setzt dieses Attribut automatisch auf den benötigten Wert.
        - Mit den Attributen im Abschnitt "Attribute der Grafikdevices" können Erscheinungsbild und - Farbgebung der Prognosedaten in den erstellten Devices angepasst werden. + Mit den "Attributen des Grafikdevices" können Erscheinungsbild und + Farbgebung der Prognosedaten in den erstellten Grafik-Devices angepasst werden.

      @@ -2253,156 +2344,7 @@ return 'unknown';
-
- - - Attribute der Grafikdevices -
    -
    -
      -
    • alias
      - In Verbindung mit "show_link" beliebiger Abzeigename. -
    • -
      - -
    • color
      - Farbauswahl der Balken. -
    • -
      - -
    • color2
      - Farbauswahl der sekundären Balken. Die zweite Farbe ist nur sinnvoll für Anzeigedevice "Generation_Consumption" - (Type pvco) und "Differential" (Type diff). -
    • -
      - -
    • consumers
      - Komma getrennte Liste der am Sunny Home Manager angeschlossenen Geräte in der Form <Verbrauchername>:<Icon>@<Farbe>.
      - Der Name des Verbrauchers muss dabei dem Namen im Reading "L3_<Verbrauchername>_Planned" entsprechen.

      - - Beispiel:
      - Trockner:scene_clothes_dryer@yellow,Waschmaschine:scene_washing_machine@lightgreen,Geschirrspueler:scene_dishwasher@orange -
      -
    • -
      - -
    • font_size <value>
      - Legt fest wieviel Platz über den Balken (bei Anzeigetyp Differential (diff) auch unter diesen) zur Anzeige der Werte - freigehalten wird. Bei Styles die große Fonts benutzen, kann der default-Wert zu klein sein, bzw. u.U. rutscht ein - Balken über die Grundlinie. In diesen Fällen bitte den Wert erhöhen.. (default: 24) -
    • -
      - -
    • height <value>
      - Höhe der Balken in px und damit Bestimmung der gesammten Höhe. - In Verbindung mit hour lassen sich damit auch recht kleine Grafikausgaben erzeugen. (default: 200) -
    • -
      - -
    • hours <4...24>
      - Anzahl der Balken/Stunden. (default: 24) -
    • -
      - -
    • hour_style
      - Format der Zeitangabe.

      - -
        - - - - - -
        nicht gesetzt - nur Stundenangabe ohne Minuten (default)
        :00 - Stunden sowie Minuten zweistellig, z.B. 10:00
        :0 - Stunden sowie Minuten einstellig, z.B. 8:0
        -
      -
    • -
      - -
    • html_start <HTML-String>
      - Angabe eines beliebigen HTML-Strings der vor der generierten Portalgrafik ausgegeben wird. -
    • -
      - -
    • html_end <HTML-String>
      - Angabe eines beliebigen HTML-Strings der nach der generierten Portalgrafik ausgegeben wird. -
    • -
      - -
    • icon
      - Setzt das Icon zur Darstellung der Zeiten mit Verbraucherempfehlung. - Dazu kann ein beliebiges Icon mit Hilfe der Standard "Select Icon"-Funktion (links unten im FHEMWEB) direkt ausgewählt - werden. -
    • - -
      -
    • legend_style <none | icon_top | icon_bottom | text_top | text_bottom>
      - Lage bzw. Art und Weise der angezeigten Consumers Legende. -
    • -
      - -
    • maxPV <0...val>
      - Maximaler Ertrag in einer Stunde zur Berechnung der Balkenhöhe, 0 = dynamisch. (default: 0) -
    • -
      - -
    • show_diff <no | top | bottom>
      - Zusätzliche Anzeige der Differenz "Ertrag - Verbrauch" wie beim Anzeigetyp Differential (diff). (default: no) -
    • -
      - -
    • show_header <1|0>
      - Anzeige der Kopfzeile mit Prognosedaten, Rest des aktuellen Tages und des nächsten Tages (default: 1) -
    • -
      - -
    • show_link <1|0>
      - Anzeige des Device-Detaillinks über der grafischen Ausgabe (default: 1) -
    • -
      - -
    • show_night <1|0>
      - Ebenfalls die Nachtstunden ohne Ertragsprognose anzeigen (default: 0) -
    • -
      - -
    • show_weather <1|0>
      - Wettericons anzeigen. (default: 1) -
    • -
      - -
    • type <pv | co | pvco | diff>
      - Layout der Portalgrafik. (default: pv)

      - -
        - - - - - - -
        pv - Erzeugung
        co - Verbrauch
        pvco - Erzeugung und Verbrauch
        diff - Differenz von Erzeugung und Verbrauch
        -
      -
    • -
      - -
    • W/kW <W | kW>
      - Wertanzeige in W oder in kW auf eine Nachkommastelle gerundet. (default: W) -
    • -
      - -
    • width <value>
      - Breite der Balken in px. ( default: 6 (auto) ) -
    • -
      - -
    • weather_color
      - Farbe der Wetter-Icons. -
    • -
      - -
    -
- +
diff --git a/fhem/contrib/DS_Starter/76_SMAPortalSPG.pm b/fhem/contrib/DS_Starter/76_SMAPortalSPG.pm new file mode 100644 index 000000000..2ae2965f4 --- /dev/null +++ b/fhem/contrib/DS_Starter/76_SMAPortalSPG.pm @@ -0,0 +1,572 @@ +######################################################################################################################## +# $Id: $ +######################################################################################################################### +# 76_SMAPortalSPG.pm +# +# (c) 2019 by Heiko Maaz e-mail: Heiko dot Maaz at t-online dot de +# forked from 98_weblink.pm by Rudolf König +# +# This Module is used by module 76_SMAPortal to create graphic devices. +# It can't be used standalone without any SMAPortal-Device. +# +# 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 . +# +######################################################################################################################### + +package main; + +use strict; +use warnings; +use Time::HiRes qw(gettimeofday); +eval "use FHEM::Meta;1" or my $modMetaAbsent = 1; + +# Versions History intern +our %SMAPortalSPG_vNotesIntern = ( + "1.0.0" => "03.06.2019 initial Version " +); + +################################################################ +sub SMAPortalSPG_Initialize($) { + my ($hash) = @_; + + my $fwd = join(",",devspec2array("TYPE=FHEMWEB:FILTER=STATE=Initialized")); + + $hash->{DefFn} = "SMAPortalSPG_Define"; + $hash->{AttrList} = "autoRefresh:selectnumbers,120,0.2,1800,0,log10 ". + "autoRefreshFW:$fwd ". + "beamColor:colorpicker,RGB ". + "beamColor2:colorpicker,RGB ". + "beamHeight ". + "beamWidth ". + "consumerList ". + "consumerLegend:none,icon_top,icon_bottom,text_top,text_bottom ". + "disable:1,0 ". + "forcePageRefresh:1,0 ". + "hourCount:slider,4,1,24 ". + "hourStyle ". + "maxPV ". + "htmlStart ". + "htmlEnd ". + "showDiff:no,top,bottom ". + "showHeader:1,0 ". + "showLink:1,0 ". + "showNight:1,0 ". + "showWeather:1,0 ". + "spaceSize ". + "suggestIcon ". + "layoutType:pv,co,pvco,diff ". + "W/kW:W,kW ". + "weatherColor:colorpicker,RGB ". + $readingFnAttributes; + $hash->{RenameFn} = "SMAPortalSPG_Rename"; + $hash->{CopyFn} = "SMAPortalSPG_Copy"; + $hash->{FW_summaryFn} = "SMAPortalSPG_FwFn"; + $hash->{FW_detailFn} = "SMAPortalSPG_FwFn"; + $hash->{AttrFn} = "SMAPortalSPG_Attr"; + $hash->{FW_hideDisplayName} = 1; # Forum 88667 + + # $hash->{FW_addDetailToSummary} = 1; + # $hash->{FW_atPageEnd} = 1; # wenn 1 -> kein Longpoll ohne informid in HTML-Tag + + eval { FHEM::Meta::InitMod( __FILE__, $hash ) }; # für Meta.pm (https://forum.fhem.de/index.php/topic,97589.0.html) + +return; +} + +################################################################ +sub SMAPortalSPG_Define($$) { + my ($hash, $def) = @_; + my ($name, $type, $link) = split("[ \t]+", $def, 3); + + if(!$link) { + return "Usage: define SMAPortalSPG "; + } + + my $arg = (split("[()]",$link))[1]; + $arg =~ s/'//g; + $hash->{PARENT} = (split(",",$arg))[0]; + $hash->{HELPER}{MODMETAABSENT} = 1 if($modMetaAbsent); # Modul Meta.pm nicht vorhanden + $hash->{LINK} = $link; + + # Versionsinformationen setzen + SMAPortalSPG_setVersionInfo($hash); + + readingsSingleUpdate($hash,"state", "initialized", 1); # Init für "state" + +return undef; +} + +################################################################ +sub SMAPortalSPG_Rename($$) { + my ($new_name,$old_name) = @_; + my $hash = $defs{$new_name}; + + $hash->{DEF} =~ s/$old_name/$new_name/g; + $hash->{LINK} =~ s/$old_name/$new_name/g; + +return; +} + +################################################################ +sub SMAPortalSPG_Copy($$) { + my ($old_name,$new_name) = @_; + my $hash = $defs{$new_name}; + + $hash->{DEF} =~ s/$old_name/$new_name/g; + $hash->{LINK} =~ s/$old_name/$new_name/g; + +return; +} + +################################################################ +sub SMAPortalSPG_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($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($do == 1) { + my @allrds = keys%{$defs{$name}{READINGS}}; + foreach my $key(@allrds) { + delete($defs{$name}{READINGS}{$key}) if($key ne "state"); + } + } + } + + if($aName eq "icon") { + $_[2] = "suggestIcon"; + } + +return undef; +} + +################################################################ +sub SMAPortalSPG_FwFn($;$$$) { + my ($FW_wname, $d, $room, $pageHash) = @_; # pageHash is set for summaryFn. + my $hash = $defs{$d}; + my $link = $hash->{LINK}; + my $height; + + RemoveInternalTimer($hash); + $hash->{HELPER}{FW} = $FW_wname; + + $link = AnalyzePerlCommand(undef, $link) if($link =~ m/^{(.*)}$/s); + + my $alias = AttrVal($d, "alias", $d); # Linktext als Aliasname oder Devicename setzen + my $dlink = "$alias"; + + my $ret = ""; + if(IsDisabled($d)) { + $height = AttrNum($d, 'beamHeight', 200); + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= ""; + $ret .= "
"; + $ret .= "SMA Portal graphic device $d is disabled"; + $ret .= "
"; + } else { + $ret .= "$dlink
" if(AttrVal($d,"showLink",0)); + $ret .= $link; + } + + # Autorefresh nur des aufrufenden FHEMWEB-Devices + my $al = AttrVal($d, "autoRefresh", 0); + if($al) { + InternalTimer(gettimeofday()+$al, "SMAPortalSPG_refresh", $hash, 0); + Log3($d, 5, "$d - next start of autoRefresh: ".FmtDateTime(gettimeofday()+$al)); + } + +return $ret; +} + +################################################################ +sub SMAPortalSPG_refresh($) { + my ($hash) = @_; + my $d = $hash->{NAME}; + + # Seitenrefresh festgelegt durch SMAPortalSPG-Attribut "autoRefresh" und "autoRefreshFW" + my $rd = AttrVal($d, "autoRefreshFW", $hash->{HELPER}{FW}); + { map { FW_directNotify("#FHEMWEB:$_", "location.reload('true')", "") } $rd } + + my $al = AttrVal($d, "autoRefresh", 0); + if($al) { + InternalTimer(gettimeofday()+$al, "SMAPortalSPG_refresh", $hash, 0); + Log3($d, 5, "$d - next start of autoRefresh: ".FmtDateTime(gettimeofday()+$al)); + } else { + RemoveInternalTimer($hash); + } + +return; +} + +############################################################################################# +# Versionierungen des Moduls setzen +# Die Verwendung von Meta.pm und Packages wird berücksichtigt +############################################################################################# +sub SMAPortalSPG_setVersionInfo($) { + my ($hash) = @_; + my $name = $hash->{NAME}; + + my $v = (sortTopicNum("desc",keys %SMAPortalSPG_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}) { # {x_version} ( nur gesetzt wenn $Id: 49_SMAPortalSPG.pm 19051 2019-03-27 22:10:48Z DS_Starter $ im Kopf komplett! vorhanden ) + $modules{$type}{META}{x_version} =~ s/1.1.1/$v/g; + } else { + $modules{$type}{META}{x_version} = $v; + } + return $@ unless (FHEM::Meta::SetInternals($hash)); # FVERSION wird gesetzt ( nur gesetzt wenn $Id: 49_SMAPortalSPG.pm 19051 2019-03-27 22:10:48Z DS_Starter $ im Kopf komplett! vorhanden ) + 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' ); + } + } else { + # herkömmliche Modulstruktur + $hash->{VERSION} = $v; + } + +return; +} + +1; + +=pod +=item summary Definition of grapic devices by the SMAPortal module +=item summary_DE Erstellung von Grafik-Devices durch das SMAPortal-Modul +=begin html + + +

SMAPortalSPG

+ + Is coming soon. + +=end html +=begin html_DE + + +

SMAPortalSPG

+ +
+Das Modul SMAPortalSPG ist ein mit SMAPortal abgestimmtes Gerätemodul zur Definition von Grafik-Devices.
+ +
    + + Define +

    + +
      + Ein SMAPortal Grafik Device wird durch den SMAPortal Befehl "set <name> createPortalGraphic <Typ>" erstellt. + Siehe auch die Beschreibung zum SMAPortal "createPortalGraphic" Befehl. +

      +
    + + + Set +
      + N/A +
    +
    + + + Get + +
      + N/A +
    +
    + + + Attribute +

    +
      +
        + +
      • alias
        + In Verbindung mit "showLink" beliebiger Abzeigename. +
      • +
        + + +
      • autoRefresh
        + Wenn gesetzt, werden aktive Browserseiten des FHEMWEB-Devices welches das SMAPortalSPG-Device aufgerufen hat, nach der + eingestellten Zeit (Sekunden) neu geladen. Sollen statt dessen Browserseiten eines bestimmten FHEMWEB-Devices neu + geladen werden, kann dieses Device mit dem Attribut "autoRefreshFW" festgelegt werden. +
      • +
        + + +
      • autoRefreshFW
        + Ist "autoRefresh" aktiviert, kann mit diesem Attribut das FHEMWEB-Device bestimmt werden dessen aktive Browserseiten + regelmäßig neu geladen werden sollen. +
      • +
        + + +
      • beamColor
        + Farbauswahl der primären Balken. +
      • +
        + + +
      • beamColor2
        + Farbauswahl der sekundären Balken. Die zweite Farbe ist nur sinnvoll für Anzeigedevice "Generation_Consumption" + (Type pvco) und "Differential" (Type diff). +
      • +
        + + +
      • beamHeight <value>
        + Höhe der Balken in px und damit Bestimmung der gesammten Höhe. + In Verbindung mit hourCount lassen sich damit auch recht kleine Grafikausgaben erzeugen. (default: 200) +
      • +
        + + +
      • beamWidth <value>
        + Breite der Balken in px. ( default: 6 (auto) ) +
      • +
        + + +
      • consumerList
        + Komma getrennte Liste der am Sunny Home Manager angeschlossenen Geräte in der Form <Verbrauchername>:<Icon>@<Farbe>.
        + Sobald die Einschaltung einer der angegebenen Verbraucher geplant ist, wird der geplante Zeitraum in der Grafik + angezeigt. + Der Name des Verbrauchers muss dabei dem Namen im Reading "L3_<Verbrauchername>_Planned" entsprechen.

        + + Beispiel:
        + Trockner:scene_clothes_dryer@yellow,Waschmaschine:scene_washing_machine@lightgreen,Geschirrspueler:scene_dishwasher@orange +
        +
      • +
        + + +
      • consumerLegend <none | icon_top | icon_bottom | text_top | text_bottom>
        + Lage bzw. Art und Weise der angezeigten Consumers Legende. +
      • +
        + + +
      • disable
        + Aktiviert/deaktiviert das Device. +
      • +
        + + +
      • forcePageRefresh
        + Das Attribut wird durch SMAPortal ausgewertet.
        + Wenn gesetzt, wird ein Reload aller Browserseiten mit aktiven FHEMWEB-Verbindungen nach dem Abschluß bestimmter + SMAPortal-Befehle erzwungen. +
      • +
        + + +
      • hourCount <4...24>
        + Anzahl der Balken/Stunden. (default: 24) +
      • +
        + + +
      • hourStyle
        + Format der Zeitangabe.

        + +
          + + + + + +
          nicht gesetzt - nur Stundenangabe ohne Minuten (default)
          :00 - Stunden sowie Minuten zweistellig, z.B. 10:00
          :0 - Stunden sowie Minuten einstellig, z.B. 8:0
          +
        +
      • +
        + + +
      • maxPV <0...val>
        + Maximaler Ertrag in einer Stunde zur Berechnung der Balkenhöhe, 0 = dynamisch. (default: 0) +
      • +
        + + +
      • htmlStart <HTML-String>
        + Angabe eines beliebigen HTML-Strings der vor der generierten Portalgrafik ausgegeben wird. +
      • +
        + + +
      • htmlEnd <HTML-String>
        + Angabe eines beliebigen HTML-Strings der nach der generierten Portalgrafik ausgegeben wird. +
      • +
        + + +
      • showDiff <no | top | bottom>
        + Zusätzliche Anzeige der Differenz "Ertrag - Verbrauch" wie beim Anzeigetyp Differential (diff). (default: no) +
      • +
        + + +
      • showHeader <1|0>
        + Anzeige der Kopfzeile mit Prognosedaten, Rest des aktuellen Tages und des nächsten Tages (default: 1) +
      • +
        + + +
      • showLink <1|0>
        + Anzeige des Device-Detaillinks über der grafischen Ausgabe (default: 1) +
      • +
        + + +
      • showNight <1|0>
        + Ebenfalls die Nachtstunden ohne Ertragsprognose anzeigen (default: 0) +
      • +
        + + +
      • showWeather <1|0>
        + Wettericons anzeigen. (default: 1) +
      • +
        + + +
      • spaceSize <value>
        + Legt fest wieviel Platz in px über den Balken (bei Anzeigetyp Differential (diff) auch unter diesen) zur Anzeige der + Werte freigehalten wird. Bei Styles die große Fonts benutzen, kann der default-Wert zu klein sein, bzw. u.U. rutscht ein + Balken über die Grundlinie. In diesen Fällen bitte den Wert erhöhen. (default: 24) +
      • +
        + + +
      • suggestIcon
        + Setzt das Icon zur Darstellung der Zeiten mit Verbraucherempfehlung. + Dazu kann ein beliebiges Icon mit Hilfe der Standard "Select Icon"-Funktion (links unten im FHEMWEB) direkt ausgewählt + werden. +
      • +
        + + +
      • layoutType <pv | co | pvco | diff>
        + Layout der Portalgrafik. (default: pv)

        + +
          + + + + + + +
          pv - Erzeugung
          co - Verbrauch
          pvco - Erzeugung und Verbrauch
          diff - Differenz von Erzeugung und Verbrauch
          +
        +
      • +
        + + +
      • W/kW <W | kW>
        + Wertanzeige in W oder in kW auf eine Nachkommastelle gerundet. (default: W) +
      • +
        + + +
      • weatherColor
        + Farbe der Wetter-Icons. +
      • +
        + +
      +
    + +
+ +=end html_DE + +=for :application/json;q=META.json 76_SMAPortalSPG.pm +{ + "abstract": "Definition of grapic devices by the SMAPortal module", + "x_lang": { + "de": { + "abstract": "Erstellung von Grafik-Devices durch das SMAPortal-Modul" + } + }, + "keywords": [ + "sma", + "photovoltaik", + "electricity", + "portal", + "smaportal", + "graphics", + "longpoll", + "refresh" + ], + "version": "v1.1.1", + "release_status": "testing", + "author": [ + "Heiko Maaz " + ], + "x_fhem_maintainer": [ + "DS_Starter" + ], + "x_fhem_maintainer_github": [ + "nasseeder1" + ], + "prereqs": { + "runtime": { + "requires": { + "FHEM": 5.00918799, + "perl": 5.014, + "Time::HiRes": 0 + }, + "recommends": { + "FHEM::Meta": 0 + }, + "suggests": { + } + } + }, + "resources": { + "repository": { + "x_dev": { + "type": "svn", + "url": "https://svn.fhem.de/trac/browser/trunk/fhem/contrib/DS_Starter", + "web": "https://svn.fhem.de/trac/browser/trunk/fhem/contrib/DS_Starter/76_SMAPortalSPG.pm", + "x_branch": "dev", + "x_filepath": "fhem/contrib/", + "x_raw": "https://svn.fhem.de/fhem/trunk/fhem/contrib/DS_Starter/76_SMAPortalSPG.pm" + } + } + } +} +=end :application/json;q=META.json + +=cut