Add the device power and device energy into attributes #25

This commit is contained in:
Jean-Marc Collin
2023-02-18 00:07:54 +01:00
parent 08d08e52de
commit 8bbcafdf4a
5 changed files with 72 additions and 20 deletions

View File

@@ -118,6 +118,17 @@ template:
unique_id: maison_occupee
state: "{{is_state('person.jmc', 'home') }}"
device_class: occupancy
- sensor:
- name: "Total énergie switch1"
unique_id: total_energie_switch1
unit_of_measurement: "kWh"
device_class: energy
state_class: total_increasing
state: >
{% set energy = state_attr('climate.thermostat_switch_1', 'total_energy') %}
{% if energy == 'unavailable' or energy is none%}unavailable{% else %}
{{ ((energy | float) / 1.0) | round(2, default=0) }}
{% endif %}
switch:
- platform: template

View File

@@ -134,6 +134,8 @@ from .const import (
CONF_CLIMATE,
UnknownEntity,
EventType,
ATTR_MEAN_POWER_CYCLE,
ATTR_TOTAL_ENERGY,
)
from .prop_algorithm import PropAlgorithm
@@ -201,6 +203,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
# _registry: dict[str, object] = {}
_last_temperature_mesure: datetime
_last_ext_temperature_mesure: datetime
_total_energy: float
def __init__(self, hass: HomeAssistant, unique_id, name, entry_infos) -> None:
"""Initialize the thermostat."""
@@ -251,6 +254,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
self._attr_translation_key = "versatile_thermostat"
self._total_energy = None
self.post_init(entry_infos)
def post_init(self, entry_infos):
@@ -441,6 +446,8 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
if self._motion_on:
self._attr_preset_modes.append(PRESET_ACTIVITY)
self._total_energy = 0
_LOGGER.debug(
"%s - Creation of a new VersatileThermostat entity: unique_id=%s heater_entity_id=%s",
self,
@@ -780,6 +787,9 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
else:
self._hvac_mode = HVACMode.OFF
old_total_energy = old_state.attributes.get(ATTR_TOTAL_ENERGY)
if old_total_energy:
self._total_energy = old_total_energy
else:
# No previous state, try and restore defaults
if self._target_temp is None:
@@ -981,6 +991,21 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
return None
@property
def mean_cycle_power(self) -> float | None:
"""Returns tne mean power consumption during the cycle"""
if self._is_over_climate:
return None
elif self._device_power:
return self._device_power * self._prop_algorithm.on_percent
else:
return None
@property
def total_energy(self) -> float | None:
"""Returns the total energy calculated for this thermostast"""
return self._total_energy
def turn_aux_heat_on(self) -> None:
"""Turn auxiliary heater on."""
if self._is_over_climate and self._underlying_climate:
@@ -1964,7 +1989,6 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
_LOGGER.debug(
"%s - No action on heater cause duration is 0", self
)
self.update_custom_attributes()
self._async_cancel_cycle = async_call_later(
self.hass,
time,
@@ -1978,6 +2002,7 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
heater_action=self._async_heater_turn_on,
next_cycle_action=_turn_off_later,
)
self.update_custom_attributes()
async def _turn_off_later(_):
await _turn_on_off_later(
@@ -1986,6 +2011,9 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
heater_action=self._async_underlying_entity_turn_off,
next_cycle_action=_turn_on_later,
)
# increment energy at the end of the cycle
self.incremente_energy()
self.update_custom_attributes()
await _turn_on_later(None)
@@ -2018,6 +2046,11 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
self.update_custom_attributes()
self.async_write_ha_state()
def incremente_energy(self):
"""increment the energy counter if device is active"""
if self.hvac_mode != HVACMode.OFF:
self._total_energy += self.mean_cycle_power * float(self._cycle_min) / 60.0
def update_custom_attributes(self):
"""Update the custom extra attributes for the entity"""
@@ -2054,6 +2087,9 @@ class VersatileThermostat(ClimateEntity, RestoreEntity):
"last_ext_temperature_datetime": self._last_ext_temperature_mesure.isoformat(),
"security_state": self._security_state,
"minimal_activation_delay_sec": self._minimal_activation_delay,
"device_power": self._device_power,
ATTR_MEAN_POWER_CYCLE: self.mean_cycle_power,
ATTR_TOTAL_ENERGY: self.total_energy,
"last_update_datetime": datetime.now().isoformat(),
}
if self._is_over_climate:

