From 5319a299bec3c2033547b5f2e7433455a8ff4b4c Mon Sep 17 00:00:00 2001 From: Nils Reiners Date: Tue, 16 Sep 2025 12:52:27 +0200 Subject: [PATCH] inverter was included --- README | 38 ++++++++++- __pycache__/data_base_csv.cpython-312.pyc | Bin 2827 -> 2827 bytes __pycache__/heat_pump.cpython-312.pyc | Bin 3566 -> 3666 bytes __pycache__/make_tunnel.cpython-312.pyc | Bin 0 -> 803 bytes __pycache__/pv_inverter.cpython-312.pyc | Bin 0 -> 3680 bytes __pycache__/shelly_pro_3m.cpython-312.pyc | Bin 3719 -> 3819 bytes heat_pump.py | 5 +- main.py | 14 ++-- make_tunnel.py | 22 +++++++ modbus_registers/pv_inverter_registers.xlsx | Bin 0 -> 11749 bytes pv_inverter.py | 68 ++++++++++++++++++++ requirements.txt | 3 +- shelly_pro_3m.py | 5 +- test_wr.py | 2 +- 14 files changed, 143 insertions(+), 14 deletions(-) create mode 100644 __pycache__/make_tunnel.cpython-312.pyc create mode 100644 __pycache__/pv_inverter.cpython-312.pyc create mode 100644 make_tunnel.py create mode 100644 modbus_registers/pv_inverter_registers.xlsx create mode 100644 pv_inverter.py diff --git a/README b/README index f4d8eb3..3fb597d 100644 --- a/README +++ b/README @@ -11,10 +11,42 @@ Was needs to be done on the Raspberry pi before the tool can run. - pip install -r requirements.txt -How to run the script: +3) How to run the script for testing: -- nohup python main.py > terminal_log 2>&1 & + nohup python main.py > terminal_log 2>&1 & For reading out the terminal_log while script is runing: -- tail -f terminal_log + tail -f terminal_log + + +4) Implement and run the ems as systemd service: +create: + /etc/systemd/system/allmende_ems.service + +insert: + [Unit] + Description=Allmende EMS Python Script + After=network.target + + [Service] + WorkingDirectory=/home/pi/projects/allmende_ems + ExecStart=/home/pi/allmende_ems/bin/python3.11 /home/pi/projects/allmende_ems/main.py + Restart=always + RestartSec=5 + StandardOutput=journal + StandardError=journal + + [Install] + WantedBy=multi-user.target + +manage the service with the following commands: +Once: + sudo systemctl daemon-reload + sudo systemctl start allmende_ems.service + sudo systemctl enable allmende_ems.service +While running: + sudo systemctl status allmende_ems.service + sudo systemctl restart allmende_ems.service + sudo systemctl stop allmende_ems.service + journalctl -u allmende_ems.service diff --git a/__pycache__/data_base_csv.cpython-312.pyc b/__pycache__/data_base_csv.cpython-312.pyc index a9f08031a9f53d0ead35497d17c7f0573cac9b77..e276725e3de3d39780f4654049ce9cd10458cb49 100644 GIT binary patch delta 23 dcmeAc>lWiX&CAQh00d^M_GAdNZ{+*S1pq^T1|$Fg delta 23 dcmeAc>lWiX&CAQh00b&6Oc}q~HuC-D0suh}1{nYV diff --git a/__pycache__/heat_pump.cpython-312.pyc b/__pycache__/heat_pump.cpython-312.pyc index 589611a6dd95462866b88126257a783e9cb1fdb6..6cb73db44f89071eb7847918b6ff4d82d369378d 100644 GIT binary patch delta 908 zcmZ8fPe>GD6o23RA9rWAT{l;D*INHzje=dmi(s`LiUKJ*M0-(lM%&O_z8TStjfy19 z!jSI}bn03b7(!>UQ*?_6*)DYsB3vVEKZw?C*vfS;Dj$s2tyF$d zd8lutTTz+foOJ34XG9!lz`+0ICJT0_6tCpWp&EO^R{yg2mw`$7nz&##vye&FZwX?% z#aw9>TaYjaETPFg%3gGmcy|!NP6+HM3ZM(Pjj{U`RPjX4v8X|ql4dXm^YUewV~#fL zuxKH_09%dSXTNo2{LJGK8;@}@B{P^7oU{FDf0PC2)L(2^P}qhsxZ`h z9nS}f0Gp65X5`JZRLsI=3nM!tlby{M``6dhODyS3&$w4+{Sod>mi@rvzKH7^hK2R; zizUPd{j?S~vqig+v-8yKW!UWV!ISG}4O6x#wyu@~Z`#LkM`JxKAs)mAyC|)0PI|$J zAB+Xwjno-$EI2XTT{s z;!l4XC!pi^A}qkRq^oUDhrR=C7DUmCXM7%`=c;XvI-IW(zs9&ir~UFinVm6+F~`#Oi7gpE=uXb zO}Pu{M!GF3e!6p|-H8ZN1j}qJxR=s^8yEU+(ufz{x#yjC-{IVy{Lh^A$uuKG*802h zogID6T3Ra@<&qSh5|`AdLu$;SE_ayAXNAL;MNK$jnbZ_Vk*GSVMD2lQ>uVo}Gcz2m z4vWc-_f``OIv96IlT4x3t|q)O3Moq3s`#gp`6xW&{gzF~ZPiny90RReaTt?DphW#S;x0Y%G0~;vx=xL<7k>)r5kY$-H3C z55xQ)%Yg4J18TsFPXrmp{U>Yg2f`-6a6qDW=!3~Jv=n=BJf#MG8WQa2|gwq81UjK zL?NpjweZbfks$Yt_rPUkWoYBnScQD3q^c(0j<9NiY{w{alOs>$tH-xm$~CGKoata1 zu50ggT)@^m3x@RzYTMj|x}LLgX#TQozLzdPjUz9s8pt<=D8b!zHsgk&xVrBA5v0By187CpsG{$Pn=O zBNCR$K2w###`%3B+v}?>YzK*`GH{ShDv{P`Qb~D9_-39_J2sHDj+rNE>VQaSTmJ-o6rbttmX^}RML{DFIjOz0EFp*=F);yx39w{~G3li#%)qYpD>Jhp5KO9) z_z#%yFKFN&FTBO`e(%kD^YPw%H_bsH|A=b7>)n}2!7#<*$oK7WZ$emp0Vq5^LE{?&(58XiAb)bZLX7mrkj|My?wntZOgAd-Tn;$*b;gz znqVcNy$=G80K1-J{SyOL?niGcOzm?fGjn12ttDkcg$ICb1;GP>C(3c9@V$cv@Xc_4 z{_l8vMIktVF0e{#;OKh-AAMknPzdH=*XotW)TJos|! zblm=Bs8uyvx~PJR@oDA#%HdkqsAa1?kqy=?o$I28#a4M8i0l%P8<465*%Wd^LQR6) z{VO21B_9=VsXdDl)Cuu(a#xu0flx=lMF~Rq13bJ?!O(c7bq!O(u!ONp6JGO9rgy8g M%;=SMIFc{;2VsiBUjP6A literal 0 HcmV?d00001 diff --git a/__pycache__/pv_inverter.cpython-312.pyc b/__pycache__/pv_inverter.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9cccd2daf7c43a6621fdc71e8f9ae6aa1abf848e GIT binary patch literal 3680 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 literal 0 HcmV?d00001 diff --git a/__pycache__/shelly_pro_3m.cpython-312.pyc b/__pycache__/shelly_pro_3m.cpython-312.pyc index d2250f0bfb3c24c71a77f938cc415180c5d4334b..c74d295fb4bbf5d3deaa4cf128fa4022a67c882e 100644 GIT binary patch delta 942 zcmZuv-Afcv6u)=weC|54?zZc$`(dk;vW7wwQUrl(;Y&y$J-8rCIpeybxZIhrjg2g% zOoEb2^cKB^=|kZ^5cU-I*n{j-JquAT1if|6Oqxgs=6B9LANQQ|oBKGom$P%#T4Z2A;{;qIzd;_a-w4)*>)g-K> zr`--Od=Rju4d{kKiD!xw>refs6vDGnQDfa1%g+TkMYshe|1MI*Cd(v_H$AA##umvP zHbE9?K^-g5fUb7=LCo+?j!SWT2|O%b$wSEqn#ie(>*e#j9UX0APwt6D(Y*=)EJjW| zt3RoWjPj`G?WH0i+az90vhBE3Oq1;niguEroz51AwmvF1XmntH!M=6dEpw+*bv=i> zBB#C4O}G#KXGDC~MpeI=B|7yks|%a0`^~;4u54Y^%gn^wrc(8sc^AfgA0DL<@jE=) zhheJcjLbLz*>0PkA;=|embgzTD=h^ l9^8Y~u delta 760 zcmYjOOK1~O6n$^z%_pCke439mnUa(mm{3B&5(}=HF5*IhPz24Qp*qthCAH;cpteNp zBGd|Q;uG9kJuO7?MiPB(GMl&Q>hSE%i?k2oI_UDB}Y zTSJj5{GddRmx;_N`Jvvp3@1;?YlXcMR-+pjHnH$?>~J=!CR>u@@ejiJ zc$(?CJnXL!XB|(HG-8g2Jr%Q2$1}NTN$2Ab3^rjWz-LV-{FCzqM0Jd&{4)C^CfGk= zW@Jsy9-wF9BXodGrBwURvrO!g{XOa&EwUHHU^VhsQz@)P;Zf>kKS@5W1D)Z}fPqsf z8UVq^veLXB1o?ymUq29L8`8=A*`3uq`kapxH1WbK6oTl&kAZ6<@L;mA*puZ4SV~)) z&L*oX?^Rg9RH)7pYEd4BYpkly=>~*%hT*@WE4{aBMbyCSN;SvcZBXwUe-Z2KuD^H_ zZn^5V?NlK3D6(F)>-EF?bnn(~ZlW&rKKWx<32|%5RGF(~AHw+52cQA0ap)ilj?t15 zFo5CzUf`sKt^`>!&epCFZ{76)w0_VhrTViW$vemdqzDbMs$P&sF|4cSv=tobBM!sp EYctE2#Q*>R diff --git a/heat_pump.py b/heat_pump.py index 008dc4c..773e84a 100644 --- a/heat_pump.py +++ b/heat_pump.py @@ -3,16 +3,17 @@ import pandas as pd import time class HeatPump: - def __init__(self, device_name: str, ip_address: str): + def __init__(self, device_name: str, ip_address: str, port: int=502): self.device_name = device_name self.ip = ip_address + self.port = port self.client = None self.connect_to_modbus() self.registers = None self.get_registers() def connect_to_modbus(self): - port = 502 + port = self.port self.client = ModbusTcpClient(self.ip, port=port) try: if not self.client.connect(): diff --git a/main.py b/main.py index 3642708..1df6135 100644 --- a/main.py +++ b/main.py @@ -3,22 +3,26 @@ from datetime import datetime from data_base_csv import DataBaseCsv from data_base_influx import DataBaseInflux from heat_pump import HeatPump +from pv_inverter import PvInverter from shelly_pro_3m import ShellyPro3m +# For dev-System run in terminal: ssh -N -L 127.0.0.1:8111:10.0.0.10:502 pi@192.168.1.146 +# For productive-System change port in heatpump to 502 + interval_seconds = 10 db = DataBaseInflux( - url="http://localhost:8086", + url="http://192.168.1.146:8086", token="Cw_naEZyvJ3isiAh1P4Eq3TsjcHmzzDFS7SlbKDsS6ZWL04fMEYixWqtNxGThDdG27S9aW5g7FP9eiq5z1rsGA==", org="allmende", bucket="allmende_db" ) -hp = HeatPump(device_name='hp_master', ip_address='10.0.0.10') +hp = HeatPump(device_name='hp_master', ip_address='localhost', port=8111) shelly = ShellyPro3m(device_name='wohnung_2_6', ip_address='192.168.1.121') -wr = SolarEdgeWechselrichter(device_name='wr_master', ip_address='192.168.1.112') +wr = PvInverter(device_name='wr_master', ip_address='192.168.1.112') -controller = SgReadyController(hp, wr) +#controller = SgReadyController(hp, wr) while True: now = datetime.now() @@ -26,6 +30,6 @@ while True: db.store_data(hp.device_name, hp.get_state()) db.store_data(shelly.device_name, shelly.get_state()) db.store_data(wr.device_name, wr.get_state()) - controller.perform_action() + #controller.perform_action() time.sleep(0.1) diff --git a/make_tunnel.py b/make_tunnel.py new file mode 100644 index 0000000..3adb257 --- /dev/null +++ b/make_tunnel.py @@ -0,0 +1,22 @@ +from sshtunnel import SSHTunnelForwarder + +# ---- KONFIG ---- +SSH_HOST = "192.168.1.146" # Raspberry Pi im 192.168.1.x Netz +SSH_PORT = 22 +SSH_USER = "pi" +PASSWORD = 'raspberry' # oder Passwort als String + + +REMOTE_IP = "10.0.0.10" # Wärmepumpe im 10.0.0.x Netz +REMOTE_PORT = 502 # Modbus/TCP Port + +def make_tunnel(port): + tunnel = SSHTunnelForwarder( + (SSH_HOST, SSH_PORT), + ssh_username=SSH_USER, + ssh_password=PASSWORD, + remote_bind_address=(REMOTE_IP, REMOTE_PORT), + local_bind_address=("127.0.0.1", port), + ) + tunnel.start() + return tunnel \ No newline at end of file diff --git a/modbus_registers/pv_inverter_registers.xlsx b/modbus_registers/pv_inverter_registers.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..ed66134e37087d31bb328ccdc17ff59625423499 GIT binary patch literal 11749 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 I0{`@X0DlETpa1{> literal 0 HcmV?d00001 diff --git a/pv_inverter.py b/pv_inverter.py new file mode 100644 index 0000000..365d7fb --- /dev/null +++ b/pv_inverter.py @@ -0,0 +1,68 @@ +import time +import pandas as pd +from pymodbus.client import ModbusTcpClient + +class PvInverter: + def __init__(self, device_name: str, ip_address: str, port: int = 502, unit: int = 1): + self.device_name = device_name + self.ip = ip_address + self.port = port + self.unit = unit + self.client = None + self.registers = None + + self.connect_to_modbus() + self.get_registers() + + def connect_to_modbus(self): + # Timeout & retries optional, aber hilfreich: + self.client = ModbusTcpClient(self.ip, port=self.port, timeout=3.0, retries=3) + if not self.client.connect(): + print("Verbindung zu Wechselrichter fehlgeschlagen.") + raise SystemExit(1) + print("Verbindung zu Wechselrichter erfolgreich.") + + # WICHTIG: NICHT hier schließen! + # finally: self.client.close() <-- entfernen + + def close(self): + if self.client: + self.client.close() + self.client = None + + def get_registers(self): + excel_path = "modbus_registers/pv_inverter_registers.xlsx" + xls = pd.ExcelFile(excel_path) + df_input_registers = xls.parse() + df_clean = df_input_registers[['MB Adresse', 'Beschreibung', 'Variabel Typ']].dropna() + df_clean['MB Adresse'] = df_clean['MB Adresse'].astype(int) + + self.registers = { + row['MB Adresse']: { + 'desc': row['Beschreibung'], + 'type': 'REAL' if str(row['Variabel Typ']).upper() == 'REAL' else 'INT' + } + for _, row in df_clean.iterrows() + } + + def get_state(self): + data = {'Zeit': time.strftime('%Y-%m-%d %H:%M:%S')} + for address, info in self.registers.items(): + reg_type = info['type'] + # Unit-ID mitgeben (wichtig bei pymodbus>=3) + result = self.client.read_holding_registers( + address=address, + count=2 if reg_type == 'REAL' else 1, + slave=self.unit # pymodbus 2.x -> 'slave', nicht 'unit' + ) + if result.isError(): + print(f"Fehler beim Lesen von Adresse {address}: {result}") + continue + + # Minimal invasiv: wie bei dir – erstes Register verwenden + value = result.registers[0] + print(f"Adresse {address} - {info['desc']}: {value}") + data[f"{address} - {info['desc']}"] = value + return data + + diff --git a/requirements.txt b/requirements.txt index aa87620..08e3db0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ pymodbus~=3.8.6 pandas -openpyxl \ No newline at end of file +openpyxl +sshtunnel \ No newline at end of file diff --git a/shelly_pro_3m.py b/shelly_pro_3m.py index d1cee53..9ba8b76 100644 --- a/shelly_pro_3m.py +++ b/shelly_pro_3m.py @@ -5,16 +5,17 @@ import pandas as pd import time class ShellyPro3m: - def __init__(self, device_name: str, ip_address: str): + def __init__(self, device_name: str, ip_address: str, port: int=502): self.device_name = device_name self.ip = ip_address + self.port = port self.client = None self.connect_to_modbus() self.registers = None self.get_registers() def connect_to_modbus(self): - port = 502 + port = self.port self.client = ModbusTcpClient(self.ip, port=port) try: if not self.client.connect(): diff --git a/test_wr.py b/test_wr.py index 4da815f..26a240b 100644 --- a/test_wr.py +++ b/test_wr.py @@ -4,7 +4,7 @@ import sys from typing import Optional # === Verbindungseinstellungen === -MODBUS_IP = "192.168.1.107" +MODBUS_IP = "192.168.1.112" MODBUS_PORT = 502 # SetApp: 1502; LCD-Menü: 502 -> ggf. anpassen UNIT_ID = 1 # Default laut Doku: 1