diff --git a/homeassistant/components/airgradient/const.py b/homeassistant/components/airgradient/const.py index 69c7ae797589..f4de03e3ca98 100644 --- a/homeassistant/components/airgradient/const.py +++ b/homeassistant/components/airgradient/const.py @@ -25,6 +25,7 @@ TEMPERATURE_UNIT = "temperature_unit" LED_BAR_MODE = "led_bar_mode" LED_BAR_BRIGHTNESS = "led_bar_brightness" DISPLAY_BRIGHTNESS = "display_brightness" +MEASUREMENT_INTERVAL = "measurement_interval" GPS_MODE = "gps_mode" FRONT_LED_BRIGHTNESS = "front_led_brightness" BACK_LED_BRIGHTNESS = "back_led_brightness" @@ -62,6 +63,7 @@ GO_CONFIG = frozenset( TVOC_LEARNING_OFFSET, PM_STANDARD_CONFIG, TEMPERATURE_UNIT, + MEASUREMENT_INTERVAL, GPS_MODE, FRONT_LED_BRIGHTNESS, BACK_LED_BRIGHTNESS, diff --git a/homeassistant/components/airgradient/number.py b/homeassistant/components/airgradient/number.py index 0a6ba95e251e..ffd6ac6d4107 100644 --- a/homeassistant/components/airgradient/number.py +++ b/homeassistant/components/airgradient/number.py @@ -9,10 +9,12 @@ from airgradient.models import ConfigurationControl from homeassistant.components.number import ( DOMAIN as NUMBER_DOMAIN, + NumberDeviceClass, NumberEntity, NumberEntityDescription, + NumberMode, ) -from homeassistant.const import EntityCategory, UnitOfRatio +from homeassistant.const import EntityCategory, UnitOfRatio, UnitOfTime from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import entity_registry as er from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback @@ -21,6 +23,7 @@ from . import AirGradientConfigEntry from .const import ( DISPLAY_BRIGHTNESS as DISPLAY_BRIGHTNESS_CONFIG, DOMAIN, + MEASUREMENT_INTERVAL as MEASUREMENT_INTERVAL_CONFIG, supports_config, ) from .coordinator import AirGradientCoordinator @@ -64,6 +67,21 @@ LED_BAR_BRIGHTNESS = AirGradientNumberEntityDescription( set_value_fn=lambda client, value: client.set_led_bar_brightness(value), ) +MEASUREMENT_INTERVAL = AirGradientNumberEntityDescription( + key="measurement_interval", + translation_key="measurement_interval", + entity_category=EntityCategory.CONFIG, + device_class=NumberDeviceClass.DURATION, + mode=NumberMode.BOX, + native_min_value=1, + native_max_value=3600, + native_step=1, + native_unit_of_measurement=UnitOfTime.SECONDS, + config_key=MEASUREMENT_INTERVAL_CONFIG, + value_fn=lambda config: config.measurement_interval, + set_value_fn=lambda client, value: client.set_measurement_interval(value), +) + async def async_setup_entry( hass: HomeAssistant, @@ -74,7 +92,7 @@ async def async_setup_entry( coordinator = entry.runtime_data model = coordinator.data.measures.model - descriptions = (DISPLAY_BRIGHTNESS, LED_BAR_BRIGHTNESS) + descriptions = (DISPLAY_BRIGHTNESS, LED_BAR_BRIGHTNESS, MEASUREMENT_INTERVAL) descriptions_by_key = {description.key: description for description in descriptions} added_entities: set[str] = set() diff --git a/homeassistant/components/airgradient/strings.json b/homeassistant/components/airgradient/strings.json index f1343f974e94..dddbd32c6adc 100644 --- a/homeassistant/components/airgradient/strings.json +++ b/homeassistant/components/airgradient/strings.json @@ -39,6 +39,9 @@ }, "led_bar_brightness": { "name": "LED bar brightness" + }, + "measurement_interval": { + "name": "Measurement interval" } }, "select": { diff --git a/tests/components/airgradient/snapshots/test_number.ambr b/tests/components/airgradient/snapshots/test_number.ambr index 4f1007c975fc..dba0f3897da9 100644 --- a/tests/components/airgradient/snapshots/test_number.ambr +++ b/tests/components/airgradient/snapshots/test_number.ambr @@ -119,3 +119,64 @@ 'state': '100', }) # --- +# name: test_v1_entities[number.airgradient_measurement_interval-entry] + EntityRegistryEntrySnapshot({ + 'aliases': list([ + None, + ]), + 'area_id': None, + 'capabilities': dict({ + : 3600, + : 1, + : , + : 1, + }), + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'number', + 'entity_category': , + 'entity_id': 'number.airgradient_measurement_interval', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'object_id_base': 'Measurement interval', + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Measurement interval', + 'platform': 'airgradient', + 'previous_unique_id': None, + 'suggested_object_id': None, + 'supported_features': 0, + 'translation_key': 'measurement_interval', + 'unique_id': '84fce612f5b8-measurement_interval', + 'unit_of_measurement': , + }) +# --- +# name: test_v1_entities[number.airgradient_measurement_interval-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + : 'duration', + : 'Airgradient Measurement interval', + : 3600, + : 1, + : , + : 1, + : , + }), + 'context': , + 'entity_id': 'number.airgradient_measurement_interval', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': '10', + }) +# --- diff --git a/tests/components/airgradient/test_number.py b/tests/components/airgradient/test_number.py index 6dbe74575b43..7648dbd50e48 100644 --- a/tests/components/airgradient/test_number.py +++ b/tests/components/airgradient/test_number.py @@ -3,7 +3,7 @@ from datetime import timedelta from unittest.mock import AsyncMock, patch -from airgradient import AirGradientConnectionError, AirGradientError +from airgradient import AirGradientConnectionError, AirGradientError, ApiVersion from freezegun.api import FrozenDateTimeFactory import pytest from syrupy.assertion import SnapshotAssertion @@ -18,7 +18,7 @@ from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers import entity_registry as er -from . import async_load_config_fixture, setup_integration +from . import async_load_config_fixture, load_config_fixture, setup_integration from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform @@ -64,6 +64,59 @@ async def test_setting_value( mock_airgradient_client.set_led_bar_brightness.assert_called_once() +@pytest.mark.parametrize( + ("entity_id", "value", "method"), + [ + ( + "number.airgradient_measurement_interval", + 300, + "set_measurement_interval", + ), + ], +) +async def test_v1_number_writes( + hass: HomeAssistant, + mock_v1_airgradient_client: AsyncMock, + mock_config_entry: MockConfigEntry, + entity_id: str, + value: int, + method: str, +) -> None: + """Test V1 number writes.""" + mock_v1_airgradient_client.get_config.return_value = load_config_fixture( + "config_v1_local.json", ApiVersion.V1 + ) + with patch("homeassistant.components.airgradient.PLATFORMS", [Platform.NUMBER]): + await setup_integration(hass, mock_config_entry) + + await hass.services.async_call( + NUMBER_DOMAIN, + SERVICE_SET_VALUE, + service_data={ATTR_VALUE: value}, + target={ATTR_ENTITY_ID: entity_id}, + blocking=True, + ) + + getattr(mock_v1_airgradient_client, method).assert_awaited_once_with(value) + + +async def test_v1_entities( + hass: HomeAssistant, + snapshot: SnapshotAssertion, + mock_v1_airgradient_client: AsyncMock, + mock_config_entry: MockConfigEntry, + entity_registry: er.EntityRegistry, +) -> None: + """Test V1 number entities.""" + mock_v1_airgradient_client.get_config.return_value = load_config_fixture( + "config_v1_local.json", ApiVersion.V1 + ) + with patch("homeassistant.components.airgradient.PLATFORMS", [Platform.NUMBER]): + await setup_integration(hass, mock_config_entry) + + await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id) + + async def test_cloud_creates_no_number( hass: HomeAssistant, mock_cloud_airgradient_client: AsyncMock,