1
0
mirror of https://github.com/home-assistant/core.git synced 2026-05-08 17:49:37 +01:00

Add new sensors for Airthings Wave Enhance (#153879)

This commit is contained in:
Ståle Storø Hauknes
2025-10-07 17:44:30 +02:00
committed by GitHub
parent b46097a7fc
commit 4587c286bb
4 changed files with 152 additions and 17 deletions
@@ -16,10 +16,12 @@ from homeassistant.components.sensor import (
from homeassistant.const import (
CONCENTRATION_PARTS_PER_BILLION,
CONCENTRATION_PARTS_PER_MILLION,
LIGHT_LUX,
PERCENTAGE,
EntityCategory,
Platform,
UnitOfPressure,
UnitOfSoundPressure,
UnitOfTemperature,
)
from homeassistant.core import HomeAssistant, callback
@@ -112,6 +114,21 @@ SENSORS_MAPPING_TEMPLATE: dict[str, SensorEntityDescription] = {
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"lux": SensorEntityDescription(
key="lux",
device_class=SensorDeviceClass.ILLUMINANCE,
native_unit_of_measurement=LIGHT_LUX,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"noise": SensorEntityDescription(
key="noise",
translation_key="ambient_noise",
device_class=SensorDeviceClass.SOUND_PRESSURE,
native_unit_of_measurement=UnitOfSoundPressure.WEIGHTED_DECIBEL_A,
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
}
PARALLEL_UPDATES = 0
@@ -41,6 +41,9 @@
},
"illuminance": {
"name": "[%key:component::sensor::entity_component::illuminance::name%]"
},
"ambient_noise": {
"name": "Ambient noise"
}
}
}
+72 -9
View File
@@ -9,12 +9,17 @@ from airthings_ble import (
AirthingsDevice,
AirthingsDeviceType,
)
from bleak.backends.device import BLEDevice
from homeassistant.components.airthings_ble.const import DOMAIN
from homeassistant.components.bluetooth.models import BluetoothServiceInfoBleak
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.device_registry import CONNECTION_BLUETOOTH, DeviceRegistry
from homeassistant.helpers.device_registry import (
CONNECTION_BLUETOOTH,
DeviceEntry,
DeviceRegistry,
)
from tests.common import MockConfigEntry, MockEntity
from tests.components.bluetooth import generate_advertisement_data, generate_ble_device
@@ -28,7 +33,15 @@ def patch_async_setup_entry(return_value=True):
)
def patch_async_ble_device_from_address(return_value: BluetoothServiceInfoBleak | None):
def patch_async_discovered_service_info(return_value: list[BluetoothServiceInfoBleak]):
"""Patch async_discovered_service_info to return given list."""
return patch(
"homeassistant.components.bluetooth.async_discovered_service_info",
return_value=return_value,
)
def patch_async_ble_device_from_address(return_value: BLEDevice | None):
"""Patch async ble device from address to return a given value."""
return patch(
"homeassistant.components.bluetooth.async_ble_device_from_address",
@@ -101,6 +114,27 @@ WAVE_SERVICE_INFO = BluetoothServiceInfoBleak(
tx_power=0,
)
WAVE_ENHANCE_SERVICE_INFO = BluetoothServiceInfoBleak(
name="cc-cc-cc-cc-cc-cc",
address="cc:cc:cc:cc:cc:cc",
device=generate_ble_device(
address="cc:cc:cc:cc:cc:cc",
name="Airthings Wave Enhance",
),
rssi=-61,
manufacturer_data={820: b"\xe4/\xa5\xae\t\x00"},
service_data={},
service_uuids=[],
source="local",
advertisement=generate_advertisement_data(
manufacturer_data={820: b"\xe4/\xa5\xae\t\x00"},
service_uuids=[],
),
connectable=True,
time=0,
tx_power=0,
)
VIEW_PLUS_SERVICE_INFO = BluetoothServiceInfoBleak(
name="cc-cc-cc-cc-cc-cc",
address="cc:cc:cc:cc:cc:cc",
@@ -211,6 +245,26 @@ WAVE_DEVICE_INFO = AirthingsDevice(
address="cc:cc:cc:cc:cc:cc",
)
WAVE_ENHANCE_DEVICE_INFO = AirthingsDevice(
manufacturer="Airthings AS",
hw_version="REV X",
sw_version="T-SUB-2.6.2-master+0",
model=AirthingsDeviceType.WAVE_ENHANCE_EU,
name="Airthings Wave Enhance",
identifier="123456",
sensors={
"lux": 25,
"battery": 85,
"humidity": 60.0,
"temperature": 21.0,
"co2": 500.0,
"voc": 155.0,
"pressure": 1020,
"noise": 40,
},
address="cc:cc:cc:cc:cc:cc",
)
TEMPERATURE_V1 = MockEntity(
unique_id="Airthings Wave Plus 123456_temperature",
name="Airthings Wave Plus 123456 Temperature",
@@ -247,23 +301,32 @@ VOC_V3 = MockEntity(
)
def create_entry(hass: HomeAssistant) -> MockConfigEntry:
def create_entry(
hass: HomeAssistant,
service_info: BluetoothServiceInfoBleak,
device_info: AirthingsDevice,
) -> MockConfigEntry:
"""Create a config entry."""
entry = MockConfigEntry(
domain=DOMAIN,
unique_id=WAVE_SERVICE_INFO.address,
title="Airthings Wave Plus (123456)",
unique_id=service_info.address,
title=f"{device_info.name} ({device_info.identifier})",
)
entry.add_to_hass(hass)
return entry
def create_device(entry: ConfigEntry, device_registry: DeviceRegistry):
def create_device(
entry: ConfigEntry,
device_registry: DeviceRegistry,
service_info: BluetoothServiceInfoBleak,
device_info: AirthingsDevice,
) -> DeviceEntry:
"""Create a device for the given entry."""
return device_registry.async_get_or_create(
config_entry_id=entry.entry_id,
connections={(CONNECTION_BLUETOOTH, WAVE_SERVICE_INFO.address)},
connections={(CONNECTION_BLUETOOTH, service_info.address)},
manufacturer="Airthings AS",
name="Airthings Wave Plus (123456)",
model="Wave Plus",
name=f"{device_info.name} ({device_info.identifier})",
model=device_info.model.product_name,
)
+60 -8
View File
@@ -2,6 +2,8 @@
import logging
import pytest
from homeassistant.components.airthings_ble.const import DOMAIN
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
@@ -16,10 +18,15 @@ from . import (
VOC_V2,
VOC_V3,
WAVE_DEVICE_INFO,
WAVE_ENHANCE_DEVICE_INFO,
WAVE_ENHANCE_SERVICE_INFO,
WAVE_SERVICE_INFO,
create_device,
create_entry,
patch_airthings_ble,
patch_airthings_device_update,
patch_async_ble_device_from_address,
patch_async_discovered_service_info,
)
from tests.components.bluetooth import inject_bluetooth_service_info
@@ -33,8 +40,8 @@ async def test_migration_from_v1_to_v3_unique_id(
device_registry: dr.DeviceRegistry,
) -> None:
"""Verify that we can migrate from v1 (pre 2023.9.0) to the latest unique id format."""
entry = create_entry(hass)
device = create_device(entry, device_registry)
entry = create_entry(hass, WAVE_SERVICE_INFO, WAVE_DEVICE_INFO)
device = create_device(entry, device_registry, WAVE_SERVICE_INFO, WAVE_DEVICE_INFO)
assert entry is not None
assert device is not None
@@ -74,8 +81,8 @@ async def test_migration_from_v2_to_v3_unique_id(
device_registry: dr.DeviceRegistry,
) -> None:
"""Verify that we can migrate from v2 (introduced in 2023.9.0) to the latest unique id format."""
entry = create_entry(hass)
device = create_device(entry, device_registry)
entry = create_entry(hass, WAVE_SERVICE_INFO, WAVE_DEVICE_INFO)
device = create_device(entry, device_registry, WAVE_SERVICE_INFO, WAVE_DEVICE_INFO)
assert entry is not None
assert device is not None
@@ -115,8 +122,8 @@ async def test_migration_from_v1_and_v2_to_v3_unique_id(
device_registry: dr.DeviceRegistry,
) -> None:
"""Test if migration works when we have both v1 (pre 2023.9.0) and v2 (introduced in 2023.9.0) unique ids."""
entry = create_entry(hass)
device = create_device(entry, device_registry)
entry = create_entry(hass, WAVE_SERVICE_INFO, WAVE_DEVICE_INFO)
device = create_device(entry, device_registry, WAVE_SERVICE_INFO, WAVE_DEVICE_INFO)
assert entry is not None
assert device is not None
@@ -165,8 +172,8 @@ async def test_migration_with_all_unique_ids(
device_registry: dr.DeviceRegistry,
) -> None:
"""Test if migration works when we have all unique ids."""
entry = create_entry(hass)
device = create_device(entry, device_registry)
entry = create_entry(hass, WAVE_SERVICE_INFO, WAVE_DEVICE_INFO)
device = create_device(entry, device_registry, WAVE_SERVICE_INFO, WAVE_DEVICE_INFO)
assert entry is not None
assert device is not None
@@ -215,3 +222,48 @@ async def test_migration_with_all_unique_ids(
assert entity_registry.async_get(v1.entity_id).unique_id == VOC_V1.unique_id
assert entity_registry.async_get(v2.entity_id).unique_id == VOC_V2.unique_id
assert entity_registry.async_get(v3.entity_id).unique_id == VOC_V3.unique_id
@pytest.mark.parametrize(
("unique_suffix", "expected_sensor_name"),
[
("lux", "Illuminance"),
("noise", "Ambient noise"),
],
)
async def test_translation_keys(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
device_registry: dr.DeviceRegistry,
unique_suffix: str,
expected_sensor_name: str,
) -> None:
"""Test that translated sensor names are correct."""
entry = create_entry(hass, WAVE_ENHANCE_SERVICE_INFO, WAVE_DEVICE_INFO)
device = create_device(
entry, device_registry, WAVE_ENHANCE_SERVICE_INFO, WAVE_ENHANCE_DEVICE_INFO
)
with (
patch_async_ble_device_from_address(WAVE_ENHANCE_SERVICE_INFO.device),
patch_async_discovered_service_info([WAVE_ENHANCE_SERVICE_INFO]),
patch_airthings_ble(WAVE_ENHANCE_DEVICE_INFO),
):
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
assert device is not None
assert device.name == "Airthings Wave Enhance (123456)"
unique_id = f"{WAVE_ENHANCE_DEVICE_INFO.address}_{unique_suffix}"
entity_id = entity_registry.async_get_entity_id(Platform.SENSOR, DOMAIN, unique_id)
assert entity_id is not None
state = hass.states.get(entity_id)
assert state is not None
expected_value = WAVE_ENHANCE_DEVICE_INFO.sensors[unique_suffix]
assert state.state == str(expected_value)
expected_name = f"Airthings Wave Enhance (123456) {expected_sensor_name}"
assert state.attributes.get("friendly_name") == expected_name