Define unit_of_measurement
of all utility_meter
sensors on HA start (#56112)
* define unit_of_measurement on hass start * delay utility_meter state * check state * store siblings * don't check unit_of_measurement
This commit is contained in:
parent
5976f898da
commit
ec9fc0052d
4 changed files with 97 additions and 37 deletions
|
@ -24,6 +24,7 @@ from .const import (
|
||||||
CONF_TARIFF,
|
CONF_TARIFF,
|
||||||
CONF_TARIFF_ENTITY,
|
CONF_TARIFF_ENTITY,
|
||||||
CONF_TARIFFS,
|
CONF_TARIFFS,
|
||||||
|
DATA_TARIFF_SENSORS,
|
||||||
DATA_UTILITY,
|
DATA_UTILITY,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
METER_TYPES,
|
METER_TYPES,
|
||||||
|
@ -98,6 +99,7 @@ async def async_setup(hass, config):
|
||||||
_LOGGER.debug("Setup %s.%s", DOMAIN, meter)
|
_LOGGER.debug("Setup %s.%s", DOMAIN, meter)
|
||||||
|
|
||||||
hass.data[DATA_UTILITY][meter] = conf
|
hass.data[DATA_UTILITY][meter] = conf
|
||||||
|
hass.data[DATA_UTILITY][meter][DATA_TARIFF_SENSORS] = []
|
||||||
|
|
||||||
if not conf[CONF_TARIFFS]:
|
if not conf[CONF_TARIFFS]:
|
||||||
# only one entity is required
|
# only one entity is required
|
||||||
|
|
|
@ -22,6 +22,7 @@ METER_TYPES = [
|
||||||
]
|
]
|
||||||
|
|
||||||
DATA_UTILITY = "utility_meter_data"
|
DATA_UTILITY = "utility_meter_data"
|
||||||
|
DATA_TARIFF_SENSORS = "utility_meter_sensors"
|
||||||
|
|
||||||
CONF_METER = "meter"
|
CONF_METER = "meter"
|
||||||
CONF_SOURCE_SENSOR = "source"
|
CONF_SOURCE_SENSOR = "source"
|
||||||
|
|
|
@ -46,6 +46,7 @@ from .const import (
|
||||||
CONF_TARIFF,
|
CONF_TARIFF,
|
||||||
CONF_TARIFF_ENTITY,
|
CONF_TARIFF_ENTITY,
|
||||||
DAILY,
|
DAILY,
|
||||||
|
DATA_TARIFF_SENSORS,
|
||||||
DATA_UTILITY,
|
DATA_UTILITY,
|
||||||
HOURLY,
|
HOURLY,
|
||||||
MONTHLY,
|
MONTHLY,
|
||||||
|
@ -96,19 +97,20 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=
|
||||||
CONF_TARIFF_ENTITY
|
CONF_TARIFF_ENTITY
|
||||||
)
|
)
|
||||||
conf_cron_pattern = hass.data[DATA_UTILITY][meter].get(CONF_CRON_PATTERN)
|
conf_cron_pattern = hass.data[DATA_UTILITY][meter].get(CONF_CRON_PATTERN)
|
||||||
|
meter_sensor = UtilityMeterSensor(
|
||||||
meters.append(
|
meter,
|
||||||
UtilityMeterSensor(
|
conf_meter_source,
|
||||||
conf_meter_source,
|
conf.get(CONF_NAME),
|
||||||
conf.get(CONF_NAME),
|
conf_meter_type,
|
||||||
conf_meter_type,
|
conf_meter_offset,
|
||||||
conf_meter_offset,
|
conf_meter_net_consumption,
|
||||||
conf_meter_net_consumption,
|
conf.get(CONF_TARIFF),
|
||||||
conf.get(CONF_TARIFF),
|
conf_meter_tariff_entity,
|
||||||
conf_meter_tariff_entity,
|
conf_cron_pattern,
|
||||||
conf_cron_pattern,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
meters.append(meter_sensor)
|
||||||
|
|
||||||
|
hass.data[DATA_UTILITY][meter][DATA_TARIFF_SENSORS].append(meter_sensor)
|
||||||
|
|
||||||
async_add_entities(meters)
|
async_add_entities(meters)
|
||||||
|
|
||||||
|
@ -126,6 +128,7 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
parent_meter,
|
||||||
source_entity,
|
source_entity,
|
||||||
name,
|
name,
|
||||||
meter_type,
|
meter_type,
|
||||||
|
@ -136,8 +139,9 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity):
|
||||||
cron_pattern=None,
|
cron_pattern=None,
|
||||||
):
|
):
|
||||||
"""Initialize the Utility Meter sensor."""
|
"""Initialize the Utility Meter sensor."""
|
||||||
|
self._parent_meter = parent_meter
|
||||||
self._sensor_source_id = source_entity
|
self._sensor_source_id = source_entity
|
||||||
self._state = 0
|
self._state = None
|
||||||
self._last_period = 0
|
self._last_period = 0
|
||||||
self._last_reset = dt_util.utcnow()
|
self._last_reset = dt_util.utcnow()
|
||||||
self._collecting = None
|
self._collecting = None
|
||||||
|
@ -153,11 +157,26 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity):
|
||||||
self._tariff = tariff
|
self._tariff = tariff
|
||||||
self._tariff_entity = tariff_entity
|
self._tariff_entity = tariff_entity
|
||||||
|
|
||||||
|
def start(self, unit):
|
||||||
|
"""Initialize unit and state upon source initial update."""
|
||||||
|
self._unit_of_measurement = unit
|
||||||
|
self._state = 0
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_reading(self, event):
|
def async_reading(self, event):
|
||||||
"""Handle the sensor state changes."""
|
"""Handle the sensor state changes."""
|
||||||
old_state = event.data.get("old_state")
|
old_state = event.data.get("old_state")
|
||||||
new_state = event.data.get("new_state")
|
new_state = event.data.get("new_state")
|
||||||
|
|
||||||
|
if self._state is None and new_state.state:
|
||||||
|
# First state update initializes the utility_meter sensors
|
||||||
|
source_state = self.hass.states.get(self._sensor_source_id)
|
||||||
|
for sensor in self.hass.data[DATA_UTILITY][self._parent_meter][
|
||||||
|
DATA_TARIFF_SENSORS
|
||||||
|
]:
|
||||||
|
sensor.start(source_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT))
|
||||||
|
|
||||||
if (
|
if (
|
||||||
old_state is None
|
old_state is None
|
||||||
or new_state is None
|
or new_state is None
|
||||||
|
@ -333,7 +352,12 @@ class UtilityMeterSensor(RestoreEntity, SensorEntity):
|
||||||
self._change_status(tariff_entity_state.state)
|
self._change_status(tariff_entity_state.state)
|
||||||
return
|
return
|
||||||
|
|
||||||
_LOGGER.debug("<%s> collecting from %s", self.name, self._sensor_source_id)
|
_LOGGER.debug(
|
||||||
|
"<%s> collecting %s from %s",
|
||||||
|
self.name,
|
||||||
|
self._unit_of_measurement,
|
||||||
|
self._sensor_source_id,
|
||||||
|
)
|
||||||
self._collecting = async_track_state_change_event(
|
self._collecting = async_track_state_change_event(
|
||||||
self.hass, [self._sensor_source_id], self.async_reading
|
self.hass, [self._sensor_source_id], self.async_reading
|
||||||
)
|
)
|
||||||
|
|
|
@ -31,6 +31,7 @@ from homeassistant.const import (
|
||||||
ENERGY_KILO_WATT_HOUR,
|
ENERGY_KILO_WATT_HOUR,
|
||||||
EVENT_HOMEASSISTANT_START,
|
EVENT_HOMEASSISTANT_START,
|
||||||
STATE_UNAVAILABLE,
|
STATE_UNAVAILABLE,
|
||||||
|
STATE_UNKNOWN,
|
||||||
)
|
)
|
||||||
from homeassistant.core import State
|
from homeassistant.core import State
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
@ -64,6 +65,8 @@ async def test_state(hass):
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
entity_id = config[DOMAIN]["energy_bill"]["source"]
|
entity_id = config[DOMAIN]["energy_bill"]["source"]
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
entity_id, 2, {ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR}
|
entity_id, 2, {ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR}
|
||||||
|
@ -74,16 +77,19 @@ async def test_state(hass):
|
||||||
assert state is not None
|
assert state is not None
|
||||||
assert state.state == "0"
|
assert state.state == "0"
|
||||||
assert state.attributes.get("status") == COLLECTING
|
assert state.attributes.get("status") == COLLECTING
|
||||||
|
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR
|
||||||
|
|
||||||
state = hass.states.get("sensor.energy_bill_midpeak")
|
state = hass.states.get("sensor.energy_bill_midpeak")
|
||||||
assert state is not None
|
assert state is not None
|
||||||
assert state.state == "0"
|
assert state.state == "0"
|
||||||
assert state.attributes.get("status") == PAUSED
|
assert state.attributes.get("status") == PAUSED
|
||||||
|
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR
|
||||||
|
|
||||||
state = hass.states.get("sensor.energy_bill_offpeak")
|
state = hass.states.get("sensor.energy_bill_offpeak")
|
||||||
assert state is not None
|
assert state is not None
|
||||||
assert state.state == "0"
|
assert state.state == "0"
|
||||||
assert state.attributes.get("status") == PAUSED
|
assert state.attributes.get("status") == PAUSED
|
||||||
|
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR
|
||||||
|
|
||||||
now = dt_util.utcnow() + timedelta(seconds=10)
|
now = dt_util.utcnow() + timedelta(seconds=10)
|
||||||
with patch("homeassistant.util.dt.utcnow", return_value=now):
|
with patch("homeassistant.util.dt.utcnow", return_value=now):
|
||||||
|
@ -187,6 +193,49 @@ async def test_state(hass):
|
||||||
assert state.state == "0.123"
|
assert state.state == "0.123"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_init(hass):
|
||||||
|
"""Test utility sensor state initializtion."""
|
||||||
|
config = {
|
||||||
|
"utility_meter": {
|
||||||
|
"energy_bill": {
|
||||||
|
"source": "sensor.energy",
|
||||||
|
"tariffs": ["onpeak", "midpeak", "offpeak"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert await async_setup_component(hass, DOMAIN, config)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.energy_bill_onpeak")
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == STATE_UNKNOWN
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.energy_bill_offpeak")
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == STATE_UNKNOWN
|
||||||
|
|
||||||
|
entity_id = config[DOMAIN]["energy_bill"]["source"]
|
||||||
|
hass.states.async_set(
|
||||||
|
entity_id, 2, {ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR}
|
||||||
|
)
|
||||||
|
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.energy_bill_onpeak")
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == "0"
|
||||||
|
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.energy_bill_offpeak")
|
||||||
|
assert state is not None
|
||||||
|
assert state.state == "0"
|
||||||
|
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == ENERGY_KILO_WATT_HOUR
|
||||||
|
|
||||||
|
|
||||||
async def test_device_class(hass):
|
async def test_device_class(hass):
|
||||||
"""Test utility device_class."""
|
"""Test utility device_class."""
|
||||||
config = {
|
config = {
|
||||||
|
@ -205,6 +254,8 @@ async def test_device_class(hass):
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
entity_id_energy = config[DOMAIN]["energy_meter"]["source"]
|
entity_id_energy = config[DOMAIN]["energy_meter"]["source"]
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
entity_id_energy, 2, {ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR}
|
entity_id_energy, 2, {ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR}
|
||||||
|
@ -218,35 +269,13 @@ async def test_device_class(hass):
|
||||||
state = hass.states.get("sensor.energy_meter")
|
state = hass.states.get("sensor.energy_meter")
|
||||||
assert state is not None
|
assert state is not None
|
||||||
assert state.state == "0"
|
assert state.state == "0"
|
||||||
assert state.attributes.get(ATTR_DEVICE_CLASS) is None
|
|
||||||
assert state.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_TOTAL
|
|
||||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) is None
|
|
||||||
|
|
||||||
state = hass.states.get("sensor.gas_meter")
|
|
||||||
assert state is not None
|
|
||||||
assert state.state == "0"
|
|
||||||
assert state.attributes.get(ATTR_DEVICE_CLASS) is None
|
|
||||||
assert state.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_TOTAL_INCREASING
|
|
||||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) is None
|
|
||||||
|
|
||||||
hass.states.async_set(
|
|
||||||
entity_id_energy, 3, {ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR}
|
|
||||||
)
|
|
||||||
hass.states.async_set(
|
|
||||||
entity_id_gas, 3, {ATTR_UNIT_OF_MEASUREMENT: "some_archaic_unit"}
|
|
||||||
)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
state = hass.states.get("sensor.energy_meter")
|
|
||||||
assert state is not None
|
|
||||||
assert state.state == "1"
|
|
||||||
assert state.attributes.get(ATTR_DEVICE_CLASS) == "energy"
|
assert state.attributes.get(ATTR_DEVICE_CLASS) == "energy"
|
||||||
assert state.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_TOTAL
|
assert state.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_TOTAL
|
||||||
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.gas_meter")
|
state = hass.states.get("sensor.gas_meter")
|
||||||
assert state is not None
|
assert state is not None
|
||||||
assert state.state == "1"
|
assert state.state == "0"
|
||||||
assert state.attributes.get(ATTR_DEVICE_CLASS) is None
|
assert state.attributes.get(ATTR_DEVICE_CLASS) is None
|
||||||
assert state.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_TOTAL_INCREASING
|
assert state.attributes.get(ATTR_STATE_CLASS) == STATE_CLASS_TOTAL_INCREASING
|
||||||
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "some_archaic_unit"
|
assert state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) == "some_archaic_unit"
|
||||||
|
@ -272,6 +301,7 @@ async def test_restore_state(hass):
|
||||||
attributes={
|
attributes={
|
||||||
ATTR_STATUS: PAUSED,
|
ATTR_STATUS: PAUSED,
|
||||||
ATTR_LAST_RESET: last_reset,
|
ATTR_LAST_RESET: last_reset,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
State(
|
State(
|
||||||
|
@ -280,6 +310,7 @@ async def test_restore_state(hass):
|
||||||
attributes={
|
attributes={
|
||||||
ATTR_STATUS: COLLECTING,
|
ATTR_STATUS: COLLECTING,
|
||||||
ATTR_LAST_RESET: last_reset,
|
ATTR_LAST_RESET: last_reset,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -293,11 +324,13 @@ async def test_restore_state(hass):
|
||||||
assert state.state == "3"
|
assert state.state == "3"
|
||||||
assert state.attributes.get("status") == PAUSED
|
assert state.attributes.get("status") == PAUSED
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
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
|
||||||
|
|
||||||
# utility_meter is loaded, now set sensors according to utility_meter:
|
# utility_meter is loaded, now set sensors according to utility_meter:
|
||||||
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
|
||||||
|
|
Loading…
Add table
Reference in a new issue