diff --git a/solar_ble.py b/solar_ble.py index 8f5e03c..d778c47 100755 --- a/solar_ble.py +++ b/solar_ble.py @@ -5,10 +5,13 @@ import datetime import struct import sys import time +from io import RawIOBase from bluepy import btle from libscrc import modbus +from feasycom_ble import BTLEUart + MAC = "DC:0D:30:9C:61:BA" INTERVAL = 15 @@ -158,72 +161,51 @@ CMD_ = b"\xff\x78\x00\x00\x00\x01" STATUS = {} -def parsePacket(data): - timestamp = datetime.datetime.utcnow().isoformat(" ") - # prefix = data[0] - operation = data[1] - cc = data[2] - res = None - if operation == 3: - if cc == 0x0E: # GET_BATTERY_STATE - res = dict( - zip( - ( - "battery_charge", - "battery_voltage", - "battery_current", - "_internal_temperature?", - "battery_temperature", - "load_voltage", - "load_current", - "load_power", - ), - struct.unpack("!xxxHHHbbHHHxx", data), - ) - ) - res["battery_voltage"] /= 10 - res["battery_current"] /= 100 - res["load_voltage"] /= 10 - res["load_current"] /= 100 - STATUS.update(res) +# GET_BATTERY_STATE +def parse_battery_state(data: bytes) -> dict: + res = dict( + zip( + ( + "battery_charge", + "battery_voltage", + "battery_current", + "_internal_temperature?", + "battery_temperature", + "load_voltage", + "load_current", + "load_power", + ), + struct.unpack("!HHHbbHHH", data), + ) + ) + res["battery_voltage"] /= 10 + res["battery_current"] /= 100 + res["load_voltage"] /= 10 + res["load_current"] /= 100 + STATUS.update(res) - elif cc == 0x08: # GET_PANEL_STATUS (OR version) - res = dict( - zip( - ("panel_voltage", "panel_current", "panel_power", "load_enabled"), - struct.unpack("!xxxHHHx?xx", data), - ) - ) - res["panel_voltage"] /= 10 - res["panel_current"] /= 100 - STATUS.update(res) - elif operation == 6 and cc == 1: - res = dict(zip(("load_enabled",), struct.unpack("!xxxxx?xx", data))) - STATUS.update(res) - - if res: - print(timestamp, res) - return res - print(timestamp, data) - sys.stdout.flush() + log(str(res)) + return res -class Delegate(btle.DefaultDelegate): - data = bytearray() +# GET_PANEL_STATUS +def parse_panel_status(data: bytes) -> dict: + res = dict( + zip( + ("panel_voltage", "panel_current", "panel_power", "load_enabled"), + struct.unpack("!HHHx?", data), + ) + ) + res["panel_voltage"] /= 10 + res["panel_current"] /= 100 + STATUS.update(res) - def handleNotification(self, cHandle, data): - # print(cHandle, data, dlen) + # elif operation == 6 and cc == 1: + # res = dict(zip(("load_enabled",), struct.unpack("!xxxxx?xx", data))) + # STATUS.update(res) - self.data.extend(data) - - c_crc = modbus(bytes(self.data[:-2])) - # byte order is inverted in regards to libscrc output - d_crc = self.data[-1] << 8 | self.data[-2] - # print(hex(c_crc), hex(d_crc)) - - if c_crc == d_crc: - parsePacket(self.data) - self.data.clear() + log(str(res)) + return res def write(fh, data): @@ -238,18 +220,6 @@ def construct_request(address, words=1, action=ACTION_READ, marker=0xFF): return struct.pack("!BBHH", marker, action, address, words) -def poll(dev: btle.Peripheral, timeout: float = 1) -> bool: - start = time.time() - while not dev.waitForNotifications(0.2): - if time.time() < start + timeout: - return False - - while dev.waitForNotifications(0.2): - pass - - return True - - def log(string: str): print(datetime.datetime.utcnow().isoformat(" "), string) sys.stdout.flush() @@ -275,51 +245,50 @@ def parse_packet(data): return payload +def readMemory(fh: RawIOBase, address: int, words: int = 1): + write(fh, construct_request(address, words=words)) + header = fh.read(3) + if header and len(header) == 3: + tag, operation, size = header + data = fh.read(size) + _crc = fh.read(2) + if data and _crc: + crc = struct.unpack_from("= INTERVAL: prev += INTERVAL - write(wd, construct_request(0x0107, 4)) # CMD_GET_PANEL_STATUS - poll(dev) + res = readMemory(dev, 0x0107, 4) # CMD_GET_PANEL_STATUS + if res: + parse_panel_status(res) - write(wd, construct_request(0x0100, 7)) # CMD_GET_BATTERY_STATE - poll(dev) + res = readMemory(dev, 0x0100, 7) # CMD_GET_BATTERY_STATE + if res: + parse_battery_state(res) # if STATUS.get('load_enabled'): # write(wd, CMD_DISABLE_LOAD)