Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.

Commit 7980117

Browse files
authored
Fix bug with session energy usage sensor, put accumulative energy usage sensor behind an option, fix charge slots updating mid-charge, fix charge end logic (#60)
* Fix none issue with session energy usage * Put accumulative energy usage sensor behind an option * Removing charge slot hashing * Fix charge end logic * Version bump for breaking changes
1 parent 13b60d0 commit 7980117

File tree

7 files changed

+38
-45
lines changed

7 files changed

+38
-45
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ This integration exposes the following entities:
6060
* Next Charge Slot End - The next time your car will stop charging according to the Ohme-generated charge plan
6161
* Sensors (Other)
6262
* CT Reading (Amps) - Reading from attached CT clamp
63-
* Accumulative Energy Usage (kWh) - Total energy used by the charger
6463
* Session Energy Usage (kWh) - Energy used in the current session
64+
* Accumulative Energy Usage (kWh) - Total energy used by the charger (If enabled in options)
6565
* 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
6666
* Switches (Settings) - **Only options available to your charger model will show**
6767
* Lock Buttons - Locks buttons on charger
@@ -84,6 +84,7 @@ This integration exposes the following entities:
8484
## Options
8585
Some options can be set from the 'Configure' menu in Home Assistant:
8686
* 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.
87+
* Enable accumulative energy usage sensor - Enable the sensor showing an all-time incrementing energy usage counter. This causes issues with some accounts.
8788

8889

8990
## Coordinators

custom_components/ohme/__init__.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import logging
22
from homeassistant import core
33
from .const import *
4+
from .utils import get_option
45
from .api_client import OhmeApiClient
56
from .coordinator import OhmeChargeSessionsCoordinator, OhmeStatisticsCoordinator, OhmeAccountInfoCoordinator, OhmeAdvancedSettingsCoordinator, OhmeChargeSchedulesCoordinator
67
from homeassistant.exceptions import ConfigEntryNotReady
@@ -25,12 +26,9 @@ async def async_setup_dependencies(hass, entry):
2526

2627
async def async_update_listener(hass, entry):
2728
"""Handle options flow credentials update."""
28-
# Re-instantiate the API client
29-
await async_setup_dependencies(hass, entry)
30-
31-
# Refresh all coordinators for good measure
32-
for coordinator in hass.data[DOMAIN][DATA_COORDINATORS]:
33-
await coordinator.async_refresh()
29+
30+
# Reload this instance
31+
await hass.config_entries.async_reload(entry.entry_id)
3432

3533

3634
async def async_setup_entry(hass, entry):
@@ -53,7 +51,24 @@ async def async_setup_entry(hass, entry):
5351
OhmeAdvancedSettingsCoordinator
5452
]
5553

54+
coordinators_skipped = []
55+
56+
# Skip statistics coordinator if we don't need it
57+
if not get_option(hass, "enable_accumulative_energy"):
58+
coordinators_skipped.append(OhmeStatisticsCoordinator)
59+
5660
for coordinator in coordinators:
61+
# If we should skip this coordinator
62+
skip = False
63+
for skipped in coordinators_skipped:
64+
if isinstance(coordinator, skipped):
65+
skip = True
66+
break
67+
68+
if skip:
69+
_LOGGER.debug(f"Skipping initial load of {coordinator.__class__.__name__}")
70+
continue
71+
5772
# Catch failures if this is an 'optional' coordinator
5873
try:
5974
await coordinator.async_config_entry_first_refresh()

custom_components/ohme/config_flow.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ async def async_step_init(self, options):
8686
): str,
8787
vol.Required(
8888
"never_session_specific", default=self._config_entry.options.get("never_session_specific", False)
89+
) : bool,
90+
vol.Required(
91+
"enable_accumulative_energy", default=self._config_entry.options.get("enable_accumulative_energy", False)
8992
) : bool
9093
}), errors=errors
9194
)

