Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix thermostat naming, bump python to 3.12, entity value caching compatibility #12

Merged
merged 3 commits into from
Jan 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .python-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.11.3
3.12.0
190 changes: 80 additions & 110 deletions custom_components/daikinone/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ async def async_setup_entry(

entities = [
DaikinOneThermostat(
ClimateEntityDescription(key=device.id, name=None),
ClimateEntityDescription(key=device.id, has_entity_name=True, name=None),
data,
device,
)
Expand Down Expand Up @@ -68,94 +68,21 @@ def __init__(
self._data = data
self._thermostat = thermostat

@property
def unique_id(self):
"""Return a unique identifier for this sensor."""
return f"{self._thermostat.id}-climate"

@property
def device_info(self) -> DeviceInfo | None:
"""Return device information for this sensor."""

return DeviceInfo(
self._attr_unique_id = f"{self._thermostat.id}-climate"
self._attr_temperature_unit = UnitOfTemperature.CELSIUS
self._attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE
)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, self._thermostat.id)},
name=f"{self._thermostat.name} Thermostat",
manufacturer=MANUFACTURER,
model=self._thermostat.model,
sw_version=self._thermostat.firmware_version,
)
self._attr_hvac_modes = self.get_hvac_modes()

@property
def available(self):
"""Return if device is available."""
return self._thermostat.online

@property
def supported_features(self):
return ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE

@property
def temperature_unit(self):
return UnitOfTemperature.CELSIUS

@property
def current_temperature(self) -> float | None:
return self._thermostat.indoor_temperature.celsius

@property
def target_temperature(self) -> float | None:
match self._thermostat.mode:
case DaikinThermostatMode.HEAT | DaikinThermostatMode.AUX_HEAT:
return self._thermostat.set_point_heat.celsius
case DaikinThermostatMode.COOL:
return self._thermostat.set_point_cool.celsius
case _:
pass

return None

@property
def target_temperature_low(self) -> float | None:
match self._thermostat.mode:
case DaikinThermostatMode.AUTO:
return self._thermostat.set_point_heat.celsius
case _:
pass

return None

@property
def target_temperature_high(self) -> float | None:
match self._thermostat.mode:
case DaikinThermostatMode.AUTO:
return self._thermostat.set_point_cool.celsius
case _:
pass

return None

@property
def min_temp(self) -> float:
# these should be the same but just in case, take the larger of the two for the min
return max(
self._thermostat.set_point_heat_min.celsius,
self._thermostat.set_point_cool_min.celsius,
)

@property
def max_temp(self) -> float:
# these should be the same but just in case, take the smaller of the two for the max
return min(
self._thermostat.set_point_heat_max.celsius,
self._thermostat.set_point_cool_max.celsius,
)

@property
def current_humidity(self) -> int:
return self._thermostat.indoor_humidity

@property
def hvac_modes(self) -> list[HVACMode]:
def get_hvac_modes(self) -> list[HVACMode]:
modes: list[HVACMode] = []

