Skip to content
Closed
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 homeassistant/components/actron_air/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
ActronAirSystemCoordinator,
)

PLATFORMS = [Platform.CLIMATE, Platform.SWITCH]
PLATFORMS = [Platform.CLIMATE, Platform.SENSOR, Platform.SWITCH]


async def async_setup_entry(hass: HomeAssistant, entry: ActronAirConfigEntry) -> bool:
Expand Down
28 changes: 6 additions & 22 deletions homeassistant/components/actron_air/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import DOMAIN
from .coordinator import ActronAirConfigEntry, ActronAirSystemCoordinator
from .entity import ActronAirEntity

PARALLEL_UPDATES = 0

Expand Down Expand Up @@ -68,32 +68,16 @@ async def async_setup_entry(
async_add_entities(entities)


class BaseClimateEntity(CoordinatorEntity[ActronAirSystemCoordinator], ClimateEntity):
class ActronAirClimateEntity(ActronAirEntity, ClimateEntity):
"""Base class for Actron Air climate entities."""

_attr_has_entity_name = True
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.FAN_MODE
| ClimateEntityFeature.TURN_ON
| ClimateEntityFeature.TURN_OFF
)
_attr_name = None
_attr_fan_modes = list(FAN_MODE_MAPPING_ACTRONAIR_TO_HA.values())
_attr_hvac_modes = list(HVAC_MODE_MAPPING_ACTRONAIR_TO_HA.values())

def __init__(
self,
coordinator: ActronAirSystemCoordinator,
name: str,
) -> None:
"""Initialize an Actron Air unit."""
super().__init__(coordinator)
self._serial_number = coordinator.serial_number


class ActronSystemClimate(BaseClimateEntity):
class ActronSystemClimate(ActronAirClimateEntity):
"""Representation of the Actron Air system."""

