Skip to content

Commit

Permalink
fix: Fixed issue with cost tracker when used with total_increasing en…
Browse files Browse the repository at this point in the history
…tities that reset
  • Loading branch information
BottlecapDave committed Feb 17, 2024
1 parent 8650d37 commit 6759317
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 5 deletions.
13 changes: 11 additions & 2 deletions custom_components/octopus_energy/cost_tracker/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from datetime import datetime, timedelta

from homeassistant.components.sensor import (
SensorStateClass,
)

class CostTrackerResult:
tracked_consumption_data: list
untracked_consumption_data: list
Expand Down Expand Up @@ -33,8 +37,13 @@ def add_consumption(current: datetime,
new_last_reset: datetime,
old_last_reset: datetime,
is_accumulative_value: bool,
is_tracking: bool):
if is_accumulative_value == False or (new_last_reset is not None and old_last_reset is not None and new_last_reset > old_last_reset):
is_tracking: bool,
state_class: str = None):
if (is_accumulative_value == False or
(new_last_reset is not None and old_last_reset is not None and new_last_reset > old_last_reset) or
# Based on https://developers.home-assistant.io/docs/core/entity/sensor/#available-state-classes, when the new value is less than the old value
# this represents a reset
(state_class == SensorStateClass.TOTAL_INCREASING and new_value < old_value)):
value = new_value
elif old_value is not None:
value = new_value - old_value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ async def _async_calculate_cost(self, event: EventType[EventStateChangedData]):
old_state = event.data["old_state"]
if new_state is None or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
return

_LOGGER.debug(f'event: {event.data}')

current = now()
rates_result: ElectricityRatesCoordinatorResult = self.coordinator.data if self.coordinator is not None and self.coordinator.data is not None else None
Expand All @@ -146,7 +148,8 @@ async def _async_calculate_cost(self, event: EventType[EventStateChangedData]):
parse_datetime(new_state.attributes["last_reset"]) if "last_reset" in new_state.attributes and new_state.attributes["last_reset"] is not None else None,
parse_datetime(old_state.attributes["last_reset"]) if "last_reset" in old_state.attributes and old_state.attributes["last_reset"] is not None else None,
self._config[CONFIG_COST_ENTITY_ACCUMULATIVE_VALUE],
self._attributes["is_tracking"])
self._attributes["is_tracking"],
new_state.attributes["state_class"] if "state_class" in new_state.attributes else None)

if (consumption_data is not None and rates_result is not None and rates_result.rates is not None):
self._reset_if_new_day(current)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ async def _async_calculate_cost(self, event: EventType[EventStateChangedData]):
parse_datetime(new_state.attributes["last_reset"]) if "last_reset" in new_state.attributes and new_state.attributes["last_reset"] is not None else None,
parse_datetime(old_state.attributes["last_reset"]) if "last_reset" in old_state.attributes and old_state.attributes["last_reset"] is not None else None,
self._config[CONFIG_COST_ENTITY_ACCUMULATIVE_VALUE],
self._attributes["is_tracking"])
self._attributes["is_tracking"],
new_state.attributes["state_class"] if "state_class" in new_state.attributes else None)

if (consumption_data is not None and rates_result is not None and rates_result.rates is not None):
self._reset_if_new_day(current)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ async def _async_calculate_cost(self, event: EventType[EventStateChangedData]):
parse_datetime(new_state.attributes["last_reset"]) if "last_reset" in new_state.attributes and new_state.attributes["last_reset"] is not None else None,
parse_datetime(old_state.attributes["last_reset"]) if "last_reset" in old_state.attributes and old_state.attributes["last_reset"] is not None else None,
self._config[CONFIG_COST_ENTITY_ACCUMULATIVE_VALUE],
self._attributes["is_tracking"])
self._attributes["is_tracking"],
new_state.attributes["state_class"] if "state_class" in new_state.attributes else None)

if (consumption_data is not None and rates_result is not None and rates_result.rates is not None):
self._reset_if_new_day(current)
Expand Down
70 changes: 70 additions & 0 deletions tests/unit/cost_tracker/test_add_consumption.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,76 @@ async def test_when_last_reset_changed_then_new_value_recorded_as_is(is_tracking
assert len(result.tracked_consumption_data) == 0
assert_consumption(result.untracked_consumption_data, datetime.strptime("2022-02-28T10:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), datetime.strptime("2022-02-28T10:30:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), new_value)

@pytest.mark.asyncio
@pytest.mark.parametrize("is_tracking", [(True),(False)])
async def test_when_state_class_total_increasing_and_new_value_less_than_old_value_then_new_value_recorded_as_is(is_tracking: bool):
# Arrange
current = datetime.strptime("2022-02-28T10:15:00+00:00", "%Y-%m-%dT%H:%M:%S%z")
tracked_consumption_data = []
untracked_consumption_data = []
new_value = 1.2
old_value = 1.5
new_last_reset = datetime.strptime("2022-02-28T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z")
old_last_reset = datetime.strptime("2022-02-28T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z")
is_accumulative_value = True

# Act
result = add_consumption(current,
tracked_consumption_data,
untracked_consumption_data,
new_value,
old_value,
new_last_reset,
old_last_reset,
is_accumulative_value,
is_tracking,
"total_increasing")

# Assert
assert result is not None

if is_tracking:
assert_consumption(result.tracked_consumption_data, datetime.strptime("2022-02-28T10:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), datetime.strptime("2022-02-28T10:30:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), new_value)
assert len(result.untracked_consumption_data) == 0
else:
assert len(result.tracked_consumption_data) == 0
assert_consumption(result.untracked_consumption_data, datetime.strptime("2022-02-28T10:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), datetime.strptime("2022-02-28T10:30:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), new_value)

@pytest.mark.asyncio
@pytest.mark.parametrize("is_tracking", [(True),(False)])
async def test_when_state_class_total_increasing_and_new_value_greater_than_old_value_then_difference_recorded(is_tracking: bool):
# Arrange
current = datetime.strptime("2022-02-28T10:15:00+00:00", "%Y-%m-%dT%H:%M:%S%z")
tracked_consumption_data = []
untracked_consumption_data = []
new_value = 1.2
old_value = 1.1
new_last_reset = datetime.strptime("2022-02-28T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z")
old_last_reset = datetime.strptime("2022-02-28T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z")
is_accumulative_value = True

# Act
result = add_consumption(current,
tracked_consumption_data,
untracked_consumption_data,
new_value,
old_value,
new_last_reset,
old_last_reset,
is_accumulative_value,
is_tracking,
"total_increasing")

# Assert
assert result is not None

if is_tracking:
assert_consumption(result.tracked_consumption_data, datetime.strptime("2022-02-28T10:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), datetime.strptime("2022-02-28T10:30:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), 0.1)
assert len(result.untracked_consumption_data) == 0
else:
assert len(result.tracked_consumption_data) == 0
assert_consumption(result.untracked_consumption_data, datetime.strptime("2022-02-28T10:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), datetime.strptime("2022-02-28T10:30:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), 0.1)

@pytest.mark.asyncio
@pytest.mark.parametrize("is_tracking", [(True),(False)])
async def test_when_consumption_exists_then_consumption_added_to_existing_consumption(is_tracking: bool):
Expand Down

0 comments on commit 6759317

Please sign in to comment.