Browse Source

Merge branch 'lumapu:main' into dev03-sysinfos

pull/426/head^2
DanielR92 2 years ago
committed by GitHub
parent
commit
4069214f3b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 31
      .github/ISSUE_TEMPLATE/report-ahoy.md
  2. 33
      .github/ISSUE_TEMPLATE/report.yaml
  3. 6
      tools/rpi/ahoy.yml.example
  4. 18
      tools/rpi/hoymiles/__init__.py
  5. 71
      tools/rpi/hoymiles/__main__.py
  6. 61
      tools/rpi/hoymiles/decoders/__init__.py
  7. 3
      tools/rpi/hoymiles/outputs.py

31
.github/ISSUE_TEMPLATE/report-ahoy.md

@ -33,18 +33,29 @@ connected between +3.3V and GND (Pin 1 & 2) of the NRF Module
* [ ] Image of the your wiring attached * [ ] Image of the your wiring attached
### Connection diagram I used: ### Connection diagram I used:
| nRF24L01+ Pin | ESP8266/32 GPIO | | nRF24L01+ Pin | ESP8266 GPIO |
| ------------- | -------------- |
| Pin 1 GND [*] | GND |
| Pin 2 +3.3V | +3.3V |
| Pin 3 CE | GPIO2 CE D4 |
| Pin 4 CSN | GPIO15 CS D8 |
| Pin 5 SCK | GPIO14 SCLK D5 |
| Pin 6 MOSI | GPIO13 MOSI D7 |
| Pin 7 MISO | GPIO12 MISO D6 |
| Pin 8 IRQ | GPIO0 IRQ D3 |
| nRF24L01+ Pin | ESP32 GPIO |
| ------------- | --------------- | | ------------- | --------------- |
| Pin 1 GND [] | GND | | Pin 1 GND [*] | GND |
| Pin 2 +3.3V | +3.3V | | Pin 2 +3.3V | +3.3V |
| Pin 3 CE | GPIO_2/_4 CE | | Pin 3 CE | GPIO4 CE D4 |
| Pin 4 CSN | GPIO15/_5 CS | | Pin 4 CSN | GPIO5 CS D5 |
| Pin 5 SCK | GPIO14/18 SCLK | | Pin 5 SCK | GPIO18 SCLK D18 |
| Pin 6 MOSI | GPIO13/23 MOSI | | Pin 6 MOSI | GPIO23 MOSI D23 |
| Pin 7 MISO | GPIO12/19 MISO | | Pin 7 MISO | GPIO19 MISO D19 |
| Pin 8 IRQ | GPIO_0/0 IRQ | | Pin 8 IRQ | GPIO0 IRQ D0 |
Note: [] GND Pin 1 has a square mark on the nRF24L01+ module Note: [*] GND Pin 1 has a square mark on the nRF24L01+ module
## Software ## Software
* [ ] AhoyDTU * [ ] AhoyDTU

33
.github/ISSUE_TEMPLATE/report.yaml

