Skip to content

Commit

Permalink
Merge pull request #49 from HotNoob/v1.1.5
Browse files Browse the repository at this point in the history
V1.1.5
  • Loading branch information
HotNoob authored Sep 6, 2024
2 parents aea29e6 + 419aec4 commit ed1d92f
Show file tree
Hide file tree
Showing 74 changed files with 1,143 additions and 11 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ srne_v3.9 = SRNE inverters - Untested
victron_gx_3.3 = Victron GX Devices - Untested
solark_v1.1 = SolarArk 8/12K Inverters - Untested
hdhk_16ch_ac_module = some chinese current monitoring device :P
srne_2021_v1.96 = SRNE inverters 2021+ (tested at ASF48100S200-H)
eg4_v58 = eg4 inverters ( EG4-6000XP ) - confirmed working
eg4_3000ehv_v1 = eg4 inverters ( EG4_3000EHV )
Expand Down
29 changes: 26 additions & 3 deletions classes/protocol_settings.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import csv
from dataclasses import dataclass
from enum import Enum
import glob
from typing import Union
from defs.common import strtoint
import itertools
Expand Down Expand Up @@ -300,7 +301,12 @@ def load__json(self, file : str = '', settings_dir : str = ''):
if not file:
file = self.protocol + '.json'

path = settings_dir + '/' + file
path = self.find_protocol_file(file, settings_dir)

#if path does not exist; nothing to load. skip.
if not path:
print("ERROR: '"+file+"' not found")
return

