Skip to content

Commit

Permalink
Add HistoricalSensor for daily per-tariff statistics #6
Browse files Browse the repository at this point in the history
Signed-off-by: Olivier Mehani <[email protected]>
  • Loading branch information
shtrom committed Aug 3, 2023
1 parent 2c8e710 commit df87dec
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 7 deletions.
5 changes: 4 additions & 1 deletion custom_components/auroraplus/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
"issue_tracker": "https://github.com/LeighCurran/AuroraPlusHA/issues",
"dependencies": [],
"codeowners": ["@LeighCurran"],
"requirements": ["AuroraPlus==1.1.6"],
"requirements": [
"AuroraPlus==1.1.6",
"homeassistant-historical-sensor==2.0.0rc2"
],
"iot_class": "cloud_polling",
"config_flow": false,
"version": "1.1.9"
Expand Down
144 changes: 138 additions & 6 deletions custom_components/auroraplus/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
SensorEntity
)

from homeassistant.components.recorder.models import (StatisticData,
StatisticMetaData)
from homeassistant.components.recorder.statistics import StatisticsRow

from homeassistant.const import (
CONF_USERNAME,
CONF_PASSWORD,
Expand All @@ -26,6 +30,12 @@

from homeassistant.util import Throttle

from homeassistant_historical_sensor import (
HistoricalSensor,
HistoricalState,
PollUpdateMixin,
)

CONF_ROUNDING = "rounding"


Expand All @@ -42,7 +52,6 @@
]

SENSORS_ENERGY = [
SENSOR_KILOWATTHOURUSAGE,
SENSOR_KILOWATTHOURUSAGETARIFF + 'T31',
SENSOR_KILOWATTHOURUSAGETARIFF + 'T41',
SENSOR_KILOWATTHOURUSAGETARIFF + 'T61',
Expand All @@ -52,7 +61,7 @@
SENSOR_KILOWATTHOURUSAGETARIFF + 'T140',
]

POSSIBLE_MONITORED = SENSORS_MONETARY + SENSORS_ENERGY
POSSIBLE_MONITORED = SENSORS_MONETARY + [SENSOR_KILOWATTHOURUSAGE]

DEFAULT_MONITORED = POSSIBLE_MONITORED

Expand Down Expand Up @@ -101,6 +110,11 @@ def aurora_init():
sensor, name,
aurora_api, rounding)
for sensor in config.get(CONF_MONITORED_CONDITIONS)
] + [
AuroraHistoricalSensor(hass,
sensor, name,
aurora_api, rounding)
for sensor in SENSORS_ENERGY
],
True)

Expand Down Expand Up @@ -226,11 +240,129 @@ async def async_update(self):
self._state = round(
self._api.KilowattHourUsage['Total'], self._rounding)
elif self._sensor.startswith(SENSOR_KILOWATTHOURUSAGETARIFF):
tariff = self._sensor.removeprefix(SENSOR_KILOWATTHOURUSAGETARIFF)
self._state = self._api.KilowattHourUsage.get(tariff)
if self._state:
self._state = round(self._state, self._rounding)
pass

else:
_LOGGER.error("Unknown sensor type found")
if self._old_state and self._state != self._old_state:
self._last_reset = datetime.datetime.now()


class AuroraHistoricalSensor(PollUpdateMixin, HistoricalSensor, SensorEntity):
def __init__(self, hass, sensor, name, aurora_api, rounding):
"""Initialize the Aurora+ sensor."""
self._hass = hass
self._name = name + ' ' + sensor
self._sensor = sensor
self._unit_of_measurement = None
self._attr_historical_states = []
self._api = aurora_api
self._uniqueid = self._name.replace(' ', '_').lower()
self._rounding = rounding
_LOGGER.debug("Created historical sensor %s", self._sensor)

@property
def name(self):
"""Return the name of the sensor."""
return self._name

# @property
# def state_class(self):
# """Return the state class of the sensor."""
# return STATE_CLASS_TOTAL

@property
def device_class(self):
"""Return device class fo the sensor."""
if self._sensor in SENSORS_MONETARY:
return DEVICE_CLASS_MONETARY
else:
return DEVICE_CLASS_ENERGY

@property
def unique_id(self):
"""Return the unique_id of the sensor."""
return self._uniqueid

@property
def statistic_id(self) -> str:
_LOGGER.debug("Statistic_id: %s",
'sensor:' + self._uniqueid)
return 'sensor:' + self._uniqueid

@property
def unit_of_measurement(self):
"""Return the unit of measurement."""
if self._sensor in SENSORS_MONETARY:
return CURRENCY_DOLLAR
else:
return ENERGY_KILO_WATT_HOUR

@property
def historical_states(self):
"""Return the historical state of the sensor."""
_LOGGER.debug("Returning historical states for: %s %s",
self._sensor, self._attr_historical_states)
return self._attr_historical_states

async def async_update_historical(self):
if self._sensor.startswith(SENSOR_KILOWATTHOURUSAGETARIFF):
tariff = self._sensor.removeprefix(SENSOR_KILOWATTHOURUSAGETARIFF)

await self._api.async_update()

metered_records = self._api.day.get(
'MeteredUsageRecords'
)
if not metered_records:
_LOGGER.warning(f"Empty daily records for {self._sensor}")
return
# _LOGGER.debug(f"MeteredUsageRecords: {metered_records}")

self._attr_historical_states = [
HistoricalState(
state=round(
float(r['KilowattHourUsage'][tariff]),
self._rounding
),
dt=datetime.datetime.fromisoformat(r['EndTime'])
)
for r in metered_records
if r
and r.get('KilowattHourUsage')
and r.get('KilowattHourUsage').get(tariff)
]
_LOGGER.debug("Done with historical states for: %s",
self._sensor)

def get_statistic_metadata(self) -> StatisticMetaData:
_LOGGER.debug("Getting statistic meta for: %s",
self._sensor)
meta = super().get_statistic_metadata()
meta["has_sum"] = True

return meta

async def async_calculate_statistic_data(
self,
hist_states: list[HistoricalState],
*,
latest: StatisticsRow | None = None,
) -> list[StatisticData]:
accumulated = latest["sum"] if latest else 0

ret = []

for hs in hist_states:
accumulated = accumulated + hs.state
ret.append(
StatisticData(
start=hs.dt,
state=hs.state,
sum=accumulated,
)
)

_LOGGER.debug("Calculated statistics for: %s, %s",
self._sensor, ret)
return ret

0 comments on commit df87dec

Please sign in to comment.