mirror of https://github.com/lumapu/ahoy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
311 lines
8.0 KiB
311 lines
8.0 KiB
#!/usr/bin/python3
|
|
# -*- coding: utf-8 -*-
|
|
import struct
|
|
|
|
class StatusResponse:
|
|
e_keys = ['voltage','current','power','energy_total','energy_daily']
|
|
|
|
def unpack(self, fmt, base):
|
|
size = struct.calcsize(fmt)
|
|
return struct.unpack(fmt, self.response[base:base+size])
|
|
|
|
@property
|
|
def phases(self):
|
|
phases = []
|
|
p_exists = True
|
|
while p_exists:
|
|
p_exists = False
|
|
phase_id = len(phases)
|
|
phase = {}
|
|
for key in self.e_keys:
|
|
prop = f'ac_{key}_{phase_id}'
|
|
if hasattr(self, prop):
|
|
p_exists = True
|
|
phase[key] = getattr(self, prop)
|
|
if p_exists:
|
|
phases.append(phase)
|
|
|
|
return phases
|
|
|
|
@property
|
|
def strings(self):
|
|
strings = []
|
|
s_exists = True
|
|
while s_exists:
|
|
s_exists = False
|
|
string_id = len(strings)
|
|
string = {}
|
|
for key in self.e_keys:
|
|
prop = f'dc_{key}_{string_id}'
|
|
if hasattr(self, prop):
|
|
s_exists = True
|
|
string[key] = getattr(self, prop)
|
|
if s_exists:
|
|
strings.append(string)
|
|
|
|
return strings
|
|
|
|
def __dict__(self):
|
|
data = {}
|
|
data['phases'] = self.phases
|
|
data['strings'] = self.strings
|
|
data['temperature'] = self.temperature
|
|
data['frequency'] = self.frequency
|
|
return data
|
|
|
|
class UnknownResponse:
|
|
@property
|
|
def hex_ascii(self):
|
|
return ' '.join([f'{b:02x}' for b in self.response])
|
|
|
|
@property
|
|
def dump_longs(self):
|
|
res = self.response
|
|
n = len(res)/4
|
|
|
|
vals = None
|
|
if n % 4 == 0:
|
|
vals = struct.unpack(f'>{int(n)}L', res)
|
|
|
|
return vals
|
|
|
|
@property
|
|
def dump_longs_pad1(self):
|
|
res = self.response[1:]
|
|
n = len(res)/4
|
|
|
|
vals = None
|
|
if n % 4 == 0:
|
|
vals = struct.unpack(f'>{int(n)}L', res)
|
|
|
|
return vals
|
|
|
|
@property
|
|
def dump_shorts(self):
|
|
n = len(self.response)/2
|
|
|
|
vals = None
|
|
if n % 2 == 0:
|
|
vals = struct.unpack(f'>{int(n)}H', self.response)
|
|
return vals
|
|
|
|
@property
|
|
def dump_shorts_pad1(self):
|
|
res = self.response[1:]
|
|
n = len(res)/2
|
|
|
|
vals = None
|
|
if n % 2 == 0:
|
|
vals = struct.unpack(f'>{int(n)}H', res)
|
|
return vals
|
|
|
|
class DEBUG_DecodeAny(UnknownResponse):
|
|
def __init__(self, response):
|
|
self.response = response
|
|
|
|
longs = self.dump_longs
|
|
if not longs:
|
|
print(' type long : unable to decode (len or not mod 4)')
|
|
else:
|
|
print(' type long : ' + str(longs))
|
|
|
|
longs = self.dump_longs_pad1
|
|
if not longs:
|
|
print(' type long pad1 : unable to decode (len or not mod 4)')
|
|
else:
|
|
print(' type long pad1 : ' + str(longs))
|
|
|
|
shorts = self.dump_shorts
|
|
if not shorts:
|
|
print(' type short : unable to decode (len or not mod 2)')
|
|
else:
|
|
print(' type short : ' + str(shorts))
|
|
|
|
shorts = self.dump_shorts_pad1
|
|
if not shorts:
|
|
print(' type short pad1: unable to decode (len or not mod 2)')
|
|
else:
|
|
print(' type short pad1: ' + str(shorts))
|
|
|
|
|
|
# 1121-Series Intervers, 1 MPPT, 1 Phase
|
|
class HM300_Decode0B(StatusResponse):
|
|
def __init__(self, response):
|
|
self.response = response
|
|
|
|
@property
|
|
def dc_voltage_0(self):
|
|
return self.unpack('>H', 2)[0]/10
|
|
@property
|
|
def dc_current_0(self):
|
|
return self.unpack('>H', 4)[0]/100
|
|
@property
|
|
def dc_power_0(self):
|
|
return self.unpack('>H', 6)[0]/10
|
|
@property
|
|
def dc_energy_total_0(self):
|
|
return self.unpack('>L', 8)[0]
|
|
@property
|
|
def dc_energy_daily_0(self):
|
|
return self.unpack('>H', 12)[0]
|
|
|
|
|
|
@property
|
|
def ac_voltage_0(self):
|
|
return self.unpack('>H', 14)[0]/10
|
|
@property
|
|
def ac_current_0(self):
|
|
return self.unpack('>H', 22)[0]/100
|
|
@property
|
|
def ac_power_0(self):
|
|
return self.unpack('>H', 18)[0]/10
|
|
@property
|
|
def frequency(self):
|
|
return self.unpack('>H', 16)[0]/100
|
|
@property
|
|
def temperature(self):
|
|
return self.unpack('>H', 26)[0]/10
|
|
|
|
|
|
# 1141-Series Inverters, 2 MPPT, 1 Phase
|
|
class HM600_Decode0B(StatusResponse):
|
|
def __init__(self, response):
|
|
self.response = response
|
|
|
|
@property
|
|
def dc_voltage_0(self):
|
|
return self.unpack('>H', 2)[0]/10
|
|
@property
|
|
def dc_current_0(self):
|
|
return self.unpack('>H', 4)[0]/100
|
|
@property
|
|
def dc_power_0(self):
|
|
return self.unpack('>H', 6)[0]/10
|
|
@property
|
|
def dc_energy_total_0(self):
|
|
return self.unpack('>L', 14)[0]
|
|
@property
|
|
def dc_energy_daily_0(self):
|
|
return self.unpack('>H', 22)[0]
|
|
|
|
@property
|
|
def dc_voltage_1(self):
|
|
return self.unpack('>H', 8)[0]/10
|
|
@property
|
|
def dc_current_1(self):
|
|
return self.unpack('>H', 10)[0]/100
|
|
@property
|
|
def dc_power_1(self):
|
|
return self.unpack('>H', 12)[0]/10
|
|
@property
|
|
def dc_energy_total_1(self):
|
|
return self.unpack('>L', 18)[0]
|
|
@property
|
|
def dc_energy_daily_1(self):
|
|
return self.unpack('>H', 24)[0]
|
|
|
|
@property
|
|
def ac_voltage_0(self):
|
|
return self.unpack('>H', 26)[0]/10
|
|
@property
|
|
def ac_current_0(self):
|
|
return self.unpack('>H', 34)[0]/10
|
|
@property
|
|
def ac_power_0(self):
|
|
return self.unpack('>H', 30)[0]/10
|
|
@property
|
|
def frequency(self):
|
|
return self.unpack('>H', 28)[0]/100
|
|
@property
|
|
def temperature(self):
|
|
return self.unpack('>H', 38)[0]/10
|
|
|
|
class HM600_Decode0C(HM600_Decode0B):
|
|
def __init__(self, response):
|
|
self.response = response
|
|
|
|
|
|
# 1161-Series Inverters, 4 MPPT, 1 Phase
|
|
class HM1200_Decode0B(StatusResponse):
|
|
def __init__(self, response):
|
|
self.response = response
|
|
|
|
@property
|
|
def dc_voltage_0(self):
|
|
return self.unpack('>H', 2)[0]/10
|
|
@property
|
|
def dc_current_0(self):
|
|
return self.unpack('>H', 4)[0]/100
|
|
@property
|
|
def dc_power_0(self):
|
|
return self.unpack('>H', 8)[0]/10
|
|
@property
|
|
def dc_energy_total_0(self):
|
|
return self.unpack('>L', 12)[0]
|
|
@property
|
|
def dc_energy_daily_0(self):
|
|
return self.unpack('>H', 20)[0]
|
|
|
|
@property
|
|
def dc_voltage_1(self):
|
|
return self.unpack('>H', 2)[0]/10
|
|
@property
|
|
def dc_current_1(self):
|
|
return self.unpack('>H', 4)[0]/100
|
|
@property
|
|
def dc_power_1(self):
|
|
return self.unpack('>H', 10)[0]/10
|
|
@property
|
|
def dc_energy_total_1(self):
|
|
return self.unpack('>L', 16)[0]
|
|
@property
|
|
def dc_energy_daily_1(self):
|
|
return self.unpack('>H', 22)[0]
|
|
|
|
@property
|
|
def dc_voltage_2(self):
|
|
return self.unpack('>H', 24)[0]/10
|
|
@property
|
|
def dc_current_2(self):
|
|
return self.unpack('>H', 26)[0]/100
|
|
@property
|
|
def dc_power_2(self):
|
|
return self.unpack('>H', 30)[0]/10
|
|
@property
|
|
def dc_energy_total_2(self):
|
|
return self.unpack('>L', 34)[0]
|
|
@property
|
|
def dc_energy_daily_2(self):
|
|
return self.unpack('>H', 42)[0]
|
|
|
|
@property
|
|
def dc_voltage_3(self):
|
|
return self.unpack('>H', 24)[0]/10
|
|
@property
|
|
def dc_current_3(self):
|
|
return self.unpack('>H', 28)[0]/100
|
|
@property
|
|
def dc_power_3(self):
|
|
return self.unpack('>H', 32)[0]/10
|
|
@property
|
|
def dc_energy_total_3(self):
|
|
return self.unpack('>L', 38)[0]
|
|
@property
|
|
def dc_energy_daily_3(self):
|
|
return self.unpack('>H', 44)[0]
|
|
|
|
@property
|
|
def ac_voltage_0(self):
|
|
return self.unpack('>H', 46)[0]/10
|
|
@property
|
|
def ac_current_0(self):
|
|
return self.unpack('>H', 54)[0]/100
|
|
@property
|
|
def ac_power_0(self):
|
|
return self.unpack('>H', 50)[0]/10
|
|
@property
|
|
def frequency(self):
|
|
return self.unpack('>H', 48)[0]/100
|
|
@property
|
|
def temperature(self):
|
|
return self.unpack('>H', 58)[0]/10
|
|
|