-
-
Notifications
You must be signed in to change notification settings - Fork 67
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Added climate control for heat pump zone (3 hours dev time)
- Loading branch information
1 parent
b479c89
commit 5e72161
Showing
4 changed files
with
244 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import logging | ||
|
||
from custom_components.octopus_energy.api_client import OctopusEnergyApiClient | ||
from homeassistant.core import HomeAssistant | ||
|
||
from .api_client.heat_pump import HeatPumpResponse | ||
from .heat_pump import get_mock_heat_pump_id | ||
from .heat_pump.zone import OctopusEnergyHeatPumpZone | ||
from .utils.debug_overrides import async_get_account_debug_override | ||
|
||
from .const import ( | ||
CONFIG_ACCOUNT_ID, | ||
DATA_ACCOUNT, | ||
DATA_CLIENT, | ||
DATA_HEAT_PUMP_CONFIGURATION_AND_STATUS_COORDINATOR, | ||
DATA_HEAT_PUMP_CONFIGURATION_AND_STATUS_KEY, | ||
DOMAIN, | ||
|
||
CONFIG_MAIN_API_KEY | ||
) | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
async def async_setup_entry(hass, entry, async_add_entities): | ||
"""Setup sensors based on our entry""" | ||
|
||
config = dict(entry.data) | ||
|
||
if entry.options: | ||
config.update(entry.options) | ||
|
||
if CONFIG_MAIN_API_KEY in config: | ||
await async_setup_default_sensors(hass, config, async_add_entities) | ||
|
||
return True | ||
|
||
async def async_setup_default_sensors(hass, config, async_add_entities): | ||
_LOGGER.debug('Setting up default sensors') | ||
|
||
entities = [] | ||
|
||
account_id = config[CONFIG_ACCOUNT_ID] | ||
client = hass.data[DOMAIN][account_id][DATA_CLIENT] | ||
account_debug_override = await async_get_account_debug_override(hass, account_id) | ||
account_result = hass.data[DOMAIN][account_id][DATA_ACCOUNT] | ||
account_info = account_result.account if account_result is not None else None | ||
|
||
mock_heat_pump = account_debug_override.mock_heat_pump if account_debug_override is not None else False | ||
if mock_heat_pump: | ||
heat_pump_id = get_mock_heat_pump_id() | ||
key = DATA_HEAT_PUMP_CONFIGURATION_AND_STATUS_KEY.format(heat_pump_id) | ||
coordinator = hass.data[DOMAIN][account_id][DATA_HEAT_PUMP_CONFIGURATION_AND_STATUS_COORDINATOR.format(heat_pump_id)] | ||
entities.extend(setup_heat_pump_sensors(hass, client, heat_pump_id, hass.data[DOMAIN][account_id][key].data, coordinator, mock_heat_pump)) | ||
elif "heat_pump_ids" in account_info: | ||
for heat_pump_id in account_info["heat_pump_ids"]: | ||
key = DATA_HEAT_PUMP_CONFIGURATION_AND_STATUS_KEY.format(heat_pump_id) | ||
coordinator = hass.data[DOMAIN][account_id][DATA_HEAT_PUMP_CONFIGURATION_AND_STATUS_COORDINATOR.format(heat_pump_id)] | ||
entities.extend(setup_heat_pump_sensors(hass, client, heat_pump_id, hass.data[DOMAIN][account_id][key].data, coordinator, mock_heat_pump)) | ||
|
||
async_add_entities(entities) | ||
|
||
def setup_heat_pump_sensors(hass: HomeAssistant, client: OctopusEnergyApiClient, heat_pump_id: str, heat_pump_response: HeatPumpResponse, coordinator, mock_heat_pump: bool): | ||
|
||
entities = [] | ||
|
||
if heat_pump_response is not None and heat_pump_response.octoHeatPumpControllerConfiguration is not None: | ||
for zone in heat_pump_response.octoHeatPumpControllerConfiguration.zones: | ||
if zone.configuration is not None: | ||
entities.append(OctopusEnergyHeatPumpZone( | ||
hass, | ||
coordinator, | ||
client, | ||
heat_pump_id, | ||
heat_pump_response.octoHeatPumpControllerConfiguration.heatPump, | ||
zone, | ||
mock_heat_pump | ||
)) | ||
|
||
return entities |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
from datetime import datetime | ||
import logging | ||
from typing import List | ||
|
||
from homeassistant.const import ( | ||
STATE_UNAVAILABLE, | ||
STATE_UNKNOWN, | ||
UnitOfTemperature, | ||
PRECISION_TENTHS, | ||
ATTR_TEMPERATURE | ||
) | ||
from homeassistant.core import HomeAssistant, callback | ||
|
||
from homeassistant.util.dt import (now) | ||
from homeassistant.helpers.update_coordinator import ( | ||
CoordinatorEntity | ||
) | ||
from homeassistant.components.climate import ( | ||
ClimateEntity, | ||
ClimateEntityFeature, | ||
HVACMode, | ||
PRESET_NONE, | ||
PRESET_BOOST, | ||
) | ||
from homeassistant.helpers.restore_state import RestoreEntity | ||
|
||
from .base import (BaseOctopusEnergyHeatPumpSensor) | ||
from ..utils.attributes import dict_to_typed_dict | ||
from ..api_client.heat_pump import ConfigurationZone, HeatPump, Sensor, Zone | ||
from ..coordinators.heatpump_configuration_and_status import HeatPumpCoordinatorResult | ||
from ..api_client import OctopusEnergyApiClient | ||
|
||
_LOGGER = logging.getLogger(__name__) | ||
|
||
class OctopusEnergyHeatPumpZone(CoordinatorEntity, BaseOctopusEnergyHeatPumpSensor, ClimateEntity): | ||
"""Sensor for interacting with a heat pump zone.""" | ||
|
||
_attr_supported_features = ( | ||
ClimateEntityFeature.TURN_OFF | ||
| ClimateEntityFeature.TURN_ON | ||
| ClimateEntityFeature.TARGET_TEMPERATURE | ||
| ClimateEntityFeature.PRESET_MODE | ||
) | ||
|
||
_attr_min_temp = 5 | ||
_attr_max_temp = 50 | ||
_attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF, HVACMode.AUTO] | ||
_attr_hvac_mode = None | ||
_attr_preset_modes = [PRESET_NONE, PRESET_BOOST] | ||
_attr_preset_mode = None | ||
_attr_temperature_unit = UnitOfTemperature.CELSIUS | ||
_attr_target_temperature_step = PRECISION_TENTHS | ||
|
||
def __init__(self, hass: HomeAssistant, coordinator, client: OctopusEnergyApiClient, heat_pump_id: str, heat_pump: HeatPump, zone: ConfigurationZone, is_mocked: bool): | ||
"""Init sensor.""" | ||
self._zone = zone | ||
self._client = client | ||
self._is_mocked = is_mocked | ||
|
||
# self._attributes = { | ||
# "type": zone.configuration.zoneType, | ||
# "calling_for_heat": zone.configuration.callForHeat, | ||
# "is_enabled": zone.configuration.enabled | ||
# } | ||
|
||
# Pass coordinator to base class | ||
CoordinatorEntity.__init__(self, coordinator) | ||
BaseOctopusEnergyHeatPumpSensor.__init__(self, hass, heat_pump_id, heat_pump, "climate") | ||
|
||
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}_{self._zone.configuration.code}" | ||
|
||
@property | ||
def name(self): | ||
"""Name of the sensor.""" | ||
return f"Zone ({self._zone.configuration.displayName}) Heat Pump ({self._heat_pump_id})" | ||
|
||
@callback | ||
def _handle_coordinator_update(self) -> None: | ||
"""Retrieve the previous rate.""" | ||
|
||
# self._attributes = { | ||
# "type": self._zone.configuration.zoneType, | ||
# "calling_for_heat": self._zone.configuration.callForHeat, | ||
# "is_enabled": self._zone.configuration.enabled | ||
# } | ||
|
||
# Find the previous rate. We only need to do this every half an hour | ||
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.octoHeatPumpControllerStatus is not None and | ||
result.data.octoHeatPumpControllerStatus.zones): | ||
_LOGGER.debug(f"Updating OctopusEnergyHeatPumpZone for '{self._heat_pump_id}/{self._zone.configuration.code}'") | ||
|
||
zones: List[Zone] = result.data.octoHeatPumpControllerStatus.zones | ||
for zone in zones: | ||
if zone.zone == self._zone.configuration.code and zone.telemetry is not None: | ||
|
||
if zone.telemetry.mode == "ON": | ||
self._attr_hvac_mode = HVACMode.HEAT | ||
self._attr_preset_mode = PRESET_NONE | ||
elif zone.telemetry.mode == "OFF": | ||
self._attr_hvac_mode = HVACMode.OFF | ||
self._attr_preset_mode = PRESET_NONE | ||
elif zone.telemetry.mode == "AUTO": | ||
self._attr_hvac_mode = HVACMode.AUTO | ||
self._attr_preset_mode = PRESET_NONE | ||
elif zone.telemetry.mode == "ON": | ||
self._attr_preset_mode = PRESET_BOOST | ||
else: | ||
raise Exception(f"Unexpected heat pump mode detected: {zone.telemetry.mode}") | ||
|
||
self._attr_target_temperature = zone.telemetry.setpointInCelsius | ||
|
||
if (result.data.octoHeatPumpControllerStatus.sensors and self._zone.configuration.primarySensor): | ||
sensors: List[Sensor] = result.data.octoHeatPumpControllerStatus.sensors | ||
for sensor in sensors: | ||
if sensor.code == self._zone.configuration.primarySensor and sensor.telemetry is not None: | ||
self._attr_current_temperature = sensor.telemetry.temperatureInCelsius | ||
|
||
self._attributes["retrieved_at"] = datetime.strptime(zone.telemetry.retrievedAt, "%Y-%m-%dT%H:%M:%S%z") | ||
|
||
self._last_updated = current | ||
|
||
self._attributes = dict_to_typed_dict(self._attributes) | ||
super()._handle_coordinator_update() | ||
|
||
async def async_set_hvac_mode(self, hvac_mode): | ||
"""Set new target hvac mode.""" | ||
# await self._client.async_set_heat_pump_mode() | ||
self._attr_hvac_mode = hvac_mode | ||
self.async_write_ha_state() | ||
|
||
async def async_turn_on(self): | ||
"""Turn the entity on.""" | ||
# await self._client.async_set_heat_pump_mode() | ||
self._attr_hvac_mode = HVACMode.HEAT | ||
self.async_write_ha_state() | ||
|
||
async def async_turn_off(self): | ||
"""Turn the entity off.""" | ||
# await self._client.async_set_heat_pump_mode() | ||
self._attr_hvac_mode = HVACMode.OFF | ||
self.async_write_ha_state() | ||
|
||
async def async_set_preset_mode(self, preset_mode): | ||
"""Set new target preset mode.""" | ||
# await self._client.async_set_heat_pump_mode() | ||
self._attr_preset_mode = preset_mode | ||
self.async_write_ha_state() | ||
|
||
async def async_set_temperature(self, **kwargs) -> None: | ||
"""Set new target temperature.""" | ||
temperature = kwargs[ATTR_TEMPERATURE] | ||
# await self._client.async_set_heat_pump_mode() | ||
self._attr_target_temperature = temperature | ||
self.async_write_ha_state() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters