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

feat: use binary sensors where applicable #64

Merged
merged 1 commit into from
Dec 3, 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
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,19 @@ Name | Description | Unit | State attributes
`fireplace_mode_minutes_left` | minutes left until fireplace mode expires | `min` |
`heat_exchanger_rotor_speed_percent` | rotor speed of heat exchanger | `%` |
`heat_exchanger_rotor_speed` | rotor speed of heat exchanger | `rpm` |
`heater_active` | auxillary heater active | boolean |
`heater_active` | auxillary heater active | `Running` \| `Not running` |
`heater_air_temperature` | air temperature at heater | `°C` |
`heater_power_percent` | auxillary heater power | `%` |
`product_number` | product number | `str` |
`supply_air_temperature` | supply air temperature | `°C` |
`supply_fan_speed` | fan speed | `%` |
`system_active` | status of the system | `On` \| `Off` \| `Reset` |
`system_active` | status of the system | `Running` \| `Not running`
`system_name` | control system name | `str` |
`system_version` | control system version | `str` |
`system_warning` | system warning | boolean | raw system error/warning codes
`system_warning` | system warning | `Problem` \| `No problem` | raw system error/warning codes
`target_temperature` | target air temperature | `°C` |
`temperature_mode` | current temperature mode setting | `str` |
`ventilation_mode` | current ventilation mode setting | `str` |
`temperature_mode` | current temperature mode setting | `Cool` \| `Normal` \| `Economy` |
`ventilation_mode` | current ventilation mode setting | `Normal` \| `Away` \| `Boost` |
`normal_temperature` | temperature setting for Normal mode | `°C` |
`economy_temperature` | temperature setting for Economy mode | `°C` |
`cool_temperature` | temperature setting for Cool mode | `°C` |
Expand All @@ -78,14 +78,14 @@ Name | Description
### Number
Name | Description
-- | --
`cool temperature` | Cool temperature installer setting
`economy temperature` | Economy temperature installer setting
`normal temperature` | Normal temperature installer setting
`cool_temperature` | Cool temperature installer setting
`economy_temperature` | Economy temperature installer setting
`normal_temperature` | Normal temperature installer setting

### Button
Name | Description
-- | --
`system reset` | Reset system warnings
`system_reset` | Reset system warnings

## Experimental features

Expand All @@ -99,7 +99,7 @@ Name | Description | Unit

Switch | Description
-- | --
`cooking_mode` | Turn `cooking` mode on/off. Emulates cooking mode when fireplace mode is active. When `cooking mode` is active, it automatically deactivates `fireplace mode` before its timer expires. This will reset rotary heat exchanger to normal operation as is desirable in warm weather.
`cooking_mode` | Turn `cooking` mode on/off. Emulates cooking mode when fireplace mode is active. When `cooking mode` is active, it automatically deactivates `fireplace_mode` before its timer expires. This will reset rotary heat exchanger to normal operation as is desirable in warm weather.

## Supported devices

Expand Down
168 changes: 168 additions & 0 deletions custom_components/saleryd_hrv/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
"""Binary sensor platform"""

from __future__ import annotations

from enum import IntEnum
from typing import TYPE_CHECKING, Any

