Add the device power and device energy into attributes #25
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"""
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user