Merge pull request #52745 from home-assistant/rc

2021.7.1
This commit is contained in:
Franck Nijhof 2021-07-08 16:40:16 +02:00 committed by GitHub
commit 27c6fe4376
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 198 additions and 148 deletions

View File

@ -634,6 +634,7 @@ omit =
homeassistant/components/mjpeg/camera.py homeassistant/components/mjpeg/camera.py
homeassistant/components/mochad/* homeassistant/components/mochad/*
homeassistant/components/modbus/climate.py homeassistant/components/modbus/climate.py
homeassistant/components/modbus/modbus.py
homeassistant/components/modem_callerid/sensor.py homeassistant/components/modem_callerid/sensor.py
homeassistant/components/motion_blinds/__init__.py homeassistant/components/motion_blinds/__init__.py
homeassistant/components/motion_blinds/const.py homeassistant/components/motion_blinds/const.py

View File

@ -59,8 +59,8 @@ def setup(hass, config):
for device in devices: for device in devices:
_LOGGER.info( _LOGGER.info(
"Discovered Ecovacs device on account: %s with nickname %s", "Discovered Ecovacs device on account: %s with nickname %s",
device["did"], device.get("did"),
device["nick"], device.get("nick"),
) )
vacbot = VacBot( vacbot = VacBot(
ecovacs_api.uid, ecovacs_api.uid,
@ -77,7 +77,8 @@ def setup(hass, config):
"""Shut down open connections to Ecovacs XMPP server.""" """Shut down open connections to Ecovacs XMPP server."""
for device in hass.data[ECOVACS_DEVICES]: for device in hass.data[ECOVACS_DEVICES]:
_LOGGER.info( _LOGGER.info(
"Shutting down connection to Ecovacs device %s", device.vacuum["did"] "Shutting down connection to Ecovacs device %s",
device.vacuum.get("did"),
) )
device.disconnect() device.disconnect()

View File

@ -77,7 +77,7 @@ class EsphomeCamera(Camera, EsphomeBaseEntity):
await self._image_cond.wait() await self._image_cond.wait()
if not self.available: if not self.available:
return None return None
return self._state.image[:] return self._state.data[:]
async def _async_camera_stream_image(self) -> bytes | None: async def _async_camera_stream_image(self) -> bytes | None:
"""Return a single camera image in a stream.""" """Return a single camera image in a stream."""
@ -88,7 +88,7 @@ class EsphomeCamera(Camera, EsphomeBaseEntity):
await self._image_cond.wait() await self._image_cond.wait()
if not self.available: if not self.available:
return None return None
return self._state.image[:] return self._state.data[:]
async def handle_async_mjpeg_stream(self, request): async def handle_async_mjpeg_stream(self, request):
"""Serve an HTTP MJPEG stream from the camera.""" """Serve an HTTP MJPEG stream from the camera."""

View File

@ -3,7 +3,7 @@
"name": "ESPHome", "name": "ESPHome",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/esphome", "documentation": "https://www.home-assistant.io/integrations/esphome",
"requirements": ["aioesphomeapi==4.0.1"], "requirements": ["aioesphomeapi==5.0.0"],
"zeroconf": ["_esphomelib._tcp.local."], "zeroconf": ["_esphomelib._tcp.local."],
"codeowners": ["@OttoWinter", "@jesserockz"], "codeowners": ["@OttoWinter", "@jesserockz"],
"after_dependencies": ["zeroconf", "tag"], "after_dependencies": ["zeroconf", "tag"],

View File

@ -1,6 +1,8 @@
"""Support for the Forecast.Solar sensor service.""" """Support for the Forecast.Solar sensor service."""
from __future__ import annotations from __future__ import annotations
from datetime import datetime
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN, SensorEntity from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN, SensorEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_IDENTIFIERS, ATTR_MANUFACTURER, ATTR_NAME from homeassistant.const import ATTR_IDENTIFIERS, ATTR_MANUFACTURER, ATTR_NAME
@ -64,5 +66,7 @@ class ForecastSolarSensorEntity(CoordinatorEntity, SensorEntity):
@property @property
def state(self) -> StateType: def state(self) -> StateType:
"""Return the state of the sensor.""" """Return the state of the sensor."""
state: StateType = getattr(self.coordinator.data, self._sensor.key) state: StateType | datetime = getattr(self.coordinator.data, self._sensor.key)
if isinstance(state, datetime):
return state.isoformat()
return state return state

View File

@ -195,12 +195,13 @@ class FritzBoxTools:
"""Scan for new devices and return a list of found device ids.""" """Scan for new devices and return a list of found device ids."""
_LOGGER.debug("Checking devices for FRITZ!Box router %s", self.host) _LOGGER.debug("Checking devices for FRITZ!Box router %s", self.host)
_default_consider_home = DEFAULT_CONSIDER_HOME.total_seconds()
if self._options: if self._options:
consider_home = self._options.get( consider_home = self._options.get(
CONF_CONSIDER_HOME, DEFAULT_CONSIDER_HOME.total_seconds() CONF_CONSIDER_HOME, _default_consider_home
) )
else: else:
consider_home = DEFAULT_CONSIDER_HOME consider_home = _default_consider_home
new_device = False new_device = False
for known_host in self._update_info(): for known_host in self._update_info():

View File

@ -66,9 +66,6 @@ class KNXFan(KnxEntity, FanEntity):
# FanSpeedMode.STEP if max_step is set # FanSpeedMode.STEP if max_step is set
self._step_range: tuple[int, int] | None = (1, max_step) if max_step else None self._step_range: tuple[int, int] | None = (1, max_step) if max_step else None
self._attr_supported_features = SUPPORT_SET_SPEED
if self._device.supports_oscillation:
self._attr_supported_features |= SUPPORT_OSCILLATE
self._attr_unique_id = str(self._device.speed.group_address) self._attr_unique_id = str(self._device.speed.group_address)
async def async_set_percentage(self, percentage: int) -> None: async def async_set_percentage(self, percentage: int) -> None:
@ -79,6 +76,16 @@ class KNXFan(KnxEntity, FanEntity):
else: else:
await self._device.set_speed(percentage) await self._device.set_speed(percentage)
@property
def supported_features(self) -> int:
"""Flag supported features."""
flags = SUPPORT_SET_SPEED
if self._device.supports_oscillation:
flags |= SUPPORT_OSCILLATE
return flags
@property @property
def percentage(self) -> int | None: def percentage(self) -> int | None:
"""Return the current speed as a percentage.""" """Return the current speed as a percentage."""

View File

@ -2,7 +2,7 @@
"domain": "lutron", "domain": "lutron",
"name": "Lutron", "name": "Lutron",
"documentation": "https://www.home-assistant.io/integrations/lutron", "documentation": "https://www.home-assistant.io/integrations/lutron",
"requirements": ["pylutron==0.2.7"], "requirements": ["pylutron==0.2.8"],
"codeowners": ["@JonGilmore"], "codeowners": ["@JonGilmore"],
"iot_class": "local_polling" "iot_class": "local_polling"
} }

View File

@ -47,7 +47,7 @@ async def async_setup_entry(
def _build_forecast_data(timestep): def _build_forecast_data(timestep):
data = {} data = {}
data[ATTR_FORECAST_TIME] = timestep.date data[ATTR_FORECAST_TIME] = timestep.date.isoformat()
if timestep.weather: if timestep.weather:
data[ATTR_FORECAST_CONDITION] = _get_weather_condition(timestep.weather.value) data[ATTR_FORECAST_CONDITION] = _get_weather_condition(timestep.weather.value)
if timestep.precipitation: if timestep.precipitation:

View File

@ -310,6 +310,8 @@ class ModbusHub:
"""Convert async to sync pymodbus call.""" """Convert async to sync pymodbus call."""
if self._config_delay: if self._config_delay:
return None return None
if not self._client:
return None
if not self._client.is_socket_open(): if not self._client.is_socket_open():
return None return None
async with self._lock: async with self._lock:

View File

@ -59,6 +59,9 @@ async def async_setup_entry(hass, entry, async_add_entities):
vol.Optional(ATTR_UNLATCH, default=False): cv.boolean, vol.Optional(ATTR_UNLATCH, default=False): cv.boolean,
}, },
"lock_n_go", "lock_n_go",
)
platform.async_register_entity_service(
"set_continuous_mode", "set_continuous_mode",
{ {
vol.Required(ATTR_ENABLE): cv.boolean, vol.Required(ATTR_ENABLE): cv.boolean,

View File

@ -191,6 +191,8 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator):
"""Get rain data from weather data.""" """Get rain data from weather data."""
if "all" in rain: if "all" in rain:
return round(rain["all"], 2) return round(rain["all"], 2)
if "3h" in rain:
return round(rain["3h"], 2)
if "1h" in rain: if "1h" in rain:
return round(rain["1h"], 2) return round(rain["1h"], 2)
return 0 return 0
@ -201,6 +203,8 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator):
if snow: if snow:
if "all" in snow: if "all" in snow:
return round(snow["all"], 2) return round(snow["all"], 2)
if "3h" in snow:
return round(snow["3h"], 2)
if "1h" in snow: if "1h" in snow:
return round(snow["1h"], 2) return round(snow["1h"], 2)
return 0 return 0

View File

@ -3,7 +3,7 @@
"name": "SimpliSafe", "name": "SimpliSafe",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/simplisafe", "documentation": "https://www.home-assistant.io/integrations/simplisafe",
"requirements": ["simplisafe-python==11.0.0"], "requirements": ["simplisafe-python==11.0.1"],
"codeowners": ["@bachya"], "codeowners": ["@bachya"],
"iot_class": "cloud_polling" "iot_class": "cloud_polling"
} }

View File

@ -52,14 +52,17 @@ CONFIG_SCHEMA = vol.Schema(
{ {
DOMAIN: vol.Schema( DOMAIN: vol.Schema(
{ {
MP_DOMAIN: vol.Schema( MP_DOMAIN: vol.All(
{ cv.deprecated(CONF_INTERFACE_ADDR),
vol.Optional(CONF_ADVERTISE_ADDR): cv.string, vol.Schema(
vol.Optional(CONF_INTERFACE_ADDR): cv.string, {
vol.Optional(CONF_HOSTS): vol.All( vol.Optional(CONF_ADVERTISE_ADDR): cv.string,
cv.ensure_list_csv, [cv.string] vol.Optional(CONF_INTERFACE_ADDR): cv.string,
), vol.Optional(CONF_HOSTS): vol.All(
} cv.ensure_list_csv, [cv.string]
),
}
),
) )
} }
) )
@ -126,6 +129,13 @@ async def async_setup_entry( # noqa: C901
if advertise_addr: if advertise_addr:
pysonos.config.EVENT_ADVERTISE_IP = advertise_addr pysonos.config.EVENT_ADVERTISE_IP = advertise_addr
if deprecated_address := config.get(CONF_INTERFACE_ADDR):
_LOGGER.warning(
"'%s' is deprecated, enable %s in the Network integration (https://www.home-assistant.io/integrations/network/)",
CONF_INTERFACE_ADDR,
deprecated_address,
)
async def _async_stop_event_listener(event: Event) -> None: async def _async_stop_event_listener(event: Event) -> None:
await asyncio.gather( await asyncio.gather(
*[speaker.async_unsubscribe() for speaker in data.discovered.values()], *[speaker.async_unsubscribe() for speaker in data.discovered.values()],

View File

@ -71,6 +71,7 @@ SUBSCRIPTION_SERVICES = [
"zoneGroupTopology", "zoneGroupTopology",
] ]
UNAVAILABLE_VALUES = {"", "NOT_IMPLEMENTED", None} UNAVAILABLE_VALUES = {"", "NOT_IMPLEMENTED", None}
UNUSED_DEVICE_KEYS = ["SPID", "TargetRoomName"]
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -407,6 +408,10 @@ class SonosSpeaker:
"""Update device properties from an event.""" """Update device properties from an event."""
if more_info := event.variables.get("more_info"): if more_info := event.variables.get("more_info"):
battery_dict = dict(x.split(":") for x in more_info.split(",")) battery_dict = dict(x.split(":") for x in more_info.split(","))
for unused in UNUSED_DEVICE_KEYS:
battery_dict.pop(unused, None)
if not battery_dict:
return
if "BattChg" not in battery_dict: if "BattChg" not in battery_dict:
_LOGGER.debug( _LOGGER.debug(
"Unknown device properties update for %s (%s), please report an issue: '%s'", "Unknown device properties update for %s (%s), please report an issue: '%s'",

View File

@ -43,7 +43,6 @@ from .const import (
) )
from .core import PROVIDERS, IdleTimer, StreamOutput from .core import PROVIDERS, IdleTimer, StreamOutput
from .hls import async_setup_hls from .hls import async_setup_hls
from .recorder import RecorderOutput
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -265,6 +264,10 @@ class Stream:
) -> None: ) -> None:
"""Make a .mp4 recording from a provided stream.""" """Make a .mp4 recording from a provided stream."""
# Keep import here so that we can import stream integration without installing reqs
# pylint: disable=import-outside-toplevel
from .recorder import RecorderOutput
# Check for file access # Check for file access
if not self.hass.config.is_allowed_path(video_path): if not self.hass.config.is_allowed_path(video_path):
raise HomeAssistantError(f"Can't write {video_path}, no access to path!") raise HomeAssistantError(f"Can't write {video_path}, no access to path!")

View File

@ -3,7 +3,7 @@
"name": "Belkin WeMo", "name": "Belkin WeMo",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/wemo", "documentation": "https://www.home-assistant.io/integrations/wemo",
"requirements": ["pywemo==0.6.3"], "requirements": ["pywemo==0.6.5"],
"ssdp": [ "ssdp": [
{ {
"manufacturer": "Belkin International Inc." "manufacturer": "Belkin International Inc."

View File

@ -5,7 +5,7 @@ from typing import Final
MAJOR_VERSION: Final = 2021 MAJOR_VERSION: Final = 2021
MINOR_VERSION: Final = 7 MINOR_VERSION: Final = 7
PATCH_VERSION: Final = "0" PATCH_VERSION: Final = "1"
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
__version__: Final = f"{__short_version__}.{PATCH_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}"
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0) REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 8, 0)

View File

@ -160,7 +160,7 @@ aioeafm==0.1.2
aioemonitor==1.0.5 aioemonitor==1.0.5
# homeassistant.components.esphome # homeassistant.components.esphome
aioesphomeapi==4.0.1 aioesphomeapi==5.0.0
# homeassistant.components.flo # homeassistant.components.flo
aioflo==0.4.1 aioflo==0.4.1
@ -1562,7 +1562,7 @@ pyloopenergy==0.2.1
pylutron-caseta==0.10.0 pylutron-caseta==0.10.0
# homeassistant.components.lutron # homeassistant.components.lutron
pylutron==0.2.7 pylutron==0.2.8
# homeassistant.components.mailgun # homeassistant.components.mailgun
pymailgunner==1.4 pymailgunner==1.4
@ -1966,7 +1966,7 @@ pyvolumio==0.1.3
pywebpush==1.9.2 pywebpush==1.9.2
# homeassistant.components.wemo # homeassistant.components.wemo
pywemo==0.6.3 pywemo==0.6.5
# homeassistant.components.wilight # homeassistant.components.wilight
pywilight==0.0.70 pywilight==0.0.70
@ -2102,7 +2102,7 @@ simplehound==0.3
simplepush==1.1.4 simplepush==1.1.4
# homeassistant.components.simplisafe # homeassistant.components.simplisafe
simplisafe-python==11.0.0 simplisafe-python==11.0.1
# homeassistant.components.sisyphus # homeassistant.components.sisyphus
sisyphus-control==3.0 sisyphus-control==3.0

View File

@ -100,7 +100,7 @@ aioeafm==0.1.2
aioemonitor==1.0.5 aioemonitor==1.0.5
# homeassistant.components.esphome # homeassistant.components.esphome
aioesphomeapi==4.0.1 aioesphomeapi==5.0.0
# homeassistant.components.flo # homeassistant.components.flo
aioflo==0.4.1 aioflo==0.4.1
@ -1084,7 +1084,7 @@ pyvolumio==0.1.3
pywebpush==1.9.2 pywebpush==1.9.2
# homeassistant.components.wemo # homeassistant.components.wemo
pywemo==0.6.3 pywemo==0.6.5
# homeassistant.components.wilight # homeassistant.components.wilight
pywilight==0.0.70 pywilight==0.0.70
@ -1148,7 +1148,7 @@ sharkiqpy==0.1.8
simplehound==0.3 simplehound==0.3
# homeassistant.components.simplisafe # homeassistant.components.simplisafe
simplisafe-python==11.0.0 simplisafe-python==11.0.1
# homeassistant.components.slack # homeassistant.components.slack
slackclient==2.5.0 slackclient==2.5.0

View File

@ -70,7 +70,7 @@ async def test_sensors(
assert entry assert entry
assert state assert state
assert entry.unique_id == f"{entry_id}_power_highest_peak_time_today" assert entry.unique_id == f"{entry_id}_power_highest_peak_time_today"
assert state.state == "2021-06-27 13:00:00+00:00" assert state.state == "2021-06-27T13:00:00+00:00"
assert state.attributes.get(ATTR_FRIENDLY_NAME) == "Highest Power Peak Time - Today" assert state.attributes.get(ATTR_FRIENDLY_NAME) == "Highest Power Peak Time - Today"
assert state.attributes.get(ATTR_STATE_CLASS) is None assert state.attributes.get(ATTR_STATE_CLASS) is None
assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_TIMESTAMP assert state.attributes.get(ATTR_DEVICE_CLASS) == DEVICE_CLASS_TIMESTAMP
@ -82,7 +82,7 @@ async def test_sensors(
assert entry assert entry
assert state assert state
assert entry.unique_id == f"{entry_id}_power_highest_peak_time_tomorrow" assert entry.unique_id == f"{entry_id}_power_highest_peak_time_tomorrow"
assert state.state == "2021-06-27 14:00:00+00:00" assert state.state == "2021-06-27T14:00:00+00:00"
assert ( assert (
state.attributes.get(ATTR_FRIENDLY_NAME) == "Highest Power Peak Time - Tomorrow" state.attributes.get(ATTR_FRIENDLY_NAME) == "Highest Power Peak Time - Tomorrow"
) )

View File

@ -2,8 +2,7 @@
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S%z" TEST_DATETIME_STRING = "2020-04-25T12:00:00+00:00"
TEST_DATETIME_STRING = "2020-04-25 12:00:00+0000"
TEST_API_KEY = "test-metoffice-api-key" TEST_API_KEY = "test-metoffice-api-key"

View File

@ -6,7 +6,6 @@ from homeassistant.components.metoffice.const import ATTRIBUTION, DOMAIN
from . import NewDateTime from . import NewDateTime
from .const import ( from .const import (
DATETIME_FORMAT,
KINGSLYNN_SENSOR_RESULTS, KINGSLYNN_SENSOR_RESULTS,
METOFFICE_CONFIG_KINGSLYNN, METOFFICE_CONFIG_KINGSLYNN,
METOFFICE_CONFIG_WAVERTREE, METOFFICE_CONFIG_WAVERTREE,
@ -54,13 +53,10 @@ async def test_one_sensor_site_running(hass, requests_mock, legacy_patchable_tim
for running_id in running_sensor_ids: for running_id in running_sensor_ids:
sensor = hass.states.get(running_id) sensor = hass.states.get(running_id)
sensor_id = sensor.attributes.get("sensor_id") sensor_id = sensor.attributes.get("sensor_id")
sensor_name, sensor_value = WAVERTREE_SENSOR_RESULTS[sensor_id] _, sensor_value = WAVERTREE_SENSOR_RESULTS[sensor_id]
assert sensor.state == sensor_value assert sensor.state == sensor_value
assert ( assert sensor.attributes.get("last_update").isoformat() == TEST_DATETIME_STRING
sensor.attributes.get("last_update").strftime(DATETIME_FORMAT)
== TEST_DATETIME_STRING
)
assert sensor.attributes.get("site_id") == "354107" assert sensor.attributes.get("site_id") == "354107"
assert sensor.attributes.get("site_name") == TEST_SITE_NAME_WAVERTREE assert sensor.attributes.get("site_name") == TEST_SITE_NAME_WAVERTREE
assert sensor.attributes.get("attribution") == ATTRIBUTION assert sensor.attributes.get("attribution") == ATTRIBUTION
@ -115,11 +111,10 @@ async def test_two_sensor_sites_running(hass, requests_mock, legacy_patchable_ti
sensor = hass.states.get(running_id) sensor = hass.states.get(running_id)
sensor_id = sensor.attributes.get("sensor_id") sensor_id = sensor.attributes.get("sensor_id")
if sensor.attributes.get("site_id") == "354107": if sensor.attributes.get("site_id") == "354107":
sensor_name, sensor_value = WAVERTREE_SENSOR_RESULTS[sensor_id] _, sensor_value = WAVERTREE_SENSOR_RESULTS[sensor_id]
assert sensor.state == sensor_value assert sensor.state == sensor_value
assert ( assert (
sensor.attributes.get("last_update").strftime(DATETIME_FORMAT) sensor.attributes.get("last_update").isoformat() == TEST_DATETIME_STRING
== TEST_DATETIME_STRING
) )
assert sensor.attributes.get("sensor_id") == sensor_id assert sensor.attributes.get("sensor_id") == sensor_id
assert sensor.attributes.get("site_id") == "354107" assert sensor.attributes.get("site_id") == "354107"
@ -127,11 +122,10 @@ async def test_two_sensor_sites_running(hass, requests_mock, legacy_patchable_ti
assert sensor.attributes.get("attribution") == ATTRIBUTION assert sensor.attributes.get("attribution") == ATTRIBUTION
else: else:
sensor_name, sensor_value = KINGSLYNN_SENSOR_RESULTS[sensor_id] _, sensor_value = KINGSLYNN_SENSOR_RESULTS[sensor_id]
assert sensor.state == sensor_value assert sensor.state == sensor_value
assert ( assert (
sensor.attributes.get("last_update").strftime(DATETIME_FORMAT) sensor.attributes.get("last_update").isoformat() == TEST_DATETIME_STRING
== TEST_DATETIME_STRING
) )
assert sensor.attributes.get("sensor_id") == sensor_id assert sensor.attributes.get("sensor_id") == sensor_id
assert sensor.attributes.get("site_id") == "322380" assert sensor.attributes.get("site_id") == "322380"

View File

@ -9,7 +9,6 @@ from homeassistant.util import utcnow
from . import NewDateTime from . import NewDateTime
from .const import ( from .const import (
DATETIME_FORMAT,
METOFFICE_CONFIG_KINGSLYNN, METOFFICE_CONFIG_KINGSLYNN,
METOFFICE_CONFIG_WAVERTREE, METOFFICE_CONFIG_WAVERTREE,
WAVERTREE_SENSOR_RESULTS, WAVERTREE_SENSOR_RESULTS,
@ -74,11 +73,11 @@ async def test_site_cannot_update(hass, requests_mock, legacy_patchable_time):
await hass.config_entries.async_setup(entry.entry_id) await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
entity = hass.states.get("weather.met_office_wavertree_3_hourly") weather = hass.states.get("weather.met_office_wavertree_3_hourly")
assert entity assert weather
entity = hass.states.get("weather.met_office_wavertree_daily") weather = hass.states.get("weather.met_office_wavertree_daily")
assert entity assert weather
requests_mock.get("/public/data/val/wxfcs/all/json/354107?res=3hourly", text="") requests_mock.get("/public/data/val/wxfcs/all/json/354107?res=3hourly", text="")
requests_mock.get("/public/data/val/wxfcs/all/json/354107?res=daily", text="") requests_mock.get("/public/data/val/wxfcs/all/json/354107?res=daily", text="")
@ -87,11 +86,11 @@ async def test_site_cannot_update(hass, requests_mock, legacy_patchable_time):
async_fire_time_changed(hass, future_time) async_fire_time_changed(hass, future_time)
await hass.async_block_till_done() await hass.async_block_till_done()
entity = hass.states.get("weather.met_office_wavertree_3_hourly") weather = hass.states.get("weather.met_office_wavertree_3_hourly")
assert entity.state == STATE_UNAVAILABLE assert weather.state == STATE_UNAVAILABLE
entity = hass.states.get("weather.met_office_wavertree_daily") weather = hass.states.get("weather.met_office_wavertree_daily")
assert entity.state == STATE_UNAVAILABLE assert weather.state == STATE_UNAVAILABLE
@patch( @patch(
@ -126,50 +125,49 @@ async def test_one_weather_site_running(hass, requests_mock, legacy_patchable_ti
await hass.async_block_till_done() await hass.async_block_till_done()
# Wavertree 3-hourly weather platform expected results # Wavertree 3-hourly weather platform expected results
entity = hass.states.get("weather.met_office_wavertree_3_hourly") weather = hass.states.get("weather.met_office_wavertree_3_hourly")
assert entity assert weather
assert entity.state == "sunny" assert weather.state == "sunny"
assert entity.attributes.get("temperature") == 17 assert weather.attributes.get("temperature") == 17
assert entity.attributes.get("wind_speed") == 9 assert weather.attributes.get("wind_speed") == 9
assert entity.attributes.get("wind_bearing") == "SSE" assert weather.attributes.get("wind_bearing") == "SSE"
assert entity.attributes.get("visibility") == "Good - 10-20" assert weather.attributes.get("visibility") == "Good - 10-20"
assert entity.attributes.get("humidity") == 50 assert weather.attributes.get("humidity") == 50
# Forecasts added - just pick out 1 entry to check # Forecasts added - just pick out 1 entry to check
assert len(entity.attributes.get("forecast")) == 35 assert len(weather.attributes.get("forecast")) == 35
assert ( assert (
entity.attributes.get("forecast")[26]["datetime"].strftime(DATETIME_FORMAT) weather.attributes.get("forecast")[26]["datetime"]
== "2020-04-28 21:00:00+0000" == "2020-04-28T21:00:00+00:00"
) )
assert entity.attributes.get("forecast")[26]["condition"] == "cloudy" assert weather.attributes.get("forecast")[26]["condition"] == "cloudy"
assert entity.attributes.get("forecast")[26]["temperature"] == 10 assert weather.attributes.get("forecast")[26]["temperature"] == 10
assert entity.attributes.get("forecast")[26]["wind_speed"] == 4 assert weather.attributes.get("forecast")[26]["wind_speed"] == 4
assert entity.attributes.get("forecast")[26]["wind_bearing"] == "NNE" assert weather.attributes.get("forecast")[26]["wind_bearing"] == "NNE"
# Wavertree daily weather platform expected results # Wavertree daily weather platform expected results
entity = hass.states.get("weather.met_office_wavertree_daily") weather = hass.states.get("weather.met_office_wavertree_daily")
assert entity assert weather
assert entity.state == "sunny" assert weather.state == "sunny"
assert entity.attributes.get("temperature") == 19 assert weather.attributes.get("temperature") == 19
assert entity.attributes.get("wind_speed") == 9 assert weather.attributes.get("wind_speed") == 9
assert entity.attributes.get("wind_bearing") == "SSE" assert weather.attributes.get("wind_bearing") == "SSE"
assert entity.attributes.get("visibility") == "Good - 10-20" assert weather.attributes.get("visibility") == "Good - 10-20"
assert entity.attributes.get("humidity") == 50 assert weather.attributes.get("humidity") == 50
# Also has Forecasts added - again, just pick out 1 entry to check # Also has Forecasts added - again, just pick out 1 entry to check
assert len(entity.attributes.get("forecast")) == 8 assert len(weather.attributes.get("forecast")) == 8
assert ( assert (
entity.attributes.get("forecast")[7]["datetime"].strftime(DATETIME_FORMAT) weather.attributes.get("forecast")[7]["datetime"] == "2020-04-29T12:00:00+00:00"
== "2020-04-29 12:00:00+0000"
) )
assert entity.attributes.get("forecast")[7]["condition"] == "rainy" assert weather.attributes.get("forecast")[7]["condition"] == "rainy"
assert entity.attributes.get("forecast")[7]["temperature"] == 13 assert weather.attributes.get("forecast")[7]["temperature"] == 13
assert entity.attributes.get("forecast")[7]["wind_speed"] == 13 assert weather.attributes.get("forecast")[7]["wind_speed"] == 13
assert entity.attributes.get("forecast")[7]["wind_bearing"] == "SE" assert weather.attributes.get("forecast")[7]["wind_bearing"] == "SE"
@patch( @patch(
@ -216,93 +214,91 @@ async def test_two_weather_sites_running(hass, requests_mock, legacy_patchable_t
await hass.async_block_till_done() await hass.async_block_till_done()
# Wavertree 3-hourly weather platform expected results # Wavertree 3-hourly weather platform expected results
entity = hass.states.get("weather.met_office_wavertree_3_hourly") weather = hass.states.get("weather.met_office_wavertree_3_hourly")
assert entity assert weather
assert entity.state == "sunny" assert weather.state == "sunny"
assert entity.attributes.get("temperature") == 17 assert weather.attributes.get("temperature") == 17
assert entity.attributes.get("wind_speed") == 9 assert weather.attributes.get("wind_speed") == 9
assert entity.attributes.get("wind_bearing") == "SSE" assert weather.attributes.get("wind_bearing") == "SSE"
assert entity.attributes.get("visibility") == "Good - 10-20" assert weather.attributes.get("visibility") == "Good - 10-20"
assert entity.attributes.get("humidity") == 50 assert weather.attributes.get("humidity") == 50
# Forecasts added - just pick out 1 entry to check # Forecasts added - just pick out 1 entry to check
assert len(entity.attributes.get("forecast")) == 35 assert len(weather.attributes.get("forecast")) == 35
assert ( assert (
entity.attributes.get("forecast")[18]["datetime"].strftime(DATETIME_FORMAT) weather.attributes.get("forecast")[18]["datetime"]
== "2020-04-27 21:00:00+0000" == "2020-04-27T21:00:00+00:00"
) )
assert entity.attributes.get("forecast")[18]["condition"] == "sunny" assert weather.attributes.get("forecast")[18]["condition"] == "sunny"
assert entity.attributes.get("forecast")[18]["temperature"] == 9 assert weather.attributes.get("forecast")[18]["temperature"] == 9
assert entity.attributes.get("forecast")[18]["wind_speed"] == 4 assert weather.attributes.get("forecast")[18]["wind_speed"] == 4
assert entity.attributes.get("forecast")[18]["wind_bearing"] == "NW" assert weather.attributes.get("forecast")[18]["wind_bearing"] == "NW"
# Wavertree daily weather platform expected results # Wavertree daily weather platform expected results
entity = hass.states.get("weather.met_office_wavertree_daily") weather = hass.states.get("weather.met_office_wavertree_daily")
assert entity assert weather
assert entity.state == "sunny" assert weather.state == "sunny"
assert entity.attributes.get("temperature") == 19 assert weather.attributes.get("temperature") == 19
assert entity.attributes.get("wind_speed") == 9 assert weather.attributes.get("wind_speed") == 9
assert entity.attributes.get("wind_bearing") == "SSE" assert weather.attributes.get("wind_bearing") == "SSE"
assert entity.attributes.get("visibility") == "Good - 10-20" assert weather.attributes.get("visibility") == "Good - 10-20"
assert entity.attributes.get("humidity") == 50 assert weather.attributes.get("humidity") == 50
# Also has Forecasts added - again, just pick out 1 entry to check # Also has Forecasts added - again, just pick out 1 entry to check
assert len(entity.attributes.get("forecast")) == 8 assert len(weather.attributes.get("forecast")) == 8
assert ( assert (
entity.attributes.get("forecast")[7]["datetime"].strftime(DATETIME_FORMAT) weather.attributes.get("forecast")[7]["datetime"] == "2020-04-29T12:00:00+00:00"
== "2020-04-29 12:00:00+0000"
) )
assert entity.attributes.get("forecast")[7]["condition"] == "rainy" assert weather.attributes.get("forecast")[7]["condition"] == "rainy"
assert entity.attributes.get("forecast")[7]["temperature"] == 13 assert weather.attributes.get("forecast")[7]["temperature"] == 13
assert entity.attributes.get("forecast")[7]["wind_speed"] == 13 assert weather.attributes.get("forecast")[7]["wind_speed"] == 13
assert entity.attributes.get("forecast")[7]["wind_bearing"] == "SE" assert weather.attributes.get("forecast")[7]["wind_bearing"] == "SE"
# King's Lynn 3-hourly weather platform expected results # King's Lynn 3-hourly weather platform expected results
entity = hass.states.get("weather.met_office_king_s_lynn_3_hourly") weather = hass.states.get("weather.met_office_king_s_lynn_3_hourly")
assert entity assert weather
assert entity.state == "sunny" assert weather.state == "sunny"
assert entity.attributes.get("temperature") == 14 assert weather.attributes.get("temperature") == 14
assert entity.attributes.get("wind_speed") == 2 assert weather.attributes.get("wind_speed") == 2
assert entity.attributes.get("wind_bearing") == "E" assert weather.attributes.get("wind_bearing") == "E"
assert entity.attributes.get("visibility") == "Very Good - 20-40" assert weather.attributes.get("visibility") == "Very Good - 20-40"
assert entity.attributes.get("humidity") == 60 assert weather.attributes.get("humidity") == 60
# Also has Forecast added - just pick out 1 entry to check # Also has Forecast added - just pick out 1 entry to check
assert len(entity.attributes.get("forecast")) == 35 assert len(weather.attributes.get("forecast")) == 35
assert ( assert (
entity.attributes.get("forecast")[18]["datetime"].strftime(DATETIME_FORMAT) weather.attributes.get("forecast")[18]["datetime"]
== "2020-04-27 21:00:00+0000" == "2020-04-27T21:00:00+00:00"
) )
assert entity.attributes.get("forecast")[18]["condition"] == "cloudy" assert weather.attributes.get("forecast")[18]["condition"] == "cloudy"
assert entity.attributes.get("forecast")[18]["temperature"] == 10 assert weather.attributes.get("forecast")[18]["temperature"] == 10
assert entity.attributes.get("forecast")[18]["wind_speed"] == 7 assert weather.attributes.get("forecast")[18]["wind_speed"] == 7
assert entity.attributes.get("forecast")[18]["wind_bearing"] == "SE" assert weather.attributes.get("forecast")[18]["wind_bearing"] == "SE"
# King's Lynn daily weather platform expected results # King's Lynn daily weather platform expected results
entity = hass.states.get("weather.met_office_king_s_lynn_daily") weather = hass.states.get("weather.met_office_king_s_lynn_daily")
assert entity assert weather
assert entity.state == "cloudy" assert weather.state == "cloudy"
assert entity.attributes.get("temperature") == 9 assert weather.attributes.get("temperature") == 9
assert entity.attributes.get("wind_speed") == 4 assert weather.attributes.get("wind_speed") == 4
assert entity.attributes.get("wind_bearing") == "ESE" assert weather.attributes.get("wind_bearing") == "ESE"
assert entity.attributes.get("visibility") == "Very Good - 20-40" assert weather.attributes.get("visibility") == "Very Good - 20-40"
assert entity.attributes.get("humidity") == 75 assert weather.attributes.get("humidity") == 75
# All should have Forecast added - again, just picking out 1 entry to check # All should have Forecast added - again, just picking out 1 entry to check
assert len(entity.attributes.get("forecast")) == 8 assert len(weather.attributes.get("forecast")) == 8
assert ( assert (
entity.attributes.get("forecast")[5]["datetime"].strftime(DATETIME_FORMAT) weather.attributes.get("forecast")[5]["datetime"] == "2020-04-28T12:00:00+00:00"
== "2020-04-28 12:00:00+0000"
) )
assert entity.attributes.get("forecast")[5]["condition"] == "cloudy" assert weather.attributes.get("forecast")[5]["condition"] == "cloudy"
assert entity.attributes.get("forecast")[5]["temperature"] == 11 assert weather.attributes.get("forecast")[5]["temperature"] == 11
assert entity.attributes.get("forecast")[5]["wind_speed"] == 7 assert weather.attributes.get("forecast")[5]["wind_speed"] == 7
assert entity.attributes.get("forecast")[5]["wind_bearing"] == "ESE" assert weather.attributes.get("forecast")[5]["wind_bearing"] == "ESE"

View File

@ -103,3 +103,23 @@ async def test_device_payload_without_battery(
await hass.async_block_till_done() await hass.async_block_till_done()
assert bad_payload in caplog.text assert bad_payload in caplog.text
async def test_device_payload_without_battery_and_ignored_keys(
hass, config_entry, config, soco, battery_event, caplog
):
"""Test device properties event update without battery info and ignored keys."""
soco.get_battery_info.return_value = None
await setup_platform(hass, config_entry, config)
subscription = soco.deviceProperties.subscribe.return_value
sub_callback = subscription.callback
ignored_payload = "SPID:InCeiling,TargetRoomName:Bouncy House"
battery_event.variables["more_info"] = ignored_payload
sub_callback(battery_event)
await hass.async_block_till_done()
assert ignored_payload not in caplog.text