Skip to content

Commit

Permalink
feat: Climate entity for DHW
Browse files Browse the repository at this point in the history
  • Loading branch information
alepee committed Nov 24, 2024
1 parent 5b9e36c commit aed4fab
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 1 deletion.
148 changes: 148 additions & 0 deletions custom_components/hitachi_yutaki/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
MASK_DEFROST,
MASK_COMPRESSOR,
PRESET_COMFORT,
PRESET_DHW_OFF,
PRESET_DHW_STANDARD,
PRESET_DHW_HIGH_DEMAND,
)
from .coordinator import HitachiYutakiDataCoordinator

Expand All @@ -48,6 +51,13 @@ class HitachiYutakiClimateEntityDescription(ClimateEntityDescription):
),
)

DHW_CLIMATE_DESCRIPTIONS: Final[tuple[HitachiYutakiClimateEntityDescription, ...]] = (
HitachiYutakiClimateEntityDescription(
key="dhw_climate",
translation_key="dhw_climate",
),
)


async def async_setup_entry(
hass: HomeAssistant,
Expand Down Expand Up @@ -84,6 +94,18 @@ async def async_setup_entry(
)
)

# Add DHW climate if configured
if coordinator.has_dhw():
entities.append(
HitachiYutakiDHWClimate(
coordinator=coordinator,
description=DHW_CLIMATE_DESCRIPTIONS[0],
device_info=DeviceInfo(
identifiers={(DOMAIN, f"{entry.entry_id}_{DEVICE_DHW}")},
),
)
)

async_add_entities(entities)


Expand Down Expand Up @@ -264,3 +286,129 @@ async def async_set_preset_mode(self, preset_mode: str) -> None:
f"{self._register_prefix}_eco_mode",
0 if preset_mode == PRESET_ECO else 1
)


class HitachiYutakiDHWClimate(CoordinatorEntity[HitachiYutakiDataCoordinator], ClimateEntity):
"""Representation of a Hitachi Yutaki DHW Climate."""

entity_description: HitachiYutakiClimateEntityDescription

def __init__(
self,
coordinator: HitachiYutakiDataCoordinator,
description: HitachiYutakiClimateEntityDescription,
device_info: DeviceInfo,
) -> None:
"""Initialize the climate entity."""
super().__init__(coordinator)
self.entity_description = description
self._attr_unique_id = f"{coordinator.slave}_dhw_climate"
self._attr_device_info = device_info
self._attr_has_entity_name = True

# Set supported features
self._attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.PRESET_MODE
)

# Set temperature settings
self._attr_min_temp = 30.0
self._attr_max_temp = 60.0
self._attr_target_temperature_step = 1.0
self._attr_temperature_unit = UnitOfTemperature.CELSIUS

# Set available modes
self._attr_hvac_modes = [HVACMode.OFF, HVACMode.HEAT]

# Set available presets
self._attr_preset_modes = [
PRESET_DHW_OFF,
PRESET_DHW_STANDARD,
PRESET_DHW_HIGH_DEMAND,
]

@property
def current_temperature(self) -> float | None:
"""Return the current temperature."""
if self.coordinator.data is None:
return None

temp = self.coordinator.data.get("dhw_current_temp")
if temp is None:
return None

# Convert from 2's complement if necessary (16-bit signed integer)
if temp > 32767: # If highest bit is set (negative number)
temp = temp - 65536

return float(temp) / 10

@property
def target_temperature(self) -> float | None:
"""Return the temperature we try to reach."""
if self.coordinator.data is None:
return None

temp = self.coordinator.data.get("dhw_target_temp")
if temp is None:
return None

return float(temp) / 10

@property
def hvac_mode(self) -> HVACMode | None:
"""Return hvac operation mode."""
if self.coordinator.data is None:
return None

power = self.coordinator.data.get("dhw_power")
return HVACMode.HEAT if power == 1 else HVACMode.OFF

@property
def preset_mode(self) -> str | None:
"""Return the current preset mode."""
if self.coordinator.data is None:
return None

power = self.coordinator.data.get("dhw_power")
if power == 0:
return PRESET_DHW_OFF

high_demand = self.coordinator.data.get("dhw_mode")
return PRESET_DHW_HIGH_DEMAND if high_demand == 1 else PRESET_DHW_STANDARD

async def async_set_temperature(self, **kwargs) -> None:
"""Set new target temperature."""
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
return

await self.coordinator.async_write_register(
"dhw_target_temp",
int(temperature * 10)
)

async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set new target hvac mode."""
await self.coordinator.async_write_register(
"dhw_power",
1 if hvac_mode == HVACMode.HEAT else 0
)

async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set new preset mode."""
if preset_mode not in self.preset_modes:
return

if preset_mode == PRESET_DHW_OFF:
await self.coordinator.async_write_register("dhw_power", 0)
return

# For all other modes, ensure power is on
await self.coordinator.async_write_register("dhw_power", 1)

# Set high demand mode
await self.coordinator.async_write_register(
"dhw_mode",
1 if preset_mode == PRESET_DHW_HIGH_DEMAND else 0
)
9 changes: 8 additions & 1 deletion custom_components/hitachi_yutaki/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,19 +144,26 @@
UNIT_MODEL_M: "Yutaki M",
}

# Add these constants for HVAC modes and presets
# HVAC modes
HVAC_MODE_MAP = {
"cool": 0,
"heat": 1,
"auto": 2,
}

# Central control modes
CENTRAL_CONTROL_MODE_MAP = {
"local": 0,
"air": 1,
"water": 2,
"total": 3,
}

# HVAC presets
PRESET_COMFORT = "comfort"
PRESET_ECO = "eco"

# DHW presets
PRESET_DHW_OFF = "off"
PRESET_DHW_STANDARD = "standard"
PRESET_DHW_HIGH_DEMAND = "high_demand"

0 comments on commit aed4fab

Please sign in to comment.