From cac10787a9dc03e52adc16d0a25ae03a4a5ef876 Mon Sep 17 00:00:00 2001 From: DS_Starter Date: Thu, 8 May 2025 16:52:51 +0000 Subject: [PATCH] 76_SolarForecast: Version 1.52.0 git-svn-id: https://svn.fhem.de/fhem/trunk@29940 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/contrib/DS_Starter/76_SolarForecast.pm | 235 ++++++++++++-------- 1 file changed, 137 insertions(+), 98 deletions(-) diff --git a/fhem/contrib/DS_Starter/76_SolarForecast.pm b/fhem/contrib/DS_Starter/76_SolarForecast.pm index eef294e56..49dd9572e 100644 --- a/fhem/contrib/DS_Starter/76_SolarForecast.pm +++ b/fhem/contrib/DS_Starter/76_SolarForecast.pm @@ -525,6 +525,7 @@ use constant { MOONICONDEF => 2, # default Mond-Phase (aus %hmoon) MOONCOLDEF => 'lightblue', # default Mond Färbung ACTCOLDEF => 'orange', # default Färbung Icon wenn aktiv + ACTCOLINVBAT => '#00e000', # default Färbung aktiver Batterie-Wechselrichter ohne Solarzellen INACTCOLDEF => 'grey', # default Färbung Icon wenn inaktiv LOCALE_TIME => setlocale (POSIX::LC_TIME), # installierte locale abfragen @@ -6926,6 +6927,7 @@ sub _attrInverterDev { ## no critic "not used" my $valid = { pv => '', ac2dc => '', + dc2ac => '', etotal => '', capacity => '', strings => '', @@ -6970,19 +6972,35 @@ sub _attrInverterDev { ## no critic "not used" } } - if ($none && !$h->{ac2dc}) { - return qq{A battery inverter requires a set key 'ac2dc'. Please consider the commandref.}; + if ($none) { # Batterie-Wechselrichter + if (!$h->{ac2dc}) { + return qq{A battery inverter requires a set key 'ac2dc'. Please consider the commandref.}; + } + + if (!$h->{dc2ac}) { + return qq{A battery inverter requires a set key 'dc2ac'. Please consider the commandref.}; + } + + if ($h->{pv}) { + return qq{A battery inverter without associated solar cells don't need the key 'pv'. Please delete this key.}; + } + + if ($h->{etotal}) { + return qq{A battery inverter without associated solar cells don't need the key 'etotal'. Please delete this key.}; + } } - - if ($none && $h->{etotal}) { - return qq{A battery inverter don't need the key 'etotal'. Please delete this key.}; - } - - if (!$none && $h->{ac2dc}) { - return qq{An inverter with connected solar cells don't need the key 'ac2dc'. Please delete this key.}; + + if (!$none) { # Standard Wechselrichter + if ($h->{ac2dc}) { + return qq{An inverter with connected solar cells don't need the key 'ac2dc'. Please delete this key.}; + } + + if ($h->{dc2ac}) { + return qq{An inverter with connected solar cells don't need the key 'dc2ac'. Please delete this key.}; + } } - if (!$h->{pv} || (!$none && !$h->{etotal}) || !$h->{capacity}) { + if ((!$none && !$h->{pv}) || (!$none && !$h->{etotal}) || !$h->{capacity}) { return qq{One or more of the keys 'pv, etotal, capacity' are missing. Please consider the commandref.}; } @@ -7009,8 +7027,10 @@ sub _attrInverterDev { ## no critic "not used" delete $data{$name}{inverters}{$in}{iasynchron}; delete $data{$name}{inverters}{$in}{ifeed}; delete $data{$name}{inverters}{$in}{isource}; + delete $data{$name}{inverters}{$in}{igeneration}; delete $data{$name}{inverters}{$in}{ietotal}; - delete $data{$name}{inverters}{$in}{ipac2dc}; + delete $data{$name}{inverters}{$in}{ipac2dc}; + delete $data{$name}{inverters}{$in}{ipdc2ac}; } elsif ($paref->{cmd} eq 'del') { delete $data{$name}{inverters}{$in}; @@ -8673,10 +8693,7 @@ sub centralTask { for my $in (1..MAXINVERTER) { # 08.05. $in = sprintf "%02d", $in; my ($err) = isDeviceValid ( { name => $name, obj => 'setupInverterDev'.$in, method => 'attr' } ); - if($err) { - delete $data{$name}{inverters}{$in}; - next; - } + next if($err); delete $data{$name}{inverters}{$in}{ireverse}; } @@ -9856,22 +9873,30 @@ sub _transferInverterValues { my ($err, $indev, $h) = isDeviceValid ( { name => $name, obj => 'setupInverterDev'.$in, method => 'attr' } ); next if($err); - my $source = defined $h->{strings} && $h->{strings} eq 'none' ? 'bat' : 'pv'; # Energie-Bezug PV oder aus Batterie - my $pac2dc = 0; + my $pdc2ac = 0; my $pgen = 0; my $etotal = 0; + + my $source = defined $h->{strings} && $h->{strings} eq 'none' ? 'bat' : 'pv'; # Energie-Bezug PV oder aus Batterie if ($source eq 'bat') { # Batteriewechselrichter ohne PV-Erzeugung $h->{etotal} = 'dum_rdng_no_etot:Wh'; # Dummy Reading für Batterie-Inverter ohne PV-Erzeugung - - if (defined $h->{ac2dc}) { - my ($revread, $revunit) = split ":", $h->{ac2dc}; - my $revuf = $revunit =~ /^kW$/xi ? 1000 : 1; - $pac2dc = ReadingsNum ($indev, $revread, 0) * $revuf; - $pac2dc = $pac2dc <= 0 ? 0 : sprintf "%.0f", $pac2dc; - } } + + if (defined $h->{ac2dc}) { + my ($a2dread, $a2dunit) = split ":", $h->{ac2dc}; + my $a2duf = $a2dunit =~ /^kW$/xi ? 1000 : 1; + $pac2dc = ReadingsNum ($indev, $a2dread, 0) * $a2duf; + $pac2dc = $pac2dc <= 0 ? 0 : sprintf "%.0f", $pac2dc; + } + + if (defined $h->{dc2ac}) { + my ($d2aread, $d2aunit) = split ":", $h->{dc2ac}; + my $d2auf = $d2aunit =~ /^kW$/xi ? 1000 : 1; + $pdc2ac = ReadingsNum ($indev, $d2aread, 0) * $d2auf; + $pdc2ac = $pdc2ac <= 0 ? 0 : sprintf "%.0f", $pdc2ac; + } if ($source eq 'pv') { my ($edread, $etunit) = split ":", $h->{etotal}; # Readingname/Unit für Energie total (PV Erzeugung) @@ -9925,7 +9950,8 @@ sub _transferInverterValues { my $feed = $h->{feed} // 'default'; $data{$name}{inverters}{$in}{igeneration} = $pgen; # aktuell erzeugte PV-Leistung, Forum: https://forum.fhem.de/index.php/topic,117864.msg1139251.html#msg1139251 - $data{$name}{inverters}{$in}{ipac2dc} = $pac2dc if(defined $pac2dc); # aktuell erzeugte AC->DC Leistung + $data{$name}{inverters}{$in}{ipac2dc} = $pac2dc; # aktuelle Leistung AC->DC + $data{$name}{inverters}{$in}{ipdc2ac} = $pdc2ac; # aktuelle Leistung DC->AC $data{$name}{inverters}{$in}{ietotal} = $etotal; # aktuellen etotal des WR speichern $data{$name}{inverters}{$in}{iname} = $indev; # Name des Inverterdevices $data{$name}{inverters}{$in}{ialias} = AttrVal ($indev, 'alias', $indev); # Alias Inverter @@ -9943,7 +9969,7 @@ sub _transferInverterValues { writeToHistory ( { paref => $paref, key => 'pvrl'.$in, val => $ethishour, hour => $nhour } ); debugLog ($paref, "collectData", "collect Inverter $in data - device: $indev, source: $source, delivery: $feed =>"); - debugLog ($paref, "collectData", "pv: $pgen W, etotal: $etotal Wh"); + debugLog ($paref, "collectData", "pv: $pgen W, AC->DC: $pac2dc W, DC->AC: $pdc2ac W, etotal: $etotal Wh"); } storeReading ('Current_PV', $pvsum.' W'); @@ -17052,24 +17078,27 @@ sub _flowGraphic { ($err) = isDeviceValid ( { name => $name, obj => 'setupInverterDev'.$in, method => 'attr' } ); next if($err); - my $pgen = InverterVal ($name, $in, 'igeneration', 0); # Erzeugung aus PV oder alternativer Quelle + my $pgen = InverterVal ($name, $in, 'igeneration', 0); # Erzeugung aus PV + my $pdc2ac = InverterVal ($name, $in, 'ipdc2ac', 0); # Wandlung DC->AC (Batterie-Wechselrichter) my $pac2dc = InverterVal ($name, $in, 'ipac2dc', 0); # Rückwandlung AC->DC (Batterie-Wechselrichter) my $feed = InverterVal ($name, $in, 'ifeed', 'default'); my $isource = InverterVal ($name, $in, 'isource', 'pv'); $pgen = __normDecPlaces ($pgen); + $pdc2ac = __normDecPlaces ($pdc2ac); $pac2dc = __normDecPlaces ($pac2dc); $pdcr->{$lfn}{pn} = $in; # Inverternummer $pdcr->{$lfn}{feed} = $feed; # Eigenschaft der Energielieferung $pdcr->{$lfn}{isource} = $isource; # Art der Energiequelle (pv oder bat) $pdcr->{$lfn}{ptyp} = 'inverter'; # Typ des Producers - $pdcr->{$lfn}{pgen} = $pgen; # aktuelle Erzeugungsleistung DC->AC + $pdcr->{$lfn}{pgen} = $pgen; # aktuelle PV Erzeugungsleistung + $pdcr->{$lfn}{pdc2ac} = $pdc2ac; # aktuelle Leistung DC->AC $pdcr->{$lfn}{pac2dc} = $pac2dc; # aktuelle Reverseleistung AC->DC $pv2node += $pgen if($feed eq 'default' && $isource eq 'pv'); # PV-Erzeugung Inverter für das Hausnetz $pv2grid += $pgen if($feed eq 'grid' && $isource eq 'pv'); # PV nur für das öffentliche Netz $pv2bat += $pgen if($feed eq 'bat' && $isource eq 'pv'); # Direktladen PV nur in die Batterie - $dc2inv2node += $pgen if($feed eq 'default' && $isource eq 'bat'); # Fall Speisung Inverter aus Batterie / Solar-Ladegerät statt PV - $node2inv2dc += $pac2dc if($feed eq 'default' && $isource eq 'bat'); # aktuelle Rückerzeugung AC->DC (Batterie-Wechselrichter) + $dc2inv2node += $pdc2ac if($feed eq 'default' && $isource eq 'bat'); # DC->AC / Speisung Inverter aus Batterie / Solar-Ladegerät statt PV + $node2inv2dc += $pac2dc if($feed eq 'default' && $isource eq 'bat'); # AC->DC (Batterie-Wechselrichter) $lfn++; } @@ -17405,10 +17434,11 @@ END3 my $isource = $pdcr->{$lfn}{isource} // ''; my $pn = $pdcr->{$lfn}{pn}; my $pgen = $pdcr->{$lfn}{pgen}; + my $pdc2ac = $pdcr->{$lfn}{pdc2ac}; my $pac2dc = $pdcr->{$lfn}{pac2dc}; my $chain_color = ''; # Farbe der Laufkette des Producers - $producer_style = $pgen > 0 || $pac2dc > 0 ? "$stna active_normal" : "$stna inactive"; + $producer_style = $pgen > 0 || $pdc2ac > 0 || $pac2dc > 0 ? "$stna active_normal" : "$stna inactive"; #if ($pgen) { #$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).';"'; @@ -17505,8 +17535,11 @@ END3 for my $lfn (@sorted) { my $pn = $pdcr->{$lfn}{pn}; my $pgen = $pdcr->{$lfn}{pgen}; + my $pdc2ac = $pdcr->{$lfn}{pdc2ac}; my $pac2dc = $pdcr->{$lfn}{pac2dc}; - my $pval = $pac2dc ? $pac2dc : $pgen; # Batterie-Wechselrichter AC->DC oder DC->AC berücksichtigen + my $pval = $pac2dc ? $pac2dc : + $pdc2ac ? $pdc2ac : + $pgen; # Batterie-Wechselrichter AC->DC oder DC->AC berücksichtigen $lcp = length $pval; # Leistungszahl abhängig von der Größe entsprechend auf der x-Achse verschieben @@ -17666,8 +17699,11 @@ sub __addProducerIcon { for my $lfn (@sorted) { my $pn = $pdcr->{$lfn}{pn}; my $pgen = $pdcr->{$lfn}{pgen}; - my $pac2dc = $pdcr->{$lfn}{pac2dc}; - my $pval = $pac2dc ? $pac2dc : $pgen; # Batterie-Wechselrichter AC->DC oder DC->AC berücksichtigen + my $pdc2ac = $pdcr->{$lfn}{pdc2ac}; + my $pac2dc = $pdcr->{$lfn}{pac2dc}; + my $pval = $pac2dc ? $pac2dc : + $pdc2ac ? $pdc2ac : + $pgen; # Batterie-Wechselrichter AC->DC oder DC->AC berücksichtigen my ($picon, $ptxt) = __substituteIcon ( { hash => $hash, # Icon des Producerdevices name => $name, @@ -17854,7 +17890,7 @@ sub __substituteIcon { } } elsif ($ptyp eq 'producer') { # Icon Producer - ($icon, $color) = split '@', ProducerVal ($hash, $pn, 'picon', PRODICONDEF); + ($icon, $color) = split '@', ProducerVal ($name, $pn, 'picon', PRODICONDEF); $txt = ProducerVal ($hash, $pn, 'palias', ''); if (!$pcurr) { @@ -17865,22 +17901,25 @@ sub __substituteIcon { my ($iday, $inight); if (InverterVal ($hash, $pn, 'isource', 'pv') eq 'bat') { - ($iday, $inight) = split ':', InverterVal ($hash, $pn, 'iicon', 'inverter:inverter'); + ($iday, $inight) = split ':', InverterVal ($name, $pn, 'iicon', 'inverter:inverter'); } else { - ($iday, $inight) = split ':', InverterVal ($hash, $pn, 'iicon', INVICONDEF); + ($iday, $inight) = split ':', InverterVal ($name, $pn, 'iicon', INVICONDEF); } if ($don || $pcurr) { # Tag -> eigenes Icon oder Standard - $txt = InverterVal ($hash, $pn, 'ialias', ''); - $iday = $iday ? $iday : INVICONDEF; + $txt = InverterVal ($name, $pn, 'ialias', ''); + my $isource = InverterVal ($name, $pn, 'isource', 'pv'); + + $iday = $iday ? $iday : INVICONDEF; ($icon, $color) = split '@', $iday; - $color = !$pcurr ? INACTCOLDEF : - $color ? $color : - ACTCOLDEF; + $color = !$pcurr ? INACTCOLDEF : + $color ? $color : + $isource eq 'bat' ? ACTCOLINVBAT : + ACTCOLDEF; } else { # Nacht -> eigenes Icon oder Mondphase - my $mpi = CurrentVal ($hash, 'moonPhaseI', MOONICONDEF); + my $mpi = CurrentVal ($name, 'moonPhaseI', MOONICONDEF); if ($inight) { # eigenes Icon + ggf. Farbe ($icon, $color) = split '@', $inight; @@ -25894,9 +25933,9 @@ to ensure that the system configuration is correct.
-
  • setupInverterDevXX <Inverter Device Name> pv=<Readingname>:<Unit> ac2dc=<Readingname>:<Unit> etotal=<Readingname>:<Unit>
    - capacity=<max. inverter power> [strings=<String1>,<String2>,...] [asynchron=<Option>] [feed=<Delivery type>] [limit=<0..100>]
    - [icon=<Day>[@<Color>][:<Night>[@<Color>]]]


    +
  • setupInverterDevXX <Inverter Device Name> pv=<Readingname>:<Unit> ac2dc=<Readingname>:<Unit> dc2ac=<Readingname>:<Unit>
    + etotal=<Readingname>:<Unit> capacity=<max. inverter power> [strings=<String1>,<String2>,...] [asynchron=<Option>]
    + [feed=<Delivery type>] [limit=<0..100>] [icon=<active>[@<Color>][:<inactive>[@<Color>]]]


    Defines any inverter device or solar charger and its readings to supply the current PV generation values.
    This can also be a dummy device with corresponding readings.
    @@ -25914,7 +25953,7 @@ to ensure that the system configuration is correct. but works as a DC-DC converter and charges a battery directly or supplies a battery inverter. The function as a solar charger is activated with feed=bat (e.g. a Victron SmartSolar MPPT). - Battery inverter This device has no connected solar cells and works as a DC-AC converter between a battery + Battery inverter This device has no connected solar cells and works as DC-AC or AC-DC converter between a battery and the household grid. The function as a battery inverter is activated with strings=none. @@ -25929,17 +25968,16 @@ to ensure that the system configuration is correct.