custom_components/ohme/const.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Component constants"""
22
DOMAIN = "ohme"
33
USER_AGENT = "dan-r-homeassistant-ohme"
4-
INTEGRATION_VERSION = "0.6.1"
4+
INTEGRATION_VERSION = "0.7.0"
55
CONFIG_VERSION = 1
66
ENTITY_TYPES = ["sensor", "binary_sensor", "switch", "button", "number", "time"]
77

custom_components/ohme/sensor.py

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
SensorEntity
88
)
99
import json
10-
import hashlib
1110
import math
1211
import logging
1312
from homeassistant.helpers.update_coordinator import CoordinatorEntity
@@ -17,7 +16,7 @@
1716
from homeassistant.util.dt import (utcnow)
1817
from .const import DOMAIN, DATA_CLIENT, DATA_COORDINATORS, DATA_SLOTS, COORDINATOR_CHARGESESSIONS, COORDINATOR_STATISTICS, COORDINATOR_ADVANCED
1918
from .coordinator import OhmeChargeSessionsCoordinator, OhmeStatisticsCoordinator, OhmeAdvancedSettingsCoordinator
20-
from .utils import charge_graph_next_slot, charge_graph_slot_list
19+
from .utils import charge_graph_next_slot, charge_graph_slot_list, get_option
2120

2221
_LOGGER = logging.getLogger(__name__)
2322

@@ -39,11 +38,13 @@ async def async_setup_entry(
3938
VoltageSensor(coordinator, hass, client),
4039
CTSensor(adv_coordinator, hass, client),
4140
EnergyUsageSensor(coordinator, hass, client),
42-
AccumulativeEnergyUsageSensor(stats_coordinator, hass, client),
4341
NextSlotEndSensor(coordinator, hass, client),
4442
NextSlotStartSensor(coordinator, hass, client),
4543
SlotListSensor(coordinator, hass, client),
4644
BatterySOCSensor(coordinator, hass, client)]
45+
46+
if get_option(hass, "enable_accumulative_energy"):
47+
sensors.append(AccumulativeEnergyUsageSensor(stats_coordinator, hass, client))
4748

4849
async_add_entities(sensors, update_before_add=True)
4950

@@ -293,7 +294,7 @@ def _handle_coordinator_update(self) -> None:
293294
new_state = self.coordinator.data['batterySoc']['wh']
294295

295296
# Let the state reset to 0, but not drop otherwise
296-
if new_state <= 0:
297+
if not new_state or new_state <= 0:
297298
self._state = 0
298299
else:
299300
self._state = max(0, self._state or 0, new_state)
@@ -422,7 +423,6 @@ def _handle_coordinator_update(self) -> None:
422423
class SlotListSensor(CoordinatorEntity[OhmeChargeSessionsCoordinator], SensorEntity):
423424
"""Sensor for next smart charge slot end time."""
424425
_attr_name = "Charge Slots"
425-
_last_hash = None
426426

427427
def __init__(
428428
self,
@@ -458,27 +458,13 @@ def native_value(self):
458458
"""Return pre-calculated state."""
459459
return self._state
460460

461-
def _hash_rule(self):
462-
"""Generate a hashed representation of the current charge rule."""
463-
serial = json.dumps(self.coordinator.data['appliedRule'], sort_keys=True)
464-
sha1 = hashlib.sha1(serial.encode('utf-8')).hexdigest()
465-
return sha1
466-
467461
@callback
468462
def _handle_coordinator_update(self) -> None:
469463
"""Get a list of charge slots."""
470464
if self.coordinator.data is None or self.coordinator.data["mode"] == "DISCONNECTED" or self.coordinator.data["mode"] == "FINISHED_CHARGE":
471465
self._state = None
472-
self._last_hash = None
473466
self._hass.data[DOMAIN][DATA_SLOTS] = []
474467
else:
475-
rule_hash = self._hash_rule()
476-
477-
# Rule has not changed, no point evaluating slots again
478-
if rule_hash == self._last_hash:
479-
_LOGGER.debug("Slot evaluation skipped - rule has not changed")
480-
return
481-
482468
slots = charge_graph_slot_list(
483469
self.coordinator.data['startTime'], self.coordinator.data['chargeGraph']['points'])
484470

@@ -490,9 +476,6 @@ def _handle_coordinator_update(self) -> None:
490476

491477
# Make sure we return None/Unknown if the list is empty
492478
self._state = None if self._state == "" else self._state
493-
494-
# Store hash of the last rule
495-
self._last_hash = self._hash_rule()
496479

497480
self._last_updated = utcnow()
498481
self.async_write_ha_state()

custom_components/ohme/translations/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
"data": {
2323
"email": "Email address",
2424
"password": "Password",
25-
"never_session_specific": "Never update an ongoing session"
25+
"never_session_specific": "Never update an ongoing session",
26+
"enable_accumulative_energy": "Enable accumulative energy sensor"
2627
},
2728
"data_description": {
2829
"password": "If you are not changing your credentials, leave the password field empty.",

custom_components/ohme/utils.py

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,6 @@ def _sanitise_points(points):
4242
return output
4343

4444

45-
def _charge_finished(data):
46-
"""Is the charge finished?"""
47-
now = int(time())
48-
data = [x['y'] for x in data if x["t"] > now]
49-
50-
if len(data) == 0 or min(data) == max(data):
51-
return True
52-
return False
53-
54-
5545
def _next_slot(data, live=False, in_progress=False):
5646
"""Get the next slot. live is whether or not we may start mid charge. Eg: For the next slot end sensor, we dont have the
5747
start but still want the end of the in progress session, but for the slot list sensor we only want slots that have
@@ -117,7 +107,7 @@ def charge_graph_slot_list(charge_start, points, skip_format=False):
117107
data = points if skip_format else _format_charge_graph(charge_start, points)
118108

119109
# Don't return any slots if charge is over
120-
if _charge_finished(data):
110+
if charge_graph_next_slot(charge_start, points)['end'] is None:
121111
return []
122112

123113
data = _sanitise_points(data)
@@ -199,6 +189,6 @@ def session_in_progress(hass, data):
199189
return True
200190

201191

202-
def get_option(hass, option):
203-
"""Return option value, default to False."""
204-
return hass.data[DOMAIN][DATA_OPTIONS].get(option, None)
192+
def get_option(hass, option, default=False):
193+
"""Return option value, with settable default."""
194+
return hass.data[DOMAIN][DATA_OPTIONS].get(option, default)

0 commit comments

Comments
 (0)