Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 35 additions & 16 deletions apps/predbat/fox.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ async def get_device_setting(self, deviceSN, key):
self.device_settings[deviceSN][key] = result
return result
else:
print("Failed to get device setting for {} key {}".format(deviceSN, key))
self.log(f"Fox: Warn: Failed to get device setting for {deviceSN} key {key}")
return None

async def set_device_setting(self, deviceSN, key, value):
Expand All @@ -414,6 +414,10 @@ async def set_device_setting(self, deviceSN, key, value):
SET_DEVICE_SETTING = "/op/v0/device/setting/set"
result = await self.request_get(SET_DEVICE_SETTING, datain={"sn": deviceSN, "key": key, "value": value, "lang": FOX_LANG}, post=True)
if result is None:
if self.device_settings.get(deviceSN, {}).get(key, None) is None:
# Failed to write setting after failure to read, assume it doesn't exist
self.log(f"Fox: Warn: Failed to set device setting for {deviceSN} key {key} value {value}, assuming not supported")
return True
return False
return True

Expand Down Expand Up @@ -779,9 +783,11 @@ async def request_get(self, path, post=False, datain=None):
retries = 0
self.log("Fox: API Requesting {} {}".format("POST" if post else "GET", path))
while retries < FOX_RETRIES:
result = await self.request_get_func(path, post=post, datain=datain)
result, allow_retry = await self.request_get_func(path, post=post, datain=datain)
if result is not None:
return result
if not allow_retry:
break
retries += 1
await asyncio.sleep(retries * random.random())
return result
Expand All @@ -799,15 +805,15 @@ async def request_get_func(self, path, post=False, datain=None):
else:
response = await asyncio.to_thread(requests.get, url, headers=headers, params=datain, timeout=TIMEOUT)
except requests.exceptions.RequestException as e:
self.log(f"Warn: GECloud: Exception during request to {url}: {e}")
self.log(f"Warn: Fox: Exception during request to {url}: {e}")
self.failures_total += 1
return None
return None, False

status_code = response.status_code
if status_code in [400, 401, 402, 403]:
self.log("Warn: Fox: Authentication error with status code {} from {}".format(status_code, url))
self.failures_total += 1
return None
return None, False

try:
data = response.json()
Expand All @@ -816,10 +822,10 @@ async def request_get_func(self, path, post=False, datain=None):
data = None
except (requests.Timeout, requests.exceptions.ReadTimeout):
self.log("Warn: Fox: Timeout from {}".format(url))
data = None
return None, True
except (requests.exceptions.RequestException, requests.exceptions.ConnectionError) as e:
self.log("Warn: Fox: Could not connect to {}".format(url))
data = None
return None, True

if response.status_code in [200, 201]:
if data is None:
Expand All @@ -830,29 +836,41 @@ async def request_get_func(self, path, post=False, datain=None):
self.failures_total += 1
if errno in [40400, 41200, 41203, 41935]:
# Rate limiting so wait up to 10 seconds
self.log("Fox: Rate limiting detected, waiting...")
await asyncio.sleep(random.random() * 10 + 1)
self.log("Info: Fox: Rate limiting detected, waiting...")
await asyncio.sleep(random.random() * 30 + 1)
return None, True
Comment on lines +839 to +841

Copilot AI Oct 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This rate limiting handling duplicates the logic at lines 869-871. Consider extracting this into a helper method to avoid code duplication.

Copilot uses AI. Check for mistakes.
elif errno in [40402]:
self.log("Fox: Has run out of API calls for today, sleeping...")
# Out of API calls for today
self.log("Warn: Fox: Has run out of API calls for today, sleeping...")
await asyncio.sleep(5 * 60)
return None
return None, False
elif errno in [44096]:
# Unsupported function code
self.log("Warn: Fox: Unsupported function code {} from {}".format(errno, url))
return None, False
elif errno in [40257]:
# Invalid parameter
self.log("Warn: Fox: Invalid parameter {} from {} message {}".format(errno, url, msg))
return None, False
else:
self.log("Warn: Fox: Error {} from {} message {}".format(errno, url, msg))
return None
return None, False

if "result" in data:
data = data["result"]
if data is None:
data = {}

self.last_success_timestamp = datetime.now(timezone.utc)
return data
return data, False
else:
self.failures_total += 1
if response.status_code == 429:
# Rate limiting so wait up to 30 seconds
await asyncio.sleep(random.random() * 30)
return None
self.log("Info: Fox: Rate limiting detected, waiting...")
await asyncio.sleep(random.random() * 30 + 1)
return None, True
return None, False

async def publish_data(self):
"""
Expand Down Expand Up @@ -1254,9 +1272,10 @@ async def test_fox_api(api_key):
# await fox_api.start()
# res = await fox_api.get_device_settings(sn)
# res = await fox_api.get_battery_charging_time(sn)
res = await fox_api.get_scheduler(sn)
# res = await fox_api.get_scheduler(sn)
# res = await fox_api.compute_schedule(sn)
# res = await fox_api.publish_data()
res = await fox_api.set_device_setting(sn, "dummy", 42)
print(res)

"""
Expand Down
2 changes: 1 addition & 1 deletion apps/predbat/predbat.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import requests
import asyncio

THIS_VERSION = "v8.26.1"
THIS_VERSION = "v8.26.2"

# fmt: off
PREDBAT_FILES = ["predbat.py", "config.py", "prediction.py", "gecloud.py","utils.py", "inverter.py", "ha.py", "download.py", "unit_test.py", "web.py", "web_helper.py", "predheat.py", "futurerate.py", "octopus.py", "solcast.py","execute.py", "plan.py", "fetch.py", "output.py", "userinterface.py", "energydataservice.py", "alertfeed.py", "compare.py", "db_manager.py", "db_engine.py", "plugin_system.py", "ohme.py", "components.py", "fox.py"]
Expand Down