-
-
Notifications
You must be signed in to change notification settings - Fork 37.4k
Add sensor platform for AirPatrol #158726
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
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
e83e125
Added sensor platform for AirPatrol
antondalgren 21d83de
Add state_class and minor copy-paste typos
antondalgren d635361
Update snapshot tests
antondalgren 849bada
Move climate_data and available property to base class
antondalgren 725741e
Update tests
antondalgren 315a646
Fix comments
antondalgren f693084
fix comment
antondalgren File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| """Sensors for AirPatrol integration.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from dataclasses import dataclass | ||
|
|
||
| from homeassistant.components.sensor import ( | ||
| SensorDeviceClass, | ||
| SensorEntity, | ||
| SensorEntityDescription, | ||
| SensorStateClass, | ||
| ) | ||
| from homeassistant.const import PERCENTAGE, UnitOfTemperature | ||
| from homeassistant.core import HomeAssistant | ||
| from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback | ||
|
|
||
| from . import AirPatrolConfigEntry | ||
| from .coordinator import AirPatrolDataUpdateCoordinator | ||
| from .entity import AirPatrolEntity | ||
|
|
||
| PARALLEL_UPDATES = 0 | ||
|
|
||
|
|
||
| @dataclass(frozen=True, kw_only=True) | ||
| class AirPatrolSensorEntityDescription(SensorEntityDescription): | ||
| """Describes AirPatrol sensor entity.""" | ||
|
|
||
| data_field: str | ||
|
|
||
|
|
||
| SENSOR_DESCRIPTIONS = ( | ||
| AirPatrolSensorEntityDescription( | ||
| key="temperature", | ||
| device_class=SensorDeviceClass.TEMPERATURE, | ||
| state_class=SensorStateClass.MEASUREMENT, | ||
| native_unit_of_measurement=UnitOfTemperature.CELSIUS, | ||
| data_field="RoomTemp", | ||
| ), | ||
| AirPatrolSensorEntityDescription( | ||
| key="humidity", | ||
| device_class=SensorDeviceClass.HUMIDITY, | ||
| state_class=SensorStateClass.MEASUREMENT, | ||
| native_unit_of_measurement=PERCENTAGE, | ||
| data_field="RoomHumidity", | ||
| ), | ||
| ) | ||
|
|
||
|
|
||
| async def async_setup_entry( | ||
| hass: HomeAssistant, | ||
| config_entry: AirPatrolConfigEntry, | ||
| async_add_entities: AddConfigEntryEntitiesCallback, | ||
| ) -> None: | ||
| """Set up AirPatrol sensors.""" | ||
| coordinator = config_entry.runtime_data | ||
| units = coordinator.data | ||
|
|
||
| async_add_entities( | ||
| AirPatrolSensor(coordinator, unit_id, description) | ||
| for unit_id, unit in units.items() | ||
| for description in SENSOR_DESCRIPTIONS | ||
| if "climate" in unit and unit["climate"] is not None | ||
| ) | ||
|
|
||
|
|
||
| class AirPatrolSensor(AirPatrolEntity, SensorEntity): | ||
| """AirPatrol sensor entity.""" | ||
|
|
||
| entity_description: AirPatrolSensorEntityDescription | ||
|
|
||
| def __init__( | ||
| self, | ||
| coordinator: AirPatrolDataUpdateCoordinator, | ||
| unit_id: str, | ||
| description: AirPatrolSensorEntityDescription, | ||
| ) -> None: | ||
| """Initialize AirPatrol sensor.""" | ||
| super().__init__(coordinator, unit_id) | ||
| self.entity_description = description | ||
| self._attr_unique_id = ( | ||
| f"{coordinator.config_entry.unique_id}-{unit_id}-{description.key}" | ||
| ) | ||
|
|
||
| @property | ||
| def native_value(self) -> float | None: | ||
| """Return the state of the sensor.""" | ||
| if value := self.climate_data.get(self.entity_description.data_field): | ||
| return float(value) | ||
| return None | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| # serializer version: 1 | ||
| # name: test_sensor_with_climate_data[sensor.living_room_humidity-entry] | ||
| EntityRegistryEntrySnapshot({ | ||
| 'aliases': set({ | ||
| }), | ||
| 'area_id': None, | ||
| 'capabilities': dict({ | ||
| 'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>, | ||
| }), | ||
| 'config_entry_id': <ANY>, | ||
| 'config_subentry_id': <ANY>, | ||
| 'device_class': None, | ||
| 'device_id': <ANY>, | ||
| 'disabled_by': None, | ||
| 'domain': 'sensor', | ||
| 'entity_category': None, | ||
| 'entity_id': 'sensor.living_room_humidity', | ||
| 'has_entity_name': True, | ||
| 'hidden_by': None, | ||
| 'icon': None, | ||
| 'id': <ANY>, | ||
| 'labels': set({ | ||
| }), | ||
| 'name': None, | ||
| 'options': dict({ | ||
| }), | ||
| 'original_device_class': <SensorDeviceClass.HUMIDITY: 'humidity'>, | ||
| 'original_icon': None, | ||
| 'original_name': 'Humidity', | ||
| 'platform': 'airpatrol', | ||
| 'previous_unique_id': None, | ||
| 'suggested_object_id': None, | ||
| 'supported_features': 0, | ||
| 'translation_key': None, | ||
| 'unique_id': 'test_user_id-test_unit_001-humidity', | ||
| 'unit_of_measurement': '%', | ||
| }) | ||
| # --- | ||
| # name: test_sensor_with_climate_data[sensor.living_room_humidity-state] | ||
| StateSnapshot({ | ||
| 'attributes': ReadOnlyDict({ | ||
| 'device_class': 'humidity', | ||
| 'friendly_name': 'living room Humidity', | ||
| 'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>, | ||
| 'unit_of_measurement': '%', | ||
| }), | ||
| 'context': <ANY>, | ||
| 'entity_id': 'sensor.living_room_humidity', | ||
| 'last_changed': <ANY>, | ||
| 'last_reported': <ANY>, | ||
| 'last_updated': <ANY>, | ||
| 'state': '45.0', | ||
| }) | ||
| # --- | ||
| # name: test_sensor_with_climate_data[sensor.living_room_temperature-entry] | ||
| EntityRegistryEntrySnapshot({ | ||
| 'aliases': set({ | ||
| }), | ||
| 'area_id': None, | ||
| 'capabilities': dict({ | ||
| 'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>, | ||
| }), | ||
| 'config_entry_id': <ANY>, | ||
| 'config_subentry_id': <ANY>, | ||
| 'device_class': None, | ||
| 'device_id': <ANY>, | ||
| 'disabled_by': None, | ||
| 'domain': 'sensor', | ||
| 'entity_category': None, | ||
| 'entity_id': 'sensor.living_room_temperature', | ||
| 'has_entity_name': True, | ||
| 'hidden_by': None, | ||
| 'icon': None, | ||
| 'id': <ANY>, | ||
| 'labels': set({ | ||
| }), | ||
| 'name': None, | ||
| 'options': dict({ | ||
| 'sensor': dict({ | ||
| 'suggested_display_precision': 1, | ||
| }), | ||
| }), | ||
| 'original_device_class': <SensorDeviceClass.TEMPERATURE: 'temperature'>, | ||
| 'original_icon': None, | ||
| 'original_name': 'Temperature', | ||
| 'platform': 'airpatrol', | ||
| 'previous_unique_id': None, | ||
| 'suggested_object_id': None, | ||
| 'supported_features': 0, | ||
| 'translation_key': None, | ||
| 'unique_id': 'test_user_id-test_unit_001-temperature', | ||
| 'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>, | ||
| }) | ||
| # --- | ||
| # name: test_sensor_with_climate_data[sensor.living_room_temperature-state] | ||
| StateSnapshot({ | ||
| 'attributes': ReadOnlyDict({ | ||
| 'device_class': 'temperature', | ||
| 'friendly_name': 'living room Temperature', | ||
| 'state_class': <SensorStateClass.MEASUREMENT: 'measurement'>, | ||
| 'unit_of_measurement': <UnitOfTemperature.CELSIUS: '°C'>, | ||
| }), | ||
| 'context': <ANY>, | ||
| 'entity_id': 'sensor.living_room_temperature', | ||
| 'last_changed': <ANY>, | ||
| 'last_reported': <ANY>, | ||
| 'last_updated': <ANY>, | ||
| 'state': '22.5', | ||
| }) | ||
| # --- |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| """Test the AirPatrol sensor platform.""" | ||
|
|
||
| from collections.abc import Generator | ||
| from unittest.mock import patch | ||
|
|
||
| from airpatrol.api import AirPatrolAPI | ||
| import pytest | ||
|
|
||
| from homeassistant.const import Platform | ||
| from homeassistant.core import HomeAssistant | ||
| from homeassistant.helpers import entity_registry as er | ||
|
|
||
| from tests.common import MockConfigEntry, SnapshotAssertion, snapshot_platform | ||
|
|
||
|
|
||
| @pytest.fixture(autouse=True) | ||
| def override_platforms() -> Generator[None]: | ||
| """Override the platforms to load for airpatrol.""" | ||
| with patch( | ||
| "homeassistant.components.airpatrol.PLATFORMS", | ||
| [Platform.SENSOR], | ||
| ): | ||
| yield | ||
|
|
||
|
|
||
| async def test_sensor_with_climate_data( | ||
| hass: HomeAssistant, | ||
| load_integration: MockConfigEntry, | ||
| get_client: AirPatrolAPI, | ||
| entity_registry: er.EntityRegistry, | ||
| snapshot: SnapshotAssertion, | ||
| ) -> None: | ||
| """Test sensor entities are created with climate data.""" | ||
| await snapshot_platform( | ||
| hass, | ||
| entity_registry, | ||
| snapshot, | ||
| load_integration.entry_id, | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize( | ||
| "climate_data", | ||
| [ | ||
| None, | ||
| ], | ||
| ) | ||
| async def test_sensor_with_no_climate_data( | ||
| hass: HomeAssistant, | ||
| load_integration: MockConfigEntry, | ||
| get_client: AirPatrolAPI, | ||
| entity_registry: er.EntityRegistry, | ||
| ) -> None: | ||
| """Test no sensor entities are created when no climate data is present.""" | ||
| assert len(entity_registry.entities) == 0 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.