1
0
mirror of https://github.com/home-assistant/core.git synced 2026-02-15 07:36:16 +00:00

Add connectivity mode diagnostics sensor for Airthings BLE (#161261)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Joostlek <joostlek@outlook.com>
This commit is contained in:
Ståle Storø Hauknes
2026-01-27 17:31:23 +01:00
committed by GitHub
parent 36b9234f26
commit 9c07550f40
5 changed files with 128 additions and 5 deletions

View File

@@ -1,6 +1,14 @@
{
"entity": {
"sensor": {
"connectivity_mode": {
"default": "mdi:bluetooth-off",
"state": {
"bluetooth": "mdi:bluetooth",
"not_configured": "mdi:alert-circle",
"smartlink": "mdi:hub"
}
},
"radon_1day_avg": {
"default": "mdi:radioactive"
},

View File

@@ -5,7 +5,7 @@ from __future__ import annotations
import dataclasses
import logging
from airthings_ble import AirthingsDevice
from airthings_ble import AirthingsConnectivityMode, AirthingsDevice
from homeassistant.components.sensor import (
SensorDeviceClass,
@@ -41,6 +41,12 @@ from .coordinator import AirthingsBLEConfigEntry, AirthingsBLEDataUpdateCoordina
_LOGGER = logging.getLogger(__name__)
CONNECTIVITY_MODE_MAP = {
AirthingsConnectivityMode.BLE.value: "bluetooth",
AirthingsConnectivityMode.SMARTLINK.value: "smartlink",
AirthingsConnectivityMode.NOT_CONFIGURED.value: "not_configured",
}
SENSORS_MAPPING_TEMPLATE: dict[str, SensorEntityDescription] = {
"radon_1day_avg": SensorEntityDescription(
key="radon_1day_avg",
@@ -129,6 +135,14 @@ SENSORS_MAPPING_TEMPLATE: dict[str, SensorEntityDescription] = {
state_class=SensorStateClass.MEASUREMENT,
suggested_display_precision=0,
),
"connectivity_mode": SensorEntityDescription(
key="connectivity_mode",
translation_key="connectivity_mode",
device_class=SensorDeviceClass.ENUM,
options=list(CONNECTIVITY_MODE_MAP.values()),
entity_category=EntityCategory.DIAGNOSTIC,
entity_registry_enabled_default=False,
),
}
PARALLEL_UPDATES = 0
@@ -256,4 +270,12 @@ class AirthingsSensor(
@property
def native_value(self) -> StateType:
"""Return the value reported by the sensor."""
return self.coordinator.data.sensors[self.entity_description.key]
value = self.coordinator.data.sensors[self.entity_description.key]
# Map connectivity mode to enum values
if self.entity_description.key == "connectivity_mode":
if not isinstance(value, str):
return None
return CONNECTIVITY_MODE_MAP.get(value)
return value

View File

@@ -30,6 +30,14 @@
"ambient_noise": {
"name": "Ambient noise"
},
"connectivity_mode": {
"name": "Connectivity mode",
"state": {
"bluetooth": "Bluetooth",
"not_configured": "Not configured",
"smartlink": "SmartLink"
}
},
"illuminance": {
"name": "[%key:component::sensor::entity_component::illuminance::name%]"
},

View File

@@ -274,6 +274,7 @@ WAVE_ENHANCE_DEVICE_INFO = AirthingsDevice(
name="Airthings Wave Enhance",
identifier="123456",
sensors={
"connectivity_mode": "Bluetooth",
"lux": 25,
"battery": 85,
"humidity": 60.0,

View File

@@ -1,7 +1,9 @@
"""Test the Airthings Wave sensor."""
from copy import deepcopy
from datetime import timedelta
import logging
from typing import Any
from freezegun.api import FrozenDateTimeFactory
import pytest
@@ -12,7 +14,7 @@ from homeassistant.components.airthings_ble.const import (
DEVICE_SPECIFIC_SCAN_INTERVAL,
DOMAIN,
)
from homeassistant.const import Platform
from homeassistant.const import STATE_UNKNOWN, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr, entity_registry as er
@@ -20,6 +22,7 @@ from . import (
CO2_V1,
CO2_V2,
CORENTIUM_HOME_2_DEVICE_INFO,
CORENTIUM_HOME_2_SERVICE_INFO,
HUMIDITY_V2,
TEMPERATURE_V1,
VOC_V1,
@@ -242,7 +245,7 @@ async def test_migration_with_all_unique_ids(
("noise", "Ambient noise"),
],
)
async def test_translation_keys(
async def test_translation_keys_wave_enhance(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
device_registry: dr.DeviceRegistry,
@@ -250,7 +253,7 @@ async def test_translation_keys(
expected_sensor_name: str,
) -> None:
"""Test that translated sensor names are correct."""
entry = create_entry(hass, WAVE_ENHANCE_SERVICE_INFO, WAVE_DEVICE_INFO)
entry = create_entry(hass, WAVE_ENHANCE_SERVICE_INFO, WAVE_ENHANCE_DEVICE_INFO)
device = create_device(
entry, device_registry, WAVE_ENHANCE_SERVICE_INFO, WAVE_ENHANCE_DEVICE_INFO
)
@@ -280,6 +283,87 @@ async def test_translation_keys(
assert state.attributes.get("friendly_name") == expected_name
async def test_disabled_connectivity_mode_corentium_home_2(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
device_registry: dr.DeviceRegistry,
) -> None:
"""Test that translated sensor names are correct for disabled sensors."""
entry = create_entry(
hass,
CORENTIUM_HOME_2_SERVICE_INFO,
CORENTIUM_HOME_2_DEVICE_INFO,
)
device = create_device(
entry,
device_registry,
CORENTIUM_HOME_2_SERVICE_INFO,
CORENTIUM_HOME_2_DEVICE_INFO,
)
with (
patch_async_ble_device_from_address(CORENTIUM_HOME_2_SERVICE_INFO.device),
patch_async_discovered_service_info([CORENTIUM_HOME_2_SERVICE_INFO]),
patch_airthings_ble(CORENTIUM_HOME_2_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 Corentium Home 2 (123456)"
unique_id = f"{CORENTIUM_HOME_2_DEVICE_INFO.address}_connectivity_mode"
entity_id = entity_registry.async_get_entity_id(Platform.SENSOR, DOMAIN, unique_id)
assert entity_id is not None
entity_entry = entity_registry.async_get(entity_id)
assert entity_entry is not None
assert entity_entry.disabled
assert entity_entry.disabled_by is er.RegistryEntryDisabler.INTEGRATION
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
@pytest.mark.parametrize(
("source_value", "expected_state"),
[
(None, STATE_UNKNOWN),
(123, STATE_UNKNOWN),
(45.6, STATE_UNKNOWN),
("Bluetooth", "bluetooth"),
],
)
async def test_connectivity_mode(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
device_registry: dr.DeviceRegistry,
source_value: Any,
expected_state: str,
) -> None:
"""Test that non-string connectivity mode values are handled correctly."""
test_device = deepcopy(CORENTIUM_HOME_2_DEVICE_INFO)
# Non-string value, will be mapped to 'unknown' state
test_device.sensors["connectivity_mode"] = source_value
entry = create_entry(hass, CORENTIUM_HOME_2_SERVICE_INFO, test_device)
create_device(entry, device_registry, CORENTIUM_HOME_2_SERVICE_INFO, test_device)
with (
patch_async_ble_device_from_address(CORENTIUM_HOME_2_SERVICE_INFO.device),
patch_async_discovered_service_info([CORENTIUM_HOME_2_SERVICE_INFO]),
patch_airthings_ble(test_device),
):
await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
state = hass.states.get(
"sensor.airthings_corentium_home_2_123456_connectivity_mode"
)
assert state is not None
assert state.state == expected_state
async def test_scan_interval_migration_corentium_home_2(
hass: HomeAssistant,
freezer: FrozenDateTimeFactory,