From 81e9b969706005847c003f138b8091600c7e6b4f Mon Sep 17 00:00:00 2001 From: Daniel Raper Date: Thu, 24 Oct 2024 10:45:40 +0100 Subject: [PATCH 1/8] Added debug logging to EnergyUsageSensor --- custom_components/ohme/sensor.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/custom_components/ohme/sensor.py b/custom_components/ohme/sensor.py index e397a3e..547d3b9 100644 --- a/custom_components/ohme/sensor.py +++ b/custom_components/ohme/sensor.py @@ -291,12 +291,15 @@ def _handle_coordinator_update(self) -> None: # Ensure we have data, then ensure value is going up and above 0 if self.coordinator.data and self.coordinator.data['batterySoc']: new_state = self.coordinator.data['batterySoc']['wh'] + _LOGGER.debug("EnergyUsageSensor: Raw Wh reading %s", new_state) # Let the state reset to 0, but not drop otherwise if not new_state or new_state <= 0: + _LOGGER.debug("EnergyUsageSensor: Resetting Wh reading to 0") self._state = 0 else: self._state = max(0, self._state or 0, new_state) + _LOGGER.debug("EnergyUsageSensor: New state is %s", self._state) self.async_write_ha_state() From 3806d5c59c706daa124c3153938ca3d524fae3be Mon Sep 17 00:00:00 2001 From: Daniel Raper Date: Thu, 24 Oct 2024 16:43:20 +0100 Subject: [PATCH 2/8] Remove deprecated acculumative energy sensor --- README.md | 4 -- custom_components/ohme/__init__.py | 21 +------- custom_components/ohme/api_client.py | 15 ------ custom_components/ohme/config_flow.py | 3 -- custom_components/ohme/const.py | 5 +- custom_components/ohme/coordinator.py | 23 +-------- custom_components/ohme/sensor.py | 54 +-------------------- custom_components/ohme/translations/en.json | 3 +- 8 files changed, 7 insertions(+), 121 deletions(-) diff --git a/README.md b/README.md index 4139476..fb4dd95 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,6 @@ This integration exposes the following entities: * Sensors (Other) * CT Reading (Amps) - Reading from attached CT clamp * Energy Usage (kWh) - Energy used in the current/last session. *This is supported by the energy dashboard.* - * Accumulative Energy Usage (kWh) - Deprecated - Total energy used by the charger (If enabled in options) * Battery State of Charge (%) - If your car is API connected this is read from the car, if not it is how much charge Ohme thinks it has added * Switches (Settings) - **Only options available to your charger model will show** * Lock Buttons - Locks buttons on charger @@ -82,7 +81,6 @@ This integration exposes the following entities: Some options can be set from the 'Configure' menu in Home Assistant: * Never update an ongoing session - Override the default behaviour of the target time, percentage and preconditioning inputs and only ever update the schedule, not the current session. This was added as changing the current session can cause issues for customers on Intelligent Octopus Go. * Don't collapse charge slots - By default, adjacent slots are merged into one. This option shows every slot, as shown in the Ohme app. -* Enable accumulative energy usage sensor - Enable the sensor showing an all-time incrementing energy usage counter. This causes issues with some accounts. ## Coordinators @@ -102,7 +100,5 @@ The coordinators are listed with their refresh intervals below. Relevant coordin * OhmeAdvancedSettingsCoordinator (1m refresh) * Sensors: CT reading sensor * Binary Sensors: Charger online -* OhmeStatisticsCoordinator (30m refresh) - * Sensors: Accumulative energy usage * OhmeChargeSchedulesCoordinator (10m refresh) * Inputs: Target time, target percentage and preconditioning (If car disconnected) diff --git a/custom_components/ohme/__init__.py b/custom_components/ohme/__init__.py index 852d6ed..c354e94 100644 --- a/custom_components/ohme/__init__.py +++ b/custom_components/ohme/__init__.py @@ -3,7 +3,7 @@ from .const import * from .utils import get_option from .api_client import OhmeApiClient -from .coordinator import OhmeChargeSessionsCoordinator, OhmeStatisticsCoordinator, OhmeAccountInfoCoordinator, OhmeAdvancedSettingsCoordinator, OhmeChargeSchedulesCoordinator +from .coordinator import OhmeChargeSessionsCoordinator, OhmeAccountInfoCoordinator, OhmeAdvancedSettingsCoordinator, OhmeChargeSchedulesCoordinator from homeassistant.exceptions import ConfigEntryNotReady _LOGGER = logging.getLogger(__name__) @@ -40,35 +40,16 @@ async def async_setup_entry(hass, entry): coordinators = [ OhmeChargeSessionsCoordinator(hass=hass), # COORDINATOR_CHARGESESSIONS OhmeAccountInfoCoordinator(hass=hass), # COORDINATOR_ACCOUNTINFO - OhmeStatisticsCoordinator(hass=hass), # COORDINATOR_STATISTICS OhmeAdvancedSettingsCoordinator(hass=hass), # COORDINATOR_ADVANCED OhmeChargeSchedulesCoordinator(hass=hass) # COORDINATOR_SCHEDULES ] # We can function without these so setup can continue coordinators_optional = [ - OhmeStatisticsCoordinator, OhmeAdvancedSettingsCoordinator ] - coordinators_skipped = [] - - # Skip statistics coordinator if we don't need it - if not get_option(hass, "enable_accumulative_energy"): - coordinators_skipped.append(OhmeStatisticsCoordinator) - for coordinator in coordinators: - # If we should skip this coordinator - skip = False - for skipped in coordinators_skipped: - if isinstance(coordinator, skipped): - skip = True - break - - if skip: - _LOGGER.debug(f"Skipping initial load of {coordinator.__class__.__name__}") - continue - # Catch failures if this is an 'optional' coordinator try: await coordinator.async_config_entry_first_refresh() diff --git a/custom_components/ohme/api_client.py b/custom_components/ohme/api_client.py index 5861d72..211cc52 100644 --- a/custom_components/ohme/api_client.py +++ b/custom_components/ohme/api_client.py @@ -95,14 +95,6 @@ async def async_refresh_session(self): # Internal methods - def _last_second_of_month_timestamp(self): - """Get the last second of this month.""" - dt = datetime.today() - dt = dt.replace(day=1) + timedelta(days=32) - dt = dt.replace(day=1, hour=0, minute=0, second=0, - microsecond=0) - timedelta(seconds=1) - return int(dt.timestamp()*1e3) - async def _handle_api_error(self, url, resp): """Raise an exception if API response failed.""" if resp.status != 200: @@ -335,13 +327,6 @@ async def async_update_device_info(self, is_retry=False): return True - async def async_get_charge_statistics(self): - """Get charge statistics. Currently this is just for all time (well, Jan 2019).""" - end_ts = self._last_second_of_month_timestamp() - resp = await self._get_request(f"/v1/chargeSessions/summary/users/{self._user_id}?&startTs={self._provision_date}&endTs={end_ts}&granularity=MONTH") - - return resp['totalStats'] - async def async_get_advanced_settings(self): """Get advanced settings (mainly for CT clamp reading)""" resp = await self._get_request(f"/v1/chargeDevices/{self._serial}/advancedSettings") diff --git a/custom_components/ohme/config_flow.py b/custom_components/ohme/config_flow.py index 1c19902..41f74e8 100644 --- a/custom_components/ohme/config_flow.py +++ b/custom_components/ohme/config_flow.py @@ -89,9 +89,6 @@ async def async_step_init(self, options): ) : bool, vol.Required( "never_collapse_slots", default=self._config_entry.options.get("never_collapse_slots", False) - ) : bool, - vol.Required( - "enable_accumulative_energy", default=self._config_entry.options.get("enable_accumulative_energy", False) ) : bool }), errors=errors ) diff --git a/custom_components/ohme/const.py b/custom_components/ohme/const.py index 4016b88..7670fbc 100644 --- a/custom_components/ohme/const.py +++ b/custom_components/ohme/const.py @@ -12,6 +12,5 @@ COORDINATOR_CHARGESESSIONS = 0 COORDINATOR_ACCOUNTINFO = 1 -COORDINATOR_STATISTICS = 2 -COORDINATOR_ADVANCED = 3 -COORDINATOR_SCHEDULES = 4 \ No newline at end of file +COORDINATOR_ADVANCED = 2 +COORDINATOR_SCHEDULES = 3 diff --git a/custom_components/ohme/coordinator.py b/custom_components/ohme/coordinator.py index 253ebde..57a6a68 100644 --- a/custom_components/ohme/coordinator.py +++ b/custom_components/ohme/coordinator.py @@ -55,28 +55,6 @@ async def _async_update_data(self): raise UpdateFailed("Error communicating with API") -class OhmeStatisticsCoordinator(DataUpdateCoordinator): - """Coordinator to update statistics from API periodically. - (But less so than the others)""" - - def __init__(self, hass): - """Initialise coordinator.""" - super().__init__( - hass, - _LOGGER, - name="Ohme Charger Statistics", - update_interval=timedelta(minutes=30), - ) - self._client = hass.data[DOMAIN][DATA_CLIENT] - - async def _async_update_data(self): - """Fetch data from API endpoint.""" - try: - return await self._client.async_get_charge_statistics() - - except BaseException: - raise UpdateFailed("Error communicating with API") - class OhmeAdvancedSettingsCoordinator(DataUpdateCoordinator): """Coordinator to pull CT clamp reading.""" @@ -98,6 +76,7 @@ async def _async_update_data(self): except BaseException: raise UpdateFailed("Error communicating with API") + class OhmeChargeSchedulesCoordinator(DataUpdateCoordinator): """Coordinator to pull charge schedules.""" diff --git a/custom_components/ohme/sensor.py b/custom_components/ohme/sensor.py index 547d3b9..c1b9b03 100644 --- a/custom_components/ohme/sensor.py +++ b/custom_components/ohme/sensor.py @@ -12,8 +12,8 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.entity import generate_entity_id from homeassistant.util.dt import (utcnow) -from .const import DOMAIN, DATA_CLIENT, DATA_COORDINATORS, DATA_SLOTS, COORDINATOR_CHARGESESSIONS, COORDINATOR_STATISTICS, COORDINATOR_ADVANCED -from .coordinator import OhmeChargeSessionsCoordinator, OhmeStatisticsCoordinator, OhmeAdvancedSettingsCoordinator +from .const import DOMAIN, DATA_CLIENT, DATA_COORDINATORS, DATA_SLOTS, COORDINATOR_CHARGESESSIONS, COORDINATOR_ADVANCED +from .coordinator import OhmeChargeSessionsCoordinator, OhmeAdvancedSettingsCoordinator from .utils import next_slot, get_option, slot_list, slot_list_str _LOGGER = logging.getLogger(__name__) @@ -28,7 +28,6 @@ async def async_setup_entry( coordinators = hass.data[DOMAIN][DATA_COORDINATORS] coordinator = coordinators[COORDINATOR_CHARGESESSIONS] - stats_coordinator = coordinators[COORDINATOR_STATISTICS] adv_coordinator = coordinators[COORDINATOR_ADVANCED] sensors = [PowerDrawSensor(coordinator, hass, client), @@ -41,9 +40,6 @@ async def async_setup_entry( SlotListSensor(coordinator, hass, client), BatterySOCSensor(coordinator, hass, client)] - if get_option(hass, "enable_accumulative_energy"): - sensors.append(AccumulativeEnergyUsageSensor(stats_coordinator, hass, client)) - async_add_entities(sensors, update_before_add=True) @@ -213,52 +209,6 @@ def native_value(self): return self.coordinator.data['clampAmps'] -class AccumulativeEnergyUsageSensor(CoordinatorEntity[OhmeStatisticsCoordinator], SensorEntity): - """Sensor for total energy usage.""" - _attr_name = "Accumulative Energy Usage" - _attr_native_unit_of_measurement = UnitOfEnergy.WATT_HOUR - _attr_suggested_unit_of_measurement = UnitOfEnergy.KILO_WATT_HOUR - _attr_suggested_display_precision = 1 - _attr_device_class = SensorDeviceClass.ENERGY - _attr_state_class = SensorStateClass.TOTAL - - def __init__( - self, - coordinator: OhmeStatisticsCoordinator, - hass: HomeAssistant, - client): - super().__init__(coordinator=coordinator) - - self._state = None - self._attributes = {} - self._last_updated = None - self._client = client - - self.entity_id = generate_entity_id( - "sensor.{}", "ohme_accumulative_energy", hass=hass) - - self._attr_device_info = hass.data[DOMAIN][DATA_CLIENT].get_device_info( - ) - - @property - def unique_id(self) -> str: - """Return the unique ID of the sensor.""" - return self._client.get_unique_id("accumulative_energy") - - @property - def icon(self): - """Icon of the sensor.""" - return "mdi:lightning-bolt" - - @property - def native_value(self): - """Get value from data returned from API by coordinator""" - if self.coordinator.data and self.coordinator.data['energyChargedTotalWh']: - return self.coordinator.data['energyChargedTotalWh'] - - return None - - class EnergyUsageSensor(CoordinatorEntity[OhmeChargeSessionsCoordinator], SensorEntity): """Sensor for total energy usage.""" _attr_name = "Energy" diff --git a/custom_components/ohme/translations/en.json b/custom_components/ohme/translations/en.json index 4c397d8..090a916 100644 --- a/custom_components/ohme/translations/en.json +++ b/custom_components/ohme/translations/en.json @@ -23,8 +23,7 @@ "email": "Email address", "password": "Password", "never_session_specific": "Never update an ongoing session", - "never_collapse_slots": "Don't collapse charge slots", - "enable_accumulative_energy": "Enable accumulative energy sensor" + "never_collapse_slots": "Don't collapse charge slots" }, "data_description": { "password": "If you are not changing your credentials, leave the password field empty.", From d319a1d88eac5aa6a84df8132ad65af85e2de5e6 Mon Sep 17 00:00:00 2001 From: Daniel Raper Date: Thu, 24 Oct 2024 17:58:44 +0100 Subject: [PATCH 3/8] Remove outdated hardware version numbers --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fb4dd95..57b4660 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,10 @@ If you find any bugs or would like to request a feature, please open an issue. ## Tested Hardware This integration has been tested with the following hardware: -* Ohme Home Pro [v1.32] -* Ohme Home [v1.32] -* Ohme Go [v1.32] -* Ohme ePod [v2.12] +* Ohme Home Pro +* Ohme Home +* Ohme Go +* Ohme ePod ## External Software The 'Charge Slot Active' binary sensor mimics the `planned_dispatches` and `completed_dispatches` attributes from the [Octopus Energy](https://github.com/BottlecapDave/HomeAssistant-OctopusEnergy) integration, so should support external software which reads this such as [predbat](https://springfall2008.github.io/batpred/devices/#ohme). @@ -81,6 +81,7 @@ This integration exposes the following entities: Some options can be set from the 'Configure' menu in Home Assistant: * Never update an ongoing session - Override the default behaviour of the target time, percentage and preconditioning inputs and only ever update the schedule, not the current session. This was added as changing the current session can cause issues for customers on Intelligent Octopus Go. * Don't collapse charge slots - By default, adjacent slots are merged into one. This option shows every slot, as shown in the Ohme app. +* Refresh Intervals - The refresh interval for the four coordinators listed below can be configured manually. The default times also serve as minimums, as to be respectful to Ohme, but you can choose to fetch data less frequently. ## Coordinators From d68005c0e8640847fd79ef27ae808a6732d4a13a Mon Sep 17 00:00:00 2001 From: Daniel Raper Date: Thu, 24 Oct 2024 18:03:45 +0100 Subject: [PATCH 4/8] Configurable refresh intervals --- custom_components/ohme/config_flow.py | 16 ++++++++++++++-- custom_components/ohme/const.py | 5 +++++ custom_components/ohme/coordinator.py | 20 ++++++++++++++------ custom_components/ohme/translations/en.json | 9 +++++++-- 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/custom_components/ohme/config_flow.py b/custom_components/ohme/config_flow.py index 41f74e8..3381658 100644 --- a/custom_components/ohme/config_flow.py +++ b/custom_components/ohme/config_flow.py @@ -1,6 +1,6 @@ import voluptuous as vol from homeassistant.config_entries import (ConfigFlow, OptionsFlow) -from .const import DOMAIN, CONFIG_VERSION +from .const import DOMAIN, CONFIG_VERSION, DEFAULT_INTERVAL_CHARGESESSIONS, DEFAULT_INTERVAL_ACCOUNTINFO, DEFAULT_INTERVAL_ADVANCED, DEFAULT_INTERVAL_SCHEDULES from .api_client import OhmeApiClient @@ -89,6 +89,18 @@ async def async_step_init(self, options): ) : bool, vol.Required( "never_collapse_slots", default=self._config_entry.options.get("never_collapse_slots", False) - ) : bool + ) : bool, + vol.Required( + "interval_chargesessions", default=self._config_entry.options.get("interval_chargesessions", DEFAULT_INTERVAL_CHARGESESSIONS) + ) : vol.All(vol.Coerce(float), vol.Clamp(min=DEFAULT_INTERVAL_CHARGESESSIONS)), + vol.Required( + "interval_accountinfo", default=self._config_entry.options.get("interval_accountinfo", DEFAULT_INTERVAL_ACCOUNTINFO) + ) : vol.All(vol.Coerce(float), vol.Clamp(min=DEFAULT_INTERVAL_ACCOUNTINFO)), + vol.Required( + "interval_advanced", default=self._config_entry.options.get("interval_advanced", DEFAULT_INTERVAL_ADVANCED) + ) : vol.All(vol.Coerce(float), vol.Clamp(min=DEFAULT_INTERVAL_ADVANCED)), + vol.Required( + "interval_schedules", default=self._config_entry.options.get("interval_schedules", DEFAULT_INTERVAL_SCHEDULES) + ) : vol.All(vol.Coerce(float), vol.Clamp(min=DEFAULT_INTERVAL_SCHEDULES)) }), errors=errors ) diff --git a/custom_components/ohme/const.py b/custom_components/ohme/const.py index 7670fbc..f332578 100644 --- a/custom_components/ohme/const.py +++ b/custom_components/ohme/const.py @@ -14,3 +14,8 @@ COORDINATOR_ACCOUNTINFO = 1 COORDINATOR_ADVANCED = 2 COORDINATOR_SCHEDULES = 3 + +DEFAULT_INTERVAL_CHARGESESSIONS = 0.5 +DEFAULT_INTERVAL_ACCOUNTINFO = 1 +DEFAULT_INTERVAL_ADVANCED = 1 +DEFAULT_INTERVAL_SCHEDULES = 10 diff --git a/custom_components/ohme/coordinator.py b/custom_components/ohme/coordinator.py index 57a6a68..ddb8b9d 100644 --- a/custom_components/ohme/coordinator.py +++ b/custom_components/ohme/coordinator.py @@ -6,7 +6,8 @@ UpdateFailed ) -from .const import DOMAIN, DATA_CLIENT +from .const import DOMAIN, DATA_CLIENT, DEFAULT_INTERVAL_CHARGESESSIONS, DEFAULT_INTERVAL_ACCOUNTINFO, DEFAULT_INTERVAL_ADVANCED, DEFAULT_INTERVAL_SCHEDULES +from .utils import get_option _LOGGER = logging.getLogger(__name__) @@ -20,7 +21,9 @@ def __init__(self, hass): hass, _LOGGER, name="Ohme Charge Sessions", - update_interval=timedelta(seconds=30), + update_interval=timedelta(minutes= + get_option(hass, "interval_chargesessions", DEFAULT_INTERVAL_CHARGESESSIONS) + ), ) self._client = hass.data[DOMAIN][DATA_CLIENT] @@ -42,7 +45,9 @@ def __init__(self, hass): hass, _LOGGER, name="Ohme Account Info", - update_interval=timedelta(minutes=1), + update_interval=timedelta(minutes= + get_option(hass, "interval_accountinfo", DEFAULT_INTERVAL_ACCOUNTINFO) + ), ) self._client = hass.data[DOMAIN][DATA_CLIENT] @@ -64,7 +69,9 @@ def __init__(self, hass): hass, _LOGGER, name="Ohme Advanced Settings", - update_interval=timedelta(minutes=1), + update_interval=timedelta(minutes= + get_option(hass, "interval_advanced", DEFAULT_INTERVAL_ADVANCED) + ), ) self._client = hass.data[DOMAIN][DATA_CLIENT] @@ -86,7 +93,9 @@ def __init__(self, hass): hass, _LOGGER, name="Ohme Charge Schedules", - update_interval=timedelta(minutes=10), + update_interval=timedelta(minutes= + get_option(hass, "interval_schedules", DEFAULT_INTERVAL_SCHEDULES) + ), ) self._client = hass.data[DOMAIN][DATA_CLIENT] @@ -97,4 +106,3 @@ async def _async_update_data(self): except BaseException: raise UpdateFailed("Error communicating with API") - diff --git a/custom_components/ohme/translations/en.json b/custom_components/ohme/translations/en.json index 090a916..a3c8001 100644 --- a/custom_components/ohme/translations/en.json +++ b/custom_components/ohme/translations/en.json @@ -23,12 +23,17 @@ "email": "Email address", "password": "Password", "never_session_specific": "Never update an ongoing session", - "never_collapse_slots": "Don't collapse charge slots" + "never_collapse_slots": "Don't collapse charge slots", + "interval_chargesessions": "Charge sessions refresh rate (minutes)", + "interval_accountinfo": "Account info refresh rate (minutes)", + "interval_advanced": "Advanced settings refresh rate (minutes)", + "interval_schedules": "Schedules refresh rate (minutes)" }, "data_description": { "password": "If you are not changing your credentials, leave the password field empty.", "never_session_specific": "When adjusting charge percentage, charge target or preconditioning settings, the schedule will always be updated even if a charge session is in progress.", - "never_collapse_slots": "By default, adjacent slots are merged into one. This option shows every slot, as shown in the Ohme app." + "never_collapse_slots": "By default, adjacent slots are merged into one. This option shows every slot, as shown in the Ohme app.", + "interval_schedules": "Details on which entities are updated by each coordinator are in the README." } } }, From 63a0572c15860fd8d0fcd83c614ecdcd8f5d79e4 Mon Sep 17 00:00:00 2001 From: Daniel Raper Date: Fri, 25 Oct 2024 09:57:47 +0100 Subject: [PATCH 5/8] Bump version --- custom_components/ohme/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/ohme/const.py b/custom_components/ohme/const.py index f332578..ec6fbb5 100644 --- a/custom_components/ohme/const.py +++ b/custom_components/ohme/const.py @@ -1,7 +1,7 @@ """Component constants""" DOMAIN = "ohme" USER_AGENT = "dan-r-homeassistant-ohme" -INTEGRATION_VERSION = "1.0.0" +INTEGRATION_VERSION = "1.0.1" CONFIG_VERSION = 1 ENTITY_TYPES = ["sensor", "binary_sensor", "switch", "button", "number", "time"] From 15c168ab25b6840b3dc982743a687139d9581058 Mon Sep 17 00:00:00 2001 From: Daniel Raper Date: Sun, 27 Oct 2024 19:47:05 +0000 Subject: [PATCH 6/8] Messy debug logging for EnergyUsageSensor --- custom_components/ohme/sensor.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/custom_components/ohme/sensor.py b/custom_components/ohme/sensor.py index c1b9b03..4e54b2b 100644 --- a/custom_components/ohme/sensor.py +++ b/custom_components/ohme/sensor.py @@ -238,10 +238,36 @@ def __init__( @callback def _handle_coordinator_update(self) -> None: + charge_graph = -1 + battery_soc = -1 + car_battery_soc = -1 + car_vehicle_status = -1 + + try: + charge_graph = self.coordinator.data['chargeGraph']['now']['y'] + except: + pass + + try: + battery_soc = self.coordinator.data['batterySoc']['wh'] + except: + pass + + try: + car_battery_soc = self.coordinator.data['car']['batterySoc']['wh'] + except: + pass + + try: + car_vehicle_status = self.coordinator.data['car']['vehicleStatus']['soc']['wh'] + except: + pass + + _LOGGER.debug("EnergyUsageSensor: CG: %s, BS: %s, CB: %s, CV: %s", charge_graph, battery_soc, car_battery_soc, car_vehicle_status) + # Ensure we have data, then ensure value is going up and above 0 if self.coordinator.data and self.coordinator.data['batterySoc']: new_state = self.coordinator.data['batterySoc']['wh'] - _LOGGER.debug("EnergyUsageSensor: Raw Wh reading %s", new_state) # Let the state reset to 0, but not drop otherwise if not new_state or new_state <= 0: From 504ccffedaed111f5449a3c2cd597c122c56526d Mon Sep 17 00:00:00 2001 From: Daniel Raper Date: Mon, 11 Nov 2024 15:51:26 +0000 Subject: [PATCH 7/8] Change source of energy sensor and amend logic --- custom_components/ohme/sensor.py | 41 ++++++++++---------------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/custom_components/ohme/sensor.py b/custom_components/ohme/sensor.py index 4e54b2b..50f634d 100644 --- a/custom_components/ohme/sensor.py +++ b/custom_components/ohme/sensor.py @@ -238,43 +238,26 @@ def __init__( @callback def _handle_coordinator_update(self) -> None: - charge_graph = -1 - battery_soc = -1 - car_battery_soc = -1 - car_vehicle_status = -1 - - try: - charge_graph = self.coordinator.data['chargeGraph']['now']['y'] - except: - pass - - try: - battery_soc = self.coordinator.data['batterySoc']['wh'] - except: - pass - - try: - car_battery_soc = self.coordinator.data['car']['batterySoc']['wh'] - except: - pass - - try: - car_vehicle_status = self.coordinator.data['car']['vehicleStatus']['soc']['wh'] - except: - pass - - _LOGGER.debug("EnergyUsageSensor: CG: %s, BS: %s, CB: %s, CV: %s", charge_graph, battery_soc, car_battery_soc, car_vehicle_status) - # Ensure we have data, then ensure value is going up and above 0 if self.coordinator.data and self.coordinator.data['batterySoc']: - new_state = self.coordinator.data['batterySoc']['wh'] + new_state = 0 + try: + new_state = self.coordinator.data['chargeGraph']['now']['y'] + except BaseException: + _LOGGER.debug("EnergyUsageSensor: ChargeGraph reading failed, falling back to batterySoc") + new_state = self.coordinator.data['batterySoc']['wh'] # Let the state reset to 0, but not drop otherwise if not new_state or new_state <= 0: _LOGGER.debug("EnergyUsageSensor: Resetting Wh reading to 0") self._state = 0 else: - self._state = max(0, self._state or 0, new_state) + # Allow a significant (90%+) drop, even if we dont hit exactly 0 + if new_state > 0 and self._state > 0 and (new_state / self._state) < 0.1: + self._state = new_state + else: + self._state = max(0, self._state or 0, new_state) + _LOGGER.debug("EnergyUsageSensor: New state is %s", self._state) self.async_write_ha_state() From c51196cb07cbfd5e401fe7afdd20de255ee3145b Mon Sep 17 00:00:00 2001 From: Daniel Raper Date: Wed, 13 Nov 2024 11:56:22 +0000 Subject: [PATCH 8/8] Fix None comparison --- custom_components/ohme/sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/ohme/sensor.py b/custom_components/ohme/sensor.py index 50f634d..2ad81ff 100644 --- a/custom_components/ohme/sensor.py +++ b/custom_components/ohme/sensor.py @@ -253,7 +253,7 @@ def _handle_coordinator_update(self) -> None: self._state = 0 else: # Allow a significant (90%+) drop, even if we dont hit exactly 0 - if new_state > 0 and self._state > 0 and (new_state / self._state) < 0.1: + if self._state and self._state > 0 and new_state > 0 and (new_state / self._state) < 0.1: self._state = new_state else: self._state = max(0, self._state or 0, new_state)