forked from home-assistant/core
Compare commits
44 Commits
2022.7.0b4
...
2022.7.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5080246fb6 | ||
|
|
a3abe7456e | ||
|
|
14c6b8d41f | ||
|
|
ea709912d4 | ||
|
|
cb5658d7dc | ||
|
|
7b1cad223d | ||
|
|
e80fd4fc78 | ||
|
|
88d723736f | ||
|
|
dc33d5db82 | ||
|
|
7ffc60fb2c | ||
|
|
d539acf2ff | ||
|
|
5018b91f6c | ||
|
|
b73cc32ffb | ||
|
|
937d0d731d | ||
|
|
bac9af50df | ||
|
|
0da09ba47e | ||
|
|
cd7f2eb73e | ||
|
|
8b01c132c1 | ||
|
|
ed6a65156c | ||
|
|
5a7e506c38 | ||
|
|
9514b0f100 | ||
|
|
fdc1b6ea9e | ||
|
|
174837dddf | ||
|
|
c9a31aab15 | ||
|
|
606a1b57e6 | ||
|
|
f6a23492fa | ||
|
|
414ea6fd8c | ||
|
|
fa220c5c25 | ||
|
|
bf5633fa43 | ||
|
|
eb0f8f9542 | ||
|
|
06c6ddb2d6 | ||
|
|
8e5b6ff185 | ||
|
|
380244fa7b | ||
|
|
9d3dde60ff | ||
|
|
519d15428c | ||
|
|
b277c28ed7 | ||
|
|
c7c8887719 | ||
|
|
06aa92b0b6 | ||
|
|
cd42555238 | ||
|
|
56e90dd30b | ||
|
|
89360516d7 | ||
|
|
9cbb684d50 | ||
|
|
43fe351f1b | ||
|
|
59aba0bc75 |
@@ -1,30 +1,18 @@
|
||||
"""The AEMET OpenData component."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from aemet_opendata.interface import AEMET
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY,
|
||||
CONF_LATITUDE,
|
||||
CONF_LONGITUDE,
|
||||
CONF_NAME,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import (
|
||||
CONF_STATION_UPDATES,
|
||||
DOMAIN,
|
||||
ENTRY_NAME,
|
||||
ENTRY_WEATHER_COORDINATOR,
|
||||
FORECAST_MODES,
|
||||
PLATFORMS,
|
||||
RENAMED_FORECAST_SENSOR_KEYS,
|
||||
)
|
||||
from .weather_update_coordinator import WeatherUpdateCoordinator
|
||||
|
||||
@@ -33,8 +21,6 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up AEMET OpenData as config entry."""
|
||||
await er.async_migrate_entries(hass, entry.entry_id, async_migrate_entity_entry)
|
||||
|
||||
name = entry.data[CONF_NAME]
|
||||
api_key = entry.data[CONF_API_KEY]
|
||||
latitude = entry.data[CONF_LATITUDE]
|
||||
@@ -74,24 +60,3 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
@callback
|
||||
def async_migrate_entity_entry(entry: er.RegistryEntry) -> dict[str, Any] | None:
|
||||
"""Migrate AEMET entity entries.
|
||||
|
||||
- Migrates unique ID from old forecast sensors to the new unique ID
|
||||
"""
|
||||
if entry.domain != Platform.SENSOR:
|
||||
return None
|
||||
for old_key, new_key in RENAMED_FORECAST_SENSOR_KEYS.items():
|
||||
for forecast_mode in FORECAST_MODES:
|
||||
old_suffix = f"-forecast-{forecast_mode}-{old_key}"
|
||||
if entry.unique_id.endswith(old_suffix):
|
||||
new_suffix = f"-forecast-{forecast_mode}-{new_key}"
|
||||
return {
|
||||
"new_unique_id": entry.unique_id.replace(old_suffix, new_suffix)
|
||||
}
|
||||
|
||||
# No migration needed
|
||||
return None
|
||||
|
||||
@@ -17,18 +17,6 @@ from homeassistant.components.weather import (
|
||||
ATTR_CONDITION_RAINY,
|
||||
ATTR_CONDITION_SNOWY,
|
||||
ATTR_CONDITION_SUNNY,
|
||||
ATTR_FORECAST_CONDITION,
|
||||
ATTR_FORECAST_NATIVE_PRECIPITATION,
|
||||
ATTR_FORECAST_NATIVE_TEMP,
|
||||
ATTR_FORECAST_NATIVE_TEMP_LOW,
|
||||
ATTR_FORECAST_NATIVE_WIND_SPEED,
|
||||
ATTR_FORECAST_PRECIPITATION,
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
ATTR_FORECAST_TEMP,
|
||||
ATTR_FORECAST_TEMP_LOW,
|
||||
ATTR_FORECAST_TIME,
|
||||
ATTR_FORECAST_WIND_BEARING,
|
||||
ATTR_FORECAST_WIND_SPEED,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
DEGREE,
|
||||
@@ -49,8 +37,16 @@ ENTRY_NAME = "name"
|
||||
ENTRY_WEATHER_COORDINATOR = "weather_coordinator"
|
||||
|
||||
ATTR_API_CONDITION = "condition"
|
||||
ATTR_API_FORECAST_CONDITION = "condition"
|
||||
ATTR_API_FORECAST_DAILY = "forecast-daily"
|
||||
ATTR_API_FORECAST_HOURLY = "forecast-hourly"
|
||||
ATTR_API_FORECAST_PRECIPITATION = "precipitation"
|
||||
ATTR_API_FORECAST_PRECIPITATION_PROBABILITY = "precipitation_probability"
|
||||
ATTR_API_FORECAST_TEMP = "temperature"
|
||||
ATTR_API_FORECAST_TEMP_LOW = "templow"
|
||||
ATTR_API_FORECAST_TIME = "datetime"
|
||||
ATTR_API_FORECAST_WIND_BEARING = "wind_bearing"
|
||||
ATTR_API_FORECAST_WIND_SPEED = "wind_speed"
|
||||
ATTR_API_HUMIDITY = "humidity"
|
||||
ATTR_API_PRESSURE = "pressure"
|
||||
ATTR_API_RAIN = "rain"
|
||||
@@ -162,14 +158,14 @@ CONDITIONS_MAP = {
|
||||
}
|
||||
|
||||
FORECAST_MONITORED_CONDITIONS = [
|
||||
ATTR_FORECAST_CONDITION,
|
||||
ATTR_FORECAST_NATIVE_PRECIPITATION,
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
ATTR_FORECAST_NATIVE_TEMP,
|
||||
ATTR_FORECAST_NATIVE_TEMP_LOW,
|
||||
ATTR_FORECAST_TIME,
|
||||
ATTR_FORECAST_WIND_BEARING,
|
||||
ATTR_FORECAST_NATIVE_WIND_SPEED,
|
||||
ATTR_API_FORECAST_CONDITION,
|
||||
ATTR_API_FORECAST_PRECIPITATION,
|
||||
ATTR_API_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
ATTR_API_FORECAST_TEMP,
|
||||
ATTR_API_FORECAST_TEMP_LOW,
|
||||
ATTR_API_FORECAST_TIME,
|
||||
ATTR_API_FORECAST_WIND_BEARING,
|
||||
ATTR_API_FORECAST_WIND_SPEED,
|
||||
]
|
||||
MONITORED_CONDITIONS = [
|
||||
ATTR_API_CONDITION,
|
||||
@@ -206,53 +202,47 @@ FORECAST_MODE_ATTR_API = {
|
||||
|
||||
FORECAST_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_CONDITION,
|
||||
key=ATTR_API_FORECAST_CONDITION,
|
||||
name="Condition",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_NATIVE_PRECIPITATION,
|
||||
key=ATTR_API_FORECAST_PRECIPITATION,
|
||||
name="Precipitation",
|
||||
native_unit_of_measurement=PRECIPITATION_MILLIMETERS_PER_HOUR,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
key=ATTR_API_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
name="Precipitation probability",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_NATIVE_TEMP,
|
||||
key=ATTR_API_FORECAST_TEMP,
|
||||
name="Temperature",
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_NATIVE_TEMP_LOW,
|
||||
key=ATTR_API_FORECAST_TEMP_LOW,
|
||||
name="Temperature Low",
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_TIME,
|
||||
key=ATTR_API_FORECAST_TIME,
|
||||
name="Time",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_WIND_BEARING,
|
||||
key=ATTR_API_FORECAST_WIND_BEARING,
|
||||
name="Wind bearing",
|
||||
native_unit_of_measurement=DEGREE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_NATIVE_WIND_SPEED,
|
||||
key=ATTR_API_FORECAST_WIND_SPEED,
|
||||
name="Wind speed",
|
||||
native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR,
|
||||
),
|
||||
)
|
||||
RENAMED_FORECAST_SENSOR_KEYS = {
|
||||
ATTR_FORECAST_PRECIPITATION: ATTR_FORECAST_NATIVE_PRECIPITATION,
|
||||
ATTR_FORECAST_TEMP: ATTR_FORECAST_NATIVE_TEMP,
|
||||
ATTR_FORECAST_TEMP_LOW: ATTR_FORECAST_NATIVE_TEMP_LOW,
|
||||
ATTR_FORECAST_WIND_SPEED: ATTR_FORECAST_NATIVE_WIND_SPEED,
|
||||
}
|
||||
WEATHER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_CONDITION,
|
||||
|
||||
@@ -10,7 +10,7 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import (
|
||||
ATTR_FORECAST_TIME,
|
||||
ATTR_API_FORECAST_TIME,
|
||||
ATTRIBUTION,
|
||||
DOMAIN,
|
||||
ENTRY_NAME,
|
||||
@@ -135,6 +135,6 @@ class AemetForecastSensor(AbstractAemetSensor):
|
||||
)
|
||||
if forecasts:
|
||||
forecast = forecasts[0].get(self.entity_description.key)
|
||||
if self.entity_description.key == ATTR_FORECAST_TIME:
|
||||
if self.entity_description.key == ATTR_API_FORECAST_TIME:
|
||||
forecast = dt_util.parse_datetime(forecast)
|
||||
return forecast
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
"""Support for the AEMET OpenData service."""
|
||||
from homeassistant.components.weather import WeatherEntity
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_FORECAST_CONDITION,
|
||||
ATTR_FORECAST_NATIVE_PRECIPITATION,
|
||||
ATTR_FORECAST_NATIVE_TEMP,
|
||||
ATTR_FORECAST_NATIVE_TEMP_LOW,
|
||||
ATTR_FORECAST_NATIVE_WIND_SPEED,
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
ATTR_FORECAST_TIME,
|
||||
ATTR_FORECAST_WIND_BEARING,
|
||||
WeatherEntity,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
LENGTH_MILLIMETERS,
|
||||
@@ -13,6 +23,14 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import (
|
||||
ATTR_API_CONDITION,
|
||||
ATTR_API_FORECAST_CONDITION,
|
||||
ATTR_API_FORECAST_PRECIPITATION,
|
||||
ATTR_API_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
ATTR_API_FORECAST_TEMP,
|
||||
ATTR_API_FORECAST_TEMP_LOW,
|
||||
ATTR_API_FORECAST_TIME,
|
||||
ATTR_API_FORECAST_WIND_BEARING,
|
||||
ATTR_API_FORECAST_WIND_SPEED,
|
||||
ATTR_API_HUMIDITY,
|
||||
ATTR_API_PRESSURE,
|
||||
ATTR_API_TEMPERATURE,
|
||||
@@ -24,10 +42,32 @@ from .const import (
|
||||
ENTRY_WEATHER_COORDINATOR,
|
||||
FORECAST_MODE_ATTR_API,
|
||||
FORECAST_MODE_DAILY,
|
||||
FORECAST_MODE_HOURLY,
|
||||
FORECAST_MODES,
|
||||
)
|
||||
from .weather_update_coordinator import WeatherUpdateCoordinator
|
||||
|
||||
FORECAST_MAP = {
|
||||
FORECAST_MODE_DAILY: {
|
||||
ATTR_API_FORECAST_CONDITION: ATTR_FORECAST_CONDITION,
|
||||
ATTR_API_FORECAST_PRECIPITATION_PROBABILITY: ATTR_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
ATTR_API_FORECAST_TEMP_LOW: ATTR_FORECAST_NATIVE_TEMP_LOW,
|
||||
ATTR_API_FORECAST_TEMP: ATTR_FORECAST_NATIVE_TEMP,
|
||||
ATTR_API_FORECAST_TIME: ATTR_FORECAST_TIME,
|
||||
ATTR_API_FORECAST_WIND_BEARING: ATTR_FORECAST_WIND_BEARING,
|
||||
ATTR_API_FORECAST_WIND_SPEED: ATTR_FORECAST_NATIVE_WIND_SPEED,
|
||||
},
|
||||
FORECAST_MODE_HOURLY: {
|
||||
ATTR_API_FORECAST_CONDITION: ATTR_FORECAST_CONDITION,
|
||||
ATTR_API_FORECAST_PRECIPITATION_PROBABILITY: ATTR_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
ATTR_API_FORECAST_PRECIPITATION: ATTR_FORECAST_NATIVE_PRECIPITATION,
|
||||
ATTR_API_FORECAST_TEMP: ATTR_FORECAST_NATIVE_TEMP,
|
||||
ATTR_API_FORECAST_TIME: ATTR_FORECAST_TIME,
|
||||
ATTR_API_FORECAST_WIND_BEARING: ATTR_FORECAST_WIND_BEARING,
|
||||
ATTR_API_FORECAST_WIND_SPEED: ATTR_FORECAST_NATIVE_WIND_SPEED,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
@@ -81,7 +121,12 @@ class AemetWeather(CoordinatorEntity[WeatherUpdateCoordinator], WeatherEntity):
|
||||
@property
|
||||
def forecast(self):
|
||||
"""Return the forecast array."""
|
||||
return self.coordinator.data[FORECAST_MODE_ATTR_API[self._forecast_mode]]
|
||||
forecasts = self.coordinator.data[FORECAST_MODE_ATTR_API[self._forecast_mode]]
|
||||
forecast_map = FORECAST_MAP[self._forecast_mode]
|
||||
return [
|
||||
{ha_key: forecast[api_key] for api_key, ha_key in forecast_map.items()}
|
||||
for forecast in forecasts
|
||||
]
|
||||
|
||||
@property
|
||||
def humidity(self):
|
||||
|
||||
@@ -42,23 +42,21 @@ from aemet_opendata.helpers import (
|
||||
)
|
||||
import async_timeout
|
||||
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_FORECAST_CONDITION,
|
||||
ATTR_FORECAST_NATIVE_PRECIPITATION,
|
||||
ATTR_FORECAST_NATIVE_TEMP,
|
||||
ATTR_FORECAST_NATIVE_TEMP_LOW,
|
||||
ATTR_FORECAST_NATIVE_WIND_SPEED,
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
ATTR_FORECAST_TIME,
|
||||
ATTR_FORECAST_WIND_BEARING,
|
||||
)
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import (
|
||||
ATTR_API_CONDITION,
|
||||
ATTR_API_FORECAST_CONDITION,
|
||||
ATTR_API_FORECAST_DAILY,
|
||||
ATTR_API_FORECAST_HOURLY,
|
||||
ATTR_API_FORECAST_PRECIPITATION,
|
||||
ATTR_API_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
ATTR_API_FORECAST_TEMP,
|
||||
ATTR_API_FORECAST_TEMP_LOW,
|
||||
ATTR_API_FORECAST_TIME,
|
||||
ATTR_API_FORECAST_WIND_BEARING,
|
||||
ATTR_API_FORECAST_WIND_SPEED,
|
||||
ATTR_API_HUMIDITY,
|
||||
ATTR_API_PRESSURE,
|
||||
ATTR_API_RAIN,
|
||||
@@ -402,15 +400,15 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator):
|
||||
return None
|
||||
|
||||
return {
|
||||
ATTR_FORECAST_CONDITION: condition,
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY: self._get_precipitation_prob_day(
|
||||
ATTR_API_FORECAST_CONDITION: condition,
|
||||
ATTR_API_FORECAST_PRECIPITATION_PROBABILITY: self._get_precipitation_prob_day(
|
||||
day
|
||||
),
|
||||
ATTR_FORECAST_NATIVE_TEMP: self._get_temperature_day(day),
|
||||
ATTR_FORECAST_NATIVE_TEMP_LOW: self._get_temperature_low_day(day),
|
||||
ATTR_FORECAST_TIME: dt_util.as_utc(date).isoformat(),
|
||||
ATTR_FORECAST_NATIVE_WIND_SPEED: self._get_wind_speed_day(day),
|
||||
ATTR_FORECAST_WIND_BEARING: self._get_wind_bearing_day(day),
|
||||
ATTR_API_FORECAST_TEMP: self._get_temperature_day(day),
|
||||
ATTR_API_FORECAST_TEMP_LOW: self._get_temperature_low_day(day),
|
||||
ATTR_API_FORECAST_TIME: dt_util.as_utc(date).isoformat(),
|
||||
ATTR_API_FORECAST_WIND_SPEED: self._get_wind_speed_day(day),
|
||||
ATTR_API_FORECAST_WIND_BEARING: self._get_wind_bearing_day(day),
|
||||
}
|
||||
|
||||
def _convert_forecast_hour(self, date, day, hour):
|
||||
@@ -420,15 +418,15 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator):
|
||||
forecast_dt = date.replace(hour=hour, minute=0, second=0)
|
||||
|
||||
return {
|
||||
ATTR_FORECAST_CONDITION: condition,
|
||||
ATTR_FORECAST_NATIVE_PRECIPITATION: self._calc_precipitation(day, hour),
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY: self._calc_precipitation_prob(
|
||||
ATTR_API_FORECAST_CONDITION: condition,
|
||||
ATTR_API_FORECAST_PRECIPITATION: self._calc_precipitation(day, hour),
|
||||
ATTR_API_FORECAST_PRECIPITATION_PROBABILITY: self._calc_precipitation_prob(
|
||||
day, hour
|
||||
),
|
||||
ATTR_FORECAST_NATIVE_TEMP: self._get_temperature(day, hour),
|
||||
ATTR_FORECAST_TIME: dt_util.as_utc(forecast_dt).isoformat(),
|
||||
ATTR_FORECAST_NATIVE_WIND_SPEED: self._get_wind_speed(day, hour),
|
||||
ATTR_FORECAST_WIND_BEARING: self._get_wind_bearing(day, hour),
|
||||
ATTR_API_FORECAST_TEMP: self._get_temperature(day, hour),
|
||||
ATTR_API_FORECAST_TIME: dt_util.as_utc(forecast_dt).isoformat(),
|
||||
ATTR_API_FORECAST_WIND_SPEED: self._get_wind_speed(day, hour),
|
||||
ATTR_API_FORECAST_WIND_BEARING: self._get_wind_bearing(day, hour),
|
||||
}
|
||||
|
||||
def _calc_precipitation(self, day, hour):
|
||||
|
||||
@@ -102,7 +102,6 @@ class KafkaManager:
|
||||
self._hass = hass
|
||||
ssl_context = ssl_util.client_context()
|
||||
self._producer = AIOKafkaProducer(
|
||||
loop=hass.loop,
|
||||
bootstrap_servers=f"{ip_address}:{port}",
|
||||
compression_type="gzip",
|
||||
security_protocol=security_protocol,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "apache_kafka",
|
||||
"name": "Apache Kafka",
|
||||
"documentation": "https://www.home-assistant.io/integrations/apache_kafka",
|
||||
"requirements": ["aiokafka==0.6.0"],
|
||||
"requirements": ["aiokafka==0.7.2"],
|
||||
"codeowners": ["@bachya"],
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["aiokafka", "kafka_python"]
|
||||
|
||||
@@ -123,6 +123,10 @@ class AppleTVEntity(Entity):
|
||||
self.atv = None
|
||||
self.async_write_ha_state()
|
||||
|
||||
if self.manager.atv:
|
||||
# ATV is already connected
|
||||
_async_connected(self.manager.atv)
|
||||
|
||||
self.async_on_remove(
|
||||
async_dispatcher_connect(
|
||||
self.hass, f"{SIGNAL_CONNECTED}_{self.unique_id}", _async_connected
|
||||
|
||||
@@ -3,7 +3,6 @@ from datetime import timedelta
|
||||
from http import HTTPStatus
|
||||
import logging
|
||||
|
||||
from aiohttp.hdrs import AUTHORIZATION
|
||||
import requests
|
||||
import voluptuous as vol
|
||||
|
||||
@@ -67,7 +66,7 @@ class BloomSky:
|
||||
_LOGGER.debug("Fetching BloomSky update")
|
||||
response = requests.get(
|
||||
f"{self.API_URL}?{self._endpoint_argument}",
|
||||
headers={AUTHORIZATION: self._api_key},
|
||||
headers={"Authorization": self._api_key},
|
||||
timeout=10,
|
||||
)
|
||||
if response.status_code == HTTPStatus.UNAUTHORIZED:
|
||||
|
||||
@@ -37,7 +37,7 @@ class ChromecastInfo:
|
||||
|
||||
@property
|
||||
def friendly_name(self) -> str:
|
||||
"""Return the UUID."""
|
||||
"""Return the Friendly Name."""
|
||||
return self.cast_info.friendly_name
|
||||
|
||||
@property
|
||||
|
||||
@@ -441,6 +441,19 @@ class CastMediaPlayerEntity(CastDevice, MediaPlayerEntity):
|
||||
connection_status.status,
|
||||
)
|
||||
self._attr_available = new_available
|
||||
if new_available and not self._cast_info.is_audio_group:
|
||||
# Poll current group status
|
||||
for group_uuid in self.mz_mgr.get_multizone_memberships(
|
||||
self._cast_info.uuid
|
||||
):
|
||||
group_media_controller = self.mz_mgr.get_multizone_mediacontroller(
|
||||
group_uuid
|
||||
)
|
||||
if not group_media_controller:
|
||||
continue
|
||||
self.multizone_new_media_status(
|
||||
group_uuid, group_media_controller.status
|
||||
)
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
def multizone_new_media_status(self, group_uuid, media_status):
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "cloud",
|
||||
"name": "Home Assistant Cloud",
|
||||
"documentation": "https://www.home-assistant.io/integrations/cloud",
|
||||
"requirements": ["hass-nabucasa==0.54.0"],
|
||||
"requirements": ["hass-nabucasa==0.54.1"],
|
||||
"dependencies": ["http", "webhook"],
|
||||
"after_dependencies": ["google_assistant", "alexa"],
|
||||
"codeowners": ["@home-assistant/cloud"],
|
||||
|
||||
@@ -113,11 +113,7 @@ async def async_setup_entry(
|
||||
|
||||
first = True
|
||||
for light_id in group.lights:
|
||||
if (
|
||||
(light := gateway.api.lights.lights.get(light_id))
|
||||
and light.ZHATYPE == Light.ZHATYPE
|
||||
and light.reachable
|
||||
):
|
||||
if (light := gateway.api.lights.lights.get(light_id)) and light.reachable:
|
||||
group.update_color_state(light, update_all_attributes=first)
|
||||
first = False
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "deCONZ",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/deconz",
|
||||
"requirements": ["pydeconz==95"],
|
||||
"requirements": ["pydeconz==97"],
|
||||
"ssdp": [
|
||||
{
|
||||
"manufacturer": "Royal Philips Electronics",
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
"""Describe logbook events."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.logbook.const import (
|
||||
LOGBOOK_ENTRY_ENTITY_ID,
|
||||
@@ -28,12 +31,13 @@ def async_describe_events(hass, async_describe_event):
|
||||
].get(doorbird_event, event.data.get(ATTR_ENTITY_ID)),
|
||||
}
|
||||
|
||||
domain_data = hass.data[DOMAIN]
|
||||
domain_data: dict[str, Any] = hass.data[DOMAIN]
|
||||
|
||||
for config_entry_id in domain_data:
|
||||
door_station = domain_data[config_entry_id][DOOR_STATION]
|
||||
|
||||
for event in door_station.doorstation_events:
|
||||
for data in domain_data.values():
|
||||
if DOOR_STATION not in data:
|
||||
# We need to skip door_station_event_entity_ids
|
||||
continue
|
||||
for event in data[DOOR_STATION].doorstation_events:
|
||||
async_describe_event(
|
||||
DOMAIN, f"{DOMAIN}_{event}", async_describe_logbook_event
|
||||
)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "elkm1",
|
||||
"name": "Elk-M1 Control",
|
||||
"documentation": "https://www.home-assistant.io/integrations/elkm1",
|
||||
"requirements": ["elkm1-lib==2.0.0"],
|
||||
"requirements": ["elkm1-lib==2.0.2"],
|
||||
"dhcp": [{ "registered_devices": true }, { "macaddress": "00409D*" }],
|
||||
"codeowners": ["@gwww", "@bdraco"],
|
||||
"dependencies": ["network"],
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "envisalink",
|
||||
"name": "Envisalink",
|
||||
"documentation": "https://www.home-assistant.io/integrations/envisalink",
|
||||
"requirements": ["pyenvisalink==4.5"],
|
||||
"requirements": ["pyenvisalink==4.6"],
|
||||
"codeowners": ["@ufodone"],
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["pyenvisalink"]
|
||||
|
||||
@@ -12,8 +12,8 @@ from bleak.backends.scanner import AdvertisementData
|
||||
from fjaraskupan import Device, State, device_filter
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.const import EVENT_HOMEASSISTANT_STOP, Platform
|
||||
from homeassistant.core import Event, HomeAssistant, callback
|
||||
from homeassistant.helpers.dispatcher import (
|
||||
async_dispatcher_connect,
|
||||
async_dispatcher_send,
|
||||
@@ -131,6 +131,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
scanner.register_detection_callback(detection_callback)
|
||||
await scanner.start()
|
||||
|
||||
async def on_hass_stop(event: Event) -> None:
|
||||
await scanner.stop()
|
||||
|
||||
entry.async_on_unload(
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, on_hass_stop)
|
||||
)
|
||||
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
return True
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ class FreeboxRouter:
|
||||
# According to the doc `syst_datas["sensors"]` is temperature sensors in celsius degree.
|
||||
# Name and id of sensors may vary under Freebox devices.
|
||||
for sensor in syst_datas["sensors"]:
|
||||
self.sensors_temperature[sensor["name"]] = sensor["value"]
|
||||
self.sensors_temperature[sensor["name"]] = sensor.get("value")
|
||||
|
||||
# Connection sensors
|
||||
connection_datas: dict[str, Any] = await self._api.connection.get_status()
|
||||
|
||||
@@ -159,7 +159,7 @@ class FreeboxDiskSensor(FreeboxSensor):
|
||||
self._disk = disk
|
||||
self._partition = partition
|
||||
self._attr_name = f"{partition['label']} {description.name}"
|
||||
self._unique_id = f"{self._router.mac} {description.key} {self._disk['id']} {self._partition['id']}"
|
||||
self._attr_unique_id = f"{self._router.mac} {description.key} {self._disk['id']} {self._partition['id']}"
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "frontend",
|
||||
"name": "Home Assistant Frontend",
|
||||
"documentation": "https://www.home-assistant.io/integrations/frontend",
|
||||
"requirements": ["home-assistant-frontend==20220630.0"],
|
||||
"requirements": ["home-assistant-frontend==20220707.0"],
|
||||
"dependencies": [
|
||||
"api",
|
||||
"auth",
|
||||
|
||||
@@ -8,6 +8,13 @@
|
||||
"mode": "Travel Mode"
|
||||
}
|
||||
},
|
||||
"origin_menu": {
|
||||
"title": "Choose Origin",
|
||||
"menu_options": {
|
||||
"origin_coordinates": "Using a map location",
|
||||
"origin_entity": "Using an entity"
|
||||
}
|
||||
},
|
||||
"origin_coordinates": {
|
||||
"title": "Choose Origin",
|
||||
"data": {
|
||||
|
||||
@@ -39,6 +39,13 @@
|
||||
},
|
||||
"title": "Choose Origin"
|
||||
},
|
||||
"origin_menu": {
|
||||
"menu_options": {
|
||||
"origin_coordinates": "Using a map location",
|
||||
"origin_entity": "Using an entity"
|
||||
},
|
||||
"title": "Choose Origin"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API Key",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "HomeKit Controller",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/homekit_controller",
|
||||
"requirements": ["aiohomekit==0.7.18"],
|
||||
"requirements": ["aiohomekit==0.7.20"],
|
||||
"zeroconf": ["_hap._tcp.local."],
|
||||
"after_dependencies": ["zeroconf"],
|
||||
"codeowners": ["@Jc2k", "@bdraco"],
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "HomematicIP Cloud",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/homematicip_cloud",
|
||||
"requirements": ["homematicip==1.0.2"],
|
||||
"requirements": ["homematicip==1.0.3"],
|
||||
"codeowners": [],
|
||||
"quality_scale": "platinum",
|
||||
"iot_class": "cloud_push",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "kaiterra",
|
||||
"name": "Kaiterra",
|
||||
"documentation": "https://www.home-assistant.io/integrations/kaiterra",
|
||||
"requirements": ["kaiterra-async-client==0.0.2"],
|
||||
"requirements": ["kaiterra-async-client==1.0.0"],
|
||||
"codeowners": ["@Michsior14"],
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["kaiterra_async_client"]
|
||||
|
||||
@@ -21,9 +21,6 @@ from homeassistant.components.weather import (
|
||||
ATTR_CONDITION_SUNNY,
|
||||
ATTR_CONDITION_WINDY,
|
||||
ATTR_CONDITION_WINDY_VARIANT,
|
||||
ATTR_FORECAST_CONDITION,
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
ATTR_FORECAST_TIME,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
DEGREE,
|
||||
@@ -68,10 +65,15 @@ ATTR_API_FORECAST = "forecast"
|
||||
UPDATE_LISTENER = "update_listener"
|
||||
PLATFORMS = [Platform.SENSOR, Platform.WEATHER]
|
||||
|
||||
ATTR_FORECAST_PRECIPITATION = "precipitation"
|
||||
ATTR_FORECAST_PRESSURE = "pressure"
|
||||
ATTR_FORECAST_TEMP = "temperature"
|
||||
ATTR_FORECAST_TEMP_LOW = "templow"
|
||||
ATTR_API_FORECAST_CONDITION = "condition"
|
||||
ATTR_API_FORECAST_PRECIPITATION = "precipitation"
|
||||
ATTR_API_FORECAST_PRECIPITATION_PROBABILITY = "precipitation_probability"
|
||||
ATTR_API_FORECAST_PRESSURE = "pressure"
|
||||
ATTR_API_FORECAST_TEMP = "temperature"
|
||||
ATTR_API_FORECAST_TEMP_LOW = "templow"
|
||||
ATTR_API_FORECAST_TIME = "datetime"
|
||||
ATTR_API_FORECAST_WIND_BEARING = "wind_bearing"
|
||||
ATTR_API_FORECAST_WIND_SPEED = "wind_speed"
|
||||
|
||||
FORECAST_MODE_HOURLY = "hourly"
|
||||
FORECAST_MODE_DAILY = "daily"
|
||||
@@ -263,39 +265,39 @@ WEATHER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
)
|
||||
FORECAST_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_CONDITION,
|
||||
key=ATTR_API_FORECAST_CONDITION,
|
||||
name="Condition",
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_PRECIPITATION,
|
||||
key=ATTR_API_FORECAST_PRECIPITATION,
|
||||
name="Precipitation",
|
||||
native_unit_of_measurement=LENGTH_MILLIMETERS,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
key=ATTR_API_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
name="Precipitation probability",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_PRESSURE,
|
||||
key=ATTR_API_FORECAST_PRESSURE,
|
||||
name="Pressure",
|
||||
native_unit_of_measurement=PRESSURE_HPA,
|
||||
device_class=SensorDeviceClass.PRESSURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_TEMP,
|
||||
key=ATTR_API_FORECAST_TEMP,
|
||||
name="Temperature",
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_TEMP_LOW,
|
||||
key=ATTR_API_FORECAST_TEMP_LOW,
|
||||
name="Temperature Low",
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_FORECAST_TIME,
|
||||
key=ATTR_API_FORECAST_TIME,
|
||||
name="Time",
|
||||
device_class=SensorDeviceClass.TIMESTAMP,
|
||||
),
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
"""Support for the OpenWeatherMap (OWM) service."""
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.weather import Forecast, WeatherEntity
|
||||
from typing import cast
|
||||
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_FORECAST_CONDITION,
|
||||
ATTR_FORECAST_NATIVE_PRECIPITATION,
|
||||
ATTR_FORECAST_NATIVE_TEMP,
|
||||
ATTR_FORECAST_NATIVE_TEMP_LOW,
|
||||
ATTR_FORECAST_NATIVE_WIND_SPEED,
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
ATTR_FORECAST_TIME,
|
||||
ATTR_FORECAST_WIND_BEARING,
|
||||
Forecast,
|
||||
WeatherEntity,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
LENGTH_MILLIMETERS,
|
||||
@@ -17,6 +30,14 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from .const import (
|
||||
ATTR_API_CONDITION,
|
||||
ATTR_API_FORECAST,
|
||||
ATTR_API_FORECAST_CONDITION,
|
||||
ATTR_API_FORECAST_PRECIPITATION,
|
||||
ATTR_API_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
ATTR_API_FORECAST_TEMP,
|
||||
ATTR_API_FORECAST_TEMP_LOW,
|
||||
ATTR_API_FORECAST_TIME,
|
||||
ATTR_API_FORECAST_WIND_BEARING,
|
||||
ATTR_API_FORECAST_WIND_SPEED,
|
||||
ATTR_API_HUMIDITY,
|
||||
ATTR_API_PRESSURE,
|
||||
ATTR_API_TEMPERATURE,
|
||||
@@ -31,6 +52,17 @@ from .const import (
|
||||
)
|
||||
from .weather_update_coordinator import WeatherUpdateCoordinator
|
||||
|
||||
FORECAST_MAP = {
|
||||
ATTR_API_FORECAST_CONDITION: ATTR_FORECAST_CONDITION,
|
||||
ATTR_API_FORECAST_PRECIPITATION: ATTR_FORECAST_NATIVE_PRECIPITATION,
|
||||
ATTR_API_FORECAST_PRECIPITATION_PROBABILITY: ATTR_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
ATTR_API_FORECAST_TEMP_LOW: ATTR_FORECAST_NATIVE_TEMP_LOW,
|
||||
ATTR_API_FORECAST_TEMP: ATTR_FORECAST_NATIVE_TEMP,
|
||||
ATTR_API_FORECAST_TIME: ATTR_FORECAST_TIME,
|
||||
ATTR_API_FORECAST_WIND_BEARING: ATTR_FORECAST_WIND_BEARING,
|
||||
ATTR_API_FORECAST_WIND_SPEED: ATTR_FORECAST_NATIVE_WIND_SPEED,
|
||||
}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
@@ -109,7 +141,16 @@ class OpenWeatherMapWeather(WeatherEntity):
|
||||
@property
|
||||
def forecast(self) -> list[Forecast] | None:
|
||||
"""Return the forecast array."""
|
||||
return self._weather_coordinator.data[ATTR_API_FORECAST]
|
||||
api_forecasts = self._weather_coordinator.data[ATTR_API_FORECAST]
|
||||
forecasts = [
|
||||
{
|
||||
ha_key: forecast[api_key]
|
||||
for api_key, ha_key in FORECAST_MAP.items()
|
||||
if api_key in forecast
|
||||
}
|
||||
for forecast in api_forecasts
|
||||
]
|
||||
return cast(list[Forecast], forecasts)
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
|
||||
@@ -8,15 +8,6 @@ from pyowm.commons.exceptions import APIRequestError, UnauthorizedError
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_CONDITION_CLEAR_NIGHT,
|
||||
ATTR_CONDITION_SUNNY,
|
||||
ATTR_FORECAST_CONDITION,
|
||||
ATTR_FORECAST_NATIVE_PRECIPITATION,
|
||||
ATTR_FORECAST_NATIVE_PRESSURE,
|
||||
ATTR_FORECAST_NATIVE_TEMP,
|
||||
ATTR_FORECAST_NATIVE_TEMP_LOW,
|
||||
ATTR_FORECAST_NATIVE_WIND_SPEED,
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
ATTR_FORECAST_TIME,
|
||||
ATTR_FORECAST_WIND_BEARING,
|
||||
)
|
||||
from homeassistant.helpers import sun
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
@@ -29,6 +20,15 @@ from .const import (
|
||||
ATTR_API_DEW_POINT,
|
||||
ATTR_API_FEELS_LIKE_TEMPERATURE,
|
||||
ATTR_API_FORECAST,
|
||||
ATTR_API_FORECAST_CONDITION,
|
||||
ATTR_API_FORECAST_PRECIPITATION,
|
||||
ATTR_API_FORECAST_PRECIPITATION_PROBABILITY,
|
||||
ATTR_API_FORECAST_PRESSURE,
|
||||
ATTR_API_FORECAST_TEMP,
|
||||
ATTR_API_FORECAST_TEMP_LOW,
|
||||
ATTR_API_FORECAST_TIME,
|
||||
ATTR_API_FORECAST_WIND_BEARING,
|
||||
ATTR_API_FORECAST_WIND_SPEED,
|
||||
ATTR_API_HUMIDITY,
|
||||
ATTR_API_PRECIPITATION_KIND,
|
||||
ATTR_API_PRESSURE,
|
||||
@@ -158,19 +158,19 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator):
|
||||
def _convert_forecast(self, entry):
|
||||
"""Convert the forecast data."""
|
||||
forecast = {
|
||||
ATTR_FORECAST_TIME: dt.utc_from_timestamp(
|
||||
ATTR_API_FORECAST_TIME: dt.utc_from_timestamp(
|
||||
entry.reference_time("unix")
|
||||
).isoformat(),
|
||||
ATTR_FORECAST_NATIVE_PRECIPITATION: self._calc_precipitation(
|
||||
ATTR_API_FORECAST_PRECIPITATION: self._calc_precipitation(
|
||||
entry.rain, entry.snow
|
||||
),
|
||||
ATTR_FORECAST_PRECIPITATION_PROBABILITY: (
|
||||
ATTR_API_FORECAST_PRECIPITATION_PROBABILITY: (
|
||||
round(entry.precipitation_probability * 100)
|
||||
),
|
||||
ATTR_FORECAST_NATIVE_PRESSURE: entry.pressure.get("press"),
|
||||
ATTR_FORECAST_NATIVE_WIND_SPEED: entry.wind().get("speed"),
|
||||
ATTR_FORECAST_WIND_BEARING: entry.wind().get("deg"),
|
||||
ATTR_FORECAST_CONDITION: self._get_condition(
|
||||
ATTR_API_FORECAST_PRESSURE: entry.pressure.get("press"),
|
||||
ATTR_API_FORECAST_WIND_SPEED: entry.wind().get("speed"),
|
||||
ATTR_API_FORECAST_WIND_BEARING: entry.wind().get("deg"),
|
||||
ATTR_API_FORECAST_CONDITION: self._get_condition(
|
||||
entry.weather_code, entry.reference_time("unix")
|
||||
),
|
||||
ATTR_API_CLOUDS: entry.clouds,
|
||||
@@ -178,16 +178,12 @@ class WeatherUpdateCoordinator(DataUpdateCoordinator):
|
||||
|
||||
temperature_dict = entry.temperature("celsius")
|
||||
if "max" in temperature_dict and "min" in temperature_dict:
|
||||
forecast[ATTR_FORECAST_NATIVE_TEMP] = entry.temperature("celsius").get(
|
||||
"max"
|
||||
)
|
||||
forecast[ATTR_FORECAST_NATIVE_TEMP_LOW] = entry.temperature("celsius").get(
|
||||
forecast[ATTR_API_FORECAST_TEMP] = entry.temperature("celsius").get("max")
|
||||
forecast[ATTR_API_FORECAST_TEMP_LOW] = entry.temperature("celsius").get(
|
||||
"min"
|
||||
)
|
||||
else:
|
||||
forecast[ATTR_FORECAST_NATIVE_TEMP] = entry.temperature("celsius").get(
|
||||
"temp"
|
||||
)
|
||||
forecast[ATTR_API_FORECAST_TEMP] = entry.temperature("celsius").get("temp")
|
||||
|
||||
return forecast
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
entry.data[CONF_IP_ADDRESS],
|
||||
entry.data[CONF_PASSWORD],
|
||||
port=entry.data[CONF_PORT],
|
||||
ssl=entry.data.get(CONF_SSL, DEFAULT_SSL),
|
||||
use_ssl=entry.data.get(CONF_SSL, DEFAULT_SSL),
|
||||
)
|
||||
except RainMachineError as err:
|
||||
raise ConfigEntryNotReady from err
|
||||
|
||||
@@ -32,7 +32,7 @@ async def async_get_controller(
|
||||
websession = aiohttp_client.async_get_clientsession(hass)
|
||||
client = Client(session=websession)
|
||||
try:
|
||||
await client.load_local(ip_address, password, port=port, ssl=ssl)
|
||||
await client.load_local(ip_address, password, port=port, use_ssl=ssl)
|
||||
except RainMachineError:
|
||||
return None
|
||||
else:
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "RainMachine",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/rainmachine",
|
||||
"requirements": ["regenmaschine==2022.06.1"],
|
||||
"requirements": ["regenmaschine==2022.07.0"],
|
||||
"codeowners": ["@bachya"],
|
||||
"iot_class": "local_polling",
|
||||
"homekit": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "satel_integra",
|
||||
"name": "Satel Integra",
|
||||
"documentation": "https://www.home-assistant.io/integrations/satel_integra",
|
||||
"requirements": ["satel_integra==0.3.4"],
|
||||
"requirements": ["satel_integra==0.3.7"],
|
||||
"codeowners": [],
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["satel_integra"]
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "SkyBell",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/skybell",
|
||||
"requirements": ["aioskybell==22.6.1"],
|
||||
"requirements": ["aioskybell==22.7.0"],
|
||||
"dependencies": ["ffmpeg"],
|
||||
"codeowners": ["@tkdrob"],
|
||||
"iot_class": "cloud_polling",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"config_flow": true,
|
||||
"iot_class": "local_push",
|
||||
"documentation": "https://www.home-assistant.io/integrations/slimproto",
|
||||
"requirements": ["aioslimproto==2.0.1"],
|
||||
"requirements": ["aioslimproto==2.1.1"],
|
||||
"codeowners": ["@marcelveldt"],
|
||||
"after_dependencies": ["media_source"]
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ from .speaker import SonosMedia, SonosSpeaker
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
LONG_SERVICE_TIMEOUT = 30.0
|
||||
UNJOIN_SERVICE_TIMEOUT = 0.1
|
||||
VOLUME_INCREMENT = 2
|
||||
|
||||
REPEAT_TO_SONOS = {
|
||||
@@ -775,7 +776,7 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity):
|
||||
async def async_unjoin_player(self):
|
||||
"""Remove this player from any group.
|
||||
|
||||
Coalesces all calls within 0.5s to allow use of SonosSpeaker.unjoin_multi()
|
||||
Coalesces all calls within UNJOIN_SERVICE_TIMEOUT to allow use of SonosSpeaker.unjoin_multi()
|
||||
which optimizes the order in which speakers are removed from their groups.
|
||||
Removing coordinators last better preserves playqueues on the speakers.
|
||||
"""
|
||||
@@ -785,6 +786,9 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity):
|
||||
async def async_process_unjoin(now: datetime.datetime) -> None:
|
||||
"""Process the unjoin with all remove requests within the coalescing period."""
|
||||
unjoin_data = sonos_data.unjoin_data.pop(household_id)
|
||||
_LOGGER.debug(
|
||||
"Processing unjoins for %s", [x.zone_name for x in unjoin_data.speakers]
|
||||
)
|
||||
await SonosSpeaker.unjoin_multi(self.hass, unjoin_data.speakers)
|
||||
unjoin_data.event.set()
|
||||
|
||||
@@ -794,6 +798,7 @@ class SonosMediaPlayerEntity(SonosEntity, MediaPlayerEntity):
|
||||
unjoin_data = sonos_data.unjoin_data[household_id] = UnjoinData(
|
||||
speakers=[self.speaker]
|
||||
)
|
||||
async_call_later(self.hass, 0.5, async_process_unjoin)
|
||||
async_call_later(self.hass, UNJOIN_SERVICE_TIMEOUT, async_process_unjoin)
|
||||
|
||||
_LOGGER.debug("Requesting unjoin for %s", self.speaker.zone_name)
|
||||
await unjoin_data.event.wait()
|
||||
|
||||
@@ -579,7 +579,7 @@ UNITS = (
|
||||
),
|
||||
UnitOfMeasurement(
|
||||
unit=TEMP_CELSIUS,
|
||||
aliases={"°c", "c", "celsius"},
|
||||
aliases={"°c", "c", "celsius", "℃"},
|
||||
device_classes={SensorDeviceClass.TEMPERATURE},
|
||||
),
|
||||
UnitOfMeasurement(
|
||||
|
||||
@@ -80,7 +80,7 @@ class UnifiDeviceScanner(DeviceScanner):
|
||||
def _connect(self):
|
||||
"""Connect to the Unifi AP SSH server."""
|
||||
|
||||
self.ssh = pxssh.pxssh()
|
||||
self.ssh = pxssh.pxssh(options={"HostKeyAlgorithms": "ssh-rsa"})
|
||||
try:
|
||||
self.ssh.login(
|
||||
self.host, self.username, password=self.password, port=self.port
|
||||
|
||||
@@ -10,7 +10,6 @@ import logging
|
||||
import os
|
||||
from typing import Union
|
||||
|
||||
from aiohttp.hdrs import USER_AGENT
|
||||
import requests
|
||||
import voluptuous as vol
|
||||
|
||||
@@ -275,7 +274,7 @@ class ZamgData:
|
||||
"""The class for handling the data retrieval."""
|
||||
|
||||
API_URL = "http://www.zamg.ac.at/ogd/"
|
||||
API_HEADERS = {USER_AGENT: f"home-assistant.zamg/ {__version__}"}
|
||||
API_HEADERS = {"User-Agent": f"home-assistant.zamg/ {__version__}"}
|
||||
|
||||
def __init__(self, station_id):
|
||||
"""Initialize the probe."""
|
||||
|
||||
@@ -188,7 +188,7 @@ class FrostLock(BinarySensor, id_suffix="frost_lock"):
|
||||
_attr_device_class: BinarySensorDeviceClass = BinarySensorDeviceClass.LOCK
|
||||
|
||||
|
||||
@MULTI_MATCH(channel_names="ikea_airpurifier", models={"STARKVIND Air purifier"})
|
||||
@MULTI_MATCH(channel_names="ikea_airpurifier")
|
||||
class ReplaceFilter(BinarySensor, id_suffix="replace_filter"):
|
||||
"""ZHA BinarySensor."""
|
||||
|
||||
|
||||
@@ -141,7 +141,7 @@ class BaseLight(LogMixin, light.LightEntity):
|
||||
self._color_channel = None
|
||||
self._identify_channel = None
|
||||
self._default_transition = None
|
||||
self._color_mode = ColorMode.UNKNOWN # Set by sub classes
|
||||
self._attr_color_mode = ColorMode.UNKNOWN # Set by sub classes
|
||||
|
||||
@property
|
||||
def extra_state_attributes(self) -> dict[str, Any]:
|
||||
@@ -159,11 +159,6 @@ class BaseLight(LogMixin, light.LightEntity):
|
||||
return False
|
||||
return self._state
|
||||
|
||||
@property
|
||||
def color_mode(self):
|
||||
"""Return the color mode of this light."""
|
||||
return self._color_mode
|
||||
|
||||
@property
|
||||
def brightness(self):
|
||||
"""Return the brightness of this light."""
|
||||
@@ -309,7 +304,7 @@ class BaseLight(LogMixin, light.LightEntity):
|
||||
if isinstance(result, Exception) or result[1] is not Status.SUCCESS:
|
||||
self.debug("turned on: %s", t_log)
|
||||
return
|
||||
self._color_mode = ColorMode.COLOR_TEMP
|
||||
self._attr_color_mode = ColorMode.COLOR_TEMP
|
||||
self._color_temp = temperature
|
||||
self._hs_color = None
|
||||
|
||||
@@ -323,7 +318,7 @@ class BaseLight(LogMixin, light.LightEntity):
|
||||
if isinstance(result, Exception) or result[1] is not Status.SUCCESS:
|
||||
self.debug("turned on: %s", t_log)
|
||||
return
|
||||
self._color_mode = ColorMode.HS
|
||||
self._attr_color_mode = ColorMode.HS
|
||||
self._hs_color = hs_color
|
||||
self._color_temp = None
|
||||
|
||||
@@ -451,13 +446,13 @@ class Light(BaseLight, ZhaEntity):
|
||||
self._attr_supported_color_modes
|
||||
)
|
||||
if len(self._attr_supported_color_modes) == 1:
|
||||
self._color_mode = next(iter(self._attr_supported_color_modes))
|
||||
self._attr_color_mode = next(iter(self._attr_supported_color_modes))
|
||||
else: # Light supports color_temp + hs, determine which mode the light is in
|
||||
assert self._color_channel
|
||||
if self._color_channel.color_mode == Color.ColorMode.Color_temperature:
|
||||
self._color_mode = ColorMode.COLOR_TEMP
|
||||
self._attr_color_mode = ColorMode.COLOR_TEMP
|
||||
else:
|
||||
self._color_mode = ColorMode.HS
|
||||
self._attr_color_mode = ColorMode.HS
|
||||
|
||||
if self._identify_channel:
|
||||
self._supported_features |= light.LightEntityFeature.FLASH
|
||||
@@ -518,7 +513,7 @@ class Light(BaseLight, ZhaEntity):
|
||||
if "off_brightness" in last_state.attributes:
|
||||
self._off_brightness = last_state.attributes["off_brightness"]
|
||||
if "color_mode" in last_state.attributes:
|
||||
self._color_mode = ColorMode(last_state.attributes["color_mode"])
|
||||
self._attr_color_mode = ColorMode(last_state.attributes["color_mode"])
|
||||
if "color_temp" in last_state.attributes:
|
||||
self._color_temp = last_state.attributes["color_temp"]
|
||||
if "hs_color" in last_state.attributes:
|
||||
@@ -558,13 +553,13 @@ class Light(BaseLight, ZhaEntity):
|
||||
|
||||
if (color_mode := results.get("color_mode")) is not None:
|
||||
if color_mode == Color.ColorMode.Color_temperature:
|
||||
self._color_mode = ColorMode.COLOR_TEMP
|
||||
self._attr_color_mode = ColorMode.COLOR_TEMP
|
||||
color_temp = results.get("color_temperature")
|
||||
if color_temp is not None and color_mode:
|
||||
self._color_temp = color_temp
|
||||
self._hs_color = None
|
||||
else:
|
||||
self._color_mode = ColorMode.HS
|
||||
self._attr_color_mode = ColorMode.HS
|
||||
color_x = results.get("current_x")
|
||||
color_y = results.get("current_y")
|
||||
if color_x is not None and color_y is not None:
|
||||
@@ -650,7 +645,7 @@ class LightGroup(BaseLight, ZhaGroupEntity):
|
||||
CONF_DEFAULT_LIGHT_TRANSITION,
|
||||
0,
|
||||
)
|
||||
self._color_mode = None
|
||||
self._attr_color_mode = None
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Run when about to be added to hass."""
|
||||
|
||||
@@ -525,9 +525,7 @@ class TimerDurationMinutes(ZHANumberConfigurationEntity, id_suffix="timer_durati
|
||||
_zcl_attribute: str = "timer_duration"
|
||||
|
||||
|
||||
@CONFIG_DIAGNOSTIC_MATCH(
|
||||
channel_names="ikea_airpurifier", models={"STARKVIND Air purifier"}
|
||||
)
|
||||
@CONFIG_DIAGNOSTIC_MATCH(channel_names="ikea_airpurifier")
|
||||
class FilterLifeTime(ZHANumberConfigurationEntity, id_suffix="filter_life_time"):
|
||||
"""Representation of a ZHA timer duration configuration entity."""
|
||||
|
||||
|
||||
@@ -472,25 +472,8 @@ class SmartEnergyMetering(Sensor):
|
||||
|
||||
@MULTI_MATCH(
|
||||
channel_names=CHANNEL_SMARTENERGY_METERING,
|
||||
models={"TS011F"},
|
||||
stop_on_match_group=CHANNEL_SMARTENERGY_METERING,
|
||||
)
|
||||
class PolledSmartEnergyMetering(SmartEnergyMetering):
|
||||
"""Polled metering sensor."""
|
||||
|
||||
@property
|
||||
def should_poll(self) -> bool:
|
||||
"""Poll the entity for current state."""
|
||||
return True
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Retrieve latest state."""
|
||||
if not self.available:
|
||||
return
|
||||
await self._channel.async_force_update()
|
||||
|
||||
|
||||
@MULTI_MATCH(channel_names=CHANNEL_SMARTENERGY_METERING)
|
||||
class SmartEnergySummation(SmartEnergyMetering, id_suffix="summation_delivered"):
|
||||
"""Smart Energy Metering summation sensor."""
|
||||
|
||||
@@ -523,6 +506,26 @@ class SmartEnergySummation(SmartEnergyMetering, id_suffix="summation_delivered")
|
||||
return round(cooked, 3)
|
||||
|
||||
|
||||
@MULTI_MATCH(
|
||||
channel_names=CHANNEL_SMARTENERGY_METERING,
|
||||
models={"TS011F"},
|
||||
stop_on_match_group=CHANNEL_SMARTENERGY_METERING,
|
||||
)
|
||||
class PolledSmartEnergySummation(SmartEnergySummation):
|
||||
"""Polled Smart Energy Metering summation sensor."""
|
||||
|
||||
@property
|
||||
def should_poll(self) -> bool:
|
||||
"""Poll the entity for current state."""
|
||||
return True
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Retrieve latest state."""
|
||||
if not self.available:
|
||||
return
|
||||
await self._channel.async_force_update()
|
||||
|
||||
|
||||
@MULTI_MATCH(channel_names=CHANNEL_PRESSURE)
|
||||
class Pressure(Sensor):
|
||||
"""Pressure sensor."""
|
||||
@@ -810,7 +813,7 @@ class TimeLeft(Sensor, id_suffix="time_left"):
|
||||
_unit = TIME_MINUTES
|
||||
|
||||
|
||||
@MULTI_MATCH(channel_names="ikea_airpurifier", models={"STARKVIND Air purifier"})
|
||||
@MULTI_MATCH(channel_names="ikea_airpurifier")
|
||||
class IkeaDeviceRunTime(Sensor, id_suffix="device_run_time"):
|
||||
"""Sensor that displays device run time (in minutes)."""
|
||||
|
||||
@@ -820,7 +823,7 @@ class IkeaDeviceRunTime(Sensor, id_suffix="device_run_time"):
|
||||
_unit = TIME_MINUTES
|
||||
|
||||
|
||||
@MULTI_MATCH(channel_names="ikea_airpurifier", models={"STARKVIND Air purifier"})
|
||||
@MULTI_MATCH(channel_names="ikea_airpurifier")
|
||||
class IkeaFilterRunTime(Sensor, id_suffix="filter_run_time"):
|
||||
"""Sensor that displays run time of the current filter (in minutes)."""
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ from .backports.enum import StrEnum
|
||||
|
||||
MAJOR_VERSION: Final = 2022
|
||||
MINOR_VERSION: Final = 7
|
||||
PATCH_VERSION: Final = "0b4"
|
||||
PATCH_VERSION: Final = "2"
|
||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0)
|
||||
|
||||
@@ -6,7 +6,7 @@ import logging
|
||||
from typing import TYPE_CHECKING, Any, Generic, TypeVar, Union, cast
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components import dhcp, mqtt, onboarding, ssdp, zeroconf
|
||||
from homeassistant.components import dhcp, onboarding, ssdp, zeroconf
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
|
||||
@@ -15,6 +15,9 @@ from .typing import UNDEFINED, DiscoveryInfoType, UndefinedType
|
||||
if TYPE_CHECKING:
|
||||
import asyncio
|
||||
|
||||
from homeassistant.components import mqtt
|
||||
|
||||
|
||||
_R = TypeVar("_R", bound="Awaitable[bool] | bool")
|
||||
DiscoveryFunctionType = Callable[[HomeAssistant], _R]
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ aiohttp_cors==0.7.0
|
||||
astral==2.2
|
||||
async-upnp-client==0.31.2
|
||||
async_timeout==4.0.2
|
||||
atomicwrites==1.4.0
|
||||
atomicwrites-homeassistant==1.4.1
|
||||
attrs==21.2.0
|
||||
awesomeversion==22.6.0
|
||||
bcrypt==3.1.7
|
||||
@@ -14,8 +14,8 @@ certifi>=2021.5.30
|
||||
ciso8601==2.2.0
|
||||
cryptography==36.0.2
|
||||
fnvhash==0.1.0
|
||||
hass-nabucasa==0.54.0
|
||||
home-assistant-frontend==20220630.0
|
||||
hass-nabucasa==0.54.1
|
||||
home-assistant-frontend==20220707.0
|
||||
httpx==0.23.0
|
||||
ifaddr==0.1.7
|
||||
jinja2==3.1.2
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "homeassistant"
|
||||
version = "2022.7.0b4"
|
||||
version = "2022.7.2"
|
||||
license = {text = "Apache-2.0"}
|
||||
description = "Open-source home automation platform running on Python 3."
|
||||
readme = "README.rst"
|
||||
@@ -28,7 +28,7 @@ dependencies = [
|
||||
"astral==2.2",
|
||||
"async_timeout==4.0.2",
|
||||
"attrs==21.2.0",
|
||||
"atomicwrites==1.4.0",
|
||||
"atomicwrites-homeassistant==1.4.1",
|
||||
"awesomeversion==22.6.0",
|
||||
"bcrypt==3.1.7",
|
||||
"certifi>=2021.5.30",
|
||||
|
||||
@@ -5,7 +5,7 @@ aiohttp==3.8.1
|
||||
astral==2.2
|
||||
async_timeout==4.0.2
|
||||
attrs==21.2.0
|
||||
atomicwrites==1.4.0
|
||||
atomicwrites-homeassistant==1.4.1
|
||||
awesomeversion==22.6.0
|
||||
bcrypt==3.1.7
|
||||
certifi>=2021.5.30
|
||||
|
||||
@@ -168,7 +168,7 @@ aioguardian==2022.03.2
|
||||
aioharmony==0.2.9
|
||||
|
||||
# homeassistant.components.homekit_controller
|
||||
aiohomekit==0.7.18
|
||||
aiohomekit==0.7.20
|
||||
|
||||
# homeassistant.components.emulated_hue
|
||||
# homeassistant.components.http
|
||||
@@ -181,7 +181,7 @@ aiohue==4.4.2
|
||||
aioimaplib==1.0.0
|
||||
|
||||
# homeassistant.components.apache_kafka
|
||||
aiokafka==0.6.0
|
||||
aiokafka==0.7.2
|
||||
|
||||
# homeassistant.components.kef
|
||||
aiokef==0.2.16
|
||||
@@ -247,10 +247,10 @@ aiosenz==1.0.0
|
||||
aioshelly==2.0.0
|
||||
|
||||
# homeassistant.components.skybell
|
||||
aioskybell==22.6.1
|
||||
aioskybell==22.7.0
|
||||
|
||||
# homeassistant.components.slimproto
|
||||
aioslimproto==2.0.1
|
||||
aioslimproto==2.1.1
|
||||
|
||||
# homeassistant.components.steamist
|
||||
aiosteamist==0.3.2
|
||||
@@ -586,7 +586,7 @@ elgato==3.0.0
|
||||
eliqonline==1.2.2
|
||||
|
||||
# homeassistant.components.elkm1
|
||||
elkm1-lib==2.0.0
|
||||
elkm1-lib==2.0.2
|
||||
|
||||
# homeassistant.components.elmax
|
||||
elmax_api==0.0.2
|
||||
@@ -795,7 +795,7 @@ habitipy==0.2.0
|
||||
hangups==0.4.18
|
||||
|
||||
# homeassistant.components.cloud
|
||||
hass-nabucasa==0.54.0
|
||||
hass-nabucasa==0.54.1
|
||||
|
||||
# homeassistant.components.splunk
|
||||
hass_splunk==0.1.1
|
||||
@@ -828,13 +828,13 @@ hole==0.7.0
|
||||
holidays==0.14.2
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20220630.0
|
||||
home-assistant-frontend==20220707.0
|
||||
|
||||
# homeassistant.components.home_connect
|
||||
homeconnect==0.7.1
|
||||
|
||||
# homeassistant.components.homematicip_cloud
|
||||
homematicip==1.0.2
|
||||
homematicip==1.0.3
|
||||
|
||||
# homeassistant.components.home_plus_control
|
||||
homepluscontrol==0.0.5
|
||||
@@ -912,7 +912,7 @@ jellyfin-apiclient-python==1.8.1
|
||||
jsonpath==0.82
|
||||
|
||||
# homeassistant.components.kaiterra
|
||||
kaiterra-async-client==0.0.2
|
||||
kaiterra-async-client==1.0.0
|
||||
|
||||
# homeassistant.components.keba
|
||||
keba-kecontact==1.1.0
|
||||
@@ -1444,7 +1444,7 @@ pydaikin==2.7.0
|
||||
pydanfossair==0.1.0
|
||||
|
||||
# homeassistant.components.deconz
|
||||
pydeconz==95
|
||||
pydeconz==97
|
||||
|
||||
# homeassistant.components.delijn
|
||||
pydelijn==1.0.0
|
||||
@@ -1477,7 +1477,7 @@ pyeight==0.3.0
|
||||
pyemby==1.8
|
||||
|
||||
# homeassistant.components.envisalink
|
||||
pyenvisalink==4.5
|
||||
pyenvisalink==4.6
|
||||
|
||||
# homeassistant.components.ephember
|
||||
pyephember==0.3.1
|
||||
@@ -2065,7 +2065,7 @@ raincloudy==0.0.7
|
||||
raspyrfm-client==1.2.8
|
||||
|
||||
# homeassistant.components.rainmachine
|
||||
regenmaschine==2022.06.1
|
||||
regenmaschine==2022.07.0
|
||||
|
||||
# homeassistant.components.renault
|
||||
renault-api==0.1.11
|
||||
@@ -2125,7 +2125,7 @@ samsungctl[websocket]==0.7.1
|
||||
samsungtvws[async,encrypted]==2.5.0
|
||||
|
||||
# homeassistant.components.satel_integra
|
||||
satel_integra==0.3.4
|
||||
satel_integra==0.3.7
|
||||
|
||||
# homeassistant.components.dhcp
|
||||
scapy==2.4.5
|
||||
|
||||
@@ -152,7 +152,7 @@ aioguardian==2022.03.2
|
||||
aioharmony==0.2.9
|
||||
|
||||
# homeassistant.components.homekit_controller
|
||||
aiohomekit==0.7.18
|
||||
aiohomekit==0.7.20
|
||||
|
||||
# homeassistant.components.emulated_hue
|
||||
# homeassistant.components.http
|
||||
@@ -162,7 +162,7 @@ aiohttp_cors==0.7.0
|
||||
aiohue==4.4.2
|
||||
|
||||
# homeassistant.components.apache_kafka
|
||||
aiokafka==0.6.0
|
||||
aiokafka==0.7.2
|
||||
|
||||
# homeassistant.components.lookin
|
||||
aiolookin==0.1.1
|
||||
@@ -216,10 +216,10 @@ aiosenz==1.0.0
|
||||
aioshelly==2.0.0
|
||||
|
||||
# homeassistant.components.skybell
|
||||
aioskybell==22.6.1
|
||||
aioskybell==22.7.0
|
||||
|
||||
# homeassistant.components.slimproto
|
||||
aioslimproto==2.0.1
|
||||
aioslimproto==2.1.1
|
||||
|
||||
# homeassistant.components.steamist
|
||||
aiosteamist==0.3.2
|
||||
@@ -426,7 +426,7 @@ eagle100==0.1.1
|
||||
elgato==3.0.0
|
||||
|
||||
# homeassistant.components.elkm1
|
||||
elkm1-lib==2.0.0
|
||||
elkm1-lib==2.0.2
|
||||
|
||||
# homeassistant.components.elmax
|
||||
elmax_api==0.0.2
|
||||
@@ -574,7 +574,7 @@ habitipy==0.2.0
|
||||
hangups==0.4.18
|
||||
|
||||
# homeassistant.components.cloud
|
||||
hass-nabucasa==0.54.0
|
||||
hass-nabucasa==0.54.1
|
||||
|
||||
# homeassistant.components.tasmota
|
||||
hatasmota==0.5.1
|
||||
@@ -595,13 +595,13 @@ hole==0.7.0
|
||||
holidays==0.14.2
|
||||
|
||||
# homeassistant.components.frontend
|
||||
home-assistant-frontend==20220630.0
|
||||
home-assistant-frontend==20220707.0
|
||||
|
||||
# homeassistant.components.home_connect
|
||||
homeconnect==0.7.1
|
||||
|
||||
# homeassistant.components.homematicip_cloud
|
||||
homematicip==1.0.2
|
||||
homematicip==1.0.3
|
||||
|
||||
# homeassistant.components.home_plus_control
|
||||
homepluscontrol==0.0.5
|
||||
@@ -974,7 +974,7 @@ pycoolmasternet-async==0.1.2
|
||||
pydaikin==2.7.0
|
||||
|
||||
# homeassistant.components.deconz
|
||||
pydeconz==95
|
||||
pydeconz==97
|
||||
|
||||
# homeassistant.components.dexcom
|
||||
pydexcom==0.2.3
|
||||
@@ -1376,7 +1376,7 @@ radios==0.1.1
|
||||
radiotherm==2.1.0
|
||||
|
||||
# homeassistant.components.rainmachine
|
||||
regenmaschine==2022.06.1
|
||||
regenmaschine==2022.07.0
|
||||
|
||||
# homeassistant.components.renault
|
||||
renault-api==0.1.11
|
||||
|
||||
@@ -2,15 +2,11 @@
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
import requests_mock
|
||||
|
||||
from homeassistant.components.aemet.const import DOMAIN
|
||||
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from .util import aemet_requests_mock
|
||||
@@ -46,83 +42,3 @@ async def test_unload_entry(hass):
|
||||
await hass.config_entries.async_unload(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert config_entry.state is ConfigEntryState.NOT_LOADED
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"old_unique_id,new_unique_id",
|
||||
[
|
||||
# Sensors which should be migrated
|
||||
(
|
||||
"aemet_unique_id-forecast-daily-precipitation",
|
||||
"aemet_unique_id-forecast-daily-native_precipitation",
|
||||
),
|
||||
(
|
||||
"aemet_unique_id-forecast-daily-temperature",
|
||||
"aemet_unique_id-forecast-daily-native_temperature",
|
||||
),
|
||||
(
|
||||
"aemet_unique_id-forecast-daily-templow",
|
||||
"aemet_unique_id-forecast-daily-native_templow",
|
||||
),
|
||||
(
|
||||
"aemet_unique_id-forecast-daily-wind_speed",
|
||||
"aemet_unique_id-forecast-daily-native_wind_speed",
|
||||
),
|
||||
(
|
||||
"aemet_unique_id-forecast-hourly-precipitation",
|
||||
"aemet_unique_id-forecast-hourly-native_precipitation",
|
||||
),
|
||||
(
|
||||
"aemet_unique_id-forecast-hourly-temperature",
|
||||
"aemet_unique_id-forecast-hourly-native_temperature",
|
||||
),
|
||||
(
|
||||
"aemet_unique_id-forecast-hourly-templow",
|
||||
"aemet_unique_id-forecast-hourly-native_templow",
|
||||
),
|
||||
(
|
||||
"aemet_unique_id-forecast-hourly-wind_speed",
|
||||
"aemet_unique_id-forecast-hourly-native_wind_speed",
|
||||
),
|
||||
# Already migrated
|
||||
(
|
||||
"aemet_unique_id-forecast-daily-native_templow",
|
||||
"aemet_unique_id-forecast-daily-native_templow",
|
||||
),
|
||||
# No migration needed
|
||||
(
|
||||
"aemet_unique_id-forecast-daily-condition",
|
||||
"aemet_unique_id-forecast-daily-condition",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_migrate_unique_id_sensor(
|
||||
hass: HomeAssistant,
|
||||
old_unique_id: str,
|
||||
new_unique_id: str,
|
||||
) -> None:
|
||||
"""Test migration of unique_id."""
|
||||
now = dt_util.parse_datetime("2021-01-09 12:00:00+00:00")
|
||||
with patch("homeassistant.util.dt.now", return_value=now), patch(
|
||||
"homeassistant.util.dt.utcnow", return_value=now
|
||||
), requests_mock.mock() as _m:
|
||||
aemet_requests_mock(_m)
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN, unique_id="aemet_unique_id", data=CONFIG
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
entity: er.RegistryEntry = entity_registry.async_get_or_create(
|
||||
domain=SENSOR_DOMAIN,
|
||||
platform=DOMAIN,
|
||||
unique_id=old_unique_id,
|
||||
config_entry=config_entry,
|
||||
)
|
||||
assert entity.unique_id == old_unique_id
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
entity_migrated = entity_registry.async_get(entity.entity_id)
|
||||
assert entity_migrated
|
||||
assert entity_migrated.unique_id == new_unique_id
|
||||
|
||||
@@ -731,6 +731,20 @@ async def test_entity_availability(hass: HomeAssistant):
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.state == "off"
|
||||
|
||||
connection_status = MagicMock()
|
||||
connection_status.status = "LOST"
|
||||
conn_status_cb(connection_status)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.state == "unavailable"
|
||||
|
||||
connection_status = MagicMock()
|
||||
connection_status.status = "CONNECTED"
|
||||
conn_status_cb(connection_status)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.state == "off"
|
||||
|
||||
connection_status = MagicMock()
|
||||
connection_status.status = "DISCONNECTED"
|
||||
conn_status_cb(connection_status)
|
||||
@@ -738,6 +752,14 @@ async def test_entity_availability(hass: HomeAssistant):
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.state == "unavailable"
|
||||
|
||||
# Can't reconnect after receiving DISCONNECTED
|
||||
connection_status = MagicMock()
|
||||
connection_status.status = "CONNECTED"
|
||||
conn_status_cb(connection_status)
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get(entity_id)
|
||||
assert state.state == "unavailable"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("port,entry_type", ((8009, None), (12345, None)))
|
||||
async def test_device_registry(hass: HomeAssistant, hass_ws_client, port, entry_type):
|
||||
@@ -1675,6 +1697,59 @@ async def test_group_media_states(hass, mz_mock):
|
||||
assert state.state == "playing"
|
||||
|
||||
|
||||
async def test_group_media_states_early(hass, mz_mock):
|
||||
"""Test media states are read from group if entity has no state.
|
||||
|
||||
This tests case asserts group state is polled when the player is created.
|
||||
"""
|
||||
entity_id = "media_player.speaker"
|
||||
reg = er.async_get(hass)
|
||||
|
||||
info = get_fake_chromecast_info()
|
||||
|
||||
mz_mock.get_multizone_memberships = MagicMock(return_value=[str(FakeGroupUUID)])
|
||||
mz_mock.get_multizone_mediacontroller = MagicMock(
|
||||
return_value=MagicMock(status=MagicMock(images=None, player_state="BUFFERING"))
|
||||
)
|
||||
|
||||
chromecast, _ = await async_setup_media_player_cast(hass, info)
|
||||
_, conn_status_cb, _, _ = get_status_callbacks(chromecast, mz_mock)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state is not None
|
||||
assert state.name == "Speaker"
|
||||
assert state.state == "unavailable"
|
||||
assert entity_id == reg.async_get_entity_id("media_player", "cast", str(info.uuid))
|
||||
|
||||
# Check group state is polled when player is first created
|
||||
connection_status = MagicMock()
|
||||
connection_status.status = "CONNECTED"
|
||||
conn_status_cb(connection_status)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == "buffering"
|
||||
|
||||
connection_status = MagicMock()
|
||||
connection_status.status = "LOST"
|
||||
conn_status_cb(connection_status)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == "unavailable"
|
||||
|
||||
# Check group state is polled when player reconnects
|
||||
mz_mock.get_multizone_mediacontroller = MagicMock(
|
||||
return_value=MagicMock(status=MagicMock(images=None, player_state="PLAYING"))
|
||||
)
|
||||
|
||||
connection_status = MagicMock()
|
||||
connection_status.status = "CONNECTED"
|
||||
conn_status_cb(connection_status)
|
||||
await hass.async_block_till_done()
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == "playing"
|
||||
|
||||
|
||||
async def test_group_media_control(hass, mz_mock, quick_play_mock):
|
||||
"""Test media controls are handled by group if entity has no state."""
|
||||
entity_id = "media_player.speaker"
|
||||
|
||||
@@ -816,6 +816,37 @@ async def test_dont_add_sensor_if_state_is_none(
|
||||
assert len(hass.states.async_all()) == 0
|
||||
|
||||
|
||||
async def test_air_quality_sensor_without_ppb(hass, aioclient_mock):
|
||||
"""Test sensor with scaled data is not created if state is None."""
|
||||
data = {
|
||||
"sensors": {
|
||||
"1": {
|
||||
"config": {
|
||||
"on": True,
|
||||
"reachable": True,
|
||||
},
|
||||
"ep": 2,
|
||||
"etag": "c2d2e42396f7c78e11e46c66e2ec0200",
|
||||
"lastseen": "2020-11-20T22:48Z",
|
||||
"manufacturername": "BOSCH",
|
||||
"modelid": "AIR",
|
||||
"name": "BOSCH Air quality sensor",
|
||||
"state": {
|
||||
"airquality": "poor",
|
||||
"lastupdated": "2020-11-20T22:48:00.209",
|
||||
},
|
||||
"swversion": "20200402",
|
||||
"type": "ZHAAirQuality",
|
||||
"uniqueid": "00:00:00:00:00:00:00:00-02-fdef",
|
||||
}
|
||||
}
|
||||
}
|
||||
with patch.dict(DECONZ_WEB_REQUEST, data):
|
||||
await setup_deconz_integration(hass, aioclient_mock)
|
||||
|
||||
assert len(hass.states.async_all()) == 1
|
||||
|
||||
|
||||
async def test_add_battery_later(hass, aioclient_mock, mock_deconz_websocket):
|
||||
"""Test that a sensor without an initial battery state creates a battery sensor once state exist."""
|
||||
data = {
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
"""The tests for the Demo component."""
|
||||
from contextlib import suppress
|
||||
import json
|
||||
import os
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.demo import DOMAIN
|
||||
from homeassistant.components.device_tracker.legacy import YAML_DEVICES
|
||||
from homeassistant.components.recorder import get_instance
|
||||
from homeassistant.components.recorder.statistics import list_statistic_ids
|
||||
from homeassistant.helpers.json import JSONEncoder
|
||||
@@ -22,11 +20,10 @@ def mock_history(hass):
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def demo_cleanup(hass):
|
||||
"""Clean up device tracker demo file."""
|
||||
yield
|
||||
with suppress(FileNotFoundError):
|
||||
os.remove(hass.config.path(YAML_DEVICES))
|
||||
def mock_device_tracker_update_config(hass):
|
||||
"""Prevent device tracker from creating known devices file."""
|
||||
with patch("homeassistant.components.device_tracker.legacy.update_config"):
|
||||
yield
|
||||
|
||||
|
||||
async def test_setting_up_demo(hass):
|
||||
|
||||
@@ -28,6 +28,8 @@ from homeassistant.helpers.json import JSONEncoder
|
||||
from homeassistant.setup import async_setup_component
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from . import common
|
||||
|
||||
from tests.common import (
|
||||
assert_setup_component,
|
||||
async_fire_time_changed,
|
||||
@@ -35,7 +37,6 @@ from tests.common import (
|
||||
mock_restore_cache,
|
||||
patch_yaml_files,
|
||||
)
|
||||
from tests.components.device_tracker import common
|
||||
|
||||
TEST_PLATFORM = {device_tracker.DOMAIN: {CONF_PLATFORM: "test"}}
|
||||
|
||||
@@ -165,6 +166,7 @@ async def test_setup_without_yaml_file(hass, enable_custom_integrations):
|
||||
"""Test with no YAML file."""
|
||||
with assert_setup_component(1, device_tracker.DOMAIN):
|
||||
assert await async_setup_component(hass, device_tracker.DOMAIN, TEST_PLATFORM)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def test_gravatar(hass):
|
||||
@@ -210,10 +212,11 @@ async def test_gravatar_and_picture(hass):
|
||||
@patch("homeassistant.components.demo.device_tracker.setup_scanner", autospec=True)
|
||||
async def test_discover_platform(mock_demo_setup_scanner, mock_see, hass):
|
||||
"""Test discovery of device_tracker demo platform."""
|
||||
await discovery.async_load_platform(
|
||||
hass, device_tracker.DOMAIN, "demo", {"test_key": "test_val"}, {"bla": {}}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
with patch("homeassistant.components.device_tracker.legacy.update_config"):
|
||||
await discovery.async_load_platform(
|
||||
hass, device_tracker.DOMAIN, "demo", {"test_key": "test_val"}, {"bla": {}}
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert device_tracker.DOMAIN in hass.config.components
|
||||
assert mock_demo_setup_scanner.called
|
||||
assert mock_demo_setup_scanner.call_args[0] == (
|
||||
|
||||
@@ -22,6 +22,7 @@ DATA_SYSTEM_GET_CONFIG = {
|
||||
"fans": [{"id": "fan0_speed", "name": "Ventilateur 1", "value": 2130}],
|
||||
"sensors": [
|
||||
{"id": "temp_hdd", "name": "Disque dur", "value": 40},
|
||||
{"id": "temp_hdd2", "name": "Disque dur 2"},
|
||||
{"id": "temp_sw", "name": "Température Switch", "value": 50},
|
||||
{"id": "temp_cpum", "name": "Température CPU M", "value": 60},
|
||||
{"id": "temp_cpub", "name": "Température CPU B", "value": 56},
|
||||
@@ -123,7 +124,42 @@ DATA_STORAGE_GET_DISKS = [
|
||||
"path": "L0Rpc3F1ZSBkdXI=",
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
{
|
||||
"idle_duration": 8290,
|
||||
"read_error_requests": 0,
|
||||
"read_requests": 2326826,
|
||||
"spinning": False,
|
||||
"table_type": "gpt",
|
||||
"firmware": "0001",
|
||||
"type": "sata",
|
||||
"idle": True,
|
||||
"connector": 0,
|
||||
"id": 2000,
|
||||
"write_error_requests": 0,
|
||||
"state": "enabled",
|
||||
"write_requests": 122733632,
|
||||
"total_bytes": 2000000000000,
|
||||
"model": "ST2000LM015-2E8174",
|
||||
"active_duration": 0,
|
||||
"temp": 0,
|
||||
"serial": "WDZYJ27Q",
|
||||
"partitions": [
|
||||
{
|
||||
"fstype": "ext4",
|
||||
"total_bytes": 1960000000000,
|
||||
"label": "Disque 2",
|
||||
"id": 2001,
|
||||
"internal": False,
|
||||
"fsck_result": "no_run_yet",
|
||||
"state": "mounted",
|
||||
"disk_id": 2000,
|
||||
"free_bytes": 1880000000000,
|
||||
"used_bytes": 85410000000,
|
||||
"path": "L0Rpc3F1ZSAy",
|
||||
}
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
# switch
|
||||
|
||||
Reference in New Issue
Block a user