Fix utility meter restore state (#66490)
* Address #63874
* avoid setting _last_period to None
* name is always set in discovery
* ValueError never happens only DecimalException
* async_tariff_change tracks state change - state machine will not pass a None
* test we only reset one utility_meter
* test corrupted restored state
* pretty sure _current_tariff doesn't change from init until here
* missing assert
* Revert "async_tariff_change tracks state change - state machine will not pass a None"
This reverts commit 24fc04a964
.
* address review comment
* always a Decimal
This commit is contained in:
parent
4dbd9b21b7
commit
b211a1faa7
4 changed files with 27 additions and 16 deletions
|
@ -182,8 +182,6 @@ class TariffSelect(RestoreEntity):
|
||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
"""Run when entity about to be added."""
|
"""Run when entity about to be added."""
|
||||||
await super().async_added_to_hass()
|
await super().async_added_to_hass()
|
||||||
if self._current_tariff is not None:
|
|
||||||
return
|
|
||||||
|
|
||||||
state = await self.async_get_last_state()
|
state = await self.async_get_last_state()
|
||||||
if not state or state.state not in self._tariffs:
|
if not state or state.state not in self._tariffs:
|
||||||
|
|
|
@ -32,6 +32,7 @@ from homeassistant.helpers.event import (
|
||||||
async_track_state_change_event,
|
async_track_state_change_event,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers.restore_state import RestoreEntity
|
from homeassistant.helpers.restore_state import RestoreEntity
|
||||||
|
from homeassistant.helpers.template import is_number
|
||||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
|
||||||
|
@ -166,13 +167,10 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity):
|
||||||
self._parent_meter = parent_meter
|
self._parent_meter = parent_meter
|
||||||
self._sensor_source_id = source_entity
|
self._sensor_source_id = source_entity
|
||||||
self._state = None
|
self._state = None
|
||||||
self._last_period = 0
|
self._last_period = Decimal(0)
|
||||||
self._last_reset = dt_util.utcnow()
|
self._last_reset = dt_util.utcnow()
|
||||||
self._collecting = None
|
self._collecting = None
|
||||||
if name:
|
self._name = name
|
||||||
self._name = name
|
|
||||||
else:
|
|
||||||
self._name = f"{source_entity} meter"
|
|
||||||
self._unit_of_measurement = None
|
self._unit_of_measurement = None
|
||||||
self._period = meter_type
|
self._period = meter_type
|
||||||
if meter_type is not None:
|
if meter_type is not None:
|
||||||
|
@ -231,8 +229,6 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity):
|
||||||
return
|
return
|
||||||
self._state += adjustment
|
self._state += adjustment
|
||||||
|
|
||||||
except ValueError as err:
|
|
||||||
_LOGGER.warning("While processing state changes: %s", err)
|
|
||||||
except DecimalException as err:
|
except DecimalException as err:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Invalid state (%s > %s): %s", old_state.state, new_state.state, err
|
"Invalid state (%s > %s): %s", old_state.state, new_state.state, err
|
||||||
|
@ -282,7 +278,7 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity):
|
||||||
return
|
return
|
||||||
_LOGGER.debug("Reset utility meter <%s>", self.entity_id)
|
_LOGGER.debug("Reset utility meter <%s>", self.entity_id)
|
||||||
self._last_reset = dt_util.utcnow()
|
self._last_reset = dt_util.utcnow()
|
||||||
self._last_period = str(self._state)
|
self._last_period = Decimal(self._state) if self._state else Decimal(0)
|
||||||
self._state = 0
|
self._state = 0
|
||||||
self.async_write_ha_state()
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
@ -319,9 +315,10 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity):
|
||||||
ATTR_UNIT_OF_MEASUREMENT
|
ATTR_UNIT_OF_MEASUREMENT
|
||||||
)
|
)
|
||||||
self._last_period = (
|
self._last_period = (
|
||||||
float(state.attributes.get(ATTR_LAST_PERIOD))
|
Decimal(state.attributes[ATTR_LAST_PERIOD])
|
||||||
if state.attributes.get(ATTR_LAST_PERIOD)
|
if state.attributes.get(ATTR_LAST_PERIOD)
|
||||||
else 0
|
and is_number(state.attributes[ATTR_LAST_PERIOD])
|
||||||
|
else Decimal(0)
|
||||||
)
|
)
|
||||||
self._last_reset = dt_util.as_utc(
|
self._last_reset = dt_util.as_utc(
|
||||||
dt_util.parse_datetime(state.attributes.get(ATTR_LAST_RESET))
|
dt_util.parse_datetime(state.attributes.get(ATTR_LAST_RESET))
|
||||||
|
@ -399,7 +396,7 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity):
|
||||||
state_attr = {
|
state_attr = {
|
||||||
ATTR_SOURCE_ID: self._sensor_source_id,
|
ATTR_SOURCE_ID: self._sensor_source_id,
|
||||||
ATTR_STATUS: PAUSED if self._collecting is None else COLLECTING,
|
ATTR_STATUS: PAUSED if self._collecting is None else COLLECTING,
|
||||||
ATTR_LAST_PERIOD: self._last_period,
|
ATTR_LAST_PERIOD: str(self._last_period),
|
||||||
}
|
}
|
||||||
if self._period is not None:
|
if self._period is not None:
|
||||||
state_attr[ATTR_PERIOD] = self._period
|
state_attr[ATTR_PERIOD] = self._period
|
||||||
|
|
|
@ -62,7 +62,12 @@ async def test_services(hass):
|
||||||
"source": "sensor.energy",
|
"source": "sensor.energy",
|
||||||
"cycle": "hourly",
|
"cycle": "hourly",
|
||||||
"tariffs": ["peak", "offpeak"],
|
"tariffs": ["peak", "offpeak"],
|
||||||
}
|
},
|
||||||
|
"energy_bill2": {
|
||||||
|
"source": "sensor.energy",
|
||||||
|
"cycle": "hourly",
|
||||||
|
"tariffs": ["peak", "offpeak"],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,6 +158,10 @@ async def test_services(hass):
|
||||||
state = hass.states.get("sensor.energy_bill_offpeak")
|
state = hass.states.get("sensor.energy_bill_offpeak")
|
||||||
assert state.state == "0"
|
assert state.state == "0"
|
||||||
|
|
||||||
|
# meanwhile energy_bill2_peak accumulated all kWh
|
||||||
|
state = hass.states.get("sensor.energy_bill2_peak")
|
||||||
|
assert state.state == "4"
|
||||||
|
|
||||||
|
|
||||||
async def test_cron(hass, legacy_patchable_time):
|
async def test_cron(hass, legacy_patchable_time):
|
||||||
"""Test cron pattern and offset fails."""
|
"""Test cron pattern and offset fails."""
|
||||||
|
|
|
@ -304,6 +304,10 @@ async def test_restore_state(hass):
|
||||||
ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR,
|
ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
State(
|
||||||
|
"sensor.energy_bill_midpeak",
|
||||||
|
"error",
|
||||||
|
),
|
||||||
State(
|
State(
|
||||||
"sensor.energy_bill_offpeak",
|
"sensor.energy_bill_offpeak",
|
||||||
"6",
|
"6",
|
||||||
|
@ -326,6 +330,9 @@ async def test_restore_state(hass):
|
||||||
assert state.attributes.get("last_reset") == last_reset
|
assert state.attributes.get("last_reset") == last_reset
|
||||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR
|
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.energy_bill_midpeak")
|
||||||
|
assert state.state == STATE_UNKNOWN
|
||||||
|
|
||||||
state = hass.states.get("sensor.energy_bill_offpeak")
|
state = hass.states.get("sensor.energy_bill_offpeak")
|
||||||
assert state.state == "6"
|
assert state.state == "6"
|
||||||
assert state.attributes.get("status") == COLLECTING
|
assert state.attributes.get("status") == COLLECTING
|
||||||
|
@ -530,7 +537,7 @@ async def _test_self_reset(hass, config, start_time, expect_reset=True):
|
||||||
assert state.attributes.get("last_reset") == now.isoformat()
|
assert state.attributes.get("last_reset") == now.isoformat()
|
||||||
assert state.state == "3"
|
assert state.state == "3"
|
||||||
else:
|
else:
|
||||||
assert state.attributes.get("last_period") == 0
|
assert state.attributes.get("last_period") == "0"
|
||||||
assert state.state == "5"
|
assert state.state == "5"
|
||||||
start_time_str = dt_util.parse_datetime(start_time).isoformat()
|
start_time_str = dt_util.parse_datetime(start_time).isoformat()
|
||||||
assert state.attributes.get("last_reset") == start_time_str
|
assert state.attributes.get("last_reset") == start_time_str
|
||||||
|
@ -559,7 +566,7 @@ async def _test_self_reset(hass, config, start_time, expect_reset=True):
|
||||||
assert state.attributes.get("last_period") == "2"
|
assert state.attributes.get("last_period") == "2"
|
||||||
assert state.state == "7"
|
assert state.state == "7"
|
||||||
else:
|
else:
|
||||||
assert state.attributes.get("last_period") == 0
|
assert state.attributes.get("last_period") == "0"
|
||||||
assert state.state == "9"
|
assert state.state == "9"
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue