From 0d56e93ea8835378de131f731fdd6bc9bb841ac3 Mon Sep 17 00:00:00 2001 From: Daniel Raper Date: Mon, 15 Jan 2024 22:32:21 +0000 Subject: [PATCH] Add backend logic for multi device support --- custom_components/ohme/__init__.py | 16 +++++++++++---- custom_components/ohme/api_client.py | 30 ++++++++++++++++++++-------- custom_components/ohme/const.py | 4 ++-- 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/custom_components/ohme/__init__.py b/custom_components/ohme/__init__.py index ca7110f..91c4c1e 100644 --- a/custom_components/ohme/__init__.py +++ b/custom_components/ohme/__init__.py @@ -12,9 +12,9 @@ async def async_setup(hass: core.HomeAssistant, config: dict) -> bool: return True -async def async_setup_dependencies(hass, config): +async def async_setup_dependencies(hass, entry): """Instantiate client and refresh session""" - client = OhmeApiClient(config['email'], config['password']) + client = OhmeApiClient(entry.data['email'], entry.data['password'], hass, entry) hass.data[DOMAIN][DATA_CLIENT] = client await client.async_create_session() @@ -32,7 +32,7 @@ async def async_setup_entry(hass, entry): if "email" not in config: return False - await async_setup_dependencies(hass, config) + await async_setup_dependencies(hass, entry) coordinators = [ OhmeChargeSessionsCoordinator(hass=hass), # COORDINATOR_CHARGESESSIONS @@ -88,7 +88,15 @@ async def async_migrate_entry(hass: core.HomeAssistant, config_entry) -> bool: # Version number has gone up if config_entry.version < CONFIG_VERSION: _LOGGER.debug("Migrating from version %s", config_entry.version) - new_data = config_entry.data + new_data = dict(config_entry.data) + + # 1 -> 2: Add serial to config + if CONFIG_VERSION >= 2: + client = OhmeApiClient(new_data['email'], new_data['password']) + await client.async_create_session() + + chargers = await client.async_get_chargers() + new_data['serial'] = chargers[0] config_entry.version = CONFIG_VERSION hass.config_entries.async_update_entry(config_entry, data=new_data) diff --git a/custom_components/ohme/api_client.py b/custom_components/ohme/api_client.py index 5f492cb..6cf5e4c 100644 --- a/custom_components/ohme/api_client.py +++ b/custom_components/ohme/api_client.py @@ -15,13 +15,15 @@ class OhmeApiClient: """API client for Ohme EV chargers.""" - def __init__(self, email, password): + def __init__(self, email, password, hass=None, config_entry=None): if email is None or password is None: raise Exception("Credentials not provided") # Credentials from configuration self._email = email self._password = password + self._hass = hass + self._config_entry = config_entry # Charger and its capabilities self._device_info = None @@ -35,7 +37,7 @@ def __init__(self, email, password): # User info self._user_id = "" - self._serial = "" + self._serial = config_entry.data['serial'] if config_entry else None # Cache the last rule to use when we disable max charge or change schedule self._last_rule = {} @@ -226,7 +228,7 @@ async def async_apply_session_rule(self, max_price=None, target_time=None, targe result = await self._put_request(f"/v1/chargeSessions/{self._serial}/rule?enableMaxPrice={max_price}&targetTs={target_ts}&enablePreconditioning={pre_condition}&toPercent={target_percent}&preconditionLengthMins={pre_condition_length}") return bool(result) - + async def async_get_schedule(self): """Get the first schedule.""" schedules = await self._get_request("/v1/chargeRules") @@ -261,7 +263,7 @@ async def async_get_charge_sessions(self, is_retry=False): """Try to fetch charge sessions endpoint. If we get a non 200 response, refresh auth token and try again""" resp = await self._get_request('/v1/chargeSessions') - resp = resp[0] + resp = next(filter(lambda x: x['chargeDevice']['id'] == self._serial, resp), None) # Cache the current rule if we are given it if resp["mode"] == "SMART_CHARGE" and 'appliedRule' in resp: @@ -276,15 +278,14 @@ async def async_get_account_info(self): async def async_get_charge_device(self): resp = await self.async_get_account_info() - device = resp[0] + device = next(filter(lambda x: x['id'] == self._serial, resp['chargeDevices']), None) return device async def async_update_device_info(self, is_retry=False): """Update _device_info with our charger model.""" resp = await self.async_get_account_info() - - device = resp['chargeDevices'][0] + device = next(filter(lambda x: x['id'] == self._serial, resp['chargeDevices']), None) info = DeviceInfo( identifiers={(DOMAIN, "ohme_charger")}, @@ -297,10 +298,23 @@ async def async_update_device_info(self, is_retry=False): self._capabilities = device['modelCapabilities'] self._user_id = resp['user']['id'] - self._serial = device['id'] self._device_info = info + self._hass.config_entries.async_update_entry( + self._config_entry, title=f"{device['modelTypeDisplayName']} ({device['id']})" + ) + return True + + async def async_get_chargers(self): + """Get a list of chargers on an account.""" + resp = await self.async_get_account_info() + chargers = [] + + for device in resp['chargeDevices']: + chargers.append(device['id']) + + return chargers async def async_get_charge_statistics(self): """Get charge statistics. Currently this is just for all time (well, Jan 2019).""" diff --git a/custom_components/ohme/const.py b/custom_components/ohme/const.py index ae0b25f..f8c8247 100644 --- a/custom_components/ohme/const.py +++ b/custom_components/ohme/const.py @@ -1,8 +1,8 @@ """Component constants""" DOMAIN = "ohme" USER_AGENT = "dan-r-homeassistant-ohme" -INTEGRATION_VERSION = "0.3.2" -CONFIG_VERSION = 1 +INTEGRATION_VERSION = "0.3.3" +CONFIG_VERSION = 2 ENTITY_TYPES = ["sensor", "binary_sensor", "switch", "button", "number", "time"] DATA_CLIENT = "client"