Skip to content

Commit

Permalink
Merge pull request #64 from bj00rn/feat/binary-sensor
Browse files Browse the repository at this point in the history
feat: use binary sensors where applicable
  • Loading branch information
bj00rn authored Dec 3, 2024
2 parents ceae2b3 + 388e26f commit ef250f9
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 90 deletions.
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

0 comments on commit ef250f9

Please sign in to comment.