diff --git a/fhem/contrib/DS_Starter/76_SolarForecast.pm b/fhem/contrib/DS_Starter/76_SolarForecast.pm
index c96d16be6..bd34ac0b0 100644
--- a/fhem/contrib/DS_Starter/76_SolarForecast.pm
+++ b/fhem/contrib/DS_Starter/76_SolarForecast.pm
@@ -467,10 +467,11 @@ my $prodicondef = 'sani_garden_pump';
my $cicondef = 'light_light_dim_100'; # default Consumer-Icon
my $ciconcoldef = 'darkorange'; # default Consumer-Icon Färbung
my $homeicondef = 'control_building_control@grey'; # default Home-Icon
+my $nodeicondef = 'virtualbox'; # default Knoten-Icon
my $invicondef = 'weather_sun'; # default Inverter-icon
-my $inviconcoldef = 'orange'; # default Inverter Färbung wenn aktiv
my $moonicondef = 2; # default Mond-Phase (aus %hmoon)
my $mooncoldef = 'lightblue'; # default Mond Färbung
+my $actcoldef = 'orange'; # default Färbung Icon wenn aktiv
my $inactcoldef = 'grey'; # default Färbung Icon wenn inaktiv
my $bPath = 'https://svn.fhem.de/trac/browser/trunk/fhem/contrib/SolarForecast/'; # Basispfad Abruf contrib SolarForecast Files
@@ -5738,7 +5739,7 @@ sub __delProducerValues {
deleteReadingspec ($hash, ".*_PPreal".$prn);
readingsDelete ($hash, 'Current_PP'.$prn);
- delete $data{$type}{$name}{current}{'generationp'.$prn};
+ delete $data{$type}{$name}{current}{'pgeneration'.$prn};
delete $data{$type}{$name}{current}{'etotalp' .$prn};
delete $data{$type}{$name}{current}{'iconp' .$prn};
delete $data{$type}{$name}{current}{'namep' .$prn};
@@ -8519,6 +8520,7 @@ sub _transferInverterValues {
$data{$type}{$name}{inverters}{$in}{igeneration} = $pv; # Hilfshash Wert current generation, Forum: https://forum.fhem.de/index.php/topic,117864.msg1139251.html#msg1139251
$data{$type}{$name}{inverters}{$in}{ietotal} = $etotal; # aktuellen etotal des WR speichern
$data{$type}{$name}{inverters}{$in}{iname} = $indev; # Name des Inverterdevices
+ $data{$type}{$name}{inverters}{$in}{ialias} = AttrVal ($indev, 'alias', $indev); # Alias Inverter
$data{$type}{$name}{inverters}{$in}{invertercap} = $h->{capacity} if(defined $h->{capacity}); # optionale Angabe max. WR-Leistung
$data{$type}{$name}{inverters}{$in}{iicon} = $h->{icon} if($h->{icon}); # Icon des Inverters
@@ -8575,7 +8577,7 @@ sub _transferProducerValues {
$p = $p < 0 ? 0 : $p;
storeReading ('Current_PP'.$prn, sprintf("%.1f", $p).' W');
- $data{$type}{$name}{current}{'generationp'.$prn} = $p;
+ $data{$type}{$name}{current}{'pgeneration'.$prn} = $p;
my $etu = $etunit =~ /^kWh$/xi ? 1000 : 1;
my $etotal = ReadingsNum ($prdev, $edread, 0) * $etu; # Erzeugung total (Wh)
@@ -9289,7 +9291,7 @@ sub _createSummaries {
for my $prn (1..$maxproducer) { # V1.32.0 : Erzeugung sonstiger Producer (01..03) hinzufügen
$prn = sprintf "%02d", $prn;
- $othprod += CurrentVal ($hash, 'generationp'.$prn, 0);
+ $othprod += CurrentVal ($hash, 'pgeneration'.$prn, 0);
}
my $consumption = int ($pvgen + $othprod - $gfeedin + $gcon - $batin + $batout);
@@ -13145,8 +13147,7 @@ sub _showConsumerInGraphicBeam {
# check if listed device is planned
####################################
- if (ReadingsVal($name, $itemName."_Planned", "no") eq "yes") {
- #get start and end hour
+ if (ReadingsVal($name, $itemName."_Planned", "no") eq "yes") { # get start and end hour
my ($start, $end); # werden auf Balken Pos 0 - 23 umgerechnet, nicht auf Stunde !!, Pos = 24 -> ungültige Pos = keine Anzeige
if($lang eq "DE") {
@@ -14076,59 +14077,63 @@ sub _flowGraphic {
my $scale = $fgscaledef;
my $hasbat = 1; # initial Batterie vorhanden
- my $flowgprods = 1; # Producer in der Energieflußgrafik anzeigen per default
- my $ppcurr = {}; # Hashref Producer current power
- my $cpcurr = {}; # Hashref Consumer current power
-
- ## definierte Inverter ermitteln und deren
- ## aktuelle Leistung bestimmen
- ############################################
- my $invertercount = 0;
- my $pvall = 0; # Summe Erzeugung alle Inverter
- my @inverters;
-
- for my $in (1..$maxinverter) {
- $in = sprintf "%02d", $in;
- my $p = InverterVal ($hash, $in, 'igeneration', 0);
-
- if (defined $p) {
- push @inverters, $in;
- $ppcurr->{$in} = $p;
- $invertercount += 1;
- $pvall += $p;
- }
- }
- ## definierte Producer ermitteln und deren
- ## aktuelle Leistung bestimmen
- ############################################
- my $producercount = 0;
- my $ppall = 0; # Summe Erzeugung alle Poducer
- my @producers;
+ ## definierte Producer + Inverter ermitteln und zusammenfassen
+ ################################################################
+ my $pdcr = {}; # Hashref Producer
+ my $ppall = 0; # Summe Erzeugung alle nicht PV-Producer
+ my $pvall = 0; # Summe Erzeugung alle Inverter
+ my $lfn = 0;
for my $pn (1..$maxproducer) {
$pn = sprintf "%02d", $pn;
- my $p = CurrentVal ($hash, 'generationp'.$pn, undef);
+ my $p = CurrentVal ($hash, 'pgeneration'.$pn, undef);
if (defined $p) {
- push @producers, $pn;
- $ppcurr->{$pn} = $p;
- $producercount += 1;
- $ppall += $p;
+ $p = sprintf "%.2f", $p;
+ $p = sprintf "%.0f", $p if($p > 10);
+ $pdcr->{$lfn}{p} = $p; # aktuelle Erzeugung nicht PV-Producer
+ $pdcr->{$lfn}{pn} = $pn; # Producernummer
+ $pdcr->{$lfn}{ptyp} = 'producer'; # Typ des Producers
+ $ppall += $p; # aktuelle Erzeuguung aller nicht PV-Producer
+
+ $lfn++;
}
}
+
+ for my $in (1..$maxinverter) {
+ $in = sprintf "%02d", $in;
+ my $p = InverterVal ($hash, $in, 'igeneration', undef);
+
+ if (defined $p) {
+ $p = sprintf "%.2f", $p;
+ $p = sprintf "%.0f", $p if($p > 10);
+ $pdcr->{$lfn}{p} = $p; # aktuelle Erzeugung Inverter
+ $pdcr->{$lfn}{pn} = $in; # Inverternummer
+ $pdcr->{$lfn}{ptyp} = 'inverter'; # Typ des Producers
+ $pvall += $p;
+
+ $lfn++;
+ }
+ }
+
+ my $pallsum = $ppall + $pvall;
+ $pallsum = sprintf "%.0f", $pallsum if($pallsum > 10);
+ my $producercount = keys %{$pdcr};
+ my @producers = sort{$a<=>$b} keys %{$pdcr};
## definierte Verbraucher ermitteln
#####################################
- my $consumercount = 0;
- my @consumers;
-
+ my $cnsmr = {}; # Hashref Consumer current power
+
for my $c (sort{$a<=>$b} keys %{$data{$type}{$name}{consumers}}) { # definierte Verbraucher ermitteln
next if(isConsumerNoshow ($hash, $c) =~ /^[13]$/xs); # auszublendende Consumer nicht berücksichtigen
- push @consumers, $c;
- $cpcurr->{$c} = ReadingsNum ($name, "consumer${c}_currentPower", 0);
- $consumercount += 1;
+ $cnsmr->{$c}{p} = ReadingsNum ($name, "consumer${c}_currentPower", 0);
+ $cnsmr->{$c}{ptyp} = 'consumer';
}
+
+ my $consumercount = keys %{$cnsmr};
+ my @consumers = sort{$a<=>$b} keys %{$cnsmr};
## Batterie + Werte festlegen
###############################
@@ -14176,8 +14181,7 @@ sub _flowGraphic {
## Werte / SteuerungVars anpassen
###################################
$flowgcons = 0 if(!$consumercount); # Consumer Anzeige ausschalten wenn keine Consumer definiert
- $flowgprods = 0 if(!$producercount && !$invertercount); # Producer Anzeige ausschalten wenn keine Producer / Inverter definiert
- my $p2home = sprintf "%.1f", ($csc + $ppall); # Energiefluß von Sonne zum Haus: Selbstverbrauch + alle Producer
+ my $p2home = sprintf "%.1f", ($csc + $ppall); # Energiefluß von Knoten zum Haus: Selbstverbrauch + alle Producer
$p2home = sprintf "%.0f", $p2home if($p2home > 10);
$p2home = 0 if($p2home == 0); # 0.0 eliminieren wenn keine Leistung zum Haus
@@ -14185,13 +14189,13 @@ sub _flowGraphic {
#########################################
my $vbwidth = 800; # width and height specify the viewBox size
my $vbminx = -10 * $flowgshift; # min-x and min-y represent the smallest X and Y coordinates that the viewBox may have
- my $vbminy = $flowgprods ? -25 : 100;
+ my $vbminy = -25;
my $vbhight = !$flowgcons ? 380 :
!$flowgconTime ? 590 :
610;
- $vbhight += 100 if($flowgprods);
+ $vbhight += 100;
my $vbox = "$vbminx $vbminy $vbwidth $vbhight";
@@ -14214,57 +14218,56 @@ END0
my $producer_start = 0;
my $producerPower = 0;
- if ($flowgprods) {
- if ($producercount % 2) {
- $producer_start = 350 - ($consDist * (($producercount -1) / 2));
- }
- else {
- $producer_start = 350 - (($consDist / 2) * ($producercount-1));
- }
+ if ($producercount % 2) {
+ $producer_start = 350 - ($consDist * (($producercount -1) / 2));
+ }
+ else {
+ $producer_start = 350 - (($consDist / 2) * ($producercount-1));
+ }
- $pos_left = $producer_start + 25;
+ $pos_left = $producer_start + 5;
- for my $prn (@producers) {
- my $palias = CurrentVal ($hash, 'aliasp'.$prn, 'namep'.$prn);
- my ($picon) = __substituteIcon ( { hash => $hash, # Icon des Producerdevices
- name => $name,
- pn => $prn,
- pcurr => $ppcurr->{$prn},
- lang => $lang
- }
- );
+ for my $lfn (@producers) {
+ my $pn = $pdcr->{$lfn}{pn};
+ my ($picon, $ptxt) = __substituteIcon ( { hash => $hash, # Icon des Producerdevices
+ name => $name,
+ pn => $pn,
+ ptyp => $pdcr->{$lfn}{ptyp},
+ don => NexthoursVal ($hash, 'NextHour00', 'DoN', 0), # Tag oder Nacht
+ pcurr => $pdcr->{$lfn}{p},
+ lang => $lang
+ }
+ );
- $picon = FW_makeImage ($picon, '');
- ($scale, $picon) = __normIconScale ($picon, $name);
-
- $ret .= qq{};
- $ret .= "$palias".$picon;
- $ret .= ' ';
+ $picon = FW_makeImage ($picon, '');
+ ($scale, $picon) = __normIconScale ($picon, $name);
+
+ $ret .= qq{};
+ $ret .= "$ptxt".$picon;
+ $ret .= ' ';
- $pos_left += $consDist;
- }
+ $pos_left += $consDist;
}
- ## Inverter Icon
- ######################
- my ($iicon, $smtxt) = __substituteIcon ( { hash => $hash,
- name => $name,
- in => '01',
- don => NexthoursVal ($hash, 'NextHour00', 'DoN', 0), # Tag oder Nacht
- pcurr => $pvall,
- lang => $lang
- }
- );
+ ## Knoten Icon
+ ################
+ my ($nicon, $ntxt) = __substituteIcon ( { hash => $hash,
+ name => $name,
+ ptyp => 'node',
+ pcurr => $pallsum,
+ lang => $lang
+ }
+ );
- $iicon = FW_makeImage ($iicon, '');
- ($scale, $iicon) = __normIconScale ($iicon, $name);
+ $nicon = FW_makeImage ($nicon, '');
+ ($scale, $nicon) = __normIconScale ($nicon, $name);
- $ret .= qq{}; # translate(X-Koordinate,Y-Koordinate), scale()-> Koordinaten ändern sich bei Größenänderung
- $ret .= "$smtxt".$iicon;
+ $ret .= qq{}; # translate(X-Koordinate,Y-Koordinate), scale()-> Koordinaten ändern sich bei Größenänderung
+ $ret .= "$ntxt".$nicon;
$ret .= ' ';
## Consumer Liste und Icons in Grafik anzeigen
- ###############################################
+ ################################################
$pos_left = 0;
my $consumer_start = 0;
my $currentPower = 0;
@@ -14281,16 +14284,18 @@ END0
for my $c (@consumers) {
my $calias = ConsumerVal ($hash, $c, 'alias', ''); # Name des Consumerdevices
- $currentPower = $cpcurr->{$c};
+ $currentPower = $cnsmr->{$c}{p};
+
my ($cicon) = __substituteIcon ( { hash => $hash, # Icon des Consumerdevices
- name => $name,
- cn => $c,
+ name => $name,
+ pn => $c,
+ ptyp => $cnsmr->{$c}{ptyp},
pcurr => $currentPower,
lang => $lang
}
- );
- $cc_dummy -= $currentPower;
-
+ );
+
+ $cc_dummy -= $currentPower;
$cicon = FW_makeImage ($cicon, '');
($scale, $cicon) = __normIconScale ($cicon, $name);
@@ -14378,33 +14383,32 @@ END3
## Producer Laufketten
########################
- if ($flowgprods) {
- $pos_left = $producer_start * 2;
- my $pos_left_start_con = 0;
- my $distance_con = 25;
+ $pos_left = $producer_start * 2;
+ my $pos_left_start_con = 0;
+ my $distance_con = 25;
- if ($producercount % 2) {
- $pos_left_start_con = 700 - ($distance_con * (($producercount -1) / 2));
- }
- else {
- $pos_left_start_con = 700 - ((($distance_con ) / 2) * ($producercount-1));
+ if ($producercount % 2) {
+ $pos_left_start_con = 700 - ($distance_con * (($producercount -1) / 2));
+ }
+ else {
+ $pos_left_start_con = 700 - ((($distance_con ) / 2) * ($producercount-1));
+ }
+
+ for my $lfn (@producers) {
+ my $pn = $pdcr->{$lfn}{pn};
+ my $p = $pdcr->{$lfn}{p};
+ my $consumer_style = 'flowg inactive_out';
+ $consumer_style = 'flowg active_out' if($p > 0);
+ my $chain_color = ''; # Farbe der Laufkette des Producers
+
+ if ($p) {
+ #$chain_color = 'style="stroke: #'.substr(Color::pahColor(0,50,100,$p,[0,255,0, 127,255,0, 255,255,0, 255,127,0, 255,0,0]),0,6).';"';
+ $chain_color = 'style="stroke: darkorange;"';
}
- for my $prn (@producers) {
- my $p = $ppcurr->{$prn};
- my $consumer_style = 'flowg inactive_out';
- $consumer_style = 'flowg active_out' if($p > 0);
- my $chain_color = ''; # Farbe der Laufkette des Producers
-
- if ($p) {
- #$chain_color = 'style="stroke: #'.substr(Color::pahColor(0,50,100,$p,[0,255,0, 127,255,0, 255,255,0, 255,127,0, 255,0,0]),0,6).';"';
- $chain_color = 'style="stroke: darkorange;"';
- }
-
- $ret .= qq{}; # Design Consumer Laufkette
- $pos_left += ($consDist * 2);
- $pos_left_start_con += $distance_con;
- }
+ $ret .= qq{}; # Design Consumer Laufkette
+ $pos_left += ($consDist * 2);
+ $pos_left_start_con += $distance_con;
}
## Consumer Laufketten
@@ -14424,7 +14428,7 @@ END3
for my $c (@consumers) {
my $power = ConsumerVal ($hash, $c, 'power', 0);
my $rpcurr = ConsumerVal ($hash, $c, 'rpcurr', ''); # Reading für akt. Verbrauch angegeben ?
- $currentPower = $cpcurr->{$c};
+ $currentPower = $cnsmr->{$c}{p};
if (!$rpcurr && isConsumerPhysOn($hash, $c)) { # Workaround wenn Verbraucher ohne Leistungsmessung
$currentPower = $power;
@@ -14450,10 +14454,10 @@ END3
## Textangaben an Grafikelementen
###################################
$cc_dummy = sprintf("%.0f", $cc_dummy); # Verbrauch Dummy-Consumer
- $ret .= qq{$pvall} if ($pvall);
+ $ret .= qq{$pallsum} if ($pallsum);
$ret .= qq{$soc %} if ($hasbat); # Lage Text Batterieladungszustand
- $ret .= qq{$p2home} if ($p2home);
- $ret .= qq{$cgfi} if ($cgfi);
+ $ret .= qq{$p2home} if ($p2home);
+ $ret .= qq{$cgfi} if ($cgfi);
$ret .= qq{$cgc} if ($cgc);
$ret .= qq{$batout} if ($batout && $hasbat);
$ret .= qq{$batin} if ($batin && $hasbat);
@@ -14463,36 +14467,34 @@ END3
my $lcp;
## Textangabe Producer
- ########################
- if ($flowgprods) {
- $pos_left = ($producer_start * 2) - 50; # -XX -> Start Lage producer Beschriftung
+ ########################
+ $pos_left = $producer_start * 2 - 70; # -XX -> Start Lage Producer Beschriftung
- for my $prn (@producers) {
- $currentPower = sprintf "%.2f", $ppcurr->{$prn};
- $currentPower = sprintf "%.0f", $currentPower if($currentPower > 10);
- $currentPower = 0 if(1 * $currentPower == 0);
- $lcp = length $currentPower;
+ for my $lfn (@producers) {
+ my $pn = $pdcr->{$lfn}{pn};
+ $currentPower = $pdcr->{$lfn}{p};
+ $currentPower = 0 if(1 * $currentPower == 0);
+ $lcp = length $currentPower;
- # Leistungszahl abhängig von der Größe entsprechend auf der x-Achse verschieben
- ###############################################################################
- if ($lcp >= 5) {$pos_left -= 10}
- elsif ($lcp == 4) {$pos_left += 10}
- elsif ($lcp == 3) {$pos_left += 15}
- elsif ($lcp == 2) {$pos_left += 20}
- elsif ($lcp == 1) {$pos_left += 40}
+ # Leistungszahl abhängig von der Größe entsprechend auf der x-Achse verschieben
+ ###############################################################################
+ if ($lcp >= 5) {$pos_left -= 10}
+ elsif ($lcp == 4) {$pos_left += 10}
+ elsif ($lcp == 3) {$pos_left += 15}
+ elsif ($lcp == 2) {$pos_left += 20}
+ elsif ($lcp == 1) {$pos_left += 40}
- $ret .= qq{$currentPower} if($flowgconPower); # Lage producer Consumption
+ $ret .= qq{$currentPower} if($flowgconPower); # Lage producer Consumption
- # Leistungszahl wieder zurück an den Ursprungspunkt
- ####################################################
- if ($lcp >= 5) {$pos_left += 10}
- elsif ($lcp == 4) {$pos_left -= 10}
- elsif ($lcp == 3) {$pos_left -= 15}
- elsif ($lcp == 2) {$pos_left -= 20}
- elsif ($lcp == 1) {$pos_left -= 40}
-
- $pos_left += ($consDist * 2);
- }
+ # Leistungszahl wieder zurück an den Ursprungspunkt
+ ####################################################
+ if ($lcp >= 5) {$pos_left += 10}
+ elsif ($lcp == 4) {$pos_left -= 10}
+ elsif ($lcp == 3) {$pos_left -= 15}
+ elsif ($lcp == 2) {$pos_left -= 20}
+ elsif ($lcp == 1) {$pos_left -= 40}
+
+ $pos_left += ($consDist * 2);
}
## Textangabe Consumer
@@ -14501,7 +14503,7 @@ END3
$pos_left = ($consumer_start * 2) - 50; # -XX -> Start Lage Consumer Beschriftung
for my $c (@consumers) {
- $currentPower = sprintf "%.1f", $cpcurr->{$c};
+ $currentPower = sprintf "%.1f", $cnsmr->{$c}{p};
$currentPower = sprintf "%.0f", $currentPower if($currentPower > 10);
my $consumerTime = ConsumerVal ($hash, $c, 'remainTime', ''); # Restlaufzeit
my $rpcurr = ConsumerVal ($hash, $c, 'rpcurr', ''); # Readingname f. current Power
@@ -14556,9 +14558,8 @@ sub __substituteIcon {
my $paref = shift;
my $hash = $paref->{hash};
my $name = $paref->{name};
- my $cn = $paref->{cn};
+ my $ptyp = $paref->{ptyp};
my $pn = $paref->{pn};
- my $in = $paref->{in};
my $don = $paref->{don};
my $pcurr = $paref->{pcurr};
my $lang = $paref->{lang};
@@ -14566,29 +14567,31 @@ sub __substituteIcon {
my ($color, $icon);
my $txt = '';
- if ($cn) { # Icon Consumer
- ($icon, $color) = split '@', ConsumerVal ($hash, $cn, 'icon', $cicondef);
+ if ($ptyp eq 'consumer') { # Icon Consumer
+ ($icon, $color) = split '@', ConsumerVal ($hash, $pn, 'icon', $cicondef);
if (!$color) {
- $color = isConsumerLogOn ($hash, $cn, $pcurr) ? $ciconcoldef : '';
+ $color = isConsumerLogOn ($hash, $pn, $pcurr) ? $ciconcoldef : '';
}
}
- elsif ($pn) { # Icon Producer
+ elsif ($ptyp eq 'producer') { # Icon Producer
($icon, $color) = split '@', CurrentVal ($hash, 'iconp'.$pn, $prodicondef);
+ $txt = CurrentVal ($hash, 'aliasp'.$pn, 'namep'.$pn);
if (!$pcurr) {
$color = 'grey';
}
}
- elsif ($in) { # Inverter, Smartloader
- my ($iday, $inight) = split ':', InverterVal ($hash, $in, 'iicon', $invicondef);
+ elsif ($ptyp eq 'inverter') { # Inverter, Smartloader
+ my ($iday, $inight) = split ':', InverterVal ($hash, $pn, 'iicon', $invicondef);
if ($don || $pcurr) { # Tag -> eigenes Icon oder Standard
+ $txt = InverterVal ($hash, $pn, 'ialias', '');
$iday = $iday ? $iday : $invicondef;
($icon, $color) = split '@', $iday;
$color = !$pcurr ? $inactcoldef :
$color ? $color :
- $inviconcoldef;
+ $actcoldef;
}
else { # Nacht -> eigenes Icon oder Mondphase
my $mpi = CurrentVal ($hash, 'moonPhaseI', $moonicondef);
@@ -14604,6 +14607,12 @@ sub __substituteIcon {
}
}
}
+ elsif ($ptyp eq 'node') { # Knoten-Icon
+ ($icon, $color) = split '@', $nodeicondef;
+ $color = !$pcurr ? $inactcoldef :
+ $color ? $color :
+ $actcoldef;
+ }
$icon .= '@'.$color if($color);
@@ -18869,7 +18878,7 @@ return $def;
# Usage:
# CurrentVal ($hash, $key, $def)
#
-# $key: generationpXX - aktuelle Erzeugung Producer XX
+# $key: pgenerationXX - aktuelle Erzeugung Producer XX
# aiinitstate - Initialisierungsstatus der KI
# aitrainstate - Traisningsstatus der KI
# aiaddistate - Add Instanz Status der KI