if (
Expand All @@ -173,34 +100,6 @@ def hvac_modes(self) -> list[HVACMode]:

return modes

@property
def hvac_mode(self):
match self._thermostat.mode:
case DaikinThermostatMode.AUTO:
return HVACMode.HEAT_COOL
case DaikinThermostatMode.HEAT:
return HVACMode.HEAT
case DaikinThermostatMode.COOL:
return HVACMode.COOL
case DaikinThermostatMode.AUX_HEAT:
return HVACMode.HEAT
case DaikinThermostatMode.OFF:
return HVACMode.OFF

@property
def hvac_action(self):
match self._thermostat.status:
case DaikinThermostatStatus.HEATING:
return HVACAction.HEATING
case DaikinThermostatStatus.COOLING:
return HVACAction.COOLING
case DaikinThermostatStatus.CIRCULATING_AIR:
return HVACAction.FAN
case DaikinThermostatStatus.DRYING:
return HVACAction.DRYING
case DaikinThermostatStatus.IDLE:
return HVACAction.IDLE

async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set new target hvac mode."""
target_mode: DaikinThermostatMode
Expand Down Expand Up @@ -312,6 +211,77 @@ async def async_update(self, no_throttle: bool = False) -> None:
await self._data.update(no_throttle=no_throttle)
self._thermostat = self._data.daikin.get_thermostat(self._thermostat.id)

# update entity data
#
self._attr_available = self._thermostat.online
self._attr_current_temperature = self._thermostat.indoor_temperature.celsius
self._attr_current_humidity = self._thermostat.indoor_humidity

# hvac mode
#
match self._thermostat.mode:
case DaikinThermostatMode.AUTO:
self._attr_hvac_mode = HVACMode.HEAT_COOL
case DaikinThermostatMode.HEAT:
self._attr_hvac_mode = HVACMode.HEAT
case DaikinThermostatMode.COOL:
self._attr_hvac_mode = HVACMode.COOL
case DaikinThermostatMode.AUX_HEAT:
self._attr_hvac_mode = HVACMode.HEAT
case DaikinThermostatMode.OFF:
self._attr_hvac_mode = HVACMode.OFF

# hvac action
#
match self._thermostat.status:
case DaikinThermostatStatus.HEATING:
self._attr_hvac_action = HVACAction.HEATING
case DaikinThermostatStatus.COOLING:
self._attr_hvac_action = HVACAction.COOLING
case DaikinThermostatStatus.CIRCULATING_AIR:
self._attr_hvac_action = HVACAction.FAN
case DaikinThermostatStatus.DRYING:
self._attr_hvac_action = HVACAction.DRYING
case DaikinThermostatStatus.IDLE:
self._attr_hvac_action = HVACAction.IDLE

# target temperature
#
match self._thermostat.mode:
case DaikinThermostatMode.HEAT | DaikinThermostatMode.AUX_HEAT:
self._attr_target_temperature = self._thermostat.set_point_heat.celsius
case DaikinThermostatMode.COOL:
self._attr_target_temperature = self._thermostat.set_point_cool.celsius
case _:
pass

# target temperature range
#
match self._thermostat.mode:
case DaikinThermostatMode.AUTO:
self._attr_target_temperature_low = self._thermostat.set_point_heat.celsius
case _:
pass
match self._thermostat.mode:
case DaikinThermostatMode.AUTO:
self._attr_target_temperature_high = self._thermostat.set_point_cool.celsius
case _:
pass

# temperature bounds
#

# these should be the same but just in case, take the larger of the two for the min
self._attr_min_temp = max(
self._thermostat.set_point_heat_min.celsius,
self._thermostat.set_point_cool_min.celsius,
)
# these should be the same but just in case, take the smaller of the two for the max
self._attr_max_temp = min(
self._thermostat.set_point_heat_max.celsius,
self._thermostat.set_point_cool_max.celsius,
)

async def update_state_optimistically(
self, update: Callable[[DaikinThermostat], None], check: Callable[[DaikinThermostat], bool]
) -> None:
Expand Down
2 changes: 1 addition & 1 deletion custom_components/daikinone/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def schema(self):
return vol.Schema({vol.Required(CONF_EMAIL): str, vol.Required(CONF_PASSWORD): str})

async def async_step_user(self, user_input: dict[str, Any] | None = None):
errors = {}
errors: dict[str, str] = {}

if user_input is not None:
email = user_input[CONF_EMAIL]
Expand Down
2 changes: 1 addition & 1 deletion custom_components/daikinone/daikinone.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ async def set_thermostat_home_set_points(
if not heat and not cool:
raise ValueError("At least one of heat or cool set points must be set")

payload = {}
payload: dict[str, Any] = {}
if heat:
payload["hspHome"] = heat.celsius
if cool:
Expand Down
22 changes: 6 additions & 16 deletions custom_components/daikinone/sensor.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import logging
from typing import Callable, TypeVar, Generic
from typing import Callable, Generic, TypeVar

from homeassistant.components.sensor import SensorEntity, SensorEntityDescription, SensorDeviceClass, SensorStateClass
from homeassistant.config_entries import ConfigEntry
Expand Down Expand Up @@ -547,8 +547,6 @@ async def async_setup_entry(


class DaikinOneSensor(SensorEntity, Generic[D]):
_state: StateType = None

def __init__(
self, description: SensorEntityDescription, data: DaikinOneData, device: D, attribute: Callable[[D], StateType]
) -> None:
Expand All @@ -559,13 +557,10 @@ def __init__(
self._device: D = device
self._attribute = attribute

@property
def unique_id(self):
"""Return a unique identifier for this sensor."""
return f"{self._device.id}-{self.name}"
self._attr_unique_id = f"{self._device.id}-{self.name}"
self._attr_device_info = self.get_device_info()

@property
def device_info(self) -> DeviceInfo | None:
def get_device_info(self) -> DeviceInfo | None:
"""Return device information for this sensor."""

info = DeviceInfo(
Expand All @@ -591,11 +586,6 @@ def device_parent(self) -> str | None:
"""Return the name of the device."""
return None

@property
def native_value(self):
"""Return the state of the sensor."""
return self._state


class DaikinOneThermostatSensor(DaikinOneSensor[DaikinThermostat]):
def __init__(
Expand All @@ -615,7 +605,7 @@ async def async_update(self) -> None:
"""Get the latest state of the sensor."""
await self._data.update()
self._device = self._data.daikin.get_thermostat(self._device.id)
self._state = self._attribute(self._device)
self._attr_native_value = self._attribute(self._device)


E = TypeVar("E", bound=DaikinEquipment)
Expand All @@ -637,4 +627,4 @@ async def async_update(self) -> None:
await self._data.update()
thermostat = self._data.daikin.get_thermostat(self._device.thermostat_id)
self._device = thermostat.equipment[self._device.id]
self._state = self._attribute(self._device)
self._attr_native_value = self._attribute(self._device)
18 changes: 7 additions & 11 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
[project]
name = "ha-daikinone"
version = "0.1.0"
description = "Add a short description here"
description = "Daikin One+ integration for Home Assistant"
authors = [
{ name = "Zach Langbert", email = "[email protected]" }
]
dependencies = ["homeassistant~=2023.6.1", "pydantic~=1.10.8", "backoff~=2.2.1"]
dependencies = ["homeassistant~=2024.1.0", "pydantic~=1.10.12", "backoff~=2.2.1"]
readme = "README.md"
requires-python = ">= 3.11"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
requires-python = ">= 3.12"

[tool.rye]
managed = true
dev-dependencies = [
"setuptools~=67.8.0",
"ruff~=0.0.270",
"pyright~=1.1.314",
"black[d]~=23.3.0",
"setuptools>=69.0.3",
"ruff>=0.1.13",
"pyright>=1.1.347",
"black[d]>=23.12.1",
]

[tool.rye.scripts]
Expand Down
Loading