from homeassistant.components.binary_sensor import (
BinarySensorDeviceClass,
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.components.sensor import SensorEntityDescription
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util import slugify
from pysaleryd.const import DataKeyEnum
from pysaleryd.utils import ErrorSystemProperty, SystemProperty
from pysaleryd.websocket import State

from .const import KEY_CLIENT_STATE, ModeEnum
from .entity import SalerydLokeEntity

if TYPE_CHECKING:
from .coordinator import SalerydLokeDataUpdateCoordinator
from .data import SalerydLokeConfigEntry


class SalerydLokeBinarySensor(SalerydLokeEntity, BinarySensorEntity):
"""Sensor base class."""

def __init__(
self,
coordinator: SalerydLokeDataUpdateCoordinator,
entry: "SalerydLokeConfigEntry",
entity_description: SensorEntityDescription,
state_when_on: IntEnum = ModeEnum.On,
) -> None:
"""Initialize the sensor."""
self.entity_id = (
f"binary_sensor.{entry.unique_id}_{slugify(entity_description.name)}"
)
self.state_when_on = state_when_on
super().__init__(coordinator, entry, entity_description)

def _is_on(self, system_property: SystemProperty):
return system_property.value

@property
def is_on(self):
system_property = SystemProperty.from_str(
self.entity_description.key,
self.coordinator.data.get(self.entity_description.key),
)
if system_property.value is None:
return

return system_property.value == self.state_when_on.value

def _get_extra_state_attributes(
self, system_property: SystemProperty
) -> dict[str, Any] | None:
return None

@property
def extra_state_attributes(self):
value = SystemProperty.from_str(
self.entity_description.key,
self.coordinator.data.get(self.entity_description.key),
)
return self._get_extra_state_attributes(value)


class SalerydLokeErrorMessageBinarySensor(SalerydLokeBinarySensor):

@property
def is_on(self):
error = ErrorSystemProperty(
self.entity_description.key,
self.coordinator.data.get(self.entity_description.key, None),
)
if error.value is None:
return

return any(error.value)

@property
def extra_state_attributes(self):
error = ErrorSystemProperty(
self.entity_description.key,
self.coordinator.data.get(self.entity_description.key, None),
)
if error.value is None:
return None

attrs = {}
for v in error.value:
attrs[v] = True

return attrs


class SalerydLokeConnectionStateBinarySensor(SalerydLokeBinarySensor):

def _get_extra_state_attributes(self, system_property: SystemProperty):
if system_property.value is None:
return None
return {system_property.value: True}


async def async_setup_entry(
hass: HomeAssistant,
entry: "SalerydLokeConfigEntry",
async_add_entities: AddEntitiesCallback,
):
"""Setup binary sensor platform."""
coordinator = entry.runtime_data.coordinator
sensors = [
# control_system_warning
SalerydLokeErrorMessageBinarySensor(
coordinator,
entry,
entity_description=BinarySensorEntityDescription(
key=DataKeyEnum.ERROR_MESSAGE,
icon="mdi:alert",
name="System warning",
device_class=BinarySensorDeviceClass.PROBLEM,
),
),
# control_system_active
SalerydLokeBinarySensor(
coordinator,
entry,
entity_description=BinarySensorEntityDescription(
key=DataKeyEnum.CONTROL_SYSTEM_STATE,
icon="mdi:power",
name="System active",
device_class=BinarySensorDeviceClass.RUNNING,
),
),
# heater_active
SalerydLokeBinarySensor(
coordinator,
entry,
entity_description=BinarySensorEntityDescription(
key=DataKeyEnum.MODE_HEATER,
icon="mdi:heating-coil",
name="Heater active",
device_class=BinarySensorDeviceClass.RUNNING,
),
),
# connection_state
SalerydLokeConnectionStateBinarySensor(
coordinator,
entry,
entity_description=BinarySensorEntityDescription(
key=KEY_CLIENT_STATE,
icon="mdi:wrench-clock",
name="Connection state",
device_class=BinarySensorDeviceClass.CONNECTIVITY,
entity_category=EntityCategory.DIAGNOSTIC,
),
state_when_on=State.RUNNING,
),
]

async_add_entities(sensors)
4 changes: 2 additions & 2 deletions custom_components/saleryd_hrv/bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ def update_data_callback(self, data):
self.logger.debug("Received data")
_data = data.copy()
self.__inject_virtual_keys(_data)
self.coordinator.async_set_updated_data(data)
self.coordinator.async_set_updated_data(_data)

def __inject_virtual_keys(self, data):
"""Inject additional keys for virtual sensors not present in the data set"""
data[KEY_CLIENT_STATE] = self.client.state.name
data[KEY_CLIENT_STATE] = self.client.state.value
data[KEY_TARGET_TEMPERATURE] = None

async def send_command(self, key: DataKeyEnum, data: str | int, auth: bool = False):
Expand Down
3 changes: 2 additions & 1 deletion custom_components/saleryd_hrv/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
SELECT = "select"
NUMBER = "number"
BUTTON = "button"
PLATFORMS = [SENSOR, SWITCH, SELECT, NUMBER, BUTTON]
BINARY_SENSOR = "binary_sensor"
PLATFORMS = [SENSOR, SWITCH, SELECT, NUMBER, BUTTON, BINARY_SENSOR]


# Configuration and options
Expand Down
81 changes: 4 additions & 77 deletions custom_components/saleryd_hrv/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from __future__ import annotations

from enum import IntEnum
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any

from homeassistant.components.sensor import (
SensorDeviceClass,
Expand Down Expand Up @@ -65,7 +65,9 @@ def native_value(self):
)
return self._get_native_value(value)

def _get_extra_state_attributes(self, system_property: SystemProperty):
def _get_extra_state_attributes(
self, system_property: SystemProperty
) -> dict[str, Any] | None:
return None

@property
Expand Down Expand Up @@ -105,35 +107,6 @@ def _get_native_value(self, system_property):
return None


class SalerydLokeErrorMessageSensor(SalerydLokeSensor):

@property
def native_value(self):
error = ErrorSystemProperty(
self.entity_description.key,
self.coordinator.data.get(self.entity_description.key, None),
)
if error.value is None:
return None

return any(error.value)

@property
def extra_state_attributes(self):
error = ErrorSystemProperty(
self.entity_description.key,
self.coordinator.data.get(self.entity_description.key, None),
)
if error.value is None:
return None

attrs = {}
for v in error.value:
attrs[v] = True

return attrs


class SalerydLokeTargetTemperatureSensor(SalerydLokeSensor):
"""Target temperature sensor"""

Expand Down Expand Up @@ -406,18 +379,6 @@ async def async_setup_entry(
entity_category=EntityCategory.DIAGNOSTIC,
),
),
# connection_state
SalerydLokeSensor(
coordinator,
entry,
entity_description=SensorEntityDescription(
key=KEY_CLIENT_STATE,
icon="mdi:wrench-clock",
name="Connection state",
device_class=SensorDeviceClass.ENUM,
entity_category=EntityCategory.DIAGNOSTIC,
),
),
# control_system_version
SalerydLokeSensor(
coordinator,
Expand All @@ -430,40 +391,6 @@ async def async_setup_entry(
entity_category=EntityCategory.DIAGNOSTIC,
),
),
# control_system_warning
SalerydLokeErrorMessageSensor(
coordinator,
entry,
entity_description=SensorEntityDescription(
key=DataKeyEnum.ERROR_MESSAGE,
icon="mdi:alert",
name="System warning",
device_class=SensorDeviceClass.ENUM,
),
),
# control_system_active
SalerydLokeEnumSensor(
coordinator,
entry,
entity_description=SensorEntityDescription(
key=DataKeyEnum.CONTROL_SYSTEM_STATE,
icon="mdi:power",
name="System active",
device_class=SensorDeviceClass.ENUM,
),
options_enum=SystemActiveModeEnum,
),
# heater_active
SalerydLokeEnumSensor(
coordinator,
entry,
entity_description=SensorEntityDescription(
key=DataKeyEnum.MODE_HEATER,
icon="mdi:heating-coil",
name="Heater active",
device_class=SensorDeviceClass.ENUM,
),
),
# normal mode target temperature
SalerydLokeSensor(
coordinator,
Expand Down
Loading