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
21 changes: 20 additions & 1 deletion apps/predbat/gecloud.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import json
import asyncio
import random
import time

"""
GE Cloud data download
Expand Down Expand Up @@ -200,8 +201,23 @@ def __init__(self, base):
self.meter = {}
self.info = {}
self.stop_cloud = False
self.api_started = False
self.register_entity_map = {}

def wait_api_started(self):
"""
Return if the API has started
"""
self.log("GECloud: Waiting for API to start")
count = 0
while not self.api_started and count < 120:
time.sleep(1)
count += 1
if not self.api_started:
self.log("Warn: GECloud: API failed to start in required time")
return False
return True

async def switch_event(self, entity_id, service):
"""
Switch event
Expand Down Expand Up @@ -331,7 +347,6 @@ async def publish_info(self, device, device_info):
capacity = round(cap * volt / 1000.0, 2)
except (ValueError, TypeError):
pass
print(info)

max_charge_rate = info.get("max_charge_rate", 0)
self.log("GECloud: Device {} battery capacity {} max charge rate {}".format(device, capacity, max_charge_rate))
Expand Down Expand Up @@ -553,6 +568,7 @@ async def start(self):
Start the client
"""
self.stop_cloud = False
self.api_started = False
self.devices = await self.async_get_devices()
self.log("GECloud: Starting up, found devices {}".format(self.devices))

Expand All @@ -577,6 +593,9 @@ async def start(self):
except Exception as e:
self.log("Error: GECloud: Exception in main loop {}".format(e))

if not self.api_started:
print("GECloud API Started")
self.api_started = True
await asyncio.sleep(5)
seconds += 5

Expand Down
5 changes: 5 additions & 0 deletions apps/predbat/inverter.py
Original file line number Diff line number Diff line change
Expand Up @@ -904,6 +904,11 @@ def update_status(self, minutes_now, quiet=False):
self.base.record_status("Error: Inverter {} unable to read charge window time as neither REST, charge_start_time or charge_start_hour are set".format(self.id), had_errors=True)
raise ValueError

if charge_start_time is None or charge_end_time is None:
self.log("Error: Inverter {} unable to read charge window time as charge_start_time or charge_end_time is None".format(self.id))
self.base.record_status("Error: Inverter {} unable to read charge window time as charge_start_time or charge_end_time is None".format(self.id), had_errors=True)
raise ValueError

# Update simulated charge enable time to match the charge window time.
if not self.inv_has_charge_enable_time:
if charge_start_time == charge_end_time:
Expand Down
27 changes: 23 additions & 4 deletions apps/predbat/octopus.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import asyncio
import json
from datetime import timezone
import time

user_agent_value = "predbat-octopus-energy"
integration_context_header = "Ha-Integration-Context"
Expand Down Expand Up @@ -294,6 +295,21 @@ def __init__(self, api_key, account_id, log):
self.intelligent_dispatches = {}
self.saving_sessions = {}
self.saving_sessions_to_join = []
self.api_started = False

def wait_api_started(self):
"""
Wait for the API to start
"""
self.log("Octopus API: Waiting for API to start")
count = 0
while not self.api_started and count < 240:
time.sleep(1)
count += 1
if not self.api_started:
self.log("Warn:Octopus API: Failed to start")
return False
return True

async def start(self):
"""
Expand All @@ -313,6 +329,9 @@ async def start(self):
await self.fetch_tariffs(self.tariffs)
self.saving_sessions = await self.async_get_saving_sessions(self.account_id)
await self.async_join_saving_session_events(self.account_id)
if not self.api_started:
print("Octopus API: Started")
self.api_started = True
await asyncio.sleep(5)
count_seconds += 5
await self.api.async_close()
Expand Down Expand Up @@ -657,7 +676,7 @@ async def async_refresh_token(self):
self.log(f"Failed to connect. Timeout of {self.api.timeout} exceeded.")
return None

async def async_graphql_query(self, query, request_context, returns_data=True):
async def async_graphql_query(self, query, request_context, returns_data=True, ignore_errors=False):
"""
Execute a graphql query
"""
Expand All @@ -668,7 +687,7 @@ async def async_graphql_query(self, query, request_context, returns_data=True):
payload = {"query": query}
headers = {"Authorization": f"JWT {self.graphql_token}", integration_context_header: request_context}
async with client.post(url, json=payload, headers=headers) as response:
response_body = await self.async_read_response(response, url)
response_body = await self.async_read_response(response, url, ignore_errors=ignore_errors)
if response_body and ("data" in response_body):
return response_body["data"]
else:
Expand All @@ -687,12 +706,12 @@ async def async_get_intelligent_device(self, account_id, device_id):
result = {}
if device_id:
self.log("Octopus API: Fetching intelligent dispatches for device {}".format(device_id))
dispatch_result = await self.async_graphql_query(intelligent_dispatches_query.format(account_id=account_id, device_id=device_id), "get-intelligent-dispatches")
device_result = await self.async_graphql_query(intelligent_device_query.format(account_id=account_id), "get-intelligent-devices")
device_result = await self.async_graphql_query(intelligent_device_query.format(account_id=account_id), "get-intelligent-devices", ignore_errors=True)
intelligent_device = {}
planned = []
completed = []
if device_result:
dispatch_result = await self.async_graphql_query(intelligent_dispatches_query.format(account_id=account_id, device_id=device_id), "get-intelligent-dispatches", ignore_errors=True)
chargePointVariants = device_result.get("chargePointVariants", [])
electricVehicles = device_result.get("electricVehicles", [])
devices = device_result.get("devices", [])
Expand Down
4 changes: 2 additions & 2 deletions apps/predbat/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -2035,7 +2035,7 @@ def calculate_yesterday(self):
self.log("Yesterday basic charge window best: {} charge limit best: {}".format(charge_window_best, charge_limit_best))

# Get Cost yesterday
cost_today_data = self.get_history_wrapper(entity_id=self.prefix + ".cost_today", days=2)
cost_today_data = self.get_history_wrapper(entity_id=self.prefix + ".cost_today", days=2, required=False)
if not cost_today_data:
self.log("Warn: No cost_today data for yesterday")
return
Expand All @@ -2044,7 +2044,7 @@ def calculate_yesterday(self):
cost_yesterday = cost_data.get(minutes_back, 0.0)
cost_yesterday_per_kwh = cost_data_per_kwh.get(minutes_back, 0.0)

cost_today_car_data = self.get_history_wrapper(entity_id=self.prefix + ".cost_today_car", days=2)
cost_today_car_data = self.get_history_wrapper(entity_id=self.prefix + ".cost_today_car", days=2, required=False)
if not cost_today_car_data:
cost_today_car_data = {}
cost_data_car = {}
Expand Down
19 changes: 14 additions & 5 deletions apps/predbat/predbat.py
Original file line number Diff line number Diff line change
Expand Up @@ -886,15 +886,24 @@ def initialize(self):
self.web_interface = WebInterface(self)
self.web_interface_task = self.create_task(self.web_interface.start())

if self.get_arg("ge_cloud_direct", False):
self.log("Starting GE cloud direct interface")
self.ge_cloud_direct = GECloudDirect(self)
self.ge_cloud_direct_task = self.create_task(self.ge_cloud_direct.start())

if self.get_arg("octopus_api_key", "") and self.get_arg("octopus_api_account", ""):
self.log("Starting Octopus API interface")
self.octopus_api_direct = OctopusAPI(self.get_arg("octopus_api_key", ""), self.get_arg("octopus_api_account", ""), self.log)
self.octopus_api_direct_task = self.create_task(self.octopus_api_direct.start())
if not self.octopus_api_direct.wait_api_started():
self.log("Error: Octopus API failed to start")
self.record_status("Error: Octopus API failed to start")
raise ValueError("Octopus API failed to start")

if self.get_arg("ge_cloud_direct", False):
self.log("Starting GE cloud direct interface")
self.ge_cloud_direct = GECloudDirect(self)
self.ge_cloud_direct_task = self.create_task(self.ge_cloud_direct.start())
# Allow GE Cloud API to start
if not self.ge_cloud_direct.wait_api_started():
self.log("Error: GE Cloud API failed to start")
self.record_status("Error: GE Cloud API failed to start")
raise ValueError("GE Cloud API failed to start")

# Printable config root
self.config_root_p = self.config_root
Expand Down