-
Notifications
You must be signed in to change notification settings - Fork 2
/
renogy.py
executable file
·125 lines (99 loc) · 4.43 KB
/
renogy.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#!/usr/bin/env python3
import argparse
from collections import namedtuple
from enum import Enum
import minimalmodbus
import serial
class SystemType(Enum):
CONTROLLER = 0
INVERTER = 1
class BatteryType(Enum):
OPEN = 1
SEALED = 2
GEL = 3
LITHIUM = 4
CUSTOM = 5
class ChargingState(Enum):
DEACTIVATED = 0
ACTIVATED = 1
MPPT = 2
EQUALIZING = 3
BOOST = 4
FLOATING = 5
LIMITING = 6
class Functions(object):
def high_order(reg: int):
return float((reg >> 8) & 0x00FF)
def low_order(reg: int):
return float(reg & 0x00FF)
def controller_temp(reg: int):
temp = (reg >> 8) & 0x7F
sign = (reg >> 8) & 0x80
return -(temp) if sign == 1 else temp
def system_type(reg: int):
return SystemType(int(Functions.low_order(reg))).name
def battery_type(reg: int):
return BatteryType(reg).name
def charging_state(reg: int):
return ChargingState(reg & 0x00FF).name
Register = namedtuple("Register", ["address", "scale", "units", "function"])
class Attribute(Register, Enum):
SYS_MAX_VOLTS = Register(0x00A, 1, "V", Functions.high_order)
SYS_MAX_AMPS = Register(0x00A, 1, "A", Functions.low_order)
SYS_MAX_DISCHARGE = Register(0x00B, 1, "A", Functions.high_order)
SYS_TYPE = Register(0x00B, 1, "", Functions.system_type)
BATT_SOC = Register(0x100, 1, "%", None)
BATT_VOLTS = Register(0x101, 0.1, "V", None)
CHARGING_AMPS = Register(0x102, 0.01, "A", None)
CONTROLLER_TEMP = Register(0x103, 1, "C", Functions.controller_temp)
LOAD_WATTS = Register(0x106, 1, "W", None)
PANEL_VOLTS = Register(0x107, 0.1, "V", None)
PANEL_AMPS = Register(0x108, 0.01, "A", None)
PANEL_WATTS = Register(0x109, 1, "W", None)
BATT_MIN_VOLTS = Register(0x10B, 0.1, "V", None)
BATT_MAX_VOLTS = Register(0x10C, 0.1, "V", None)
DAY_CHARGE = Register(0x10F, 1, "W", None)
DAY_DISCHARGE = Register(0x110, 1, "W", None)
DAY_GEN_POWER = Register(0x113, 1, "W", None)
DAY_CON_POWER = Register(0x114, 1, "W", None)
UPTIME_DAYS = Register(0x115, 1, "", None)
BATT_FULL_COUNT = Register(0x117, 1, "", None)
CHARGING_STATE = Register(0x120, 1, "", Functions.charging_state)
BATT_CAPACITY = Register(0xE002, 1, "W", None)
SYS_BATT_VOLTS = Register(0xE003, 1, "V", Functions.high_order)
RECON_BATT_VOLTS = Register(0xE003, 1, "V", Functions.low_order)
BATT_TYPE = Register(0xE004, 1, "", Functions.battery_type)
OVER_VOLTS = Register(0xE005, 0.1, "V", None)
CHARGE_VOLTS = Register(0xE006, 0.1, "V", None)
EQUALIZE_VOLTS = Register(0xE007, 0.1, "V", None)
BOOST_VOLTS = Register(0xE008, 0.1, "V", None)
FLOAT_VOLTS = Register(0xE009, 0.1, "V", None)
BOOST_RECOV_VOLTS = Register(0xE00A, 0.1, "V", None)
DISCH_RECOV_VOLTS = Register(0xE00B, 0.1, "V", None)
UNDER_WARN_VOLT = Register(0xE00C, 0.1, "V", None)
OVER_DISCH_VOLTS = Register(0xE00D, 0.1, "V", None)
DISCH_WARN_VOLTS = Register(0xE00E, 0.1, "V", None)
BOOST_TIME = Register(0xE012, 1, "", None)
class Controller(object):
def __init__(self, device="/dev/ttyUSB0", debug=False):
self.renogy = minimalmodbus.Instrument(device, 0xFF)
self.renogy.serial.baudrate = 9600
self.renogy.serial.bytesize = 8
self.renogy.serial.parity = serial.PARITY_NONE
self.renogy.serial.stopbits = 1
self.renogy.serial.timeout = 2
self.renogy.debug = debug
def get(self, attr: Attribute):
value = float(self.renogy.read_register(attr.address)) * attr.scale
return attr.function(int(value)) if attr.function else round(value, 2)
def get_all(self, attrs=Attribute):
values = {attr.name.lower(): self.get(attr) for attr in attrs}
return values
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Renogy charge controller monitor")
parser.add_argument("--device", default="/dev/ttyUSB0", help="Serial device")
parser.add_argument("--debug", action="store_true", help="Enable modbus debug")
args = parser.parse_args()
controller = Controller(args.device, args.debug)
for name, value in controller.get_all().items():
print(f"{name:>20}: {value}")