@ -81,18 +81,29 @@ body:
description: Tell us which connection diagram you used? description: Tell us which connection diagram you used?
value: | value: |
## Connection diagram I used: ## Connection diagram I used:
| nRF24L01+ Pin | ESP8266/32 GPIO | | nRF24L01+ Pin | ESP8266 GPIO |
| ------------- | -------------- |
| Pin 1 GND [*] | GND |
| Pin 2 +3.3V | +3.3V |
| Pin 3 CE | GPIO2 CE D4 |
| Pin 4 CSN | GPIO15 CS D8 |
| Pin 5 SCK | GPIO14 SCLK D5 |
| Pin 6 MOSI | GPIO13 MOSI D7 |
| Pin 7 MISO | GPIO12 MISO D6 |
| Pin 8 IRQ | GPIO0 IRQ D3 |
| nRF24L01+ Pin | ESP32 GPIO |
| ------------- | --------------- | | ------------- | --------------- |
| Pin 1 GND [] | GND | | Pin 1 GND [*] | GND |
| Pin 2 +3.3V | +3.3V | | Pin 2 +3.3V | +3.3V |
| Pin 3 CE | GPIO_2/_4 CE | | Pin 3 CE | GPIO4 CE D4 |
| Pin 4 CSN | GPIO15/_5 CS | | Pin 4 CSN | GPIO5 CS D5 |
| Pin 5 SCK | GPIO14/18 SCLK | | Pin 5 SCK | GPIO18 SCLK D18 |
| Pin 6 MOSI | GPIO13/23 MOSI | | Pin 6 MOSI | GPIO23 MOSI D23 |
| Pin 7 MISO | GPIO12/19 MISO | | Pin 7 MISO | GPIO19 MISO D19 |
| Pin 8 IRQ | GPIO_0/0 IRQ | | Pin 8 IRQ | GPIO0 IRQ D0 |
Note: [] GND Pin 1 has a square mark on the nRF24L01+ module Note: [*] GND Pin 1 has a square mark on the nRF24L01+ module
validations: validations:
required: true required: true
- type: checkboxes - type: checkboxes

6
tools/rpi/ahoy.yml.example

@ -2,6 +2,12 @@
ahoy: ahoy:
interval: 5 interval: 5
logging:
filename: 'hoymiles.log'
# DEBUG, INFO, WARNING, ERROR, FATAL
level: 'INFO'
sunset: sunset:
disabled: false disabled: false
latitude: 51.799118 latitude: 51.799118

18
tools/rpi/hoymiles/__init__.py

@ -9,7 +9,7 @@ import struct
import time import time
import re import re
from datetime import datetime from datetime import datetime
import json import logging
import crcmod import crcmod
from RF24 import RF24, RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX, RF24_250KBPS, RF24_CRC_DISABLED, RF24_CRC_8, RF24_CRC_16 from RF24 import RF24, RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX, RF24_250KBPS, RF24_CRC_DISABLED, RF24_CRC_8, RF24_CRC_16
from .decoders import * from .decoders import *
@ -51,16 +51,6 @@ def ser_to_esb_addr(inverter_ser):
air_order = ser_to_hm_addr(inverter_ser)[::-1] + b'\x01' air_order = ser_to_hm_addr(inverter_ser)[::-1] + b'\x01'
return air_order[::-1] return air_order[::-1]
def print_addr(inverter_ser):
"""
Debug print addresses
:param str inverter_ser: inverter serial
"""
print(f"ser# {inverter_ser} ", end='')
print(f" -> HM {' '.join([f'{byte:02x}' for byte in ser_to_hm_addr(inverter_ser)])}", end='')
print(f" -> ESB {' '.join([f'{byte:02x}' for byte in ser_to_esb_addr(inverter_ser)])}")
class ResponseDecoderFactory: class ResponseDecoderFactory:
""" """
Prepare payload decoder Prepare payload decoder
@ -584,7 +574,7 @@ class InverterTransaction:
if HOYMILES_TRANSACTION_LOGGING: if HOYMILES_TRANSACTION_LOGGING:
c_datetime = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f") c_datetime = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
print(f'{c_datetime} Transmit {len(packet)} | {hexify_payload(packet)}') logging.debug(f'{c_datetime} Transmit {len(packet)} | {hexify_payload(packet)}')
self.radio.transmit(packet, txpower=self.txpower) self.radio.transmit(packet, txpower=self.txpower)
@ -592,14 +582,14 @@ class InverterTransaction:
try: try:
for response in self.radio.receive(): for response in self.radio.receive():
if HOYMILES_TRANSACTION_LOGGING: if HOYMILES_TRANSACTION_LOGGING:
print(response) logging.debug(response)
self.frame_append(response) self.frame_append(response)
wait = True wait = True
except TimeoutError: except TimeoutError:
pass pass
except BufferError as e: except BufferError as e:
print(f'Buffer error {e}') logging.warning(f'Buffer error {e}')
pass pass
return wait return wait

