diff --git a/_docs/entities/heat_pump.md b/_docs/entities/heat_pump.md index 01bf2aae..734159b0 100644 --- a/_docs/entities/heat_pump.md +++ b/_docs/entities/heat_pump.md @@ -83,3 +83,12 @@ This represents the current outdoor temperature as observed by the heat pump. !!! note As the integration uses cloud polling this will inherently have a delay. + +## Fixed Target Flow Temperature + +`sensor.octopus_energy_heat_pump_{{HEAT_PUMP_ID}}_fixed_target_flow_temperature` + +This represents the configured fixed target flow temperature for the heat pump, which is used in fixed mode only. + +!!! note + This is not the actual current flow temperature, but rather the configured target. \ No newline at end of file diff --git a/custom_components/octopus_energy/heat_pump/sensor_fixed_target_flow_temperature.py b/custom_components/octopus_energy/heat_pump/sensor_fixed_target_flow_temperature.py new file mode 100644 index 00000000..90357186 --- /dev/null +++ b/custom_components/octopus_energy/heat_pump/sensor_fixed_target_flow_temperature.py @@ -0,0 +1,111 @@ +from datetime import datetime +import logging +from typing import List + +from homeassistant.const import ( + STATE_UNAVAILABLE, + STATE_UNKNOWN, + UnitOfTemperature +) +from homeassistant.core import HomeAssistant, callback + +from homeassistant.util.dt import (now) +from homeassistant.helpers.update_coordinator import ( + CoordinatorEntity +) +from homeassistant.components.sensor import ( + RestoreSensor, + SensorDeviceClass, + SensorStateClass, +) + +from .base import (BaseOctopusEnergyHeatPumpSensor) +from ..utils.attributes import dict_to_typed_dict +from ..api_client.heat_pump import HeatPump +from ..coordinators.heat_pump_configuration_and_status import HeatPumpCoordinatorResult + +_LOGGER = logging.getLogger(__name__) + +class OctopusEnergyHeatPumpSensorFixedTargetFlowTemperature(CoordinatorEntity, BaseOctopusEnergyHeatPumpSensor, RestoreSensor): + """Sensor for displaying the live heat output of a heat pump.""" + + def __init__(self, hass: HomeAssistant, coordinator, heat_pump_id: str, heat_pump: HeatPump): + """Init sensor.""" + # Pass coordinator to base class + CoordinatorEntity.__init__(self, coordinator) + BaseOctopusEnergyHeatPumpSensor.__init__(self, hass, heat_pump_id, heat_pump) + + self._state = None + self._last_updated = None + + @property + def unique_id(self): + """The id of the sensor.""" + return f"octopus_energy_heat_pump_{self._heat_pump_id}_fixed_target_flow_temperature" + + @property + def name(self): + """Name of the sensor.""" + return f"Fixed Target Flow Temperature Heat Pump ({self._heat_pump_id})" + + @property + def state_class(self): + """The state class of sensor""" + return SensorStateClass.MEASUREMENT + + @property + def device_class(self): + """The type of sensor""" + return SensorDeviceClass.TEMPERATURE + + @property + def icon(self): + """Icon of the sensor.""" + return "mdi:thermometer" + + @property + def native_unit_of_measurement(self): + """Unit of measurement of the sensor.""" + return UnitOfTemperature.CELSIUS + + @property + def extra_state_attributes(self): + """Attributes of the sensor.""" + return self._attributes + + @property + def native_value(self): + return self._state + + @callback + def _handle_coordinator_update(self) -> None: + """Retrieve the configured fixed target flow temperature for the heat pump.""" + current = now() + result: HeatPumpCoordinatorResult = self.coordinator.data if self.coordinator is not None and self.coordinator.data is not None else None + + if (result is not None + and result.data is not None + and result.data.octoHeatPumpControllerConfiguration is not None + and result.data.octoHeatPumpControllerConfiguration.heatPump is not None + and result.data.octoHeatPumpControllerConfiguration.heatPump.heatingFlowTemperature is not None + and result.data.octoHeatPumpControllerConfiguration.heatPump.heatingFlowTemperature.currentTemperature is not None): + _LOGGER.debug(f"Updating OctopusEnergyHeatPumpSensorFixedTargetFlowTemperature for '{self._heat_pump_id}'") + + self._state = float(result.data.octoHeatPumpControllerConfiguration.heatPump.heatingFlowTemperature.currentTemperature.value) + self._last_updated = current + + self._attributes = dict_to_typed_dict(self._attributes) + super()._handle_coordinator_update() + + async def async_added_to_hass(self): + """Call when entity about to be added to hass.""" + # If not None, we got an initial value. + await super().async_added_to_hass() + state = await self.async_get_last_state() + last_sensor_state = await self.async_get_last_sensor_data() + + if state is not None and last_sensor_state is not None and self._state is None: + self._state = None if state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN) else last_sensor_state.native_value + self._attributes = dict_to_typed_dict(state.attributes, []) + + _LOGGER.debug(f'Restored OctopusEnergyHeatPumpSensorFixedTargetFlowTemperature state: {self._state}') \ No newline at end of file diff --git a/custom_components/octopus_energy/sensor.py b/custom_components/octopus_energy/sensor.py index 032036d3..1e6c5fcc 100644 --- a/custom_components/octopus_energy/sensor.py +++ b/custom_components/octopus_energy/sensor.py @@ -67,6 +67,7 @@ from .heat_pump.sensor_lifetime_scop import OctopusEnergyHeatPumpSensorLifetimeSCoP from .heat_pump.sensor_lifetime_heat_output import OctopusEnergyHeatPumpSensorLifetimeHeatOutput from .heat_pump.sensor_lifetime_energy_input import OctopusEnergyHeatPumpSensorLifetimeEnergyInput +from .heat_pump.sensor_fixed_target_flow_temperature import OctopusEnergyHeatPumpSensorFixedTargetFlowTemperature from .api_client.intelligent_device import IntelligentDevice from .intelligent.current_state import OctopusEnergyIntelligentCurrentState from .intelligent import get_intelligent_features @@ -577,6 +578,13 @@ def setup_heat_pump_sensors(hass: HomeAssistant, account_id: str, heat_pump_id: entities.append(OctopusEnergyHeatPumpDataLastRetrieved(hass, coordinator, account_id, heat_pump_id)) if heat_pump_response.octoHeatPumpControllerConfiguration is not None: + entities.append(OctopusEnergyHeatPumpSensorFixedTargetFlowTemperature( + hass, + coordinator, + heat_pump_id, + heat_pump_response.octoHeatPumpControllerConfiguration.heatPump + )) + for zone in heat_pump_response.octoHeatPumpControllerConfiguration.zones: if zone.configuration is not None and zone.configuration.sensors is not None: if zone.configuration.enabled == False: