Skip to content

Commit

Permalink
Merge pull request #207 from ocrease/AwayMode
Browse files Browse the repository at this point in the history
Improved user experience of Away mode
  • Loading branch information
MindrustUK authored Nov 15, 2024
2 parents d4a73a8 + 2e7ee4e commit 53067e2
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 88 deletions.
16 changes: 5 additions & 11 deletions custom_components/heatmiserneo/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ async def async_setup_entry(
class HeatmiserNeoClimateEntityDescription(
HeatmiserNeoEntityDescription, ClimateEntityDescription
):
"""Describes a button entity."""
"""Describes a Climate entity."""


CLIMATE: tuple[HeatmiserNeoClimateEntityDescription, ...] = (
Expand Down Expand Up @@ -484,15 +484,10 @@ async def async_set_preset_mode(self, preset_mode: str) -> None:
"""Set preset mode."""
device = self.data
if preset_mode == PRESET_AWAY:
await self._hub.set_away(True)
device.away = True
elif device.away:
await self._hub.set_away(False)
device.away = False

if device.holiday:
await self._hub.cancel_holiday()
device.holiday = False
await self.async_set_away_mode()
elif device.away or device.holiday:
await self.async_cancel_away_or_holiday()

hold_temp = float(device.target_temperature)
hold_duration = 0
hold_on = False
Expand All @@ -509,4 +504,3 @@ async def async_set_preset_mode(self, preset_mode: str) -> None:
device.hold_time = timedelta(minutes=hold_duration)

self.coordinator.async_update_listeners()
await self.coordinator.async_request_refresh()
1 change: 1 addition & 0 deletions custom_components/heatmiserneo/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ class ModeSelectOption(str, enum.Enum):
STANDBY = "standby"
MANUAL_ON = "on"
MANUAL_OFF = "off"
AWAY = "away"


HEATMISER_TEMPERATURE_UNIT_HA_UNIT = {
Expand Down
12 changes: 11 additions & 1 deletion custom_components/heatmiserneo/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
"""Coordinator object for the HeatmiserNeo integration."""

import asyncio
from collections.abc import Callable
from datetime import timedelta
import logging

from neohubapi.neohub import NeoHub
from neohubapi.neohub import NeoHub, NeoStat

from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
Expand Down Expand Up @@ -73,3 +74,12 @@ def _get_device_sn(self, device_id: int) -> str:
return self._device_serial_numbers.get(device_id, {}).get(
"serial_number", "UNKNOWN"
)

def update_in_memory_state(
self, action: Callable[[NeoStat], None], filter: Callable[[NeoStat], bool]
) -> None:
"""Call action on devices matching filter."""
devices, _ = self.data
for device in devices.values():
if filter(device):
action(device)
40 changes: 37 additions & 3 deletions custom_components/heatmiserneo/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from collections.abc import Awaitable, Callable
from dataclasses import dataclass
from functools import partial
import logging
from typing import Any

Expand All @@ -15,8 +16,9 @@
from homeassistant.helpers.entity import EntityDescription
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import DOMAIN, HEATMISER_PRODUCT_LIST
from .const import DOMAIN, HEATMISER_PRODUCT_LIST, HEATMISER_TYPE_IDS_AWAY
from .coordinator import HeatmiserNeoCoordinator
from .helpers import cancel_holiday, set_away

_LOGGER = logging.getLogger(__name__)

Expand All @@ -30,7 +32,8 @@ class HeatmiserNeoEntityDescription(EntityDescription):
icon_fn: Callable[[NeoStat], str | None] | None = None
# extra_attrs: list[str] | None = None
custom_functions: (
dict[str, Callable[[NeoStat, NeoHub, ServiceCall], Awaitable[None]]] | None
dict[str, Callable[[type[HeatmiserNeoEntity], ServiceCall], Awaitable[None]]]
| None
) = None


Expand Down Expand Up @@ -135,12 +138,43 @@ async def call_custom_action(self, service_call: ServiceCall) -> None:
)
return
await self.entity_description.custom_functions.get(service_call.service)(
self.data, self._hub, service_call
self, service_call
)

async def async_cancel_away_or_holiday(self) -> None:
"""Cancel away/holiday mode."""
if _device_supports_away(self.data):
dev = self.data
if dev.away:
await self._hub.set_away(False)
self.coordinator.update_in_memory_state(
partial(set_away, False),
_device_supports_away,
)
if dev.holiday:
await self._hub.cancel_holiday(False)
self.coordinator.update_in_memory_state(
cancel_holiday,
_device_supports_away,
)

async def async_set_away_mode(self) -> None:
"""Set away mode."""
if _device_supports_away(self.data):
dev = self.data
if not (dev.away or dev.holiday):
await self._hub.set_away(True)
self.coordinator.update_in_memory_state(
partial(set_away, True), _device_supports_away
)


async def call_custom_action(
entity: HeatmiserNeoEntity, service_call: ServiceCall
) -> None:
"""Call a custom action specified in the entity description."""
await entity.call_custom_action(service_call)


def _device_supports_away(dev: NeoStat) -> bool:
return dev.device_type in HEATMISER_TYPE_IDS_AWAY
16 changes: 16 additions & 0 deletions custom_components/heatmiserneo/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-only
"""Constants used by multiple Heatmiser Neo modules."""

from neohubapi.neohub import NeoStat


def set_away(state: bool, dev: NeoStat) -> None:
"""Set away flag on device."""
dev.away = state
if state:
dev.target_temperature = dev._data_.FROST_TEMP


def cancel_holiday(dev: NeoStat) -> None:
"""Cancel holiday on device."""
dev.holiday = False
Loading

0 comments on commit 53067e2

Please sign in to comment.