From b008f524bf1622c66932f0ea493c2a06d7c9807f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Odd=20Str=C3=A5b=C3=B8?= <oddstr13@openshell.no>
Date: Thu, 18 Nov 2021 23:38:52 +0100
Subject: [PATCH] Retry on read error

---
 solar_ble.py | 88 ++++++++++++++++++++++++++++------------------------
 1 file changed, 48 insertions(+), 40 deletions(-)

diff --git a/solar_ble.py b/solar_ble.py
index 3c843b1..234624b 100755
--- a/solar_ble.py
+++ b/solar_ble.py
@@ -6,7 +6,7 @@ import struct
 import sys
 import time
 from io import RawIOBase
-from typing import Collection, Optional, cast
+from typing import Callable, Collection, Optional, cast
 
 from bluepy import btle
 from libscrc import modbus
@@ -293,6 +293,27 @@ class Periodical:
         return False
 
 
+def try_read_parse(
+    dev: BTLEUart,
+    address: int,
+    words: int = 1,
+    parser: Callable = None,
+    attempts=5,
+) -> Optional[dict]:
+    while attempts:
+        attempts -= 1
+        res = readMemory(dev, address, words)
+        if res:
+            try:
+                if parser:
+                    return parser(res)
+            except struct.error as e:
+                log(e)
+                log("0x0100 Unpack error:", len(res), res)
+                log("Flushed from read buffer; ", dev.read(timeout=0.5))
+    return None
+
+
 if __name__ == "__main__":
     conf = get_config()
     consumers = get_consumers(conf)
@@ -304,7 +325,7 @@ if __name__ == "__main__":
         while True:
             try:
                 log("Connecting...")
-                with BTLEUart(MAC, timeout=10) as dev:
+                with BTLEUart(MAC, timeout=5) as dev:
                     log("Connected.")
 
                     # write(dev, construct_request(0, 32))
@@ -314,58 +335,45 @@ if __name__ == "__main__":
                     #    log(f"Reading 0x{address:04X}...")
                     #    write(wd, construct_request(address, 16))
                     days = 7
-                    res = readMemory(dev, 0x010B, 21)
+                    res = try_read_parse(dev, 0x010B, 21, parse_historical_entry)
                     if res:
-                        d = parse_historical_entry(res)
-                        log(d)
+                        log(res)
                         for consumer in consumers:
-                            consumer.write(d)
-                        days = cast(int, d.get("run_days", 7))
+                            consumer.write(res)
+                        days = cast(int, res.get("run_days", 7))
 
                     for i in range(days):
-                        res = readMemory(dev, 0xF000 + i, 10)
+                        res = try_read_parse(
+                            dev, 0xF000 + i, 10, parse_historical_entry
+                        )
                         if res:
-                            d = parse_historical_entry(res)
-                            log({i: d})
+                            log({i: res})
                             for consumer in consumers:
-                                consumer.write({str(i): d})
+                                consumer.write({str(i): res})
 
                     while True:
                         now = time.time()
+
                         if per_voltages(now):
-                            # CMD_GET_BATTERY_STATE + CMD_GET_PANEL_STATUS
-                            res = readMemory(dev, 0x0100, 11)
-                            if res:
-                                try:
-                                    d = parse_battery_state(res)
-                                    log(d)
-                                    for consumer in consumers:
-                                        consumer.write(d)
-                                except struct.error as e:
-                                    log(e)
-                                    log("0x0100 Unpack error:", len(res), res)
-                                    log(
-                                        "Flushed from read buffer; ",
-                                        dev.read(timeout=0.5),
-                                    )
+                            data = try_read_parse(dev, 0x0100, 11, parse_battery_state)
+                            if data:
+                                log(data)
+                                for consumer in consumers:
+                                    consumer.write(data)
+
                         if per_current_hist(now):
-                            res = readMemory(dev, 0x010B, 21)
-                            if res:
-                                try:
-                                    d = parse_historical_entry(res)
-                                    log(d)
-                                    for consumer in consumers:
-                                        consumer.write(d)
-                                except struct.error as e:
-                                    log(e)
-                                    log("0x010B Unpack error:", len(res), res)
-                                    log(
-                                        "Flushed from read buffer; ",
-                                        dev.read(timeout=0.5),
-                                    )
+                            data = try_read_parse(
+                                dev, 0x010B, 21, parse_historical_entry
+                            )
+                            if data:
+                                log(data)
+                                for consumer in consumers:
+                                    consumer.write(data)
+
                         # print(".")
                         for consumer in consumers:
                             consumer.poll()
+
                         time.sleep(max(0, 1 - time.time() - now))
 
                     # if STATUS.get('load_enabled'):