Skip to content

Commit

Permalink
feat: Added service for adjusting specified dates within cost tracker…
Browse files Browse the repository at this point in the history
… accumulative sensors (e.g. week and month)
  • Loading branch information
BottlecapDave committed Mar 8, 2024
1 parent d28d63b commit 26a9462
Show file tree
Hide file tree
Showing 10 changed files with 142 additions and 27 deletions.
13 changes: 12 additions & 1 deletion _docs/services.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,15 @@ Resets a given [cost tracker](./setup/cost_tracker.md) sensor back to zero befor

| Attribute | Optional | Description |
| ------------------------ | -------- | --------------------------------------------------------------------------------------------------------------------- |
| `target.entity_id` | `no` | The name of the cost tracker sensor(s) whose configuration is to be updated. |
| `target.entity_id` | `no` | The name of the cost tracker sensor(s) that should be reset. |

## octopus_energy.adjust_accumulative_cost_tracker

Allows you to adjust the cost/consumption for any given date recorded by an accumulative [cost tracker](./setup/cost_tracker.md) sensor (e.g. week or month).

| Attribute | Optional | Description |
| ------------------------ | -------- | --------------------------------------------------------------------------------------------------------------------- |
| `target.entity_id` | `no` | The name of the cost tracker sensor(s) that should be updated. |
| `data.date` | `no` | The date of the data within the cost tracker to be adjusted. |
| `data.consumption` | `no` | The new consumption recorded against the specified date. |
| `data.cost` | `no` | The new cost recorded against the specified date. |
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ async def async_added_to_hass(self):
async def _async_calculate_cost(self, event: EventType[EventStateChangedData]):
new_state = event.data["new_state"]
old_state = event.data["old_state"]
if new_state is None or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN) or old_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
if new_state is None or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN) or old_state is None or old_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
return

_LOGGER.debug(f'event: {event.data}')
Expand Down
47 changes: 36 additions & 11 deletions custom_components/octopus_energy/cost_tracker/cost_tracker_month.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import logging

from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers.entity import generate_entity_id
from homeassistant.util.dt import (now)

Expand All @@ -27,6 +28,7 @@
from ..const import (
CONFIG_COST_MONTH_DAY_RESET,
CONFIG_COST_NAME,
DOMAIN,
)

from . import accumulate_cost
Expand Down Expand Up @@ -147,7 +149,39 @@ async def _async_calculate_cost(self, event: EventType[EventStateChangedData]):

_LOGGER.debug(f"Source entity updated for '{self.entity_id}'; Event: {event.data}")

result = accumulate_cost(current, self._attributes["accumulated_data"], float(new_state.state), float(new_state.attributes["total_consumption"]))
self._recalculate_cost(current, float(new_state.state), float(new_state.attributes["total_consumption"]))

@callback
async def async_reset_cost_tracker(self):
"""Resets the sensor"""
self._state = 0
self._attributes["accumulated_data"] = []
self._attributes["total_consumption"] = 0

self.async_write_ha_state()

@callback
async def async_adjust_accumulative_cost_tracker(self, date, consumption: float, cost: float):
"""Adjusts the sensor"""
selected_date = None
for data in self._attributes["accumulated_data"]:
if data["start"].date() == date:
selected_date = data["start"]

if selected_date is None:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="cost_tracker_invalid_date",
translation_placeholders={
"min_date": self._attributes["accumulated_data"][0]["start"].date(),
"max_date": self._attributes["accumulated_data"][-1]["start"].date()
},
)

self._recalculate_cost(selected_date, cost, consumption)

def _recalculate_cost(self, current: datetime, new_cost: float, new_consumption: float):
result = accumulate_cost(current, self._attributes["accumulated_data"], new_cost, new_consumption)

self._attributes["total_consumption"] = result.total_consumption
self._attributes["accumulated_data"] = result.accumulative_data
Expand All @@ -171,13 +205,4 @@ def _reset_if_new_week(self, current: datetime):

return True

return False

@callback
async def async_reset_cost_tracker(self):
"""Resets the sensor"""
self._state = 0
self._attributes["accumulated_data"] = []
self._attributes["total_consumption"] = 0

self.async_write_ha_state()
return False
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ async def async_added_to_hass(self):
async def _async_calculate_cost(self, event: EventType[EventStateChangedData]):
new_state = event.data["new_state"]
old_state = event.data["old_state"]
if new_state is None or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN) or old_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
if new_state is None or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN) or old_state is None or old_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
return

current = now()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ async def async_added_to_hass(self):
async def _async_calculate_cost(self, event: EventType[EventStateChangedData]):
new_state = event.data["new_state"]
old_state = event.data["old_state"]
if new_state is None or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN) or old_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
if new_state is None or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN) or old_state is None or old_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN):
return

current = now()
Expand Down
47 changes: 36 additions & 11 deletions custom_components/octopus_energy/cost_tracker/cost_tracker_week.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import logging

from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import ServiceValidationError
from homeassistant.helpers.entity import generate_entity_id
from homeassistant.util.dt import (now)

Expand All @@ -27,6 +28,7 @@
from ..const import (
CONFIG_COST_NAME,
CONFIG_COST_WEEKDAY_RESET,
DOMAIN,
)

from . import accumulate_cost
Expand Down Expand Up @@ -147,7 +149,39 @@ async def _async_calculate_cost(self, event: EventType[EventStateChangedData]):