View File

@@ -135,6 +135,9 @@ SERVICE_SET_SECURITY = "set_security"
DEFAULT_SECURITY_MIN_ON_PERCENT = 0.5
DEFAULT_SECURITY_DEFAULT_ON_PERCENT = 0.1
ATTR_TOTAL_ENERGY = "total_energy"
ATTR_MEAN_POWER_CYCLE = "mean_cycle_power"
class EventType(Enum):
"""The event type that can be sent"""

View File

@@ -9,7 +9,7 @@ from pytest_homeassistant_custom_component.common import MockConfigEntry
from homeassistant.helpers.entity_component import EntityComponent
from ..climate import VersatileThermostat
from ..const import DOMAIN, PRESET_SECURITY, PRESET_POWER, EventType
from ..const import *
from homeassistant.components.climate import (
ClimateEntity,

View File

@@ -11,24 +11,25 @@ async def test_tpi_calculation(hass: HomeAssistant, skip_hass_states_is_state):
title="TheOverSwitchMockName",
unique_id="uniqueId",
data={
"name": "TheOverSwitchMockName",
"thermostat_type": "thermostat_over_switch",
"temperature_sensor_entity_id": "sensor.mock_temp_sensor",
"external_temperature_sensor_entity_id": "sensor.mock_ext_temp_sensor",
"cycle_min": 5,
"temp_min": 15,
"temp_max": 30,
"use_window_feature": False,
"use_motion_feature": False,
"use_power_feature": False,
"use_presence_feature": False,
"heater_entity_id": "switch.mock_switch",
"proportional_function": "tpi",
"tpi_coef_int": 0.3,
"tpi_coef_ext": 0.01,
"minimal_activation_delay": 30,
"security_delay_min": 5,
"security_default_on_percent": 0.3,
CONF_NAME: "TheOverSwitchMockName",
CONF_THERMOSTAT_TYPE: CONF_THERMOSTAT_SWITCH,
CONF_TEMP_SENSOR: "sensor.mock_temp_sensor",
CONF_EXTERNAL_TEMP_SENSOR: "sensor.mock_ext_temp_sensor",
CONF_CYCLE_MIN: 5,
CONF_TEMP_MIN: 15,
CONF_TEMP_MAX: 30,
CONF_USE_WINDOW_FEATURE: False,
CONF_USE_MOTION_FEATURE: False,
CONF_USE_POWER_FEATURE: False,
CONF_USE_PRESENCE_FEATURE: False,
CONF_HEATER: "switch.mock_switch",
CONF_PROP_FUNCTION: PROPORTIONAL_FUNCTION_TPI,
CONF_TPI_COEF_INT: 0.3,
CONF_TPI_COEF_EXT: 0.01,
CONF_MINIMAL_ACTIVATION_DELAY: 30,
CONF_SECURITY_DELAY_MIN: 5,
CONF_SECURITY_MIN_ON_PERCENT: 0.3,
# CONF_DEVICE_POWER: 100,
},
)
@@ -45,6 +46,7 @@ async def test_tpi_calculation(hass: HomeAssistant, skip_hass_states_is_state):
assert tpi_algo.calculated_on_percent == 1
assert tpi_algo.on_time_sec == 300
assert tpi_algo.off_time_sec == 0
assert entity.mean_cycle_power is None
tpi_algo.calculate(15, 14, 5)
assert tpi_algo.on_percent == 0.4