mirror of
https://github.com/lumapu/ahoy.git
synced 2025-06-05 12:11:39 +02:00
Merge branch 'lumapu:main' into dev03-sysinfos
This commit is contained in:
commit
4069214f3b
7 changed files with 132 additions and 93 deletions
33
.github/ISSUE_TEMPLATE/report-ahoy.md
vendored
33
.github/ISSUE_TEMPLATE/report-ahoy.md
vendored
|
@ -33,18 +33,29 @@ connected between +3.3V and GND (Pin 1 & 2) of the NRF Module
|
|||
* [ ] Image of the your wiring attached
|
||||
|
||||
### Connection diagram I used:
|
||||
| nRF24L01+ Pin | ESP8266/32 GPIO |
|
||||
| ------------- | --------------- |
|
||||
| Pin 1 GND [] | GND |
|
||||
| Pin 2 +3.3V | +3.3V |
|
||||
| Pin 3 CE | GPIO_2/_4 CE |
|
||||
| Pin 4 CSN | GPIO15/_5 CS |
|
||||
| Pin 5 SCK | GPIO14/18 SCLK |
|
||||
| Pin 6 MOSI | GPIO13/23 MOSI |
|
||||
| Pin 7 MISO | GPIO12/19 MISO |
|
||||
| Pin 8 IRQ | GPIO_0/0 IRQ |
|
||||
| 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 |
|
||||
|
||||
Note: [] GND Pin 1 has a square mark on the nRF24L01+ module
|
||||
| nRF24L01+ Pin | ESP32 GPIO |
|
||||
| ------------- | --------------- |
|
||||
| Pin 1 GND [*] | GND |
|
||||
| Pin 2 +3.3V | +3.3V |
|
||||
| Pin 3 CE | GPIO4 CE D4 |
|
||||
| Pin 4 CSN | GPIO5 CS D5 |
|
||||
| Pin 5 SCK | GPIO18 SCLK D18 |
|
||||
| Pin 6 MOSI | GPIO23 MOSI D23 |
|
||||
| Pin 7 MISO | GPIO19 MISO D19 |
|
||||
| Pin 8 IRQ | GPIO0 IRQ D0 |
|
||||
|
||||
Note: [*] GND Pin 1 has a square mark on the nRF24L01+ module
|
||||
|
||||
## Software
|
||||
* [ ] AhoyDTU
|
||||
|
|
33
.github/ISSUE_TEMPLATE/report.yaml
vendored
33
.github/ISSUE_TEMPLATE/report.yaml
vendored
|
@ -81,18 +81,29 @@ body:
|
|||
description: Tell us which connection diagram you used?
|
||||
value: |
|
||||
## 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 2 +3.3V | +3.3V |
|
||||
| Pin 3 CE | GPIO_2/_4 CE |
|
||||
| Pin 4 CSN | GPIO15/_5 CS |
|
||||
| Pin 5 SCK | GPIO14/18 SCLK |
|
||||
| Pin 6 MOSI | GPIO13/23 MOSI |
|
||||
| Pin 7 MISO | GPIO12/19 MISO |
|
||||
| Pin 8 IRQ | GPIO_0/0 IRQ |
|
||||
|
||||
Note: [] GND Pin 1 has a square mark on the nRF24L01+ module
|
||||
| Pin 1 GND [*] | GND |
|
||||
| Pin 2 +3.3V | +3.3V |
|
||||
| Pin 3 CE | GPIO4 CE D4 |
|
||||
| Pin 4 CSN | GPIO5 CS D5 |
|
||||
| Pin 5 SCK | GPIO18 SCLK D18 |
|
||||
| Pin 6 MOSI | GPIO23 MOSI D23 |
|
||||
| Pin 7 MISO | GPIO19 MISO D19 |
|
||||
| Pin 8 IRQ | GPIO0 IRQ D0 |
|
||||
|
||||
Note: [*] GND Pin 1 has a square mark on the nRF24L01+ module
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
|
|
|
@ -2,6 +2,12 @@
|
|||
|
||||
ahoy:
|
||||
interval: 5
|
||||
|
||||
logging:
|
||||
filename: 'hoymiles.log'
|
||||
# DEBUG, INFO, WARNING, ERROR, FATAL
|
||||
level: 'INFO'
|
||||
|
||||
sunset:
|
||||
disabled: false
|
||||
latitude: 51.799118
|
||||
|
|
|
@ -9,7 +9,7 @@ import struct
|
|||
import time
|
||||
import re
|
||||
from datetime import datetime
|
||||
import json
|
||||
import logging
|
||||
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 .decoders import *
|
||||
|
@ -51,16 +51,6 @@ def ser_to_esb_addr(inverter_ser):
|
|||
air_order = ser_to_hm_addr(inverter_ser)[::-1] + b'\x01'
|
||||
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:
|
||||
"""
|
||||
Prepare payload decoder
|
||||
|
@ -584,7 +574,7 @@ class InverterTransaction:
|
|||
|
||||
if HOYMILES_TRANSACTION_LOGGING:
|
||||
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)
|
||||
|
||||
|
@ -592,14 +582,14 @@ class InverterTransaction:
|
|||
try:
|
||||
for response in self.radio.receive():
|
||||
if HOYMILES_TRANSACTION_LOGGING:
|
||||
print(response)
|
||||
logging.debug(response)
|
||||
|
||||
self.frame_append(response)
|
||||
wait = True
|
||||
except TimeoutError:
|
||||
pass
|
||||
except BufferError as e:
|
||||
print(f'Buffer error {e}')
|
||||
logging.warning(f'Buffer error {e}')
|
||||
pass
|
||||
|
||||
return wait
|
||||
|
|
|
@ -19,6 +19,7 @@ import yaml
|
|||
from yaml.loader import SafeLoader
|
||||
import paho.mqtt.client
|
||||
import hoymiles
|
||||
import logging
|
||||
|
||||
class InfoCommands(IntEnum):
|
||||
InverterDevInform_Simple = 0 # 0x00
|
||||
|
@ -48,25 +49,24 @@ class SunsetHandler:
|
|||
longitude = sunset_config.get('longitude')
|
||||
altitude = sunset_config.get('altitude')
|
||||
self.suntimes = SunTimes(longitude=longitude, latitude=latitude, altitude=altitude)
|
||||
self.nextSunset = self.suntimes.setutc(datetime.now())
|
||||
print (f'Todays sunset is at {self.nextSunset}')
|
||||
self.nextSunset = self.suntimes.setutc(datetime.utcnow())
|
||||
logging.info (f'Todays sunset is at {self.nextSunset} UTC')
|
||||
|
||||
def checkWaitForSunrise(self):
|
||||
if not self.suntimes:
|
||||
return
|
||||
# if the sunset already happened for today
|
||||
now = datetime.now()
|
||||
now = datetime.utcnow()
|
||||
if self.nextSunset < now:
|
||||
# wait until the sun rises tomorrow
|
||||
tomorrow = now + timedelta(days=1)
|
||||
nextSunrise = self.suntimes.riseutc(tomorrow)
|
||||
self.nextSunset = self.suntimes.setutc(tomorrow)
|
||||
time_to_sleep = (nextSunrise - datetime.now()).total_seconds()
|
||||
print (f'Waiting for sunrise at {nextSunrise} ({time_to_sleep} seconds)')
|
||||
time_to_sleep = int((nextSunrise - datetime.now()).total_seconds())
|
||||
logging.info (f'Waiting for sunrise at {nextSunrise} UTC ({time_to_sleep} seconds)')
|
||||
if time_to_sleep > 0:
|
||||
time.sleep(time_to_sleep)
|
||||
print (f'Woke up... next sunset is at {self.nextSunset}')
|
||||
return
|
||||
logging.info (f'Woke up... next sunset is at {self.nextSunset} UTC')
|
||||
|
||||
def main_loop(ahoy_config):
|
||||
"""Main loop"""
|
||||
|
@ -75,6 +75,7 @@ def main_loop(ahoy_config):
|
|||
if not inverter.get('disabled', False)]
|
||||
|
||||
sunset = SunsetHandler(ahoy_config.get('sunset'))
|
||||
dtu_ser = ahoy_config.get('dtu', {}).get('serial')
|
||||
|
||||
loop_interval = ahoy_config.get('interval', 1)
|
||||
try:
|
||||
|
@ -86,12 +87,10 @@ def main_loop(ahoy_config):
|
|||
|
||||
for inverter in inverters:
|
||||
if hoymiles.HOYMILES_DEBUG_LOGGING:
|
||||
print(f'Poll inverter {inverter["serial"]}')
|
||||
poll_inverter(inverter, do_init)
|
||||
logging.debug(f'Poll inverter {inverter["serial"]}')
|
||||
poll_inverter(inverter, dtu_ser, do_init, 3)
|
||||
do_init = False
|
||||
|
||||
print('', end='', flush=True)
|
||||
|
||||
if loop_interval > 0:
|
||||
time_to_sleep = loop_interval - (time.time() - t_loop_start)
|
||||
if time_to_sleep > 0:
|
||||
|
@ -100,12 +99,12 @@ def main_loop(ahoy_config):
|
|||
except KeyboardInterrupt:
|
||||
sys.exit()
|
||||
except Exception as e:
|
||||
print ('Exception catched: %s' % e)
|
||||
traceback.print_exc()
|
||||
logging.fatal('Exception catched: %s' % e)
|
||||
logging.fatal(traceback.print_exc())
|
||||
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
|
||||
|
||||
|
@ -114,7 +113,6 @@ def poll_inverter(inverter, do_init, retries=4):
|
|||
:type retries: int
|
||||
"""
|
||||
inverter_ser = inverter.get('serial')
|
||||
dtu_ser = ahoy_config.get('dtu', {}).get('serial')
|
||||
|
||||
# Queue at least status data request
|
||||
inv_str = str(inverter_ser)
|
||||
|
@ -148,14 +146,14 @@ def poll_inverter(inverter, do_init, retries=4):
|
|||
response = com.get_payload()
|
||||
payload_ttl = 0
|
||||
except Exception as e_all:
|
||||
print(f'Error while retrieving data: {e_all}')
|
||||
logging.error(f'Error while retrieving data: {e_all}')
|
||||
pass
|
||||
|
||||
# Handle the response data if any
|
||||
if response:
|
||||
c_datetime = datetime.now()
|
||||
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,
|
||||
request=com.request,
|
||||
inverter_ser=inverter_ser
|
||||
|
@ -165,18 +163,18 @@ def poll_inverter(inverter, do_init, retries=4):
|
|||
data = result.__dict__()
|
||||
|
||||
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:
|
||||
print(f', pf={data["powerfactor"]}', end='')
|
||||
dbg += f', pf={data["powerfactor"]}'
|
||||
phase_id = 0
|
||||
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
|
||||
string_id = 0
|
||||
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
|
||||
print()
|
||||
logging.debug(dbg)
|
||||
|
||||
if 'event_count' in data:
|
||||
if event_message_index[inv_str] < data['event_count']:
|
||||
|
@ -257,7 +255,7 @@ def mqtt_on_command(client, userdata, message):
|
|||
inverter_ser = next(
|
||||
item[0] for item in mqtt_command_topic_subs if item[1] == message.topic)
|
||||
except StopIteration:
|
||||
print('Unexpedtedly received mqtt message for {message.topic}')
|
||||
logging.warning('Unexpedtedly received mqtt message for {message.topic}')
|
||||
|
||||
if inverter_ser:
|
||||
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(
|
||||
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__':
|
||||
parser = argparse.ArgumentParser(description='Ahoy - Hoymiles solar inverter gateway', prog="hoymiles")
|
||||
parser.add_argument("-c", "--config-file", nargs="?", required=True,
|
||||
help="configuration file")
|
||||
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,
|
||||
help="Enable debug output")
|
||||
help="Enable detailed debug output (loglevel must be DEBUG)")
|
||||
global_config = parser.parse_args()
|
||||
|
||||
# Load ahoy.yml config file
|
||||
|
@ -294,13 +309,14 @@ if __name__ == '__main__':
|
|||
with open('ahoy.yml', 'r') as fh_yaml:
|
||||
cfg = yaml.load(fh_yaml, Loader=SafeLoader)
|
||||
except FileNotFoundError:
|
||||
print("Could not load config file. Try --help")
|
||||
logging.error("Could not load config file. Try --help")
|
||||
sys.exit(2)
|
||||
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)
|
||||
|
||||
ahoy_config = dict(cfg.get('ahoy', {}))
|
||||
init_logging(ahoy_config)
|
||||
|
||||
# Prepare for multiple transceivers, makes them configurable (currently
|
||||
# only one supported)
|
||||
|
@ -367,4 +383,5 @@ if __name__ == '__main__':
|
|||
mqtt_client.subscribe(topic_item[1])
|
||||
mqtt_command_topic_subs.append(topic_item)
|
||||
|
||||
logging.info(f'Starting main_loop with inverter(s) {g_inverters}')
|
||||
main_loop(ahoy_config)
|
||||
|
|
|
@ -8,6 +8,7 @@ Hoymiles Micro-Inverters decoder library
|
|||
import struct
|
||||
from datetime import datetime, timedelta
|
||||
import crcmod
|
||||
import logging
|
||||
|
||||
f_crc_m = crcmod.predefined.mkPredefinedCrcFun('modbus')
|
||||
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]
|
||||
|
||||
print(f'{"Pos": <{cw}}', end='')
|
||||
print(''.join([f'{num: >{cw}}' for num in range(0, len(payload))]))
|
||||
print(f'{"Hex": <{cw}}', end='')
|
||||
print(''.join([f'{byte: >{cw}}' for byte in l_hexlified]))
|
||||
dbg = f'{"Pos": <{cw}}'
|
||||
dbg += ''.join([f'{num: >{cw}}' for num in range(0, len(payload))])
|
||||
logging.debug(dbg)
|
||||
dbg = f'{"Hex": <{cw}}'
|
||||
dbg += ''.join([f'{byte: >{cw}}' for byte in l_hexlified])
|
||||
logging.debug(dbg)
|
||||
|
||||
l_fmt = struct.calcsize(s_fmt)
|
||||
if len(payload) >= l_fmt:
|
||||
for offset in range(0, l_fmt):
|
||||
print(f'{s_fmt: <{cw}}', end='')
|
||||
print(' ' * cw * offset, end='')
|
||||
print(''.join(
|
||||
[f'{num[0]: >{cw*l_fmt}}' for num in g_unpack(s_fmt, payload[offset:])]))
|
||||
dbg = f'{s_fmt: <{cw}}'
|
||||
dbg += ' ' * cw * offset
|
||||
dbg += ''.join([f'{num[0]: >{cw*l_fmt}}' for num in g_unpack(s_fmt, payload[offset:])])
|
||||
logging.debug(dbg)
|
||||
|
||||
class Response:
|
||||
""" All Response Shared methods """
|
||||
|
@ -299,27 +302,28 @@ class EventsResponse(UnknownResponse):
|
|||
|
||||
crc_valid = self.validate_crc_m()
|
||||
if crc_valid:
|
||||
#print(' payload has valid modbus crc')
|
||||
#logging.debug(' payload has valid modbus crc')
|
||||
self.response = self.response[:-2]
|
||||
|
||||
status = struct.unpack('>H', self.response[:2])[0]
|
||||
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
|
||||
for i_chunk in range(2, len(self.response), 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])
|
||||
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']:
|
||||
print(f' {fmt:7}: ' + str(struct.unpack('>' + fmt, chunk)))
|
||||
print(end='', flush=True)
|
||||
dbg += f' {fmt:7}: ' + str(struct.unpack('>' + fmt, chunk))
|
||||
logging.debug(dbg)
|
||||
|
||||
class HardwareInfoResponse(UnknownResponse):
|
||||
def __init__(self, *args, **params):
|
||||
|
@ -340,8 +344,7 @@ class HardwareInfoResponse(UnknownResponse):
|
|||
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}')
|
||||
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}')
|
||||
|
||||
class DebugDecodeAny(UnknownResponse):
|
||||
"""Default decoder"""
|
||||
|
@ -351,40 +354,40 @@ class DebugDecodeAny(UnknownResponse):
|
|||
|
||||
crc8_valid = self.validate_crc8()
|
||||
if crc8_valid:
|
||||
print(' payload has valid crc8')
|
||||
logging.debug(' payload has valid crc8')
|
||||
self.response = self.response[:-1]
|
||||
|
||||
crc_valid = self.validate_crc_m()
|
||||
if crc_valid:
|
||||
print(' payload has valid modbus crc')
|
||||
logging.debug(' payload has valid modbus crc')
|
||||
self.response = self.response[:-2]
|
||||
|
||||
l_payload = len(self.response)
|
||||
print(f' payload has {l_payload} bytes')
|
||||
logging.debug(f' payload has {l_payload} bytes')
|
||||
|
||||
print()
|
||||
print('Field view: int')
|
||||
logging.debug()
|
||||
logging.debug('Field view: int')
|
||||
print_table_unpack('>B', self.response)
|
||||
|
||||
print()
|
||||
print('Field view: shorts')
|
||||
logging.debug()
|
||||
logging.debug('Field view: shorts')
|
||||
print_table_unpack('>H', self.response)
|
||||
|
||||
print()
|
||||
print('Field view: longs')
|
||||
logging.debug()
|
||||
logging.debug('Field view: longs')
|
||||
print_table_unpack('>L', self.response)
|
||||
|
||||
try:
|
||||
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:
|
||||
print(' type utf-8 : utf-8 decode error')
|
||||
logging.debug(' type utf-8 : utf-8 decode error')
|
||||
|
||||
try:
|
||||
if len(self.response) > 2:
|
||||
print(' type ascii : ' + self.response.decode('ascii'))
|
||||
logging.debug(' type ascii : ' + self.response.decode('ascii'))
|
||||
except UnicodeDecodeError:
|
||||
print(' type ascii : ascii decode error')
|
||||
logging.debug(' type ascii : ascii decode error')
|
||||
|
||||
|
||||
# 1121-Series Intervers, 1 MPPT, 1 Phase
|
||||
|
|
|
@ -6,6 +6,7 @@ Hoymiles output plugin library
|
|||
"""
|
||||
|
||||
import socket
|
||||
import logging
|
||||
from datetime import datetime, timezone
|
||||
from hoymiles.decoders import StatusResponse
|
||||
|
||||
|
@ -310,4 +311,4 @@ class VolkszaehlerOutputPlugin(OutputPluginFactory):
|
|||
try:
|
||||
output.store_status(data, self.session)
|
||||
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…
Add table
Add a link
Reference in a new issue