Kostal Plenticore 10 Plus
Kostal Plenticore 10 Plus | |
---|---|
Allgemein | |
Protokoll | IP und RS485 |
Typ | Hybrid Wechselrichter |
Kategorie | Wechselrichter |
Technische Details | |
Kommunikation | n/a |
Kanäle | n/a |
Betriebsspannung | 400 V |
Leistungsaufnahme | |
Versorgung | 3P AC |
Abmessungen | 56,3x40,5x23,3 cm (BxHxT) |
Sonstiges | |
Modulname | HTTPMOD, Modbus, Python3 |
Hersteller | KOSTAL Solar Electric GmbH |
Ein Hinweis allgemeiner Art: Die hier abgebildeten Code Stücke sind nicht ausschließlich durch mich entstanden. Ich bedanke mich für die Unterstützung und Bereitstellung vieler Einzelkomponenten durch Dritte. Der Einsatz ist auf eigene Gefahr und für etwaige Schäden wird keinerlei Haftung übernommen. Bitte beachtet bei der Hardware die Gewährleistungsbestimmungen und Vorgaben der Hersteller.
Der Kostal Plenticore 10 Plus Hersteller Link ist ein Hybrid Wechselrichter mit IP-Konnektivität.
Er verfügt über einen LAN-Anschluss und ist auf die Steuerung via WebGUI des Herstellers ausgelegt. Weiterhin kann eine Abfrage mit Modbus/TCP oder auch über eine undokumentierte API erfolgen. Für die API bietet der Hersteller keinerlei Support!
Voraussetzungen Energietechnik
Der Wechselrichter, der Speicher und der KSEM wurden durch einen Fachbetrieb installiert und konfiguriert. Die gesamte Anlage läuft fehlerfrei und wurde durch den Fachbetrieb abgenommen, sowie beim Netzbetreiber angemeldet.
Geräte-Registrierung
Hierfür ist die Dokumentation des Herstellers heranzuziehen. Für eine Verlängerung der Gewährleistungszeit kann man den Plenticore im Herstellerportal registrieren. Dies ist jedoch für die Anbindung an FHEM nicht notwendig.
Hersteller Dokumentation
Für Links auf dieser Wiki Seite wird keine Haftung übernommen. Die Inhalte unterliegen der Verantwortung der Firma Kostal.
- Plenticore Plus - Verlängerung der Gewährleistungszeit
- Plenticore Plus - Betriebsanleitung
- Plenticore Plus - Kurzanleitung
- Plenticore Plus - Datenblatt
- Plenticore Plus - Software Update UI: 01.15.04581 FW: 01.43
Einbindung in das Netzwerk
Als Grundlage ist der Plenticore mit dem LAN zu verbinden, wodurch er eine TCP/IP Adresse per DHCP bekommt. Diese ist dann entweder am Display des Plenticore abzulesen, oder über die Oberfläche des Routers zu ermitteln.
Das gleiche gilt für den BYD Speicher, der jedoch zusätzlich auch über WLAN verfügt. Eine Netzwerkanbindung des Speichers ist beim Plenticore nicht zwingend notwendig, da der Plenticore mit dem Speicher über eine RS-485 Schnittstelle kommuniziert. Bei dieser Anbindung werden jedoch noch nicht alle möglichen Werte aus dem Speicher ausgelesen. Später wird hierzu jedoch noch mehr geschrieben, um alle Möglichkeiten offen zu halten.
Der KSEM kann ebenfalls auch direkt per LAN ausgelesen werden, was jedoch ebenfalls nicht zwingend notwendig ist. Eine Kommunikation des KSEM mit dem Plenticore erfolgt über zwei mögliche Wege. Beim Betrieb mit Speicher ist zwingend die RS485 Schnittstelle erforderlich, über die auch der Plenticore alle Werte übermittelt bekommt. Auch diese sind dann am Plenticore abfragbar. Der zweite Weg wäre dann über die LAN Verbindung, bei der jedoch kein Speicher am Plenticore konfigurierbar ist.
Voraussetzungen FHEM Umfeld
Alle Geräte müssen mit TCP/IP erreichbar sein
Alle Module sollten auf einem aktuellen Stand sein
Python
Ein Python 3 sollte vorhanden sein
Wenn man die erweiterten Funktionalitäten, wie Statistiken, Speicher auslesen und später auch das Setzen von Werten im Plenticore, verwenden möchte.
Es müssen folgende Python Module vorhanden sein
python3-pip
pip3 install pycryptodome
pip3 install -U fhem
Eine LogDB/LogDBRep sollte bereits vorhanden sein, was hier nicht weiter erklärt wird.
Verwendete Module
- Modbus - HTTPMOD - expandJSON - DbLog - DbRep - dummy - Shelly - HourCounter - readingsGroup
Einbindung in FHEM: Überblick
Hardware Anbindung (alles über LAN)
Kostal Plenticore Plus
Kostal Plenticore Plus die Basis information (Modbus/TCP)
Der Kostal Plenticore wird mittels des Moduls MODBUS eingebunden. Dazu gilt zunächst die folgende Konfiguration als Basis, die am Display des Plenticore oder auch in der Kostal Dokumentation zu entnehmen ist.
Plenticore Modbus Definition
GeräteId : 71 IP-Adresse: <IP-Adresse> Port : 1502
Modbus Timing
Das Gerät aktualisiert sich im Abstand von 60 Sekunden durch den stetigen Modbus/TCP Datenstrom. Der Plenticore ist als Modbus Master implementiert und sendet somit alle Daten permanent ins Netzwerk.
Die Zeit kann auch verändert werden, jedoch sollte sie nicht zu kurz gewählt sein.
RAW Definition des konfigurations Dummy
Diese Dummy soll alle Konfigurationsparameter halten, auf die dann die anderen Geräte Definitionen zentral zugreifen. Hier können auch default Namen und Vorschläge für Werte in Form von Slidern und Auswahllisten hinterlegt werden.
defmod PV_Anlage_1_config dummy
attr PV_Anlage_1_config DbLogExclude .*
attr PV_Anlage_1_config alias PV_Anlage_1_config
attr PV_Anlage_1_config comment Steht das reading module_*_count auf 0 wird diese Ausrichtung nicht berücksichtigt\
Passworte zu dieser Konfiguration liegen im Dateiverzeichnis ~./python/pwd_*.json\
\
Korrekturkurven:\
Steilheit Parallel\
verschiebung\
tempk -0.39 25\
cloudk -0.65 0\
raink -0.30 0\
Der Slider für die Steilheit wird mit - k/100 umgerechnet. 39 ==> -0.39
attr PV_Anlage_1_config event-on-change-reading .*
attr PV_Anlage_1_config group PV Eigenverbrauch
attr PV_Anlage_1_config icon solar_icon
attr PV_Anlage_1_config readingList IP-Address_Plenticore IP-Address_BYD IP-Address_KSEM IP-Address_FHEM module_1_active module_2_active module_3_active module_1_name module_2_name module_3_name module_1_direction module_2_direction module_3_direction module_1_count module_2_count module_3_count module_1_power module_2_power module_3_power module_1_plain module_2_plain module_3_plain forecast_cloudk forecast_cloudk_base forecast_raink forecast_raink_base forecast_tempk forecast_tempk_base forecast_factor
attr PV_Anlage_1_config room Strom->Photovoltaik
attr PV_Anlage_1_config setList IP-Address_Plenticore IP-Address_BYD IP-Address_KSEM IP-Address_FHEM module_1_name:East,SouthEast,South,SouthWest,West module_2_name:East,SouthEast,South,SouthWest,West module_3_name:East,SouthEast,South,SouthWest,West module_1_direction:slider,-90,5,+90 module_2_direction:slider,-90,5,90 module_3_direction:slider,-90,5,90 module_1_count:slider,0,1,40 module_2_count:slider,0,1,40 module_3_count:slider,0,1,40 module_1_power:slider,250,10,400 module_2_power:slider,250,10,400 module_3_power:slider,250,10,400 module_1_plain:slider,15,1,45 module_2_plain:slider,15,1,45 module_3_plain:slider,15,1,45 forecast_cloudk:slider,0,1,100 forecast_cloudk_base:slider,0,1,10 forecast_raink:slider,0,1,100 forecast_raink_base:slider,0,1,10 forecast_tempk:slider,0,1,100 forecast_tempk_base:slider,10,1,30 forecast_factor:1,1.5,2,2.5,3,3.5,4,4.5,5
attr PV_Anlage_1_config sortby 06
attr PV_Anlage_1_config verbose 0
RAW Definition des Wechselrichters
defmod PV_Anlage_1 ModbusAttr 71 60 <IP-Address_Plenticore>:1502 TCP
attr PV_Anlage_1 DbLogExclude .*
attr PV_Anlage_1 DbLogInclude Act_state_of_charge,Actual_battery_charge_-minus_or_discharge_-plus_Power,Actual_battery_charge_usable_Power,Battery_temperature,Home_own_consumption_from_PV,Home_own_consumption_from_battery,Home_own_consumption_from_grid,Inverter_state,Power_DC1,Power_DC2,Power_DC_Sum,Total_DC_Power,Total_DC_Power_Max,Total_PV_Power_reserve,Voltage_DC1,Voltage_DC2,.*_yield,Solar_.*
attr PV_Anlage_1 alias PV_Einspeisung
attr PV_Anlage_1 comment Kostal Plenticore 10 Plus mit BYD Speicher
attr PV_Anlage_1 dev-h-defFormat %.2f
attr PV_Anlage_1 dev-h-defLen 2
attr PV_Anlage_1 dev-h-defPoll 1
attr PV_Anlage_1 dev-h-defRevRegs 1
attr PV_Anlage_1 dev-h-defUnpack f>
attr PV_Anlage_1 dev-type-STR-format %s
attr PV_Anlage_1 dev-type-STR-len 8
attr PV_Anlage_1 dev-type-STR-revRegs 0
attr PV_Anlage_1 dev-type-STR-unpack a*
attr PV_Anlage_1 event-on-change-reading Act_state_of_charge,Actual_battery_charge_.*,Battery_temperature,Home_own_consumption_from_.*,Inverter_state,Power_DC1,Power_DC2,Power_DC_Sum,Total_DC_Power,Total_DC_Power_Max,Total_PV_Power_reserve,Voltage_DC1,Voltage_DC2,.*_yield,Solar_.*
attr PV_Anlage_1 group PV Eigenverbrauch
attr PV_Anlage_1 icon sani_solar
attr PV_Anlage_1 obj-h100-reading Total_DC_Power
attr PV_Anlage_1 obj-h104-format %s
attr PV_Anlage_1 obj-h104-reading State_of_energy_manager
attr PV_Anlage_1 obj-h104-revRegs 0
attr PV_Anlage_1 obj-h104-unpack N
attr PV_Anlage_1 obj-h106-reading Home_own_consumption_from_battery
attr PV_Anlage_1 obj-h108-reading Home_own_consumption_from_grid
attr PV_Anlage_1 obj-h110-reading Total_home_consumption_Battery
attr PV_Anlage_1 obj-h112-reading Total_home_consumption_Grid
attr PV_Anlage_1 obj-h114-reading Total_home_consumption_PV
attr PV_Anlage_1 obj-h116-reading Home_own_consumption_from_PV
attr PV_Anlage_1 obj-h118-reading Total_home_consumption
attr PV_Anlage_1 obj-h120-reading Isolation_resistance
attr PV_Anlage_1 obj-h122-reading Power_limit_from_EVU
attr PV_Anlage_1 obj-h124-reading Total_home_consumption_rate
attr PV_Anlage_1 obj-h14-reading Inverter_serial_number
attr PV_Anlage_1 obj-h14-type STR
attr PV_Anlage_1 obj-h144-reading Worktime
attr PV_Anlage_1 obj-h150-reading Actual_cos_phi
attr PV_Anlage_1 obj-h152-reading Grid_frequency
attr PV_Anlage_1 obj-h154-reading Current_Phase_1
attr PV_Anlage_1 obj-h156-reading Active_power_Phase_1
attr PV_Anlage_1 obj-h158-reading Voltage_Phase_1
attr PV_Anlage_1 obj-h160-reading Current_Phase_2
attr PV_Anlage_1 obj-h162-reading Active_power_Phase_2
attr PV_Anlage_1 obj-h164-reading Voltage_Phase_2
attr PV_Anlage_1 obj-h166-reading Current_Phase_3
attr PV_Anlage_1 obj-h168-reading Active_power_Phase_3
attr PV_Anlage_1 obj-h170-reading Voltage_Phase_3
attr PV_Anlage_1 obj-h172-reading Total_AC_active_power
attr PV_Anlage_1 obj-h174-reading Total_AC_reactive_power
attr PV_Anlage_1 obj-h178-reading Total_AC_apparent_power
attr PV_Anlage_1 obj-h190-reading Battery_charge_current
attr PV_Anlage_1 obj-h194-format %.0f
attr PV_Anlage_1 obj-h194-reading Number_of_battery_cycles
attr PV_Anlage_1 obj-h200-reading Actual_battery_charge_-minus_or_discharge_-plus_current
attr PV_Anlage_1 obj-h202-reading PSSB_fuse_state
attr PV_Anlage_1 obj-h208-format %.0f
attr PV_Anlage_1 obj-h208-reading Battery_ready_flag
attr PV_Anlage_1 obj-h210-reading Act_state_of_charge
attr PV_Anlage_1 obj-h212-reading Battery_state
attr PV_Anlage_1 obj-h214-reading Battery_temperature
attr PV_Anlage_1 obj-h216-reading Battery_voltage
attr PV_Anlage_1 obj-h218-reading Cos_phi_(powermeter)
attr PV_Anlage_1 obj-h220-reading Frequency_(powermeter)
attr PV_Anlage_1 obj-h222-reading Current_phase_1_(powermeter)
attr PV_Anlage_1 obj-h224-reading Active_power_phase_1_(powermeter)
attr PV_Anlage_1 obj-h226-reading Reactive_power_phase_1_(powermeter)
attr PV_Anlage_1 obj-h228-reading Apparent_power_phase_1_(powermeter)
attr PV_Anlage_1 obj-h230-reading Voltage_phase_1_(powermeter)
attr PV_Anlage_1 obj-h232-reading Current_phase_2_(powermeter)
attr PV_Anlage_1 obj-h234-reading Active_power_phase_2_(powermeter)
attr PV_Anlage_1 obj-h236-reading Reactive_power_phase_2_(powermeter)
attr PV_Anlage_1 obj-h238-reading Apparent_power_phase_2_(powermeter)
attr PV_Anlage_1 obj-h240-reading Voltage_phase_2_(powermeter)
attr PV_Anlage_1 obj-h242-reading Current_phase_3_(powermeter)
attr PV_Anlage_1 obj-h244-reading Active_power_phase_3_(powermeter)
attr PV_Anlage_1 obj-h246-reading Reactive_power_phase_3_(powermeter)
attr PV_Anlage_1 obj-h248-reading Apparent_power_phase_3_(powermeter)
attr PV_Anlage_1 obj-h250-reading Voltage_phase_3_(powermeter)
attr PV_Anlage_1 obj-h252-reading Total_active_power_(powermeter)
attr PV_Anlage_1 obj-h254-reading Total_reactive_power_(powermeter)
attr PV_Anlage_1 obj-h256-reading Total_apparent_power_(powermeter)
attr PV_Anlage_1 obj-h258-reading Current_DC1
attr PV_Anlage_1 obj-h260-reading Power_DC1
attr PV_Anlage_1 obj-h266-reading Voltage_DC1
attr PV_Anlage_1 obj-h268-reading Current_DC2
attr PV_Anlage_1 obj-h270-reading Power_DC2
attr PV_Anlage_1 obj-h276-reading Voltage_DC2
attr PV_Anlage_1 obj-h278-reading Current_DC3
attr PV_Anlage_1 obj-h280-reading Power_DC3
attr PV_Anlage_1 obj-h286-reading Voltage_DC3
attr PV_Anlage_1 obj-h320-reading Total_yield
attr PV_Anlage_1 obj-h322-reading Daily_yield
attr PV_Anlage_1 obj-h324-reading Yearly_yield
attr PV_Anlage_1 obj-h326-reading Monthly_yield
attr PV_Anlage_1 obj-h38-reading Software-Version_Maincontroller_(MC)
attr PV_Anlage_1 obj-h38-type STR
attr PV_Anlage_1 obj-h384-len 16
attr PV_Anlage_1 obj-h384-reading Inverter_network_name
attr PV_Anlage_1 obj-h384-type STR
attr PV_Anlage_1 obj-h420-reading IP-address
attr PV_Anlage_1 obj-h420-type STR
attr PV_Anlage_1 obj-h428-reading IP-subnetmask
attr PV_Anlage_1 obj-h428-type STR
attr PV_Anlage_1 obj-h436-reading IP-gateway
attr PV_Anlage_1 obj-h436-type STR
attr PV_Anlage_1 obj-h446-reading IP-DNS1
attr PV_Anlage_1 obj-h446-type STR
attr PV_Anlage_1 obj-h454-reading IP-DNS2
attr PV_Anlage_1 obj-h454-type STR
attr PV_Anlage_1 obj-h46-reading Software-Version_IO-Controller_(IOC)
attr PV_Anlage_1 obj-h46-type STR
attr PV_Anlage_1 obj-h514-len 1
attr PV_Anlage_1 obj-h514-reading Battery_actual_SOC
attr PV_Anlage_1 obj-h517-reading Battery_Manufacturer
attr PV_Anlage_1 obj-h517-type STR
attr PV_Anlage_1 obj-h525-format %c
attr PV_Anlage_1 obj-h525-reading Battery_Model_ID
attr PV_Anlage_1 obj-h525-unpack N
attr PV_Anlage_1 obj-h527-format %c
attr PV_Anlage_1 obj-h527-reading Battery_Serial_Number
attr PV_Anlage_1 obj-h529-len 4
attr PV_Anlage_1 obj-h529-reading Work_Capacity
attr PV_Anlage_1 obj-h529-unpack N
attr PV_Anlage_1 obj-h531-format %.0f
attr PV_Anlage_1 obj-h531-reading Inverter_Max_Power
attr PV_Anlage_1 obj-h531-unpack N
attr PV_Anlage_1 obj-h535-revRegs 0
attr PV_Anlage_1 obj-h535-unpack n
attr PV_Anlage_1 obj-h551-revRegs 0
attr PV_Anlage_1 obj-h559-revRegs 0
attr PV_Anlage_1 obj-h56-format %.0f
attr PV_Anlage_1 obj-h56-reading Inverter_state
attr PV_Anlage_1 obj-h56-unpack N
attr PV_Anlage_1 obj-h575-len 1
attr PV_Anlage_1 obj-h575-reading Inverter_Generation_Power_(actual)
attr PV_Anlage_1 obj-h577-len 2
attr PV_Anlage_1 obj-h577-reading Generation_Energy
attr PV_Anlage_1 obj-h577-unpack N
attr PV_Anlage_1 obj-h578-reading Total_energy
attr PV_Anlage_1 obj-h582-reading Actual_battery_charge-discharge_power
attr PV_Anlage_1 obj-h586-format %s
attr PV_Anlage_1 obj-h586-reading Battery_Firmware
attr PV_Anlage_1 obj-h586-unpack N
attr PV_Anlage_1 obj-h588-format %s
attr PV_Anlage_1 obj-h588-len 1
attr PV_Anlage_1 obj-h588-reading Battery_Type
attr PV_Anlage_1 obj-h588-unpack N
attr PV_Anlage_1 obj-h6-reading Inverter_article_number
attr PV_Anlage_1 obj-h6-type STR
attr PV_Anlage_1 obj-h768-len 32
attr PV_Anlage_1 obj-h768-reading Productname
attr PV_Anlage_1 obj-h768-type STR
attr PV_Anlage_1 obj-h800-len 32
attr PV_Anlage_1 obj-h800-reading Power_class
attr PV_Anlage_1 obj-h800-type STR
attr PV_Anlage_1 room Strom->Photovoltaik
attr PV_Anlage_1 sortby 01
attr PV_Anlage_1 stateFormat {sprintf("\
<TABLE>\
\
<TR>\
<TH ALIGN=\"MIDDLE\" WIDTH=\"20\">Batterie %s</TH>\
<TH ALIGN=\"MIDDLE\" WIDTH=\"20\">aktuell</TH>\
<TH ALIGN=\"RIGHT\" WIDTH=\"20\">Hausverbrauch</TH>\
<TH ALIGN=\"MIDDLE\" WIDTH=\"20\">Erträge</TH>\
</TR>\
\
<TR>\
<TD ALIGN=\"MIDDLE\" WIDTH=\"20\">\
Leistung: %04d W<br>\
Temp.: %02.1f °C<br>\
Ladung total: %2d %%<br>\
Ladung Res.: %04d Wh\
</TD>\
\
<TD ALIGN=\"RIGHT\" WIDTH=\"20\">\
DC total: %05d W<br>\
<br>\
<br>\
PV reserve: %05d W\
</TD>\
\
<TD ALIGN=\"RIGHT\" WIDTH=\"20\">\
von PV: %05d W <br>\
von Batterie: %05d W<br>\
vom Netz: %05d W<br>\
ins Haus: %05d W<br>\
Netz: %05d W\
</TD>\
\
<TD ALIGN=\"RIGHT\" WIDTH=\"20\">\
Tag: %05d KWh <br>\
Monat: %05d KWh<br>\
Jahr: %05d KWh<br>\
Total: %05d KWh\
</TD>\
</TR>\
\
</TABLE>\
" , \
(ReadingsVal($name,"Actual_battery_charge_-minus_or_discharge_-plus_Power",0) lt 0) ? "<span style='color:#00FF00'>Laden</span>":"<span style='color:#FF0000'>Entladen</span>" ,\
\
ReadingsVal($name,"Actual_battery_charge_-minus_or_discharge_-plus_Power",0),\
ReadingsVal($name,"Battery_temperature",0) ,\
ReadingsVal($name,"Act_state_of_charge",0) ,\
ReadingsVal($name,"Actual_battery_charge_usable_Power",0) ,\
\
ReadingsVal($name,"Power_DC_Sum","0"),\
ReadingsVal($name,"Total_PV_Power_reserve","0"),\
\
ReadingsVal($name,"Home_own_consumption_from_PV",0) ,\
ReadingsVal($name,"Home_own_consumption_from_battery",0) ,\
ReadingsVal($name,"Home_own_consumption_from_grid",0),\
ReadingsVal($name,"Home_own_consumption_from_PV",0) +ReadingsVal($name,"Home_own_consumption_from_battery",0)+ReadingsVal($name,"Home_own_consumption_from_grid",0),\
ReadingsVal($name,"Total_active_power_(powermeter)",0),\
\
round(ReadingsVal($name,"Daily_yield",0)/1000 ,0),\
round(ReadingsVal($name,"Monthly_yield",0)/1000 ,0) ,\
round(ReadingsVal($name,"Yearly_yield",0)/1000 ,0) ,\
round(ReadingsVal($name,"Total_yield",0)/1000 ,0)\
)}
attr PV_Anlage_1 userReadings Power_DC_Sum:Total_DC_Power.* { ReadingsVal($NAME,"Power_DC1","0")+ReadingsVal($NAME,"Power_DC2","0") },\
\
Total_PV_Power_reserve:Total_DC_Power.* {my $reserve = ReadingsVal($NAME,"Power_DC_Sum","0") * 0.90 - ReadingsVal($NAME,"Home_own_consumption_from_PV","0");;;; ($reserve lt 0)?0:round($reserve,3) },\
\
Total_DC_Power_Max:Total_DC_Power.* { my $Bat_out = (ReadingsVal($NAME,"Actual_battery_charge_-minus_or_discharge_-plus_current","0")*ReadingsVal($NAME,"Battery_voltage","0"));;;; ($Bat_out gt 0)?ReadingsVal($NAME,"Power_DC_Sum","0") + $Bat_out :ReadingsVal($NAME,"Power_DC_Sum","0") },\
\
Actual_battery_charge_-minus_or_discharge_-plus_Power:Actual_battery_charge_-minus_or_discharge_-plus_current.* {round((ReadingsVal($NAME,"Actual_battery_charge_-minus_or_discharge_-plus_current","0")*ReadingsVal($NAME,"Battery_voltage","0")),0)},\
\
Actual_battery_charge_usable_Power:Act_state_of_charge.* {my $x = (8960*(ReadingsVal($NAME,"Act_state_of_charge","0")-10)/100);;;; ($x lt 0)?0:round($x,0) }
attr PV_Anlage_1 verbose 0
Userreadings
Um später einige Abfragen und Diagramme einfacher zu erstellen wurden einige userreadings erstellt, die bereits bei der RAW Definition mit vorhanden sind. Während der Integration in FHEM und der Konfiguration kann es hierdurch jedoch noch Fehlermeldungen im FHEM Log geben, da noch nicht alle Werte vorhanden sind. Dies betrifft insbesondere die Statistics_* readings, die durch ein Python Skript später erzeugt werden.
Power_DC_Sum
Trigger: Total_DC_Power.* Dies berechnet direkt die Summe der DC-Leistung. Wenn der Plenticore einen Speicher hat, wird dieser am String 3 angeschlossen, sollte also kein Speicher vorhanden sein muss man hier den dritten String auch noch addieren.
Total_PV_Power_reserve
Trigger: Total_DC_Power.* Hier wird ein reading erstellt, dass als Information über die verwendbare Reserve dient. Die Leistung, die in den Speicher geht sollte man nämlich besser sofort verbrauchen, anstatt es erst zu speicher. Die 0.90 ist ein circa Wirkungsgrad, um von DC- auf AC-Leistung zu kommen. Der Total_PV_Power_reserve Wert kann gerne nach eigenen Belangen kalkuliert werden.
Total_DC_Power_Max
Trigger: Total_DC_Power.* Um die Batterieleistung mit zu berücksichtigen wird dieser Momentan Wert ermittelt.
Actual_battery_charge_-minus_or_discharge_-plus_Power
Trigger: Actual_battery_charge_-minus_or_discharge_-plus_current.* Berechnung der Batterie Leistung aus Spannung und Strom , der wert kann positiv oder negativ sein, je nach dem ob geladen oder entladen wird.
Actual_battery_charge_usable_Power
Trigger: Act_state_of_charge.* Dieser Wert gibt an, wieviel Leistung im Speicher vorhanden ist, reduziert um 10% Verluste. An dieser Stelle muss die Nennleistung des Speichers eingetragen werden, da diese noch nicht ausgelesen werden kann. Obwohl es im "Battery_Type 892941625" stecken könnte. 8929 >> 8.93 KW ???
Kostal Plenticore Plus die API (über HTTPMOD mit Python Skript Authentifizierung)
Der Plenticore erstellt intern noch diverse Statistiken, die auch über das WebGUI angesehen werden können. Diese werden jedoch nicht über MODBUS/TCP ausgegeben, aber zum Kostal Portal übermittelt, wo man dann bereits in Form von Diagrammen einen schönen Überblick bekommt. Das Ziel ist jedoch die Statistiken lokal im FHEM abzulegen, sie lassen sich nur über die API des Plenticore abfragen. Für diesen API Zugang über HTTP steht nun ein neues Gerät zur Verfügung, dass mit HTTPMOD und Python Authentifizierungsskripten die mehrstufige Anmeldung durchführt. Das ganze soll in Zukunft den starren Zugriff auf die Statistiken ablösen und eine Erweiterung zum setzen von Parametern im Plenticore bieten.
Umstellungsaufwand, falls jemand früher eingestiegen ist.
Die erste Folge für den Umstieg ist, dass die Statistiken nicht mehr im PV_Anlage_1 Gerät abgelegt werden, da diese ja teil der API Abfrage sind. Das muss dann in der Bilanz berücksichtigt werden, da die Einträge dann unter dem neuen PV_Anlage_1_API Gerät abgelegt werden.
2020.09.06 Die Umstellung auf das Plenticore API Gerät hat begonnen
- Die Bilanz RAW Definition wurde aktualisiert
- In der PV_Schedule Definition wurde das Kommando für die Aktualisierung der Statistik auf PV_Anlage_1_API umgestellt
- Das Logging von Dum.Energy läuft wie gehabt weiter
- Die Statistic_* readings aus dem PV_Anlage_1 Gerät können in der SQL Datenbank zum neuen PV_Anlage_1_API Gerät verschoben werden.
## Bitte zuerst den Zeitraum, in dem die Statistikeinträge in die Datenbank geschrieben wurden feststellen.
MySQL [fhem]> SELECT TIMESTAMP,DEVICE,READING,VALUE FROM history WHERE DEVICE='PV_Anlage_1_API' AND READING LIKE 'Statistic_%' AND TIMESTAMP > '2020-07-01 00:00:00' ORDER BY TIMESTAMP LIMIT 500;
## Für den Update dann din Zeitraum so weit es geht einschränken, damit die Laufzeit in der Datenbank nicht so hoch wird.
MySQL [fhem]> UPDATE history SET TIMESTAMP=TIMESTAMP, DEVICE='PV_Anlage_1_API' WHERE DEVICE='PV_Anlage_1' AND READING LIKE 'Statistic_%' AND TIMESTAMP > '2020-07-01 00:00:00';
- Die alten Statistic_* readings können nun aus dem PV_Anlage_1 Gerät entfernt werden
deletereading PV_Anlage_1 Statistic_.*
deletereading PV_Anlage_1 statistics_.*
- Für das Logging kann nun im Attribut "DbLogInclude" ebenfalls "Statistic_.*" entfernt werden
- Beim Attribut "event-on-change-reading" sind auch "statistics_.*,Statistic_.*" zu entfernen
- Das userreading im PV_Anlage_1 kann ebenfalls geändert werden und um diese Zeile verkürzt werden.
statistics_clean:statistics_output.* { my $x = ReadingsVal($NAME,"statistics_output",0);; $x =~ s/"moduleid": "scb:statistic:EnergyFlow", |, "moduleid": "scb:statistic:EnergyFlow"|"processdata": \[//g;; $x =~ s/id": "|, "unit": "", "value"|^\[|\]\}\]$//g;; $x =~ s/moduleid/statistics_00_moduleid/g;; $x =~ s/processdata/statistics/g;; $x =~ s/\}\, \{/\, /g;; $x =~ s/\{\{/\{/g;; return $x }
- Nun noch das Gerät Plenticore_Statistics für das expandJSON löschen
delete Plenticore_Statistics
Das sollten nun alle erforderlichen Bereinigungen für den Wechsel auf das PV_Anlage_1_API Gerät sein.
Plenticore API
http://<IP-Address_Plenticore>/api/v1
Dateiverzeichnis
/opt/fhem /opt/fhem/python/pwd_fhem.json /opt/fhem/python/pwd_plenticore.json /opt/fhem/python/bin /opt/fhem/python/bin/plenticore_auth_finish.py /opt/fhem/python/bin/plenticore_auth_session.py
Python 3
fhem@raspberrypi:~/python/bin$ which python3 /usr/bin/python3 fhem@raspberrypi:~/python/bin$ python3 --version Python 3.7.3
Passworte
Die Passworte für den Plenticore und den FHEM Zugang liegen in einzelnen JSON Dateien
fhem@raspberrypi:~/python$ cat pwd_plenticore.json
{ "username": "user", "password": "<Steht auf dem Gehäuse>" }
fhem@raspberrypi:~/python$ cat pwd_fhem.json
{ "username": "<Ein Fhem Telnet User>", "password": "<Das Passwort des Users>" }
plenticore_auth_* Erläuterung
Die folgenden beiden Skripte sind ein Extrakt aus den bisherigen Skripten, ohne die HTTP Aufrufe. Sie dienen nur noch der Generierung von Authentifizierungsschlüsseln. Optimaler Weise müssten sie auch noch nach Perl konvertiert werden, was für später geplant ist. Als Übergabeparameter werden aus dem PV_Anlage_1_API Gerät Rückgabewerte der HTTP Aufrufe verwendet.
plenticore_auth_finish
fhem@raspberrypi:~/python/bin$ cat plenticore_auth_finish.py
import string
import base64
import hashlib
import hmac
from Crypto.Cipher import AES
import binascii
import sys
import json
import fhem
web = sys.argv[1]
randomString = sys.argv[2]
nonce = sys.argv[3]
salt = sys.argv[4]
rounds = sys.argv[5]
try:
with open('/opt/fhem/python/pwd_plenticore.json', 'r') as f:
credentials=json.load(f)
except Exception as e:
print('Something went wrong: {}'.format(e))
PASSWD = credentials["password"]
u = randomString
#u = base64.b64encode(u.encode('utf-8')).decode('utf-8')
#print("randomString: ",u)
i = nonce
o = int(rounds)
a = salt
bitSalt = base64.b64decode(a)
#print("nonce : ",i)
#print("salt : ",a)
#print("rounds : ",o)
def getPBKDF2Hash(password, bytedSalt, rounds):
return hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), bytedSalt, rounds)
r = getPBKDF2Hash(PASSWD,bitSalt,o)
s = hmac.new(r, "Client Key".encode('utf-8'), hashlib.sha256).digest()
c = hmac.new(r, "Server Key".encode('utf-8'), hashlib.sha256).digest()
_ = hashlib.sha256(s).digest()
d = "n=user,r="+u+",r="+i+",s="+a+",i="+str(o)+",c=biws,r="+i
g = hmac.new(_, d.encode('utf-8'), hashlib.sha256).digest()
p = hmac.new(c, d.encode('utf-8'), hashlib.sha256).digest()
f = bytes(a ^ b for (a, b) in zip(s, g))
proof = base64.b64encode(f).decode('utf-8')
try:
with open('/opt/fhem/python/pwd_fhem.json', 'r') as f:
credentials=json.load(f)
except Exception as e:
print('Something went wrong: {}'.format(e))
fh = fhem.Fhem(web, protocol="http", port=8083, username=credentials["username"], password=credentials["password"])
fh.send_cmd("setreading PV_Anlage_1_API auth_proof " + proof)
#print("proof : ",proof)
plenticore_auth_session
fhem@raspberrypi:~/python/bin$ cat plenticore_auth_session.py
import string
import base64
import hashlib
import os
import hmac
from Crypto.Cipher import AES
import binascii
import sys
import json
import fhem
web = sys.argv[1]
randomString = sys.argv[2]
nonce = sys.argv[3]
salt = sys.argv[4]
rounds = sys.argv[5]
token = sys.argv[6]
try:
with open('/opt/fhem/python/pwd_plenticore.json', 'r') as f:
credentials=json.load(f)
except Exception as e:
print('Something went wrong: {}'.format(e))
PASSWD = credentials["password"]
u = randomString
i = nonce
o = int(rounds)
a = salt
bitSalt = base64.b64decode(a)
def getPBKDF2Hash(password, bytedSalt, rounds):
return hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), bytedSalt, rounds)
r = getPBKDF2Hash(PASSWD,bitSalt,o)
#print("randomString: ",u)
#print("nonce : ",i)
#print("salt : ",a)
#print("rounds : ",o)
#print("token : ",token)
s = hmac.new(r, "Client Key".encode('utf-8'), hashlib.sha256).digest()
_ = hashlib.sha256(s).digest()
d = "n=user,r="+u+",r="+i+",s="+a+",i="+str(o)+",c=biws,r="+i
y = hmac.new(_, "Session Key".encode('utf-8'), hashlib.sha256)
y.update(d.encode('utf-8'))
y.update(s)
P = y.digest()
protocol_key = P
t = os.urandom(16)
e2 = AES.new(protocol_key,AES.MODE_GCM,t)
e2, authtag = e2.encrypt_and_digest(token.encode('utf-8'))
iv = base64.b64encode(t).decode('utf-8')
authtag = base64.b64encode(authtag).decode("utf-8")
payload = base64.b64encode(e2).decode('utf-8')
try:
with open('/opt/fhem/python/pwd_fhem.json', 'r') as f:
credentials=json.load(f)
except Exception as e:
print('Something went wrong: {}'.format(e))
fh = fhem.Fhem(web, protocol="http", port=8083, username=credentials["username"], password=credentials["password"])
fh.send_cmd("setreading PV_Anlage_1_API auth_iv " + iv)
fh.send_cmd("setreading PV_Anlage_1_API auth_authtag " + authtag)
fh.send_cmd("setreading PV_Anlage_1_API auth_payload " + payload)
#print("iv : ",iv)
#print("authtag : ",authtag)
#print("payload : ",payload)
Ablaufbeschreibung PV_Anlage_1_API
Hier soll ein kurzer Überblick für den Anmeldeablauf geschaffen werden.
Automatischer Login mit Sessionaufbau
Der Ablauf startet mit dem ersten Aufruf und läuft dann vollautomatisch bis zur Rückgabe der auth_sessionId
1. get PV_Anlage_1_API 01/auth/start
- auth_randomString64 - wird generiert
- /api/v1/auth/start - HTTP request erfolgt
- auth_nonce, auth_salt, auth_rounds werden empfangen
2. auth_Step1_Message:auth_nonce.* wird getriggert
- plenticore_auth_finish.py wird ausgeführt
- auth_proof - wird berechnet
3. auth_Step2_Message:auth_proof.* wird getriggert
- Start von 02_/auth/finish
4. get PV_Anlage_1_API 02_/auth/finish
- /api/v1/auth/finish - HTTP request erfolgt
- auth_signature, auth_token werden empfangen
5. auth_Step3_Message:auth_token.* wird getriggert
- plenticore_auth_session.py wird ausgeführt
- auth_iv, auth_authtag, auth_payload - wird berechnet
6. auth_Step4_Message:auth_payload.* wird getriggert
- Start von 03_/auth/session
7. get PV_Anlage_1_API 03_/auth/session
- /api/v1/auth/session - HTTP request erfolgt
- auth_sessionId wird empfangen
Bei allen folgenden HTTP requests wird nun die erhaltene auth_sessenId übermittelt.
Bereits Implementierte Abfragen:
get:
start, finish, create_session werden für den Login benötigt und arbeiten über userreadings mit zwei Python Skripten zusammen
01_/auth/start
02_/auth/finish
03_/auth/create_session
Auskunft über den Anmeldezustand, es muss aber kurz vorher abgefragt werden
04_/auth/me
Informationen zum Plenticore
05_/info/version
Abfrage der Statistiken
20_/processdata/scb_statistic_EnergyFlow
Liste aller Plenticore Module. Es werden keine readings erzeugt. Das Ergebnis steht im httpbody, der mit attr <Device> showBody 1 angezeigt werden kann.
21_/modules_list
Hier gibt es viele technische Loggings. Sollte es Probleme beim Abholen der Daten geben kann es am timeout liegen
24_/logdata/download
Speicher Typen wurden folgende ermittelt 0=keine , 4=BYD
Diese Abfragewerte können auch gesetzt werden, siehe set Befehle
31_Battery_Type
32_Battery_MinHomeComsumption
33_Battery_Strategy
34_Battery_MinSoc
35_Battery_SmartBatteryControl_Enable
36_Battery_DynamicSoc_Enable
Das zeigt den FW Update Status beim FW Laden an. Die Rückmeldung ist im httpbody zu sehen, es werden keine readings erzeugt.
41_/update/status
set:
Eine bestehende Session wird abgemeldet
06_/auth/logout
Die letzten Events können in deutsch oder englisch abgeholt werden
23_/events/latest_5 [en-gb,de-de]
Batterie Einstellungen können verändert werden. Es werden einige Werte vorgeschlagen. Bitte vorher immer den aktuellen Wert abfragen und besser aufschreiben!
Hier sollte nur etwas gesetzt werden, wenn man sich sicher ist, was man tut.
31_Battery_Type [0,4]
32_Battery_MinHomeComsumption
33_Battery_Strategy
34_Battery_MinSoc
35_Battery_SmartBatteryControl_Enable
36_Battery_DynamicSoc_Enable
Der Reboot läuft noch nicht. Da ist die API ziemlich zickig. Ich habe jedoch bereits ein Skript, womit es geht :-)
51_/system/reboot
Sollte die Session abgelaufen, oder noch kein Login vollzogen worden sein, so wird automatisch eine Login mit Sessionaufbau durchgeführt.
RAW Definition des PV_Anlage_1_API
defmod PV_Anlage_1_API HTTPMOD http://%IP-Address_Plenticore%/api/v1/auth/me 0
attr PV_Anlage_1_API DbLogExclude .*
attr PV_Anlage_1_API DbLogInclude Statistic_.*
attr PV_Anlage_1_API authRetries 1
attr PV_Anlage_1_API dontRequeueAfterAuth 1
attr PV_Anlage_1_API enableControlSet 0
attr PV_Anlage_1_API enableCookies 1
attr PV_Anlage_1_API get01Data {"nonce": "%randomString64%","username": "user"}
attr PV_Anlage_1_API get01Name 01_/auth/start
attr PV_Anlage_1_API get01URL http://%IP-Address_Plenticore%/api/v1/auth/start
attr PV_Anlage_1_API get02-1Name auth_signature
attr PV_Anlage_1_API get02-2Name auth_token
attr PV_Anlage_1_API get02Data {"transactionId": "%auth_transactionId%", "proof": "%auth_proof%"}
attr PV_Anlage_1_API get02JSON .
attr PV_Anlage_1_API get02Name 02_/auth/finish
attr PV_Anlage_1_API get02URL http://%IP-Address_Plenticore%/api/v1/auth/finish
attr PV_Anlage_1_API get03Data {"transactionId": "%auth_transactionId%", "iv": "%auth_iv%", "tag": "%auth_authtag%", "payload": "%auth_payload%"}
attr PV_Anlage_1_API get03Name 03_/auth/create_session
attr PV_Anlage_1_API get03URL http://%IP-Address_Plenticore%/api/v1/auth/create_session
attr PV_Anlage_1_API get04-1Name auth_me_active
attr PV_Anlage_1_API get04-2Name auth_me_locked
attr PV_Anlage_1_API get04-3Name auth_me_authenticated
attr PV_Anlage_1_API get04-4Name auth_me_anonymous
attr PV_Anlage_1_API get04-5Name auth_me_role
attr PV_Anlage_1_API get04-6Name auth_me_permissions
attr PV_Anlage_1_API get04Header authorization: Session %auth_sessionId%
attr PV_Anlage_1_API get04JSON .
attr PV_Anlage_1_API get04Name 04_/auth/me
attr PV_Anlage_1_API get04URL http://%IP-Address_Plenticore%/api/v1/auth/me
attr PV_Anlage_1_API get05-1Name info_name
attr PV_Anlage_1_API get05-2Name info_api_version
attr PV_Anlage_1_API get05-3Name info_sw_version
attr PV_Anlage_1_API get05-4Name info_hostname
attr PV_Anlage_1_API get05JSON .
attr PV_Anlage_1_API get05Name 05_/info/version
attr PV_Anlage_1_API get05URL http://%IP-Address_Plenticore%/api/v1/info/version
attr PV_Anlage_1_API get20-10Name Statistic_EnergyHome_Month
attr PV_Anlage_1_API get20-11Name Statistic_EnergyHome_Total
attr PV_Anlage_1_API get20-12Name Statistic_EnergyHome_Year
attr PV_Anlage_1_API get20-13Name Statistic_EnergyHomeBat_Day
attr PV_Anlage_1_API get20-14Name Statistic_EnergyHomeBat_Month
attr PV_Anlage_1_API get20-15Name Statistic_EnergyHomeBat_Total
attr PV_Anlage_1_API get20-16Name Statistic_EnergyHomeBat_Year
attr PV_Anlage_1_API get20-17Name Statistic_EnergyHomeGrid_Day
attr PV_Anlage_1_API get20-18Name Statistic_EnergyHomeGrid_Month
attr PV_Anlage_1_API get20-19Name Statistic_EnergyHomeGrid_Total
attr PV_Anlage_1_API get20-1Name Statistic_Autarky_Day
attr PV_Anlage_1_API get20-20Name Statistic_EnergyHomeGrid_Year
attr PV_Anlage_1_API get20-21Name Statistic_EnergyHomePv_Day
attr PV_Anlage_1_API get20-22Name Statistic_EnergyHomePv_Month
attr PV_Anlage_1_API get20-23Name Statistic_EnergyHomePv_Total
attr PV_Anlage_1_API get20-24Name Statistic_EnergyHomePv_Year
attr PV_Anlage_1_API get20-25Name Statistic_OwnConsumptionRate_Day
attr PV_Anlage_1_API get20-26Name Statistic_OwnConsumptionRate_Month
attr PV_Anlage_1_API get20-27Name Statistic_OwnConsumptionRate_Total
attr PV_Anlage_1_API get20-28Name Statistic_OwnConsumptionRate_Year
attr PV_Anlage_1_API get20-29Name Statistic_Yield_Day
attr PV_Anlage_1_API get20-2Name Statistic_Autarky_Month
attr PV_Anlage_1_API get20-30Name Statistic_Yield_Month
attr PV_Anlage_1_API get20-31Name Statistic_Yield_Total
attr PV_Anlage_1_API get20-32Name Statistic_Yield_Year
attr PV_Anlage_1_API get20-3Name Statistic_Autarky_Total
attr PV_Anlage_1_API get20-4Name Statistic_Autarky_Year
attr PV_Anlage_1_API get20-5Name Statistic_CO2Saving_Day
attr PV_Anlage_1_API get20-6Name Statistic_CO2Saving_Month
attr PV_Anlage_1_API get20-7Name Statistic_CO2Saving_Total
attr PV_Anlage_1_API get20-8Name Statistic_CO2Saving_Year
attr PV_Anlage_1_API get20-9Name Statistic_EnergyHome_Day
attr PV_Anlage_1_API get20Header authorization: Session %auth_sessionId%
attr PV_Anlage_1_API get20JSON 01_processdata_.._value
attr PV_Anlage_1_API get20Name 20_/processdata/scb_statistic_EnergyFlow
attr PV_Anlage_1_API get20URL http://%IP-Address_Plenticore%/api/v1/processdata/scb:statistic:EnergyFlow
attr PV_Anlage_1_API get21Name 21_/modules_list
attr PV_Anlage_1_API get21URL http://%IP-Address_Plenticore%/api/v1/modules
attr PV_Anlage_1_API get24Data {"end":"%end_date%","begin":"%begin_date%"}
attr PV_Anlage_1_API get24Header01 authorization: Session %auth_sessionId%
attr PV_Anlage_1_API get24Header02 Content-type: application/json, Accept: application/json, Connection: keep-alive
attr PV_Anlage_1_API get24Name 24_/logdata/download
attr PV_Anlage_1_API get24URL http://%IP-Address_Plenticore%/api/v1/logdata/download
attr PV_Anlage_1_API get31-1Name Battery_Type
attr PV_Anlage_1_API get31-2Name Battery_Type
attr PV_Anlage_1_API get31Header01 authorization: Session %auth_sessionId%
attr PV_Anlage_1_API get31Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_Anlage_1_API get31JSON .
attr PV_Anlage_1_API get31Name 31_Battery_Type
attr PV_Anlage_1_API get31URL http://%IP-Address_Plenticore%/api/v1/settings/devices:local/Battery:Type
attr PV_Anlage_1_API get32-1Name Battery_MinHomeComsumption
attr PV_Anlage_1_API get32-2Name Battery_MinHomeComsumption
attr PV_Anlage_1_API get32Header01 authorization: Session %auth_sessionId%
attr PV_Anlage_1_API get32Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_Anlage_1_API get32JSON .
attr PV_Anlage_1_API get32Name 32_Battery_MinHomeComsumption
attr PV_Anlage_1_API get32URL http://%IP-Address_Plenticore%/api/v1/settings/devices:local/Battery:MinHomeComsumption
attr PV_Anlage_1_API get33-1Name Battery_Strategy
attr PV_Anlage_1_API get33-2Name Battery_Strategy
attr PV_Anlage_1_API get33Header01 authorization: Session %auth_sessionId%
attr PV_Anlage_1_API get33Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_Anlage_1_API get33JSON .
attr PV_Anlage_1_API get33Name 33_Battery_Strategy
attr PV_Anlage_1_API get33URL http://%IP-Address_Plenticore%/api/v1/settings/devices:local/Battery:Strategy
attr PV_Anlage_1_API get34-1Name Battery_MinSoc
attr PV_Anlage_1_API get34-2Name Battery_MinSoc
attr PV_Anlage_1_API get34Header01 authorization: Session %auth_sessionId%
attr PV_Anlage_1_API get34Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_Anlage_1_API get34JSON .
attr PV_Anlage_1_API get34Name 34_Battery_MinSoc
attr PV_Anlage_1_API get34URL http://%IP-Address_Plenticore%/api/v1/settings/devices:local/Battery:MinSoc
attr PV_Anlage_1_API get35-1Name Battery_SmartBatteryControl_Enable
attr PV_Anlage_1_API get35-2Name Battery_SmartBatteryControl_Enable
attr PV_Anlage_1_API get35Header01 authorization: Session %auth_sessionId%
attr PV_Anlage_1_API get35Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_Anlage_1_API get35JSON .
attr PV_Anlage_1_API get35Name 35_Battery_SmartBatteryControl_Enable
attr PV_Anlage_1_API get35URL http://%IP-Address_Plenticore%/api/v1/settings/devices:local/Battery:SmartBatteryControl:Enable
attr PV_Anlage_1_API get36-1Name Battery_DynamicSoc_Enable
attr PV_Anlage_1_API get36-2Name Battery_DynamicSoc_Enable
attr PV_Anlage_1_API get36Header01 authorization: Session %auth_sessionId%
attr PV_Anlage_1_API get36Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_Anlage_1_API get36JSON .
attr PV_Anlage_1_API get36Name 36_Battery_DynamicSoc_Enable
attr PV_Anlage_1_API get36URL http://%IP-Address_Plenticore%/api/v1/settings/devices:local/Battery:DynamicSoc:Enable
attr PV_Anlage_1_API get41Header authorization: Session %auth_sessionId%
attr PV_Anlage_1_API get41Name 41_/update/status
attr PV_Anlage_1_API get41URL http://%IP-Address_Plenticore%/api/v1/update/status
attr PV_Anlage_1_API getHeader01 Accept-Encoding: gzip,deflate
attr PV_Anlage_1_API getHeader02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_Anlage_1_API group PV Eigenverbrauch
attr PV_Anlage_1_API icon sani_solar
attr PV_Anlage_1_API reAuthRegex "authenticated":false|"processdata":\[\]|wrong credentials
attr PV_Anlage_1_API reading01-1Name auth_nonce
attr PV_Anlage_1_API reading01-2Name auth_rounds
attr PV_Anlage_1_API reading01-3Name auth_salt
attr PV_Anlage_1_API reading01-4Name auth_transactionId
attr PV_Anlage_1_API reading01JSON .
attr PV_Anlage_1_API reading02JSON sessionId
attr PV_Anlage_1_API reading02Name auth_sessionId
attr PV_Anlage_1_API reading0301JSON message
attr PV_Anlage_1_API reading0301Name info_message
attr PV_Anlage_1_API reading0302JSON error
attr PV_Anlage_1_API reading0302Name info_error
attr PV_Anlage_1_API replacement01Mode expression
attr PV_Anlage_1_API replacement01Regex %IP-Address_Plenticore%
attr PV_Anlage_1_API replacement01Value {ReadingsVal("PV_Anlage_1_config","IP-Address_Plenticore","")}
attr PV_Anlage_1_API replacement02Mode reading
attr PV_Anlage_1_API replacement02Regex %auth_transactionId%
attr PV_Anlage_1_API replacement02Value auth_transactionId
attr PV_Anlage_1_API replacement03Mode reading
attr PV_Anlage_1_API replacement03Regex %auth_proof%
attr PV_Anlage_1_API replacement03Value auth_proof
attr PV_Anlage_1_API replacement04Mode expression
attr PV_Anlage_1_API replacement04Regex %randomString64%
attr PV_Anlage_1_API replacement04Value {my $NAME = "PV_Anlage_1_API" ;;;;fhem("deletereading ".$NAME." message");;;;fhem("deletereading ".$NAME." auth.*");;;;my @chars=('a'..'z','A'..'Z','0'..'9');; my $r;; foreach(1..16) {$r.=$chars[rand @chars];;};;;; fhem("setreading ".$NAME." auth_randomString64 ".$r);;;; $r;;;;}
attr PV_Anlage_1_API replacement05Mode reading
attr PV_Anlage_1_API replacement05Regex %auth_randomString64%
attr PV_Anlage_1_API replacement05Value auth_randomString64
attr PV_Anlage_1_API replacement06Mode reading
attr PV_Anlage_1_API replacement06Regex %auth_token%
attr PV_Anlage_1_API replacement06Value auth_token
attr PV_Anlage_1_API replacement07Mode reading
attr PV_Anlage_1_API replacement07Regex %auth_signature%
attr PV_Anlage_1_API replacement07Value auth_signature
attr PV_Anlage_1_API replacement08Mode reading
attr PV_Anlage_1_API replacement08Regex %auth_authtag%
attr PV_Anlage_1_API replacement08Value auth_authtag
attr PV_Anlage_1_API replacement09Mode reading
attr PV_Anlage_1_API replacement09Regex %auth_payload%
attr PV_Anlage_1_API replacement09Value auth_payload
attr PV_Anlage_1_API replacement10Mode reading
attr PV_Anlage_1_API replacement10Regex %auth_iv%
attr PV_Anlage_1_API replacement10Value auth_iv
attr PV_Anlage_1_API replacement11Mode reading
attr PV_Anlage_1_API replacement11Regex %auth_sessionId%
attr PV_Anlage_1_API replacement11Value auth_sessionId
attr PV_Anlage_1_API replacement12Mode expression
attr PV_Anlage_1_API replacement12Regex %begin_date%
attr PV_Anlage_1_API replacement12Value {POSIX::strftime("%Y-%m-%d",localtime(time))}
attr PV_Anlage_1_API replacement13Mode expression
attr PV_Anlage_1_API replacement13Regex %end_date%
attr PV_Anlage_1_API replacement13Value {POSIX::strftime("%Y-%m-%d",localtime(time))}
attr PV_Anlage_1_API room Strom->Photovoltaik
attr PV_Anlage_1_API set06Header01 authorization: Session %auth_sessionId%
attr PV_Anlage_1_API set06Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_Anlage_1_API set06Method POST
attr PV_Anlage_1_API set06Name 06_/auth/logout
attr PV_Anlage_1_API set06NoArg 1
attr PV_Anlage_1_API set06URL http://%IP-Address_Plenticore%/api/v1/auth/logout
attr PV_Anlage_1_API set23-10Name Event_02_code
attr PV_Anlage_1_API set23-11Name Event_02_description
attr PV_Anlage_1_API set23-12Name Event_02_end_time
attr PV_Anlage_1_API set23-13Name Event_02_group
attr PV_Anlage_1_API set23-14Name Event_02_is_active
attr PV_Anlage_1_API set23-15Name Event_02_long_description
attr PV_Anlage_1_API set23-16Name Event_02_start_time
attr PV_Anlage_1_API set23-17Name Event_03_category
attr PV_Anlage_1_API set23-18Name Event_03_code
attr PV_Anlage_1_API set23-19Name Event_03_description
attr PV_Anlage_1_API set23-1Name Event_01_category
attr PV_Anlage_1_API set23-20Name Event_03_end_time
attr PV_Anlage_1_API set23-21Name Event_03_group
attr PV_Anlage_1_API set23-22Name Event_03_is_active
attr PV_Anlage_1_API set23-23Name Event_03_long_description
attr PV_Anlage_1_API set23-24Name Event_03_start_time
attr PV_Anlage_1_API set23-25Name Event_04_category
attr PV_Anlage_1_API set23-26Name Event_04_code
attr PV_Anlage_1_API set23-27Name Event_04_description
attr PV_Anlage_1_API set23-28Name Event_04_end_time
attr PV_Anlage_1_API set23-29Name Event_04_group
attr PV_Anlage_1_API set23-2Name Event_01_code
attr PV_Anlage_1_API set23-30Name Event_04_is_active
attr PV_Anlage_1_API set23-31Name Event_04_long_description
attr PV_Anlage_1_API set23-32Name Event_04_start_time
attr PV_Anlage_1_API set23-33Name Event_05_category
attr PV_Anlage_1_API set23-34Name Event_05_code
attr PV_Anlage_1_API set23-35Name Event_05_description
attr PV_Anlage_1_API set23-36Name Event_05_end_time
attr PV_Anlage_1_API set23-37Name Event_05_group
attr PV_Anlage_1_API set23-38Name Event_05_is_active
attr PV_Anlage_1_API set23-39Name Event_05_long_description
attr PV_Anlage_1_API set23-3Name Event_01_description
attr PV_Anlage_1_API set23-40Name Event_05_start_time
attr PV_Anlage_1_API set23-4Name Event_01_end_time
attr PV_Anlage_1_API set23-5Name Event_01_group
attr PV_Anlage_1_API set23-6Name Event_01_is_active
attr PV_Anlage_1_API set23-7Name Event_01_long_description
attr PV_Anlage_1_API set23-8Name Event_01_start_time
attr PV_Anlage_1_API set23-9Name Event_02_category
attr PV_Anlage_1_API set23Data {"max":5,"language":"$val"}
attr PV_Anlage_1_API set23Header01 authorization: Session %auth_sessionId%
attr PV_Anlage_1_API set23Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_Anlage_1_API set23Hint en-gb,de-de
attr PV_Anlage_1_API set23JSON .
attr PV_Anlage_1_API set23Name 23_/events/latest_5
attr PV_Anlage_1_API set23ParseResponse 1
attr PV_Anlage_1_API set23TextArg 1
attr PV_Anlage_1_API set23URL http://%IP-Address_Plenticore%/api/v1/events/latest
attr PV_Anlage_1_API set31Data [{"moduleid":"devices:local","settings":[{"id":"Battery:Type","value":"$val"}]}]
attr PV_Anlage_1_API set31Header01 authorization: Session %auth_sessionId%
attr PV_Anlage_1_API set31Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_Anlage_1_API set31Hint 0,4
attr PV_Anlage_1_API set31Method PUT
attr PV_Anlage_1_API set31Name 31_Battery_Type
attr PV_Anlage_1_API set31URL http://%IP-Address_Plenticore%/api/v1/settings
attr PV_Anlage_1_API set33Data [{"moduleid":"devices:local","settings":[{"id":"Battery:Strategy","value":"$val"}]}]
attr PV_Anlage_1_API set33Header01 authorization: Session %auth_sessionId%
attr PV_Anlage_1_API set33Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_Anlage_1_API set33Hint 1,2
attr PV_Anlage_1_API set33Method PUT
attr PV_Anlage_1_API set33Name 31_Battery_Strategy
attr PV_Anlage_1_API set33URL http://%IP-Address_Plenticore%/api/v1/settings
attr PV_Anlage_1_API set34Data [{"moduleid":"devices:local","settings":[{"id":"Battery:MinSoc","value":"$val"}]}]
attr PV_Anlage_1_API set34Header01 authorization: Session %auth_sessionId%
attr PV_Anlage_1_API set34Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_Anlage_1_API set34Hint slider,10,5,100
attr PV_Anlage_1_API set34Method PUT
attr PV_Anlage_1_API set34Name 34_Battery_MinSoc
attr PV_Anlage_1_API set34URL http://%IP-Address_Plenticore%/api/v1/settings
attr PV_Anlage_1_API set35Data [{"moduleid":"devices:local","settings":[{"id":"Battery:SmartBatteryControl:Enable","value":"$val"}]}]
attr PV_Anlage_1_API set35Header01 authorization: Session %auth_sessionId%
attr PV_Anlage_1_API set35Header02 Content-type:application/json, Accept:application/json, Connection:keep-alive
attr PV_Anlage_1_API set35Hint 0,1
attr PV_Anlage_1_API set35Method PUT
attr PV_Anlage_1_API set35Name 35_SmartBatteryControl_Enable
attr PV_Anlage_1_API set35URL http://%IP-Address_Plenticore%/api/v1/settings
attr PV_Anlage_1_API set51Header01 authorization: Session %auth_sessionId%
attr PV_Anlage_1_API set51Header02 Accept: application/json, Connection: keep-alive, Content-type: application/json
attr PV_Anlage_1_API set51Method Post
attr PV_Anlage_1_API set51Name 51_/system/reboot
attr PV_Anlage_1_API set51NoArg 1
attr PV_Anlage_1_API set51URL http://%IP-Address_Plenticore%/api/v1/system/reboot
attr PV_Anlage_1_API showBody 1
attr PV_Anlage_1_API showError 1
attr PV_Anlage_1_API sid01Data {"nonce": "%randomString64%","username": "user"}
attr PV_Anlage_1_API sid01ParseResponse 1
attr PV_Anlage_1_API sid01URL http://%IP-Address_Plenticore%/api/v1/auth/start
attr PV_Anlage_1_API sidHeader01 Accept-Encoding: gzip,deflate
attr PV_Anlage_1_API sidHeader02 Content-type: application/json, Accept: application/json, Connection: keep-alive
attr PV_Anlage_1_API sortby 02
attr PV_Anlage_1_API timeout 7
attr PV_Anlage_1_API userReadings auth_first_called:auth_randomString64.* {my $first_called = InternalVal("$NAME","path","n.a");;;; $first_called =~ s/:/_/g;;;;$first_called ;;;;$first_called =~ s/\/api\/v1//g;;;; my %replace = ("/auth/me" => "04_/auth/me","/info/version" => "05_/info/version","/processdata/scb_statistic_EnergyFlow" => "20_/processdata/scb_statistic_EnergyFlow","/modules_list" => "21_/modules_list","/logdata/download" => "24_/logdata/download","/update/status" => "41_/update/status","/system/reboot" => "04_/auth/me",);;;; $replace{$first_called};;;;},\
\
auth_Step1_Message:auth_transactionId.* {system("/usr/bin/python3 /opt/fhem/python/bin/plenticore_auth_finish.py ".ReadingsVal("PV_Anlage_1_config","IP-Address_FHEM","")." ".ReadingsVal("$NAME","auth_randomString64","")." ".ReadingsVal("$NAME","auth_nonce","")." ".ReadingsVal("$NAME","auth_salt","")." ".ReadingsVal("$NAME","auth_rounds","29000")." &");;;; "Prepare auth_finish started with auth_nonce ".ReadingsVal("$NAME","auth_nonce","")},\
\
auth_Step2_Message:auth_proof.* { fhem("get ".$NAME." 02_/auth/finish") ;;;; "HTTP Request 02_/auth/finish with auth_proof ".ReadingsVal("$NAME","auth_proof","")},\
\
auth_Step3_Message:auth_token.* {system("/usr/bin/python3 /opt/fhem/python/bin/plenticore_auth_session.py ".ReadingsVal("PV_Anlage_1_config","IP-Address_FHEM","")." ".ReadingsVal("$NAME","auth_randomString64","")." ".ReadingsVal("$NAME","auth_nonce","")." ".ReadingsVal("$NAME","auth_salt","")." ".ReadingsVal("$NAME","auth_rounds","")." ".ReadingsVal("$NAME","auth_token","")." &");;;; "Prepare auth_session started with auth_token ".ReadingsVal("$NAME","auth_token","")},\
\
auth_Step4_Message:auth_payload.* { fhem("get ".$NAME." 03_/auth/create_session") ;;;; "HTTP Request 03_/auth/create_session with auth_authtag ".ReadingsVal("$NAME","auth_authtag","")},\
\
auth_Step5_Message:auth_sessionId.* {my $restack = ReadingsVal("$NAME","auth_first_called","") ;;;; ($restack != "")?fhem("get ".$NAME." ".$restack):"No further HTTP request";;;;}
attr PV_Anlage_1_API verbose 5
Kostal Smart Energy Manager (KSEM) (Modbus/TCP)
Diese Einbindung ist nicht zwingend notwendig, jedoch weil es möglich ist hier beschrieben. Das Gerät ist hier mit "disable 1" konfiguriert, um es zu verwenden muss das Attribut auf 0 gesetzt oder einfach gelöscht werden.
Auch hier ist ein Interval von 60 Sekunden gesetzt worden. Das Logging ist noch komplett deaktiviert, weshalb man seine Werte noch selber definieren muss.
RAW Definition des KSEM
Zun Thema KSEM bestand direkter Kontakt mit dem Kostal Service. Der KSEM ermittelt nicht alle Werte, welche in der SunSpec spezifiziert sind. Alle nicht unterstützen Werte sind in den Registern mit 0x8000 gekennzeichnet. Für die nicht unterstützten Zählerstände wird 0x800000000 ausgegeben. Der Summenstrom M_AC_Current (sum of active phases) kann aber durch den Endanwender selber aus der Summe der Einzelwerte (Phase A AC current, Phase B AC current Phase C AC current) berechnet werden. Die einzelnen Spannungen zwischen den Phasen können nicht gemessen werden und werden deshalb nicht ausgegeben.
defmod PV_KSEM ModbusAttr 1 60 <IP-Address_KSEM>:502 TCP
attr PV_KSEM DbLogExclude .*
attr PV_KSEM alias PV_Energy_Manager
attr PV_KSEM dev-h-defPoll 1
attr PV_KSEM dev-type-INT16-len 1
attr PV_KSEM dev-type-INT16-unpack s>
attr PV_KSEM dev-type-INT16_Current-expr $val * (10 ** ReadingsNum("$name" ,"M_AC_Current_SF",0))
attr PV_KSEM dev-type-INT16_Current-format %.2f
attr PV_KSEM dev-type-INT16_Current-len 1
attr PV_KSEM dev-type-INT16_Current-unpack s>
attr PV_KSEM dev-type-INT16_Freq-expr $val * (10 ** ReadingsNum("$name" ,"M_AC_Freq_SF",0))
attr PV_KSEM dev-type-INT16_Freq-format %.2f
attr PV_KSEM dev-type-INT16_Freq-len 1
attr PV_KSEM dev-type-INT16_Freq-unpack s>
attr PV_KSEM dev-type-INT16_PF-expr $val * (10 ** ReadingsNum("$name" ,"M_AC_PF_SF",0))
attr PV_KSEM dev-type-INT16_PF-format %.2f
attr PV_KSEM dev-type-INT16_PF-len 1
attr PV_KSEM dev-type-INT16_PF-unpack s>
attr PV_KSEM dev-type-INT16_Power-expr $val * (10 ** ReadingsNum("$name" ,"M_AC_Power_SF",0))
attr PV_KSEM dev-type-INT16_Power-format %.2f
attr PV_KSEM dev-type-INT16_Power-len 1
attr PV_KSEM dev-type-INT16_Power-unpack s>
attr PV_KSEM dev-type-INT16_VA-expr $val * (10 ** ReadingsNum("$name" ,"M_AC_VA_SF",0))
attr PV_KSEM dev-type-INT16_VA-format %.2f
attr PV_KSEM dev-type-INT16_VA-len 1
attr PV_KSEM dev-type-INT16_VA-unpack s>
attr PV_KSEM dev-type-INT16_VAR-expr $val * (10 ** ReadingsNum("$name" ,"M_AC_VAR_SF",0))
attr PV_KSEM dev-type-INT16_VAR-format %.2f
attr PV_KSEM dev-type-INT16_VAR-len 1
attr PV_KSEM dev-type-INT16_VAR-unpack s>
attr PV_KSEM dev-type-INT16_Voltage-expr $val * (10 ** ReadingsNum("$name" ,"M_AC_Voltage_SF",0))
attr PV_KSEM dev-type-INT16_Voltage-format %.2f
attr PV_KSEM dev-type-INT16_Voltage-len 1
attr PV_KSEM dev-type-INT16_Voltage-unpack s>
attr PV_KSEM dev-type-STR32-expr $val =~ s/[\00]+//gr
attr PV_KSEM dev-type-STR32-format %s
attr PV_KSEM dev-type-STR32-len 16
attr PV_KSEM dev-type-STR32-unpack a*
attr PV_KSEM dev-type-UINT16-format %s
attr PV_KSEM dev-type-UINT16-len 1
attr PV_KSEM dev-type-UINT32-format %s
attr PV_KSEM dev-type-UINT32-len 2
attr PV_KSEM dev-type-UINT64-expr $val/10000
attr PV_KSEM dev-type-UINT64-format %s
attr PV_KSEM dev-type-UINT64-len 4
attr PV_KSEM dev-type-UINT64-unpack Q>
attr PV_KSEM disable 1
attr PV_KSEM group PV Eigenverbrauch
attr PV_KSEM icon measure_power
attr PV_KSEM obj-h40072-reading M_AC_Current_A
attr PV_KSEM obj-h40072-type INT16_Current
attr PV_KSEM obj-h40073-reading M_AC_Current_B
attr PV_KSEM obj-h40073-type INT16_Current
attr PV_KSEM obj-h40074-reading M_AC_Current_C
attr PV_KSEM obj-h40074-type INT16_Current
attr PV_KSEM obj-h40075-reading M_AC_Current_SF
attr PV_KSEM obj-h40075-type INT16
attr PV_KSEM obj-h40077-reading M_AC_Voltage_AN
attr PV_KSEM obj-h40077-type INT16_Voltage
attr PV_KSEM obj-h40078-reading M_AC_Voltage_BN
attr PV_KSEM obj-h40078-type INT16_Voltage
attr PV_KSEM obj-h40079-reading M_AC_Voltage_CN
attr PV_KSEM obj-h40079-type INT16_Voltage
attr PV_KSEM obj-h40084-reading M_AC_Voltage_SF
attr PV_KSEM obj-h40084-type INT16
attr PV_KSEM obj-h40085-reading M_AC_Freq
attr PV_KSEM obj-h40085-type INT16_Freq
attr PV_KSEM obj-h40086-reading M_AC_Freq_SF
attr PV_KSEM obj-h40086-type INT16
attr PV_KSEM obj-h40087-reading M_AC_Power
attr PV_KSEM obj-h40087-type INT16_Power
attr PV_KSEM obj-h40088-reading M_AC_Power_A
attr PV_KSEM obj-h40088-type INT16_Power
attr PV_KSEM obj-h40089-reading M_AC_Power_B
attr PV_KSEM obj-h40089-type INT16_Power
attr PV_KSEM obj-h40090-reading M_AC_Power_C
attr PV_KSEM obj-h40090-type INT16_Power
attr PV_KSEM obj-h40091-reading M_AC_Power_SF
attr PV_KSEM obj-h40091-type INT16
attr PV_KSEM obj-h40092-reading M_AC_VA
attr PV_KSEM obj-h40092-type INT16_VA
attr PV_KSEM obj-h40093-reading M_AC_VA_A
attr PV_KSEM obj-h40093-type INT16_VA
attr PV_KSEM obj-h40094-reading M_AC_VA_B
attr PV_KSEM obj-h40094-type INT16_VA
attr PV_KSEM obj-h40095-reading M_AC_VA_C
attr PV_KSEM obj-h40095-type INT16_VA
attr PV_KSEM obj-h40096-reading M_AC_VA_SF
attr PV_KSEM obj-h40096-type INT16
attr PV_KSEM obj-h40097-reading M_AC_VAR
attr PV_KSEM obj-h40097-type INT16_VAR
attr PV_KSEM obj-h40098-reading M_AC_VAR_A
attr PV_KSEM obj-h40098-type INT16_VAR
attr PV_KSEM obj-h40099-reading M_AC_VAR_B
attr PV_KSEM obj-h40099-type INT16_VAR
attr PV_KSEM obj-h40100-reading M_AC_VAR_C
attr PV_KSEM obj-h40100-type INT16_VAR
attr PV_KSEM obj-h40101-reading M_AC_VAR_SF
attr PV_KSEM obj-h40101-type INT16
attr PV_KSEM obj-h40102-reading M_AC_PF
attr PV_KSEM obj-h40102-type INT16_PF
attr PV_KSEM obj-h40103-reading M_AC_PF_A
attr PV_KSEM obj-h40103-type INT16_PF
attr PV_KSEM obj-h40104-reading M_AC_PF_B
attr PV_KSEM obj-h40104-type INT16_PF
attr PV_KSEM obj-h40105-reading M_AC_PF_C
attr PV_KSEM obj-h40105-type INT16_PF
attr PV_KSEM obj-h40106-reading M_AC_PF_SF
attr PV_KSEM obj-h40106-type INT16
attr PV_KSEM obj-h40108-reading M_Exported
attr PV_KSEM obj-h40108-type UINT32
attr PV_KSEM obj-h40110-reading M_Exported_A
attr PV_KSEM obj-h40110-type UINT32
attr PV_KSEM obj-h40112-reading M_Exported_B
attr PV_KSEM obj-h40112-type UINT32
attr PV_KSEM obj-h40114-reading M_Exported_C
attr PV_KSEM obj-h40114-type UINT32
attr PV_KSEM obj-h40116-reading M_Imported
attr PV_KSEM obj-h40116-type UINT32
attr PV_KSEM obj-h40118-reading M_Imported_A
attr PV_KSEM obj-h40118-type UINT32
attr PV_KSEM obj-h40120-reading M_Imported_B
attr PV_KSEM obj-h40120-type UINT32
attr PV_KSEM obj-h40122-reading M_Imported_C
attr PV_KSEM obj-h40122-type UINT32
attr PV_KSEM obj-h40125-reading M_Exported_VA
attr PV_KSEM obj-h40125-type UINT32
attr PV_KSEM obj-h40127-reading M_Exported_VA_A
attr PV_KSEM obj-h40127-type UINT32
attr PV_KSEM obj-h40129-reading M_Exported_VA_B
attr PV_KSEM obj-h40129-type UINT32
attr PV_KSEM obj-h40131-reading M_Exported_VA_C
attr PV_KSEM obj-h40131-type UINT32
attr PV_KSEM obj-h40133-reading M_Imported_VA
attr PV_KSEM obj-h40133-type UINT32
attr PV_KSEM obj-h40135-reading M_Imported_VA_A
attr PV_KSEM obj-h40135-type UINT32
attr PV_KSEM obj-h40137-reading M_Imported_VA_B
attr PV_KSEM obj-h40137-type UINT32
attr PV_KSEM obj-h40139-reading M_Imported_VA_C
attr PV_KSEM obj-h40139-type UINT32
attr PV_KSEM obj-h512-reading Active_energy+
attr PV_KSEM obj-h512-type UINT64
attr PV_KSEM obj-h516-reading Active_energy-
attr PV_KSEM obj-h516-type UINT64
attr PV_KSEM obj-h8192-reading ManufacturerID
attr PV_KSEM obj-h8192-type UINT16
attr PV_KSEM obj-h8193-reading ProductID
attr PV_KSEM obj-h8193-type UINT16
attr PV_KSEM obj-h8194-reading ProductVersion
attr PV_KSEM obj-h8194-type UINT16
attr PV_KSEM obj-h8195-reading FirmwareVersion
attr PV_KSEM obj-h8195-type UINT16
attr PV_KSEM obj-h8196-reading VendorName
attr PV_KSEM obj-h8196-type STR32
attr PV_KSEM obj-h8212-reading Productname
attr PV_KSEM obj-h8212-type STR32
attr PV_KSEM obj-h8228-reading SerialNumber
attr PV_KSEM obj-h8228-type STR32
attr PV_KSEM obj-h8244-reading MeasuringInterval
attr PV_KSEM obj-h8244-type UINT16
attr PV_KSEM room Strom->Photovoltaik
attr PV_KSEM sortby 03
attr PV_KSEM userReadings M_AC_Current:M_AC_Current_.* { ReadingsVal($NAME,"M_AC_Current_A",0) + ReadingsVal($NAME,"M_AC_Current_B",0) + ReadingsVal($NAME,"M_AC_Current_C",0) }
attr PV_KSEM verbose 0
BYD Speicher (über Python Skript)
Diese Einbindung ist nicht zwingend notwendig. Ein Ziel ist es das Python Skript später zu entfernen und eine direkten HTTPMOD Abfrage zu implementieren.
byd_status.py
fhem@raspberrypi:~/python/bin$ cat byd_status.py
#
# Copyright: Kilian Knoll, 10.3.2019
#
# Utility to parse rundata values of BYD HV BOX
# Version 1.0
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#
# Please note that any incorrect or careless usage of this module as well as errors in the implementation can damage your BYD box!
# Therefore, the author does not provide any guarantee or warranty concerning to correctness, functionality or performance and does not accept any liability for damage caused by this module, examples or mentioned information.
# Thus, use it at your own risk!
#
#
# Purpose:
# Query Rundata values from BYD box via local network connection
# Used with BYD HV 6.4
#
# Returnvalue: 0 (Everything all right)
# Returnvalue: -1 (Crap happened)
# BYDdata: (Empty list in case Returnvalue =-1)
# BYDdata: (Full list of key-value pairs in case Returnvalue = 0)
#
# Tested with
# BYD HV 6.4,
# Firmware verison V3.012 R
# python 3.6
#
# Changes:
# 2020.07.30 added fhem connectivity
# added sys for argument parsing; BYDboxIP and FHEM Web Interface are read from commandline
# password and username is read from file
import fhem
import sys
BYDboxIP = sys.argv[1]
web = sys.argv[2]
import requests
import time
import json
from pprint import pprint
from bs4 import BeautifulSoup
from requests.auth import HTTPBasicAuth
import logging
#from loggerdate import loggerdate
def getvaluefromsubstring (inputvalue):
# The input we pass looks like this:
#<input readonly="readonly" type="text" value="5186"/>
#Goal is to return value 5186 as outputvalue
#Not sure if BeautifulSoup could do more / better...
output_array= str(inputvalue)
A,B,C,outputvalue= output_array.split('=')
outputvalue, A= outputvalue.split('/')
A,outputvalue,B = outputvalue.split('"')
if ('%' in outputvalue):
outputvalue, A = outputvalue.split('%')
outputvalue = float(outputvalue)
return outputvalue
def readbyd():
#Please adjust the parameters below as appropriate for your environment:
# username and password are read from pwd_byd.json file ; BYDboxIP is parsed from sys commandline
#print ("Start reading Password file for BYD access....")
try:
with open('/opt/fhem/python/pwd_byd.json', 'r') as f:
credentials=json.load(f)
except Exception as e:
print('Something went wrong: {}'.format(e))
#No configurable parameters beyond this point
mySession = requests.Session()
try:
BYDdata={}
#The two stupid lines below took me almost a day...
#You can access other pages on the BYD WEB-Page - calling other asp scripts
url='http://'+BYDboxIP+'/asp/RunData.asp'
r4 = mySession.get(url, auth=HTTPBasicAuth(credentials["username"],credentials["password"]))
if (r4.status_code == 200):
page = r4.text
soup = BeautifulSoup(page, 'html.parser')
soup_ele = soup.body.form.table #This gets us the RunData Table and Array Num 1 values - using this approach
soup_reduced = soup_ele.find_all('input',attrs={"type": "text"} ) #Using this option now since BYD introduced some glitches with Firmware 3.012R on their html code..
# It now contains a list of the following kind
#<input readonly="readonly" type="text" value="19.300"/>
#print (soup_reduced)
#print ("ERSTES ELE", soup_reduced[1])
Keyvalue=[]
for elems in soup_reduced:
elems=str(elems)
myarray= elems.split('=')
CurValue=myarray[3].split('"')
Keyvalue.append(CurValue[1])
BYDdata["arrayvoltage"]= float(Keyvalue[0])
BYDdata["packvoltage"] = float(Keyvalue[1])
BYDdata["current"] = float(Keyvalue[2])
BYDdata["soc"] = (Keyvalue[3])
BYDdata["sysTemp"] = Keyvalue[4]
BYDdata["maxcellvol"] = float(Keyvalue[5])
BYDdata["mincellvol"] = float(Keyvalue[6])
BYDdata["maxcelltemp"] = float(Keyvalue[7])
BYDdata["mincelltemp"] = float(Keyvalue[8])
BYDdata["maxvolpos"] = int(Keyvalue[9])
BYDdata["minvolpos"] = Keyvalue[10]
BYDdata["maxtemppos"] = Keyvalue[11]
BYDdata["mintemppos"] = Keyvalue[12]
BYDdata["power"] = Keyvalue[13]
"""
# Oh well - with Firmware 3.012R, these values have disappeared from the BYD web pages...
BYDdata["soc"] = Keyvalue[3]
BYDdata["socwh"] = Keyvalue[4]
BYDdata["socah"] = Keyvalue[5]
BYDdata["soh"] = Keyvalue[6]
"""
Error_Connecting = 0
else:
print ("Not sure if we are able to connect with the given username & password - error code is:", r4.status_code)
print ("Header information passed is :", r4.headers)
Error_Connecting = r4.status_code
url='http://'+BYDboxIP+'/asp/StatisticInformation.asp'
r2 = mySession.get(url, auth=HTTPBasicAuth(credentials["username"],credentials["password"]))
if ((r2.status_code == 200) and (Error_Connecting == 0)):
page = r2.text
soup = BeautifulSoup(page, 'html.parser')
soup_ele = soup.body.table
#print (soup_ele)
#print ("--------------------------------------------------------")
#<input readonly="readonly" type="text" value="5186"/>
soup_reduced = soup_ele.find_all('input')
#print (soup_reduced)
#print ("soup_ele", soup_ele)
i = 0
a= len(soup_ele.contents)
total_charge_energy = soup_ele.find("td", text="Total Charge Energy:").find_next_sibling("td").text
total_charge_energy,b = (total_charge_energy.split())
total_charge_energy = float(total_charge_energy)
#print ("Total Charge Energy :", total_charge_energy)
BYDdata["Total_Charge_Energy"]= total_charge_energy
total_discharge_energy = soup_ele.find("td", text="Total Discharge Energy:").find_next_sibling("td").text
total_discharge_energy,b = (total_discharge_energy.split())
total_discharge_energy = float(total_discharge_energy)
#print ("Total Discharge Energy :", total_discharge_energy)
BYDdata["Total_Discharge_Energy"] = total_discharge_energy
total_cycle_counts = soup_ele.find("td", text="Total Cycle Counts:").find_next_sibling("td").text
#total_cycle_counts,b = (total_cycle_counts.split())
total_cycle_counts = float(total_cycle_counts)
#print ("Total Cycle Counts :", total_cycle_counts)
BYDdata["Total_Cycle_Counts"] = total_cycle_counts
else:
print ("Not sure if we are able to connect with the given username & password - error code is:", r4.status_code)
print ("Header information passed is :", r2.headers)
Error_Connecting = r2.status_code
except Exception as Connecterror:
print ("Error connecting to the BYD box :", Connecterror)
#logging.error("%s %s %s", loggerdate(), ",readbyd: Ran into exception querying the BYD Box : ", Connecterror)
Error_Connecting = Connecterror
if (Error_Connecting == 0):
if (len(BYDdata) >1): # We have something in the list
Returnvalue =0
else: # We ran into trouble and allocate an empty list
BYDdata={}
print ("Issue getting data from BYD ", Error_Connecting)
return (Returnvalue, BYDdata)
if __name__ == "__main__":
#print ("Start querying BYD....")
try:
Myreturnvalue, Mydata = readbyd();
if (Myreturnvalue == 0):
#print ("Returnvalue -should be zero if successful : ", Myreturnvalue)
#print ("----------------Start Values BYD ----------------")
#pprint (Mydata)
#print ("----------------End - Values from BYD ----------------")
#print ("Specific values from array....")
#print ("BYD Total Charge Energy :", Mydata['Total_Charge_Energy'], "Kwh")
#print ("BYD Total Discharge Energy :", Mydata['Total_Discharge_Energy'], "Kwh")
#print ("Calculations...")
#print ("Charging (+) / Discharging (-) Energy :", round(Mydata['packvoltage']*Mydata['current'],0), "W")
#print ("Efficiency is :",round(Mydata['Total_Discharge_Energy']/Mydata['Total_Charge_Energy'],3))
#print (Mydata)
message = json.dumps(Mydata)
#print ("Start reading Password file for FHEM access....")
try:
with open('/opt/fhem/python/pwd_fhem.json', 'r') as f:
credentials=json.load(f)
except Exception as e:
print('Something went wrong: {}'.format(e))
#print ("Start login to FHEM ....")
fh = fhem.Fhem(web, protocol="http", port=8083, username=credentials["username"], password=credentials["password"])
#print ("Start transfer of data to FHEM ....")
fh.send_cmd("setreading BYD_Status output " + message)
else:
print ("Error unable to query BYD Box")
except Exception as ex:
print ("Issues querying BYD Box :", ex)
Userreadings
Die Userreadings gehören zum Gerät BYD_Status .
output
Trigger: externes Python Skript Das Python Skript beschreibt im Gerät BYD_Status das reading output, das dort dann weiter verarbeitet wird.
Passworte
Das Passwort für den BYD Speicher liegt in einer JSON Datei
fhem@raspberrypi:~/python$ cat pwd_byd.json
{ "username": "installer", "password": "<Steht in der BYD Dokumentation>" }
RAW Definition BYD_Status
defmod BYD_Status expandJSON BYD_Status:output:.\{.*}
attr BYD_Status DbLogExclude .*
attr BYD_Status alias BYD_Status
attr BYD_Status comment Das Device wird über ein Python Skript im reading output befüllt.\
deletereading BYD_Status [min|max|cur|array|Total|pack|power|soc|sys].*\
attr BYD_Status group PV Eigenverbrauch
attr BYD_Status icon measure_battery_50
attr BYD_Status room Strom->Photovoltaik,Strom->System
attr BYD_Status sortby 02
Timeing für die PV extra Funktionen
RAW Definition PV_Schedule (DOIF)
defmod PV_Schedule DOIF ################################################################################################################\
## 1 BYD Status aktualisieren\
##\
([+:05] and !([:00] or [:30]))\
(\
{system("/usr/bin/python3 /opt/fhem/python/bin/byd_status.py ".ReadingsVal("BYD_Status","IP-Address_BYD","?")." ".ReadingsVal("BYD_Status","IP-Address_FHEM","?")." &")}\
)\
################################################################################################################\
## 2 Plenticore Status aktualisieren. Dies geschieht über das PV_Anlage_1_API Device\
##\
DOELSEIF\
([:57])\
(\
get PV_Anlage_1_API 20_/processdata/scb_statistic_EnergyFlow\
set Dum.Energy update\
\
)\
################################################################################################################\
## 3 DWD Prognose aktualisieren, die Daten stehen immer verzögert zur Verfügung.\
##\
DOELSEIF\
([06:37] or [11:37] or [17:37] or [00:37])\
(\
{system("/usr/bin/python3 /opt/fhem/python/bin/dwd_get_forecast.py ".ReadingsVal("PV_Anlage_1","IP-Address_FHEM","?")." ".ReadingsVal("PV_Anlage_1","Solar_Station","?")." &")}\
)\
\
################################################################################################################\
## 4 PV Prognose vom aktuellen Tag aktualisieren\
## zwischen 7 und 19 Uhr zur vollen Stunde\
DOELSEIF\
([07:00-19:00] and [:00])\
(\
{Solar_forecast("LogDB","LogDBRep_delete_PV_Forecast","PV_Anlage_1","Solar_Calculation_fc","DWD_Prognose","ProPlanta",0)}\
)\
################################################################################################################\
## 5 PV Prognose für den nächsten Tag aktualisieren\
## \
DOELSEIF\
([09:11])\
(\
{Solar_forecast("LogDB","LogDBRep_delete_PV_Forecast","PV_Anlage_1","Solar_Calculation_fc","DWD_Prognose","ProPlanta",1)}\
)\
attr PV_Schedule DbLogExclude .*
attr PV_Schedule alias PV_Schedule
attr PV_Schedule cmdState BYD Status|Plenticore Status
attr PV_Schedule do always
attr PV_Schedule room Strom->System
attr PV_Schedule sortby 11
attr PV_Schedule wait 0:0:0:10:0:0
Energie Bilanz
Die Energie Bilanz soll einen kompakten Überblick über die Produktions und Verbrauchswerte Liefern. Hierbei werden die Momentanwerte direkt berechnet, die restlichen Werte werden als Statistiken aus dem Gerät abgefragt. Die Aktualisierung erfolgt zyklisch über das Gerät PV_Schedule.
RAW Definition Energiebilanz
defmod Dum.Energy dummy
attr Dum.Energy DbLogExclude .*
attr Dum.Energy DbLogInclude Autarky.*,GridFeed.*,PV.*,.*Consumption.*
attr Dum.Energy alias Energiebilanz
attr Dum.Energy comment TotalConsumption,AutarkyQuoteDay,SelfConsumptionQuoteDay,AutarkyQuoteMonth,SelfConsumptionQuoteMonth
attr Dum.Energy event-on-change-reading PV,GridConsumption,GridFeedIn,SelfConsumptionQuote,Autarky.*,GridFeed.*,PV.*,.*Consumption.*
attr Dum.Energy event-on-update-reading TotalConsumption,AutarkyQuoteDay,SelfConsumptionQuoteDay,AutarkyQuoteMonth,SelfConsumptionQuoteMonth
attr Dum.Energy group Energiebilanz
attr Dum.Energy icon measure_power_meter
attr Dum.Energy room Strom->Energie
attr Dum.Energy stateFormat {\
my $pvt = ReadingsVal("$name","PVTotal", "")." W";;\
my $pvtd = ReadingsVal("$name","PVTotalDay", "")." kWh";;\
my $pvtm = ReadingsVal("$name","PVTotalMonth", "")." kWh";;\
my $pvty = ReadingsVal("$name","PVTotalYear", "")." kWh";;\
\
my $pv = ReadingsVal("$name","PV", "")." W";;\
my $pvd = ReadingsVal("$name","PVDay", "")." kWh";;\
my $pvm = ReadingsVal("$name","PVMonth", "")." kWh";;\
my $pvy = ReadingsVal("$name","PVYear", "")." kWh";;\
\
my $gfi = ReadingsVal("$name","GridFeedIn", "")." W";;\
my $gfid = ReadingsVal("$name","GridFeedInDay", "")." kWh";;\
my $gfim = ReadingsVal("$name","GridFeedInMonth", "")." kWh";;\
my $gfiy = ReadingsVal("$name","GridFeedInYear", "")." kWh";;\
my $eb = ReadingsVal("$name","GridConsumption", "")." W";;\
my $ebd = ReadingsVal("$name","GridConsumptionDay", "")." kWh";;\
my $ebm = ReadingsVal("$name","GridConsumptionMonth", "")." kWh";;\
my $eby = ReadingsVal("$name","GridConsumptionYear", "")." kWh";;\
my $et = ReadingsVal("$name","TotalConsumption", "")." W";;\
my $etd = ReadingsVal("$name","TotalConsumptionDay", "")." kWh";;\
my $etm = ReadingsVal("$name","TotalConsumptionMonth", "")." kWh";;\
my $ety = ReadingsVal("$name","TotalConsumptionYear", "")." kWh";;\
my $aq = ReadingsVal("$name","AutarkyQuote", "")." %";;\
my $aqd = ReadingsVal("$name","AutarkyQuoteDay", "")." %";;\
my $aqm = ReadingsVal("$name","AutarkyQuoteMonth", "")." %";;\
my $aqy = ReadingsVal("$name","AutarkyQuoteYear", "")." %";;\
my $sq = ReadingsVal("$name","SelfConsumptionQuote", "")." %";;\
my $sqd = ReadingsVal("$name","SelfConsumptionQuoteDay", "")." %";;\
my $sqm = ReadingsVal("$name","SelfConsumptionQuoteMonth", "")." %";;\
my $sqy = ReadingsVal("$name","SelfConsumptionQuoteYear", "")." %";;\
my $md = ReadingsTimestamp("$name", "AutarkyQuote", "");;\
my $cd = ReadingsTimestamp("PV_Anlage_1_API", "Statistic_Autarky_Day", "");;\
my $cm = ReadingsTimestamp("PV_Anlage_1_API", "Statistic_Autarky_Month", "");;\
my $cy = ReadingsTimestamp("PV_Anlage_1_API", "Statistic_Autarky_Year", "");;\
"<html><table border=2 bordercolor='darkgreen' cellspacing=0>\
<tr><td style='padding-right:5px;;padding-left:5px;;font-weight:bold'> </td><td style='padding-right:5px;;padding-left:5px;;font-weight:bold'>aktueller Wert</td><td style='padding-right:5px;;padding-left:5px;;font-weight:bold'>Heute</td><td style='padding-right:5px;;padding-left:5px;;font-weight:bold'>dieser Monat</td><td style='padding-right:5px;;padding-left:5px;;font-weight:bold'>dieses Jahr</td></tr>\
<tr><td style='padding-right:5px;;padding-left:5px;;text-align:left;;font-weight:bold'>PV-Erzeugung-Total</td><td style='padding-right:5px;;padding-left:5px'>".$pvt."</td><td style='padding-right:5px;;padding-left:5px'>".$pvtd."</td><td style='padding-right:5px;;padding-left:5px'>".$pvtm."</td><td style='padding-right:5px;;padding-left:5px'>".$pvty."</td></tr>\
<tr><td style='padding-right:5px;;padding-left:5px;;text-align:left;;font-weight:bold'>PV-Einspeisung</td><td style='padding-right:5px;;padding-left:5px'>".$gfi."</td><td style='padding-right:5px;;padding-left:5px'>".$gfid."</td><td style='padding-right:5px;;padding-left:5px'>".$gfim."</td><td style='padding-right:5px;;padding-left:5px'>".$gfiy."</td></tr>\
<tr><td style='padding-right:5px;;padding-left:5px;;text-align:left;;font-weight:bold'>Netz-Bezug</td><td style='padding-right:5px;;padding-left:5px'>".$eb."</td><td style='padding-right:5px;;padding-left:5px'>".$ebd."</td><td style='padding-right:5px;;padding-left:5px'>".$ebm."</td><td style='padding-right:5px;;padding-left:5px'>".$eby."</td></tr>\
<tr><td style='padding-right:5px;;padding-left:5px;;text-align:left;;font-weight:bold'>PV-Bezug</td><td style='padding-right:5px;;padding-left:5px'>".$pv."</td><td style='padding-right:5px;;padding-left:5px'>".$pvd."</td><td style='padding-right:5px;;padding-left:5px'>".$pvm."</td><td style='padding-right:5px;;padding-left:5px'>".$pvy."</td></tr>\
<tr><td style='padding-right:5px;;padding-left:5px;;text-align:left;;font-weight:bold'>Energieverbrauch</td><td style='padding-right:5px;;padding-left:5px'>".$et."</td><td style='padding-right:5px;;padding-left:5px'>".$etd."</td><td style='padding-right:5px;;padding-left:5px'>".$etm."</td><td style='padding-right:5px;;padding-left:5px'>".$ety."</td></tr>\
<tr><td style='padding-right:5px;;padding-left:5px;;text-align:left;;font-weight:bold'>Autarkiequote</td><td style='padding-right:5px;;padding-left:5px'>".$aq."</td><td style='padding-right:5px;;padding-left:5px'>".$aqd."</td><td style='padding-right:5px;;padding-left:5px'>".$aqm."</td><td style='padding-right:5px;;padding-left:5px'>".$aqy."</td></tr>\
<tr><td style='padding-right:5px;;padding-left:5px;;text-align:left;;font-weight:bold'>Eigenverbrauchsquote</td><td style='padding-right:5px;;padding-left:5px'>".$sq."</td><td style='padding-right:5px;;padding-left:5px'>".$sqd."</td><td style='padding-right:5px;;padding-left:5px'>".$sqm."</td><td style='padding-right:5px;;padding-left:5px'>".$sqy."</td></tr>\
<tr><td style='padding-right:5px;;padding-left:5px;;text-align:left;;font-weight:bold'>Berechnung am</td><td style='padding-right:5px;;padding-left:5px'>".$md."</td><td style='padding-right:5px;;padding-left:5px'>".$cd."</td><td style='padding-right:5px;;padding-left:5px'>".$cm."</td><td style='padding-right:5px;;padding-left:5px'>".$cy."</td></tr>\
</table></html>"\
}
attr Dum.Energy userReadings PVTotal {round(ReadingsVal("PV_Anlage_1","Total_AC_active_power",""),0)},\
PVTotalDay {round( ReadingsVal("PV_Anlage_1_API","Statistic_Yield_Day", "")/1000 ,2)},\
PVTotalMonth {round( ReadingsVal("PV_Anlage_1_API","Statistic_Yield_Month", "")/1000 ,2)},\
PVTotalYear {round( ReadingsVal("PV_Anlage_1_API","Statistic_Yield_Year", "")/1000 ,2)},\
\
PV {round( ReadingsVal("PV_Anlage_1","Home_own_consumption_from_battery", "")+ReadingsVal("PV_Anlage_1","Home_own_consumption_from_PV", "") ,0)},\
PVDay {round( (ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomeBat_Day", "")+ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomePv_Day", ""))/1000 ,2)},\
PVMonth {round( (ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomeBat_Month", "")+ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomePv_Month", ""))/1000 ,2)},\
PVYear {round( (ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomeBat_Year", "")+ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomePv_Year", ""))/1000 ,2)},\
\
GridFeedIn { ReadingsVal("PV_Anlage_1","Total_active_power_(powermeter)",0)<=0 ? abs(round(ReadingsVal("PV_Anlage_1","Total_active_power_(powermeter)",0),0)) : 0 },\
GridFeedInDay {round((ReadingsVal("PV_Anlage_1_API","Statistic_Yield_Day", "")-ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomeBat_Day", "")-ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomePv_Day", ""))/1000,2)},\
GridFeedInMonth {round((ReadingsVal("PV_Anlage_1_API","Statistic_Yield_Month", "")-ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomeBat_Month", "")-ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomePv_Month", ""))/1000,2)},\
GridFeedInYear {round((ReadingsVal("PV_Anlage_1_API","Statistic_Yield_Year", "")-ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomeBat_Year", "")-ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomePv_Year", ""))/1000,2)},\
\
GridConsumption { ReadingsVal("PV_Anlage_1","Total_active_power_(powermeter)",0)>=0 ? round(ReadingsVal("PV_Anlage_1","Total_active_power_(powermeter)",0),0) : 0 },\
GridConsumptionDay {round(abs(ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomeGrid_Day",""))/1000 ,2)},\
GridConsumptionMonth {round(ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomeGrid_Month","")/1000 ,2)},\
GridConsumptionYear {round(ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomeGrid_Year","")/1000 ,2)},\
\
TotalConsumption {round((ReadingsVal("PV_Anlage_1","Home_own_consumption_from_PV","")+ReadingsVal("PV_Anlage_1","Home_own_consumption_from_battery","")+ReadingsVal("PV_Anlage_1","Home_own_consumption_from_grid","")),0)},\
\
TotalConsumptionDay {round( (ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomePv_Day","")+ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomeBat_Day","")+ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomeGrid_Day","") )/1000 ,2)},\
TotalConsumptionMonth {round( (ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomePv_Month","")+ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomeBat_Month","")+ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomeGrid_Month","") )/1000 ,2)},\
TotalConsumptionYear {round( (ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomePv_Year","")+ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomeBat_Year","")+ReadingsVal("PV_Anlage_1_API","Statistic_EnergyHomeGrid_Year","") )/1000,2)},\
\
AutarkyQuote {my $valA = ReadingsVal("PV_Anlage_1", "Total_AC_active_power","")-ReadingsVal("PV_Anlage_1", "Home_own_consumption_from_grid","");;;; my $calcVal = ($valA > 0)?round($valA /($valA + ReadingsVal("PV_Anlage_1", "Home_own_consumption_from_grid",""))*100 ,0) : 0 ;;;; ($calcVal > 100)?100:$calcVal },\
AutarkyQuoteDay {round(ReadingsVal("PV_Anlage_1_API","Statistic_Autarky_Day", ""),0)},\
AutarkyQuoteMonth {round(ReadingsVal("PV_Anlage_1_API","Statistic_Autarky_Month", ""),0)},\
AutarkyQuoteYear {round(ReadingsVal("PV_Anlage_1_API","Statistic_Autarky_Year", ""),0)},\
\
SelfConsumptionQuote {my $valS = ReadingsVal("PV_Anlage_1","Total_AC_active_power", 0) ;;;; my $calcVal = ($valS > 0)?round((ReadingsVal("PV_Anlage_1","Home_own_consumption_from_PV", "0") + ReadingsVal("PV_Anlage_1","Home_own_consumption_from_battery","0")) / $valS * 100 ,0) : 0 ;;;; ($calcVal > 100)?100:$calcVal},\
SelfConsumptionQuoteDay {round(ReadingsVal("PV_Anlage_1_API","Statistic_OwnConsumptionRate_Day", ""),0)},\
SelfConsumptionQuoteMonth {round(ReadingsVal("PV_Anlage_1_API","Statistic_OwnConsumptionRate_Month", ""),0)},\
SelfConsumptionQuoteYear {round(ReadingsVal("PV_Anlage_1_API","Statistic_OwnConsumptionRate_Year", ""),0)}
Erstellen von zusätzlichen Werten in der Datenbank
Hier werden Werte konsolidiert, weil z.B. der Wert PV_total_Month stetig steigt. Am Ende des Monats sind die gesamten Zwischenwerte ohne Aussagekraft und werden dann später mal gelöscht.
RAW Definition LogDBRep_PV_total_diff_Week
defmod LogDBRep_PV_total_diff_Week DbRep LogDB
attr LogDBRep_PV_total_diff_Week DbLogExclude .*
attr LogDBRep_PV_total_diff_Week aggregation week
attr LogDBRep_PV_total_diff_Week allowDeletion 0
attr LogDBRep_PV_total_diff_Week device Dum.Energy
attr LogDBRep_PV_total_diff_Week reading PVTotalMonth
attr LogDBRep_PV_total_diff_Week room Strom->Energie,System
attr LogDBRep_PV_total_diff_Week timestamp_begin previous_month_begin
attr LogDBRep_PV_total_diff_Week timestamp_end previous_week_end
RAW Definition LogDBRep_PV_total_max_Month
defmod LogDBRep_PV_total_max_Month DbRep LogDB
attr LogDBRep_PV_total_max_Month DbLogExclude .*
attr LogDBRep_PV_total_max_Month aggregation month
attr LogDBRep_PV_total_max_Month allowDeletion 0
attr LogDBRep_PV_total_max_Month device Dum.Energy
attr LogDBRep_PV_total_max_Month reading PVTotalMonth
attr LogDBRep_PV_total_max_Month room Strom->Energie,System
attr LogDBRep_PV_total_max_Month timestamp_begin previous_month_begin
attr LogDBRep_PV_total_max_Month timestamp_end previous_day_end
RAW Definition LogDBRep_PV_used_diff_Week
defmod LogDBRep_PV_used_diff_Week DbRep LogDB
attr LogDBRep_PV_used_diff_Week DbLogExclude .*
attr LogDBRep_PV_used_diff_Week aggregation week
attr LogDBRep_PV_used_diff_Week allowDeletion 0
attr LogDBRep_PV_used_diff_Week device Dum.Energy
attr LogDBRep_PV_used_diff_Week reading PVMonth
attr LogDBRep_PV_used_diff_Week room Strom->Energie,System
attr LogDBRep_PV_used_diff_Week timestamp_begin previous_month_begin
attr LogDBRep_PV_used_diff_Week timestamp_end previous_week_end
RAW Definition LogDBRep_PV_used_max_Month
defmod LogDBRep_PV_used_max_Month DbRep LogDB
attr LogDBRep_PV_used_max_Month DbLogExclude .*
attr LogDBRep_PV_used_max_Month aggregation month
attr LogDBRep_PV_used_max_Month allowDeletion 0
attr LogDBRep_PV_used_max_Month device Dum.Energy
attr LogDBRep_PV_used_max_Month reading PVMonth
attr LogDBRep_PV_used_max_Month room Strom->Energie,System
attr LogDBRep_PV_used_max_Month timestamp_begin previous_month_begin
attr LogDBRep_PV_used_max_Month timestamp_end previous_month_end
Timing für die Datenbank Einträge
defmod DF_DB_Service DOIF ## Monatlich Einträge\
([01:13] and ($mday==1))(\
set LogDBRep_PV_used_max_Month maxValue writeToDB;;\
set LogDBRep_PV_total_max_Month maxValue writeToDB\
)\
## Wöchentliche Einträge\
DOELSEIF\
([01:17] and ($wday==1))(\
set LogDBRep_PV_used_diff_Week diffValue writeToDB;;\
set LogDBRep_PV_total_diff_Week diffValue writeToDB\
)\
attr DF_DB_Service DbLogExclude .*
attr DF_DB_Service do always
attr DF_DB_Service room System
Löschen von nicht mehr benötigten Werten in der Datenbank
Hier wird endgültig aufgeräumt, alte momentan Werte werden gelöscht, wenn sie nach z.B. drei Monaten keine Relevanz mehr haben. Dafür wurden im vorherigen Abschnitt zusätzliche Werte in der Datenbank erzeugt, die in Diagrammen trotzdem noch einen trend erkennen lassen. Wen eine immer größer werdende Datenbank mit steigenden Antwortzeiten nicht stört, der kann das Aufräumen auch weg lassen. Bei einer späteren Migration führt dies natürlich zu höherem Aufwand und hohen Laufzeiten.
Wetter-/Leistungs-Prognose
Dies ist ein Thema, dass nicht wirklich gut zu fassen ist und ist eher etwas für Enthusiasten :-), wer schon mal mit Sonne, Wolken und Regen gerechnet hat versteht was ich meine. Dieser Ansatz ist nicht wissenschaftlicher Art und hat auch keinen Anspruch mathematischer Perfektion. Nach reinem Gefühl und mit aus dem Fenster schauen kommt jedoch ein respektables Ergebnis dabei heraus. Viel Vergnügen und Spaß beim mitbasteln ;-)
Deutscher Wetter Dienst (DWD)
Der DWD liefert über Mosmix kostenlos, stunden aktuelle Prognosedaten. Über einen Link am Ende dieser Seite kommt man zum "Photovoltaikforum", wo dieses Thema erarbeitet wurde, weshalb ich es hier nicht wiederholen möchte. Über Mosmix werden für diese Anwendung die Werte Rad1h und TTT bezogen, was der globalen Sonneneinstrahlung und der Temperatur entspricht. Die Daten werden ins Gerät "DWD_Prognose" in das reading "output" als JSON String geschrieben und dort in readings ausgepackt. Der Trigger erfolgt über das DOIF PV_Schedule , die Abholzeiten muss man noch auf die eigenen Belange anpassen, damit man nicht abholt bevor die Daten vom DWD bereit stehen. Hier gibt es oftmals Verzögerungen von über einer Stunde, was man auf dem FTP Server am Zeitstempel sehen kann. Auch hier bitte nicht zu oft abholen, damit die Server nicht überlastet werden.
Daten abholen und auspacken
fhem@raspberrypi:~/python/bin$ cat dwd_get_forecast.py
#
# Copyright (C) 2020 Kilian Knoll kilian.knoll@gmx.de
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Purpose
#Extract weather forecast data from DWD Mosmix for a given Station ID
#
# Background information:
# DWD provides 10 day forecast weather - and radiation data at an hourly resolution for over 5000 Stations worldwide (focus is on Germany/Europe though...)
# Description of kml file:
#https://www.dwd.de/DE/leistungen/opendata/help/schluessel_datenformate/kml/mosmix_elemente_pdf.pdf?__blob=publicationFile&v=3
#
#List of available stations:
#https://www.dwd.de/DE/leistungen/met_verfahren_mosmix/mosmix_stationskatalog.cfg?view=nasPublication&nn=495490
#
# How to use this ?
# 1) Find the station close by your geographic location:
# Go to the website below, zoom to your location - and click on "Mosmix Stationen anzeigen"
# Once you found the closest station, please change the station number to the station number
# https://wettwarn.de/mosmix/mosmix.html
# In my case, I picked Station P755 (which is close to Munich)
# 2) Make changes in code below to reflect your station number - and the corresponding URL
# change
# self.mystation = P755
# below to the one you identified during step 1
# change the URL further down below to reflect the station:
# self.urlpath = 'https://opendata.dwd.de/weather/local_forecasts/mos/MOSMIX_L/single_stations/P755/kml'
# Your one time setup is done...
#
# Implementation
# DWD provides two types of kml files
# single station kml files. These get updated approx every 6 hours
# all stations. These get updated hourly. However the file is pretty large. On embedded systems such as raspberry pi, I ran out of memory trying to parse XML files that size (exceeded 1GB of memory). Hence the decision to use the single station files
# we are only looking for a couple of key parameters that are relevant
#Currently the following Parameters get extracted from the kml file and put into a twodimensional array:
#mytimestamp : Timestamp of the forecast data
#Rad1h : Radiation Energy [kj/m²]
#TTT : Temperature 2 m above surface [°C]
#PPPP : Presssure Values (Surface Pressure reduced)
#FF : Wind speed [m/s]
#
#
# Update August 05 2020
# Kilian did a lot of perfect prework, but I did some cleanup on this code. Originally it was written for a different environment.
# The changes are related to the communication with FHEM and the code is triggered also by FHEM for a one time run each call.
# Unnessesary data is dropped, to reduce the amount of data transfered to FHEM
import urllib.request
import shutil
import zipfile
from bs4 import BeautifulSoup
import requests
import xml.etree.ElementTree as ET
import time
import datetime
import queue
import threading
import logging
import pprint
import fhem
import json
import sys
web = sys.argv[1]
station = sys.argv[2]
pp = pprint.PrettyPrinter(indent=4)
def connvertINTtimestamptoDWD(inputstring):
# Purpose: Convert a timestamp as presented by the UTC: 1545030000.0
# and return it to a UTC representation: 2018-12-17T08:00:00.000000Z
#mynewtime =time.mktime(datetime.datetime.strptime(inputstring, "%Y-%m-%dT%H:%M:%S.%fZ").timetuple())
#print ("neue Zeit ", mynewtime)
mysecondtime = (datetime.datetime.fromtimestamp(inputstring).strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3]) + "Z"
return (mysecondtime)
# Main class that holds the required information
class dwdforecast(threading.Thread):
def __init__ (self, myqueue):
print ("Starting dwdforecast init ...")
self.myqueue = myqueue
self.event = threading.Event()
self.ext = 'kmz'
self.mystation = station # This should be your closes station
# Only use the "all_stations" if you got decent hardware
#self.urlpath = 'http://opendata.dwd.de/weather/local_forecasts/mos/MOSMIX_S/all_stations/kml'
#On Raspberries & alikes, use the one for your specific station:
self.urlpath = 'https://opendata.dwd.de/weather/local_forecasts/mos/MOSMIX_L/single_stations/' + self.mystation +'/kml'
self.lasttimecheck = 1534800680.0 # Dec 14th 2018 (pure initialization)
self.sleeptime = 15 #Time interval we poll the server [seconds]- please increase time since updates from DWD are hourly at best
self.myinit = 0 #So we can populate the queue initially / subsequently
threading.Thread.__init__ (self)
print ("I am looking for data from DWD for the following station: ", self.mystation)
print ("I will be polling the following URL for the latest updates ", self.urlpath)
# Based on the user specified URL, find the latest file file with it´s timestamp
def GetURLForLatest(self,urlpath, ext=''):
try:
page = requests.get(urlpath).text
except Exception as ErrorGetWebdata:
logging.error("%s %s",",GetURLForLatest Error getting data from the internet:", ErrorGetWebdata)
soup = BeautifulSoup(page, 'html.parser')
soup_reduced= soup.find_all('pre')
soup_reduced = soup_reduced[0]
counter = 0
for elements in soup_reduced:
elements = str(elements)
if (counter >0):
words =elements.split()
mytime = words[0] +"-" + words[1]
logging.debug("%s %s" ,",GetURLForLatest :DWD Filetimestamp found :", mytime)
mynewtime =time.mktime(datetime.datetime.strptime(mytime, "%d-%b-%Y-%H:%M").timetuple())
logging.debug("%s %s" ,",GetURLForLatest :DWD Filetimestamp found :", mynewtime)
#print ("From function GetURLForLatest -mynewtime", 2*mynewtime)
if (elements.find("LATEST") >0):
#print ("My element", elements)
counter = 1
myurl = [urlpath + '/' + node.get('href') for node in soup.find_all('a') if node.get('href').endswith(ext)]
return (myurl, mynewtime)
def connvertDWDtimestamptoINT(self,inputstring):
# Purpose: Convert a timestamp as presented by the DWD: 2018-12-25T07:00:00.000Z
# and return it to a UTC representation
mynewtime =time.mktime(datetime.datetime.strptime(inputstring, "%Y-%m-%dT%H:%M:%S.%fZ").timetuple())
#print ("neue Zeit ", mynewtime)
#mysecondtime = datetime.datetime.fromtimestamp(mynewtime).strftime('%Y-%m-%dT%H:%M:%S.%fZ')
#print ("Einmal retour", mysecondtime)
mycurrentINTtimestamp =int(mynewtime)
return (mycurrentINTtimestamp)
def connvertDWDtimestamptoINT(self,inputstring):
# Purpose: Convert a timestamp as presented by the DWD: 2018-12-25T07:00:00.000Z
# and return it to a UTC representation
mynewtime =time.mktime(datetime.datetime.strptime(inputstring, "%Y-%m-%dT%H:%M:%S.%fZ").timetuple())
mycurrentINTtimestamp =int(mynewtime)
return (mycurrentINTtimestamp)
try:
def run(self):
if (self.myinit== 0): #We populate the first timestamp to signal to main that we are up & running
temptimestamp = time.time()
print ("From dwdforecast - initial queue population", temptimestamp)
self.myqueue.put(temptimestamp)
self.myinit = 1
time.sleep(1)
try:
self.mydownloadfiles, self.mynewtime = self.GetURLForLatest(self.urlpath, self.ext)
#print ("Downloadfiles = ", self.mydownloadfiles)
#print ("Timestamp = ", self.mynewtime)
except Exception as ErrorReadFromDWD:
logging.error("%s %s" ,",dwdforecast :", ErrorReadFromDWD)
self.myarray =[]
for self.file in self.mydownloadfiles:
self.myarray.append(self.file)
self.temp_length = len(self.myarray)
self.url = self.myarray[self.temp_length-1]
logging.debug("%s %s %s",",dwdforecast : -BEFORE if- time comparison :", self.mynewtime, self.lasttimecheck)
# if (self.mynewtime > self.lasttimecheck):
logging.debug("%s %s %s" ,",dwdforecast : -in if- time comparison :", self.mynewtime, self.lasttimecheck)
#print ("DWD Weather - we have found a new kml file that we will download - timestamp was :", self.mynewtime)
#print ("DWD Weather - self.lasttimecheck was ", self.lasttimecheck)
self.lasttimecheck = self.mynewtime
self.file_name = "dwd_temp1.gz"
self.out_file = "dwd_temp2.gz"
self.targetdir ="./"
try:
time.sleep(10) #Assumption is - we see the file on the DWD server - but it has not yet been copied over
# Download the file from `url` and save it locally under `self.file_name`:
with urllib.request.urlopen(self.url) as self.response, open(self.file_name, 'wb') as self.out_file:
shutil.copyfileobj(self.response, self.out_file)
time.sleep(5) #not sure if this gets rid of the access problems
with zipfile.ZipFile(self.file_name,"r") as zip_ref:
Myzipfilename = (zip_ref.namelist())
Myzipfilename = str(Myzipfilename[0])
zip_ref.extractall(self.targetdir)
logging.debug("%s %s" ,",dwdforecast : -File that I extract is zipfile :", Myzipfilename)
time.sleep(5) #not sure if this gets rid of the access problems
except Exception as MyException:
logging.error("%s %s", ",subroutine dwdforecast exception getting the data from server : ", MyException)
self.tree = ET.parse(Myzipfilename)
self.root = self.tree.getroot()
self.root.tag
"""
<kml:kml xmlns:dwd="https://opendata.dwd.de/weather/lib/pointforecast_dwd_extension_V1_0.xsd" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:xal="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
<kml:kml xmlns:dwd="https://opendata.dwd.de/weather/lib/pointforecast_dwd_extension_V1_0.xsd" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:xal="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
"""
#--------------------------------------------------
#Namespace definition for kml file:
#
self.ns = {'dwd': 'https://opendata.dwd.de/weather/lib/pointforecast_dwd_extension_V1_0.xsd', 'gx': 'http://www.google.com/kml/ext/2.2',
'kml': 'http://www.opengis.net/kml/2.2', 'atom': 'http://www.w3.org/2005/Atom', 'xal':'urn:oasis:names:tc:ciq:xsdschema:xAL:2.0'}
#--------------------------------------------------
# We get the timestamps
#
self.timestamps = self.root.findall('kml:Document/kml:ExtendedData/dwd:ProductDefinition/dwd:ForecastTimeSteps/dwd:TimeStep',self.ns)
self.i = 0
self.timevalue=[]
for self.child in self.timestamps:
#print ("TIMESTAMPS", child.text)
self.timevalue.append(self.child.text)
"""
for j in timevalue:
print ("Zeit",i, " ", timevalue[i])
i = i+1
"""
for self.elem in self.tree.findall('./kml:Document/kml:Placemark',self.ns): #Position us at the Placemark
#print ("SUCERJH ", sucher)
#print ("Elemente ", elem.tag, elem.attrib, elem.text)
self.mylocation = self.elem.find('kml:name',self.ns).text #Look for the station Number
# Hier IF Frage einbauen
if (self.mylocation == self.mystation):
#print ("meine location", self.mylocation)
self.myforecastdata = self.elem.find('kml:ExtendedData',self.ns)
for self.elem in self.myforecastdata:
#We may get the following strings and are only interested in the right hand quoted property name WPcd1:
#{'{https://opendata.dwd.de/weather/lib/pointforecast_dwd_extension_V1_0.xsd}elementName': 'WPcd1'}
self.trash = str(self.elem.attrib)
self.trash1,self.mosmix_element = self.trash.split("': '")
self.mosmix_element, self.trash = self.mosmix_element.split("'}")
#-------------------------------------------------------------
# Currently looking at the following key Data:
# Looking for the following mosmix_elements
#FF : Wind Speed [m/s]
#Rad1h : Global irridance [kJ/m²]
#TTT : Temperature 2m above ground [Kelvin]
#PPPP : Pressure reduced [Pa]
#-------------------------------------------------------------
if ('FF' == self.mosmix_element):
self.FF_temp = self.elem[0].text
self.FF = list (self.FF_temp.split())
if ('Rad1h' == self.mosmix_element):
self.Rad1h_temp = self.elem[0].text
self.Rad1h = list (self.Rad1h_temp.split())
if ('TTT' == self.mosmix_element):
self.TTT_temp = self.elem[0].text
self.TTT = list(self.TTT_temp.split())
counter = 0
# We convert from Kelvin to Celcius...:
for i in self.TTT:
self.TTT[counter]=round((float(self.TTT[counter])-273.13),2)
#print (self.TTT[counter])
counter = counter +1
if ('PPPP' == self.mosmix_element):
self.PPPP_temp = self.elem[0].text
self.PPPP = list (self.PPPP_temp.split())
#------------------------------------
# Define empty array
self.mosmixdata =[]
for self.j in range(6): #Right now we have timevalue, mytimestamp, self.FF Rad1h TTT PPPP
self.column = []
self.counter = 0
for self.i in self.timevalue:
self.column.append(0)
self.mosmixdata.append(self.column)
#------------------------------------
#Populate values
counter = 0
for self.i in self.timevalue:
self.mytimestamp = self.connvertDWDtimestamptoINT(self.timevalue[counter])
self.mosmixdata[0][counter]=self.timevalue[counter]
self.mosmixdata[1][counter]=self.mytimestamp
self.mosmixdata[2][counter]=self.Rad1h[counter]
self.mosmixdata[3][counter]=self.TTT[counter]
self.mosmixdata[4][counter]=self.PPPP[counter]
self.mosmixdata[5][counter]=self.FF[counter]
counter = counter + 1
#------------------------------------------
self.cols = len(self.mosmixdata)
rows = 0
if self.cols:
self.rows = len(self.mosmixdata[0])
self.MosmixFileFirsttimestamp = self.mosmixdata[1][0]
#print ("My first stamp from the file is:",self.mosmixdata[0][0],"Endstring",self.mosmixdata[1][0] )
#print ("-------------------------------------------------")
#print (self.mosmixdata)
self.indexcounter_addrows=1
self.MyWeathervalues = {}
try:
print ("Here is what we got from DWD :")
for j in range(self.rows):
if (self.indexcounter_addrows >0): #We are adding from the point onward - see self.indexcounter_addrows if check below
#print ("counting indices", self.indexcounter_addrows)
#self.MyWeathervalues.update({'mydatetime':self.mosmixdata[0][j]})
#self.MyWeathervalues.update({'mytimestamp':self.mosmixdata[1][j]})
if (self.mosmixdata[2][j] > '0.00'): #Reduce unneccessary data
self.time = time.strftime("%Y-%m-%d_%H:%M", time.localtime(self.mosmixdata[1][j]))
self.MyWeathervalues.update({self.time + '_Rad1wh':int(round(float(self.mosmixdata[2][j]) * 0.277778 , 0))})
self.MyWeathervalues.update({self.time + '_TTT':self.mosmixdata[3][j]})
#self.MyWeathervalues.update({'TTT':self.mosmixdata[3][j]})
#self.MyWeathervalues.update({'PPPP':self.mosmixdata[4][j]})
#self.MyWeathervalues.update({'FF':self.mosmixdata[5][j]})
#print ('mydatetime',self.mosmixdata[0][j],'mytimestamp ',self.mosmixdata[1][j],'Rad1h ',self.mosmixdata[2][j],'TTT ',self.mosmixdata[3][j], 'PPPP',self.mosmixdata[4][j],'FF',self.mosmixdata[5][j])
import re
self.message = re.sub("'", "\"", str(self.MyWeathervalues))
#print(self.MyWeathervalues)
#print(self.message)
try:
with open('/opt/fhem/python/pwd_fhem.json', 'r') as f:
credentials=json.load(f)
except Exception as e:
print('Something went wrong: {}'.format(e))
fh = fhem.Fhem(web, protocol="http", port=8083, username=credentials["username"], password=credentials["password"])
fh.send_cmd("setreading DWD_Prognose output " + self.message)
self.mytimestamp = connvertINTtimestamptoDWD(self.mynewtime)
logging.debug ("%s %s %s %s", ",Subroutine dwdforecast -we have used DWD file from time : ", self.mynewtime, " ", self.mytimestamp)
except Exception as ErrorDWDArray:
print ("Shit happened ?", ErrorDWDArray)
logging.error ("%s %s", ",subroutine dwdforecast final exception : ", ErrorDWDArray)
logging.debug("%s %s", "From dwdforecast - we have found a true commit and have updated the database at the following dwd time :", self.mynewtime)
time.sleep(self.sleeptime) # We are putting in a sleep
self.myqueue.put(self.mynewtime)
except Exception as ExceptionError:
print ("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
print ("XXX-Aus Subroutine dwdforecast -verrant ? ", ExceptionError)
print ("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
logging.error("%s %s", ",subroutine dwdforecast final exception : ", ExceptionError)
if __name__ == "__main__":
logging.basicConfig(filename="./log/dwd_debug.txt",level=logging.DEBUG)
#
"""
Interaction can be 'Simple' - or 'Complex'
Simple : Try to get weather data once only - then terminate
Complex : Start a seperate queue that continuously polls the DWD server on the internet to get updated data
"""
Interaction = 'Simple' #Interaction can be 'Simple' - or 'Complex'
#
#-----------------------------------------------------------------
# START Queue (To read dwd values and populate them to database):
try:
myQueue1 = queue.Queue()
myThread1= dwdforecast(myQueue1)
myThread1.start()
while myQueue1.empty():
print(" Waiting on DWD dwdforecastdata Queue results to tell it is started...")
logging.info("%s " ",Main :Waiting on Queue results to be populated ...")
time.sleep(1)
# Queue End (To read values from DWD)
#_________________________________________________________________
i = 0
try:
while i <1:
if not myQueue1.empty(): # Falls was in der Queue steht machen wir was
quelength = myQueue1.qsize() # Wenn da viele Werte angelaufen sind, nehmen wir jetzt einfach den Letzten
#print ("LAENGE der QUEUE -XXXXXXXXXXXXXXXXXXXXXXX : ", quelength)
logging.info("%s %s " ,",Main :Queue length is : ", quelength)
for x in range (0,quelength):
LastDWDtimestamp = myQueue1.get() # Das ist die magische Zeile in der wir den Wert aus der Queue abholen
mylasttimestamp = connvertINTtimestamptoDWD(LastDWDtimestamp)
print ("From Main : DWD File access I checked / got uploaded by DWD was at :", LastDWDtimestamp,mylasttimestamp )
i = i + 1
except Exception as OtherExceptionError:
print ("hit some other error.... !", OtherExceptionError)
except Exception as FinalExceptionError:
print ("I am clueless ... Hit some other error .... !", FinalExceptionError)
RAW Definition DWD_Prognose
defmod DWD_Prognose expandJSON DWD_Prognose:output:.\{.*}
attr DWD_Prognose DbLogExclude .*
attr DWD_Prognose alias DWD_Prognose
attr DWD_Prognose comment Das Device wird über ein Python Skript im reading output befüllt.\
deletereading DWD_Prognose .*_Rad1wh\
attr DWD_Prognose group PV Eigenverbrauch
attr DWD_Prognose icon measure_photovoltaic_inst
attr DWD_Prognose room Informationen->Wetter,Strom->Photovoltaik,Strom->System
attr DWD_Prognose sortby 02
ProPlanta
Die Webseite Proplanta stellt unter anderem chOfRain*, cloud* in einer drei Stunden Prognose bereit und dies dann für mehrere Tage fc0 , fc1, ...
RAW Definition ProPlanta
defmod ProPlanta PROPLANTA <Wohnort> de
attr ProPlanta DbLogExclude .*
attr ProPlanta DbLogInclude cloudBaseMax,cloudBaseMin,fc0_cloud06,fc0_cloud09,fc0_cloud12,fc0_cloud15,fc0_cloud18,fc0_rad,fc0_sun,fc0_uv,fc0_weather06,fc0_weather09,fc0_weather12,fc0_weather15,fc0_weather18,fc1_cloud06,fc1_cloud09,fc1_cloud12,fc1_cloud15,fc1_cloud18,fc1_rad,fc1_sun,fc1_uv,fc1_weather06,fc1_weather09,fc1_weather12,fc1_weather15,fc1_weather18,weather
attr ProPlanta INTERVAL 900
attr ProPlanta alias ProPlanta
attr ProPlanta event-on-change-reading cloudBaseMax,cloudBaseMin,fc0_cloud06,fc0_cloud09,fc0_cloud12,fc0_cloud15,fc0_cloud18,fc0_rad,fc0_sun,fc0_uv,fc0_weather06,fc0_weather09,fc0_weather12,fc0_weather15,fc0_weather18,fc1_cloud06,fc1_cloud09,fc1_cloud12,fc1_cloud15,fc1_cloud18,fc1_rad,fc1_sun,fc1_uv,fc1_weather06,fc1_weather09,fc1_weather12,fc1_weather15,fc1_weather18,weather
attr ProPlanta forecastDays 4
attr ProPlanta group ASC Environment
attr ProPlanta icon weather_sunrise
attr ProPlanta room Informationen->Wetter,Rollos
attr ProPlanta sortby 02
myUtils.pm Funktionen
Solar_forecast
Achtung, diese Funktion ist noch nicht vollständig ausprogrammiert. Es wurden bereits Übergabeparameter integriert, um z.B. andere Wetterdienste zu berücksichtigen. Um diese Funktion zu nutzen muss ein Dummy PV_Anlage_1_config vorhanden sein, in dem unter anderem die Modul und Anlagen Ausrichtung konfiguriert wird. Rückfragen gerne im Forum.
###########################################################
# Subroutine to calculate radiation
###########################################################
sub Solar_forecast($$$$$$$) {
my $logdb = $_[0] ;
my $logdbrep = $_[1] ; # Das wird zum Löschen in der LogDB verwendet und muss entsprechend konfiguriert sein.
my $logdevice = $_[2] ;
# Hier könnte man noch andere Wetterdienste berücksichtigen bzw den Device Namen ändern
my $wetter_1 = $_[4] ; if ($wetter_1 ne "DWD_Prognose") {return("$wetter_1 not supported")} ;
my $wetter_2 = $_[5] ; if ($wetter_2 ne "ProPlanta" ) {return("$wetter_2 not supported")} ;
my $fc = $_[6] ; # Wieviel Tage in die Zukunft soll es gehen? 0,1,2
my $reading = $_[3].$fc ;
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); $year += 1900; $mon += 1;
my ($Solar_Cloud,$Solar_Rain,$Solar_Temp,$Solar_SolarRadiation,$logentry,$i) = (0) x 6 ;
my ($cloudk,$raink,$tempk,$cloudk_base,$raink_base,$tempk_base) = (0) x 6 ;
my ($Solar_Correction_Cloud,$Solar_Correction_Rain,$Solar_Correction_Temp,$Solar_Plain) = (1) x 4 ;
my (@Solar_,@module_count) = (0,0,0) ;
# Initialisieren des Basis TIMESTAMP für den Forecast
my $timestring = time_str2num($year."-".$mon."-".$mday." 08:00:00") ;
my $timestamp = POSIX::strftime("%Y-%m-%d %H:00:00",localtime($timestring+$fc*24*60*60)) ;
# Bei Forecast ab dem nächsten Tag zuerst die bisherigen Einträge für den Tag löschen
# if ( $fc != 0 ) {
fhem "set ".$logdbrep." sqlCmd DELETE FROM history WHERE DEVICE='".$logdevice."' AND READING='".$reading."' AND TIMESTAMP>='".$timestamp."'" ;
# };
# Es werden Stundenwerte von 08:00 bis 19:00 Uhr berechnet
for ($i = 8; $i <= 19; $i++) {
$timestring = time_str2num($year."-".$mon."-".$mday." ".$i.":00:00") ;
$timestamp = POSIX::strftime("%Y-%m-%d %H:00:00",localtime($timestring+$fc*24*60*60)) ;
# Bei ProPlanta liegen die Werte im drei Stunden Abstand vor
if ( $wetter_2 eq "ProPlanta") {
if ( $i >= 6 and $i < 9 )
{ $Solar_Cloud = ReadingsVal($wetter_2,"fc".$fc."_cloud09" ,0) ;
$Solar_Rain = ReadingsVal($wetter_2,"fc".$fc."_chOfRain09",0) ; } ;
if ( $i >= 9 and $i < 12 )
{ $Solar_Cloud = ReadingsVal($wetter_2,"fc".$fc."_cloud09" ,0) ;
$Solar_Rain = ReadingsVal($wetter_2,"fc".$fc."_chOfRain09",0) ; } ;
if ( $i >= 12 and $i < 15 )
{ $Solar_Cloud = ReadingsVal($wetter_2,"fc".$fc."_cloud12" ,0) ;
$Solar_Rain = ReadingsVal($wetter_2,"fc".$fc."_chOfRain12",0) ; } ;
if ( $i >= 15 and $i < 18 )
{ $Solar_Cloud = ReadingsVal($wetter_2,"fc".$fc."_cloud15" ,0) ;
$Solar_Rain = ReadingsVal($wetter_2,"fc".$fc."_chOfRain15",0) ; } ;
};
if ( $wetter_1 eq "DWD_Prognose") {
$Solar_Temp = ReadingsVal($wetter_1,POSIX::strftime("%Y-%m-%d_%H_00_TTT", localtime($timestring+($fc*24-1)*60*60)),0)+10;
$Solar_SolarRadiation = ReadingsVal($wetter_1,POSIX::strftime("%Y-%m-%d_%H_00_Rad1wh",localtime($timestring+($fc*24-1)*60*60)),0) ;
};
$cloudk = ReadingsVal($logdevice."_config","forecast_cloudk",0) * -0.01 ;
if ($cloudk ne 0) {
$cloudk_base = ReadingsVal($logdevice."_config","forecast_cloudk_base",0) ;
$Solar_Correction_Cloud = round((1 + ($Solar_Cloud - $cloudk_base) * $cloudk / 100),3) ;
};
$raink = ReadingsVal($logdevice."_config","forecast_raink",0) * -0.01 ;
if ($raink ne 0) {
$raink_base = ReadingsVal($logdevice."_config","forecast_raink_base" ,0) ;
$Solar_Correction_Rain = round((1 + ($Solar_Rain - $raink_base ) * $raink / 100),3) ;
};
$tempk = ReadingsVal($logdevice."_config","forecast_tempk",0) * -0.01 ;
if ($tempk ne 0) {
$tempk_base = ReadingsVal($logdevice."_config","forecast_tempk_base" ,0) ;
$Solar_Correction_Temp = round((1 + ($Solar_Temp - $tempk_base ) * $tempk / 100),3) ;
};
my $Solar_Correction_Faktor = ReadingsVal($logdevice."_config","forecast_factor",1) ;
$logentry = 0 ;
for(my $j=1;$j<4;$j++){
$module_count[$j] = ReadingsVal($logdevice."_config","module_".$j."_count",0) ;
if ($module_count[$j] ne 0) {
$Solar_Plain = round(Solar_plain(ReadingsVal($logdevice."_config","module_".$j."_plain",0) , ReadingsVal($logdevice."_config","module_".$j."_direction",0) , $timestamp),3) ;
Log 3, "plain/direction : ".ReadingsVal($logdevice."_config","module_".$j."_plain",0)."/".ReadingsVal($logdevice."_config","module_".$j."_direction",0)." >>> ".$Solar_Plain ;
$Solar_[$j] = $module_count[$j] * ReadingsVal($logdevice."_config","module_".$j."_power",1)/1000 ;
$Solar_[$j] = $Solar_[$j] * $Solar_Plain ;
$Solar_[$j] = $Solar_[$j] * $Solar_SolarRadiation ;
$Solar_[$j] = $Solar_[$j] * $Solar_Correction_Temp * $Solar_Correction_Cloud * $Solar_Correction_Rain * $Solar_Correction_Faktor ;
$Solar_[$j] = ($Solar_[$j] lt 0)?0:round($Solar_[$j],0) ;
};
$logentry = $logentry + $Solar_[$j] ;
if ($fc == 0 and $hour == $i) {
fhem "setreading ".$logdevice." Solar_".ReadingsVal($logdevice."_config","module_".$j."_name",0)." ".$Solar_[$j] ;
};
};
if ($fc == 0) {
fhem "set ".$logdb." addCacheLine ".$timestamp."|".$logdevice."|addlog|".$reading.": ".$logentry."|".$reading."|".$logentry."|";
};
if ($fc == 0 and $hour == $i and ($module_count[1] ne 0 or $module_count[2] ne 0 or $module_count[3] ne 0)) {
fhem "setreading ".$logdevice." Solar_SolarRadiation ".$Solar_SolarRadiation ;
fhem "setreading ".$logdevice." Solar_Cloud ".$Solar_Cloud ;
fhem "setreading ".$logdevice." Solar_Rain ".$Solar_Rain ;
fhem "setreading ".$logdevice." Solar_Temp ".$Solar_Temp ;
fhem "setreading ".$logdevice." Solar_Correction_Cloud ".$Solar_Correction_Cloud ;
fhem "setreading ".$logdevice." Solar_Correction_Rain ".$Solar_Correction_Rain ;
fhem "setreading ".$logdevice." Solar_Correction_Temp ".$Solar_Correction_Temp ;
fhem "setreading ".$logdevice." Solar_Calculation ".$logentry ;
Log 3, "Solar_Plain : ".$Solar_Plain ;
Log 3, "Solar_SolarRadiation : ".$Solar_SolarRadiation ;
Log 3, "Solar_Cloud : ".$Solar_Cloud ;
Log 3, "cloudk : ".$cloudk." ".$cloudk_base ;
Log 3, "Solar_Correction_Cloud : ".$Solar_Correction_Cloud ;
Log 3, "Solar_Rain : ".$Solar_Rain ;
Log 3, "raink : ".$raink." ".$raink_base ;
Log 3, "Solar_Correction_Rain : ".$Solar_Correction_Rain ;
Log 3, "Solar_Temp : ".$Solar_Temp ;
Log 3, "tempk : ".$tempk." ".$tempk_base ;
Log 3, "Solar_Correction_Temp : ".$Solar_Correction_Temp ;
Log 3, $fc." ".$hour." ".$i." ".$Solar_SolarRadiation." ".$logentry ;
} ;
if ($fc != 0) {
fhem "set ".$logdb." addCacheLine ".$timestamp."|".$logdevice."|addlog|".$reading.": ".$logentry."|".$reading."|".$logentry."|";
Log 3, "Solar_Plain : ".$Solar_Plain ;
Log 3, "Solar_SolarRadiation : ".$Solar_SolarRadiation ;
Log 3, "Solar_Cloud : ".$Solar_Cloud ;
Log 3, "cloudk : ".$cloudk." ".$cloudk_base ;
Log 3, "Solar_Correction_Cloud : ".$Solar_Correction_Cloud ;
Log 3, "Solar_Rain : ".$Solar_Rain ;
Log 3, "raink : ".$raink." ".$raink_base ;
Log 3, "Solar_Correction_Rain : ".$Solar_Correction_Rain ;
Log 3, "Solar_Temp : ".$Solar_Temp ;
Log 3, "tempk : ".$tempk." ".$tempk_base ;
Log 3, "Solar_Correction_Temp : ".$Solar_Correction_Temp ;
Log 3, $fc." ".$i." ".$Solar_SolarRadiation." ".$logentry ;
} ;
} ;
return (0);
};
Solar_Plain
sub Solar_plain($$$) {
my $angle = $_[0];
my $orienta = $_[1];
my $time = $_[2];
my $azimuth = fhem "get Astro text SunAz ".$time ;
# angles in radiant
my $rad = 57.296;
my $elevation = fhem "get Astro text SunAlt ".$time ;
$elevation = $elevation / $rad;
$angle = $angle / $rad;
my $orientation = ($azimuth - 180 - $orienta) / $rad;
# avoid unrealistic values (normally formula should only be used within boundaries of orientation +/- 90 degrees)
return (0.001) if ($elevation <= 0.25);
if(cos($orientation) < 0.05 && cos($orientation) > -0.2) {$orientation =$orientation - 0.2};
# Log3 "", 1, "Solar radiation, azimuth = $azimuth, orientation=$orientation, elevation=$elevation, angle=$angle";
my $factor = sin($angle) /
(sin( $elevation) /
cos( $elevation)) *
cos($orientation) +
cos($angle);
# otherwise too big values (normally formula should only be used within boundaries of orientation +/- 90 degrees)
if ($factor > - 0.05 && $factor < 0.05) {$factor = 0.05};
return ($factor);
};
wunderground
Diesen Dienst nutze ich nicht für die Prognose, jedoch kann man dort private Wetterstationen in seinem näheren Umfeld finden, die die Sonneneinstrahlung und den UV Index messen. Das ist dann als aktueller Wert in den Diagrammen zu sehen und lässt aktuelle Beschattung durch Wolken erkennen. Hier kann man dann auch mehrere Stationen definieren und eventuell mit einem Durchschnitt arbeiten, wenn nicht gerade der Nachbar eine Station hat. Für diese Abfrage ist keine Registrierung notwendig und man muss auch nicht selber mir einer Station Daten liefern. Aber bitte, die Abfrage nicht in einem zu kurzen Abstand, also mit hoher Frequenz stellen! Hier wird alle 15 Minuten (900 Sekunden) abgefragt.
RAW Definition Wetter_<Wohnort>
Ggf. muss UConv vorher noch aktiviert werden:
define uconvInit notify global:INITIALIZED {use UConv}
defmod wetter_<Wohnort> HTTPMOD https://www.wunderground.com/dashboard/pws/<Wohnort_Station> 900
attr wetter_<Wohnort> DbLogExclude .*
attr wetter_<Wohnort> DbLogInclude solarRadiation,solarUV,temperature
attr wetter_<Wohnort> alias wetter_<Wohnort>
attr wetter_<Wohnort> enableControlSet 1
attr wetter_<Wohnort> event-on-change-reading solarRadiation,solarUV,temperature
attr wetter_<Wohnort> group ASC Environment
attr wetter_<Wohnort> icon weather_sunrise
attr wetter_<Wohnort> reading01Name date
attr wetter_<Wohnort> reading01Regex Summary.*>([[:alpha:]]{1,8} [\d]{1,2}, [\d]{4})<.*Temperature
attr wetter_<Wohnort> reading02Format %.1f
attr wetter_<Wohnort> reading02Name dewpointTemperature
attr wetter_<Wohnort> reading02OExpr UConv::f2c($val,2)
attr wetter_<Wohnort> reading02Regex DEWPOINT.*>([\d\.]+)<.*HUMIDITY
attr wetter_<Wohnort> reading03Name dewpointTemperature_EN
attr wetter_<Wohnort> reading03Regex DEWPOINT.*>([\d\.]+)<.*HUMIDITY
attr wetter_<Wohnort> reading04Format %.0f
attr wetter_<Wohnort> reading04Name humidity
attr wetter_<Wohnort> reading04Regex HUMIDITY.*>([\d\.]+)<.*WIND
attr wetter_<Wohnort> reading05Name precip1hrmetric
attr wetter_<Wohnort> reading05Regex PRECIP RATE.*>([\d\.]+)<.*PRECIP TOTAL
attr wetter_<Wohnort> reading06Name preciptodaymetric
attr wetter_<Wohnort> reading06Regex PRECIP TOTAL.*>([\d\.]+)<.*tile-precipitation
attr wetter_<Wohnort> reading07Format %.2f
attr wetter_<Wohnort> reading07Name pressure
attr wetter_<Wohnort> reading07OExpr UConv::inhg2hpa($val,2)
attr wetter_<Wohnort> reading07Regex CURRENT.*>([\d\.]+)<.*tile-pressure
attr wetter_<Wohnort> reading08Format %.2f
attr wetter_<Wohnort> reading08Name pressure_EN
attr wetter_<Wohnort> reading08Regex CURRENT.*>([\d\.]+)<.*tile-pressure
attr wetter_<Wohnort> reading09Name solarRadiation
attr wetter_<Wohnort> reading09Regex SOLAR RADIATION.*CURRENT.*weather__text">([\d\.]+)
attr wetter_<Wohnort> reading10Name solarUV
attr wetter_<Wohnort> reading10Regex CURRENT UV.*>([\d\.]+)<.*UV RISK
attr wetter_<Wohnort> reading11Format %.1f
attr wetter_<Wohnort> reading11Name temperature
attr wetter_<Wohnort> reading11OExpr UConv::f2c($val,2)
attr wetter_<Wohnort> reading11Regex current-temp.*">([- ]*[\d\.]+).*DEWPOINT
attr wetter_<Wohnort> reading12Format %.1f
attr wetter_<Wohnort> reading12Name temperature_EN
attr wetter_<Wohnort> reading12Regex current-temp.*">([- ]*[\d\.]+).*DEWPOINT
attr wetter_<Wohnort> reading13Format %.1f
attr wetter_<Wohnort> reading13Name windChill
attr wetter_<Wohnort> reading13OExpr UConv::f2c($val,2)
attr wetter_<Wohnort> reading13Regex Feels Like.*>([\d\.]+)<.*wind-dial__container
attr wetter_<Wohnort> reading14Format %.1f
attr wetter_<Wohnort> reading14Name windChill_EN
attr wetter_<Wohnort> reading14Regex Feels Like.*>([\d\.]+)<.*wind-dial__container
attr wetter_<Wohnort> reading15Name windDirection
attr wetter_<Wohnort> reading15OExpr UConv::compasspoint2compasspoint($val,"en",1,"de")
attr wetter_<Wohnort> reading15Regex WIND FROM.*>([NESW]+)<.*GUST
attr wetter_<Wohnort> reading16Name windDirection_EN
attr wetter_<Wohnort> reading16Regex WIND FROM.*>([NESW]+)<.*GUST
attr wetter_<Wohnort> reading17Format %.0f
attr wetter_<Wohnort> reading17Name windSpeed
attr wetter_<Wohnort> reading17OExpr UConv::mph2kph($val,1)
attr wetter_<Wohnort> reading17Regex wind-dial__container.*>([\d\.]+)<.*unit-speed
attr wetter_<Wohnort> reading18Name windSpeed_EN
attr wetter_<Wohnort> reading18Regex wind-dial__container.*>([\d\.]+)<.*unit-speed
attr wetter_<Wohnort> reading19Name windGust
attr wetter_<Wohnort> reading19OExpr UConv::mph2kph($val,1)
attr wetter_<Wohnort> reading19Regex GUST.*>([\d\.]+)<.*mph
attr wetter_<Wohnort> reading20Name windGust_EN
attr wetter_<Wohnort> reading20Regex GUST.*>([\d\.]+)<.*mph
attr wetter_<Wohnort> room Informationen->Wetter,Rollos
attr wetter_<Wohnort> sortby 03
attr wetter_<Wohnort> stateFormat T: temperature °C | F: humidity % | W: windSpeed km/h | D: pressure hPa | U: solarUV | R: solarRadiation W/m²
attr wetter_<Wohnort> timeout 5
Diagramme
RAW Definition Hauptverbraucher
SVG_LogDB_Photovoltaik_2
defmod SVG_LogDB_Photovoltaik_2 SVG LogDB:SVG_LogDB_Photovoltaik_2:HISTORY attr SVG_LogDB_Photovoltaik_2 DbLogExclude .* attr SVG_LogDB_Photovoltaik_2 group PV Eigenverbrauch attr SVG_LogDB_Photovoltaik_2 plotsize 1400,320 attr SVG_LogDB_Photovoltaik_2 room Strom->Info,Strom->Photovoltaik attr SVG_LogDB_Photovoltaik_2 sortby 00
GPLOTFILE SVG_LogDB_Photovoltaik_2.gplot
# Created by FHEM/98_SVG.pm, 2020-03-16 10:23:52 set terminal png transparent size <SIZE> crop set output '<OUT>.png' set xdata time set timefmt "%Y-%m-%d_%H:%M:%S" set xlabel " " set title 'Hauptverbraucher' set ytics set y2tics set grid set ylabel "Leistung" set y2label "" #LogDB PV_Anlage_1:Total_DC_Power:::$val=abs($val) #LogDB PV_Anlage_1:Total_PV_Power_reserve:: #LogDB StromZaehler_Heizung:SMAEM1901401955_Saldo_Wirkleistung:::$val=abs($val) #LogDB shelly02:Power_0:: #LogDB shelly03:Power:: plot "<IN>" using 1:2 axes x1y2 title 'Total_DC' ls l1 lw 1 with lines,\ "<IN>" using 1:2 axes x1y2 title 'Total_PV_reserve' ls l2 lw 1 with lines,\ "<IN>" using 1:2 axes x1y2 title 'Heizung' ls l0 lw 1 with lines,\ "<IN>" using 1:2 axes x1y2 title 'Pool' ls l8 lw 1 with lines,\ "<IN>" using 1:2 axes x1y2 title 'Waschmaschine' ls l3 lw 1 with lines
RAW Definition Leistungsbezug
SVG_LogDB_Photovoltaik_3
defmod SVG_LogDB_Photovoltaik_3 SVG LogDB:SVG_LogDB_Photovoltaik_3:HISTORY attr SVG_LogDB_Photovoltaik_3 DbLogExclude .* attr SVG_LogDB_Photovoltaik_3 group PV Eigenverbrauch attr SVG_LogDB_Photovoltaik_3 plotsize 1400,320 attr SVG_LogDB_Photovoltaik_3 room Strom->Info,Strom->Photovoltaik attr SVG_LogDB_Photovoltaik_3 sortby 00
GPLOTFILE SVG_LogDB_Photovoltaik_3.gplot
# Created by FHEM/98_SVG.pm, 2020-07-22 13:51:57 set terminal png transparent size <SIZE> crop set output '<OUT>.png' set xdata time set timefmt "%Y-%m-%d_%H:%M:%S" set xlabel " " set title 'Leistungsbezug' set ytics set y2tics set grid set ylabel "Leistung" set y2label "" set yrange [0:9500] set y2range [0:9500] #LogDB PV_Anlage_1:Total_DC_Power:::$val=abs($val) #LogDB PV_Anlage_1:Home_own_consumption_from_PV:: #LogDB PV_Anlage_1:Home_own_consumption_from_battery:: #LogDB PV_Anlage_1:Home_own_consumption_from_grid:: #LogDB PV_Anlage_1:Actual_battery_charge_usable_Power:: #LogDB PV_Anlage_1:Total_DC_Power_Max:: #LogDB PV_Anlage_1:Battery_temperature:::$val=$val*100 #LogDB Heizung:heatSourceIN:::$val=$val*100 plot "<IN>" using 1:2 axes x1y2 title 'Total_DC_Power' ls l1fill lw 1 with lines,\ "<IN>" using 1:2 axes x1y2 title 'own_PV' ls l2fill lw 1 with lines,\ "<IN>" using 1:2 axes x1y2 title 'Bat_use' ls l0fill lw 1 with lines,\ "<IN>" using 1:2 axes x1y2 title 'Grid_use' ls l5 lw 1 with lines,\ "<IN>" using 1:2 axes x1y2 title 'Bat_usable' ls l4 lw 1 with lines,\ "<IN>" using 1:2 axes x1y2 title 'Total_DC_Max' ls l6 lw 2 with lines,\ "<IN>" using 1:2 axes x1y2 title 'Bat_Temp_Trend' ls l0 lw 2 with lines,\ "<IN>" using 1:2 axes x1y2 title 'Out_Temp_Trend' ls l2 lw 2 with lines
RAW Definition PV_Bilanz
SVG_LogDB_PV_Bilanz
defmod SVG_LogDB_PV_Bilanz SVG LogDB:SVG_LogDB_PV_Bilanz:HISTORY attr SVG_LogDB_PV_Bilanz DbLogExclude .* attr SVG_LogDB_PV_Bilanz alias SVG_LogDB_PV_Bilanz attr SVG_LogDB_PV_Bilanz fixedrange year attr SVG_LogDB_PV_Bilanz plotsize 1400,320 attr SVG_LogDB_PV_Bilanz room Strom->Energie
GPLOTFILE SVG_LogDB_PV_Bilanz.gplot
# Created by FHEM/98_SVG.pm, 2020-08-02 09:55:06 set terminal png transparent size <SIZE> crop set output '<OUT>.png' set xdata time set timefmt "%Y-%m-%d_%H:%M:%S" set xlabel " " set title 'PV_Bilanz' set ytics set y2tics set grid ytics set ylabel "KWh" set y2label "KWh" #LogDB Dum.Energy:max_month_PVMonth:: #LogDB Dum.Energy:diff_week_PVMonth:: #LogDB Dum.Energy:diff_week_PVTotalMonth:: plot "<IN>" using 1:2 axes x1y2 title 'PV-Bezug-Monat' ls l2fill lw 2 with points,\ "<IN>" using 1:2 axes x1y2 title 'PV-Bezug-Woche' ls l2fill lw 1 with fsteps,\ "<IN>" using 1:2 axes x1y2 title 'PV-Total-Woche' ls l1fill lw 1 with lines
RAW Definition Forecast / Calculation
SVG_LogDB_Photovoltaik_4
defmod SVG_LogDB_Photovoltaik_4 SVG LogDB:SVG_LogDB_Photovoltaik_4:HISTORY attr SVG_LogDB_Photovoltaik_4 DbLogExclude .* attr SVG_LogDB_Photovoltaik_4 group PV Eigenverbrauch attr SVG_LogDB_Photovoltaik_4 plotsize 1400,320 attr SVG_LogDB_Photovoltaik_4 room Strom->Photovoltaik attr SVG_LogDB_Photovoltaik_4 sortby 00
GPLOTFILE SVG_LogDB_Photovoltaik_4.gplot
# Created by FHEM/98_SVG.pm, 2020-08-17 08:58:42 set terminal png transparent size <SIZE> crop set output '<OUT>.png' set xdata time set timefmt "%Y-%m-%d_%H:%M:%S" set xlabel " " set title 'Forecast / Calculation' set ytics set y2tics set grid set ylabel "Leistung" set y2label "Leistung" set yrange [0:10000] set y2range [0:10000] #LogDB Astro:SunAlt:::$val=($val>0?$val*50+7000:7000) #LogDB wetter_wolfskehlen_II:solarRadiation:::$val=($val>0?$val*3+7000:7000) #LogDB PV_Anlage_1:Solar_SolarRadiation:::$val=($val>0?$val*3+7000:7000) #LogDB Astro:SunAlt:::$val=7000 #LogDB PV_Anlage_1:Solar_Calculation_fc1:: #LogDB PV_Anlage_1:Power_DC_Sum:: #LogDB PV_Anlage_1:Solar_Calculation:: #LogDB PV_Anlage_1:Solar_East:: #LogDB PV_Anlage_1:Solar_South:: #LogDB PV_Anlage_1:Solar_West:: plot "<IN>" using 1:2 axes x1y2 title 'Sonnenhöhe' ls l7 lw 1 with lines,\ "<IN>" using 1:2 axes x1y2 title 'SolarRadiation' ls l8 lw 2 with lines,\ "<IN>" using 1:2 axes x1y2 title 'SolarRadiationPrognose' ls l8 lw 1 with lines,\ "<IN>" using 1:2 axes x1y2 title '70%' ls l0 lw 1 with lines,\ "<IN>" using 1:2 axes x1y2 title 'Calculation_fc1' ls l0 lw 2 with lines,\ "<IN>" using 1:2 axes x1y2 title 'Power_DC_Sum' ls l1fill lw 1 with lines,\ "<IN>" using 1:2 axes x1y2 title 'Calculation' ls l5 lw 1 with lines,\ "<IN>" using 1:2 axes x1y2 title 'East' ls l2 lw 0.5 with lines,\ "<IN>" using 1:2 axes x1y2 title 'South' ls l3 lw 0.5 with lines,\ "<IN>" using 1:2 axes x1y2 title 'West' ls l4 lw 0.5 with lines
PV Eigenverbrauch-Steuerung
Beispiel Luft Wärme Pumpe
RAW Definition LWP_LuftWärmePumpe (dummy Modul)
defmod LWP dummy
attr LWP DbLogExclude .*
attr LWP DbLogInclude state
attr LWP alias LWP_LuftWärmePumpe
attr LWP group PV Eigenverbrauch
attr LWP icon sani_earth_source_heat_pump
attr LWP readingList LWP_Button PowerLevelMinTime PowerLimitOn PowerLimitOff RunTimeMin RunTimePerDay SetCmdOff SetCmdOn TimeStart TimeEnd
attr LWP room Strom->Photovoltaik
attr LWP setList LWP_Button:uzsuToggle,on,off PowerLevelMinTime:slider,60,30,300 PowerLimitOn:slider,1000,250,4000 PowerLimitOff:slider,1000,250,4000 RunTimeMin:slider,300,300,7200 RunTimePerDay:slider,900,300,28800 SetCmdOff SetCmdOn TimeStart:time TimeEnd:time
attr LWP sortby 05
attr LWP stateFormat state
attr LWP verbose 0
attr LWP webCmd LWP_Button
setstate LWP off
setstate LWP 2020-08-05 17:38:03 LWP_Button off
setstate LWP 2020-03-02 11:56:45 PowerLevelMinTime 600
setstate LWP 2020-02-05 14:11:42 PowerLimitOff 2250
setstate LWP 2020-03-02 11:56:39 PowerLimitOn 3000
setstate LWP 2019-08-02 10:31:21 RunTimeMin 3600
setstate LWP 2020-02-05 14:13:01 RunTimePerDay 28800
setstate LWP 2019-12-29 10:47:55 SetCmdOff set shelly01 off 0
setstate LWP 2019-07-22 15:35:59 SetCmdOn set shelly01 on 0
setstate LWP 2019-11-09 12:13:04 TimeEnd 16:00
setstate LWP 2020-04-02 13:51:54 TimeStart 12:30
setstate LWP 2020-08-05 17:38:03 state off
RAW Definition LWP_PV (DOIF Modul)
defmod LWP_PV DOIF ################################################################################################################\
## 1 Eigenverbrauch abschalten: wenn Mindestlaufzeit erreicht wurde und Maximallaufzeit pro Tag erreicht ist\
##\
([LWP_Counter:pulseTimePerDay] >= [LWP:RunTimePerDay] and\
[LWP_Counter:pulseTimeIncrement] >= [LWP:RunTimeMin] and\
[LWP:state] ne "off" and [LWP:LWP_Button] eq "off" )\
\
({Log 3, " LWP_PV cmd_1 PV : LWP off"}\
{fhem("".ReadingsVal("LWP","SetCmdOff",0))}\
{fhem("set LWP off")}\
{fhem("set Heizung hotWaterTemperatureTarget 50.0")}\
)\
################################################################################################################\
## 2 Eigenverbrauch abschalten: wenn Mindestlaufzeit erreicht wurde und die PV Produktion unter dem Mindestbedarf ist\
##\
DOELSEIF\
( ([PV_Anlage_1:Total_PV_Power_reserve]+[StromZaehler_Heizung:SMAEM1901401955_Bezug_Wirkleistung]) < [LWP:PowerLimitOff] and\
[LWP_Counter:pulseTimeIncrement] >= [LWP:RunTimeMin] and\
[LWP:state] ne "off" and\
[LWP:LWP_Button] ne "on" )\
\
({Log 3, " LWP_PV cmd_2 PV : LWP off"}\
{fhem("".ReadingsVal("LWP","SetCmdOff",0))}\
{fhem("set LWP off")}\
{fhem("set Heizung hotWaterTemperatureTarget 50.0")}\
)\
################################################################################################################\
## 3 Stop, wenn es nur ein kurzer peak ist. Dieser Do Zweig setzt den wait timer vom Einschaltkommando cmd_4 wieder außerkraft,\
## wenn wärend der Wartezeit die PV Anlage zuwenig liefert.\
##\
DOELSEIF\
([PV_Anlage_1:Total_PV_Power_reserve] < [LWP:PowerLimitOff] and\
[LWP_PV:wait_timer] ne "no timer" and\
[LWP_PV:wait_timer] ne "" and\
[LWP:state] eq "off" )\
\
({Log 3, "LWP_PV cmd_3 PV : Stop wait timer LWP"})\
################################################################################################################\
## 4 Eigenverbrauch einschalten: wenn PV Produktion über dem Mindestbedarf ist und die Laufzeit pro Tag noch nicht erreicht ist\
##\
DOELSEIF\
([Astro:ObsSeason] ne "Sommer" and [Astro:ObsSeason] ne "Frühling" and\
[PV_Anlage_1:Total_PV_Power_reserve] >= [LWP:PowerLimitOn] and\
[[LWP:TimeStart]-[LWP:TimeEnd]] and\
[LWP:state] eq "off" and\
[LWP_Counter:pulseTimePerDay] < [LWP:RunTimePerDay] and\
[Heizung:hotWaterTemperature] < 60 )\
\
({Log 3, "LWP_PV cmd_4 : LWP on"}\
{fhem("".ReadingsVal("LWP","SetCmdOn",0))}\
{fhem("set LWP on")}\
{fhem("set Heizung hotWaterTemperatureTarget 60.0")}\
)\
################################################################################################################\
## 5 Signal für den PV-Modus der LWP einschalten.\
##\
DOELSEIF\
([LWP:LWP_Button] eq "on" )\
\
({Log 3, "LWP_PV cmd_5 PV : LWP on for manuel PV-Modus"}\
{fhem("".ReadingsVal("LWP","SetCmdOn",0))}\
{fhem("set LWP on")}\
{fhem("set Heizung hotWaterTemperatureTarget 60.0")}\
)\
################################################################################################################\
## 6 Signal für den PV-Modus der LWP abschalten.\
##\
DOELSEIF\
([LWP:LWP_Button] eq "off" and\
[$SELF:cmd_nr] eq "5" )\
\
({Log 3, "LWP_PV cmd_6 PV : LWP off after manuel PV-Modus"}\
{fhem("".ReadingsVal("LWP","SetCmdOff",0))}\
{fhem("set LWP off")}\
{fhem("set Heizung hotWaterTemperatureTarget 50.0")}\
)\
################################################################################################################\
## 7 Stop wait Timer für das Abschalten, wenn die LWP beim Starten noch anläuft\
##\
DOELSEIF\
([StromZaehler_Heizung:SMAEM1901401955_Bezug_Wirkleistung] > 300 and\
[Heizung:opStateHeatPump1] eq "Wärmepumpe kommt" and\
[Heizung:opStateHeatPump3] eq "Pumpenvorlauf" )\
\
({Log 3, "LWP_PV cmd_7 : Stop wait timer LWP"})\
################################################################################################################\
## 8 LWP Ende\
##\
DOELSEIF\
([StromZaehler_Heizung:SMAEM1901401955_Bezug_Wirkleistung] < 300 and\
[LWP_Counter:pulseTimeIncrement] >= [LWP:RunTimeMin] and\
([Heizung:opStateHeatPump1] ne "Wärmepumpe läuft" or [Heizung:opStateHeatPump3] eq "Luftabtauen" ) and\
([$SELF:cmd_nr] eq "4" or [$SELF:cmd_nr] eq "5" or [$SELF:cmd_nr] eq "10") )\
\
({Log 3, "LWP_PV cmd_8 : LWP run finished"}\
{fhem("".ReadingsVal("LWP","SetCmdOff",0))}\
{fhem("set LWP off")}\
{fhem("set Heizung hotWaterTemperatureTarget 50.0")}\
{fhem("setreading LWP LWP_Button off")}\
)\
################################################################################################################\
## 9 LWP Zwangseinschalten: Sollte das Brauchwasser noch nicht aufgeheizt sein, wird um die Hysterese erhöht.\
## Dies kann passieren, wenn am Tag vorher der PV-Modus lief und dann das Wasser noch knapp über dem Mindestwert ist.\
##\
DOELSEIF\
([Astro:ObsSeason] ne "Sommer" and [Astro:ObsSeason] ne "Frühling" and\
[[LWP:TimeEnd]] and\
[Heizung:hotWaterTemperature] < 47 and\
([LWP_Counter:pulseTimePerDay] < [Pool:RunTimePerDay] or\
[LWP_Counter:countsPerDay] eq 0) )\
\
({Log 3, "LWP_PV cmd_9 : LWP on for water heating"}\
\
{fhem("set Heizung hotWaterTemperatureTarget ". (ReadingsVal("Heizung","hotWaterTemperature",46)+4))}\
\
{Log 3, "LWP_PV cmd_9 : LWP hotWaterTemperatureTarget ".ReadingsVal("Heizung","hotWaterTemperatureTarget",0)}\
)\
################################################################################################################\
## 10 Hohe Priorität im Winter fuer die LWP\
## Einschalten, wenn der Pool läuft, der Speicher geladen ist und noch Überschuss da ist.\
##\
DOELSEIF\
([Astro:ObsSeason] eq "Winter" and\
[PV_Anlage_1:Total_PV_Power_reserve] >= 2000 and\
[shelly02:power_0] > 800 and\
[PV_Anlage_1:Act_state_of_charge] > 60 and\
[Heizung:hotWaterTemperature] < 60 and \
[$SELF:cmd_nr] ne "10" )\
\
({Log 3, "LWP_PV cmd_10 : LWP Priorität"}\
{fhem("set LWP_PV cmd_4")}\
)\
attr LWP_PV DbLogExclude .*
attr LWP_PV DbLogInclude state,cmd.*,Device,LWP_Status,wait_timer
attr LWP_PV alias LWP_PV
attr LWP_PV cmdState Maximalzeit pro Tag überschritten|Eigenverbrauch aus|Stop wait timer|Eigenverbrauch ein|LWP ein für manuellen PV-Modus|LWP aus nach manuellem PV-Modus|Stop wait timer fuer aus|LWP aus nach PV-Modus|LWP Brauchwasser nachheizen|LWP Priorität
attr LWP_PV do always
attr LWP_PV group PV Eigenverbrauch-Steuerung
attr LWP_PV icon sani_earth_source_heat_pump
attr LWP_PV room Strom->Photovoltaik
attr LWP_PV sortby 01
attr LWP_PV stateFormat state : LWP_Status : Brauchwasser e_Heizung_hotWaterTemperature °C
attr LWP_PV userReadings LWP_Status { ReadingsVal("Heizung","state","") }
attr LWP_PV verbose 5
attr LWP_PV wait 0:10:0:[LWP:PowerLevelMinTime]:0:0:900:0:0
- RAW Definition LWP_Signale (Shelly Modul: shelly1pm)
defmod shelly01 Shelly 192.168.178.55
attr shelly01 DbLogExclude .*
attr shelly01 DbLogInclude relay.*,power.*,energy.*
attr shelly01 alias LWP_Signale
attr shelly01 event-on-change-reading relay.*,power.*,energy.*,state,network
attr shelly01 group PV Eigenverbrauch-Steuerung
attr shelly01 icon taster_ch_1
attr shelly01 mode relay
attr shelly01 model shelly1pm
attr shelly01 room Shelly,Heizung->System,Strom->Photovoltaik
attr shelly01 sortby 02
attr shelly01 stateFormat {sprintf("\
<TABLE>\
\
<TR>\
<TD VALIGN=\"TOP\" ALIGN=\"LEFT\" WIDTH=\"50\">\
WebLink: %s\
</TD>\
\
<TD VALIGN=\"TOP\" ALIGN=\"RIGHT\" WIDTH=\"100\">\
Gesamt 0: %08.2f KWh<br>\
</TD>\
\
<TD VALIGN=\"TOP\" ALIGN=\"RIGHT\" WIDTH=\"70\">\
Relais 0: %s %06.1f Watt<br>\
</TD>\
</TR>\
\
</TABLE>\
" ,\
ReadingsVal($name,"WebLink","none") ,\
ReadingsVal($name,"energy_0",0)/1000,\
(ReadingsVal($name,"relay","") eq "off") ? "<span style='color:#FF0000'>off</span>":"<span style='color:#00FF00'>on</span>",\
ReadingsVal($name,"power",0),\
)}
attr shelly01 userReadings WebLink:network { my $ip=ReadingsVal($NAME,"network","");; $ip =~ s/connected to //gs;; $ip =~ s/<[^>]*>//gs;; return("<html><a href='http://".$ip."/'>WEB</a></html>") }
attr shelly01 webCmd |
</pre>
==== RAW Definition LWP_Counter (HourCounter Modul) ====
<pre>
defmod LWP_Counter HourCounter LWP:on.* LWP:off
attr LWP_Counter DbLogExclude .*
attr LWP_Counter alias LWP_Counter
attr LWP_Counter event-min-interval .*:600
attr LWP_Counter event-on-change-reading .*
attr LWP_Counter group PV Eigenverbrauch-Steuerung
attr LWP_Counter icon time_timer
attr LWP_Counter interval 5
attr LWP_Counter room Strom->Photovoltaik
attr LWP_Counter sortby 03
attr LWP_Counter verbose 0
RAW Definition rg_LWP_Status (readingsGroup Modul)
defmod rg_LWP_Status readingsGroup <Device>,<Information>,<Wert> LWP:<Status>,state LWP:<PowerLevelMinTime>,PowerLevelMinTime LWP:<PowerLimitOn>,PowerLimitOn LWP:<PowerLimitOff>,PowerLimitOff LWP:<TimeStart>,!TimeStart LWP:<TimeEnd>,!TimeEnd LWP:<RunTimeMin>,RunTimeMin LWP_Counter:<RunTimeMin>,pulseTimeIncrement LWP:<RunTimePerDay>,RunTimePerDay LWP_Counter:<RunTimePerDay>,pulseTimePerDay LWP_PV:<wait>,wait_timer LWP_PV:<TimeStart>,timer_01_c04 LWP_PV:<TimeEnd>,timer_02_c04
attr rg_LWP_Status DbLogExclude .*
attr rg_LWP_Status alias Status LuftWärmePumpe Eigenverbrauch
attr rg_LWP_Status commands {state => 'state:on,off',\
PowerLevelMinTime => 'PowerLevelMinTime:selectnumbers,60,60,600,0,lin',\
PowerLimitOn => 'PowerLimitOn:selectnumbers,1000,250,4000,0,lin',\
PowerLimitOff => 'PowerLimitOff:selectnumbers,1000,250,4000,0,lin',\
RunTimeMin => 'RunTimeMin:selectnumbers,300,300,14400,0,lin',\
RunTimePerDay => 'RunTimePerDay:selectnumbers,300,300,28800,0,lin',\
TimeStart => 'TimeStart:time',\
TimeEnd => 'TimeEnd:time'}
attr rg_LWP_Status group PV Status
attr rg_LWP_Status nameStyle style="color:grey"
attr rg_LWP_Status room Strom->Photovoltaik
attr rg_LWP_Status sortby 01
attr rg_LWP_Status style style="font-size:18px"
Beispiel Pool
RAW Definition Pool_Softube (dummy Modul)
defmod Pool dummy
attr Pool DbLogExclude .*
attr Pool DbLogInclude state
attr Pool alias Pool_Softube
attr Pool event-on-change-reading .*
attr Pool group PV Eigenverbrauch
attr Pool icon scene_swimming
attr Pool readingList Pool_Button PowerLevelMinTime PowerLimitOn PowerLimitOff RunTimeMin RunTimePerDay RunTimePerDaySummer RunTimePerDayWinter SetCmdOff SetCmdOn TimeStart TimeEnd
attr Pool room Strom->Photovoltaik
attr Pool setList Pool_Button:uzsuToggle,on,off PowerLevelMinTime:slider,30,30,300 PowerLimitOn:slider,500,100,1500 PowerLimitOff:slider,0,100,1000 RunTimeMin:slider,300,300,7200 RunTimePerDay:slider,900,300,64800 RunTimePerDaySummer:slider,900,300,7200 RunTimePerDayWinter:slider,3600,900,64800 SetCmdOff SetCmdOn TimeStart:time TimeEnd:time
attr Pool sortby 06
attr Pool stateFormat state
attr Pool verbose 0
attr Pool webCmd Pool_Button
setstate Pool off
setstate Pool 2020-08-28 13:30:10 Pool_Button off
setstate Pool 2019-12-02 10:31:26 PowerLevelMinTime 600
setstate Pool 2019-11-13 10:58:18 PowerLimitOff 800
setstate Pool 2019-11-06 11:49:00 PowerLimitOn 1000
setstate Pool 2020-08-14 16:26:09 RunTimeMin 1800
setstate Pool 2020-08-28 06:15:00 RunTimePerDay 3600
setstate Pool 2020-08-16 13:17:45 RunTimePerDaySummer 3600
setstate Pool 2020-08-25 19:32:33 RunTimePerDayWinter 14400
setstate Pool 2019-08-01 14:18:08 SetCmdOff set shelly02 off 0
setstate Pool 2019-08-02 09:33:06 SetCmdOn set shelly02 on 0
setstate Pool 2019-10-31 21:53:28 TimeEnd 16:00
setstate Pool 2020-04-08 18:19:29 TimeStart 12:30
setstate Pool 2020-08-28 14:13:02 state off
RAW Definition Pool_PV (DOIF Modul)
defmod Pool_PV DOIF ################################################################################################################\
## 1 Eigenverbrauch abschalten: wenn Mindestlaufzeit erreicht wurde und Maximallaufzeit pro Tag erreicht ist\
##\
([Pool_Counter:pulseTimePerDay] >= [Pool:RunTimePerDay] and\
[Pool_Counter:pulseTimeIncrement] >= [Pool:RunTimeMin] and\
[Pool:state] eq "on" and [Pool:Pool_Button] eq "off" )\
\
({Log 3, "Pool_PV cmd_1 : Pool off"}\
{fhem("".ReadingsVal("Pool","SetCmdOff",0))}\
{fhem("set Pool off")} )\
################################################################################################################\
## 2 Eigenverbrauch abschalten: wenn Mindestlaufzeit erreicht wurde und die PV-Produktion unter dem Mindestbedarf ist.\
## Bei Pool Nutzung und Pflegeprogramm wird nicht abgeschaltet.\
##\
DOELSEIF\
([PV_Anlage_1:Total_PV_Power_reserve] < [Pool:PowerLimitOff] and\
[Pool_Counter:pulseTimeIncrement] >= [Pool:RunTimeMin] and\
[Pool:state] eq "on" and \
[$SELF:cmd_nr] ne "5" and [$SELF:cmd_nr] ne "7" and [$SELF:cmd_nr] ne "10" )\
\
({Log 3, "Pool_PV cmd_2 : Pool off"}\
{fhem("".ReadingsVal("Pool","SetCmdOff",0))}\
{fhem("set Pool off")}\
{fhem("setreading Pool Pool_Button off")} )\
################################################################################################################\
## 3 Stop, wenn es nur ein kurzer Peak ist. Dieser Do Zweig setzt den wait timer vom Einschaltkommando cmd_4 wieder außer kraft,\
## wenn während der Wartezeit die PV-Anlage zuwenig liefert.\
##\
DOELSEIF\
([PV_Anlage_1:Total_PV_Power_reserve] < [Pool:PowerLimitOff] and\
[$SELF:wait_timer] ne "no timer" and\
[$SELF:wait_timer] ne "" and\
[Pool:state] eq "off" )\
\
({Log 3, "Pool_PV cmd_3 : Stop wait timer Pool"})\
################################################################################################################\
## 4 Eigenverbrauch einschalten: wenn PV-Produktion über dem Mindestbedarf ist und PV-Strom ins Netz eingespeist wird\
## und die Laufzeit pro Tag noch nicht erreicht ist;; Bei über 7000 Watt Einspeisung sofort aktivieren\
##\
DOELSEIF\
(([PV_Anlage_1:Total_PV_Power_reserve] >= [Pool:PowerLimitOn] and\
[[Pool:TimeStart]-[Pool:TimeEnd]] and\
[Pool:state] eq "off" and\
[Pool_Counter:pulseTimePerDay] < [Pool:RunTimePerDay]\
) or\
[PV_Anlage_1:Total_active_power_(powermeter)] <= -7000\
)\
\
({Log 3, "Pool_PV cmd_4 : Pool on"}\
{fhem("set Pool_Counter pulseTimeIncrement 0")}\
{fhem("".ReadingsVal("Pool","SetCmdOn",0))}\
{fhem("set Pool on")} )\
################################################################################################################\
## 5 Steckdose für die Benutzung des Pools einschalten.\
##\
DOELSEIF\
([Pool:Pool_Button] eq "on" )\
\
({Log 3, "Pool_PV cmd_5 : Pool on for usage"}\
{fhem("".ReadingsVal("Pool","SetCmdOn",0))}\
{fhem("set Pool on")} )\
################################################################################################################\
## 6 Steckdose des Pools abschalten.\
##\
DOELSEIF\
([Pool:Pool_Button] eq "off" and\
[$SELF:cmd_nr] eq "5" or [$SELF:cmd_nr] eq "7")\
\
({Log 3, "Pool_PV cmd_6 : Pool off after usage"}\
{fhem("".ReadingsVal("Pool","SetCmdOff",0))}\
{fhem("set Pool off")} )\
################################################################################################################\
## 7 Stop wait Timer für das Abschalten, wenn die Pumpe beim Starten noch anläuft\
##\
DOELSEIF\
([shelly02:power_0] > 10 and\
([$SELF:cmd_nr] eq "5" or [$SELF:cmd_nr] eq "7" or [$SELF:cmd_nr] eq "9") and\
[$SELF:wait_timer] ne "no timer" and\
[$SELF:wait_timer] ne "" and\
[Pool:state] eq "on" )\
\
({Log 3, "Pool_PV cmd_7 : Stop wait timer Pool"})\
################################################################################################################\
## 8 Pool Ende\
##\
DOELSEIF\
([shelly02:power_0] < 10 and\
([$SELF:cmd_nr] eq "5" or [$SELF:cmd_nr] eq "7" or [$SELF:cmd_nr] eq "9") )\
\
({Log 3, "Pool_PV cmd_8 : Pool run finished"}\
{fhem("".ReadingsVal("Pool","SetCmdOff",0))}\
{fhem("set Pool off")}\
{fhem("setreading Pool Pool_Button off")} )\
################################################################################################################\
## 9 Pflege Zwangseinschaltung: Es muss mindestens einmal pro Tag eingeschaltet werden, auch wenn kein PV Strom vorhanden war.\
##\
DOELSEIF\
([[Pool:TimeEnd]] and\
([Pool_Counter:pulseTimePerDay] < [Pool:RunTimePerDay] or\
[Pool_Counter:countsPerDay] eq 0)\
)\
\
({Log 3, "Pool_PV cmd_9 : Pool on for maintanance"}\
{fhem("set Pool_Counter pulseTimeIncrement 0")}\
{fhem("".ReadingsVal("Pool","SetCmdOn",0))}\
{fhem("set Pool on")} )\
################################################################################################################\
## 10 Pflege Zwangseinschaltung bei günstigem Strom nachts im Winter\
##\
DOELSEIF\
([Astro:ObsSeason] eq "Winter" and\
[Strom_Kosten:aWATTar_Trigger] eq "on" and\
[22:00-05:00]\
)\
\
({Log 3, "Pool_PV cmd_10 : Pool on for maintanance by aWATTar"}\
{fhem("set Pool_Counter pulseTimeIncrement 0")}\
{fhem("".ReadingsVal("Pool","SetCmdOn",0))}\
{fhem("set Pool on")} )\
################################################################################################################\
## 11 Abschaltung bei teurem Strom\
##\
DOELSEIF\
([Astro:ObsSeason] eq "Winter" and\
[Strom_Kosten:aWATTar_Trigger] eq "off" and\
[$SELF:cmd_nr] eq "10"\
)\
\
({Log 3, "Pool_PV cmd_11 : Pool off after maintanance by aWATTar"}\
{fhem("".ReadingsVal("Pool","SetCmdOff",0))}\
{fhem("set Pool off")} )\
################################################################################################################\
## 12 Pool durch kürzere Laufzeit abkühlen lassen\
##\
DOELSEIF\
([06:15] and [Heizung:averageAmbientTemperature])\
\
(\
{ if ( [Heizung:averageAmbientTemperature] >= 18 )\
{fhem("setreading Pool RunTimePerDay ".ReadingsVal("Pool","RunTimePerDaySummer",0) )}\
else \
{fhem("setreading Pool RunTimePerDay ".ReadingsVal("Pool","RunTimePerDayWinter",0) )}\
},\
{Log 3, "Pool_PV cmd_12 : Pool RunTimePerDay switched"}\
)\
\
attr Pool_PV DbLogExclude .*
attr Pool_PV DbLogInclude cmd.*,state,cmd.*,Device,Pool_Pumpe_Status,wait_timer
attr Pool_PV alias Pool_PV
attr Pool_PV cmdState Maximalzeit pro Tag überschritten|Eigenverbrauch aus|Stop wait timer|Eigenverbrauch freigegeben|Pool ein für Benutzung|Pool aus nach Benutzung|Stop wait timer für aus|Pool aus|Pflegemodus ohne PV|Strombörse ein|Strombörse aus|RunTimePerDay switched
attr Pool_PV disable 0
attr Pool_PV do always
attr Pool_PV event-on-change-reading .*
attr Pool_PV group PV Eigenverbrauch-Steuerung
attr Pool_PV icon scene_swimming
attr Pool_PV room Strom->Photovoltaik
attr Pool_PV sortby 11
attr Pool_PV stateFormat state : Pool_Pumpe_Status
attr Pool_PV userReadings Pool_Pumpe_Status { ReadingsVal("shelly02","power_0",0)>10 ? "Pool_Pumpe_laeuft" : "Pool_Pumpe_aus"}
attr Pool_PV verbose 0
attr Pool_PV wait 0:10:0:[Pool:PowerLevelMinTime]:0:0:0:300:0:300
RAW Definition Pool_Signale (Shelly Modul: shelly1pm)
defmod shelly02 Shelly 192.168.178.52
attr shelly02 DbLogExclude .*
attr shelly02 DbLogInclude relay.*,power.*,energy.*
attr shelly02 alias Pool_Signale
attr shelly02 comment relais_0 => Pool limit 1000 W\
relail_1 => Terrasse Lichterkette limit 100 W
attr shelly02 event-on-change-reading relay.*,power.*,energy.*,state,network
attr shelly02 group PV Eigenverbrauch-Steuerung
attr shelly02 icon taster_ch_1
attr shelly02 mode relay
attr shelly02 model shelly2.5
attr shelly02 room Shelly,Strom->Photovoltaik
attr shelly02 sortby 12
attr shelly02 stateFormat {sprintf("\
<TABLE>\
\
<TR>\
<TD VALIGN=\"TOP\" ALIGN=\"LEFT\" WIDTH=\"50\">\
Status: %s<br>\
WebLink: %s\
</TD>\
\
<TD VALIGN=\"TOP\" ALIGN=\"RIGHT\" WIDTH=\"100\">\
Gesamt 0: %08.2f KWh<br>Gesamt 1: %08.2f KWh\
</TD>\
\
<TD VALIGN=\"TOP\" ALIGN=\"RIGHT\" WIDTH=\"70\">\
Relais 0: %s %06.1f Watt<br>\
Relais 1: %s %06.1f Watt<br>\
</TD>\
</TR>\
\
</TABLE>\
" ,\
(ReadingsVal($name,"state","none") eq "OK") ? "<span style='color:#00FF00'>OK</span>":"<span style='color:#FF0000'>Error</span>",\
ReadingsVal($name,"WebLink","none") ,\
ReadingsVal($name,"energy_0",0)/1000,\
ReadingsVal($name,"energy_1",0)/1000,\
(ReadingsVal($name,"relay_0","") eq "off") ? "<span style='color:#FF0000'>off</span>":"<span style='color:#00FF00'>on</span>",\
ReadingsVal($name,"power_0",0),\
(ReadingsVal($name,"relay_1","") eq "off") ? "<span style='color:#FF0000'>off</span>":"<span style='color:#00FF00'>on</span>",\
ReadingsVal($name,"power_1",0)\
)}
attr shelly02 userReadings WebLink:network { my $ip=ReadingsVal($name,"network","");; $ip =~ s/connected to //gs;; $ip =~ s/<[^>]*>//gs;; return("<html><a href='http://".$ip."/'>WEB</a></html>") }
attr shelly02 verbose 0
attr shelly02 webCmd |
RAW Definition Pool_Counter (HourCounter Modul)
defmod Pool_Counter HourCounter shelly02:power_0:\s[0-9]{2,}(\.[0-9]{1,2})*$ shelly02:power_0:\s[0-9]{1}(\.[0-9]{1,2})*$
attr Pool_Counter DbLogExclude .*
attr Pool_Counter alias Pool_Counter
attr Pool_Counter comment On und Off des Pools werden direkt über den Shelly Stromverbrauch getriggert.
attr Pool_Counter event-on-change-reading .*
attr Pool_Counter group PV Eigenverbrauch-Steuerung
attr Pool_Counter icon time_timer
attr Pool_Counter interval 5
attr Pool_Counter room Strom->Photovoltaik
attr Pool_Counter sortby 13
attr Pool_Counter verbose 0
RAW Definition rg_Pool_Status (readingsGroup Modul)
defmod rg_Pool_Status readingsGroup <Device>,<Information>,<Wert> Pool:<Status>,!state Pool:<PowerLevelMinTime>,!PowerLevelMinTime Pool:<PowerLimitOn>,!PowerLimitOn Pool:<PowerLimitOff>,!PowerLimitOff Pool:<TimeStart>,!TimeStart Pool:<TimeEnd>,!TimeEnd Pool:<RunTimeMin>,!RunTimeMin Pool_Counter:<RunTimeMin>,!pulseTimeIncrement Pool:<RunTimePerDay>,!RunTimePerDay Pool:<RunTimePerDaySummer>,!RunTimePerDaySummer Pool:<RunTimePerDayWinter>,!RunTimePerDayWinter Pool_Counter:<RunTimePerDay>,!pulseTimePerDay Pool_PV:<wait>,wait_timer Pool_PV:<TimeStart>,timer_01_c04 Pool_PV:<TimeEnd>,timer_02_c04
attr rg_Pool_Status DbLogExclude .*
attr rg_Pool_Status alias Status Softube Pool Eigenverbrauch
attr rg_Pool_Status commands {state => 'state:on,off',\
PowerLevelMinTime => 'PowerLevelMinTime:selectnumbers,60,30,600,0,lin',\
PowerLimitOn => 'PowerLimitOn:selectnumbers,500,250,1500,0,lin',\
PowerLimitOff => 'PowerLimitOff:selectnumbers,0,100,1000,0,lin',\
RunTimeMin => 'RunTimeMin:selectnumbers,300,300,14400,0,lin',\
RunTimePerDaySummer => 'RunTimePerDaySummer:selectnumbers,300,300,3600,0,lin',\
RunTimePerDayWinter => 'RunTimePerDayWinter:selectnumbers,3600,900,64800,0,lin',\
TimeStart => 'TimeStart:time',\
TimeEnd => 'TimeEnd:time'}
attr rg_Pool_Status group PV Status
attr rg_Pool_Status nameStyle style="color:grey"
attr rg_Pool_Status room Strom->Photovoltaik
attr rg_Pool_Status sortby 02
attr rg_Pool_Status style style="font-size:18px"
Beispiel Waschmaschine (mit Walzenschalter ;-) )
RAW Definition Waschmaschine (dummy Modul)
defmod Waschmaschine dummy
attr Waschmaschine DbLogExclude .*
attr Waschmaschine DbLogInclude state
attr Waschmaschine alias Waschmaschine
attr Waschmaschine group PV Eigenverbrauch
attr Waschmaschine icon scene_washing_machine
attr Waschmaschine readingList Waschmaschine_Button PowerLevelMinTime PowerPhaseUse PowerLimitOn PowerLimitOff RunTimeMin RunTimePerDay SetCmdOff SetCmdOn TimeStart TimeEnd
attr Waschmaschine room Strom->Photovoltaik
attr Waschmaschine setList Waschmaschine_Button:uzsuToggle,on,off PowerLevelMinTime:slider,30,30,300 PowerLimitOn:slider,250,250,2000 PowerLimitOff:slider,0,250,1000 RunTimeMin:slider,300,300,7200 RunTimePerDay:slider,7200,300,28800 SetCmdOff SetCmdOn TimeStart:time TimeEnd:time
attr Waschmaschine sortby 07
attr Waschmaschine stateFormat state
attr Waschmaschine verbose 0
attr Waschmaschine webCmd Waschmaschine_Button
setstate Waschmaschine off
setstate Waschmaschine 2019-12-02 10:29:49 PowerLevelMinTime 300
setstate Waschmaschine 2019-11-06 10:56:22 PowerLimitOff 250
setstate Waschmaschine 2020-04-20 13:06:04 PowerLimitOn 2000
setstate Waschmaschine 2019-12-02 15:01:18 RunTimeMin 5400
setstate Waschmaschine 2020-04-02 13:59:34 RunTimePerDay 19200
setstate Waschmaschine 2020-01-01 16:29:10 SetCmdOff set shelly03 off 0
setstate Waschmaschine 2020-01-01 16:29:24 SetCmdOn set shelly03 on 0
setstate Waschmaschine 2019-08-01 14:25:25 TimeEnd 18:00
setstate Waschmaschine 2019-10-28 09:13:30 TimeStart 09:00
setstate Waschmaschine 2020-08-27 16:17:23 Waschmaschine_Button off
setstate Waschmaschine 2020-08-28 16:29:42 state off
RAW Definition Waschmaschine_PV (DOIF Modul)
defmod Waschmaschine_PV DOIF ################################################################################################################\
## 1 Eigenverbrauch sperren: wenn Mindestlaufzeit erreicht wurde und Maximallaufzeit pro Tag erreicht ist\
## jedoch nicht wenn manueller Betrieb aktiv ist.\
##\
([Waschmaschine_Counter:pulseTimePerDay] >= [Waschmaschine:RunTimePerDay] and\
[Waschmaschine_Counter:pulseTimeIncrement] >= [Waschmaschine:RunTimeMin] and\
[Waschmaschine:state] eq "on" and [Waschmaschine:Waschmaschine_Button] eq "off" )\
\
({Log 3, "Waschmaschine cmd_1 : Eigenverbrauch sperren"}\
{fhem("".ReadingsVal("Waschmaschine","SetCmdOff",0))}\
{fhem("setreading Waschmaschine_PV Waschmaschine_Status Steckdose ist ausgeschaltet")}\
{fhem("set Waschmaschine off")}\
)\
################################################################################################################\
## 2 Eigenverbrauch sperren: wenn Mindestlaufzeit erreicht wurde und die PV-Produktion unter dem Mindestbedarf ist\
## ausser bei manuellem Einschalten und wenn das Waschprogramm bereits läuft\
##\
## ([PV_Anlage_1:Total_PV_Power_reserve] < [Waschmaschine:PowerLimitOff] and\
## [Waschmaschine_Counter:pulseTimeIncrement] >= [Waschmaschine:RunTimeMin] and\
##\
DOELSEIF\
([PV_Anlage_1:Total_PV_Power_reserve] < [Waschmaschine:PowerLimitOn] and\
[Waschmaschine:state] eq "on" and\
[$SELF:cmd_nr] ne "5" and [$SELF:cmd_nr] ne "7" )\
\
({Log 3, "Waschmaschine cmd_2 : Eigenverbrauch sperren"}\
{fhem("".ReadingsVal("Waschmaschine","SetCmdOff",0))}\
{fhem("setreading Waschmaschine_PV Waschmaschine_Status Steckdose ist ausgeschaltet")}\
{fhem("set Waschmaschine off")}\
)\
################################################################################################################\
## 3 Stop, wenn es nur ein kurzer Peak ist. Dieser Do Zweig setzt den wait timer vom Einschaltkommando cmd_4\
## wieder außer kraft, wenn während der Wartezeit die PV-Anlage zuwenig liefert.\
##\
DOELSEIF\
([PV_Anlage_1:Total_PV_Power_reserve] < [Waschmaschine:PowerLimitOff] and\
[Waschmaschine_PV:wait_timer] ne "no timer" and\
[Waschmaschine_PV:wait_timer] ne "" and\
[Waschmaschine:state] eq "off" )\
\
({Log 3, "Waschmaschine cmd_3 : Waschmaschine stop wait timer"})\
################################################################################################################\
## 4 Eigenverbrauch freigeben: wenn PV-Produktion über dem Mindestbedarf ist und PV-Strom ins Netz eingespeist\
## wird und die Laufzeit pro Tag noch nicht erreicht ist\
##\
DOELSEIF\
([PV_Anlage_1:Total_PV_Power_reserve] > [Waschmaschine:PowerLimitOn] and\
[Waschmaschine:state] eq "off" and\
[[Waschmaschine:TimeStart]-[Waschmaschine:TimeEnd]] and\
[Waschmaschine_Counter:pulseTimePerDay] < [Waschmaschine:RunTimePerDay] )\
\
({Log 3, "Waschmaschine cmd_4 : Waschmaschine freigabe"}\
{fhem("".ReadingsVal("Waschmaschine","SetCmdOn",0))}\
{fhem("setreading Waschmaschine_PV Waschmaschine_Status Steckdose ist eingeschaltet")}\
{fhem("set Waschmaschine on")}\
)\
################################################################################################################\
## 5 Steckdose manuell für die Benutzung der Waschmaschine einschalten.\
##\
DOELSEIF\
([Waschmaschine:Waschmaschine_Button] eq "on" )\
\
({Log 3, "Waschmaschine cmd_5 : Waschmaschine manuell ein"}\
{fhem("".ReadingsVal("Waschmaschine","SetCmdOn",0))}\
{fhem("setreading Waschmaschine_PV Waschmaschine_Status Steckdose ist eingeschaltet")}\
{fhem("set Waschmaschine on")}\
)\
################################################################################################################\
## 6 Steckdose der Waschmaschine manuell abschalten.\
##\
DOELSEIF\
([Waschmaschine:Waschmaschine_Button] eq "off" and\
([$SELF:cmd_nr] eq "5" or [$SELF:cmd_nr] eq "7") )\
\
({Log 3, "Waschmaschine cmd_6 : Waschmaschine manuell aus"}\
{fhem("".ReadingsVal("Waschmaschine","SetCmdOff",0))}\
{fhem("setreading Waschmaschine_PV Waschmaschine_Status Steckdose ist ausgeschaltet")}\
{fhem("set Waschmaschine off")}\
)\
################################################################################################################\
## 7 Statuswechsel wenn das Waschprogramm gestartet ist\
##\
DOELSEIF\
([shelly03:power] > 0 and\
[shelly03:power_Waschmaschine_avg] < 70 and\
[$SELF:cmd_nr] ne "7")\
\
({Log 3, "Waschmaschine cmd_7 : Waschmaschine Programm gestarted"}\
{fhem("setreading Waschmaschine_PV Waschmaschine_Status Waschprogramm gestartet")}\
)\
################################################################################################################\
## 8 Abschalten der Steckdose beim Waschprogramm Ende\
##\
DOELSEIF\
([shelly03:power] == 0 and\
[shelly03:power_Waschmaschine_avg] > 0 and\
[$SELF:cmd_nr] eq "7" )\
\
({Log 3, "Waschmaschine cmd_8 : Waschmaschine aus"}\
{fhem("".ReadingsVal("Waschmaschine","SetCmdOff",0))}\
{fhem("set Waschmaschine off")}\
{fhem("setreading Waschmaschine Waschmaschine_Button off")}\
{fhem("setreading Waschmaschine_PV Waschmaschine_Status Waschprogramm beendet")}\
)\
################################################################################################################\
## 9 Abschalten der Steckdose wenn die Waschmaschine nicht gebraucht wurde\
##\
DOELSEIF\
([PV_Anlage_1:Total_PV_Power_reserve] < [Waschmaschine:PowerLimitOn] and\
[[Waschmaschine:TimeEnd]-[Waschmaschine:TimeStart]] and\
[Waschmaschine:state] eq "on" and\
[$SELF:cmd_nr] ne "5" and [$SELF:cmd_nr] ne "7" )\
\
({Log 3, "Waschmaschine cmd_9 : Eigenverbrauch sperren"}\
{fhem("".ReadingsVal("Waschmaschine","SetCmdOff",0))}\
{fhem("set Waschmaschine off")}\
{fhem("setreading Waschmaschine Waschmaschine_Button off")}\
{fhem("setreading Waschmaschine_PV Waschmaschine_Status Steckdose ist ausgeschaltet")}\
)
attr Waschmaschine_PV DbLogExclude .*
attr Waschmaschine_PV DbLogInclude state,STATE,cmd.*,Device,Waschmaschine_Status,wait_timer
attr Waschmaschine_PV alias Waschmaschine_PV
attr Waschmaschine_PV cmdState Maximalzeit pro Tag überschritten|Eigenverbrauch gesperrt|Stop wait timer|Eigenverbrauch freigegeben|Waschmaschine manuell ein|Waschmaschine manuell aus|Waschmaschine laeuft|Waschmaschine aus|Eigenverbrauch gesperrt
attr Waschmaschine_PV do always
attr Waschmaschine_PV group PV Eigenverbrauch-Steuerung
attr Waschmaschine_PV icon scene_washing_machine
attr Waschmaschine_PV room Strom->Photovoltaik
attr Waschmaschine_PV sortby 21
attr Waschmaschine_PV stateFormat state : Waschmaschine_Status
attr Waschmaschine_PV verbose 0
attr Waschmaschine_PV wait 0:0:0:[Waschmaschine:PowerLevelMinTime]:0:30:0:60:30
RAW Definition Waschmaschine_Signale (Shelly Modul: shelly1pm)
defmod shelly03 Shelly 192.168.178.54
attr shelly03 DbLogExclude .*
attr shelly03 DbLogInclude relay.*,power.*,energy.*
attr shelly03 alias Waschmaschine_Signale
attr shelly03 event-on-change-reading relay.*,power.*,energy.*,network
attr shelly03 group PV Eigenverbrauch-Steuerung
attr shelly03 icon taster_ch_1
attr shelly03 interval 60
attr shelly03 mode relay
attr shelly03 model shelly1pm
attr shelly03 room Shelly,Strom->Photovoltaik
attr shelly03 sortby 22
attr shelly03 stateFormat {sprintf("\
<TABLE>\
\
<TR>\
<TD VALIGN=\"TOP\" ALIGN=\"LEFT\" WIDTH=\"50\">\
WebLink: %s\
</TD>\
\
<TD VALIGN=\"TOP\" ALIGN=\"RIGHT\" WIDTH=\"100\">\
Gesamt 0: %08.2f KWh<br>\
</TD>\
\
<TD VALIGN=\"TOP\" ALIGN=\"RIGHT\" WIDTH=\"70\">\
Relais 0: %s %06.1f Watt<br>\
</TD>\
</TR>\
\
</TABLE>\
" ,\
ReadingsVal($name,"WebLink","none") ,\
ReadingsVal($name,"energy",0)/1000,\
(ReadingsVal($name,"relay","") eq "off") ? "<span style='color:#FF0000'>off</span>":"<span style='color:#00FF00'>on</span>",\
ReadingsVal($name,"power",0)\
)}
attr shelly03 userReadings WebLink:network { my $ip=ReadingsVal($NAME,"network","");; $ip =~ s/connected to //gs;; $ip =~ s/<[^>]*>//gs;; return("<html><a href='http://".$ip."/'>WEB</a></html>") },\
power_Waschmaschine_avg:power.* { movingAverage($NAME,"power",300) }
attr shelly03 webCmd |
RAW Definition Waschmaschine_Counter (HourCounter Modul)
defmod Waschmaschine_Counter HourCounter Waschmaschine_PV:.*laeuft Waschmaschine_PV:Waschmaschine_Status:.*[beendet|ausgeschaltet]
attr Waschmaschine_Counter DbLogExclude .*
attr Waschmaschine_Counter alias Waschmaschine_Counter
attr Waschmaschine_Counter event-on-change-reading .*
attr Waschmaschine_Counter group PV Eigenverbrauch-Steuerung
attr Waschmaschine_Counter icon time_timer
attr Waschmaschine_Counter interval 5
attr Waschmaschine_Counter room Strom->Photovoltaik
attr Waschmaschine_Counter sortby 23
attr Waschmaschine_Counter verbose 0
RAW Definition rg_Waschmaschine_Status (readingsGroup Modul)
defmod rg_Waschmaschine_Status readingsGroup <Device>,<Information>,<Wert> Waschmaschine:<Status>,!state Waschmaschine:<PowerLevelMinTime>,!PowerLevelMinTime Waschmaschine:<PowerLimitOn>,!PowerLimitOn Waschmaschine:<PowerLimitOff>,!PowerLimitOff Waschmaschine:<TimeStart>,!TimeStart Waschmaschine:<TimeEnd>,!TimeEnd Waschmaschine:<RunTimeMin>,!RunTimeMin Waschmaschine_Counter:<RunTimeMin>,!pulseTimeIncrement Waschmaschine:<RunTimePerDay>,!RunTimePerDay Waschmaschine_Counter:<RunTimePerDay>,!pulseTimePerDay Waschmaschine_PV:<wait>,wait_timer Waschmaschine_PV:<TimeStart>,timer_01_c04 Waschmaschine_PV:<TimeEnd>,timer_02_c04
attr rg_Waschmaschine_Status DbLogExclude .*
attr rg_Waschmaschine_Status alias Status Waschmaschine Eigenverbrauch
attr rg_Waschmaschine_Status commands {state => 'state:on,off',\
PowerLevelMinTime => 'PowerLevelMinTime:selectnumbers,30,30,300,0,lin',\
PowerLimitOn => 'PowerLimitOn:selectnumbers,250,250,2000,0,lin',\
PowerLimitOff => 'PowerLimitOff:selectnumbers,0,50,800,0,lin',\
RunTimeMin => 'RunTimeMin:selectnumbers,300,300,7200,0,lin',\
RunTimePerDay => 'RunTimePerDay:selectnumbers,7200,300,28800,0,lin',\
TimeStart => 'TimeStart:time',\
TimeEnd => 'TimeEnd:time'}
attr rg_Waschmaschine_Status group PV Status
attr rg_Waschmaschine_Status nameStyle style="color:grey"
attr rg_Waschmaschine_Status room Strom->Photovoltaik
attr rg_Waschmaschine_Status sortby 03
attr rg_Waschmaschine_Status style style="font-size:18px"
Problemlösung
Projekte der FHEM-Community
- Beitrag - Photovoltaik mit Eigenverbrauch Steuerung (Kostal plenticore; EM410)
- Beitrag - Kostal Plenticore Bilanz
- Beitrag - Kostal Plenticore, EM410 (KSEM), BYD, Solarprognose, Forecast
- Thema - Modul 93_DbRep - Reporting und Management von Datenbankinhalten (DbLog)
- Thema - Neue Version von HTTPMOD mit neuen Features zum Testen