Browse Source

RPI: add handling for InverterDevInform_All message, handle

RealTimeRunData_Reality similar to RealTimeRunData_Debug
pull/272/head
Christian Ehrlicher 3 years ago
parent
commit
de90c19eb3
  1. 15
      tools/rpi/hoymiles/__init__.py
  2. 46
      tools/rpi/hoymiles/__main__.py
  3. 40
      tools/rpi/hoymiles/decoders/__init__.py

15
tools/rpi/hoymiles/__init__.py

@ -482,21 +482,20 @@ def compose_esb_packet(packet, mtu=17, **params):
fragment = compose_esb_fragment(packet[i:i+mtu], **params)
yield fragment
def compose_set_time_payload(timestamp=None):
def compose_send_time_payload(cmdId):
"""
Build set time request packet
:param timestamp: time to set (default: int(time.time()) )
:type timestamp: int
:param cmd to request
:type cmd: uint8
:return: payload
:rtype: bytes
"""
if not timestamp:
timestamp = int(time.time())
timestamp = int(time.time())
payload = b'\x0b\x00'
payload = struct.pack('>B', cmdId) + b'\x00'
payload = payload + struct.pack('>L', timestamp) # big-endian: msb at low address
payload = payload + b'\x00\x00\x00\x05\x00\x00\x00\x00'
payload = payload + b'\x00\x00\x00\x00\x00\x00\x00\x00'
return frame_payload(payload)
@ -649,7 +648,7 @@ class InverterTransaction:
except StopIteration:
seq_last = max(frames, key=lambda frame:frame.seq).seq if len(frames) else 0
self.__retransmit_frame(seq_last + 1)
raise BufferError(f'Missing packet: Last packet {len(self.scratch)}')
raise BufferError(f'Missing packet: Last packet {seq_last + 1}')
# Rebuild payload from unordered frames
payload = b''

46
tools/rpi/hoymiles/__main__.py

