diff --git a/fhem/contrib/DS_Starter/76_SolarForecast.pm b/fhem/contrib/DS_Starter/76_SolarForecast.pm
index c8533e561..04be7ff5c 100644
--- a/fhem/contrib/DS_Starter/76_SolarForecast.pm
+++ b/fhem/contrib/DS_Starter/76_SolarForecast.pm
@@ -120,6 +120,7 @@ BEGIN {
# Versions History intern
my %vNotesIntern = (
+ "0.56.0" => "11.09.2021 new Attr flowGraphicShowConsumer, extend calc consumer power consumption ",
"0.55.3" => "08.09.2021 add powerthreshold to etotal key ",
"0.55.2" => "08.09.2021 minor fixes, use Color ",
"0.55.1" => "05.09.2021 delete invalid consumer index, Forum: https://forum.fhem.de/index.php/topic,117864.msg1173219.html#msg1173219 ",
@@ -507,9 +508,10 @@ my $defcmode = "can";
my $caicondef = 'light_light_dim_100@gold'; # default consumerAdviceIcon
my $defflowGSize = 300; # default flowGraphicSize
+my $defpopercent = 0.5; # Standard % aktuelle Leistung an nominaler Leistung gemäß Typenschild
# Default CSS-Style
-my $cssdef = qq{.flowg.text { stroke: none; fill: gray; } \n}.
+my $cssdef = qq{.flowg.text { stroke: none; fill: gray; font-size: 32px;} \n}.
qq{.flowg.sun_active { stroke: orange; fill: orange; } \n}.
qq{.flowg.sun_inactive { stroke: gray; fill: gray; } \n}.
qq{.flowg.bat25 { stroke: red; fill: red; } \n}.
@@ -583,6 +585,7 @@ sub Initialize {
"disable:1,0 ".
"flowGraphicSize ".
"flowGraphicAnimate:1,0 ".
+ "flowGraphicShowConsumer:1,0 ".
"follow70percentRule:1,dynamic,0 ".
"forcePageRefresh:1,0 ".
"graphicSelect:both,flow,forecast,none ".
@@ -2167,6 +2170,10 @@ sub _specialActivities {
deleteConsumerPlanning ($hash, $c);
my $calias = ConsumerVal ($hash, $c, "alias", "");
Log3 ($name, 3, qq{$name - Consumer planning of "$calias" deleted});
+
+ $data{$type}{$name}{consumers}{$c}{minutesOn} = 0;
+ $data{$type}{$name}{consumers}{$c}{numberDayStarts} = 0;
+ $data{$type}{$name}{consumers}{$c}{onoff} = "off";
}
deleteReadingspec ($hash, "consumer.*_planned.*");
@@ -2602,61 +2609,116 @@ sub _manageConsumerData {
my $paref = shift;
my $hash = $paref->{hash};
my $name = $paref->{name};
+ my $t = $paref->{t}; # aktuelle Zeit
my $chour = $paref->{chour};
my $day = $paref->{day};
my $daref = $paref->{daref};
my $nhour = $chour+1;
- my $type = $hash->{TYPE};
-
- my $acref = $data{$type}{$name}{consumers};
+ my $type = $hash->{TYPE};
- for my $c (sort{$a<=>$b} keys %{$acref}) {
- my $consumer = $acref->{$c}{name};
- my $alias = $acref->{$c}{alias};
+ for my $c (sort{$a<=>$b} keys %{$data{$type}{$name}{consumers}}) {
+ my $consumer = ConsumerVal ($hash, $c, "name", "");
+ my $alias = ConsumerVal ($hash, $c, "alias", "");
## aktuelle Leistung auslesen
##############################
- my $paread = $acref->{$c}{rpcurr};
- my $up = $acref->{$c}{upcurr};
+ my $paread = ConsumerVal ($hash, $c, "rpcurr", "");
+ my $up = ConsumerVal ($hash, $c, "upcurr", "");
+ my $pcurr = 0;
+
if($paread) {
- my $eup = $up =~ /^kW$/xi ? 1000 : 1;
- my $pcurr = ReadingsNum ($consumer, $paread, 0) * $eup;
+ my $eup = $up =~ /^kW$/xi ? 1000 : 1;
+ $pcurr = ReadingsNum ($consumer, $paread, 0) * $eup;
push @$daref, "consumer${c}_currentPower<>". $pcurr." W";
}
- else {
- deleteReadingspec ($hash, "consumer${c}_currentPower");
- }
## Verbrauch auslesen + speichern
##################################
- my $enread = $acref->{$c}{retotal};
- my $u = $acref->{$c}{uetotal};
+ my $pthreshold = 0;
+ my $enread = ConsumerVal ($hash, $c, "retotal", "");
+ my $u = ConsumerVal ($hash, $c, "uetotal", "");
+
if($enread) {
- my $eu = $u =~ /^kWh$/xi ? 1000 : 1;
- my $etot = ReadingsNum ($consumer, $enread, 0) * $eu; # Summe Energieverbrauch des Verbrauchers
- my $ehist = HistoryVal ($hash, $day, sprintf("%02d",$nhour), "csmt${c}", undef); # gespeicherter Totalverbrauch
- my $pthreshold = ConsumerVal ($hash, $c, "powerthreshold", 0); # Schwellenwert (Wh pro Stunde) ab der ein Verbraucher als aktiv gewertet wird
+ my $eu = $u =~ /^kWh$/xi ? 1000 : 1;
+ my $etot = ReadingsNum ($consumer, $enread, 0) * $eu; # Summe Energieverbrauch des Verbrauchers
+ my $ehist = HistoryVal ($hash, $day, sprintf("%02d",$nhour), "csmt${c}", undef); # gespeicherter Totalverbrauch
+ $pthreshold = ConsumerVal ($hash, $c, "powerthreshold", 0); # Schwellenwert (Wh pro Stunde) ab der ein Verbraucher als aktiv gewertet wird
+
+ ## aktuelle Leitung ermitteln wenn kein Reading d. aktuellen Leistung verfügbar
+ ################################################################################
+ if(!$paread){
+ my $timespan = $t - ConsumerVal ($hash, $c, "old_etottime", $t);
+ my $delta = $etot - ConsumerVal ($hash, $c, "old_etotal", $etot);
+ $pcurr = sprintf("%.6f", $delta / (3600 * $timespan)) if($delta > 0); # Einheitenformel beachten !!: W = Wh / (3600 * s)
+
+ $data{$type}{$name}{consumers}{$c}{old_etotal} = $etot;
+ $data{$type}{$name}{consumers}{$c}{old_etottime} = $t;
+
+ push @$daref, "consumer${c}_currentPower<>". $pcurr." W";
+ }
+ else {
+ deleteReadingspec ($hash, "consumer${c}_currentPower");
+ }
if(defined $ehist && $etot >= $ehist && ($etot - $ehist) >= $pthreshold) {
my $consumerco = $etot - $ehist;
$consumerco += HistoryVal ($hash, $day, sprintf("%02d",$nhour), "csme${c}", 0);
$paref->{consumerco} = $consumerco;
- $paref->{nhour} = sprintf("%02d",$nhour);
+ $paref->{nhour} = sprintf("%02d",$nhour); # Verbrauch des Consumers aktuelle Stunde
$paref->{histname} = "csme${c}";
setPVhistory ($paref);
delete $paref->{histname};
}
- $paref->{consumerco} = $etot;
+ $paref->{consumerco} = $etot; # Totalverbrauch des Verbrauchers
$paref->{nhour} = sprintf("%02d",$nhour);
$paref->{histname} = "csmt${c}";
setPVhistory ($paref);
delete $paref->{histname};
}
+ ## Verbraucher - Laufzeit und Zyklen pro Tag ermitteln
+ ## Laufzeit (in Minuten) wird pro Stunde erfasst
+ ## bei Tageswechsel Rücksetzen in _specialActivities
+ #######################################################
+ my $nompower = ConsumerVal ($hash, $c, "power", 0); # nominale Leistung lt. Typenschild
+ my $rpcurr = ConsumerVal ($hash, $c, "rpcurr", ""); # Reading für akt. Verbrauch angegeben ?
+ my $swstate = ConsumerVal ($hash, $c, "state", "undef"); # Schaltzustand des Consumerdevices
+
+ if (!$rpcurr && $swstate eq "on") { # Workaround wenn Verbraucher ohne Leistungsmessung
+ $pcurr = $nompower;
+ }
+
+ my $currpowerpercent = $pcurr;
+ $currpowerpercent = (($pcurr / $nompower) * 100) if($nompower > 0);
+
+ $data{$type}{$name}{consumers}{$c}{currpowerpercent} = $currpowerpercent;
+
+ if($pcurr > $pthreshold || $currpowerpercent > $defpopercent) {
+ if($data{$type}{$name}{consumers}{$c}{onoff} ne "on") {
+ $data{$type}{$name}{consumers}{$c}{startTime} = $t;
+ $data{$type}{$name}{consumers}{$c}{onoff} = "on";
+ my $stimes = ConsumerVal ($hash, $c, "numberDayStarts", 0); # Anzahl der On-Schaltungen am Tag
+ $data{$type}{$name}{consumers}{$c}{numberDayStarts} = $stimes+1;
+ }
+
+ my $starthour = strftime "%H", localtime(ConsumerVal ($hash, $c, "startTime", $t));
+
+ if($chour eq $starthour) {
+ my $runtime = ($t - ConsumerVal ($hash, $c, "startTime", $t)) / 60 ; # in Minuten ! (gettimeofday sind ms !)
+ $data{$type}{$name}{consumers}{$c}{minutesOn} = ConsumerVal ($hash, $c, "minutesOn", 0) + $runtime;
+ }
+ else {
+ $data{$type}{$name}{consumers}{$c}{minutesOn} = 0; # neue Stunde hat begonnen
+ }
+ }
+ else {
+ $data{$type}{$name}{consumers}{$c}{onoff} = "off";
+ }
+
## Durchschnittsverbrauch / Betriebszeit ermitteln + speichern
################################################################
my $consumerco = 0;
@@ -3852,6 +3914,7 @@ sub entryGraphic {
lang => AttrVal ("global", 'language', 'EN'),
flowgh => AttrVal ($name, 'flowGraphicSize', $defflowGSize), # Größe Energieflußgrafik
flowgani => AttrVal ($name, 'flowGraphicAnimate', 0), # Animation Energieflußgrafik
+ flowgcons => AttrVal ($name, 'flowGraphicShowConsumer', 1), # Verbraucher in der Energieflußgrafik anzeigen
css => AttrVal ($name, 'Css', $cssdef), # Css Styles
};
@@ -5030,10 +5093,11 @@ sub _flowGraphic {
my $name = $paref->{name};
my $flowgh = $paref->{flowgh};
my $flowgani = $paref->{flowgani};
+ my $flowgcons = $paref->{flowgcons};
my $css = $paref->{css};
my $style = 'width:'.$flowgh.'px; height:'.$flowgh.'px;';
- my $fs = $flowgh < 300 ? '48px' : '32px';
+ # my $fs = $flowgh < 300 ? '48px' : '32px';
my $animation = $flowgani ? '@keyframes dash { to { stroke-dashoffset: 0; } }' : ''; # Animation Ja/Nein
my $cpv = ReadingsNum($name, 'Current_PV', 0);
@@ -5047,6 +5111,8 @@ sub _flowGraphic {
my $csc = ReadingsNum($name, 'Current_SelfConsumption', 0);
my $csc_style = $csc ? 'flowg active_out' : 'flowg inactive_out';
+
+ my $cc = ReadingsNum($name, 'Current_Consumption', 0);
my $batin = ReadingsNum($name, 'Current_PowerBatIn', undef);
my $batout = ReadingsNum($name, 'Current_PowerBatOut', undef);
@@ -5122,34 +5188,39 @@ END0
## get consumer list and display it in Graphics
################################################
- my $type = $hash->{TYPE};
- my @consumers = sort{$a<=>$b} keys %{$data{$type}{$name}{consumers}}; # definierte Verbraucher ermitteln
- my $consumercount = scalar @consumers;
+ my $pos_left = 0;
+ my $consumercount = 0;
my $consumer_start = 0;
my $consumer_distance = 100;
+ my @consumers;
+ my $currentPower = 0;
- my $currentPower;
-
- if ($consumercount % 2) {
- $consumer_start = 250 - ($consumer_distance * (($consumercount -1) / 2));
- }
- else {
- $consumer_start = 250 - ((($consumer_distance ) / 2) * ($consumercount-1));
- }
-
- $consumer_start = 0 if $consumer_start < 0;
- my $pos_left = $consumer_start + 15;
-
- for my $c0 (@consumers) {
- my $calias = ConsumerVal ($hash, $c0, "alias", ""); # Name des Consumerdevices
- $currentPower = ReadingsNum ($name, "consumer${c0}_currentPower", 0);
- my $cicon = substConsumerIcon ($hash, $c0); # Icon des Consumerdevices
+ if ($flowgcons) {
+ my $type = $hash->{TYPE};
+ @consumers = sort{$a<=>$b} keys %{$data{$type}{$name}{consumers}}; # definierte Verbraucher ermitteln
+ $consumercount = scalar @consumers;
- $ret .= '