2
0
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:
nasseeder1 2025-01-22 22:32:23 +00:00
parent d04d74bb5a
commit 33735a1418
2 changed files with 474 additions and 220 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: 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,

View File

@ -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&nbsp;status:&nbsp;<pstate><br>Info:&nbsp;<supplmnt><br>Mode:&nbsp;<mode><br>On:&nbsp;<start><br>Off:&nbsp;<stop><br>Remaining lock time:&nbsp;<RLT> seconds}, pstate => { EN => qq{Planning&nbsp;status:&nbsp;<pstate><br>Info:&nbsp;<supplmnt><br>Mode:&nbsp;<mode><br>On:&nbsp;<start><br>Off:&nbsp;<stop><br>Remaining lock time:&nbsp;<RLT> seconds},
DE => qq{Planungsstatus:&nbsp;<pstate><br>Info:&nbsp;<supplmnt><br>Modus:&nbsp;<mode><br>Ein:&nbsp;<start><br>Aus:&nbsp;<stop><br>verbleibende Sperrzeit:&nbsp;<RLT> Sekunden} }, DE => qq{Planungsstatus:&nbsp;<pstate><br>Info:&nbsp;<supplmnt><br>Modus:&nbsp;<mode><br>Ein:&nbsp;<start><br>Aus:&nbsp;<stop><br>verbleibende Sperrzeit:&nbsp;<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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>};
$out .= qq{<td style="text-decoration:underline;"> $hqtxt{msgimp}{$lang} </td>};
$out .= qq{<td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</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> &nbsp; </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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>};
$out .= qq{<td style="text-decoration:underline;"> $hqtxt{msgimp}{$lang} </td>};
$out .= qq{<td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</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> &nbsp; </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 &lt;Device Name&gt; type=&lt;type&gt; power=&lt;power&gt; [switchdev=&lt;device&gt;]<br> <li><b>consumerXX &lt;Device&gt;[:&lt;Alias&gt;] type=&lt;type&gt; power=&lt;power&gt; [switchdev=&lt;device&gt;]<br>
[mode=&lt;mode&gt;] [icon=&lt;Icon&gt;[@&lt;Color&gt;]] [mintime=&lt;minutes&gt; | SunPath[:&lt;Offset_Sunrise&gt;:&lt;Offset_Sunset&gt;]] <br> [mode=&lt;mode&gt;] [icon=&lt;Icon&gt;[@&lt;Color&gt;]] [mintime=&lt;minutes&gt; | SunPath[:&lt;Offset_Sunrise&gt;:&lt;Offset_Sunset&gt;]] <br>
[on=&lt;command&gt;] [off=&lt;command&gt;] [swstate=&lt;Readingname&gt;:&lt;on-Regex&gt;:&lt;off-Regex&gt] [asynchron=&lt;Option&gt] <br> [on=&lt;command&gt;] [off=&lt;command&gt;] [swstate=&lt;Readingname&gt;:&lt;on-Regex&gt;:&lt;off-Regex&gt] [asynchron=&lt;Option&gt] <br>
[notbefore=&lt;Expression&gt;] [notafter=&lt;Expression&gt;] [locktime=&lt;offlt&gt;[:&lt;onlt&gt;]] <br> [notbefore=&lt;Expression&gt;] [notafter=&lt;Expression&gt;] [locktime=&lt;offlt&gt;[:&lt;onlt&gt;]] <br>
@ -22628,8 +22871,8 @@ to ensure that the system configuration is correct.
[surpmeth=&lt;Option&gt] [interruptable=&lt;Option&gt] [noshow=&lt;Option&gt] [exconfc=&lt;Option&gt] </b><br> [surpmeth=&lt;Option&gt] [interruptable=&lt;Option&gt] [noshow=&lt;Option&gt] [exconfc=&lt;Option&gt] </b><br>
<br> <br>
Registers a consumer &lt;Device Name&gt; with the SolarForecast Device. In this case, &lt;Device Name&gt; Registers a consumer &lt;Device&gt; 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, &lt;Device&gt; 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 &lt;Device&gt;. </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 &lt;Device Name&gt; type=&lt;type&gt; power=&lt;power&gt; [switchdev=&lt;device&gt;]<br> <li><b>consumerXX &lt;Device&gt;[:&lt;Alias&gt;] type=&lt;type&gt; power=&lt;power&gt; [switchdev=&lt;device&gt;]<br>
[mode=&lt;mode&gt;] [icon=&lt;Icon&gt;[@&lt;Farbe&gt;]] [mintime=&lt;minutes&gt; | SunPath[:&lt;Offset_Sunrise&gt;:&lt;Offset_Sunset&gt;]] <br> [mode=&lt;mode&gt;] [icon=&lt;Icon&gt;[@&lt;Farbe&gt;]] [mintime=&lt;minutes&gt; | SunPath[:&lt;Offset_Sunrise&gt;:&lt;Offset_Sunset&gt;]] <br>
[on=&lt;Kommando&gt;] [off=&lt;Kommando&gt;] [swstate=&lt;Readingname&gt;:&lt;on-Regex&gt;:&lt;off-Regex&gt] [asynchron=&lt;Option&gt] <br> [on=&lt;Kommando&gt;] [off=&lt;Kommando&gt;] [swstate=&lt;Readingname&gt;:&lt;on-Regex&gt;:&lt;off-Regex&gt] [asynchron=&lt;Option&gt] <br>
[notbefore=&lt;Ausdruck&gt;] [notafter=&lt;Ausdruck&gt;] [locktime=&lt;offlt&gt;[:&lt;onlt&gt;]] <br> [notbefore=&lt;Ausdruck&gt;] [notafter=&lt;Ausdruck&gt;] [locktime=&lt;offlt&gt;[:&lt;onlt&gt;]] <br>
@ -25113,8 +25361,8 @@ die ordnungsgemäße Anlagenkonfiguration geprüft werden.
[surpmeth=&lt;Option&gt] [interruptable=&lt;Option&gt] [noshow=&lt;Option&gt] [exconfc=&lt;Option&gt] </b><br> [surpmeth=&lt;Option&gt] [interruptable=&lt;Option&gt] [noshow=&lt;Option&gt] [exconfc=&lt;Option&gt] </b><br>
<br> <br>
Registriert einen Verbraucher &lt;Device Name&gt; beim SolarForecast Device. Dabei ist &lt;Device Name&gt; Registriert einen Verbraucher &lt;Device&gt; beim SolarForecast Device. Ein optionaler Alias kann angegeben werden. <br>
ein in FHEM bereits angelegtes Verbraucher Device, z.B. eine Schaltsteckdose. Dabei ist &lt;Device&gt; 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 &lt;Device&gt; 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>