Skip to content

Commit 950fd4b

Browse files
authored
Merge branch 'v1.1.10-pr92' into main
2 parents d03a843 + be2c3ea commit 950fd4b

19 files changed

+249
-26
lines changed

.github/workflows/python-3.10.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
name: Python 3.10
55

6+
permissions:
7+
contents: read
8+
69
on:
710
push:
811
branches: [ "main" ]

.github/workflows/python-3.11.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
name: Python 3.11
55

6+
permissions:
7+
contents: read
8+
69
on:
710
push:
811
branches: [ "main" ]

.github/workflows/python-3.12.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
name: Python 3.12
55

6+
permissions:
7+
contents: read
8+
69
on:
710
push:
811
branches: [ "main" ]

.github/workflows/python-3.13.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
name: Python 3.13
55

6+
permissions:
7+
contents: read
8+
69
on:
710
push:
811
branches: [ "main" ]

.github/workflows/python-3.9.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
name: Python 3.9
55

6+
permissions:
7+
contents: read
8+
69
on:
710
push:
811
branches: [ "main" ]

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM python:3.9-alpine as base
1+
FROM python:3.13-alpine as base
22
FROM base as builder
33
RUN mkdir /install
44
WORKDIR /install

README.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ solark_v1.1 = SolarArk 8/12K Inverters - Untested
5858
hdhk_16ch_ac_module = some chinese current monitoring device :P
5959
srne_2021_v1.96 = SRNE inverters 2021+ (tested at ASF48100S200-H, ok-ish for HF2430U60-100 )
6060
61-
eg4_v58 = eg4 inverters ( EG4-6000XP ) - confirmed working
61+
eg4_v58 = eg4 inverters ( EG4-6000XP, EG4-18K ) - confirmed working
6262
eg4_3000ehv_v1 = eg4 inverters ( EG4_3000EHV )
6363
```
6464

@@ -154,11 +154,16 @@ if you installed this when it was called growatt2mqtt-hotnoob or InverterModBusT
154154

155155
### donate
156156
this took me a while to make; and i had to make it because there werent any working solutions.
157-
donations would be appreciated.
158-
![BitCoin Donation](https://github.com/HotNoob/growatt2mqtt-hotnoob/blob/main/images/donate_to_hotnoob.png?raw=true)
157+
donations / sponsoring this repo would be appreciated.
159158

160-
```(btc) bc1qh394vazcguedkw2rlklnuhapdq7qgpnnz9c3t0```
161-
162-
### Use Docker - untested
159+
### Use Docker
163160
- ```docker build . -t protocol_gateway ```
164161
- ```docker run --device=/dev/ttyUSB0 protocol_gateway```
162+
163+
### Use Docker Image
164+
- ``` docker pull hotn00b/pythonprotocolgateway ```
165+
- ```docker run -v $(pwd)/config.cfg:/app/config.cfg --device=/dev/ttyUSB0 hotn00b/pythonprotocolgateway```
166+
167+
See [config.cfg.example](https://github.com/HotNoob/PythonProtocolGateway/blob/main/config.cfg.example)
168+
169+
[Docker Image Repo](https://hub.docker.com/r/hotn00b/pythonprotocolgateway)

classes/protocol_settings.py

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ class registry_map_entry:
188188
register_bit : int
189189
register_byte : int
190190
''' byte offset for canbus ect... '''
191+
191192
variable_name : str
192193
documented_name : str
193194
unit : str
@@ -208,6 +209,9 @@ class registry_map_entry:
208209
data_type_size : int = -1
209210
''' for non-fixed size types like ASCII'''
210211

212+
data_byteorder : str = ''
213+
''' entry specific byte order little | big | '' '''
214+
211215
read_command : bytes = None
212216
''' for transports/protocols that require sending a command ontop of "register" '''
213217

@@ -512,16 +516,30 @@ def process_row(row):
512516

513517
#region data type
514518
data_type = Data_Type.USHORT
515-
516519
data_type_len : int = -1
520+
data_byteorder : str = ''
517521
#optional row, only needed for non-default data types
518522
if "data type" in row and row["data type"]:
523+
data_type_str : str = ''
524+
519525
matches = data_type_regex.search(row["data type"])
520526
if matches:
521527
data_type_len = int(matches.group("length"))
522-
data_type = Data_Type.fromString(matches.group("datatype"))
528+
data_type_str = matches.group("datatype")
523529
else:
524-
data_type = Data_Type.fromString(row["data type"])
530+
data_type_str = row["data type"]
531+
532+
#check if datatype specifies byteorder
533+
if data_type_str.upper().endswith("_LE"):
534+
data_byteorder = "little"
535+
data_type_str = data_type_str[:-3]
536+
elif data_type_str.upper().endswith("_BE"):
537+
data_byteorder = "big"
538+
data_type_str = data_type_str[:-3]
539+
540+
541+
data_type = Data_Type.fromString(data_type_str)
542+
525543

526544

527545
if "values" not in row:
@@ -658,6 +676,7 @@ def process_row(row):
658676
unit_mod= unit_multiplier,
659677
data_type= data_type,
660678
data_type_size = data_type_len,
679+
data_byteorder = data_byteorder,
661680
concatenate = concatenate,
662681
concatenate_registers = concatenate_registers,
663682
values=values,
@@ -857,6 +876,10 @@ def load_registry_map(self, registry_type : Registry_Type, file : str = "", sett
857876
def process_register_bytes(self, registry : dict[int,bytes], entry : registry_map_entry):
858877
''' process bytes into data'''
859878

879+
byte_order : str = self.byteorder
880+
if entry.data_byteorder: #allow map entry to override byteorder
881+
byte_order = entry.data_byteorder
882+
860883
if isinstance(registry[entry.register], tuple):
861884
register = registry[entry.register][0] #can bus uses tuple for timestamp
862885
else:
@@ -869,14 +892,15 @@ def process_register_bytes(self, registry : dict[int,bytes], entry : registry_ma
869892
register = register[:entry.data_type_size]
870893

871894
if entry.data_type == Data_Type.UINT:
872-
value = int.from_bytes(register[:4], byteorder=self.byteorder, signed=False)
895+
value = int.from_bytes(register[:4], byteorder=byte_order, signed=False)
873896
elif entry.data_type == Data_Type.INT:
874-
value = int.from_bytes(register[:4], byteorder=self.byteorder, signed=True)
897+
value = int.from_bytes(register[:4], byteorder=byte_order, signed=True)
875898
elif entry.data_type == Data_Type.USHORT:
876-
value = int.from_bytes(register[:2], byteorder=self.byteorder, signed=False)
899+
value = int.from_bytes(register[:2], byteorder=byte_order, signed=False)
877900
elif entry.data_type == Data_Type.SHORT:
878-
value = int.from_bytes(register[:2], byteorder=self.byteorder, signed=True)
901+
value = int.from_bytes(register[:2], byteorder=byte_order, signed=True)
879902
elif entry.data_type == Data_Type._16BIT_FLAGS or entry.data_type == Data_Type._8BIT_FLAGS or entry.data_type == Data_Type._32BIT_FLAGS:
903+
val = int.from_bytes(register, byteorder=byte_order, signed=False)
880904
#16 bit flags
881905
start_bit : int = 0
882906
end_bit : int = 16 #default 16 bit
@@ -952,11 +976,20 @@ def process_register_bytes(self, registry : dict[int,bytes], entry : registry_ma
952976
# If positive, simply extract the value using the bit mask
953977
value = (register >> bit_index) & bit_mask
954978

955-
elif entry.data_type.value > 200 or entry.data_type == Data_Type.BYTE: #bit types
979+
elif entry.data_type == Data_Type.BYTE: #bit types
980+
value = int.from_bytes(register[:1], byteorder=byte_order, signed=False)
981+
elif entry.data_type.value > 200: #bit types
956982
bit_size = Data_Type.getSize(entry.data_type)
957983
bit_mask = (1 << bit_size) - 1 # Create a mask for extracting X bits
958984
bit_index = entry.register_bit
985+
986+
987+
if isinstance(register, bytes):
988+
register = int.from_bytes(register, byteorder=byte_order)
989+
959990
value = (register >> bit_index) & bit_mask
991+
992+
960993
elif entry.data_type == Data_Type.HEX:
961994
value = register.hex() #convert bytes to hex
962995
elif entry.data_type == Data_Type.ASCII:
@@ -986,6 +1019,11 @@ def process_register_bytes(self, registry : dict[int,bytes], entry : registry_ma
9861019

9871020
def process_register_ushort(self, registry : dict[int, int], entry : registry_map_entry ):
9881021
''' process ushort type registry into data'''
1022+
1023+
byte_order : str = self.byteorder
1024+
if entry.data_byteorder:
1025+
byte_order = entry.data_byteorder
1026+
9891027
if entry.data_type == Data_Type.UINT: #read uint
9901028
if entry.register + 1 not in registry:
9911029
return
@@ -1074,10 +1112,10 @@ def process_register_ushort(self, registry : dict[int, int], entry : registry_ma
10741112
bit_index = entry.register_bit
10751113
value = (registry[entry.register] >> bit_index) & bit_mask
10761114
elif entry.data_type == Data_Type.HEX:
1077-
value = registry[entry.register].to_bytes((16 + 7) // 8, byteorder=self.byteorder) #convert to ushort to bytes
1115+
value = registry[entry.register].to_bytes((16 + 7) // 8, byteorder=byte_order) #convert to ushort to bytes
10781116
value = value.hex() #convert bytes to hex
10791117
elif entry.data_type == Data_Type.ASCII:
1080-
value = registry[entry.register].to_bytes((16 + 7) // 8, byteorder=self.byteorder) #convert to ushort to bytes
1118+
value = registry[entry.register].to_bytes((16 + 7) // 8, byteorder=byte_order) #convert to ushort to bytes
10811119
try:
10821120
value = value.decode("utf-8") #convert bytes to ascii
10831121
except UnicodeDecodeError as e:

classes/transports/transport_base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ def __init__(self, settings : "SectionProxy") -> None:
9898
self.device_name = settings.get(["device_name", "name"], fallback=self.device_manufacturer+"_"+self.device_serial_number)
9999
self.bridge = settings.get("bridge", self.bridge)
100100
self.read_interval = settings.getfloat("read_interval", self.read_interval)
101-
self.max_precision = settings.getint(["max_precision", "precision"], self.max_precision)
101+
self.max_precision = settings.getint(["max_precision", "precision"], fallback=self.max_precision)
102102
if "write_enabled" in settings or "enable_write" in settings:
103103
self.write_enabled = settings.getboolean(["write_enabled", "enable_write"], self.write_enabled)
104104

defs/common.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import re
2+
import os
23

34
import serial.tools.list_ports
45

@@ -46,13 +47,23 @@ def strtoint(val : str) -> int:
4647
return int(val)
4748

4849
def get_usb_serial_port_info(port : str = "") -> str:
50+
51+
# If port is a symlink
52+
if os.path.islink(port):
53+
port = os.path.realpath(port)
54+
4955
for p in serial.tools.list_ports.comports():
5056
if str(p.device).upper() == port.upper():
5157
return "["+hex(p.vid)+":"+hex(p.pid)+":"+str(p.serial_number)+":"+str(p.location)+"]"
5258

5359
return ""
5460

5561
def find_usb_serial_port(port : str = "", vendor_id : str = "", product_id : str = "", serial_number : str = "", location : str = "") -> str:
62+
63+
# If port is a symlink
64+
if os.path.islink(port):
65+
port = os.path.realpath(port)
66+
5667
if not port.startswith("["):
5768
return port
5869

0 commit comments

Comments
 (0)