_LOGGER.debug(f"Source entity updated for '{self.entity_id}'; Event: {event.data}")

result = accumulate_cost(current, self._attributes["accumulated_data"], float(new_state.state), float(new_state.attributes["total_consumption"]))
self._recalculate_cost(current, float(new_state.state), float(new_state.attributes["total_consumption"]))

@callback
async def async_reset_cost_tracker(self):
"""Resets the sensor"""
self._state = 0
self._attributes["total_consumption"] = 0
self._attributes["accumulated_data"] = []

self.async_write_ha_state()

@callback
async def async_adjust_accumulative_cost_tracker(self, date, consumption: float, cost: float):
"""Adjusts the sensor"""
selected_date = None
for data in self._attributes["accumulated_data"]:
if data["start"].date() == date:
selected_date = data["start"]

if selected_date is None:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="cost_tracker_invalid_date",
translation_placeholders={
"min_date": self._attributes["accumulated_data"][0]["start"].date(),
"max_date": self._attributes["accumulated_data"][-1]["start"].date()
},
)

self._recalculate_cost(selected_date, cost, consumption)

def _recalculate_cost(self, current: datetime, new_cost: float, new_consumption: float):
result = accumulate_cost(current, self._attributes["accumulated_data"], new_cost, new_consumption)

self._attributes["total_consumption"] = result.total_consumption
self._attributes["accumulated_data"] = result.accumulative_data
Expand All @@ -171,13 +205,4 @@ def _reset_if_new_week(self, current: datetime):

return True

return False

@callback
async def async_reset_cost_tracker(self):
"""Resets the sensor"""
self._state = 0
self._attributes["total_consumption"] = 0
self._attributes["accumulated_data"] = []

self.async_write_ha_state()
return False
4 changes: 3 additions & 1 deletion custom_components/octopus_energy/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
"refresh_previous_consumption_data": "mdi:refresh",
"join_octoplus_saving_session_event": "mdi:leaf",
"spin_wheel_of_fortune": "mdi:star-circle",
"update_cost_tracker": "mdi:invoice-edit"
"update_cost_tracker": "mdi:invoice-edit",
"reset_cost_tracker": "mdi:refresh",
"adjust_accumulative_cost_tracker": "mdi:numeric"
}
}
16 changes: 16 additions & 0 deletions custom_components/octopus_energy/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from homeassistant.util.dt import (utcnow)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_platform, issue_registry as ir, entity_registry as er
import homeassistant.helpers.config_validation as cv

from .electricity.current_consumption import OctopusEnergyCurrentElectricityConsumption
from .electricity.current_accumulative_consumption import OctopusEnergyCurrentAccumulativeElectricityConsumption
Expand Down Expand Up @@ -152,6 +153,21 @@ async def async_setup_entry(hass, entry, async_add_entities):
"async_reset_cost_tracker"
)

platform.async_register_entity_service(
"adjust_accumulative_cost_tracker",
vol.All(
vol.Schema(
{
vol.Required("date"): cv.date,
vol.Required("consumption"): cv.positive_float,
vol.Required("cost"): cv.positive_float,
},
extra=vol.ALLOW_EXTRA,
),
),
"async_adjust_accumulative_cost_tracker"
)

async def async_setup_default_sensors(hass: HomeAssistant, config, async_add_entities):
account_id = config[CONFIG_ACCOUNT_ID]

Expand Down
31 changes: 31 additions & 0 deletions custom_components/octopus_energy/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ update_target_config:
The optional offset to apply to the target rate when it starts
selector:
text:

purge_invalid_external_statistic_ids:
name: Purge invalid external statistics
description: Removes external statistics for all meters that don't have an active tariff

refresh_previous_consumption_data:
name: Refresh previous consumption data
description: Refreshes the previous consumption data for a given entity from a given date.
Expand All @@ -47,6 +49,7 @@ refresh_previous_consumption_data:
required: true
selector:
date:

join_octoplus_saving_session_event:
name: Join Octoplus saving session event
description: Joins a given Octoplus saving session event.
Expand All @@ -60,6 +63,7 @@ join_octoplus_saving_session_event:
description: The code of the event that is to be joined.
selector:
text:

spin_wheel_of_fortune:
name: Spin wheel of fortune
description: Spins the wheel of fortune for a given energy type
Expand Down Expand Up @@ -89,3 +93,30 @@ reset_cost_tracker:
entity:
integration: octopus_energy
domain: sensor

adjust_accumulative_cost_tracker:
name: Adjusts accumulative cost tracker
description: Adjusts a record within an accumulative (week or month) cost tracker
target:
entity:
integration: octopus_energy
domain: sensor
fields:
date:
name: Date
description: The date to adjust within the accumulative cost tracker
selector:
date:
cost:
name: Cost
description: The new cost for the selected date
selector:
number:
step: any
consumption:
name: Consumption
description: The new consumption for the selected date
selector:
number:
step: any

5 changes: 5 additions & 0 deletions custom_components/octopus_energy/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@
"account_not_found": "Account information is not found"
}
},
"exceptions": {
"cost_tracker_invalid_date": {
"message": "Date must be between {min_date} and {max_date}"
}
},
"issues": {
"account_not_found": {
"title": "Account \"{account_id}\" not found",
Expand Down

0 comments on commit 26a9462

Please sign in to comment.