Cost sensor handle consumption sensor in Wh (#53746)
This commit is contained in:
parent
05a7853720
commit
2cbcd5f2a9
2 changed files with 107 additions and 6 deletions
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
import logging
|
||||||
from typing import Any, Final, Literal, TypeVar, cast
|
from typing import Any, Final, Literal, TypeVar, cast
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
|
@ -11,6 +12,11 @@ from homeassistant.components.sensor import (
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
SensorEntity,
|
SensorEntity,
|
||||||
)
|
)
|
||||||
|
from homeassistant.const import (
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT,
|
||||||
|
ENERGY_KILO_WATT_HOUR,
|
||||||
|
ENERGY_WATT_HOUR,
|
||||||
|
)
|
||||||
from homeassistant.core import HomeAssistant, State, callback, split_entity_id
|
from homeassistant.core import HomeAssistant, State, callback, split_entity_id
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.helpers.event import async_track_state_change_event
|
from homeassistant.helpers.event import async_track_state_change_event
|
||||||
|
@ -20,6 +26,8 @@ import homeassistant.util.dt as dt_util
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .data import EnergyManager, async_get_manager
|
from .data import EnergyManager, async_get_manager
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_platform(
|
async def async_setup_platform(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
@ -188,6 +196,12 @@ class EnergyCostSensor(SensorEntity):
|
||||||
energy_price = float(energy_price_state.state)
|
energy_price = float(energy_price_state.state)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if energy_price_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT, "").endswith(
|
||||||
|
f"/{ENERGY_WATT_HOUR}"
|
||||||
|
):
|
||||||
|
energy_price *= 1000.0
|
||||||
|
|
||||||
else:
|
else:
|
||||||
energy_price_state = None
|
energy_price_state = None
|
||||||
energy_price = cast(float, self._flow["number_energy_price"])
|
energy_price = cast(float, self._flow["number_energy_price"])
|
||||||
|
@ -197,6 +211,16 @@ class EnergyCostSensor(SensorEntity):
|
||||||
self._reset(energy_state)
|
self._reset(energy_state)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
energy_unit = energy_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT)
|
||||||
|
|
||||||
|
if energy_unit == ENERGY_WATT_HOUR:
|
||||||
|
energy_price /= 1000
|
||||||
|
elif energy_unit != ENERGY_KILO_WATT_HOUR:
|
||||||
|
_LOGGER.warning(
|
||||||
|
"Found unexpected unit %s for %s", energy_unit, energy_state.entity_id
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
if (
|
if (
|
||||||
energy_state.attributes[ATTR_LAST_RESET]
|
energy_state.attributes[ATTR_LAST_RESET]
|
||||||
!= self._last_energy_sensor_state.attributes[ATTR_LAST_RESET]
|
!= self._last_energy_sensor_state.attributes[ATTR_LAST_RESET]
|
||||||
|
|
|
@ -16,6 +16,8 @@ from homeassistant.const import (
|
||||||
ATTR_DEVICE_CLASS,
|
ATTR_DEVICE_CLASS,
|
||||||
ATTR_UNIT_OF_MEASUREMENT,
|
ATTR_UNIT_OF_MEASUREMENT,
|
||||||
DEVICE_CLASS_MONETARY,
|
DEVICE_CLASS_MONETARY,
|
||||||
|
ENERGY_KILO_WATT_HOUR,
|
||||||
|
ENERGY_WATT_HOUR,
|
||||||
)
|
)
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
import homeassistant.util.dt as dt_util
|
import homeassistant.util.dt as dt_util
|
||||||
|
@ -133,7 +135,9 @@ async def test_cost_sensor_price_entity(
|
||||||
# Optionally initialize dependent entities
|
# Optionally initialize dependent entities
|
||||||
if initial_energy is not None:
|
if initial_energy is not None:
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
usage_sensor_entity_id, initial_energy, {"last_reset": last_reset}
|
usage_sensor_entity_id,
|
||||||
|
initial_energy,
|
||||||
|
{"last_reset": last_reset, ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR},
|
||||||
)
|
)
|
||||||
hass.states.async_set("sensor.energy_price", "1")
|
hass.states.async_set("sensor.energy_price", "1")
|
||||||
|
|
||||||
|
@ -152,7 +156,12 @@ async def test_cost_sensor_price_entity(
|
||||||
if initial_energy is None:
|
if initial_energy is None:
|
||||||
with patch("homeassistant.util.dt.utcnow", return_value=now):
|
with patch("homeassistant.util.dt.utcnow", return_value=now):
|
||||||
hass.states.async_set(
|
hass.states.async_set(
|
||||||
usage_sensor_entity_id, "0", {"last_reset": last_reset}
|
usage_sensor_entity_id,
|
||||||
|
"0",
|
||||||
|
{
|
||||||
|
"last_reset": last_reset,
|
||||||
|
ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR,
|
||||||
|
},
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
@ -169,7 +178,11 @@ async def test_cost_sensor_price_entity(
|
||||||
# # assert entry.unique_id == "energy_energy_consumption cost"
|
# # assert entry.unique_id == "energy_energy_consumption cost"
|
||||||
|
|
||||||
# Energy use bumped to 10 kWh
|
# Energy use bumped to 10 kWh
|
||||||
hass.states.async_set(usage_sensor_entity_id, "10", {"last_reset": last_reset})
|
hass.states.async_set(
|
||||||
|
usage_sensor_entity_id,
|
||||||
|
"10",
|
||||||
|
{"last_reset": last_reset, ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR},
|
||||||
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
state = hass.states.get(cost_sensor_entity_id)
|
state = hass.states.get(cost_sensor_entity_id)
|
||||||
assert state.state == "10.0" # 0 EUR + (10-0) kWh * 1 EUR/kWh = 10 EUR
|
assert state.state == "10.0" # 0 EUR + (10-0) kWh * 1 EUR/kWh = 10 EUR
|
||||||
|
@ -189,7 +202,11 @@ async def test_cost_sensor_price_entity(
|
||||||
assert state.state == "10.0" # 10 EUR + (10-10) kWh * 2 EUR/kWh = 10 EUR
|
assert state.state == "10.0" # 10 EUR + (10-10) kWh * 2 EUR/kWh = 10 EUR
|
||||||
|
|
||||||
# Additional consumption is using the new price
|
# Additional consumption is using the new price
|
||||||
hass.states.async_set(usage_sensor_entity_id, "14.5", {"last_reset": last_reset})
|
hass.states.async_set(
|
||||||
|
usage_sensor_entity_id,
|
||||||
|
"14.5",
|
||||||
|
{"last_reset": last_reset, ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR},
|
||||||
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
state = hass.states.get(cost_sensor_entity_id)
|
state = hass.states.get(cost_sensor_entity_id)
|
||||||
assert state.state == "19.0" # 10 EUR + (14.5-10) kWh * 2 EUR/kWh = 19 EUR
|
assert state.state == "19.0" # 10 EUR + (14.5-10) kWh * 2 EUR/kWh = 19 EUR
|
||||||
|
@ -202,13 +219,21 @@ async def test_cost_sensor_price_entity(
|
||||||
|
|
||||||
# Energy sensor is reset, with start point at 4kWh
|
# Energy sensor is reset, with start point at 4kWh
|
||||||
last_reset = (now + timedelta(seconds=1)).isoformat()
|
last_reset = (now + timedelta(seconds=1)).isoformat()
|
||||||
hass.states.async_set(usage_sensor_entity_id, "4", {"last_reset": last_reset})
|
hass.states.async_set(
|
||||||
|
usage_sensor_entity_id,
|
||||||
|
"4",
|
||||||
|
{"last_reset": last_reset, ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR},
|
||||||
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
state = hass.states.get(cost_sensor_entity_id)
|
state = hass.states.get(cost_sensor_entity_id)
|
||||||
assert state.state == "0.0" # 0 EUR + (4-4) kWh * 2 EUR/kWh = 0 EUR
|
assert state.state == "0.0" # 0 EUR + (4-4) kWh * 2 EUR/kWh = 0 EUR
|
||||||
|
|
||||||
# Energy use bumped to 10 kWh
|
# Energy use bumped to 10 kWh
|
||||||
hass.states.async_set(usage_sensor_entity_id, "10", {"last_reset": last_reset})
|
hass.states.async_set(
|
||||||
|
usage_sensor_entity_id,
|
||||||
|
"10",
|
||||||
|
{"last_reset": last_reset, ATTR_UNIT_OF_MEASUREMENT: ENERGY_KILO_WATT_HOUR},
|
||||||
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
state = hass.states.get(cost_sensor_entity_id)
|
state = hass.states.get(cost_sensor_entity_id)
|
||||||
assert state.state == "12.0" # 0 EUR + (10-4) kWh * 2 EUR/kWh = 12 EUR
|
assert state.state == "12.0" # 0 EUR + (10-4) kWh * 2 EUR/kWh = 12 EUR
|
||||||
|
@ -218,3 +243,55 @@ async def test_cost_sensor_price_entity(
|
||||||
statistics = await hass.loop.run_in_executor(None, _compile_statistics, hass)
|
statistics = await hass.loop.run_in_executor(None, _compile_statistics, hass)
|
||||||
assert cost_sensor_entity_id in statistics
|
assert cost_sensor_entity_id in statistics
|
||||||
assert statistics[cost_sensor_entity_id]["stat"]["sum"] == 31.0
|
assert statistics[cost_sensor_entity_id]["stat"]["sum"] == 31.0
|
||||||
|
|
||||||
|
|
||||||
|
async def test_cost_sensor_handle_wh(hass, hass_storage) -> None:
|
||||||
|
"""Test energy cost price from sensor entity."""
|
||||||
|
energy_data = data.EnergyManager.default_preferences()
|
||||||
|
energy_data["energy_sources"].append(
|
||||||
|
{
|
||||||
|
"type": "grid",
|
||||||
|
"flow_from": [
|
||||||
|
{
|
||||||
|
"stat_energy_from": "sensor.energy_consumption",
|
||||||
|
"entity_energy_from": "sensor.energy_consumption",
|
||||||
|
"stat_cost": None,
|
||||||
|
"entity_energy_price": None,
|
||||||
|
"number_energy_price": 0.5,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"flow_to": [],
|
||||||
|
"cost_adjustment_day": 0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
hass_storage[data.STORAGE_KEY] = {
|
||||||
|
"version": 1,
|
||||||
|
"data": energy_data,
|
||||||
|
}
|
||||||
|
|
||||||
|
now = dt_util.utcnow()
|
||||||
|
last_reset = dt_util.utc_from_timestamp(0).isoformat()
|
||||||
|
|
||||||
|
hass.states.async_set(
|
||||||
|
"sensor.energy_consumption",
|
||||||
|
10000,
|
||||||
|
{"last_reset": last_reset, "unit_of_measurement": ENERGY_WATT_HOUR},
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch("homeassistant.util.dt.utcnow", return_value=now):
|
||||||
|
await setup_integration(hass)
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.energy_consumption_cost")
|
||||||
|
assert state.state == "0.0"
|
||||||
|
|
||||||
|
# Energy use bumped to 10 kWh
|
||||||
|
hass.states.async_set(
|
||||||
|
"sensor.energy_consumption",
|
||||||
|
20000,
|
||||||
|
{"last_reset": last_reset, "unit_of_measurement": ENERGY_WATT_HOUR},
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
state = hass.states.get("sensor.energy_consumption_cost")
|
||||||
|
assert state.state == "5.0"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue