Skip to content

Commit 56a7ccd

Browse files
committed
feat: Updated action to retrieve dispatches based on point in time ((1.5 hours dev time)
1 parent 4ad31f9 commit 56a7ccd

File tree

11 files changed

+190
-25
lines changed

11 files changed

+190
-25
lines changed

_docs/services.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,25 @@ Refreshes intelligent dispatches for a given account.
308308

309309
For an automation example, please refer to the available [blueprint](./blueprints.md#manual-intelligent-dispatch-refreshes).
310310

311+
### get_point_in_time_intelligent_dispatch_history
312+
313+
Retrieve the intelligent dispatch history which was active for a given point in time based on up to the last 48 hours of intelligent dispatches that have been captured locally. This can be used to determine why [is dispatching](./entities/intelligent.md#is-dispatching) or [off peak](./entities/electricity.md#off-peak) might have turned on during a certain time period.
314+
315+
!!! info
316+
317+
The OE API doesn't provide historic intelligent dispatch information, so this information is stored locally as it changes. Therefore depending on how often your dispatch information refreshes, it can take a while for data to become available.
318+
319+
!!! note
320+
321+
The data that powers this service is available at `config/.storage/octopus_energy.intelligent_dispatches_history_{{DEVICE_ID}}`
322+
323+
324+
| Attribute | Optional | Description |
325+
| ------------------------ | -------- | --------------------------------------------------------------------------------------------------------------------- |
326+
| `target.entity_id` | `no` | The [dispatching](./entities/intelligent.md#is-dispatching) entity that you want to refresh the content for (e.g. `binary_sensor.octopus_energy_{{DEVICE_ID}}_intelligent_dispatching`). |
327+
| `data.point_in_time` | `no` | The point in time to get the historic dispatch information that was active at the time.
328+
329+
311330
## Miscellaneous
312331

313332
### octopus_energy.purge_invalid_external_statistic_ids

custom_components/octopus_energy/binary_sensor.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
import voluptuous as vol
44

5-
from homeassistant.core import HomeAssistant
5+
from homeassistant.core import HomeAssistant, SupportsResponse
66
from homeassistant.helpers import config_validation as cv, entity_platform, issue_registry as ir
77
from homeassistant.util.dt import (utcnow)
8+
import homeassistant.helpers.config_validation as cv
89

910
from .electricity.off_peak import OctopusEnergyElectricityOffPeak
1011
from .octoplus.saving_sessions import OctopusEnergySavingSessions
@@ -225,8 +226,9 @@ def get_intelligent_entities(hass, account_id: str, config: dict):
225226
for intelligent_device in intelligent_devices:
226227

227228
if intelligent_device.device_type == INTELLIGENT_DEVICE_KIND_ELECTRIC_VEHICLES or intelligent_device.device_type == INTELLIGENT_DEVICE_KIND_ELECTRIC_VEHICLE_CHARGERS:
229+
230+
platform = entity_platform.async_get_current_platform()
228231
if (manually_refresh_dispatches):
229-
platform = entity_platform.async_get_current_platform()
230232
platform.async_register_entity_service(
231233
"refresh_intelligent_dispatches",
232234
vol.All(
@@ -238,16 +240,19 @@ def get_intelligent_entities(hass, account_id: str, config: dict):
238240
"async_refresh_dispatches"
239241
)
240242

241-
platform.async_register_entity_service(
242-
"get_intelligent_dispatch_history",
243-
vol.All(
244-
cv.make_entity_service_schema(
245-
{},
246-
extra=vol.ALLOW_EXTRA,
247-
),
248-
),
249-
"async_get_intelligent_dispatch_history"
250-
)
243+
platform.async_register_entity_service(
244+
"get_point_in_time_intelligent_dispatch_history",
245+
vol.All(
246+
cv.make_entity_service_schema(
247+
{
248+
vol.Required("point_in_time"): cv.datetime
249+
},
250+
extra=vol.ALLOW_EXTRA,
251+
),
252+
),
253+
"async_get_point_in_time_intelligent_dispatch_history",
254+
supports_response=SupportsResponse.ONLY
255+
)
251256

252257
coordinator = hass.data[DOMAIN][account_id][DATA_INTELLIGENT_DISPATCHES_COORDINATOR.format(intelligent_device.id)]
253258
entities.append(OctopusEnergyIntelligentDispatching(hass, coordinator, intelligent_device, account_id, intelligent_rate_mode, manually_refresh_dispatches))

custom_components/octopus_energy/coordinators/intelligent_dispatches.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,13 +330,17 @@ async def async_update_intelligent_dispatches_data(is_manual_refresh = False):
330330
client: OctopusEnergyApiClient = hass.data[DOMAIN][account_id][DATA_CLIENT]
331331
account_result = hass.data[DOMAIN][account_id][DATA_ACCOUNT]
332332
account_info = account_result.account if account_result is not None else None
333+
existing_intelligent_dispatches_result = (hass.data[DOMAIN][account_id][DATA_INTELLIGENT_DISPATCHES][device_id]
334+
if DATA_INTELLIGENT_DISPATCHES in hass.data[DOMAIN][account_id] and
335+
device_id in hass.data[DOMAIN][account_id][DATA_INTELLIGENT_DISPATCHES]
336+
else None)
333337

334338
hass.data[DOMAIN][account_id][DATA_INTELLIGENT_DISPATCHES][device_id] = await async_refresh_intelligent_dispatches(
335339
current,
336340
client,
337341
account_info,
338342
intelligent_device,
339-
hass.data[DOMAIN][account_id][DATA_INTELLIGENT_DISPATCHES][device_id] if DATA_INTELLIGENT_DISPATCHES in hass.data[DOMAIN][account_id] and device_id in hass.data[DOMAIN][account_id][DATA_INTELLIGENT_DISPATCHES] else None,
343+
existing_intelligent_dispatches_result,
340344
mock_intelligent_data,
341345
is_manual_refresh,
342346
planned_dispatches_supported,

custom_components/octopus_energy/icons.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@
1515
"boost_water_heater": "mdi:thermometer-plus",
1616
"set_heat_pump_flow_temp_config": "mdi:heat-pump",
1717
"refresh_intelligent_dispatches": "mdi:refresh",
18-
"get_intelligent_dispatch_history": "mdi:history"
18+
"get_point_in_time_intelligent_dispatch_history": "mdi:history"
1919
}
2020
}

custom_components/octopus_energy/intelligent/__init__.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,4 +404,17 @@ def device_type_to_friendly_string(device_type: str) -> str:
404404
elif device_type == INTELLIGENT_DEVICE_KIND_ELECTRIC_VEHICLES:
405405
return "Electric Vehicle"
406406
else:
407-
return device_type
407+
return device_type
408+
409+
def get_applicable_intelligent_dispatch_history(history: IntelligentDispatchesHistory, time: datetime) -> IntelligentDispatchesHistoryItem | None:
410+
if history is None or history.history is None or len(history.history) == 0:
411+
return None
412+
413+
applicable_history_item: IntelligentDispatchesHistoryItem | None = None
414+
for history_item in history.history:
415+
if history_item.timestamp <= time:
416+
applicable_history_item = history_item
417+
else:
418+
break
419+
420+
return applicable_history_item

custom_components/octopus_energy/intelligent/dispatching.py

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
from datetime import datetime
12
import logging
23

4+
from custom_components.octopus_energy.const import DOMAIN
35
from homeassistant.const import (
46
STATE_UNAVAILABLE,
57
STATE_UNKNOWN,
@@ -8,7 +10,7 @@
810
from homeassistant.helpers.entity import generate_entity_id
911
from homeassistant.exceptions import ServiceValidationError
1012

11-
from homeassistant.util.dt import (utcnow)
13+
from homeassistant.util.dt import (utcnow, as_local)
1214
from homeassistant.components.binary_sensor import (
1315
BinarySensorEntity,
1416
)
@@ -17,6 +19,7 @@
1719
from ..intelligent import (
1820
dispatches_to_dictionary_list,
1921
get_applicable_dispatch_periods,
22+
get_applicable_intelligent_dispatch_history,
2023
get_current_and_next_dispatching_periods,
2124
simple_dispatches_to_dictionary_list
2225
)
@@ -158,10 +161,31 @@ async def async_refresh_dispatches(self):
158161
raise ServiceValidationError(result.last_error)
159162

160163
@callback
161-
async def async_get_intelligent_dispatch_history(self):
164+
async def async_get_point_in_time_intelligent_dispatch_history(self, point_in_time: datetime):
162165
"""Refresh dispatches"""
166+
local_point_in_time = as_local(point_in_time)
163167
result: IntelligentDispatchesCoordinatorResult = await self.coordinator.refresh_dispatches()
164-
if result is not None:
165-
return result.history.to_dict()
168+
applicable_dispatches = get_applicable_intelligent_dispatch_history(result.history if result is not None else None, local_point_in_time)
169+
if applicable_dispatches is not None:
170+
return applicable_dispatches.to_dict()
166171

167-
return []
172+
earliest_timestamp = (as_local(result.history.history[0].timestamp).isoformat()
173+
if result is not None and
174+
result.history is not None and
175+
result.history.history is not None and
176+
len(result.history.history) > 0
177+
else None)
178+
179+
if earliest_timestamp is not None:
180+
raise ServiceValidationError(
181+
translation_domain=DOMAIN,
182+
translation_key="point_in_time_intelligent_dispatch_history_data_out_of_bounds",
183+
translation_placeholders={
184+
"earliest_timestamp": earliest_timestamp,
185+
},
186+
)
187+
188+
raise ServiceValidationError(
189+
translation_domain=DOMAIN,
190+
translation_key="point_in_time_intelligent_dispatch_history_data_unavailable",
191+
)

custom_components/octopus_energy/services.yaml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -391,10 +391,17 @@ refresh_intelligent_dispatches:
391391
integration: octopus_energy
392392
domain: binary_sensor
393393

394-
get_intelligent_dispatch_history:
395-
name: Get intelligent dispatch history
396-
description: Retrieve the history for up to the last 48 hours of intelligent dispatches that have been captured locally. For more information please see https://bottlecapdave.github.io/HomeAssistant-OctopusEnergy/services/#octopus_energyget_intelligent_dispatch_history.
394+
get_point_in_time_intelligent_dispatch_history:
395+
name: Get point in time intelligent dispatch history
396+
description:
397+
Retrieve the intelligent dispatch history which was active for a given point in time based on up to the last 48 hours of intelligent dispatches that have been captured locally. For more information please see https://bottlecapdave.github.io/HomeAssistant-OctopusEnergy/services/#octopus_energyget_intelligent_dispatch_history.
397398
target:
398399
entity:
399400
integration: octopus_energy
400-
domain: binary_sensor
401+
domain: binary_sensor
402+
fields:
403+
point_in_time:
404+
name: Point in time
405+
description: The datetime of the point in time to retrieve the application intelligent dispatch history for
406+
selector:
407+
datetime:

custom_components/octopus_energy/storage/intelligent_dispatches_history.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def __init__(self, history: list[IntelligentDispatchesHistoryItem]):
3636
def from_dict(data: dict):
3737
history = []
3838
for item in data["history"]:
39-
history.append(IntelligentDispatches.from_dict(item))
39+
history.append(IntelligentDispatchesHistoryItem.from_dict(item))
4040

4141
return IntelligentDispatchesHistory(history)
4242

custom_components/octopus_energy/translations/en.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,12 @@
341341
},
342342
"invalid_rate_weightings": {
343343
"message": "{error}"
344+
},
345+
"point_in_time_intelligent_dispatch_history_data_out_of_bounds": {
346+
"message": "The requested point in time is before historic data is available. The earliest available intelligent dispatch data is from {earliest_timestamp}"
347+
},
348+
"point_in_time_intelligent_dispatch_history_data_unavailable": {
349+
"message": "No intelligent dispatch data is currently available. This will refresh automatically over time when intelligent dispatches are updated."
344350
}
345351
},
346352
"issues": {

tests/unit/intelligent/test_get_applicable_dispatches.py

Whitespace-only changes.

0 commit comments

Comments
 (0)