71
tools/rpi/hoymiles/__main__.py

@ -19,6 +19,7 @@ import yaml
from yaml.loader import SafeLoader from yaml.loader import SafeLoader
import paho.mqtt.client import paho.mqtt.client
import hoymiles import hoymiles
import logging
class InfoCommands(IntEnum): class InfoCommands(IntEnum):
InverterDevInform_Simple = 0 # 0x00 InverterDevInform_Simple = 0 # 0x00
@ -48,25 +49,24 @@ class SunsetHandler:
longitude = sunset_config.get('longitude') longitude = sunset_config.get('longitude')
altitude = sunset_config.get('altitude') altitude = sunset_config.get('altitude')
self.suntimes = SunTimes(longitude=longitude, latitude=latitude, altitude=altitude) self.suntimes = SunTimes(longitude=longitude, latitude=latitude, altitude=altitude)
self.nextSunset = self.suntimes.setutc(datetime.now()) self.nextSunset = self.suntimes.setutc(datetime.utcnow())
print (f'Todays sunset is at {self.nextSunset}') logging.info (f'Todays sunset is at {self.nextSunset} UTC')
def checkWaitForSunrise(self): def checkWaitForSunrise(self):
if not self.suntimes: if not self.suntimes:
return return
# if the sunset already happened for today # if the sunset already happened for today
now = datetime.now() now = datetime.utcnow()
if self.nextSunset < now: if self.nextSunset < now:
# wait until the sun rises tomorrow # wait until the sun rises tomorrow
tomorrow = now + timedelta(days=1) tomorrow = now + timedelta(days=1)
nextSunrise = self.suntimes.riseutc(tomorrow) nextSunrise = self.suntimes.riseutc(tomorrow)
self.nextSunset = self.suntimes.setutc(tomorrow) self.nextSunset = self.suntimes.setutc(tomorrow)
time_to_sleep = (nextSunrise - datetime.now()).total_seconds() time_to_sleep = int((nextSunrise - datetime.now()).total_seconds())
print (f'Waiting for sunrise at {nextSunrise} ({time_to_sleep} seconds)') logging.info (f'Waiting for sunrise at {nextSunrise} UTC ({time_to_sleep} seconds)')
if time_to_sleep > 0: if time_to_sleep > 0:
time.sleep(time_to_sleep) time.sleep(time_to_sleep)
print (f'Woke up... next sunset is at {self.nextSunset}') logging.info (f'Woke up... next sunset is at {self.nextSunset} UTC')
return
def main_loop(ahoy_config): def main_loop(ahoy_config):
"""Main loop""" """Main loop"""
@ -75,6 +75,7 @@ def main_loop(ahoy_config):
if not inverter.get('disabled', False)] if not inverter.get('disabled', False)]
sunset = SunsetHandler(ahoy_config.get('sunset')) sunset = SunsetHandler(ahoy_config.get('sunset'))
dtu_ser = ahoy_config.get('dtu', {}).get('serial')
loop_interval = ahoy_config.get('interval', 1) loop_interval = ahoy_config.get('interval', 1)
try: try:
@ -86,12 +87,10 @@ def main_loop(ahoy_config):
for inverter in inverters: for inverter in inverters:
if hoymiles.HOYMILES_DEBUG_LOGGING: if hoymiles.HOYMILES_DEBUG_LOGGING:
print(f'Poll inverter {inverter["serial"]}') logging.debug(f'Poll inverter {inverter["serial"]}')
poll_inverter(inverter, do_init) poll_inverter(inverter, dtu_ser, do_init, 3)
do_init = False do_init = False
print('', end='', flush=True)
if loop_interval > 0: if loop_interval > 0:
time_to_sleep = loop_interval - (time.time() - t_loop_start) time_to_sleep = loop_interval - (time.time() - t_loop_start)
if time_to_sleep > 0: if time_to_sleep > 0:
@ -100,12 +99,12 @@ def main_loop(ahoy_config):
except KeyboardInterrupt: except KeyboardInterrupt:
sys.exit() sys.exit()
except Exception as e: except Exception as e:
print ('Exception catched: %s' % e) logging.fatal('Exception catched: %s' % e)
traceback.print_exc() logging.fatal(traceback.print_exc())
raise raise
def poll_inverter(inverter, do_init, retries=4): def poll_inverter(inverter, dtu_ser, do_init, retries):
""" """
Send/Receive command_queue, initiate status poll on inverter Send/Receive command_queue, initiate status poll on inverter
@ -114,7 +113,6 @@ def poll_inverter(inverter, do_init, retries=4):
:type retries: int :type retries: int
""" """
inverter_ser = inverter.get('serial') inverter_ser = inverter.get('serial')
dtu_ser = ahoy_config.get('dtu', {}).get('serial')
# Queue at least status data request # Queue at least status data request
inv_str = str(inverter_ser) inv_str = str(inverter_ser)
@ -148,14 +146,14 @@ def poll_inverter(inverter, do_init, retries=4):
response = com.get_payload() response = com.get_payload()
payload_ttl = 0 payload_ttl = 0
except Exception as e_all: except Exception as e_all:
print(f'Error while retrieving data: {e_all}') logging.error(f'Error while retrieving data: {e_all}')
pass pass
# Handle the response data if any # Handle the response data if any
if response: if response:
c_datetime = datetime.now() c_datetime = datetime.now()
if hoymiles.HOYMILES_DEBUG_LOGGING: if hoymiles.HOYMILES_DEBUG_LOGGING:
print(f'{c_datetime} Payload: ' + hoymiles.hexify_payload(response)) logging.debug(f'{c_datetime} Payload: ' + hoymiles.hexify_payload(response))
decoder = hoymiles.ResponseDecoder(response, decoder = hoymiles.ResponseDecoder(response,
request=com.request, request=com.request,
inverter_ser=inverter_ser inverter_ser=inverter_ser
@ -165,18 +163,18 @@ def poll_inverter(inverter, do_init, retries=4):
data = result.__dict__() data = result.__dict__()
if hoymiles.HOYMILES_DEBUG_LOGGING: if hoymiles.HOYMILES_DEBUG_LOGGING:
print(f'{c_datetime} Decoded: temp={data["temperature"]}, total={data["energy_total"]/1000:.3f}', end='') dbg = f'{c_datetime} Decoded: temp={data["temperature"]}, total={data["energy_total"]/1000:.3f}'
if data['powerfactor'] is not None: if data['powerfactor'] is not None:
print(f', pf={data["powerfactor"]}', end='') dbg += f', pf={data["powerfactor"]}'
phase_id = 0 phase_id = 0
for phase in data['phases']: for phase in data['phases']:
print(f' phase{phase_id}=voltage:{phase["voltage"]}, current:{phase["current"]}, power:{phase["power"]}, frequency:{data["frequency"]}', end='') dbg += f' phase{phase_id}=voltage:{phase["voltage"]}, current:{phase["current"]}, power:{phase["power"]}, frequency:{data["frequency"]}'
phase_id = phase_id + 1 phase_id = phase_id + 1
string_id = 0 string_id = 0
for string in data['strings']: for string in data['strings']:
print(f' string{string_id}=voltage:{string["voltage"]}, current:{string["current"]}, power:{string["power"]}, total:{string["energy_total"]/1000}, daily:{string["energy_daily"]}', end='') dbg += f' string{string_id}=voltage:{string["voltage"]}, current:{string["current"]}, power:{string["power"]}, total:{string["energy_total"]/1000}, daily:{string["energy_daily"]}'
string_id = string_id + 1 string_id = string_id + 1
print() logging.debug(dbg)
if 'event_count' in data: if 'event_count' in data:
if event_message_index[inv_str] < data['event_count']: if event_message_index[inv_str] < data['event_count']:
@ -257,7 +255,7 @@ def mqtt_on_command(client, userdata, message):
inverter_ser = next( inverter_ser = next(
item[0] for item in mqtt_command_topic_subs if item[1] == message.topic) item[0] for item in mqtt_command_topic_subs if item[1] == message.topic)
except StopIteration: except StopIteration:
print('Unexpedtedly received mqtt message for {message.topic}') logging.warning('Unexpedtedly received mqtt message for {message.topic}')
if inverter_ser: if inverter_ser:
p_message = message.payload.decode('utf-8').lower() p_message = message.payload.decode('utf-8').lower()
@ -275,14 +273,31 @@ def mqtt_on_command(client, userdata, message):
command_queue[str(inverter_ser)].append( command_queue[str(inverter_ser)].append(
hoymiles.frame_payload(payload[1:])) hoymiles.frame_payload(payload[1:]))
def init_logging(ahoy_config):
log_config = ahoy_config.get('logging')
fn = 'hoymiles.log'
lvl = logging.ERROR
if log_config:
fn = log_config.get('filename', fn)
level = log_config.get('level', 'ERROR')
if level == 'DEBUG':
lvl = logging.DEBUG
elif level == 'INFO':
lvl = logging.INFO
elif level == 'WARNING':
lvl = logging.WARNING
elif level == 'ERROR':
lvl = logging.ERROR
logging.basicConfig(filename=fn, format='%(asctime)s %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S', level=lvl)
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Ahoy - Hoymiles solar inverter gateway', prog="hoymiles") parser = argparse.ArgumentParser(description='Ahoy - Hoymiles solar inverter gateway', prog="hoymiles")
parser.add_argument("-c", "--config-file", nargs="?", required=True, parser.add_argument("-c", "--config-file", nargs="?", required=True,
help="configuration file") help="configuration file")
parser.add_argument("--log-transactions", action="store_true", default=False, parser.add_argument("--log-transactions", action="store_true", default=False,
help="Enable transaction logging output") help="Enable transaction logging output (loglevel must be DEBUG)")
parser.add_argument("--verbose", action="store_true", default=False, parser.add_argument("--verbose", action="store_true", default=False,
help="Enable debug output") help="Enable detailed debug output (loglevel must be DEBUG)")
global_config = parser.parse_args() global_config = parser.parse_args()
# Load ahoy.yml config file # Load ahoy.yml config file
@ -294,13 +309,14 @@ if __name__ == '__main__':
with open('ahoy.yml', 'r') as fh_yaml: with open('ahoy.yml', 'r') as fh_yaml:
cfg = yaml.load(fh_yaml, Loader=SafeLoader) cfg = yaml.load(fh_yaml, Loader=SafeLoader)
except FileNotFoundError: except FileNotFoundError:
print("Could not load config file. Try --help") logging.error("Could not load config file. Try --help")
sys.exit(2) sys.exit(2)
except yaml.YAMLError as e_yaml: except yaml.YAMLError as e_yaml:
print('Failed to load config frile {global_config.config_file}: {e_yaml}') logging.error('Failed to load config file {global_config.config_file}: {e_yaml}')
sys.exit(1) sys.exit(1)
ahoy_config = dict(cfg.get('ahoy', {})) ahoy_config = dict(cfg.get('ahoy', {}))
init_logging(ahoy_config)
# Prepare for multiple transceivers, makes them configurable (currently # Prepare for multiple transceivers, makes them configurable (currently
# only one supported) # only one supported)
@ -367,4 +383,5 @@ if __name__ == '__main__':
mqtt_client.subscribe(topic_item[1]) mqtt_client.subscribe(topic_item[1])
mqtt_command_topic_subs.append(topic_item) mqtt_command_topic_subs.append(topic_item)
logging.info(f'Starting main_loop with inverter(s) {g_inverters}')
main_loop(ahoy_config) main_loop(ahoy_config)

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

@ -8,6 +8,7 @@ Hoymiles Micro-Inverters decoder library
import struct import struct
from datetime import datetime, timedelta from datetime import datetime, timedelta
import crcmod import crcmod
import logging
f_crc_m = crcmod.predefined.mkPredefinedCrcFun('modbus') f_crc_m = crcmod.predefined.mkPredefinedCrcFun('modbus')
f_crc8 = crcmod.mkCrcFun(0x101, initCrc=0, xorOut=0) f_crc8 = crcmod.mkCrcFun(0x101, initCrc=0, xorOut=0)
@ -44,18 +45,20 @@ def print_table_unpack(s_fmt, payload, cw=6):
l_hexlified = [f'{byte:02x}' for byte in payload] l_hexlified = [f'{byte:02x}' for byte in payload]
print(f'{"Pos": <{cw}}', end='') dbg = f'{"Pos": <{cw}}'
print(''.join([f'{num: >{cw}}' for num in range(0, len(payload))])) dbg += ''.join([f'{num: >{cw}}' for num in range(0, len(payload))])
print(f'{"Hex": <{cw}}', end='') logging.debug(dbg)
print(''.join([f'{byte: >{cw}}' for byte in l_hexlified])) dbg = f'{"Hex": <{cw}}'
dbg += ''.join([f'{byte: >{cw}}' for byte in l_hexlified])
logging.debug(dbg)
l_fmt = struct.calcsize(s_fmt) l_fmt = struct.calcsize(s_fmt)
if len(payload) >= l_fmt: if len(payload) >= l_fmt:
for offset in range(0, l_fmt): for offset in range(0, l_fmt):
print(f'{s_fmt: <{cw}}', end='') dbg = f'{s_fmt: <{cw}}'
print(' ' * cw * offset, end='') dbg += ' ' * cw * offset
print(''.join( dbg += ''.join([f'{num[0]: >{cw*l_fmt}}' for num in g_unpack(s_fmt, payload[offset:])])
[f'{num[0]: >{cw*l_fmt}}' for num in g_unpack(s_fmt, payload[offset:])])) logging.debug(dbg)
class Response: class Response:
""" All Response Shared methods """ """ All Response Shared methods """
@ -299,27 +302,28 @@ class EventsResponse(UnknownResponse):
crc_valid = self.validate_crc_m() crc_valid = self.validate_crc_m()
if crc_valid: if crc_valid:
#print(' payload has valid modbus crc') #logging.debug(' payload has valid modbus crc')
self.response = self.response[:-2] self.response = self.response[:-2]
status = struct.unpack('>H', self.response[:2])[0] status = struct.unpack('>H', self.response[:2])[0]
a_text = self.alarm_codes.get(status, 'N/A') a_text = self.alarm_codes.get(status, 'N/A')
print (f' Inverter status: {a_text} ({status})') logging.info (f' Inverter status: {a_text} ({status})')
chunk_size = 12 chunk_size = 12
for i_chunk in range(2, len(self.response), chunk_size): for i_chunk in range(2, len(self.response), chunk_size):
chunk = self.response[i_chunk:i_chunk+chunk_size] chunk = self.response[i_chunk:i_chunk+chunk_size]
print(' '.join([f'{byte:02x}' for byte in chunk]) + ': ') logging.debug(' '.join([f'{byte:02x}' for byte in chunk]) + ': ')
opcode, a_code, a_count, uptime_sec = struct.unpack('>BBHH', chunk[0:6]) opcode, a_code, a_count, uptime_sec = struct.unpack('>BBHH', chunk[0:6])
a_text = self.alarm_codes.get(a_code, 'N/A') a_text = self.alarm_codes.get(a_code, 'N/A')
print(f' uptime={timedelta(seconds=uptime_sec)} a_count={a_count} opcode={opcode} a_code={a_code} a_text={a_text}') logging.debug(f' uptime={timedelta(seconds=uptime_sec)} a_count={a_count} opcode={opcode} a_code={a_code} a_text={a_text}')
dbg = ''
for fmt in ['BBHHHHH']: for fmt in ['BBHHHHH']:
print(f' {fmt:7}: ' + str(struct.unpack('>' + fmt, chunk))) dbg += f' {fmt:7}: ' + str(struct.unpack('>' + fmt, chunk))
print(end='', flush=True) logging.debug(dbg)
class HardwareInfoResponse(UnknownResponse): class HardwareInfoResponse(UnknownResponse):
def __init__(self, *args, **params): def __init__(self, *args, **params):
@ -340,8 +344,7 @@ class HardwareInfoResponse(UnknownResponse):
fw_version_pat = int((fw_version % 100)) fw_version_pat = int((fw_version % 100))
fw_build_mm = int(fw_build_mmdd / 100) fw_build_mm = int(fw_build_mmdd / 100)
fw_build_dd = int(fw_build_mmdd % 100) fw_build_dd = int(fw_build_mmdd % 100)
print() logging.debug(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}')
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): class DebugDecodeAny(UnknownResponse):
"""Default decoder""" """Default decoder"""
@ -351,40 +354,40 @@ class DebugDecodeAny(UnknownResponse):
crc8_valid = self.validate_crc8() crc8_valid = self.validate_crc8()
if crc8_valid: if crc8_valid:
print(' payload has valid crc8') logging.debug(' payload has valid crc8')
self.response = self.response[:-1] self.response = self.response[:-1]
crc_valid = self.validate_crc_m() crc_valid = self.validate_crc_m()
if crc_valid: if crc_valid:
print(' payload has valid modbus crc') logging.debug(' payload has valid modbus crc')
self.response = self.response[:-2] self.response = self.response[:-2]
l_payload = len(self.response) l_payload = len(self.response)
print(f' payload has {l_payload} bytes') logging.debug(f' payload has {l_payload} bytes')
print() logging.debug()
print('Field view: int') logging.debug('Field view: int')
print_table_unpack('>B', self.response) print_table_unpack('>B', self.response)
print() logging.debug()
print('Field view: shorts') logging.debug('Field view: shorts')
print_table_unpack('>H', self.response) print_table_unpack('>H', self.response)
print() logging.debug()
print('Field view: longs') logging.debug('Field view: longs')
print_table_unpack('>L', self.response) print_table_unpack('>L', self.response)
try: try:
if len(self.response) > 2: if len(self.response) > 2:
print(' type utf-8 : ' + self.response.decode('utf-8')) logging.debug(' type utf-8 : ' + self.response.decode('utf-8'))
except UnicodeDecodeError: except UnicodeDecodeError:
print(' type utf-8 : utf-8 decode error') logging.debug(' type utf-8 : utf-8 decode error')
try: try:
if len(self.response) > 2: if len(self.response) > 2:
print(' type ascii : ' + self.response.decode('ascii')) logging.debug(' type ascii : ' + self.response.decode('ascii'))
except UnicodeDecodeError: except UnicodeDecodeError:
print(' type ascii : ascii decode error') logging.debug(' type ascii : ascii decode error')
# 1121-Series Intervers, 1 MPPT, 1 Phase # 1121-Series Intervers, 1 MPPT, 1 Phase

3
tools/rpi/hoymiles/outputs.py

@ -6,6 +6,7 @@ Hoymiles output plugin library
""" """
import socket import socket
import logging
from datetime import datetime, timezone from datetime import datetime, timezone
from hoymiles.decoders import StatusResponse from hoymiles.decoders import StatusResponse
@ -310,4 +311,4 @@ class VolkszaehlerOutputPlugin(OutputPluginFactory):
try: try:
output.store_status(data, self.session) output.store_status(data, self.session)
except ValueError as e: except ValueError as e:
print('Could not send data to volkszaehler instance: %s' % e) logging.warning('Could not send data to volkszaehler instance: %s' % e)

Loading…
Cancel
Save