mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-01-31 00:36:25 +00:00
76_SolarForecast: consumer device may have specified an own alias
git-svn-id: https://svn.fhem.de/fhem/trunk@29559 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
d04d74bb5a
commit
33735a1418
@ -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: consumer device may have specified an own alias
|
||||||
- feature: 73_AutoShuttersControl:
|
- feature: 73_AutoShuttersControl:
|
||||||
https://forum.fhem.de/index.php?topic=136510.0
|
https://forum.fhem.de/index.php?topic=136510.0
|
||||||
- feature: 76_SolarForecast: add temporary Migrate Getter x_migrate,
|
- feature: 76_SolarForecast: add temporary Migrate Getter x_migrate,
|
||||||
|
@ -104,6 +104,8 @@ BEGIN {
|
|||||||
getAllSets
|
getAllSets
|
||||||
HttpUtils_NonblockingGet
|
HttpUtils_NonblockingGet
|
||||||
HttpUtils_BlockingGet
|
HttpUtils_BlockingGet
|
||||||
|
GetFileFromURL
|
||||||
|
GetHttpFile
|
||||||
init_done
|
init_done
|
||||||
InternalTimer
|
InternalTimer
|
||||||
InternalVal
|
InternalVal
|
||||||
@ -157,6 +159,8 @@ BEGIN {
|
|||||||
|
|
||||||
# Versions History intern
|
# Versions History intern
|
||||||
my %vNotesIntern = (
|
my %vNotesIntern = (
|
||||||
|
"1.44.1" => "20.01.2025 Notification system: minor fixes, integration of controls_solarforecast_messages_test/prod ".
|
||||||
|
"Define: random start of Timer subs, consumerXX: consumer device may have specified an own alias ",
|
||||||
"1.44.0" => "19.01.2025 _listDataPoolCircular: may select a dedicated hour, add temporary Migrate funktion x_migrate ".
|
"1.44.0" => "19.01.2025 _listDataPoolCircular: may select a dedicated hour, add temporary Migrate funktion x_migrate ".
|
||||||
"fix interruptable key check in consumer attr Forum:https://forum.fhem.de/index.php?msg=1331073 ".
|
"fix interruptable key check in consumer attr Forum:https://forum.fhem.de/index.php?msg=1331073 ".
|
||||||
"set prdef to 1.0, Implementation of a Messaging System ",
|
"set prdef to 1.0, Implementation of a Messaging System ",
|
||||||
@ -423,6 +427,7 @@ my $calcmaxd = 30;
|
|||||||
my @dweattrmust = qw(TTT Neff RR1c ww SunUp SunRise SunSet); # Werte die im Attr forecastProperties des Weather-DWD_Opendata Devices mindestens gesetzt sein müssen
|
my @dweattrmust = qw(TTT Neff RR1c ww SunUp SunRise SunSet); # Werte die im Attr forecastProperties des Weather-DWD_Opendata Devices mindestens gesetzt sein müssen
|
||||||
my @draattrmust = qw(Rad1h); # Werte die im Attr forecastProperties des Radiation-DWD_Opendata Devices mindestens gesetzt sein müssen
|
my @draattrmust = qw(Rad1h); # Werte die im Attr forecastProperties des Radiation-DWD_Opendata Devices mindestens gesetzt sein müssen
|
||||||
my $whistrepeat = 851; # Wiederholungsintervall Cache File Daten schreiben
|
my $whistrepeat = 851; # Wiederholungsintervall Cache File Daten schreiben
|
||||||
|
my $gmfblto = 30; # Timeout Aholen Message File aus contrib
|
||||||
|
|
||||||
my $solapirepdef = 3600; # default Abrufintervall SolCast API (s)
|
my $solapirepdef = 3600; # default Abrufintervall SolCast API (s)
|
||||||
my $forapirepdef = 900; # default Abrufintervall ForecastSolar API (s)
|
my $forapirepdef = 900; # default Abrufintervall ForecastSolar API (s)
|
||||||
@ -489,10 +494,14 @@ my $actcoldef = 'orange';
|
|||||||
my $inactcoldef = 'grey'; # default Färbung Icon wenn inaktiv
|
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
|
my $bPath = 'https://svn.fhem.de/trac/browser/trunk/fhem/contrib/SolarForecast/'; # Basispfad Abruf contrib SolarForecast Files
|
||||||
my $pPath = '?format=txt'; # Download Format
|
my $cfile = 'controls_solarforecast.txt'; # Controlfile Update FTUI-Files
|
||||||
my $cfile = 'controls_solarforecast.txt'; # Name des Controlfiles
|
my $msgfiletest = 'controls_solarforecast_messages_test.txt'; # TEST Input-File Notification System
|
||||||
|
my $msgfileprod = 'controls_solarforecast_messages_prod.txt'; # PRODUKTIVES Input-File Notification System
|
||||||
|
my $pPath = '?format=txt'; # Download Format
|
||||||
|
my $gmfilerepeat = 3000; # Wiederholungsuntervall Aholen Message File aus contrib
|
||||||
|
my $idxlimit = 900000; # Notification System: Indexe > $idxlimit sind reserviert für Steuerungsaufgaben
|
||||||
|
|
||||||
|
my $messagefile = $msgfileprod;
|
||||||
# mögliche Debug-Module
|
# mögliche Debug-Module
|
||||||
my @dd = qw( aiProcess
|
my @dd = qw( aiProcess
|
||||||
aiData
|
aiData
|
||||||
@ -803,12 +812,14 @@ my %hqtxt = ( # H
|
|||||||
DE => qq{heute} },
|
DE => qq{heute} },
|
||||||
simsg => { EN => qq{Message},
|
simsg => { EN => qq{Message},
|
||||||
DE => qq{Mitteilung} },
|
DE => qq{Mitteilung} },
|
||||||
msgsys => { EN => qq{Messaging system},
|
msgsys => { EN => qq{Notification system},
|
||||||
DE => qq{Mitteilungssystem} },
|
DE => qq{Mitteilungssystem} },
|
||||||
msgimp => { EN => qq{Importance},
|
msgimp => { EN => qq{Importance},
|
||||||
DE => qq{Wichtigkeit} },
|
DE => qq{Wichtigkeit} },
|
||||||
number => { EN => qq{Number},
|
number => { EN => qq{Number},
|
||||||
DE => qq{Nummer} },
|
DE => qq{Nummer} },
|
||||||
|
ludich => { EN => qq{last update Input channels},
|
||||||
|
DE => qq{letzte Aktualisierung Eingangskanäle} },
|
||||||
ctnsly => { EN => qq{continuously},
|
ctnsly => { EN => qq{continuously},
|
||||||
DE => qq{fortlaufend} },
|
DE => qq{fortlaufend} },
|
||||||
yday => { EN => qq{yesterday},
|
yday => { EN => qq{yesterday},
|
||||||
@ -871,6 +882,8 @@ my %hqtxt = ( # H
|
|||||||
DE => qq{Oh nein 😢, die Anlagenkonfiguration ist fehlerhaft. Bitte überprüfen Sie die Einstellungen und Hinweise!} },
|
DE => qq{Oh nein 😢, die Anlagenkonfiguration ist fehlerhaft. Bitte überprüfen Sie die Einstellungen und Hinweise!} },
|
||||||
pstate => { EN => qq{Planning status: <pstate><br>Info: <supplmnt><br>Mode: <mode><br>On: <start><br>Off: <stop><br>Remaining lock time: <RLT> seconds},
|
pstate => { EN => qq{Planning status: <pstate><br>Info: <supplmnt><br>Mode: <mode><br>On: <start><br>Off: <stop><br>Remaining lock time: <RLT> seconds},
|
||||||
DE => qq{Planungsstatus: <pstate><br>Info: <supplmnt><br>Modus: <mode><br>Ein: <start><br>Aus: <stop><br>verbleibende Sperrzeit: <RLT> Sekunden} },
|
DE => qq{Planungsstatus: <pstate><br>Info: <supplmnt><br>Modus: <mode><br>Ein: <start><br>Aus: <stop><br>verbleibende Sperrzeit: <RLT> Sekunden} },
|
||||||
|
dmgsig => { EN => qq{Read messages are not signaled again until a FHEM restart, but are retained if they are relevant.},
|
||||||
|
DE => qq{Gelesene Mitteilungen werden bis zu einem FHEM Neustart nicht wieder signalisiert, bleiben bei Relevanz jedoch erhalten.} },
|
||||||
);
|
);
|
||||||
|
|
||||||
my %htitles = ( # Hash Hilfetexte (Mouse Over)
|
my %htitles = ( # Hash Hilfetexte (Mouse Over)
|
||||||
@ -1301,7 +1314,9 @@ my %hfspvh = (
|
|||||||
# $data{$name}{dwdcatalog} # temporärer Speicher DWD Stationskatalog
|
# $data{$name}{dwdcatalog} # temporärer Speicher DWD Stationskatalog
|
||||||
# $data{$name}{strings} # temporärer Speicher Stringkonfiguration
|
# $data{$name}{strings} # temporärer Speicher Stringkonfiguration
|
||||||
# $data{$name}{aidectree}{object} # AI Decision Tree Object
|
# $data{$name}{aidectree}{object} # AI Decision Tree Object
|
||||||
# $data{$name}{messages} # Speicher Mitteilungssystem
|
# $data{$name}{messages} # Mitteilungssystem - permanent erneuerter Speicher
|
||||||
|
# $data{$name}{filemessages} # Mitteilungssystem - Input vom Message File
|
||||||
|
# $data{$name}{preparedmessages} # Mitteilungssystem - vorbereitete Messages innerhalb des Code
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
# Init Fn
|
# Init Fn
|
||||||
@ -1479,9 +1494,10 @@ sub Define {
|
|||||||
reloadCacheFiles ($params);
|
reloadCacheFiles ($params);
|
||||||
singleUpdateState ( {hash => $hash, state => 'initialized', evt => 1} );
|
singleUpdateState ( {hash => $hash, state => 'initialized', evt => 1} );
|
||||||
|
|
||||||
$readyfnlist{$name} = $hash; # Registrierung in Ready-Schleife
|
$readyfnlist{$name} = $hash; # Registrierung in Ready-Schleife
|
||||||
InternalTimer (gettimeofday()+$whistrepeat, "FHEM::SolarForecast::periodicWriteMemcache", $hash, 0); # Einstieg periodisches Schreiben historische Daten
|
InternalTimer (gettimeofday() + $whistrepeat + int(rand(300)), "FHEM::SolarForecast::periodicWriteMemcache", $hash, 0); # Einstieg periodisches Schreiben historische Daten
|
||||||
|
InternalTimer (gettimeofday() + 120 + int(rand(300)), "FHEM::SolarForecast::getMessageFileNonBlocking", $hash, 0);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4661,7 +4677,7 @@ sub _getoutputMessages { ## no critic "not used"
|
|||||||
my $out = outputMessages ($paref);
|
my $out = outputMessages ($paref);
|
||||||
$out = qq{<html>$out</html>};
|
$out = qq{<html>$out</html>};
|
||||||
|
|
||||||
$data{$name}{messages}{9999}{RD} = 1; # Lesekennzeichen setzen
|
$data{$name}{messages}{999999}{RD} = 1; # Lesekennzeichen setzen
|
||||||
|
|
||||||
## asynchrone Ausgabe
|
## asynchrone Ausgabe
|
||||||
#######################
|
#######################
|
||||||
@ -5400,21 +5416,10 @@ sub __updPreFile {
|
|||||||
$err .= "Please check whether the path $path is present and accessible.<br>";
|
$err .= "Please check whether the path $path is present and accessible.<br>";
|
||||||
$err .= "After installing FTUI, come back and execute the get command again.";
|
$err .= "After installing FTUI, come back and execute the get command again.";
|
||||||
return $err;
|
return $err;
|
||||||
|
|
||||||
#my $ok = mkdir $path;
|
|
||||||
|
|
||||||
#if (!$ok) {
|
|
||||||
# $err = "MKDIR ERROR: $!";
|
|
||||||
# Log3 ($name, 1, "$name - $err");
|
|
||||||
# return $err;
|
|
||||||
#}
|
|
||||||
#else {
|
|
||||||
# Log3 ($name, 3, "$name - MKDIR $path");
|
|
||||||
#}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
($err, my $remFile) = __updGetUrl ($name, $bPath.$file.$pPath);
|
($err, my $remFile) = __httpBlockingGet ($name, $bPath.$file.$pPath);
|
||||||
|
|
||||||
if ($err) {
|
if ($err) {
|
||||||
Log3 ($name, 1, "$name - $err");
|
Log3 ($name, 1, "$name - $err");
|
||||||
@ -5446,27 +5451,27 @@ return;
|
|||||||
###############################################################
|
###############################################################
|
||||||
# File von url holen
|
# File von url holen
|
||||||
###############################################################
|
###############################################################
|
||||||
sub __updGetUrl {
|
sub __httpBlockingGet {
|
||||||
my $name = shift;
|
my $name = shift;
|
||||||
my $url = shift;
|
my $url = shift;
|
||||||
|
|
||||||
$url =~ s/%/%25/g;
|
$url =~ s/%/%25/g;
|
||||||
my %upd_connecthash;
|
my %connecthash;
|
||||||
my $unicodeEncoding = 1;
|
my $unicodeEncoding = 1;
|
||||||
|
|
||||||
$upd_connecthash{url} = $url;
|
$connecthash{url} = $url;
|
||||||
$upd_connecthash{keepalive} = ($url =~ m/localUpdate/ ? 0 : 1); # Forum #49798
|
$connecthash{keepalive} = ($url =~ m/localUpdate/ ? 0 : 1); # Forum #49798
|
||||||
$upd_connecthash{forceEncoding} = '' if($unicodeEncoding);
|
$connecthash{forceEncoding} = '' if($unicodeEncoding);
|
||||||
|
|
||||||
my ($err, $dat) = HttpUtils_BlockingGet (\%upd_connecthash);
|
my ($err, $dat) = HttpUtils_BlockingGet (\%connecthash);
|
||||||
|
|
||||||
if ($err) {
|
if ($err) {
|
||||||
$err = "update ERROR: $err";
|
$err = "GetUrl ERROR: $err";
|
||||||
return ($err, '');
|
return ($err, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$dat) {
|
if (!$dat) {
|
||||||
$err = 'update ERROR: empty file received';
|
$err = 'WARNING - empty file received';
|
||||||
return ($err, '');
|
return ($err, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7347,7 +7352,7 @@ sub runTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
releaseCentralTask ($hash);
|
releaseCentralTask ($hash);
|
||||||
centralTask ($hash, 1);
|
centralTask ($hash, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -7363,13 +7368,13 @@ sub runTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
releaseCentralTask ($hash);
|
releaseCentralTask ($hash);
|
||||||
centralTask ($hash, 1);
|
centralTask ($hash, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
delete $hash->{HELPER}{S03DONE};
|
delete $hash->{HELPER}{S03DONE};
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7638,6 +7643,7 @@ sub centralTask {
|
|||||||
setTimeTracking ($hash, $cst, 'runTimeCentralTask'); # Zyklus-Laufzeit ermitteln
|
setTimeTracking ($hash, $cst, 'runTimeCentralTask'); # Zyklus-Laufzeit ermitteln
|
||||||
|
|
||||||
createReadingsFromArray ($hash, $evt); # Readings erzeugen
|
createReadingsFromArray ($hash, $evt); # Readings erzeugen
|
||||||
|
_readSystemMessages ($centpars); # Notification System - System Messages zusammenstellen
|
||||||
|
|
||||||
if ($evt) {
|
if ($evt) {
|
||||||
$centpars->{evt} = $evt;
|
$centpars->{evt} = $evt;
|
||||||
@ -7822,10 +7828,10 @@ sub _collectAllRegConsumers {
|
|||||||
|
|
||||||
for my $c (1..$maxconsumer) {
|
for my $c (1..$maxconsumer) {
|
||||||
$c = sprintf "%02d", $c;
|
$c = sprintf "%02d", $c;
|
||||||
my ($err, $consumer, $hc) = isDeviceValid ( { name => $name, obj => "consumer${c}", method => 'attr' } );
|
my ($err, $consumer, $hc, $alias) = isDeviceValid ( { name => $name, obj => "consumer${c}", method => 'attr' } );
|
||||||
next if($err);
|
next if($err);
|
||||||
|
|
||||||
push @{$data{$name}{current}{consumerdevs}}, $consumer; # alle Consumerdevices in CurrentHash eintragen
|
push @{$data{$name}{current}{consumerdevs}}, $consumer; # alle Consumerdevices in CurrentHash eintragen
|
||||||
|
|
||||||
my $dswitch = $hc->{switchdev}; # alternatives Schaltdevice
|
my $dswitch = $hc->{switchdev}; # alternatives Schaltdevice
|
||||||
|
|
||||||
@ -7833,13 +7839,13 @@ sub _collectAllRegConsumers {
|
|||||||
my ($err) = isDeviceValid ( { name => $name, obj => $dswitch, method => 'string' } );
|
my ($err) = isDeviceValid ( { name => $name, obj => $dswitch, method => 'string' } );
|
||||||
next if($err);
|
next if($err);
|
||||||
|
|
||||||
push @{$data{$name}{current}{consumerdevs}}, $dswitch if($dswitch ne $consumer); # Switchdevice zusätzlich in CurrentHash eintragen
|
push @{$data{$name}{current}{consumerdevs}}, $dswitch if($dswitch ne $consumer); # Switchdevice zusätzlich in CurrentHash eintragen
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$dswitch = $consumer;
|
$dswitch = $consumer;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $alias = AttrVal ($consumer, 'alias', $consumer);
|
$alias = AttrVal ($consumer, 'alias', $consumer) if(!$alias);
|
||||||
|
|
||||||
my ($rtot,$utot,$ethreshold);
|
my ($rtot,$utot,$ethreshold);
|
||||||
if (exists $hc->{etotal}) {
|
if (exists $hc->{etotal}) {
|
||||||
@ -9912,7 +9918,7 @@ return $sf;
|
|||||||
}
|
}
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
# Erstellung Batterie Ladefreigabe
|
# Erstellung Batterie Ladefreigabe + SoC Prognose
|
||||||
################################################################
|
################################################################
|
||||||
sub _batChargeRecmd {
|
sub _batChargeRecmd {
|
||||||
my $paref = shift;
|
my $paref = shift;
|
||||||
@ -10046,11 +10052,7 @@ sub _batChargeRecmd {
|
|||||||
|
|
||||||
$socwh = sprintf "%.0f", $socwh;
|
$socwh = sprintf "%.0f", $socwh;
|
||||||
my $progsoc = sprintf "%.1f", (100 * $socwh / $batinstcap); # Prognose SoC in %
|
my $progsoc = sprintf "%.1f", (100 * $socwh / $batinstcap); # Prognose SoC in %
|
||||||
|
|
||||||
#$progsoc = $progsoc < $batoptsoc ? $batoptsoc :
|
|
||||||
# $progsoc < $lowSoc ? $lowSoc :
|
|
||||||
# $progsoc;
|
|
||||||
|
|
||||||
__createNextHoursSFCReadings ( {name => $name,
|
__createNextHoursSFCReadings ( {name => $name,
|
||||||
nhr => $nhr,
|
nhr => $nhr,
|
||||||
bn => $bn,
|
bn => $bn,
|
||||||
@ -12626,65 +12628,6 @@ sub __calcFcQuality {
|
|||||||
return $hdv;
|
return $hdv;
|
||||||
}
|
}
|
||||||
|
|
||||||
################################################################
|
|
||||||
# Berechnen Tag / Stunden Verschieber
|
|
||||||
# aus aktueller Stunde + lfd. Nummer
|
|
||||||
################################################################
|
|
||||||
sub calcDayHourMove {
|
|
||||||
my $chour = shift;
|
|
||||||
my $num = shift;
|
|
||||||
|
|
||||||
my $fh = $chour + $num;
|
|
||||||
my $fd = int ($fh / 24) ;
|
|
||||||
$fh = $fh - ($fd * 24);
|
|
||||||
|
|
||||||
return ($fd, $fh);
|
|
||||||
}
|
|
||||||
|
|
||||||
################################################################
|
|
||||||
# Zeit gemäß DWD_OpenData-Format
|
|
||||||
# Berechnen Tag / Stunden Verschieber ab aktuellen Tag
|
|
||||||
# Input: YYYY-MM-DD HH:MM:SS
|
|
||||||
# Output: $fd - 0 (Heute), 1 (Morgen), 2 (Übermorgen), ....
|
|
||||||
# $fh - Stunde von $fd ohne führende Null
|
|
||||||
# Return: fc${fd}_${fh}
|
|
||||||
################################################################
|
|
||||||
sub formatWeatherTimestrg {
|
|
||||||
my $date = shift // return;
|
|
||||||
|
|
||||||
my $cdate = strftime "%Y-%m-%d", localtime(time);
|
|
||||||
my $refts = timestringToTimestamp ($cdate.' 00:00:00'); # Referenztimestring
|
|
||||||
my $datts = timestringToTimestamp ($date);
|
|
||||||
my $fd = int (($datts - $refts) / 86400);
|
|
||||||
my $fh = int ((split /[ :]/, $date)[1]);
|
|
||||||
|
|
||||||
return "fc${fd}_${fh}";
|
|
||||||
}
|
|
||||||
|
|
||||||
################################################################
|
|
||||||
# Spezialfall auflösen wenn Wert von $val2 dem
|
|
||||||
# Redingwert von $val1 entspricht sofern $val1 negativ ist
|
|
||||||
################################################################
|
|
||||||
sub substSpecialCases {
|
|
||||||
my $paref = shift;
|
|
||||||
my $dev = $paref->{dev};
|
|
||||||
my $rdg = $paref->{rdg};
|
|
||||||
my $rdgf = $paref->{rdgf};
|
|
||||||
|
|
||||||
my $val1 = ReadingsNum ($dev, $rdg, 0) * $rdgf;
|
|
||||||
my $val2;
|
|
||||||
|
|
||||||
if($val1 <= 0) {
|
|
||||||
$val2 = abs($val1);
|
|
||||||
$val1 = 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$val2 = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ($val1,$val2);
|
|
||||||
}
|
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
# Energieverbrauch des Hauses in History speichern
|
# Energieverbrauch des Hauses in History speichern
|
||||||
################################################################
|
################################################################
|
||||||
@ -12970,6 +12913,36 @@ sub _genSpecialReadings {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
###################################################################################
|
||||||
|
# Messagefile für Notification System lesen
|
||||||
|
# Filestruktur:
|
||||||
|
# 0|SV|1
|
||||||
|
# 0|DE|Mitteilung ....
|
||||||
|
# 0|EN|Message...
|
||||||
|
# $data{$name}{preparedmessages}{999500}{TS}: Timestamp Stand prepared Messages
|
||||||
|
###################################################################################
|
||||||
|
sub _readSystemMessages {
|
||||||
|
my $paref = shift;
|
||||||
|
my $name = $paref->{name};
|
||||||
|
|
||||||
|
delete $data{$name}{preparedmessages};
|
||||||
|
|
||||||
|
my $midx = 0;
|
||||||
|
|
||||||
|
if (!ReadingsVal ($name, '.migrated', 0)) {
|
||||||
|
$midx++;
|
||||||
|
$data{$name}{preparedmessages}{$midx}{SV} = 1;
|
||||||
|
$data{$name}{preparedmessages}{$midx}{DE} = 'Die gespeicherten PV Daten können mit "get ... x_migrate" in ein neues Format umgesetzt werden welches den Median Ansatz bei der PV Prognose aktiviert und nutzt.';
|
||||||
|
$data{$name}{preparedmessages}{$midx}{DE} .= '<br>Mit einem späteren Update des Moduls erfolgt diese Umstellung automatisch.';
|
||||||
|
$data{$name}{preparedmessages}{$midx}{EN} = 'The stored PV data can be converted with “get ... x_migrate” into a new format which activates and uses the median approach in the PV forecast.';
|
||||||
|
$data{$name}{preparedmessages}{$midx}{EN} .= '<br>With a later update of the module, this changeover will take place automatically.';
|
||||||
|
}
|
||||||
|
|
||||||
|
$data{$name}{preparedmessages}{999500}{TS} = time;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
# FHEMWEB Fn
|
# FHEMWEB Fn
|
||||||
################################################################
|
################################################################
|
||||||
@ -13483,7 +13456,7 @@ sub _graphicHeader {
|
|||||||
|
|
||||||
## Message-Icon
|
## Message-Icon
|
||||||
#################
|
#################
|
||||||
my ($micon, $midx) = __fillupMessages ($paref);
|
my ($micon, $midx) = fillupMessageSystem ($paref);
|
||||||
$img = FW_makeImage ($micon);
|
$img = FW_makeImage ($micon);
|
||||||
my $msgicon = $midx ? "<a onClick=$cmdoutmsg>$img</a>" : $img;
|
my $msgicon = $midx ? "<a onClick=$cmdoutmsg>$img</a>" : $img;
|
||||||
my $msgtitle = $midx ? $htitles{outpmsg}{$lang} : $htitles{nomsgfo}{$lang};
|
my $msgtitle = $midx ? $htitles{outpmsg}{$lang} : $htitles{nomsgfo}{$lang};
|
||||||
@ -13766,47 +13739,6 @@ sub _graphicHeader {
|
|||||||
return $header;
|
return $header;
|
||||||
}
|
}
|
||||||
|
|
||||||
################################################################
|
|
||||||
# Mitteilungssystem füllen
|
|
||||||
# Schweregrad SV:
|
|
||||||
# 0 - keine Mitteilung
|
|
||||||
# 1 - Mitteilung
|
|
||||||
# 2 - Warnung
|
|
||||||
# 3 - Fehler / Problem
|
|
||||||
################################################################
|
|
||||||
sub __fillupMessages {
|
|
||||||
my $paref = shift;
|
|
||||||
my $name = $paref->{name};
|
|
||||||
my $lang = $paref->{lang};
|
|
||||||
|
|
||||||
for my $idx (keys %{$data{$name}{messages}}) {
|
|
||||||
next if($idx == 9999);
|
|
||||||
delete $data{$name}{messages}{$idx};
|
|
||||||
}
|
|
||||||
|
|
||||||
my $midx = 0;
|
|
||||||
my $max_sv = 0;
|
|
||||||
|
|
||||||
if (!ReadingsVal ($name, '.migrated', 0)) {
|
|
||||||
$midx++;
|
|
||||||
$data{$name}{messages}{$midx}{SV} = 1;
|
|
||||||
$data{$name}{messages}{$midx}{DE} = 'Die gespeicherten PV Daten können mit "get ... x_migrate" in ein neues Format umgesetzt werden welches den Median Ansatz bei der PV Prognose aktiviert und nutzt.';
|
|
||||||
$data{$name}{messages}{$midx}{DE} .= '<br>Mit einem späteren Update des Moduls erfolgt diese Umstellung automatisch.';
|
|
||||||
$data{$name}{messages}{$midx}{EN} = 'The stored PV data can be converted with “get ... x_migrate” into a new format which activates and uses the median approach in the PV forecast.';
|
|
||||||
$data{$name}{messages}{$midx}{EN} .= '<br>With a later update of the module, this changeover will take place automatically.';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($midx && !defined $data{$name}{messages}{9999}{RD}) { # RD = Read-Bit
|
|
||||||
my @aidx = map { $_ } (1..$midx); # größte vorhandene Severity finden
|
|
||||||
my @values = map { $data{$name}{messages}{$_}{SV} } @aidx;
|
|
||||||
$max_sv = max(@values);
|
|
||||||
}
|
|
||||||
|
|
||||||
my $max_icon = $svicons{$max_sv}; # ... und das dazugehörige Icon
|
|
||||||
|
|
||||||
return ($max_icon, $midx);
|
|
||||||
}
|
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
# erstelle Update-Icon
|
# erstelle Update-Icon
|
||||||
################################################################
|
################################################################
|
||||||
@ -16501,6 +16433,313 @@ sub _addHourAiRawdata {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
###############################################################
|
||||||
|
# Abruf und Einlesen Messagefile nonBlocking
|
||||||
|
###############################################################
|
||||||
|
sub getMessageFileNonBlocking {
|
||||||
|
my $hash = shift;
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
|
||||||
|
RemoveInternalTimer ($hash, "FHEM::SolarForecast::getMessageFileNonBlocking");
|
||||||
|
InternalTimer (gettimeofday() + $gmfilerepeat, "FHEM::SolarForecast::getMessageFileNonBlocking", $hash, 0);
|
||||||
|
|
||||||
|
my (undef, $disabled, $inactive) = controller ($name);
|
||||||
|
return if($disabled || $inactive);
|
||||||
|
|
||||||
|
delete $hash->{HELPER}{GMFRUNNING} if(defined $hash->{HELPER}{GMFRUNNING}{pid} && $hash->{HELPER}{GMFRUNNING}{pid} =~ /DEAD/xs);
|
||||||
|
|
||||||
|
if (defined $hash->{HELPER}{GMFRUNNING}{pid}) {
|
||||||
|
Log3 ($name, 3, qq{$name - another Message File Process with PID "$hash->{HELPER}{GMFRUNNING}{pid}" is already running ... get Message File is aborted});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log3 ($name, 4, "$name - Notification System - Message file >$messagefile< is retrieved non blocking");
|
||||||
|
|
||||||
|
my $paref = { name => $name,
|
||||||
|
hash => $hash,
|
||||||
|
block => 1
|
||||||
|
};
|
||||||
|
|
||||||
|
$hash->{HELPER}{GMFRUNNING} = BlockingCall ( "FHEM::SolarForecast::_retrieveMessageFile",
|
||||||
|
$paref,
|
||||||
|
"FHEM::SolarForecast::_processMessageFile",
|
||||||
|
$gmfblto,
|
||||||
|
"FHEM::SolarForecast::_abortGetMessageFile",
|
||||||
|
$hash
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
if (defined $hash->{HELPER}{GMFRUNNING}) {
|
||||||
|
$hash->{HELPER}{GMFRUNNING}{loglevel} = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################
|
||||||
|
# Message File aus contrib abholen
|
||||||
|
###############################################################
|
||||||
|
sub _retrieveMessageFile {
|
||||||
|
my $paref = shift;
|
||||||
|
my $name = $paref->{name};
|
||||||
|
my $block = $paref->{block} // 0;
|
||||||
|
|
||||||
|
my $valid = 1;
|
||||||
|
my ($err, $remfile) = __httpBlockingGet ($name, $bPath.$messagefile.$pPath);
|
||||||
|
|
||||||
|
$remfile = q{} if($remfile =~ /No\snode\strunk\/fhem\/contrib\/SolarForecast\//xs);
|
||||||
|
|
||||||
|
if ($err) {
|
||||||
|
$valid = 0;
|
||||||
|
Log3 ($name, 4, "$name - Notification System - retrieve of remote Message File faulty: $err");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$remfile) {
|
||||||
|
$valid = 0;
|
||||||
|
Log3 ($name, 4, "$name - Notification System - no remote Message File >$messagefile< found");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($valid) {
|
||||||
|
$err = __updWriteFile ("$root/FHEM/", $messagefile, $remfile);
|
||||||
|
|
||||||
|
if ($err) {
|
||||||
|
$valid = 0;
|
||||||
|
Log3 ($name, 1, "$name - $err");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Log3 ($name, 4, "$name - Notification System - new Message File updated to $root/FHEM/$messagefile");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$paref->{valid} = $valid;
|
||||||
|
my $serial = encode_base64 (Serialize ( $paref ), ""); # Serialisierung
|
||||||
|
|
||||||
|
$block ? return ($serial) : return \&_processMessageFile ($serial);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
###############################################################
|
||||||
|
# Folgeroutine nach Message File aus contrib abholen
|
||||||
|
###############################################################
|
||||||
|
sub _processMessageFile {
|
||||||
|
my $serial = decode_base64 (shift);
|
||||||
|
|
||||||
|
my $paref = eval { thaw ($serial) }; # Deserialisierung
|
||||||
|
my $name = $paref->{name};
|
||||||
|
my $valid = $paref->{valid};
|
||||||
|
my $hash = $defs{$name};
|
||||||
|
|
||||||
|
if ($valid) {
|
||||||
|
__readFileMessages ($paref);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# Messagefile für Notification System lesen
|
||||||
|
# Filestruktur:
|
||||||
|
# 0|SV|1
|
||||||
|
# 0|DE|Mitteilung ....
|
||||||
|
# 0|EN|Message...
|
||||||
|
# $data{$name}{messages}{999000}{TS}: Timestamp Stand Message File
|
||||||
|
######################################################################
|
||||||
|
sub __readFileMessages {
|
||||||
|
my $paref = shift;
|
||||||
|
my $name = $paref->{name};
|
||||||
|
|
||||||
|
my $hash = $defs{$name};
|
||||||
|
|
||||||
|
open (FD, "$root/FHEM/$messagefile") or do { return $! };
|
||||||
|
|
||||||
|
delete $data{$name}{filemessages};
|
||||||
|
|
||||||
|
my @locList = map { $_ =~ s/[\r\n]//; $_ } <FD>;
|
||||||
|
close (FD);
|
||||||
|
|
||||||
|
Log3 ($name, 4, "$name - Notification System - read local Message File >$messagefile< with ".scalar @locList." entries.");
|
||||||
|
|
||||||
|
for my $l (@locList) {
|
||||||
|
next if ($l =~ /^\#/xs);
|
||||||
|
my @l = split /\|/, $l, 3;
|
||||||
|
next if(!isNumeric ($l[0]));
|
||||||
|
next if($l[1] !~ /^(DE|EN|SV)$/xs);
|
||||||
|
|
||||||
|
$data{$name}{filemessages}{$l[0]}{$l[1]} = $l[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
$data{$name}{filemessages}{999000}{TS} = time;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
####################################################################################################
|
||||||
|
# Abbruchroutine BlockingCall Timeout
|
||||||
|
####################################################################################################
|
||||||
|
sub _abortGetMessageFile {
|
||||||
|
my $hash = shift;
|
||||||
|
my $cause = shift // "Timeout: process terminated";
|
||||||
|
my $name = $hash->{NAME};
|
||||||
|
my $type = $hash->{TYPE};
|
||||||
|
|
||||||
|
Log3 ($name, 1, "$name -> BlockingCall $hash->{HELPER}{GMFRUNNING}{fn} pid:$hash->{HELPER}{AIBLOCKRUNNING}{pid} aborted: $cause");
|
||||||
|
|
||||||
|
delete $hash->{HELPER}{GMFRUNNING};
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# Mitteilungssystem füllen
|
||||||
|
# Schweregrad SV:
|
||||||
|
# 0 - keine Mitteilung
|
||||||
|
# 1 - Mitteilung
|
||||||
|
# 2 - Warnung
|
||||||
|
# 3 - Fehler / Problem
|
||||||
|
#
|
||||||
|
# Statusspeicher:
|
||||||
|
# $data{$name}{messages}{999999}{RD}: 1 - gelesen, 0 - ungelesen
|
||||||
|
# $data{$name}{messages}{999000}{TS}: Timestamp Stand Message File
|
||||||
|
# $data{$name}{messages}{999500}{TS}: Timestamp Stand prepared Messages
|
||||||
|
##########################################################################
|
||||||
|
sub fillupMessageSystem {
|
||||||
|
my $paref = shift;
|
||||||
|
my $hash = $paref->{hash};
|
||||||
|
my $name = $paref->{name};
|
||||||
|
my $lang = $paref->{lang};
|
||||||
|
|
||||||
|
my $otxt = q{};
|
||||||
|
my $ntxt = q{};
|
||||||
|
my $midx = 0;
|
||||||
|
my $max_sv = 0;
|
||||||
|
|
||||||
|
## Aufnahme Stand für alt/neu Vergleich + Clear Messages
|
||||||
|
##########################################################
|
||||||
|
for my $idx (sort keys %{$data{$name}{messages}}) {
|
||||||
|
next if($idx >= $idxlimit);
|
||||||
|
$otxt .= $data{$name}{messages}{$idx}{SV} if(defined $data{$name}{messages}{$idx}{SV});
|
||||||
|
$otxt .= $data{$name}{messages}{$idx}{DE} if(defined $data{$name}{messages}{$idx}{DE});
|
||||||
|
$otxt .= $data{$name}{messages}{$idx}{EN} if(defined $data{$name}{messages}{$idx}{EN});
|
||||||
|
|
||||||
|
delete $data{$name}{messages}{$idx};
|
||||||
|
}
|
||||||
|
|
||||||
|
## Messages füllen
|
||||||
|
########################################################################
|
||||||
|
# Integration File Messages
|
||||||
|
for my $mfi (sort keys %{$data{$name}{filemessages}}) {
|
||||||
|
next if($mfi >= $idxlimit);
|
||||||
|
$midx++;
|
||||||
|
$data{$name}{messages}{$midx}{SV} = $data{$name}{filemessages}{$mfi}{SV};
|
||||||
|
$data{$name}{messages}{$midx}{DE} = $data{$name}{filemessages}{$mfi}{DE};
|
||||||
|
$data{$name}{messages}{$midx}{EN} = $data{$name}{filemessages}{$mfi}{EN};
|
||||||
|
}
|
||||||
|
|
||||||
|
# Integration prepared Messages
|
||||||
|
for my $smi (sort keys %{$data{$name}{preparedmessages}}) {
|
||||||
|
next if($smi >= $idxlimit);
|
||||||
|
$midx++;
|
||||||
|
$data{$name}{messages}{$midx}{SV} = $data{$name}{preparedmessages}{$smi}{SV};
|
||||||
|
$data{$name}{messages}{$midx}{DE} = $data{$name}{preparedmessages}{$smi}{DE};
|
||||||
|
$data{$name}{messages}{$midx}{EN} = $data{$name}{preparedmessages}{$smi}{EN};
|
||||||
|
}
|
||||||
|
|
||||||
|
$data{$name}{messages}{999000}{TS} = $data{$name}{filemessages}{999000}{TS} // 0;
|
||||||
|
$data{$name}{messages}{999500}{TS} = $data{$name}{preparedmessages}{999500}{TS} // 0;
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
## Ende Messages auffüllen
|
||||||
|
|
||||||
|
|
||||||
|
## Vergleich auf geänderte Messages
|
||||||
|
#####################################
|
||||||
|
for my $idx (sort keys %{$data{$name}{messages}}) {
|
||||||
|
next if($idx >= $idxlimit);
|
||||||
|
$ntxt .= $data{$name}{messages}{$idx}{SV} if(defined $data{$name}{messages}{$idx}{SV});
|
||||||
|
$ntxt .= $data{$name}{messages}{$idx}{DE} if(defined $data{$name}{messages}{$idx}{DE});
|
||||||
|
$ntxt .= $data{$name}{messages}{$idx}{EN} if(defined $data{$name}{messages}{$idx}{EN});
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ntxt ne $otxt) { # es gibt neue Post! bzw. Änderungen -> Read-Bit läschen
|
||||||
|
delete $data{$name}{messages}{999999}{RD};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ($midx && !defined $data{$name}{messages}{999999}{RD}) { # RD = Read-Bit (undef -> Messages nicht gelesen)
|
||||||
|
my @aidx = map { $_ } (1..$midx); # größte vorhandene Severity finden ...
|
||||||
|
my @values = map { $data{$name}{messages}{$_}{SV} } @aidx;
|
||||||
|
$max_sv = max(@values);
|
||||||
|
}
|
||||||
|
|
||||||
|
my $max_icon = $svicons{$max_sv}; # ... und das dazugehörige Icon
|
||||||
|
|
||||||
|
return ($max_icon, $midx);
|
||||||
|
}
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# Ausgabe des Mitteilungsystems
|
||||||
|
################################################################
|
||||||
|
sub outputMessages {
|
||||||
|
my $paref = shift;
|
||||||
|
my $name = $paref->{name};
|
||||||
|
my $lang = $paref->{lang};
|
||||||
|
|
||||||
|
my ($micon, $midx) = fillupMessageSystem ($paref); # Ergebnisse füllen (sind leer wenn Browser nicht refreshed)
|
||||||
|
my $tnf = $data{$name}{messages}{999000}{TS} ?
|
||||||
|
(timestampToTimestring ($data{$name}{messages}{999000}{TS}, $lang))[0] :
|
||||||
|
'n.a.';
|
||||||
|
my $tpm = $data{$name}{messages}{999500}{TS} ?
|
||||||
|
(timestampToTimestring ($data{$name}{messages}{999500}{TS}, $lang))[0] :
|
||||||
|
'n.a.';
|
||||||
|
## Ausgabe
|
||||||
|
############
|
||||||
|
my $out = qq{<html>};
|
||||||
|
$out .= qq{<b>$hqtxt{msgsys}{$lang}</b> <br><br>};
|
||||||
|
$out .= qq{$hqtxt{ludich}{$lang} - <b>File:</b> $tnf, <b>System:</b> $tpm <br>};
|
||||||
|
$out .= qq{($hqtxt{dmgsig}{$lang}) <br><br>};
|
||||||
|
|
||||||
|
$out .= qq{<table class="roomoverview" style="text-align:left; border:1px solid; padding:5px; border-spacing:5px; margin-left:auto; margin-right:auto;">};
|
||||||
|
$out .= qq{<tr style="font-weight:bold;">};
|
||||||
|
$out .= qq{<td style="text-decoration:underline;"> Pos. </td>};
|
||||||
|
$out .= qq{<td> </td>};
|
||||||
|
$out .= qq{<td style="text-decoration:underline;"> $hqtxt{msgimp}{$lang} </td>};
|
||||||
|
$out .= qq{<td> </td>};
|
||||||
|
$out .= qq{<td style="text-decoration:underline;"> $hqtxt{simsg}{$lang} </td>};
|
||||||
|
$out .= qq{</tr>};
|
||||||
|
$out .= qq{<tr></tr>};
|
||||||
|
|
||||||
|
my $hc = 0;
|
||||||
|
|
||||||
|
for my $key (sort keys %{$data{$name}{messages}}) {
|
||||||
|
next if($key >= $idxlimit);
|
||||||
|
|
||||||
|
$hc++;
|
||||||
|
my $enmsg = encode ("utf8", $data{$name}{messages}{$key}{$lang});
|
||||||
|
|
||||||
|
$out .= qq{<tr>};
|
||||||
|
$out .= qq{<td style="padding: 5px; text-align: center"> $key </td>};
|
||||||
|
$out .= qq{<td style="padding: 5px;"> </td>};
|
||||||
|
$out .= qq{<td style="padding: 5px; text-align: center"> $data{$name}{messages}{$key}{SV} </td>};
|
||||||
|
$out .= qq{<td style="padding: 5px;"> </td>};
|
||||||
|
$out .= qq{<td style="padding-right: 5px; text-align: left"> $enmsg </td>};
|
||||||
|
$out .= qq{</tr>};
|
||||||
|
|
||||||
|
if ($hc < $midx) { # Zwischenzeile
|
||||||
|
$out .= qq{<tr>};
|
||||||
|
$out .= qq{<td> </td>};
|
||||||
|
$out .= qq{</tr>};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$out .= qq{</table>};
|
||||||
|
$out .= qq{</html>};
|
||||||
|
|
||||||
|
$out .= "<br>";
|
||||||
|
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
###############################################################
|
###############################################################
|
||||||
# Eintritt in den KI Train Prozess normal/Blocking
|
# Eintritt in den KI Train Prozess normal/Blocking
|
||||||
###############################################################
|
###############################################################
|
||||||
@ -16688,7 +16927,6 @@ return;
|
|||||||
sub aiTrain { ## no critic "not used"
|
sub aiTrain { ## no critic "not used"
|
||||||
my $paref = shift;
|
my $paref = shift;
|
||||||
my $name = $paref->{name};
|
my $name = $paref->{name};
|
||||||
my $type = $paref->{type};
|
|
||||||
my $block = $paref->{block} // 0;
|
my $block = $paref->{block} // 0;
|
||||||
|
|
||||||
my $hash = $defs{$name};
|
my $hash = $defs{$name};
|
||||||
@ -18188,63 +18426,6 @@ sub _writeAsCsv {
|
|||||||
return "The memory structure was written to the file $outfile";
|
return "The memory structure was written to the file $outfile";
|
||||||
}
|
}
|
||||||
|
|
||||||
################################################################
|
|
||||||
# Ausgabe des Mitteilungsystems
|
|
||||||
################################################################
|
|
||||||
sub outputMessages {
|
|
||||||
my $paref = shift;
|
|
||||||
my $name = $paref->{name};
|
|
||||||
my $lang = $paref->{lang};
|
|
||||||
|
|
||||||
my ($micon, $midx) = __fillupMessages ($paref); # Ergebnisse füllen (sind leer wenn Browser nicht refreshed)
|
|
||||||
|
|
||||||
## Ausgabe
|
|
||||||
############
|
|
||||||
my $out = qq{<html>};
|
|
||||||
$out .= qq{<b>}.$hqtxt{msgsys}{$lang}.qq{</b> <br>};
|
|
||||||
$out .= qq{<b>Hinweis:</b> Gelesene Mitteilungen werden bis zu einem FHEM Neustart nicht wieder signalisiert, bleiben jedoch bei Relevanz erhalten. <br><br>};
|
|
||||||
|
|
||||||
$out .= qq{<table class="roomoverview" style="text-align:left; border:1px solid; padding:5px; border-spacing:5px; margin-left:auto; margin-right:auto;">};
|
|
||||||
$out .= qq{<tr style="font-weight:bold;">};
|
|
||||||
$out .= qq{<td style="text-decoration:underline;"> $hqtxt{number}{$lang} </td>};
|
|
||||||
$out .= qq{<td> </td>};
|
|
||||||
$out .= qq{<td style="text-decoration:underline;"> $hqtxt{msgimp}{$lang} </td>};
|
|
||||||
$out .= qq{<td> </td>};
|
|
||||||
$out .= qq{<td style="text-decoration:underline;"> $hqtxt{simsg}{$lang} </td>};
|
|
||||||
$out .= qq{</tr>};
|
|
||||||
$out .= qq{<tr></tr>};
|
|
||||||
|
|
||||||
my $hc = 0;
|
|
||||||
|
|
||||||
for my $key (sort keys %{$data{$name}{messages}}) {
|
|
||||||
next if($key == 9999);
|
|
||||||
|
|
||||||
$hc++;
|
|
||||||
my $enmsg = encode ("utf8", $data{$name}{messages}{$key}{$lang});
|
|
||||||
|
|
||||||
$out .= qq{<tr>};
|
|
||||||
$out .= qq{<td style="padding: 5px; text-align: center"> $key </td>};
|
|
||||||
$out .= qq{<td style="padding: 5px;"> </td>};
|
|
||||||
$out .= qq{<td style="padding: 5px; text-align: center"> $data{$name}{messages}{$key}{SV} </td>};
|
|
||||||
$out .= qq{<td style="padding: 5px;"> </td>};
|
|
||||||
$out .= qq{<td style="padding-right: 5px; text-align: left"> $enmsg </td>};
|
|
||||||
$out .= qq{</tr>};
|
|
||||||
|
|
||||||
if ($hc < $midx) { # Zwischenzeile
|
|
||||||
$out .= qq{<tr>};
|
|
||||||
$out .= qq{<td> </td>};
|
|
||||||
$out .= qq{</tr>};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$out .= qq{</table>};
|
|
||||||
$out .= qq{</html>};
|
|
||||||
|
|
||||||
$out .= "<br>";
|
|
||||||
|
|
||||||
return $out;
|
|
||||||
}
|
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
# validiert die aktuelle Anlagenkonfiguration
|
# validiert die aktuelle Anlagenkonfiguration
|
||||||
################################################################
|
################################################################
|
||||||
@ -18986,11 +19167,70 @@ sub medianArray {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# Berechnen Tag / Stunden Verschieber
|
||||||
|
# aus aktueller Stunde + lfd. Nummer
|
||||||
|
################################################################
|
||||||
|
sub calcDayHourMove {
|
||||||
|
my $chour = shift;
|
||||||
|
my $num = shift;
|
||||||
|
|
||||||
|
my $fh = $chour + $num;
|
||||||
|
my $fd = int ($fh / 24) ;
|
||||||
|
$fh = $fh - ($fd * 24);
|
||||||
|
|
||||||
|
return ($fd, $fh);
|
||||||
|
}
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# Zeit gemäß DWD_OpenData-Format
|
||||||
|
# Berechnen Tag / Stunden Verschieber ab aktuellen Tag
|
||||||
|
# Input: YYYY-MM-DD HH:MM:SS
|
||||||
|
# Output: $fd - 0 (Heute), 1 (Morgen), 2 (Übermorgen), ....
|
||||||
|
# $fh - Stunde von $fd ohne führende Null
|
||||||
|
# Return: fc${fd}_${fh}
|
||||||
|
################################################################
|
||||||
|
sub formatWeatherTimestrg {
|
||||||
|
my $date = shift // return;
|
||||||
|
|
||||||
|
my $cdate = strftime "%Y-%m-%d", localtime(time);
|
||||||
|
my $refts = timestringToTimestamp ($cdate.' 00:00:00'); # Referenztimestring
|
||||||
|
my $datts = timestringToTimestamp ($date);
|
||||||
|
my $fd = int (($datts - $refts) / 86400);
|
||||||
|
my $fh = int ((split /[ :]/, $date)[1]);
|
||||||
|
|
||||||
|
return "fc${fd}_${fh}";
|
||||||
|
}
|
||||||
|
|
||||||
|
################################################################
|
||||||
|
# Spezialfall auflösen wenn Wert von $val2 dem
|
||||||
|
# Redingwert von $val1 entspricht sofern $val1 negativ ist
|
||||||
|
################################################################
|
||||||
|
sub substSpecialCases {
|
||||||
|
my $paref = shift;
|
||||||
|
my $dev = $paref->{dev};
|
||||||
|
my $rdg = $paref->{rdg};
|
||||||
|
my $rdgf = $paref->{rdgf};
|
||||||
|
|
||||||
|
my $val1 = ReadingsNum ($dev, $rdg, 0) * $rdgf;
|
||||||
|
my $val2;
|
||||||
|
|
||||||
|
if($val1 <= 0) {
|
||||||
|
$val2 = abs($val1);
|
||||||
|
$val1 = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$val2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ($val1,$val2);
|
||||||
|
}
|
||||||
|
|
||||||
################################################################
|
################################################################
|
||||||
# Timestrings berechnen
|
# Timestrings berechnen
|
||||||
# gibt Zeitstring in lokaler Zeit zurück
|
# gibt Zeitstring in lokaler Zeit zurück
|
||||||
################################################################
|
################################################################
|
||||||
sub timestampToTimestring {
|
sub timestampToTimestring {
|
||||||
my $epoch = shift;
|
my $epoch = shift;
|
||||||
my $lang = shift // '';
|
my $lang = shift // '';
|
||||||
|
|
||||||
@ -20027,7 +20267,7 @@ return $is;
|
|||||||
# reading: Device ist im Reading Value enthalten
|
# reading: Device ist im Reading Value enthalten
|
||||||
# attr: Device ist im Attr Value enthalten
|
# attr: Device ist im Attr Value enthalten
|
||||||
# string: Device ist im Objekt-Inhalt enthalten
|
# string: Device ist im Objekt-Inhalt enthalten
|
||||||
# return: $valid - ist die Angabe valide (1)
|
# return: $err - evtl. Fehler
|
||||||
# $a->[0] - das extrahierte Device
|
# $a->[0] - das extrahierte Device
|
||||||
# $h - Hash der geparsten Entität
|
# $h - Hash der geparsten Entität
|
||||||
#####################################################################
|
#####################################################################
|
||||||
@ -20054,24 +20294,27 @@ sub isDeviceValid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
my ($a, $h) = parseParams ($dev);
|
my ($a, $h) = parseParams ($dev);
|
||||||
|
|
||||||
|
my ($dv, $al) = !$a->[0] ? ('', '') :
|
||||||
|
$a->[0] =~ /:/xs ? (split ':', $a->[0]) :
|
||||||
|
($a->[0], ''); # (optionalen) SF-spezifischen Alias abtrennen
|
||||||
|
|
||||||
if ($a->[0] && $a->[0] =~ /\@/xs ) { # Remote Device
|
if (!$dv || !$defs{$dv}) {
|
||||||
$a->[0] = (split '@', $a->[0])[0];
|
$dv //= '';
|
||||||
return ($err, $a->[0], $h); # ToDo: $h aus remote Werten anreichern
|
$err = qq{The device '$dv' doesn't exist or is not a valid device.};
|
||||||
}
|
$err = qq{There is no device set. Check the syntax with the command reference.} if(!$dv);
|
||||||
|
$err = qq{The device '$dv' doesn't exist anymore! Delete or change the attribute '$obj'.} if(!$defs{$dv} && $method eq 'attr' && $obj =~ /consumer/);
|
||||||
if (!$a->[0] || !$defs{$a->[0]}) {
|
|
||||||
$a->[0] //= '';
|
|
||||||
$err = qq{The device '$a->[0]' doesn't exist or is not a valid device.};
|
|
||||||
$err = qq{There is no device set. Check the syntax with the command reference.} if(!$a->[0]);
|
|
||||||
$err = qq{The device '$a->[0]' doesn't exist anymore! Delete or change the attribute '$obj'.} if(!$defs{$a->[0]} && $method eq 'attr' && $obj =~ /consumer/);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($err) {
|
if ($err) {
|
||||||
Log3 ($name, 1, "$name - ERROR - $err");
|
Log3 ($name, 1, "$name - ERROR - $err");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($al) { # Leerzeichen im SF-Alias generieren
|
||||||
|
$al =~ s/\+/ /g;
|
||||||
|
}
|
||||||
|
|
||||||
return ($err, $a->[0], $h);
|
return ($err, $dv, $h, $al);
|
||||||
}
|
}
|
||||||
|
|
||||||
#####################################################################
|
#####################################################################
|
||||||
@ -22619,7 +22862,7 @@ to ensure that the system configuration is correct.
|
|||||||
<br>
|
<br>
|
||||||
|
|
||||||
<a id="SolarForecast-attr-consumer" data-pattern="consumer.*"></a>
|
<a id="SolarForecast-attr-consumer" data-pattern="consumer.*"></a>
|
||||||
<li><b>consumerXX <Device Name> type=<type> power=<power> [switchdev=<device>]<br>
|
<li><b>consumerXX <Device>[:<Alias>] type=<type> power=<power> [switchdev=<device>]<br>
|
||||||
[mode=<mode>] [icon=<Icon>[@<Color>]] [mintime=<minutes> | SunPath[:<Offset_Sunrise>:<Offset_Sunset>]] <br>
|
[mode=<mode>] [icon=<Icon>[@<Color>]] [mintime=<minutes> | SunPath[:<Offset_Sunrise>:<Offset_Sunset>]] <br>
|
||||||
[on=<command>] [off=<command>] [swstate=<Readingname>:<on-Regex>:<off-Regex>] [asynchron=<Option>] <br>
|
[on=<command>] [off=<command>] [swstate=<Readingname>:<on-Regex>:<off-Regex>] [asynchron=<Option>] <br>
|
||||||
[notbefore=<Expression>] [notafter=<Expression>] [locktime=<offlt>[:<onlt>]] <br>
|
[notbefore=<Expression>] [notafter=<Expression>] [locktime=<offlt>[:<onlt>]] <br>
|
||||||
@ -22628,8 +22871,8 @@ to ensure that the system configuration is correct.
|
|||||||
[surpmeth=<Option>] [interruptable=<Option>] [noshow=<Option>] [exconfc=<Option>] </b><br>
|
[surpmeth=<Option>] [interruptable=<Option>] [noshow=<Option>] [exconfc=<Option>] </b><br>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
Registers a consumer <Device Name> with the SolarForecast Device. In this case, <Device Name>
|
Registers a consumer <Device> with the SolarForecast Device. An optional alias can be specified. <br>
|
||||||
is a consumer device already created in FHEM, e.g. a switchable socket.
|
In this case, <Device> is a consumer device already created in FHEM, e.g. a switchable socket.
|
||||||
Most of the keys are optional, but are a prerequisite for certain functionalities and are filled with
|
Most of the keys are optional, but are a prerequisite for certain functionalities and are filled with
|
||||||
default values. <br>
|
default values. <br>
|
||||||
If the dish is defined "auto", the automatic mode in the integrated consumer graphic can be switched with the
|
If the dish is defined "auto", the automatic mode in the integrated consumer graphic can be switched with the
|
||||||
@ -22664,6 +22907,11 @@ to ensure that the system configuration is correct.
|
|||||||
<ul>
|
<ul>
|
||||||
<table>
|
<table>
|
||||||
<colgroup> <col width="12%"> <col width="88%"> </colgroup>
|
<colgroup> <col width="12%"> <col width="88%"> </colgroup>
|
||||||
|
<tr><td> <b>Device</b> </td><td>Consumer device. In the simple case, the device works both as an energy meter and as a switch. </td></tr>
|
||||||
|
<tr><td> </td><td>In the optional alias, spaces must be replaced by '+' (e.g. 'Ein+toller+Alias'). </td></tr>
|
||||||
|
<tr><td> </td><td>If the consumer consists of different devices/channels (e.g. Homematic), the energy meter is defined as a <Device>. </td></tr>
|
||||||
|
<tr><td> </td><td>The associated switching device is specified with the key 'switchdev'. </td></tr>
|
||||||
|
<tr><td> </td><td> </td></tr>
|
||||||
<tr><td> <b>type</b> </td><td>Type of consumer. The following types are allowed: </td></tr>
|
<tr><td> <b>type</b> </td><td>Type of consumer. The following types are allowed: </td></tr>
|
||||||
<tr><td> </td><td><b>dishwasher</b> - Consumer is a dishwasher </td></tr>
|
<tr><td> </td><td><b>dishwasher</b> - Consumer is a dishwasher </td></tr>
|
||||||
<tr><td> </td><td><b>dryer</b> - Consumer is a tumble dryer </td></tr>
|
<tr><td> </td><td><b>dryer</b> - Consumer is a tumble dryer </td></tr>
|
||||||
@ -25104,7 +25352,7 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
|||||||
<br>
|
<br>
|
||||||
|
|
||||||
<a id="SolarForecast-attr-consumer" data-pattern="consumer.*"></a>
|
<a id="SolarForecast-attr-consumer" data-pattern="consumer.*"></a>
|
||||||
<li><b>consumerXX <Device Name> type=<type> power=<power> [switchdev=<device>]<br>
|
<li><b>consumerXX <Device>[:<Alias>] type=<type> power=<power> [switchdev=<device>]<br>
|
||||||
[mode=<mode>] [icon=<Icon>[@<Farbe>]] [mintime=<minutes> | SunPath[:<Offset_Sunrise>:<Offset_Sunset>]] <br>
|
[mode=<mode>] [icon=<Icon>[@<Farbe>]] [mintime=<minutes> | SunPath[:<Offset_Sunrise>:<Offset_Sunset>]] <br>
|
||||||
[on=<Kommando>] [off=<Kommando>] [swstate=<Readingname>:<on-Regex>:<off-Regex>] [asynchron=<Option>] <br>
|
[on=<Kommando>] [off=<Kommando>] [swstate=<Readingname>:<on-Regex>:<off-Regex>] [asynchron=<Option>] <br>
|
||||||
[notbefore=<Ausdruck>] [notafter=<Ausdruck>] [locktime=<offlt>[:<onlt>]] <br>
|
[notbefore=<Ausdruck>] [notafter=<Ausdruck>] [locktime=<offlt>[:<onlt>]] <br>
|
||||||
@ -25113,8 +25361,8 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
|||||||
[surpmeth=<Option>] [interruptable=<Option>] [noshow=<Option>] [exconfc=<Option>] </b><br>
|
[surpmeth=<Option>] [interruptable=<Option>] [noshow=<Option>] [exconfc=<Option>] </b><br>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
Registriert einen Verbraucher <Device Name> beim SolarForecast Device. Dabei ist <Device Name>
|
Registriert einen Verbraucher <Device> beim SolarForecast Device. Ein optionaler Alias kann angegeben werden. <br>
|
||||||
ein in FHEM bereits angelegtes Verbraucher Device, z.B. eine Schaltsteckdose.
|
Dabei ist <Device> ein in FHEM bereits angelegtes Verbraucher Device, z.B. eine Schaltsteckdose.
|
||||||
Die meisten Schlüssel sind optional, sind aber für bestimmte Funktionalitäten Voraussetzung und werden mit
|
Die meisten Schlüssel sind optional, sind aber für bestimmte Funktionalitäten Voraussetzung und werden mit
|
||||||
default-Werten besetzt. <br>
|
default-Werten besetzt. <br>
|
||||||
Ist der Schüssel "auto" definiert, kann der Automatikmodus in der integrierten Verbrauchergrafik mit den
|
Ist der Schüssel "auto" definiert, kann der Automatikmodus in der integrierten Verbrauchergrafik mit den
|
||||||
@ -25148,6 +25396,11 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
|
|||||||
<ul>
|
<ul>
|
||||||
<table>
|
<table>
|
||||||
<colgroup> <col width="12%"> <col width="88%"> </colgroup>
|
<colgroup> <col width="12%"> <col width="88%"> </colgroup>
|
||||||
|
<tr><td> <b>Device</b> </td><td>Verbraucher-Gerät. Im einfachen Fall arbeitet das Gerät sowohl als Energiemesser als auch als Schalter. </td></tr>
|
||||||
|
<tr><td> </td><td>Im optionalen Alias sind Leerzeichen durch '+' zu ersetzen (z.B. 'Ein+toller+Alias'). </td></tr>
|
||||||
|
<tr><td> </td><td>Besteht der Verbraucher aus verschiedenen Geräten/Kanäalen (z.B. Homematic), wird der Energiemesser als <Device> definiert. </td></tr>
|
||||||
|
<tr><td> </td><td>Das dazugehörige Schalt-Gerät wird mit dem Schlüssel 'switchdev' spezifiziert. </td></tr>
|
||||||
|
<tr><td> </td><td> </td></tr>
|
||||||
<tr><td> <b>type</b> </td><td>Typ des Verbrauchers. Folgende Typen sind erlaubt: </td></tr>
|
<tr><td> <b>type</b> </td><td>Typ des Verbrauchers. Folgende Typen sind erlaubt: </td></tr>
|
||||||
<tr><td> </td><td><b>dishwasher</b> - Verbraucher ist eine Spülmaschine </td></tr>
|
<tr><td> </td><td><b>dishwasher</b> - Verbraucher ist eine Spülmaschine </td></tr>
|
||||||
<tr><td> </td><td><b>dryer</b> - Verbraucher ist ein Wäschetrockner </td></tr>
|
<tr><td> </td><td><b>dryer</b> - Verbraucher ist ein Wäschetrockner </td></tr>
|
||||||
|
Loading…
Reference in New Issue
Block a user