Skip to content
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

Versions from 0.40 and up

## Ongoing

- Fix mypy errors in Core

## v0.57.6

- Fix issue #897, partly via plugwise [v1.7.8](https://github.com/plugwise/python-plugwise/releases/tag/v1.7.8)
Expand Down
4 changes: 2 additions & 2 deletions custom_components/plugwise/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def __init__(
self._notification: dict[str, str] = {} # pw-beta

@property
def is_on(self) -> bool:
def is_on(self) -> bool | None:
"""Return true if the binary sensor is on."""
# pw-beta: show Plugwise notifications as HA persistent notifications
if self._notification:
Expand All @@ -174,7 +174,7 @@ def is_on(self) -> bool:
self.hass, message, "Plugwise Notification:", f"{DOMAIN}.{notify_id}"
)

return self.device[BINARY_SENSORS][self.entity_description.key]
return self.device.get(BINARY_SENSORS, {}).get(self.entity_description.key)

@property
def extra_state_attributes(self) -> Mapping[str, Any] | None:
Expand Down
57 changes: 29 additions & 28 deletions custom_components/plugwise/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .const import (
Expand Down Expand Up @@ -108,7 +107,7 @@ class PlugwiseClimateEntity(PlugwiseEntity, ClimateEntity):
_enable_turn_on_off_backwards_compatibility = False

_previous_mode: str = HVACAction.HEATING # Upstream
_homekit_mode: str | None = None # pw-beta homekit emulation + intentional unsort
_homekit_mode: HVACMode | None = None # pw-beta homekit emulation + intentional unsort

def __init__(
self,
Expand All @@ -127,11 +126,11 @@ def __init__(
if (location := self.device.get(LOCATION)) is not None:
self._location = location

self._attr_max_temp = min(self.device[THERMOSTAT][UPPER_BOUND], 35.0)
self._attr_min_temp = self.device[THERMOSTAT][LOWER_BOUND]
self._attr_max_temp = min(self.device.get(THERMOSTAT, {}).get(UPPER_BOUND, 35.0), 35.0)
self._attr_min_temp = self.device.get(THERMOSTAT, {}).get(LOWER_BOUND, 0.0)
# Ensure we don't drop below 0.1
self._attr_target_temperature_step = max(
self.device[THERMOSTAT][RESOLUTION], 0.1
self.device.get(THERMOSTAT, {}).get(RESOLUTION, 0.5), 0.1
)
self._attr_unique_id = f"{device_id}-climate"

Expand All @@ -148,7 +147,7 @@ def __init__(
self._attr_supported_features |= (
ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
)
if presets := self.device["preset_modes"]: # can be NONE
if presets := self.device.get("preset_modes", None): # can be NONE
self._attr_supported_features |= ClimateEntityFeature.PRESET_MODE
self._attr_preset_modes = presets

Expand All @@ -167,47 +166,51 @@ def _previous_action_mode(self, coordinator: PlugwiseDataUpdateCoordinator) -> N
self._previous_mode = mode

@property
def current_temperature(self) -> float:
def current_temperature(self) -> float | None:
"""Return the current temperature."""
return self.device[SENSORS][ATTR_TEMPERATURE]
return self.device.get(SENSORS, {}).get(ATTR_TEMPERATURE)

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

Connected to the HVACMode combination of AUTO-HEAT.
"""

return self.device[THERMOSTAT][TARGET_TEMP]
return self.device.get(THERMOSTAT, {}).get(TARGET_TEMP)

@property
def target_temperature_high(self) -> float:
def target_temperature_high(self) -> float | None:
"""Return the temperature we try to reach in case of cooling.

Connected to the HVACMode combination of AUTO-HEAT_COOL.
"""
return self.device[THERMOSTAT][TARGET_TEMP_HIGH]
return self.device.get(THERMOSTAT, {}).get(TARGET_TEMP_HIGH)

@property
def target_temperature_low(self) -> float:
def target_temperature_low(self) -> float | None:
"""Return the heating temperature we try to reach in case of heating.

Connected to the HVACMode combination AUTO-HEAT_COOL.
"""
return self.device[THERMOSTAT][TARGET_TEMP_LOW]
return self.device.get(THERMOSTAT, {}).get(TARGET_TEMP_LOW)

@property
def hvac_mode(self) -> HVACMode:
"""Return HVAC operation ie. auto, cool, heat, heat_cool, or off mode."""
if (
mode := self.device[CLIMATE_MODE]
) is None or mode not in self.hvac_modes: # pw-beta add to Core
mode = self.device.get(CLIMATE_MODE)
if mode is None:
return HVACMode.HEAT # pragma: no cover
try:
hvac = HVACMode(mode)
except ValueError:
return HVACMode.HEAT # pragma: no cover
if hvac not in self.hvac_modes:
return HVACMode.HEAT # pragma: no cover
# pw-beta homekit emulation
if self._homekit_enabled and self._homekit_mode == HVACMode.OFF:
mode = HVACMode.OFF # pragma: no cover

return HVACMode(mode)
return HVACMode.OFF # pragma: no cover
return hvac

@property
def hvac_modes(self) -> list[HVACMode]:
Expand All @@ -219,14 +222,15 @@ def hvac_modes(self) -> list[HVACMode]:
):
hvac_modes.append(HVACMode.OFF)

if self.device.get(AVAILABLE_SCHEDULES):
if self.device.get(AVAILABLE_SCHEDULES, []):
hvac_modes.append(HVACMode.AUTO)

if self.coordinator.api.cooling_present:
if REGULATION_MODES in self._gateway_data:
if self._gateway_data[SELECT_REGULATION_MODE] == HVACAction.COOLING:
selected = self._gateway_data.get(SELECT_REGULATION_MODE)
if selected == HVACAction.COOLING:
hvac_modes.append(HVACMode.COOL)
if self._gateway_data[SELECT_REGULATION_MODE] == HVACAction.HEATING:
if selected == HVACAction.HEATING:
hvac_modes.append(HVACMode.HEAT)
else:
hvac_modes.append(HVACMode.HEAT_COOL)
Expand All @@ -249,7 +253,7 @@ def hvac_action(self) -> HVACAction: # pw-beta add to Core
@property
def preset_mode(self) -> str | None:
"""Return the current preset mode."""
return self.device[ACTIVE_PRESET]
return self.device.get(ACTIVE_PRESET)

@plugwise_command
async def async_set_temperature(self, **kwargs: Any) -> None:
Expand All @@ -272,9 +276,6 @@ async def async_set_temperature(self, **kwargs: Any) -> None:
@plugwise_command
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the hvac mode."""
if hvac_mode not in self.hvac_modes:
raise HomeAssistantError("Unsupported hvac_mode")

if hvac_mode == self.hvac_mode:
return

Expand All @@ -297,7 +298,7 @@ async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
await self.async_set_preset_mode(PRESET_AWAY) # pragma: no cover
if (
self._homekit_mode in [HVACMode.HEAT, HVACMode.HEAT_COOL]
and self.device[ACTIVE_PRESET] == PRESET_AWAY
and self.device.get(ACTIVE_PRESET) == PRESET_AWAY
): # pragma: no cover
await self.async_set_preset_mode(PRESET_HOME) # pragma: no cover

Expand Down
2 changes: 1 addition & 1 deletion custom_components/plugwise/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def available(self) -> bool:
# Upstream: Do not change the AVAILABLE line below: some Plugwise devices and zones
# Upstream: do not provide their availability-status!
self._dev_id in self.coordinator.data
and (AVAILABLE not in self.device or self.device[AVAILABLE] is True)
and (self.device.get(AVAILABLE, True) is True)
and super().available
)

Expand Down
12 changes: 6 additions & 6 deletions custom_components/plugwise/number.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,23 +121,23 @@ def __init__(
) -> None:
"""Initiate Plugwise Number."""
super().__init__(coordinator, device_id)
self.actuator = self.device[description.key] # Upstream
self.device_id = device_id
self.entity_description = description
self._attr_unique_id = f"{device_id}-{description.key}"
self._attr_mode = NumberMode.BOX
self._attr_native_max_value = self.device[description.key][UPPER_BOUND] # Upstream const
self._attr_native_min_value = self.device[description.key][LOWER_BOUND] # Upstream const
ctrl = self.device.get(description.key, {})
self._attr_native_max_value = ctrl.get(UPPER_BOUND, 100.0) # Upstream const
self._attr_native_min_value = ctrl.get(LOWER_BOUND, 0.0) # Upstream const

native_step = self.device[description.key][RESOLUTION] # Upstream const
native_step = ctrl.get(RESOLUTION, 0.5) # Upstream const
if description.key != TEMPERATURE_OFFSET: # Upstream const
native_step = max(native_step, 0.5)
self._attr_native_step = native_step

@property
def native_value(self) -> float:
def native_value(self) -> float | None:
"""Return the present setpoint value."""
return self.device[self.entity_description.key]["setpoint"]
return self.device.get(self.entity_description.key, {}).get("setpoint")

@plugwise_command
async def async_set_native_value(self, value: float) -> None:
Expand Down
6 changes: 3 additions & 3 deletions custom_components/plugwise/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,14 @@ def __init__(
self._location = location

@property
def current_option(self) -> str| None:
def current_option(self) -> str | None:
"""Return the selected entity option to represent the entity state."""
return self.device[self.entity_description.key]
return self.device.get(self.entity_description.key)

@property
def options(self) -> list[str]:
"""Return the available select-options."""
return self.device[self.entity_description.options_key]
return self.device.get(self.entity_description.options_key, [])

@plugwise_command
async def async_select_option(self, option: str) -> None:
Expand Down
4 changes: 2 additions & 2 deletions custom_components/plugwise/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,6 @@ def __init__(
self._attr_unique_id = f"{device_id}-{description.key}"

@property
def native_value(self) -> int | float:
def native_value(self) -> int | float | None:
"""Return the value reported by the sensor."""
return self.device[SENSORS][self.entity_description.key] # Upstream consts
return self.device.get(SENSORS, {}).get(self.entity_description.key) # Upstream consts
4 changes: 2 additions & 2 deletions custom_components/plugwise/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,9 @@ def __init__(
self._attr_unique_id = f"{device_id}-{description.key}"

@property
def is_on(self) -> bool:
def is_on(self) -> bool | None:
"""Return True if entity is on."""
return self.device[SWITCHES][self.entity_description.key] # Upstream const
return self.device.get(SWITCHES, {}).get(self.entity_description.key) # Upstream const

@plugwise_command
async def async_turn_on(self, **kwargs: Any) -> None:
Expand Down
Loading