@ -7,6 +7,7 @@ Hoymiles micro-inverters main application
import sys
import struct
from enum import IntEnum
import re
import time
from datetime import datetime
@ -16,7 +17,7 @@ from yaml.loader import SafeLoader
import paho.mqtt.client
import hoymiles
def main_loop():
def main_loop(do_init):
"""Main loop"""
inverters = [
inverter for inverter in ahoy_config.get('inverters', [])
@ -25,9 +26,29 @@ def main_loop():
for inverter in inverters:
if hoymiles.HOYMILES_DEBUG_LOGGING:
print(f'Poll inverter {inverter["serial"]}')
poll_inverter(inverter)
def poll_inverter(inverter, retries=4):
poll_inverter(inverter, do_init)
class InfoCommands(IntEnum):
InverterDevInform_Simple = 0 # 0x00
InverterDevInform_All = 1 # 0x01
GridOnProFilePara = 2 # 0x02
HardWareConfig = 3 # 0x03
SimpleCalibrationPara = 4 # 0x04
SystemConfigPara = 5 # 0x05
RealTimeRunData_Debug = 11 # 0x0b
RealTimeRunData_Reality = 12 # 0x0c
RealTimeRunData_A_Phase = 13 # 0x0d
RealTimeRunData_B_Phase = 14 # 0x0e
RealTimeRunData_C_Phase = 15 # 0x0f
AlarmData = 17 # 0x11, Alarm data - all unsent alarms
AlarmUpdate = 18 # 0x12, Alarm data - all pending alarms
RecordData = 19 # 0x13
InternalData = 20 # 0x14
GetLossRate = 21 # 0x15
GetSelfCheckState = 30 # 0x1e
InitDataState = 0xff
def poll_inverter(inverter, do_init, retries=4):
"""
Send/Receive command_queue, initiate status poll on inverter
@ -39,11 +60,15 @@ def poll_inverter(inverter, retries=4):
dtu_ser = ahoy_config.get('dtu', {}).get('serial')
# Queue at least status data request
command_queue[str(inverter_ser)].append(hoymiles.compose_set_time_payload())
inv_str = str(inverter_ser)
if do_init:
command_queue[inv_str].append(hoymiles.compose_send_time_payload(InfoCommands.InverterDevInform_All))
# command_queue[inv_str].append(hoymiles.compose_send_time_payload(InfoCommands.SystemConfigPara))
command_queue[inv_str].append(hoymiles.compose_send_time_payload(InfoCommands.RealTimeRunData_Debug))
# Putt all queued commands for current inverter on air
while len(command_queue[str(inverter_ser)]) > 0:
payload = command_queue[str(inverter_ser)].pop(0)
# Put all queued commands for current inverter on air
while len(command_queue[inv_str]) > 0:
payload = command_queue[inv_str].pop(0)
# Send payload {ttl}-times until we get at least one reponse
payload_ttl = retries
@ -276,10 +301,13 @@ if __name__ == '__main__':
loop_interval = ahoy_config.get('interval', 1)
try:
do_init = True
while True:
t_loop_start = time.time()
main_loop()
main_loop(do_init)
do_init = False
print('', end='', flush=True)

40
tools/rpi/hoymiles/decoders/__init__.py

@ -314,6 +314,28 @@ class EventsResponse(UnknownResponse):
print(f' {fmt:7}: ' + str(struct.unpack('>' + fmt, chunk)))
print(end='', flush=True)
class HardwareInfoResponse(UnknownResponse):
def __init__(self, *args, **params):
super().__init__(*args, **params)
"""
const byteAssign_t InfoAssignment[] = {
{ FLD_FW_VERSION, UNIT_NONE, CH0, 0, 2, 1 },
{ FLD_FW_BUILD_YEAR, UNIT_NONE, CH0, 2, 2, 1 },
{ FLD_FW_BUILD_MONTH_DAY, UNIT_NONE, CH0, 4, 2, 1 },
{ FLD_HW_ID, UNIT_NONE, CH0, 8, 2, 1 }
};
self.response = bytes('\x27\x1a\x07\xe5\x04\x4d\x03\x4a\x00\x68\x00\x00\x00\x00\xe6\xfb', 'latin1')
"""
fw_version, fw_build_yyyy, fw_build_mmdd, unknown, hw_id = struct.unpack('>HHHHH', self.response[0:10])
fw_version_maj = int((fw_version / 10000))
fw_version_min = int((fw_version % 10000) / 100)
fw_version_pat = int((fw_version % 100))
fw_build_mm = int(fw_build_mmdd / 100)
fw_build_dd = int(fw_build_mmdd % 100)
print()
print(f'Firmware: {fw_version_maj}.{fw_version_min}.{fw_version_pat} build at {fw_build_dd}/{fw_build_mm}/{fw_build_yyyy}, HW revision {hw_id}')
class DebugDecodeAny(UnknownResponse):
"""Default decoder"""
@ -359,6 +381,9 @@ class DebugDecodeAny(UnknownResponse):
# 1121-Series Intervers, 1 MPPT, 1 Phase
class Hm300Decode01(HardwareInfoResponse):
""" Firmware version / date """
class Hm300Decode02(EventsResponse):
""" Inverter generic events log """
@ -407,6 +432,9 @@ class Hm300Decode0B(StatusResponse):
""" Inverter temperature in °C """
return self.unpack('>H', 26)[0]/10
class Hm300Decode0C(Hm300Decode0B):
""" 1121-series mirco-inverters status data """
class Hm300Decode11(EventsResponse):
""" Inverter generic events log """
@ -415,6 +443,9 @@ class Hm300Decode12(EventsResponse):
# 1141-Series Inverters, 2 MPPT, 1 Phase
class Hm600Decode01(HardwareInfoResponse):
""" Firmware version / date """
class Hm600Decode02(EventsResponse):
""" Inverter generic events log """
@ -492,6 +523,9 @@ class Hm600Decode0B(StatusResponse):
""" Event counter """
return self.unpack('>H', 40)[0]
class Hm600Decode0C(Hm600Decode0B):
""" 1141-series mirco-inverters status data """
class Hm600Decode11(EventsResponse):
""" Inverter generic events log """
@ -500,6 +534,9 @@ class Hm600Decode12(EventsResponse):
# 1161-Series Inverters, 2 MPPT, 1 Phase
class Hm1200Decode01(HardwareInfoResponse):
""" Firmware version / date """
class Hm1200Decode02(EventsResponse):
""" Inverter generic events log """
@ -619,6 +656,9 @@ class Hm1200Decode0B(StatusResponse):
""" Event counter """
return self.unpack('>H', 60)[0]
class Hm1200Decode0C(Hm1200Decode0B):
""" 1161-series mirco-inverters status data """
class Hm1200Decode11(EventsResponse):
""" Inverter generic events log """

Loading…
Cancel
Save