_attr_supported_features = (
Expand All @@ -109,7 +93,7 @@ def __init__(
name: str,
) -> None:
"""Initialize an Actron Air unit."""
super().__init__(coordinator, name)
super().__init__(coordinator)
serial_number = coordinator.serial_number
self._attr_unique_id = serial_number
self._attr_device_info = DeviceInfo(
Expand Down Expand Up @@ -182,7 +166,7 @@ async def async_set_temperature(self, **kwargs: Any) -> None:
await self._status.user_aircon_settings.set_temperature(temperature=temp)


class ActronZoneClimate(BaseClimateEntity):
class ActronZoneClimate(ActronAirClimateEntity):
"""Representation of a zone within the Actron Air system."""

_attr_supported_features = (
Expand All @@ -197,7 +181,7 @@ def __init__(
zone: ActronAirZone,
) -> None:
"""Initialize an Actron Air unit."""
super().__init__(coordinator, zone.title)
super().__init__(coordinator)
serial_number = coordinator.serial_number
self._zone_id: int = zone.zone_id
self._attr_unique_id: str = f"{serial_number}_zone_{zone.zone_id}"
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/actron_air/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class ActronAirRuntimeData:
type ActronAirConfigEntry = ConfigEntry[ActronAirRuntimeData]


class ActronAirSystemCoordinator(DataUpdateCoordinator[ActronAirACSystem]):
class ActronAirSystemCoordinator(DataUpdateCoordinator[ActronAirStatus]):
"""System coordinator for Actron Air integration."""

def __init__(
Expand Down
32 changes: 32 additions & 0 deletions homeassistant/components/actron_air/entity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""Base entity classes for Actron Air integration."""

from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import DOMAIN
from .coordinator import ActronAirSystemCoordinator


class ActronAirEntity(CoordinatorEntity[ActronAirSystemCoordinator]):
"""Base class for Actron Air entities."""

_attr_has_entity_name = True

def __init__(self, coordinator: ActronAirSystemCoordinator) -> None:
"""Initialize the entity."""
super().__init__(coordinator)
self._serial_number = coordinator.serial_number

self._attr_device_info: DeviceInfo = DeviceInfo(
identifiers={(DOMAIN, self._serial_number)},
name=coordinator.data.ac_system.system_name,
manufacturer="Actron Air",
model_id=coordinator.data.ac_system.master_wc_model,
sw_version=coordinator.data.ac_system.master_wc_firmware_version,
serial_number=self._serial_number,
)

@property
def available(self) -> bool:
"""Return True if entity is available."""
return not self.coordinator.is_device_stale()
20 changes: 8 additions & 12 deletions homeassistant/components/actron_air/quality_scale.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,15 @@ rules:
docs-troubleshooting: done
docs-use-cases: done
dynamic-devices: todo
entity-category:
entity-category: done
entity-device-class: done
entity-disabled-by-default: done
entity-translations: done
exception-translations: done
icon-translations: done
reconfiguration-flow:
status: exempt
comment: This integration does not use entity categories.
entity-device-class:
status: exempt
comment: This integration does not use entity device classes.
entity-disabled-by-default:
status: exempt
comment: Not required for this integration at this stage.
entity-translations: todo
exception-translations: todo
icon-translations: todo
reconfiguration-flow: todo
comment: This integration does not have settings in the configuration flow.
repair-issues:
status: exempt
comment: This integration does not have any known issues that require repair.
Expand Down
206 changes: 206 additions & 0 deletions homeassistant/components/actron_air/sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
"""Sensor platform for Actron Air integration."""

from collections.abc import Callable
from dataclasses import dataclass

from actron_neo_api import ActronAirPeripheral, ActronAirStatus

from homeassistant.components.sensor import (
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
SensorStateClass,
)
from homeassistant.const import (
PERCENTAGE,
REVOLUTIONS_PER_MINUTE,
EntityCategory,
UnitOfPower,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .const import DOMAIN
from .coordinator import ActronAirConfigEntry, ActronAirSystemCoordinator
from .entity import ActronAirEntity

PARALLEL_UPDATES = 0


@dataclass(frozen=True, kw_only=True)
class ActronAirSensorEntityDescription(SensorEntityDescription):
"""Describes Actron Air sensor entity."""

value_fn: Callable[[ActronAirStatus], str | int | float | bool | None] | None = None


@dataclass(frozen=True, kw_only=True)
class ActronAirPeripheralSensorEntityDescription(SensorEntityDescription):
"""Describes Actron Air peripheral sensor entity."""

value_fn: Callable[[ActronAirPeripheral], str | int | float | None] | None = None


AC_SENSORS: tuple[ActronAirSensorEntityDescription, ...] = (
ActronAirSensorEntityDescription(
key="compressor_chasing_temperature",
translation_key="compressor_chasing_temperature",
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
value_fn=lambda status: status.compressor_chasing_temperature,
),
ActronAirSensorEntityDescription(
key="compressor_live_temperature",
translation_key="compressor_live_temperature",
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
value_fn=lambda status: status.compressor_live_temperature,
),
ActronAirSensorEntityDescription(
key="compressor_speed",
translation_key="compressor_speed",
native_unit_of_measurement=REVOLUTIONS_PER_MINUTE,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
value_fn=lambda status: status.compressor_speed,
),
ActronAirSensorEntityDescription(
key="compressor_power",
translation_key="compressor_power",
device_class=SensorDeviceClass.POWER,
native_unit_of_measurement=UnitOfPower.WATT,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
value_fn=lambda status: status.compressor_power,
),
ActronAirSensorEntityDescription(
key="outdoor_temperature",
translation_key="outdoor_temperature",
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
state_class=SensorStateClass.MEASUREMENT,
entity_registry_enabled_default=False,
value_fn=lambda status: status.outdoor_temperature,
),
)

PERIPHERAL_SENSORS: tuple[ActronAirPeripheralSensorEntityDescription, ...] = (
ActronAirPeripheralSensorEntityDescription(
key="battery",
device_class=SensorDeviceClass.BATTERY,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda peripheral: peripheral.battery_level,
),
ActronAirPeripheralSensorEntityDescription(
key="humidity",
device_class=SensorDeviceClass.HUMIDITY,
native_unit_of_measurement=PERCENTAGE,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda peripheral: peripheral.humidity,
),
ActronAirPeripheralSensorEntityDescription(
key="temperature",
device_class=SensorDeviceClass.TEMPERATURE,
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
state_class=SensorStateClass.MEASUREMENT,
value_fn=lambda peripheral: peripheral.temperature,
),
)


async def async_setup_entry(
hass: HomeAssistant,
entry: ActronAirConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up Actron Air sensor platform entities."""
system_coordinators = entry.runtime_data.system_coordinators
entities: list[SensorEntity] = []

for coordinator in system_coordinators.values():
status = coordinator.data

# Add AC system sensors
entities.extend(
ActronAirSensor(coordinator, description) for description in AC_SENSORS
)

# Add peripheral sensors
for peripheral in status.peripherals:
entities.extend(
ActronAirPeripheralSensorEntity(coordinator, peripheral, description)
for description in PERIPHERAL_SENSORS
)

# Register all sensors
async_add_entities(entities)


class ActronAirSensor(ActronAirEntity, SensorEntity):
"""Representation of an Actron Air sensor."""

entity_description: ActronAirSensorEntityDescription
_attr_entity_category: EntityCategory | None = EntityCategory.DIAGNOSTIC

def __init__(
self,
coordinator: ActronAirSystemCoordinator,
description: ActronAirSensorEntityDescription,
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator)
self.entity_description = description
self._attr_unique_id = f"{self._serial_number}_{description.key}"

@property
def native_value(self) -> str | int | float | bool | None:
"""Return the state of the sensor."""
if self.entity_description.value_fn is None:
return None
return self.entity_description.value_fn(self.coordinator.data)


class ActronAirPeripheralSensorEntity(ActronAirEntity, SensorEntity):
"""Representation of an Actron Air peripheral sensor."""

entity_description: ActronAirPeripheralSensorEntityDescription
_attr_entity_category = None

def __init__(
self,
coordinator: ActronAirSystemCoordinator,
peripheral: ActronAirPeripheral,
description: ActronAirPeripheralSensorEntityDescription,
) -> None:
"""Initialize the sensor."""
super().__init__(coordinator)

self._peripheral = peripheral
zone = peripheral.zones[0]
suggested_area = zone.title
zone_entity_id = f"{self._serial_number}_zone_{zone.zone_id}"

self.entity_description = description
self._attr_unique_id = f"{peripheral.serial_number}_{description.key}"
self._attr_device_info: DeviceInfo = DeviceInfo(
identifiers={(DOMAIN, peripheral.serial_number)},
name=f"{peripheral.device_type} {peripheral.logical_address}",
model=peripheral.device_type,
suggested_area=suggested_area,
via_device=(DOMAIN, zone_entity_id),
serial_number=peripheral.serial_number,
)

@property
def native_value(self) -> str | int | float | None:
"""Return the state of the sensor."""
if self.entity_description.value_fn is None:
return None
return self.entity_description.value_fn(self._peripheral)
20 changes: 20 additions & 0 deletions homeassistant/components/actron_air/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,26 @@
}
},
"entity": {
"sensor": {
"compressor_chasing_temperature": {
"name": "Compressor chasing temperature"
},
"compressor_coil_inlet_temperature": {
"name": "Compressor coil inlet temperature"
},
"compressor_live_temperature": {
"name": "Compressor live temperature"
},
"compressor_power": {
"name": "Compressor power"
},
"compressor_speed": {
"name": "Compressor speed"
},
"outdoor_temperature": {
"name": "Outdoor temperature"
}
},
"switch": {
"away_mode": {
"name": "Away mode"
Expand Down
Loading
Loading