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

Add concentration level sensor to Aranet #137291

Draft
wants to merge 14 commits into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 12 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
27 changes: 25 additions & 2 deletions homeassistant/components/aranet/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from dataclasses import dataclass
from typing import Any

from aranet4.client import Aranet4Advertisement
from aranet4.client import Aranet4Advertisement, AranetType, Color
from bleak.backends.device import BLEDevice

from homeassistant.components.bluetooth.passive_update_processor import (
Expand Down Expand Up @@ -74,6 +74,11 @@ class AranetSensorEntityDescription(SensorEntityDescription):
native_unit_of_measurement=UnitOfPressure.HPA,
state_class=SensorStateClass.MEASUREMENT,
),
"status": AranetSensorEntityDescription(
key="concentration_level",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for all the other sensors here we use the device key as the sensor key and translation_key.
should we use status_level for this one too, to keep it consistent?

name="Concentration Level",
device_class=SensorDeviceClass.ENUM,
),
"co2": AranetSensorEntityDescription(
key="co2",
name="Carbon Dioxide",
Expand Down Expand Up @@ -161,7 +166,11 @@ def sensor_update_to_bluetooth_data_update(
val = getattr(adv.readings, key)
if val == -1:
continue
val *= desc.scale
val = (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of this, can we set the translation_key add add translations for the states?
Also, isn't the user expecting to see "Red/Yellow/Green", based on the official app and device screen?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I think we should represent it same as the app does, also for the translations (was actually waiting to have time to search for example for the translations later, thanks @abmantis)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I'll add translations for the states instead.

Also, isn't the user expecting to see "Red/Yellow/Green", based on the official app and device screen?

I'm not sure if this would necessarily be the expectation. See #137291 (comment).

Copy link
Author

@parkerbxyz parkerbxyz Feb 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if I did the translations correctly, and I haven't figured out how to test the translations programmatically. Could someone take a look at the latest changes and let me know if you have any further suggestions?

get_friendly_status(val.name, adv.readings.type)
if key == "status"
else val * desc.scale
)
data[tag] = val
names[tag] = desc.name
descs[tag] = desc
Expand Down Expand Up @@ -213,3 +222,17 @@ def available(self) -> bool:
def native_value(self) -> int | float | None:
"""Return the native value."""
return self.processor.entity_data.get(self.entity_key)


def get_friendly_status(status: Color, device_type: AranetType) -> str:
"""Map status code to human-readable status based on device type."""
status_map = {
"ERROR": "Error",
"RED": "Unhealthy",
(device_type.ARANET4, "GREEN"): "Good",
(device_type.ARANET4, "YELLOW"): "Average",
(device_type.ARANET_RADON, "GREEN"): "Normal",
(device_type.ARANET_RADON, "YELLOW"): "Elevated",
}

return status_map.get((device_type, status), status_map.get(status, status))
18 changes: 16 additions & 2 deletions tests/components/aranet/test_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ async def test_sensors_aranet4(
assert len(hass.states.async_all("sensor")) == 0
inject_bluetooth_service_info(hass, VALID_DATA_SERVICE_INFO)
await hass.async_block_till_done()
assert len(hass.states.async_all("sensor")) == 6
assert len(hass.states.async_all("sensor")) == 7

batt_sensor = hass.states.get("sensor.aranet4_12345_battery")
batt_sensor_attrs = batt_sensor.attributes
Expand Down Expand Up @@ -214,6 +214,13 @@ async def test_sensors_aranet4(
assert interval_sensor_attrs[ATTR_UNIT_OF_MEASUREMENT] == "s"
assert interval_sensor_attrs[ATTR_STATE_CLASS] == "measurement"

status_sensor = hass.states.get("sensor.aranet4_12345_concentration_level")
status_sensor_attrs = status_sensor.attributes
assert status_sensor.state == "Good"
assert (
status_sensor_attrs[ATTR_FRIENDLY_NAME] == "Aranet4 12345 Concentration Level"
)

# Check device context for the battery sensor
entity = entity_registry.async_get("sensor.aranet4_12345_battery")
device = device_registry.async_get(entity.device_id)
Expand Down Expand Up @@ -245,7 +252,7 @@ async def test_sensors_aranetrn(
assert len(hass.states.async_all("sensor")) == 0
inject_bluetooth_service_info(hass, VALID_ARANET_RADON_DATA_SERVICE_INFO)
await hass.async_block_till_done()
assert len(hass.states.async_all("sensor")) == 6
assert len(hass.states.async_all("sensor")) == 7

batt_sensor = hass.states.get("sensor.aranetrn_12345_battery")
batt_sensor_attrs = batt_sensor.attributes
Expand Down Expand Up @@ -291,6 +298,13 @@ async def test_sensors_aranetrn(
assert interval_sensor_attrs[ATTR_UNIT_OF_MEASUREMENT] == "s"
assert interval_sensor_attrs[ATTR_STATE_CLASS] == "measurement"

status_sensor = hass.states.get("sensor.aranetrn_12345_concentration_level")
status_sensor_attrs = status_sensor.attributes
assert status_sensor.state == "Normal"
assert (
status_sensor_attrs[ATTR_FRIENDLY_NAME] == "AranetRn+ 12345 Concentration Level"
)

# Check device context for the battery sensor
entity = entity_registry.async_get("sensor.aranetrn_12345_battery")
device = device_registry.async_get(entity.device_id)
Expand Down