-
-
Notifications
You must be signed in to change notification settings - Fork 36.2k
Actron air add sensor entity type #158154
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
base: dev
Are you sure you want to change the base?
Changes from all commits
95ad48e
fb71911
5a5d3fb
d4fcdbe
93f41d9
2f36d64
14f6803
7f098c3
df741be
63ce89f
df1cc6d
33d30c4
8a1488c
bc06cd6
1a95660
2b8c986
e460c6e
7415691
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| """Sensor platform for Actron Air integration.""" | ||
|
|
||
| from actron_neo_api import ActronAirPeripheral | ||
|
|
||
| from homeassistant.const import EntityCategory | ||
| from homeassistant.helpers.device_registry import DeviceInfo | ||
| from homeassistant.helpers.update_coordinator import CoordinatorEntity | ||
|
|
||
| from .const import DOMAIN | ||
| from .coordinator import ActronAirSystemCoordinator | ||
|
|
||
|
|
||
| class ActronAirAcSensor(CoordinatorEntity[ActronAirSystemCoordinator]): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this only a base class for the sensor and why don't we create a base for all other entity types? |
||
| """Base class for Actron Air sensors.""" | ||
|
|
||
| _attr_entity_category: EntityCategory | None = EntityCategory.DIAGNOSTIC | ||
| _attr_has_entity_name = True | ||
|
|
||
| def __init__(self, coordinator: ActronAirSystemCoordinator) -> None: | ||
| """Initialize the sensor.""" | ||
| super().__init__(coordinator) | ||
| self._status = self.coordinator.data | ||
| self._serial_number = coordinator.serial_number | ||
|
|
||
| self._attr_device_info: DeviceInfo = DeviceInfo( | ||
| identifiers={(DOMAIN, self._serial_number)}, | ||
| name=self._status.ac_system.system_name, | ||
| manufacturer="Actron Air", | ||
| model_id=self._status.ac_system.master_wc_model, | ||
| sw_version=self._status.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() | ||
|
|
||
|
|
||
| class ActronAirPeripheralSensor(ActronAirAcSensor): | ||
| """Base class for Actron Air peripheral sensors.""" | ||
|
|
||
| _attr_entity_category = None | ||
|
|
||
| def __init__( | ||
| self, coordinator: ActronAirSystemCoordinator, peripheral: ActronAirPeripheral | ||
| ) -> None: | ||
| """Initialize the sensor.""" | ||
| super().__init__(coordinator) | ||
| self._ac_serial = coordinator.serial_number | ||
| self._peripheral = peripheral | ||
| self._serial_number = peripheral.serial_number | ||
|
|
||
| suggested_area = None | ||
| if hasattr(peripheral, "zones") and len(peripheral.zones) == 1: | ||
| zone = peripheral.zones[0] | ||
| if hasattr(zone, "title") and zone.title: | ||
|
Comment on lines
+55
to
+57
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why would we use hasattr here? |
||
| suggested_area = zone.title | ||
|
|
||
| self._attr_device_info: DeviceInfo = DeviceInfo( | ||
| identifiers={(DOMAIN, self._serial_number)}, | ||
| name=f"{peripheral.device_type} {peripheral.logical_address}", | ||
| model=peripheral.device_type, | ||
| suggested_area=suggested_area, | ||
| via_device=(DOMAIN, self._ac_serial), | ||
| ) | ||
|
|
||
| @property | ||
| def available(self) -> bool: | ||
| """Return True if entity is available.""" | ||
| return not self.coordinator.is_device_stale() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,7 +26,9 @@ rules: | |
| unique-config-entry: done | ||
|
|
||
| # Silver | ||
| action-exceptions: todo | ||
| action-exceptions: | ||
| status: exempt | ||
| comment: This integration does not have custom service actions. | ||
|
Comment on lines
+29
to
+31
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No you do have climate and switch actions, which are also considered actions |
||
| config-entry-unloading: done | ||
| docs-configuration-parameters: | ||
| status: exempt | ||
|
|
@@ -54,19 +56,15 @@ rules: | |
| docs-troubleshooting: done | ||
| docs-use-cases: done | ||
| dynamic-devices: todo | ||
| entity-category: | ||
| 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: | ||
| 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: 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. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,185 @@ | ||
| """Sensor platform for Actron Air integration.""" | ||
|
|
||
| from dataclasses import dataclass | ||
|
|
||
| from actron_neo_api import ActronAirPeripheral | ||
|
|
||
| from homeassistant.components.sensor import ( | ||
| SensorDeviceClass, | ||
| SensorEntity, | ||
| SensorEntityDescription, | ||
| SensorStateClass, | ||
| ) | ||
| from homeassistant.const import PERCENTAGE, UnitOfPower, UnitOfTemperature | ||
| from homeassistant.core import HomeAssistant | ||
| from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback | ||
|
|
||
| from .coordinator import ActronAirConfigEntry, ActronAirSystemCoordinator | ||
| from .entity import ActronAirAcSensor, ActronAirPeripheralSensor | ||
|
|
||
| PARALLEL_UPDATES = 0 | ||
|
|
||
|
|
||
| @dataclass(frozen=True, kw_only=True) | ||
| class ActronAirSensorEntityDescription(SensorEntityDescription): | ||
| """Describes Actron Air sensor entity.""" | ||
|
|
||
| attribute_name: str | None = None | ||
|
|
||
|
|
||
| AC_SENSORS: tuple[ActronAirSensorEntityDescription, ...] = ( | ||
| ActronAirSensorEntityDescription( | ||
| key="clean_filter", | ||
| translation_key="clean_filter", | ||
| ), | ||
|
Comment on lines
+31
to
+34
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What can the state be? |
||
| ActronAirSensorEntityDescription( | ||
| key="defrost_mode", | ||
| translation_key="defrost_mode", | ||
| entity_registry_enabled_default=False, | ||
| ), | ||
|
Comment on lines
+35
to
+39
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What can the state be? |
||
| 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, | ||
| ), | ||
| 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, | ||
| ), | ||
| ActronAirSensorEntityDescription( | ||
| key="compressor_mode", | ||
| translation_key="compressor_mode", | ||
| entity_registry_enabled_default=False, | ||
| ), | ||
|
Comment on lines
+56
to
+60
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What can the state be? |
||
| ActronAirSensorEntityDescription( | ||
| key="compressor_speed", | ||
| translation_key="compressor_speed", | ||
| native_unit_of_measurement="RPM", | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe we have a constant for this unit of measurement |
||
| state_class=SensorStateClass.MEASUREMENT, | ||
| entity_registry_enabled_default=False, | ||
| ), | ||
| 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, | ||
| ), | ||
| 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, | ||
| ), | ||
| ) | ||
|
|
||
| PERIPHERAL_SENSORS: tuple[ActronAirSensorEntityDescription, ...] = ( | ||
| ActronAirSensorEntityDescription( | ||
| key="battery", | ||
| attribute_name="battery_level", | ||
| device_class=SensorDeviceClass.BATTERY, | ||
| native_unit_of_measurement=PERCENTAGE, | ||
| state_class=SensorStateClass.MEASUREMENT, | ||
| ), | ||
| ActronAirSensorEntityDescription( | ||
| key="humidity", | ||
| device_class=SensorDeviceClass.HUMIDITY, | ||
| native_unit_of_measurement=PERCENTAGE, | ||
| state_class=SensorStateClass.MEASUREMENT, | ||
| ), | ||
| ActronAirSensorEntityDescription( | ||
| key="temperature", | ||
| device_class=SensorDeviceClass.TEMPERATURE, | ||
| native_unit_of_measurement=UnitOfTemperature.CELSIUS, | ||
| state_class=SensorStateClass.MEASUREMENT, | ||
| ), | ||
| ) | ||
|
|
||
|
|
||
| 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(ActronAirAcSensor, SensorEntity): | ||
| """Representation of an Actron Air sensor.""" | ||
|
|
||
| entity_description: ActronAirSensorEntityDescription | ||
|
|
||
| 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.""" | ||
| return getattr( | ||
| self.coordinator.data, | ||
| self.entity_description.attribute_name or self.entity_description.key, | ||
| None, | ||
| ) | ||
|
Comment on lines
+155
to
+159
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of using |
||
|
|
||
|
|
||
| class ActronAirPeripheralSensorEntity(ActronAirPeripheralSensor, SensorEntity): | ||
| """Representation of an Actron Air peripheral sensor.""" | ||
|
|
||
| entity_description: ActronAirSensorEntityDescription | ||
|
|
||
| def __init__( | ||
| self, | ||
| coordinator: ActronAirSystemCoordinator, | ||
| peripheral: ActronAirPeripheral, | ||
| description: ActronAirSensorEntityDescription, | ||
| ) -> None: | ||
| """Initialize the sensor.""" | ||
| super().__init__(coordinator, peripheral) | ||
| self.entity_description = description | ||
| self._attr_unique_id = f"{peripheral.serial_number}_{description.key}" | ||
|
|
||
| @property | ||
| def native_value(self) -> str | int | float | None: | ||
| """Return the state of the sensor.""" | ||
| return getattr( | ||
| self._peripheral, | ||
| self.entity_description.attribute_name or self.entity_description.key, | ||
| None, | ||
| ) | ||
Uh oh!
There was an error while loading. Please reload this page.