Kostal Plenticore 10 Plus: Unterschied zwischen den Versionen
Keine Bearbeitungszusammenfassung |
Keine Bearbeitungszusammenfassung |
||
Zeile 69: | Zeile 69: | ||
5. Wetter Prognose | 5. Wetter Prognose | ||
== Statistiken == | |||
Der Plenticore erstellt intern noch diverse Statistiken, die auch über das WebGUI angesehen werden können. Diese werden jedoch nicht über MODBUS/TCP ausgegeben, jedoch auch 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. | |||
== 1.1.1 Einbindung in FHEM: Kostal Plenticore Plus die Basis information (Modbus/TCP) == | |||
== Einbindung in FHEM: Kostal Plenticore | |||
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. | 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. | ||
Zeile 78: | Zeile 79: | ||
Port : 1502 | Port : 1502 | ||
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 Wechselrichters | RAW Definition des Wechselrichters | ||
<pre> | <pre> | ||
Zeile 349: | Zeile 352: | ||
Wer keine Statistiken haben möchte kann das einfach entfernen. | Wer keine Statistiken haben möchte kann das einfach entfernen. | ||
== 1.1.2 Einbindung in FHEM: Kostal Plenticore Plus die Statistiken (über Python Skript) == | |||
fhem@raspberrypi:~/python/bin$ cat plenticore_statistic.py | |||
</pre> | |||
import random | |||
import string | |||
import base64 | |||
import json | |||
import requests | |||
import hashlib | |||
import os | |||
import hmac | |||
from Crypto.Cipher import AES | |||
import binascii | |||
import fhem | |||
import asyncio | |||
import sys | |||
plenticore = sys.argv[1] | |||
web = sys.argv[2] | |||
request = '/processdata/scb:statistic:EnergyFlow' | |||
== Einbindung in FHEM: KSEM mit MODBUS/TCP == | 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)) | |||
USER_TYPE = credentials["username"] | |||
PASSWD = credentials["password"] | |||
BASE_URL = "http://" + plenticore + "/api/v1" | |||
AUTH_START = "/auth/start" | |||
AUTH_FINISH = "/auth/finish" | |||
AUTH_CREATE_SESSION = "/auth/create_session" | |||
ME = "/auth/me" | |||
def randomString(stringLength): | |||
letters = string.ascii_letters | |||
return ''.join(random.choice(letters) for i in range(stringLength)) | |||
u = randomString(12) | |||
u = base64.b64encode(u.encode('utf-8')).decode('utf-8') | |||
step1 = { | |||
"username": USER_TYPE, | |||
"nonce": u | |||
} | |||
step1 = json.dumps(step1) | |||
url = BASE_URL + AUTH_START | |||
headers = {'Content-type': 'application/json', 'Accept': 'application/json'} | |||
response = requests.post(url, data=step1, headers=headers) | |||
response = json.loads(response.text) | |||
i = response['nonce'] | |||
e = response['transactionId'] | |||
o = response['rounds'] | |||
a = response['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) | |||
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') | |||
step2 = { | |||
"transactionId": e, | |||
"proof": proof | |||
} | |||
step2 = json.dumps(step2) | |||
url = BASE_URL + AUTH_FINISH | |||
headers = {'Content-type': 'application/json', 'Accept': 'application/json'} | |||
response = requests.post(url, data=step2, headers=headers) | |||
response = json.loads(response.text) | |||
token = response['token'] | |||
signature = response['signature'] | |||
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')) | |||
step3 = { | |||
"transactionId": e, | |||
"iv": base64.b64encode(t).decode('utf-8'), | |||
"tag": base64.b64encode(authtag).decode("utf-8"), | |||
"payload": base64.b64encode(e2).decode('utf-8') | |||
} | |||
step3 = json.dumps(step3) | |||
headers = { 'Content-type': 'application/json', 'Accept': 'application/json' } | |||
url = BASE_URL + AUTH_CREATE_SESSION | |||
response = requests.post(url, data=step3, headers=headers) | |||
response = json.loads(response.text) | |||
sessionId = response['sessionId'] | |||
#create a new header with the new Session-ID for all further requests | |||
headers = { 'Content-type': 'application/json', 'Accept': 'application/json', 'authorization': "Session " + sessionId } | |||
url = BASE_URL + ME | |||
response = requests.get(url = url, headers = headers) | |||
response = json.loads(response.text) | |||
authOK = response['authenticated'] | |||
if not authOK: | |||
print("authorization NOT OK") | |||
sys.exit() | |||
url = BASE_URL + "/info/version" | |||
response = requests.get(url = url, headers = headers) | |||
response = json.loads(response.text) | |||
swversion = response['sw_version'] | |||
apiversion = response['api_version'] | |||
hostname = response['hostname'] | |||
name = response['name'] | |||
print("Connected to the inverter " + name + "/" + hostname + " with SW-Version " + swversion + " and API-Version " + apiversion) | |||
# Auth OK, now send your desired requests | |||
url = BASE_URL + request | |||
response = requests.get(url = url, headers = headers) | |||
response = json.loads(response.text) | |||
message = json.dumps(response) | |||
#print(json.dumps(response, indent=4, sort_keys=True)) | |||
#print(message) | |||
async def run(): | |||
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 statistics_output " + message) | |||
asyncio.get_event_loop().run_until_complete(run()) | |||
</pre> | |||
'''Userreadings''' | |||
Die userreading gehören zum Gerät PV_Anlage_1 . | |||
statistics_clean | |||
Trigger: statistics_output.* | |||
Dies ist ein temporäres reading, das nur als zwischen Schritt für die Erstellung der Statistics_* readings verwendet wird. Befüllt wird zuerst | |||
das reading statistics_output durch ein regelmäßig laufendes Python Skript. Auch dies wird noch später behandelt. | |||
statistics_output | |||
Trigger: externes Python Skript | |||
Das userreading wird über ein Python Skript mit Daten gefüllt und dient als Trigger für statistics_clean | |||
'''Passworte''' | |||
Die Passworte für den Plenticore und den FHEM Zugang liegen in einzelnen JSON Dateien | |||
fhem@raspberrypi:~/python$ cat pwd_plenticore.json | |||
<pre> | |||
{ | |||
"username": "user", | |||
"password": "<Steht auf dem Gehäuse>" | |||
} | |||
</pre> | |||
fhem@raspberrypi:~/python$ cat pwd_fhem.json | |||
<pre> | |||
{ | |||
"username": "<Ein Fhem Telnet User>", | |||
"password": "<Das Passwort des Users>" | |||
} | |||
</pre> | |||
== 1.2 Einbindung in FHEM: KSEM mit MODBUS/TCP == | |||
Diese Einbindung ist nicht zwingend notwendig, jedoch weil es möglich ist hier beschrieben. | 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 intervall von 60 Sekunden gesetzt worden. Das Logging ist noch komplett deaktiviert, weshalb man seine Werte noch selber definieren muss. | |||
RAW Definition des KSEM | |||
<pre> | |||
defmod PV_KSEM ModbusAttr 1 60 192.168.178.17:502 TCP | |||
attr PV_KSEM DbLogExclude .* | |||
attr PV_KSEM alias PV_Energy_Manager | |||
attr PV_KSEM comment Der KSEM ermittelt nicht alle Werte, welche in der SunSpec spezifiziert sind.\ | |||
Alle nicht unterstützen Werte sind mit 0x8000 gekennzeichnet.\ | |||
Für die nicht unterstützten Zählerstände wird die 0x800000000 ausgegeben.\ | |||
\ | |||
Der Summenstrom M_AC_Current (sum of active phases) kann aber durch den Endanwender selber\ | |||
berechnet werden aus der Summe der Einzelwerte (Phase A AC current, Phase B AC current Phase C AC current)\ | |||
\ | |||
Die einzelnen Spannungen zwischen den Phasen können nicht gemessen werden und werden deshalb nicht ausgegeben. | |||
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 | |||
</pre> | |||
== Einbindung in FHEM: BYD Speicher über eine Python Skript Abfrage == | == Einbindung in FHEM: BYD Speicher über eine Python Skript Abfrage == | ||
Zeile 358: | Zeile 726: | ||
Diese Einbindung ist nicht zwingend notwendig, jedoch weil es möglich ist hier beschrieben. | Diese Einbindung ist nicht zwingend notwendig, jedoch weil es möglich ist hier beschrieben. | ||
Version vom 27. August 2020, 15:46 Uhr
{{Infobox Hardware |Bild=|200px |Bildbeschreibung=Kostal Plenticore Plus mit BYD und KSEM |HWProtocol=IP |HWType=Hybrid Wechselrichter |HWCategory=Wechselrichter |HWVoltage=400 V |HWPoweredBy=3P AC |HWSize=39x67x45 cm (BxHxT) |HWComm=n/a |HWChannels=n/a |HWPowerConsumption=? |HWDeviceFHEM=[[HTTPMOD],[Modbus]] |HWManufacturer=Kostal GmbH }}
Achtung, diese Wiki Seite ist noch eine totale Baustelle :-) Momentan werden die Informationen noch aus den Threads im Forum hierher übertragen.
Der Kostal Plenticore 10 Plus [1] ist eine Hybrip 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!
Vorausetzungen 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.
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 deer 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
1. Alle Geräte müssen mit TCP/IP erreichbar sein 2. Alle Module sollten auf einem aktuellen stand sein 3. Python 3.1 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. 3.2 Es müssen folgende Python Module vorhanden sein
pip3 install pycryptodome pip3 install -U fhem
4. Eine LogDB/LogDBRep sollte bereits vorhanden sein, was hier nicht weiter erklärt wird.
Einbindung in FHEM: Überblick
1. Hardware Anbindung (alles über LAN) 1.1.1 Kostal Plenticore Plus die Basis information (Modbus/TCP) 1.1.2 Kostal Plenticore Plus die Statistiken (über Python Skript) 1.2 Kostel Smart Energy Manager (KSEM) (Modbus/TCP) 1.3 BYD Speicher (über Python Skript)
2. PV Eigenverbrauch-Steuerung
3. Energie Bilanz
4. Diagramme
5. Wetter Prognose
Statistiken
Der Plenticore erstellt intern noch diverse Statistiken, die auch über das WebGUI angesehen werden können. Diese werden jedoch nicht über MODBUS/TCP ausgegeben, jedoch auch 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.
1.1.1 Einbindung in FHEM: 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. GeräteId : 71 IP-Adresse: <IP-Adresse> Port : 1502
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 Wechselrichters
defmod PV_Anlage_1 ModbusAttr 71 60 192.168.178.18: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_.*,Statistic_.* 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 statistics_.*,Statistic_.*,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) },\ \ Solar_SolarRadiation:Total_DC_Power.* { my $x1 = POSIX::strftime("%Y-%m-%d_%H_00_Rad1wh",localtime(time-60*60)) ;;;; my $x2 = POSIX::strftime("%Y-%m-%d_%H_00_Rad1wh",localtime()) ;;;; my $x_avg = round((ReadingsVal("DWD_Prognose",$x1,0)+ReadingsVal("DWD_Prognose",$x2,0))/2 , 0) ;;;; my $time = POSIX::strftime("%M",localtime()) ;;;; ($time < 30)?ReadingsVal("DWD_Prognose",$x1,0):$x_avg },\ \ 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 } 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 ???
Solar_SolarRadiation
Trigger: Total_DC_Power.* Wer noch keine Wetter prognose verwendet kann dieses reading entfernen. Das wird später noch behandelt.
statistics_clean
Trigger: statistics_output.* Dies ist ein temporäres reading, das nur als zwischen Schritt für die Erstellung der Statistics_* readings verwendet wird. Befüllt wird zuerst das reading statistics_output durch ein regelmäßig laufendes Python Skript. Auch dies wird noch später behandelt. Wer keine Statistiken haben möchte kann das einfach entfernen.
1.1.2 Einbindung in FHEM: Kostal Plenticore Plus die Statistiken (über Python Skript)
fhem@raspberrypi:~/python/bin$ cat plenticore_statistic.py
import random import string import base64 import json import requests import hashlib import os import hmac from Crypto.Cipher import AES import binascii
import fhem import asyncio
import sys plenticore = sys.argv[1] web = sys.argv[2] request = '/processdata/scb:statistic:EnergyFlow'
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))
USER_TYPE = credentials["username"] PASSWD = credentials["password"] BASE_URL = "http://" + plenticore + "/api/v1" AUTH_START = "/auth/start" AUTH_FINISH = "/auth/finish" AUTH_CREATE_SESSION = "/auth/create_session" ME = "/auth/me"
def randomString(stringLength):
letters = string.ascii_letters return .join(random.choice(letters) for i in range(stringLength))
u = randomString(12) u = base64.b64encode(u.encode('utf-8')).decode('utf-8')
step1 = {
"username": USER_TYPE, "nonce": u
} step1 = json.dumps(step1)
url = BASE_URL + AUTH_START headers = {'Content-type': 'application/json', 'Accept': 'application/json'} response = requests.post(url, data=step1, headers=headers) response = json.loads(response.text) i = response['nonce'] e = response['transactionId'] o = response['rounds'] a = response['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) 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')
step2 = {
"transactionId": e, "proof": proof
} step2 = json.dumps(step2)
url = BASE_URL + AUTH_FINISH headers = {'Content-type': 'application/json', 'Accept': 'application/json'} response = requests.post(url, data=step2, headers=headers) response = json.loads(response.text) token = response['token'] signature = response['signature']
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'))
step3 = {
"transactionId": e, "iv": base64.b64encode(t).decode('utf-8'), "tag": base64.b64encode(authtag).decode("utf-8"), "payload": base64.b64encode(e2).decode('utf-8')
} step3 = json.dumps(step3)
headers = { 'Content-type': 'application/json', 'Accept': 'application/json' } url = BASE_URL + AUTH_CREATE_SESSION response = requests.post(url, data=step3, headers=headers) response = json.loads(response.text) sessionId = response['sessionId']
- create a new header with the new Session-ID for all further requests
headers = { 'Content-type': 'application/json', 'Accept': 'application/json', 'authorization': "Session " + sessionId } url = BASE_URL + ME response = requests.get(url = url, headers = headers) response = json.loads(response.text) authOK = response['authenticated'] if not authOK:
print("authorization NOT OK") sys.exit()
url = BASE_URL + "/info/version" response = requests.get(url = url, headers = headers) response = json.loads(response.text) swversion = response['sw_version'] apiversion = response['api_version'] hostname = response['hostname'] name = response['name'] print("Connected to the inverter " + name + "/" + hostname + " with SW-Version " + swversion + " and API-Version " + apiversion)
- Auth OK, now send your desired requests
url = BASE_URL + request
response = requests.get(url = url, headers = headers) response = json.loads(response.text)
message = json.dumps(response)
- print(json.dumps(response, indent=4, sort_keys=True))
- print(message)
async def run():
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 statistics_output " + message)
asyncio.get_event_loop().run_until_complete(run())
Userreadings Die userreading gehören zum Gerät PV_Anlage_1 .
statistics_clean
Trigger: statistics_output.* Dies ist ein temporäres reading, das nur als zwischen Schritt für die Erstellung der Statistics_* readings verwendet wird. Befüllt wird zuerst das reading statistics_output durch ein regelmäßig laufendes Python Skript. Auch dies wird noch später behandelt.
statistics_output
Trigger: externes Python Skript Das userreading wird über ein Python Skript mit Daten gefüllt und dient als Trigger für statistics_clean
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>" }
1.2 Einbindung in FHEM: KSEM mit 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 intervall von 60 Sekunden gesetzt worden. Das Logging ist noch komplett deaktiviert, weshalb man seine Werte noch selber definieren muss.
RAW Definition des KSEM
defmod PV_KSEM ModbusAttr 1 60 192.168.178.17:502 TCP attr PV_KSEM DbLogExclude .* attr PV_KSEM alias PV_Energy_Manager attr PV_KSEM comment Der KSEM ermittelt nicht alle Werte, welche in der SunSpec spezifiziert sind.\ Alle nicht unterstützen Werte sind mit 0x8000 gekennzeichnet.\ Für die nicht unterstützten Zählerstände wird die 0x800000000 ausgegeben.\ \ Der Summenstrom M_AC_Current (sum of active phases) kann aber durch den Endanwender selber\ berechnet werden aus der Summe der Einzelwerte (Phase A AC current, Phase B AC current Phase C AC current)\ \ Die einzelnen Spannungen zwischen den Phasen können nicht gemessen werden und werden deshalb nicht ausgegeben. 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
Einbindung in FHEM: BYD Speicher über eine Python Skript Abfrage
Diese Einbindung ist nicht zwingend notwendig, jedoch weil es möglich ist hier beschrieben.
Problemlösung
Projekte der FHEM-Community
- Beitrag - Photovoltaik mit Eigenverbrauch Steuerung (Kostal plenticore; EM410)
- Beitrag - Kostal Plenticore Bilanz