Add tests for Netatmo sensor (#46393)

* Add tests for Netatmo sensor

* Fix coveragerc

* Remove freezegun dependency

* Use f-strings instead of string concatenation

* Update tests/components/netatmo/test_sensor.py

Co-authored-by: Erik Montnemery <erik@montnemery.com>

* Address comment on config options test

* Replace deprecated call to async_get_registry()

* Fix public weather sensor update test

* Clean up

* Prevent division by zero

Co-authored-by: Erik Montnemery <erik@montnemery.com>
Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
Tobias Sauerwein 2021-03-24 07:17:51 +01:00 committed by GitHub
parent 3dec394cad
commit 0d699bb768
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 844 additions and 14 deletions

View file

@ -644,7 +644,7 @@ omit =
homeassistant/components/nello/lock.py
homeassistant/components/nest/legacy/*
homeassistant/components/netatmo/data_handler.py
homeassistant/components/netatmo/sensor.py
homeassistant/components/netatmo/helper.py
homeassistant/components/netdata/sensor.py
homeassistant/components/netgear/device_tracker.py
homeassistant/components/netgear_lte/*

View file

@ -641,7 +641,7 @@ class NetatmoPublicSensor(NetatmoBase, SensorEntity):
elif self.type == "guststrength":
data = self._data.get_latest_gust_strengths()
if not data:
if data is None:
if self._state is None:
return
_LOGGER.debug(
@ -650,8 +650,8 @@ class NetatmoPublicSensor(NetatmoBase, SensorEntity):
self._state = None
return
values = [x for x in data.values() if x is not None]
if self._mode == "avg":
self._state = round(sum(values) / len(values), 1)
elif self._mode == "max":
self._state = max(values)
if values := [x for x in data.values() if x is not None]:
if self._mode == "avg":
self._state = round(sum(values) / len(values), 1)
elif self._mode == "max":
self._state = max(values)

View file

@ -29,6 +29,8 @@ COMMON_RESPONSE = {
"user": {"id": "91763b24c43d3e344f424e8b", "email": "john@doe.com"},
}
TEST_TIME = 1559347200.0
def fake_post_request(**args):
"""Return fake data."""

View file

@ -5,7 +5,7 @@ from unittest.mock import patch
import pytest
from .common import ALL_SCOPES, fake_post_request, fake_post_request_no_data
from .common import ALL_SCOPES, TEST_TIME, fake_post_request, fake_post_request_no_data
from tests.common import MockConfigEntry
@ -81,11 +81,10 @@ async def mock_entry_fixture(hass, config_entry):
@pytest.fixture(name="sensor_entry")
async def mock_sensor_entry_fixture(hass, config_entry):
"""Mock setup of sensor platform."""
with selected_platforms(["sensor"]):
with patch("time.time", return_value=TEST_TIME), selected_platforms(["sensor"]):
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
return config_entry
await hass.async_block_till_done()
yield config_entry
@pytest.fixture(name="camera_entry")
@ -131,5 +130,5 @@ async def mock_entry_error_fixture(hass, config_entry):
mock_auth.return_value.post_request.side_effect = fake_post_request_no_data
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
return config_entry
await hass.async_block_till_done()
yield config_entry

View file

@ -0,0 +1,235 @@
"""The tests for the Netatmo sensor platform."""
from datetime import timedelta
from unittest.mock import patch
import pytest
from homeassistant.components.netatmo import sensor
from homeassistant.components.netatmo.sensor import MODULE_TYPE_WIND
from homeassistant.config_entries import RELOAD_AFTER_UPDATE_DELAY
from homeassistant.helpers import entity_registry as er
from homeassistant.util import dt
from .common import TEST_TIME
from .conftest import selected_platforms
from tests.common import async_fire_time_changed
async def test_weather_sensor(hass, sensor_entry):
"""Test weather sensor setup."""
prefix = "sensor.netatmo_mystation_"
assert hass.states.get(f"{prefix}temperature").state == "24.6"
assert hass.states.get(f"{prefix}humidity").state == "36"
assert hass.states.get(f"{prefix}co2").state == "749"
assert hass.states.get(f"{prefix}pressure").state == "1017.3"
async def test_public_weather_sensor(hass, sensor_entry):
"""Test public weather sensor setup."""
prefix = "sensor.netatmo_home_max_"
assert hass.states.get(f"{prefix}temperature").state == "27.4"
assert hass.states.get(f"{prefix}humidity").state == "76"
assert hass.states.get(f"{prefix}pressure").state == "1014.4"
prefix = "sensor.netatmo_home_avg_"
assert hass.states.get(f"{prefix}temperature").state == "22.7"
assert hass.states.get(f"{prefix}humidity").state == "63.2"
assert hass.states.get(f"{prefix}pressure").state == "1010.3"
assert len(hass.states.async_all()) > 0
entities_before_change = len(hass.states.async_all())
valid_option = {
"lat_ne": 32.91336,
"lon_ne": -117.187429,
"lat_sw": 32.83336,
"lon_sw": -117.26743,
"show_on_map": True,
"area_name": "Home avg",
"mode": "max",
}
result = await hass.config_entries.options.async_init(sensor_entry.entry_id)
result = await hass.config_entries.options.async_configure(
result["flow_id"], user_input={"new_area": "Home avg"}
)
result = await hass.config_entries.options.async_configure(
result["flow_id"], user_input=valid_option
)
result = await hass.config_entries.options.async_configure(
result["flow_id"], user_input={}
)
await hass.async_block_till_done()
async_fire_time_changed(
hass,
dt.utcnow() + timedelta(seconds=RELOAD_AFTER_UPDATE_DELAY + 1),
)
await hass.async_block_till_done()
assert hass.states.get(f"{prefix}temperature").state == "27.4"
assert hass.states.get(f"{prefix}humidity").state == "76"
assert hass.states.get(f"{prefix}pressure").state == "1014.4"
assert len(hass.states.async_all()) == entities_before_change
@pytest.mark.parametrize(
"strength, expected",
[(50, "Full"), (60, "High"), (80, "Medium"), (90, "Low")],
)
async def test_process_wifi(strength, expected):
"""Test wifi strength translation."""
assert sensor.process_wifi(strength) == expected
@pytest.mark.parametrize(
"strength, expected",
[(50, "Full"), (70, "High"), (80, "Medium"), (90, "Low")],
)
async def test_process_rf(strength, expected):
"""Test radio strength translation."""
assert sensor.process_rf(strength) == expected
@pytest.mark.parametrize(
"health, expected",
[(4, "Unhealthy"), (3, "Poor"), (2, "Fair"), (1, "Fine"), (0, "Healthy")],
)
async def test_process_health(health, expected):
"""Test health index translation."""
assert sensor.process_health(health) == expected
@pytest.mark.parametrize(
"model, data, expected",
[
(MODULE_TYPE_WIND, 5591, "Full"),
(MODULE_TYPE_WIND, 5181, "High"),
(MODULE_TYPE_WIND, 4771, "Medium"),
(MODULE_TYPE_WIND, 4361, "Low"),
(MODULE_TYPE_WIND, 4300, "Very Low"),
],
)
async def test_process_battery(model, data, expected):
"""Test battery level translation."""
assert sensor.process_battery(data, model) == expected
@pytest.mark.parametrize(
"angle, expected",
[
(0, "N"),
(40, "NE"),
(70, "E"),
(130, "SE"),
(160, "S"),
(220, "SW"),
(250, "W"),
(310, "NW"),
(340, "N"),
],
)
async def test_process_angle(angle, expected):
"""Test wind direction translation."""
assert sensor.process_angle(angle) == expected
@pytest.mark.parametrize(
"angle, expected",
[(-1, 359), (-40, 320)],
)
async def test_fix_angle(angle, expected):
"""Test wind angle fix."""
assert sensor.fix_angle(angle) == expected
@pytest.mark.parametrize(
"uid, name, expected",
[
("12:34:56:37:11:ca-reachable", "netatmo_mystation_reachable", "True"),
("12:34:56:03:1b:e4-rf_status", "netatmo_mystation_yard_radio", "Full"),
(
"12:34:56:05:25:6e-rf_status",
"netatmo_valley_road_rain_gauge_radio",
"Medium",
),
(
"12:34:56:36:fc:de-rf_status_lvl",
"netatmo_mystation_netatmooutdoor_radio_level",
"65",
),
(
"12:34:56:37:11:ca-wifi_status_lvl",
"netatmo_mystation_wifi_level",
"45",
),
(
"12:34:56:37:11:ca-wifi_status",
"netatmo_mystation_wifi_status",
"Full",
),
(
"12:34:56:37:11:ca-temp_trend",
"netatmo_mystation_temperature_trend",
"stable",
),
(
"12:34:56:37:11:ca-pressure_trend",
"netatmo_mystation_pressure_trend",
"down",
),
("12:34:56:05:51:20-sum_rain_1", "netatmo_mystation_yard_rain_last_hour", "0"),
("12:34:56:05:51:20-sum_rain_24", "netatmo_mystation_yard_rain_today", "0"),
("12:34:56:03:1b:e4-windangle", "netatmo_mystation_garden_direction", "SW"),
(
"12:34:56:03:1b:e4-windangle_value",
"netatmo_mystation_garden_angle",
"217",
),
("12:34:56:03:1b:e4-gustangle", "mystation_garden_gust_direction", "S"),
(
"12:34:56:03:1b:e4-gustangle",
"netatmo_mystation_garden_gust_direction",
"S",
),
(
"12:34:56:03:1b:e4-gustangle_value",
"netatmo_mystation_garden_gust_angle_value",
"206",
),
(
"12:34:56:03:1b:e4-guststrength",
"netatmo_mystation_garden_gust_strength",
"9",
),
(
"12:34:56:26:68:92-health_idx",
"netatmo_baby_bedroom_health",
"Fine",
),
],
)
async def test_weather_sensor_enabling(hass, config_entry, uid, name, expected):
"""Test enabling of by default disabled sensors."""
with patch("time.time", return_value=TEST_TIME), selected_platforms(["sensor"]):
states_before = len(hass.states.async_all())
assert hass.states.get(f"sensor.{name}") is None
registry = er.async_get(hass)
registry.async_get_or_create(
"sensor",
"netatmo",
uid,
suggested_object_id=name,
disabled_by=None,
)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
assert len(hass.states.async_all()) > states_before
assert hass.states.get(f"sensor.{name}").state == expected

View file

@ -0,0 +1,202 @@
{
"body": {
"devices": [
{
"_id": "12:34:56:26:69:0c",
"cipher_id": "enc:16:1UqwQlYV5AY2pfyEi5H47dmmFOOL3mCUo+KAkchL4A2CLI5u0e45Xr5jeAswO+XO",
"date_setup": 1544560184,
"last_setup": 1544560184,
"type": "NHC",
"last_status_store": 1558268332,
"firmware": 45,
"last_upgrade": 1544560186,
"wifi_status": 58,
"reachable": false,
"co2_calibrating": false,
"station_name": "Bedroom",
"data_type": [
"Temperature",
"CO2",
"Humidity",
"Noise",
"Pressure",
"health_idx"
],
"place": {
"city": "Frankfurt",
"country": "DE",
"timezone": "Europe/Berlin",
"location": [
52.516263,
13.377726
]
}
},
{
"_id": "12:34:56:25:cf:a8",
"cipher_id": "enc:16:A+Jm0yFWBwUyKinFDutPZK7I2PuHN1fqaE9oB/KF+McbFs3oN9CKpR/dYbqL4om2",
"date_setup": 1544562192,
"last_setup": 1544562192,
"type": "NHC",
"last_status_store": 1559198922,
"firmware": 45,
"last_upgrade": 1544562194,
"wifi_status": 41,
"reachable": true,
"co2_calibrating": false,
"station_name": "Kitchen",
"data_type": [
"Temperature",
"CO2",
"Humidity",
"Noise",
"Pressure",
"health_idx"
],
"place": {
"city": "Frankfurt",
"country": "DE",
"timezone": "Europe/Berlin",
"location": [
52.516263,
13.377726
]
}
},
{
"_id": "12:34:56:26:65:14",
"cipher_id": "enc:16:7kK6ZzG4L7NgfZZ6+dMvNxw4l6vXu+88SEJkCUklNdPa4KYIHmsfa1moOilEK61i",
"date_setup": 1544564061,
"last_setup": 1544564061,
"type": "NHC",
"last_status_store": 1559067159,
"firmware": 45,
"last_upgrade": 1544564302,
"wifi_status": 66,
"reachable": true,
"co2_calibrating": false,
"station_name": "Livingroom",
"data_type": [
"Temperature",
"CO2",
"Humidity",
"Noise",
"Pressure",
"health_idx"
],
"place": {
"city": "Frankfurt",
"country": "DE",
"timezone": "Europe/Berlin",
"location": [
52.516263,
13.377726
]
}
},
{
"_id": "12:34:56:3e:c5:46",
"station_name": "Parents Bedroom",
"date_setup": 1570732241,
"last_setup": 1570732241,
"type": "NHC",
"last_status_store": 1572073818,
"module_name": "Indoor",
"firmware": 45,
"wifi_status": 67,
"reachable": true,
"co2_calibrating": false,
"data_type": [
"Temperature",
"CO2",
"Humidity",
"Noise",
"Pressure",
"health_idx"
],
"place": {
"city": "Frankfurt",
"country": "DE",
"timezone": "Europe/Berlin",
"location": [
52.516263,
13.377726
]
},
"dashboard_data": {
"time_utc": 1572073816,
"Temperature": 20.3,
"CO2": 494,
"Humidity": 63,
"Noise": 42,
"Pressure": 1014.5,
"AbsolutePressure": 1004.1,
"health_idx": 1,
"min_temp": 20.3,
"max_temp": 21.6,
"date_max_temp": 1572059333,
"date_min_temp": 1572073816
}
},
{
"_id": "12:34:56:26:68:92",
"station_name": "Baby Bedroom",
"date_setup": 1571342643,
"last_setup": 1571342643,
"type": "NHC",
"last_status_store": 1572073995,
"module_name": "Indoor",
"firmware": 45,
"wifi_status": 68,
"reachable": true,
"co2_calibrating": false,
"data_type": [
"Temperature",
"CO2",
"Humidity",
"Noise",
"Pressure",
"health_idx"
],
"place": {
"city": "Frankfurt",
"country": "DE",
"timezone": "Europe/Berlin",
"location": [
52.516263,
13.377726
]
},
"dashboard_data": {
"time_utc": 1572073994,
"Temperature": 21.6,
"CO2": 1053,
"Humidity": 66,
"Noise": 45,
"Pressure": 1021.4,
"AbsolutePressure": 1011,
"health_idx": 1,
"min_temp": 20.9,
"max_temp": 21.6,
"date_max_temp": 1572073690,
"date_min_temp": 1572064254
}
}
],
"user": {
"mail": "john@doe.com",
"administrative": {
"lang": "de-DE",
"reg_locale": "de-DE",
"country": "DE",
"unit": 0,
"windunit": 0,
"pressureunit": 0,
"feel_like_algo": 0
}
}
},
"status": "ok",
"time_exec": 0.095954179763794,
"time_server": 1559463229
}

View file

@ -0,0 +1,392 @@
{
"status": "ok",
"time_server": 1560248397,
"time_exec": 0,
"body": [
{
"_id": "70:ee:50:36:94:7c",
"place": {
"location": [
8.791382999999996,
50.2136394
],
"timezone": "Europe/Berlin",
"country": "DE",
"altitude": 132
},
"mark": 14,
"measures": {
"02:00:00:36:f2:94": {
"res": {
"1560248022": [
21.4,
62
]
},
"type": [
"temperature",
"humidity"
]
},
"70:ee:50:36:94:7c": {
"res": {
"1560248030": [
1010.6
]
},
"type": [
"pressure"
]
},
"05:00:00:05:33:84": {
"rain_60min": 0.2,
"rain_24h": 12.322000000000001,
"rain_live": 0.5,
"rain_timeutc": 1560248022
}
},
"modules": [
"05:00:00:05:33:84",
"02:00:00:36:f2:94"
],
"module_types": {
"05:00:00:05:33:84": "NAModule3",
"02:00:00:36:f2:94": "NAModule1"
}
},
{
"_id": "70:ee:50:1f:68:9e",
"place": {
"location": [
8.795445200000017,
50.2130169
],
"timezone": "Europe/Berlin",
"country": "DE",
"altitude": 125
},
"mark": 14,
"measures": {
"02:00:00:1f:82:28": {
"res": {
"1560248312": [
21.1,
69
]
},
"type": [
"temperature",
"humidity"
]
},
"70:ee:50:1f:68:9e": {
"res": {
"1560248344": [
1007.3
]
},
"type": [
"pressure"
]
},
"05:00:00:02:bb:6e": {
"rain_60min": 0,
"rain_24h": 9.999,
"rain_live": 0,
"rain_timeutc": 1560248344
}
},
"modules": [
"02:00:00:1f:82:28",
"05:00:00:02:bb:6e"
],
"module_types": {
"02:00:00:1f:82:28": "NAModule1",
"05:00:00:02:bb:6e": "NAModule3"
}
},
{
"_id": "70:ee:50:27:25:b0",
"place": {
"location": [
8.7807159,
50.1946167
],
"timezone": "Europe/Berlin",
"country": "DE",
"altitude": 112
},
"mark": 14,
"measures": {
"02:00:00:27:19:b2": {
"res": {
"1560247889": [
23.2,
60
]
},
"type": [
"temperature",
"humidity"
]
},
"70:ee:50:27:25:b0": {
"res": {
"1560247907": [
1012.8
]
},
"type": [
"pressure"
]
},
"05:00:00:03:5d:2e": {
"rain_60min": 0,
"rain_24h": 11.716000000000001,
"rain_live": 0,
"rain_timeutc": 1560247896
}
},
"modules": [
"02:00:00:27:19:b2",
"05:00:00:03:5d:2e"
],
"module_types": {
"02:00:00:27:19:b2": "NAModule1",
"05:00:00:03:5d:2e": "NAModule3"
}
},
{
"_id": "70:ee:50:04:ed:7a",
"place": {
"location": [
8.785034,
50.192169
],
"timezone": "Europe/Berlin",
"country": "DE",
"altitude": 112
},
"mark": 14,
"measures": {
"02:00:00:04:c2:2e": {
"res": {
"1560248137": [
19.8,
76
]
},
"type": [
"temperature",
"humidity"
]
},
"70:ee:50:04:ed:7a": {
"res": {
"1560248152": [
1005.4
]
},
"type": [
"pressure"
]
}
},
"modules": [
"02:00:00:04:c2:2e"
],
"module_types": {
"02:00:00:04:c2:2e": "NAModule1"
}
},
{
"_id": "70:ee:50:27:9f:2c",
"place": {
"location": [
8.785342,
50.193573
],
"timezone": "Europe/Berlin",
"country": "DE",
"altitude": 116
},
"mark": 1,
"measures": {
"02:00:00:27:aa:70": {
"res": {
"1560247821": [
25.5,
56
]
},
"type": [
"temperature",
"humidity"
]
},
"70:ee:50:27:9f:2c": {
"res": {
"1560247853": [
1010.6
]
},
"type": [
"pressure"
]
}
},
"modules": [
"02:00:00:27:aa:70"
],
"module_types": {
"02:00:00:27:aa:70": "NAModule1"
}
},
{
"_id": "70:ee:50:01:20:fa",
"place": {
"location": [
8.7953,
50.195241
],
"timezone": "Europe/Berlin",
"country": "DE",
"altitude": 119
},
"mark": 1,
"measures": {
"02:00:00:00:f7:ba": {
"res": {
"1560247831": [
27.4,
58
]
},
"type": [
"temperature",
"humidity"
]
},
"70:ee:50:01:20:fa": {
"res": {
"1560247876": [
1014.4
]
},
"type": [
"pressure"
]
}
},
"modules": [
"02:00:00:00:f7:ba"
],
"module_types": {
"02:00:00:00:f7:ba": "NAModule1"
}
},
{
"_id": "70:ee:50:3c:02:78",
"place": {
"location": [
8.795953681700666,
50.19530139868166
],
"timezone": "Europe/Berlin",
"country": "DE",
"altitude": 119
},
"mark": 7,
"measures": {
"02:00:00:3c:21:f2": {
"res": {
"1560248225": [
23.3,
58
]
},
"type": [
"temperature",
"humidity"
]
},
"70:ee:50:3c:02:78": {
"res": {
"1560248270": [
1011.7
]
},
"type": [
"pressure"
]
}
},
"modules": [
"02:00:00:3c:21:f2"
],
"module_types": {
"02:00:00:3c:21:f2": "NAModule1"
}
},
{
"_id": "70:ee:50:36:a9:fc",
"place": {
"location": [
8.801164269110814,
50.19596181704958
],
"timezone": "Europe/Berlin",
"country": "DE",
"altitude": 113
},
"mark": 14,
"measures": {
"02:00:00:36:a9:50": {
"res": {
"1560248145": [
20.1,
67
]
},
"type": [
"temperature",
"humidity"
]
},
"70:ee:50:36:a9:fc": {
"res": {
"1560248191": [
1010
]
},
"type": [
"pressure"
]
},
"05:00:00:02:92:82": {
"rain_60min": 0,
"rain_24h": 11.009,
"rain_live": 0,
"rain_timeutc": 1560248184
},
"06:00:00:03:19:76": {
"wind_strength": 15,
"wind_angle": 17,
"gust_strength": 31,
"gust_angle": 217,
"wind_timeutc": 1560248190
}
},
"modules": [
"05:00:00:02:92:82",
"02:00:00:36:a9:50",
"06:00:00:03:19:76"
],
"module_types": {
"05:00:00:02:92:82": "NAModule3",
"02:00:00:36:a9:50": "NAModule1",
"06:00:00:03:19:76": "NAModule2"
}
}
]
}