with open(path) as f:
self.codes = json.loads(f.read())
Expand Down Expand Up @@ -618,6 +624,23 @@ def calculate_registry_ranges(self, map : list[registry_map_entry], max_register
ranges.append((min(registers), max(registers)-min(registers)+1)) ## APPENDING A TUPLE!

return ranges
def find_protocol_file(self, file : str, base_dir : str = '' ) -> str:

path = base_dir + '/' + file
if os.path.exists(path):
return path

suffix = file.split('_', 1)[0]

path = base_dir + '/' + suffix +'/' + file
if os.path.exists(path):
return path

#find file by name, recurisvely. last resort
search_pattern = os.path.join(base_dir, '**', file)
matches = glob.glob(search_pattern, recursive=True)
return matches[0] if matches else None


def load_registry_map(self, registry_type : Registry_Type, file : str = '', settings_dir : str = ''):
if not settings_dir:
Expand All @@ -629,10 +652,10 @@ def load_registry_map(self, registry_type : Registry_Type, file : str = '', sett
else:
file = self.protocol + '.'+registry_type.name.lower()+'_registry_map.csv'

path = settings_dir + '/' + file
path = self.find_protocol_file(file, settings_dir)

#if path does not exist; nothing to load. skip.
if not os.path.exists(path):
if not path:
return

self.registry_map[registry_type] = self.load__registry(path, registry_type)
Expand Down
42 changes: 37 additions & 5 deletions classes/transports/modbus_rtu.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import logging
from classes.protocol_settings import Registry_Type, protocol_settings
from pymodbus.client.sync import ModbusSerialClient

import inspect


try:
from pymodbus.client.sync import ModbusSerialClient
except ImportError:
from pymodbus.client import ModbusSerialClient

from .modbus_base import modbus_base
from configparser import SectionProxy
from defs.common import find_usb_serial_port, get_usb_serial_port_info, strtoint
Expand All @@ -11,6 +19,8 @@ class modbus_rtu(modbus_base):
baudrate : int = 9600
client : ModbusSerialClient

pymodbus_slave_arg = 'unit'

def __init__(self, settings : SectionProxy, protocolSettings : protocol_settings = None):
#logger = logging.getLogger(__name__)
#logging.basicConfig(level=logging.DEBUG)
Expand All @@ -33,16 +43,34 @@ def __init__(self, settings : SectionProxy, protocolSettings : protocol_settings
address : int = settings.getint("address", 0)
self.addresses = [address]

self.client = ModbusSerialClient(method='rtu', port=self.port,
baudrate=int(self.baudrate),
stopbits=1, parity='N', bytesize=8, timeout=2
)
# pymodbus compatability; unit was renamed to address
if 'slave' in inspect.signature(ModbusSerialClient.read_holding_registers).parameters:
self.pymodbus_slave_arg = 'slave'


# Get the signature of the __init__ method
init_signature = inspect.signature(ModbusSerialClient.__init__)

if 'method' in init_signature.parameters:
self.client = ModbusSerialClient(method='rtu', port=self.port,
baudrate=int(self.baudrate),
stopbits=1, parity='N', bytesize=8, timeout=2
)
else:
self.client = ModbusSerialClient(port=self.port,
baudrate=int(self.baudrate),
stopbits=1, parity='N', bytesize=8, timeout=2
)

def read_registers(self, start, count=1, registry_type : Registry_Type = Registry_Type.INPUT, **kwargs):

if 'unit' not in kwargs:
kwargs = {'unit': int(self.addresses[0]), **kwargs}

#compatability
if self.pymodbus_slave_arg != 'unit':
kwargs['slave'] = kwargs.pop('unit')

if registry_type == Registry_Type.INPUT:
return self.client.read_input_registers(start, count, **kwargs)
elif registry_type == Registry_Type.HOLDING:
Expand All @@ -55,6 +83,10 @@ def write_register(self, register : int, value : int, **kwargs):
if 'unit' not in kwargs:
kwargs = {'unit': self.addresses[0], **kwargs}

#compatability
if self.pymodbus_slave_arg != 'unit':
kwargs['slave'] = kwargs.pop('unit')

self.client.write_register(register, value, **kwargs) #function code 0x06 writes to holding register

def connect(self):
Expand Down
1 change: 1 addition & 0 deletions documentation/.scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
folder for scripts releated to documentation
94 changes: 94 additions & 0 deletions documentation/.scripts/generate_indexes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import os
import urllib


def extract_first_header(file_path):
"""Extract the first header from a markdown file."""
with open(file_path, "r", encoding="utf-8") as file:
for line in file:
line = line.strip()
if line.startswith("#"):
return line.replace('#', '')
return None


def generate_readme(directory : str, folder_order : str = [], output_file : str ="README.md"):
with open(directory+'/'+output_file, "w", encoding="utf-8") as readme:
readme.write("# README Index\n\n")
readme.write("This README file contains an index of all files in the documentation directory.\n\n")
readme.write("## File List\n\n")

note_file : str = directory+'/note.md'
if os.path.exists(note_file):
readme.write("\n## Additional Notes\n\n")
with open(note_file, "r", encoding="utf-8") as note:
readme.write(note.read())


previous_folder = ""

folder_lines : dict[str, list[str]] = {}

for root, dirs, files in os.walk(directory):
relative_folder = os.path.relpath(root, directory).replace("\\", "/") #use linux path structure

#exclude . folders
if relative_folder[0] == '.':
continue

if relative_folder != previous_folder:
# Create a bold header for each new folder
folder_lines[relative_folder] = []
folder_lines[relative_folder].append(f"**{relative_folder}**\n\n")

previous_folder = relative_folder

#generate index in folder
generate_readme(directory+"/"+relative_folder)

for file in files:
file_path = os.path.relpath(os.path.join(root, file), directory).replace("\\", "/") #use linux path structure
file_path = urllib.parse.quote(file_path)

if file == "README.md": #skip
continue

if file.endswith(".md"):
first_header = extract_first_header(os.path.join(root, file))
if first_header:
folder_lines[relative_folder].append(f"- [{file}]({file_path}) - {first_header}")
else:
folder_lines[relative_folder].append(f"- [{file}]({file_path})")
else:
folder_lines[relative_folder].append(f"- [{file}]({file_path})")

# Add an extra line break between different folders
if files:
folder_lines[relative_folder].append("")

#write output
for folder in folder_lines:
if folder in folder_order: #skip ordered folders for the end
continue

for line in folder_lines[folder]:
readme.write(line + "\n")

#write ordered output
for folder in folder_order:
if folder not in folder_lines: #not found
continue

for line in folder_lines[folder]:
readme.write(line + "\n")



if __name__ == "__main__":
# Change the working directory to the location of the script
script_dir = os.path.dirname(os.path.abspath(__file__))
os.chdir(script_dir)

# Specify the directory you want to index
directory_to_index = "../"
generate_readme(directory_to_index, ["3rdparty", "3rdparty/protocols"])
33 changes: 33 additions & 0 deletions documentation/3rdparty/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# README Index

This README file contains an index of all files in the documentation directory.

## File List

**protocols**


- [CAN-Bus-protocol-PYLON-low-voltage-V1.2-20180408.pdf](protocols/CAN-Bus-protocol-PYLON-low-voltage-V1.2-20180408.pdf)
- [converter.txt](protocols/converter.txt)
- [EG4-3000-EHV - MODBUS Communication Protocol.pdf](protocols/EG4-3000-EHV%20-%20MODBUS%20Communication%20Protocol.pdf)
- [EG4-6000XP-MODBUS-Communication-Protocol.pdf](protocols/EG4-6000XP-MODBUS-Communication-Protocol.pdf)
- [Growatt Modbus Protocol v1.24.pdf](protocols/Growatt%20Modbus%20Protocol%20v1.24.pdf)
- [Growatt PV Inverter Modbus RS485 RTU Protocol V3.04.pdf](protocols/Growatt%20PV%20Inverter%20Modbus%20RS485%20RTU%20Protocol%20V3.04.pdf)
- [Growatt PV Inverter Modbus RS485 RTU Protocol V3.15.pdf](protocols/Growatt%20PV%20Inverter%20Modbus%20RS485%20RTU%20Protocol%20V3.15.pdf)
- [hdhk_16ch_ac_module_modbus_rtu.jpeg](protocols/hdhk_16ch_ac_module_modbus_rtu.jpeg)
- [hdhk_16ch_ac_module_modbus_rtu_chinese.pdf](protocols/hdhk_16ch_ac_module_modbus_rtu_chinese.pdf)
- [hdhk_16ch_ac_module_modbus_rtu_translated_english.pdf](protocols/hdhk_16ch_ac_module_modbus_rtu_translated_english.pdf)
- [MAX Series Modbus RTU Protocol.pdf](protocols/MAX%20Series%20Modbus%20RTU%20Protocol.pdf)
- [note.md](protocols/note.md)
- [OffGrid-Modbus-RS485RS232-RTU-Protocol-V0.14-20210420.pdf](protocols/OffGrid-Modbus-RS485RS232-RTU-Protocol-V0.14-20210420.pdf)
- [PACE-BMS-Modbus-Protocol-for-RS485-V1.3-20170627.pdf](protocols/PACE-BMS-Modbus-Protocol-for-RS485-V1.3-20170627.pdf)
- [PACE-BMS-RS485-communication-protocol-20180615.pdf](protocols/PACE-BMS-RS485-communication-protocol-20180615.pdf)
- [PACE-CAN-communication-protocal(PACE-CAN-TY)-20161216-.pdf](protocols/PACE-CAN-communication-protocal%EF%BC%88PACE-CAN-TY%EF%BC%89-20161216-.pdf)
- [PYLON LFP Battery communication protocol - RS485 V2.8 20161216.pdf](protocols/PYLON%20LFP%20Battery%20communication%20protocol%20-%20RS485%20V2.8%2020161216.pdf)
- [RS485-protocol-pylon-low-voltage-V3.3-20180821.pdf](protocols/RS485-protocol-pylon-low-voltage-V3.3-20180821.pdf)
- [Sigineer-Solar-Inverter-RS485-Port-Modbus-RTU-Protocol-v0.11-20200302.pdf](protocols/Sigineer-Solar-Inverter-RS485-Port-Modbus-RTU-Protocol-v0.11-20200302.pdf)
- [Sol-Ark ModBus V1.1.pdf](protocols/Sol-Ark%20ModBus%20V1.1.pdf)
- [SRNE_MODBUS_v3.9.pdf](protocols/SRNE_MODBUS_v3.9.pdf)
- [Victron VE-Bus-products-MK2-Protocol-3-14.pdf](protocols/Victron%20VE-Bus-products-MK2-Protocol-3-14.pdf)
- [Victron-CCGX-Modbus-TCP-register-list-3.30.xlsx](protocols/Victron-CCGX-Modbus-TCP-register-list-3.30.xlsx)

File renamed without changes.
File renamed without changes.
23 changes: 23 additions & 0 deletions documentation/3rdparty/protocols/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# README Index

This README file contains an index of all files in the documentation directory.

## File List


## Additional Notes


Protocol Documentation
---
[V3.14](Growatt%20PV%20Inverter%20Modbus%20RS485%20RTU%20Protocol%20V3.14.pdf)

Source: https://github.com/jrbenito/canadianSolar-pvoutput/blob/732efe68b71f67129f5b31442f82f2be0d79e605/docs/

Note: The original file name doesn't match the version specified in the document.

----
[V3.04](Growatt%20PV%20Inverter%20Modbus%20RS485%20RTU%20Protocol%20V3.04.pdf)

Source: http://www.growatt.pl/dokumenty/Inne/Growatt%20PV%20Inverter%20Modbus%20RS485%20RTU%20Protocol%20V3.04.pdf

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes.
66 changes: 66 additions & 0 deletions documentation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# README Index

This README file contains an index of all files in the documentation directory.

## File List

**dashboards**


- [grafana.md](dashboards/grafana.md)
- [homeassistant.md](dashboards/homeassistant.md)
- [nodered.md](dashboards/nodered.md) - Setting up PythonProtocolGateway on a RasPi with NodeRed Dashboard 2.0

**devices**


- [EG4.md](devices/EG4.md) - EG4 to MQTT
- [Growatt.md](devices/Growatt.md) - Growatt To MQTT
- [Sigineer.md](devices/Sigineer.md) - Sigineer to MQTT
- [SOK.md](devices/SOK.md) - SOK to MQTT
- [SolArk.md](devices/SolArk.md) - SolArk to MQTT

**usage**


- [creating_and_editing_protocols.md](usage/creating_and_editing_protocols.md) - Creating and Editing Protocols ‐ JSON ‐ CSV
- [protocols.md](usage/protocols.md) - Custom / Editing Protocols
- [transports.md](usage/transports.md) - Transports

**usage/configuration_examples**


- [modbus_rtu_to_modbus_tcp.md](usage/configuration_examples/modbus_rtu_to_modbus_tcp.md) - ModBus RTU to ModBus TCP
- [modbus_rtu_to_mqtt.md](usage/configuration_examples/modbus_rtu_to_mqtt.md) - ModBus RTU to MQTT

**3rdparty**



**3rdparty/protocols**


- [CAN-Bus-protocol-PYLON-low-voltage-V1.2-20180408.pdf](3rdparty/protocols/CAN-Bus-protocol-PYLON-low-voltage-V1.2-20180408.pdf)
- [converter.txt](3rdparty/protocols/converter.txt)
- [EG4-3000-EHV - MODBUS Communication Protocol.pdf](3rdparty/protocols/EG4-3000-EHV%20-%20MODBUS%20Communication%20Protocol.pdf)
- [EG4-6000XP-MODBUS-Communication-Protocol.pdf](3rdparty/protocols/EG4-6000XP-MODBUS-Communication-Protocol.pdf)
- [Growatt Modbus Protocol v1.24.pdf](3rdparty/protocols/Growatt%20Modbus%20Protocol%20v1.24.pdf)
- [Growatt PV Inverter Modbus RS485 RTU Protocol V3.04.pdf](3rdparty/protocols/Growatt%20PV%20Inverter%20Modbus%20RS485%20RTU%20Protocol%20V3.04.pdf)
- [Growatt PV Inverter Modbus RS485 RTU Protocol V3.15.pdf](3rdparty/protocols/Growatt%20PV%20Inverter%20Modbus%20RS485%20RTU%20Protocol%20V3.15.pdf)
- [hdhk_16ch_ac_module_modbus_rtu.jpeg](3rdparty/protocols/hdhk_16ch_ac_module_modbus_rtu.jpeg)
- [hdhk_16ch_ac_module_modbus_rtu_chinese.pdf](3rdparty/protocols/hdhk_16ch_ac_module_modbus_rtu_chinese.pdf)
- [hdhk_16ch_ac_module_modbus_rtu_translated_english.pdf](3rdparty/protocols/hdhk_16ch_ac_module_modbus_rtu_translated_english.pdf)
- [MAX Series Modbus RTU Protocol.pdf](3rdparty/protocols/MAX%20Series%20Modbus%20RTU%20Protocol.pdf)
- [note.md](3rdparty/protocols/note.md)
- [OffGrid-Modbus-RS485RS232-RTU-Protocol-V0.14-20210420.pdf](3rdparty/protocols/OffGrid-Modbus-RS485RS232-RTU-Protocol-V0.14-20210420.pdf)
- [PACE-BMS-Modbus-Protocol-for-RS485-V1.3-20170627.pdf](3rdparty/protocols/PACE-BMS-Modbus-Protocol-for-RS485-V1.3-20170627.pdf)
- [PACE-BMS-RS485-communication-protocol-20180615.pdf](3rdparty/protocols/PACE-BMS-RS485-communication-protocol-20180615.pdf)
- [PACE-CAN-communication-protocal(PACE-CAN-TY)-20161216-.pdf](3rdparty/protocols/PACE-CAN-communication-protocal%EF%BC%88PACE-CAN-TY%EF%BC%89-20161216-.pdf)
- [PYLON LFP Battery communication protocol - RS485 V2.8 20161216.pdf](3rdparty/protocols/PYLON%20LFP%20Battery%20communication%20protocol%20-%20RS485%20V2.8%2020161216.pdf)
- [RS485-protocol-pylon-low-voltage-V3.3-20180821.pdf](3rdparty/protocols/RS485-protocol-pylon-low-voltage-V3.3-20180821.pdf)
- [Sigineer-Solar-Inverter-RS485-Port-Modbus-RTU-Protocol-v0.11-20200302.pdf](3rdparty/protocols/Sigineer-Solar-Inverter-RS485-Port-Modbus-RTU-Protocol-v0.11-20200302.pdf)
- [Sol-Ark ModBus V1.1.pdf](3rdparty/protocols/Sol-Ark%20ModBus%20V1.1.pdf)
- [SRNE_MODBUS_v3.9.pdf](3rdparty/protocols/SRNE_MODBUS_v3.9.pdf)
- [Victron VE-Bus-products-MK2-Protocol-3-14.pdf](3rdparty/protocols/Victron%20VE-Bus-products-MK2-Protocol-3-14.pdf)
- [Victron-CCGX-Modbus-TCP-register-list-3.30.xlsx](3rdparty/protocols/Victron-CCGX-Modbus-TCP-register-list-3.30.xlsx)

6 changes: 6 additions & 0 deletions documentation/dashboards/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# README Index

This README file contains an index of all files in the documentation directory.

## File List

Empty file.
Empty file.
Loading

0 comments on commit ed1d92f

Please sign in to comment.