2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-01-31 06:39:11 +00:00

76_SolarForecast: consumerkey 'mode' can device/reading combination

git-svn-id: https://svn.fhem.de/fhem/trunk@29417 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
nasseeder1 2024-12-08 20:00:05 +00:00
parent 47e0bea16b
commit fa52f73aa6
2 changed files with 197 additions and 129 deletions

View File

@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it # Do not insert empty lines here, update check depends on it
- feature: 76_SolarForecast: consumerkey 'mode' can device/reading combination
- feature: 76_SolarForecast: possible asynchron mode Battery Dev, code change - feature: 76_SolarForecast: possible asynchron mode Battery Dev, code change
- feature: 76_SMAInverter.pm: add installer login, code optimized - feature: 76_SMAInverter.pm: add installer login, code optimized
- feature: 76_SolarForecast: possible asynchron mode for Meter & Inverter - feature: 76_SolarForecast: possible asynchron mode for Meter & Inverter

View File

@ -157,6 +157,7 @@ BEGIN {
# Versions History intern # Versions History intern
my %vNotesIntern = ( my %vNotesIntern = (
"1.39.2" => "08.12.2024 rollout delHashRefDeep, extended consumer key 'mode' by device/reading combination ",
"1.39.1" => "07.12.2024 new control releaseCentralTask, new delHashRefDeep in some cases ". "1.39.1" => "07.12.2024 new control releaseCentralTask, new delHashRefDeep in some cases ".
"possible asynchron mode for setupBatteryDev ", "possible asynchron mode for setupBatteryDev ",
"1.39.0" => "04.12.2024 possible asynchron mode for setupMeterDev, setupInverterDevXX ". "1.39.0" => "04.12.2024 possible asynchron mode for setupMeterDev, setupInverterDevXX ".
@ -431,8 +432,8 @@ my $epiecMaxCycles = 10;
my @ctypes = qw(dishwasher dryer washingmachine heater charger other my @ctypes = qw(dishwasher dryer washingmachine heater charger other
noSchedule); # erlaubte Consumer Typen noSchedule); # erlaubte Consumer Typen
my $defmintime = 60; # default Einplanungsdauer in Minuten my $defmintime = 60; # default Einplanungsdauer in Minuten
my $defctype = "other"; # default Verbrauchertyp my $defctype = 'other'; # default Verbrauchertyp
my $defcmode = "can"; # default Planungsmode der Verbraucher my $defcmode = 'can'; # default Planungsmode der Verbraucher
my $defpopercent = 1.0; # Standard % aktuelle Leistung an nominaler Leistung gemäß Typenschild my $defpopercent = 1.0; # Standard % aktuelle Leistung an nominaler Leistung gemäß Typenschild
my $defhyst = 0; # default Hysterese my $defhyst = 0; # default Hysterese
@ -1421,6 +1422,7 @@ sub _readCacheFile {
my $valid = $dtree->isa('AI::DecisionTree'); my $valid = $dtree->isa('AI::DecisionTree');
if ($valid) { if ($valid) {
delHashRefDeep ($data{$type}{$name}{aidectree}{aitrained});
delete $data{$type}{$name}{aidectree}{aitrained}; delete $data{$type}{$name}{aidectree}{aitrained};
$data{$type}{$name}{aidectree}{aitrained} = $dtree; $data{$type}{$name}{aidectree}{aitrained} = $dtree;
$data{$type}{$name}{current}{aitrainstate} = 'ok'; $data{$type}{$name}{current}{aitrainstate} = 'ok';
@ -1439,6 +1441,7 @@ sub _readCacheFile {
my ($err, $data) = fileRetrieve ($file); my ($err, $data) = fileRetrieve ($file);
if (!$err && $data) { if (!$err && $data) {
delHashRefDeep ($data{$type}{$name}{aidectree}{airaw});
delete $data{$type}{$name}{aidectree}{airaw}; delete $data{$type}{$name}{aidectree}{airaw};
$data{$type}{$name}{aidectree}{airaw} = $data; $data{$type}{$name}{aidectree}{airaw} = $data;
$data{$type}{$name}{current}{aitrawstate} = 'ok'; $data{$type}{$name}{current}{aitrawstate} = 'ok';
@ -1452,6 +1455,7 @@ sub _readCacheFile {
my ($err, $statapi) = fileRetrieve ($file); my ($err, $statapi) = fileRetrieve ($file);
if (!$err && $statapi) { if (!$err && $statapi) {
delHashRefDeep ($data{$type}{$name}{statusapi});
delete $data{$type}{$name}{statusapi}; delete $data{$type}{$name}{statusapi};
$data{$type}{$name}{statusapi} = $statapi; $data{$type}{$name}{statusapi} = $statapi;
Log3 ($name, 3, qq{$name - cached data "$title" restored}); Log3 ($name, 3, qq{$name - cached data "$title" restored});
@ -1464,6 +1468,7 @@ sub _readCacheFile {
my ($err, $wthtapi) = fileRetrieve ($file); my ($err, $wthtapi) = fileRetrieve ($file);
if (!$err && $wthtapi) { if (!$err && $wthtapi) {
delHashRefDeep ($data{$type}{$name}{weatherapi});
delete $data{$type}{$name}{weatherapi}; delete $data{$type}{$name}{weatherapi};
$data{$type}{$name}{weatherapi} = $wthtapi; $data{$type}{$name}{weatherapi} = $wthtapi;
Log3 ($name, 3, qq{$name - cached data "$title" restored}); Log3 ($name, 3, qq{$name - cached data "$title" restored});
@ -1476,6 +1481,7 @@ sub _readCacheFile {
my ($err, $dwdc) = fileRetrieve ($file); my ($err, $dwdc) = fileRetrieve ($file);
if (!$err && $dwdc) { if (!$err && $dwdc) {
delHashRefDeep ($data{$type}{$name}{dwdcatalog});
delete $data{$type}{$name}{dwdcatalog}; delete $data{$type}{$name}{dwdcatalog};
$data{$type}{$name}{dwdcatalog} = $dwdc; $data{$type}{$name}{dwdcatalog} = $dwdc;
debugLog ($paref, 'dwdComm', qq{$title restored}); debugLog ($paref, 'dwdComm', qq{$title restored});
@ -1797,6 +1803,7 @@ sub _setVictronCredentials { ## no critic "not used"
my ($a,$h) = parseParams ($arg); my ($a,$h) = parseParams ($arg);
if ($a->[0] && $a->[0] eq 'delete') { if ($a->[0] && $a->[0] eq 'delete') {
delHashRefDeep ($data{$type}{$name}{statusapi}{'?VRM'});
delete $data{$type}{$name}{statusapi}{'?VRM'}; delete $data{$type}{$name}{statusapi}{'?VRM'};
$msg = qq{Credentials for the Victron VRM API are deleted. }; $msg = qq{Credentials for the Victron VRM API are deleted. };
} }
@ -2290,6 +2297,7 @@ sub _setreset { ## no critic "not used"
Log3 ($name, 3, qq{$name - roofIdentPair: pair key "$pk" deleted}); Log3 ($name, 3, qq{$name - roofIdentPair: pair key "$pk" deleted});
} }
else { else {
delHashRefDeep ($data{$type}{$name}{statusapi}{'?IdPair'});
delete $data{$type}{$name}{statusapi}{'?IdPair'}; delete $data{$type}{$name}{statusapi}{'?IdPair'};
Log3($name, 3, qq{$name - roofIdentPair: all pair keys deleted}); Log3($name, 3, qq{$name - roofIdentPair: all pair keys deleted});
} }
@ -5595,8 +5603,20 @@ sub _attrconsumer { ## no critic "not used"
} }
if (exists $h->{mode} && $h->{mode} !~ /^(?:can|must)$/xs) { if (exists $h->{mode} && $h->{mode} !~ /^(?:can|must)$/xs) {
if ($h->{mode} =~ /.*:.*/xs) {
my ($dv, $rd) = split ':', $h->{mode};
($err) = isDeviceValid ( { name => $hash->{NAME}, obj => $dv, method => 'string' } );
return $err if($err);
my $mode = ReadingsVal ($dv, $rd, '');
if ($mode !~ /^(?:can|must)$/xs) {
return "The reading '$rd' of device '$dv' is invalid or doesn't contain a valid mode";
}
}
else {
return qq{The mode "$h->{mode}" isn't allowed!}; return qq{The mode "$h->{mode}" isn't allowed!};
} }
}
my $valid; my $valid;
@ -6570,6 +6590,7 @@ sub Rename {
my $type = (split '::', __PACKAGE__)[1]; my $type = (split '::', __PACKAGE__)[1];
$data{$type}{$new_name} = $data{$type}{$old_name}; $data{$type}{$new_name} = $data{$type}{$old_name};
delHashRefDeep ($data{$type}{$old_name});
delete $data{$type}{$old_name}; delete $data{$type}{$old_name};
my @ftd = _searchCacheFiles ($old_name); my @ftd = _searchCacheFiles ($old_name);
@ -6653,6 +6674,7 @@ sub Delete {
my $type = $hash->{TYPE}; my $type = $hash->{TYPE};
delHashRefDeep ($data{$type}{$name});
delete $data{$type}{$name}; delete $data{$type}{$name};
return; return;
@ -6793,6 +6815,7 @@ sub delConsumerFromMem {
} }
} }
delHashRefDeep ($data{$type}{$name}{consumers}{$c});
delete $data{$type}{$name}{consumers}{$c}; delete $data{$type}{$name}{consumers}{$c};
Log3 ($name, 3, qq{$name - Consumer "$c - $calias" deleted from memory}); Log3 ($name, 3, qq{$name - Consumer "$c - $calias" deleted from memory});
@ -7356,6 +7379,7 @@ sub createStringConfig { ## no critic "not used"
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $type = $hash->{TYPE}; my $type = $hash->{TYPE};
delHashRefDeep ($data{$type}{$name}{strings});
delete $data{$type}{$name}{strings}; # Stringhash zurücksetzen delete $data{$type}{$name}{strings}; # Stringhash zurücksetzen
$data{$type}{$name}{current}{allStringsFullfilled} = 0; $data{$type}{$name}{current}{allStringsFullfilled} = 0;
@ -8096,6 +8120,7 @@ sub _transferWeatherValues {
my $type = $paref->{type}; my $type = $paref->{type};
delHashRefDeep ($data{$type}{$name}{weatherdata});
delete $data{$type}{$name}{weatherdata}; # Wetterdaten Hash löschen delete $data{$type}{$name}{weatherdata}; # Wetterdaten Hash löschen
$paref->{apiu} = $apiu; # API wird verwendet $paref->{apiu} = $apiu; # API wird verwendet
@ -10458,7 +10483,7 @@ sub ___doPlanning {
debugLog ($paref, "consumerPlanning", qq{consumer "$c" - epiece1: $epiece1}); debugLog ($paref, "consumerPlanning", qq{consumer "$c" - epiece1: $epiece1});
my $mode = ConsumerVal ($hash, $c, 'mode', 'can'); my $mode = getConsumerPlanningMode ($hash, $c); # Planungsmode 'can' oder 'must'
my $calias = ConsumerVal ($hash, $c, 'alias', ''); my $calias = ConsumerVal ($hash, $c, 'alias', '');
my $mintime = ConsumerVal ($hash, $c, 'mintime', $defmintime); # Einplanungsdauer my $mintime = ConsumerVal ($hash, $c, 'mintime', $defmintime); # Einplanungsdauer
my $oldplanstate = ConsumerVal ($hash, $c, 'planstate', ''); # V. 1.35.0 my $oldplanstate = ConsumerVal ($hash, $c, 'planstate', ''); # V. 1.35.0
@ -11000,7 +11025,7 @@ sub ___switchConsumerOn {
if ($auto && $oncom && $swoncond && !$swoffcond && !$iilt && # kein Einschalten wenn zusätzliche Switch off Bedingung oder Sperrzeit zutrifft if ($auto && $oncom && $swoncond && !$swoffcond && !$iilt && # kein Einschalten wenn zusätzliche Switch off Bedingung oder Sperrzeit zutrifft
$simpCstat =~ /planned|priority|starting/xs && $isInTime) { # Verbraucher Start ist geplant && Startzeit überschritten $simpCstat =~ /planned|priority|starting/xs && $isInTime) { # Verbraucher Start ist geplant && Startzeit überschritten
my $mode = ConsumerVal ($hash, $c, "mode", $defcmode); # Consumer Planungsmode my $mode = getConsumerPlanningMode ($hash, $c); # Planungsmode 'can' oder 'must'
my $enable = ___enableSwitchByBatPrioCharge ($paref); # Vorrangladung Batterie ? my $enable = ___enableSwitchByBatPrioCharge ($paref); # Vorrangladung Batterie ?
debugLog ($paref, "consumerSwitching${c}", qq{Consumer switch enable by battery state: $enable}); debugLog ($paref, "consumerSwitching${c}", qq{Consumer switch enable by battery state: $enable});
@ -11075,9 +11100,9 @@ sub ___switchConsumerOff {
my $stopts = ConsumerVal ($hash, $c, "planswitchoff", undef); # geplante Unix Stopzeit my $stopts = ConsumerVal ($hash, $c, "planswitchoff", undef); # geplante Unix Stopzeit
my $auto = ConsumerVal ($hash, $c, "auto", 1); my $auto = ConsumerVal ($hash, $c, "auto", 1);
my $calias = ConsumerVal ($hash, $c, "alias", ""); # Consumer Device Alias my $calias = ConsumerVal ($hash, $c, "alias", ""); # Consumer Device Alias
my $mode = ConsumerVal ($hash, $c, "mode", $defcmode); # Consumer Planungsmode
my $hyst = ConsumerVal ($hash, $c, "hysteresis", $defhyst); # Hysterese my $hyst = ConsumerVal ($hash, $c, "hysteresis", $defhyst); # Hysterese
my $mode = getConsumerPlanningMode ($hash, $c); # Planungsmode 'can' oder 'must'
my $offcom = ConsumerVal ($hash, $c, 'offcom', ''); # Set Command für "off" my $offcom = ConsumerVal ($hash, $c, 'offcom', ''); # Set Command für "off"
my ($swoffcond,$infoff,$err) = isAddSwitchOffCond ($hash, $c); # zusätzliche Switch off Bedingung my ($swoffcond,$infoff,$err) = isAddSwitchOffCond ($hash, $c); # zusätzliche Switch off Bedingung
my $simpCstat = simplifyCstate ($pstate); my $simpCstat = simplifyCstate ($pstate);
@ -17908,6 +17933,7 @@ return;
sub delHashRefDeep { sub delHashRefDeep {
my $href = shift; my $href = shift;
if (ref $href eq 'HASH') {
for my $key (keys %{$href}) { for my $key (keys %{$href}) {
if (ref $href->{$key} eq 'HASH') { if (ref $href->{$key} eq 'HASH') {
delHashRefDeep ($href->{$key}); delHashRefDeep ($href->{$key});
@ -17915,6 +17941,7 @@ sub delHashRefDeep {
delete $href->{$key}; delete $href->{$key};
} }
}
$href = undef; # Optional: Garbage Collection erzwingen $href = undef; # Optional: Garbage Collection erzwingen
@ -18055,6 +18082,44 @@ sub createAssociatedWith {
return; return;
} }
################################################################
# Funktion liefert den Planungsmodus eines Verbrauchers
# mode kann sein:
# can
# must
################################################################
sub getConsumerPlanningMode {
my $hash = shift;
my $c = shift;
my $name = $hash->{NAME};
my $mode = ConsumerVal ($hash, $c, 'mode', $defcmode); # Consumer Planungsmode
if ($mode =~ /^(?:can|must)$/xs) {
return $mode;
}
## Mode kann über Device:Reading gesteuert sein
#################################################
my ($dv, $rd) = split ':', $mode;
my ($err) = isDeviceValid ( { name => $hash->{NAME}, obj => $dv, method => 'string' } );
if ($err) {
Log3 ($name, 1, qq{$name - ERROR - consumer >$c< - The device '$dv' in consumer key 'mode' doesn't exist. Fall back to '$defcmode' mode.});
return $defcmode;
}
$err = q{};
$mode = ReadingsVal ($dv, $rd, '');
if ($mode !~ /^(?:can|must)$/xs) {
Log3 ($name, 1, qq{$name - ERROR - consumer >$c< - The reading '$rd' of device '$dv' is invalid or doesn't contain a valid mode. Fall back to '$defcmode' mode.});
return $defcmode;
}
return $mode;
}
################################################################ ################################################################
# Planungsdaten Consumer löschen # Planungsdaten Consumer löschen
# $c - Consumer Nummer # $c - Consumer Nummer
@ -21234,6 +21299,7 @@ to ensure that the system configuration is correct.
<tr><td> </td><td><b>must</b> - The consumer is optimally planned, even if there will probably not be enough PV surplus. </td></tr> <tr><td> </td><td><b>must</b> - The consumer is optimally planned, even if there will probably not be enough PV surplus. </td></tr>
<tr><td> </td><td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The load is started even if there is insufficient PV surplus, provided that <tr><td> </td><td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; The load is started even if there is insufficient PV surplus, provided that
a set "swoncond" condition is met and "swoffcond" is not met. </td></tr> a set "swoncond" condition is met and "swoffcond" is not met. </td></tr>
<tr><td> </td><td><b>Device:Reading</b> - Device/Reading combination to be able to change the planning mode dynamically. The reading must return 'can' or 'must'. </td></tr>
<tr><td> </td><td> </td></tr> <tr><td> </td><td> </td></tr>
<tr><td> <b>icon</b> </td><td>Icon and, if applicable, its color for displaying the consumer in the overview graphic (optional) </td></tr> <tr><td> <b>icon</b> </td><td>Icon and, if applicable, its color for displaying the consumer in the overview graphic (optional) </td></tr>
<tr><td> </td><td> </td></tr> <tr><td> </td><td> </td></tr>
@ -23655,6 +23721,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
<tr><td> </td><td><b>must</b> - der Verbraucher wird optimiert eingeplant auch wenn wahrscheinlich nicht genügend PV Überschuß vorhanden sein wird </td></tr> <tr><td> </td><td><b>must</b> - der Verbraucher wird optimiert eingeplant auch wenn wahrscheinlich nicht genügend PV Überschuß vorhanden sein wird </td></tr>
<tr><td> </td><td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Der Start des Verbrauchers erfolgt auch bei ungenügendem PV-Überschuß sofern eine <tr><td> </td><td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Der Start des Verbrauchers erfolgt auch bei ungenügendem PV-Überschuß sofern eine
gesetzte "swoncond" Bedingung erfüllt und "swoffcond" nicht erfüllt ist. </td></tr> gesetzte "swoncond" Bedingung erfüllt und "swoffcond" nicht erfüllt ist. </td></tr>
<tr><td> </td><td><b>Device:Reading</b> - Device/Reading Kombination um den Planungsmodus dynamisch ändern zu können. Das Reading muß 'can' oder 'must' zurückgeben. </td></tr>
<tr><td> </td><td> </td></tr> <tr><td> </td><td> </td></tr>
<tr><td> <b>icon</b> </td><td>Icon und ggf. dessen Farbe zur Darstellung des Verbrauchers in der Übersichtsgrafik (optional) </td></tr> <tr><td> <b>icon</b> </td><td>Icon und ggf. dessen Farbe zur Darstellung des Verbrauchers in der Übersichtsgrafik (optional) </td></tr>
<tr><td> </td><td> </td></tr> <tr><td> </td><td> </td></tr>