Skip to content

Commit

Permalink
Added next slot sensor
Browse files Browse the repository at this point in the history
  • Loading branch information
dan-r committed Dec 27, 2023
1 parent 9a65591 commit b829fc1
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 2 deletions.
45 changes: 43 additions & 2 deletions custom_components/ohme/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from homeassistant.helpers.entity import generate_entity_id
from .const import DOMAIN, DATA_CLIENT, DATA_COORDINATOR, DATA_STATISTICS_COORDINATOR
from .coordinator import OhmeUpdateCoordinator, OhmeStatisticsUpdateCoordinator

from .utils import charge_graph_next_slot

async def async_setup_entry(
hass: core.HomeAssistant,
Expand All @@ -24,7 +24,7 @@ async def async_setup_entry(
stats_coordinator = hass.data[DOMAIN][DATA_STATISTICS_COORDINATOR]

sensors = [PowerDrawSensor(coordinator, hass, client), EnergyUsageSensor(
stats_coordinator, hass, client)]
stats_coordinator, hass, client), NextSlotSensor(coordinator, hass, client)]

async_add_entities(sensors, update_before_add=True)

Expand Down Expand Up @@ -112,3 +112,44 @@ def native_value(self):
return self.coordinator.data['energyChargedTotalWh'] / 1000

return None

class NextSlotSensor(CoordinatorEntity[OhmeStatisticsUpdateCoordinator], SensorEntity):
"""Sensor for next smart charge slot."""
_attr_name = "Ohme Next Smart Charge Slot"
_attr_device_class = SensorDeviceClass.TIMESTAMP

def __init__(
self,
coordinator: OhmeUpdateCoordinator,
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_next_slot", 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("next_slot")

@property
def icon(self):
"""Icon of the sensor."""
return "mdi:clock-star-four-points-outline"

@property
def native_value(self):
"""Get value from data returned from API by coordinator"""
if self.coordinator.data and self.coordinator.data['mode'] != "DISCONNECTED":
return charge_graph_next_slot(self.coordinator.data['startTime'], self.coordinator.data['chargeGraph']['points'])

return None
36 changes: 36 additions & 0 deletions custom_components/ohme/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from time import time
from datetime import datetime
import pytz

def charge_graph_next_slot(charge_start, points):
"""Get the next charge slot from a list of graph points."""
# Get start and current timestamp in seconds
charge_start = round(charge_start / 1000)
now = int(time())

# Replace relative timestamp (seconds) with real timestamp
data = [{"t": x["x"] + charge_start, "y": x["y"]} for x in points]

# Filter to points from now onwards
data = [x for x in data if x["t"] > now]

# Give up if we have less than 3 points
if len(data) < 3:
return False

next_ts = None

# Loop through every remaining value, skipping the last
for idx in range(0, len(data) - 1):
# Calculate the delta between this element and the next
delta = data[idx + 1]["y"] - data[idx]["y"]

# If the next point has a Y delta of 10+, consider this the start of a slot
# This should be 0+ but I had some strange results in testing... revisit
if delta > 10:
next_ts = data[idx]["t"]
break

# This needs to be presented with tzinfo or Home Assistant will reject it
return None if next_ts is None else datetime.utcfromtimestamp(next_ts).replace(tzinfo=pytz.utc)

0 comments on commit b829fc1

Please sign in to comment.