mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 04:37:06 +00:00
2023.10.4 (#102397)
This commit is contained in:
commit
aa5c4d8786
@ -1,5 +1,6 @@
|
||||
"""The AEMET OpenData component."""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from aemet_opendata.exceptions import TownNotFound
|
||||
@ -8,6 +9,7 @@ from aemet_opendata.interface import AEMET, ConnectionOptions
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers import aiohttp_client
|
||||
|
||||
from .const import (
|
||||
@ -37,6 +39,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
except TownNotFound as err:
|
||||
_LOGGER.error(err)
|
||||
return False
|
||||
except asyncio.TimeoutError as err:
|
||||
raise ConfigEntryNotReady("AEMET OpenData API timed out") from err
|
||||
|
||||
weather_coordinator = WeatherUpdateCoordinator(hass, aemet)
|
||||
await weather_coordinator.async_config_entry_first_refresh()
|
||||
|
@ -11,5 +11,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/airzone",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["aioairzone"],
|
||||
"requirements": ["aioairzone==0.6.8"]
|
||||
"requirements": ["aioairzone==0.6.9"]
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
"bleak-retry-connector==3.2.1",
|
||||
"bluetooth-adapters==0.16.1",
|
||||
"bluetooth-auto-recovery==1.2.3",
|
||||
"bluetooth-data-tools==1.12.0",
|
||||
"dbus-fast==2.11.1"
|
||||
"bluetooth-data-tools==1.13.0",
|
||||
"dbus-fast==2.12.0"
|
||||
]
|
||||
}
|
||||
|
@ -483,7 +483,7 @@ class CalendarEntity(Entity):
|
||||
|
||||
_entity_component_unrecorded_attributes = frozenset({"description"})
|
||||
|
||||
_alarm_unsubs: list[CALLBACK_TYPE] = []
|
||||
_alarm_unsubs: list[CALLBACK_TYPE] | None = None
|
||||
|
||||
@property
|
||||
def event(self) -> CalendarEvent | None:
|
||||
@ -528,6 +528,8 @@ class CalendarEntity(Entity):
|
||||
the current or upcoming event.
|
||||
"""
|
||||
super().async_write_ha_state()
|
||||
if self._alarm_unsubs is None:
|
||||
self._alarm_unsubs = []
|
||||
_LOGGER.debug(
|
||||
"Clearing %s alarms (%s)", self.entity_id, len(self._alarm_unsubs)
|
||||
)
|
||||
@ -571,9 +573,9 @@ class CalendarEntity(Entity):
|
||||
|
||||
To be extended by integrations.
|
||||
"""
|
||||
for unsub in self._alarm_unsubs:
|
||||
for unsub in self._alarm_unsubs or ():
|
||||
unsub()
|
||||
self._alarm_unsubs.clear()
|
||||
self._alarm_unsubs = None
|
||||
|
||||
async def async_get_events(
|
||||
self,
|
||||
|
@ -5,5 +5,5 @@
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/duotecno",
|
||||
"iot_class": "local_push",
|
||||
"requirements": ["pyDuotecno==2023.10.0"]
|
||||
"requirements": ["pyDuotecno==2023.10.1"]
|
||||
}
|
||||
|
@ -25,8 +25,11 @@ from aioesphomeapi import (
|
||||
BluetoothProxyFeature,
|
||||
DeviceInfo,
|
||||
)
|
||||
from aioesphomeapi.connection import APIConnectionError, TimeoutAPIError
|
||||
from aioesphomeapi.core import BluetoothGATTAPIError
|
||||
from aioesphomeapi.core import (
|
||||
APIConnectionError,
|
||||
BluetoothGATTAPIError,
|
||||
TimeoutAPIError,
|
||||
)
|
||||
from async_interrupt import interrupt
|
||||
from bleak.backends.characteristic import BleakGATTCharacteristic
|
||||
from bleak.backends.client import BaseBleakClient, NotifyCallback
|
||||
@ -389,8 +392,8 @@ class ESPHomeClient(BaseBleakClient):
|
||||
return await self._disconnect()
|
||||
|
||||
async def _disconnect(self) -> bool:
|
||||
self._async_disconnected_cleanup()
|
||||
await self._client.bluetooth_device_disconnect(self._address_as_int)
|
||||
self._async_ble_device_disconnected()
|
||||
await self._wait_for_free_connection_slot(DISCONNECT_TIMEOUT)
|
||||
return True
|
||||
|
||||
|
@ -538,7 +538,7 @@ class ESPHomeManager:
|
||||
on_connect=self.on_connect,
|
||||
on_disconnect=self.on_disconnect,
|
||||
zeroconf_instance=self.zeroconf_instance,
|
||||
name=self.host,
|
||||
name=entry.data.get(CONF_DEVICE_NAME, self.host),
|
||||
on_connect_error=self.on_connect_error,
|
||||
)
|
||||
self.reconnect_logic = reconnect_logic
|
||||
|
@ -16,8 +16,8 @@
|
||||
"loggers": ["aioesphomeapi", "noiseprotocol"],
|
||||
"requirements": [
|
||||
"async-interrupt==1.1.1",
|
||||
"aioesphomeapi==17.0.1",
|
||||
"bluetooth-data-tools==1.12.0",
|
||||
"aioesphomeapi==18.0.7",
|
||||
"bluetooth-data-tools==1.13.0",
|
||||
"esphome-dashboard-api==1.2.3"
|
||||
],
|
||||
"zeroconf": ["_esphomelib._tcp.local."]
|
||||
|
@ -55,6 +55,8 @@ _VOICE_ASSISTANT_EVENT_TYPES: EsphomeEnumMapper[
|
||||
VoiceAssistantEventType.VOICE_ASSISTANT_TTS_END: PipelineEventType.TTS_END,
|
||||
VoiceAssistantEventType.VOICE_ASSISTANT_WAKE_WORD_START: PipelineEventType.WAKE_WORD_START,
|
||||
VoiceAssistantEventType.VOICE_ASSISTANT_WAKE_WORD_END: PipelineEventType.WAKE_WORD_END,
|
||||
VoiceAssistantEventType.VOICE_ASSISTANT_STT_VAD_START: PipelineEventType.STT_VAD_START,
|
||||
VoiceAssistantEventType.VOICE_ASSISTANT_STT_VAD_END: PipelineEventType.STT_VAD_END,
|
||||
}
|
||||
)
|
||||
|
||||
@ -161,7 +163,7 @@ class VoiceAssistantUDPServer(asyncio.DatagramProtocol):
|
||||
try:
|
||||
event_type = _VOICE_ASSISTANT_EVENT_TYPES.from_hass(event.type)
|
||||
except KeyError:
|
||||
_LOGGER.warning("Received unknown pipeline event type: %s", event.type)
|
||||
_LOGGER.debug("Received unknown pipeline event type: %s", event.type)
|
||||
return
|
||||
|
||||
data_to_send = None
|
||||
@ -296,6 +298,10 @@ class VoiceAssistantUDPServer(asyncio.DatagramProtocol):
|
||||
if self.transport is None:
|
||||
return
|
||||
|
||||
self.handle_event(
|
||||
VoiceAssistantEventType.VOICE_ASSISTANT_TTS_STREAM_START, {}
|
||||
)
|
||||
|
||||
_extension, audio_bytes = await tts.async_get_media_source_audio(
|
||||
self.hass,
|
||||
media_id,
|
||||
@ -321,4 +327,7 @@ class VoiceAssistantUDPServer(asyncio.DatagramProtocol):
|
||||
sample_offset += samples_in_chunk
|
||||
|
||||
finally:
|
||||
self.handle_event(
|
||||
VoiceAssistantEventType.VOICE_ASSISTANT_TTS_STREAM_END, {}
|
||||
)
|
||||
self._tts_done.set()
|
||||
|
@ -118,9 +118,7 @@ class GoogleMapsScanner:
|
||||
)
|
||||
_LOGGER.debug("%s < %s", last_seen, self._prev_seen[dev_id])
|
||||
continue
|
||||
if last_seen == self._prev_seen.get(dev_id, last_seen) and hasattr(
|
||||
self, "success_init"
|
||||
):
|
||||
if last_seen == self._prev_seen.get(dev_id):
|
||||
_LOGGER.debug(
|
||||
"Ignoring %s update because timestamp "
|
||||
"is the same as the last timestamp %s",
|
||||
|
@ -20,5 +20,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/ld2410_ble",
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_push",
|
||||
"requirements": ["bluetooth-data-tools==1.12.0", "ld2410-ble==0.1.1"]
|
||||
"requirements": ["bluetooth-data-tools==1.13.0", "ld2410-ble==0.1.1"]
|
||||
}
|
||||
|
@ -32,5 +32,5 @@
|
||||
"dependencies": ["bluetooth_adapters"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/led_ble",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["bluetooth-data-tools==1.12.0", "led-ble==1.0.1"]
|
||||
"requirements": ["bluetooth-data-tools==1.13.0", "led-ble==1.0.1"]
|
||||
}
|
||||
|
@ -250,9 +250,9 @@ class MatterClimate(MatterEntity, ClimateEntity):
|
||||
self._attr_min_temp = DEFAULT_MIN_TEMP
|
||||
# update max_temp
|
||||
if self._attr_hvac_mode in (HVACMode.COOL, HVACMode.HEAT_COOL):
|
||||
attribute = clusters.Thermostat.Attributes.AbsMaxHeatSetpointLimit
|
||||
else:
|
||||
attribute = clusters.Thermostat.Attributes.AbsMaxCoolSetpointLimit
|
||||
else:
|
||||
attribute = clusters.Thermostat.Attributes.AbsMaxHeatSetpointLimit
|
||||
if (value := self._get_temperature_in_degrees(attribute)) is not None:
|
||||
self._attr_max_temp = value
|
||||
else:
|
||||
|
@ -176,7 +176,13 @@ async def async_subscribe(
|
||||
raise HomeAssistantError(
|
||||
f"Cannot subscribe to topic '{topic}', MQTT is not enabled"
|
||||
)
|
||||
mqtt_data = get_mqtt_data(hass)
|
||||
try:
|
||||
mqtt_data = get_mqtt_data(hass)
|
||||
except KeyError as ex:
|
||||
raise HomeAssistantError(
|
||||
f"Cannot subscribe to topic '{topic}', "
|
||||
"make sure MQTT is set up correctly"
|
||||
) from ex
|
||||
async_remove = await mqtt_data.client.async_subscribe(
|
||||
topic,
|
||||
catch_log_exception(
|
||||
|
@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/nina",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["pynina"],
|
||||
"requirements": ["PyNINA==0.3.2"]
|
||||
"requirements": ["PyNINA==0.3.3"]
|
||||
}
|
||||
|
@ -7,5 +7,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/opower",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["opower"],
|
||||
"requirements": ["opower==0.0.35"]
|
||||
"requirements": ["opower==0.0.36"]
|
||||
}
|
||||
|
@ -6,5 +6,5 @@
|
||||
"dependencies": ["bluetooth_adapters"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/private_ble_device",
|
||||
"iot_class": "local_push",
|
||||
"requirements": ["bluetooth-data-tools==1.12.0"]
|
||||
"requirements": ["bluetooth-data-tools==1.13.0"]
|
||||
}
|
||||
|
@ -7,5 +7,5 @@
|
||||
"integration_type": "service",
|
||||
"iot_class": "cloud_polling",
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["vehicle==1.0.1"]
|
||||
"requirements": ["vehicle==2.0.0"]
|
||||
}
|
||||
|
@ -15,5 +15,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/screenlogic",
|
||||
"iot_class": "local_push",
|
||||
"loggers": ["screenlogicpy"],
|
||||
"requirements": ["screenlogicpy==0.9.2"]
|
||||
"requirements": ["screenlogicpy==0.9.3"]
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from asyncio import run_coroutine_threadsafe
|
||||
import datetime as dt
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import Any
|
||||
@ -27,7 +26,7 @@ from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.util.dt import utc_from_timestamp
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
from . import HomeAssistantSpotifyData
|
||||
from .browse_media import async_browse_media_internal
|
||||
@ -199,13 +198,6 @@ class SpotifyMediaPlayer(MediaPlayerEntity):
|
||||
return None
|
||||
return self._currently_playing["progress_ms"] / 1000
|
||||
|
||||
@property
|
||||
def media_position_updated_at(self) -> dt.datetime | None:
|
||||
"""When was the position of the current playing media valid."""
|
||||
if not self._currently_playing:
|
||||
return None
|
||||
return utc_from_timestamp(self._currently_playing["timestamp"] / 1000)
|
||||
|
||||
@property
|
||||
def media_image_url(self) -> str | None:
|
||||
"""Return the media image URL."""
|
||||
@ -413,6 +405,9 @@ class SpotifyMediaPlayer(MediaPlayerEntity):
|
||||
additional_types=[MediaType.EPISODE]
|
||||
)
|
||||
self._currently_playing = current or {}
|
||||
# Record the last updated time, because Spotify's timestamp property is unreliable
|
||||
# and doesn't actually return the fetch time as is mentioned in the API description
|
||||
self._attr_media_position_updated_at = utcnow() if current is not None else None
|
||||
|
||||
context = self._currently_playing.get("context") or {}
|
||||
|
||||
|
@ -20,9 +20,14 @@ from homeassistant.const import (
|
||||
CONF_PORT,
|
||||
CONF_USERNAME,
|
||||
CONF_VERIFY_SSL,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import CALLBACK_TYPE, Event, HomeAssistant, callback
|
||||
from homeassistant.helpers import aiohttp_client, device_registry as dr
|
||||
from homeassistant.helpers import (
|
||||
aiohttp_client,
|
||||
device_registry as dr,
|
||||
entity_registry as er,
|
||||
)
|
||||
from homeassistant.helpers.device_registry import (
|
||||
DeviceEntry,
|
||||
DeviceEntryType,
|
||||
@ -33,6 +38,7 @@ from homeassistant.helpers.dispatcher import (
|
||||
async_dispatcher_send,
|
||||
)
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.entity_registry import async_entries_for_config_entry
|
||||
from homeassistant.helpers.event import async_call_later, async_track_time_interval
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
@ -131,7 +137,7 @@ class UniFiController:
|
||||
# Client control options
|
||||
|
||||
# Config entry option with list of clients to control network access.
|
||||
self.option_block_clients = options.get(CONF_BLOCK_CLIENT, [])
|
||||
self.option_block_clients: list[str] = options.get(CONF_BLOCK_CLIENT, [])
|
||||
# Config entry option to control DPI restriction groups.
|
||||
self.option_dpi_restrictions: bool = options.get(
|
||||
CONF_DPI_RESTRICTIONS, DEFAULT_DPI_RESTRICTIONS
|
||||
@ -244,7 +250,16 @@ class UniFiController:
|
||||
assert self.config_entry.unique_id is not None
|
||||
self.is_admin = self.api.sites[self.config_entry.unique_id].role == "admin"
|
||||
|
||||
for mac in self.option_block_clients:
|
||||
# Restore device tracker clients that are not a part of active clients list.
|
||||
macs: list[str] = []
|
||||
entity_registry = er.async_get(self.hass)
|
||||
for entry in async_entries_for_config_entry(
|
||||
entity_registry, self.config_entry.entry_id
|
||||
):
|
||||
if entry.domain == Platform.DEVICE_TRACKER:
|
||||
macs.append(entry.unique_id.split("-", 1)[0])
|
||||
|
||||
for mac in self.option_block_clients + macs:
|
||||
if mac not in self.api.clients and mac in self.api.clients_all:
|
||||
self.api.clients.process_raw([dict(self.api.clients_all[mac].raw)])
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
"velbus-packet",
|
||||
"velbus-protocol"
|
||||
],
|
||||
"requirements": ["velbus-aio==2023.2.0"],
|
||||
"requirements": ["velbus-aio==2023.10.1"],
|
||||
"usb": [
|
||||
{
|
||||
"vid": "10CF",
|
||||
|
@ -45,25 +45,18 @@ class VelbusSensor(VelbusEntity, SensorEntity):
|
||||
"""Initialize a sensor Velbus entity."""
|
||||
super().__init__(channel)
|
||||
self._is_counter: bool = counter
|
||||
# define the unique id
|
||||
if self._is_counter:
|
||||
self._attr_unique_id = f"{self._attr_unique_id}-counter"
|
||||
# define the name
|
||||
if self._is_counter:
|
||||
self._attr_name = f"{self._attr_name}-counter"
|
||||
# define the device class
|
||||
if self._is_counter:
|
||||
self._attr_device_class = SensorDeviceClass.POWER
|
||||
elif channel.is_counter_channel():
|
||||
self._attr_device_class = SensorDeviceClass.ENERGY
|
||||
self._attr_icon = "mdi:counter"
|
||||
self._attr_name = f"{self._attr_name}-counter"
|
||||
self._attr_state_class = SensorStateClass.TOTAL_INCREASING
|
||||
self._attr_unique_id = f"{self._attr_unique_id}-counter"
|
||||
elif channel.is_counter_channel():
|
||||
self._attr_device_class = SensorDeviceClass.POWER
|
||||
self._attr_state_class = SensorStateClass.MEASUREMENT
|
||||
elif channel.is_temperature():
|
||||
self._attr_device_class = SensorDeviceClass.TEMPERATURE
|
||||
# define the icon
|
||||
if self._is_counter:
|
||||
self._attr_icon = "mdi:counter"
|
||||
# the state class
|
||||
if self._is_counter:
|
||||
self._attr_state_class = SensorStateClass.TOTAL_INCREASING
|
||||
self._attr_state_class = SensorStateClass.MEASUREMENT
|
||||
else:
|
||||
self._attr_state_class = SensorStateClass.MEASUREMENT
|
||||
# unit
|
||||
|
@ -146,6 +146,7 @@ class VenstarSensor(VenstarEntity, SensorEntity):
|
||||
super().__init__(coordinator, config)
|
||||
self.entity_description = entity_description
|
||||
self.sensor_name = sensor_name
|
||||
self._attr_name = entity_description.name_fn(sensor_name)
|
||||
self._config = config
|
||||
|
||||
@property
|
||||
@ -153,11 +154,6 @@ class VenstarSensor(VenstarEntity, SensorEntity):
|
||||
"""Return the unique id."""
|
||||
return f"{self._config.entry_id}_{self.sensor_name.replace(' ', '_')}_{self.entity_description.key}"
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the device."""
|
||||
return self.entity_description.name_fn(self.sensor_name)
|
||||
|
||||
@property
|
||||
def native_value(self) -> int:
|
||||
"""Return state of the sensor."""
|
||||
|
@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/waqi",
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["aiowaqi"],
|
||||
"requirements": ["aiowaqi==2.0.0"]
|
||||
"requirements": ["aiowaqi==2.1.0"]
|
||||
}
|
||||
|
@ -21,15 +21,15 @@
|
||||
"universal_silabs_flasher"
|
||||
],
|
||||
"requirements": [
|
||||
"bellows==0.36.5",
|
||||
"bellows==0.36.7",
|
||||
"pyserial==3.5",
|
||||
"pyserial-asyncio==0.6",
|
||||
"zha-quirks==0.0.104",
|
||||
"zha-quirks==0.0.105",
|
||||
"zigpy-deconz==0.21.1",
|
||||
"zigpy==0.57.2",
|
||||
"zigpy-xbee==0.18.3",
|
||||
"zigpy-zigate==0.11.0",
|
||||
"zigpy-znp==0.11.5",
|
||||
"zigpy-znp==0.11.6",
|
||||
"universal-silabs-flasher==0.0.14",
|
||||
"pyserial-asyncio-fast==0.11"
|
||||
],
|
||||
|
@ -259,9 +259,11 @@ class ZWaveClimate(ZWaveBaseEntity, ClimateEntity):
|
||||
def _current_mode_setpoint_enums(self) -> list[ThermostatSetpointType]:
|
||||
"""Return the list of enums that are relevant to the current thermostat mode."""
|
||||
if self._current_mode is None or self._current_mode.value is None:
|
||||
# Thermostat(valve) with no support for setting a mode
|
||||
# is considered heating-only
|
||||
return [ThermostatSetpointType.HEATING]
|
||||
# Thermostat with no support for setting a mode is just a setpoint
|
||||
if self.info.primary_value.property_key is None:
|
||||
return []
|
||||
return [ThermostatSetpointType(int(self.info.primary_value.property_key))]
|
||||
|
||||
return THERMOSTAT_MODE_SETPOINT_MAP.get(int(self._current_mode.value), [])
|
||||
|
||||
@property
|
||||
|
@ -163,12 +163,12 @@
|
||||
}
|
||||
},
|
||||
"device_config_file_changed": {
|
||||
"title": "Z-Wave device configuration file changed: {device_name}",
|
||||
"title": "Device configuration file changed: {device_name}",
|
||||
"fix_flow": {
|
||||
"step": {
|
||||
"confirm": {
|
||||
"title": "Z-Wave device configuration file changed: {device_name}",
|
||||
"description": "Z-Wave JS discovers a lot of device metadata by interviewing the device. However, some of the information has to be loaded from a configuration file. Some of this information is only evaluated once, during the device interview.\n\nWhen a device config file is updated, this information may be stale and and the device must be re-interviewed to pick up the changes.\n\n This is not a required operation and device functionality will be impacted during the re-interview process, but you may see improvements for your device once it is complete.\n\nIf you'd like to proceed, click on SUBMIT below. The re-interview will take place in the background."
|
||||
"title": "Device configuration file changed: {device_name}",
|
||||
"description": "The device configuration file for {device_name} has changed.\n\nZ-Wave JS discovers a lot of device metadata by interviewing the device. However, some of the information has to be loaded from a configuration file. Some of this information is only evaluated once, during the device interview.\n\nWhen a device config file is updated, this information may be stale and and the device must be re-interviewed to pick up the changes.\n\n This is not a required operation and device functionality will be impacted during the re-interview process, but you may see improvements for your device once it is complete.\n\nIf you'd like to proceed, click on SUBMIT below. The re-interview will take place in the background."
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
|
@ -7,7 +7,7 @@ from typing import Final
|
||||
APPLICATION_NAME: Final = "HomeAssistant"
|
||||
MAJOR_VERSION: Final = 2023
|
||||
MINOR_VERSION: Final = 10
|
||||
PATCH_VERSION: Final = "3"
|
||||
PATCH_VERSION: Final = "4"
|
||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 11, 0)
|
||||
|
@ -11,11 +11,11 @@ bleak-retry-connector==3.2.1
|
||||
bleak==0.21.1
|
||||
bluetooth-adapters==0.16.1
|
||||
bluetooth-auto-recovery==1.2.3
|
||||
bluetooth-data-tools==1.12.0
|
||||
bluetooth-data-tools==1.13.0
|
||||
certifi>=2021.5.30
|
||||
ciso8601==2.3.0
|
||||
cryptography==41.0.4
|
||||
dbus-fast==2.11.1
|
||||
dbus-fast==2.12.0
|
||||
fnv-hash-fast==0.4.1
|
||||
ha-av==10.1.1
|
||||
hass-nabucasa==0.71.0
|
||||
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "homeassistant"
|
||||
version = "2023.10.3"
|
||||
version = "2023.10.4"
|
||||
license = {text = "Apache-2.0"}
|
||||
description = "Open-source home automation platform running on Python 3."
|
||||
readme = "README.rst"
|
||||
|
@ -80,7 +80,7 @@ PyMetno==0.11.0
|
||||
PyMicroBot==0.0.9
|
||||
|
||||
# homeassistant.components.nina
|
||||
PyNINA==0.3.2
|
||||
PyNINA==0.3.3
|
||||
|
||||
# homeassistant.components.mobile_app
|
||||
# homeassistant.components.owntracks
|
||||
@ -189,7 +189,7 @@ aioairq==0.2.4
|
||||
aioairzone-cloud==0.2.3
|
||||
|
||||
# homeassistant.components.airzone
|
||||
aioairzone==0.6.8
|
||||
aioairzone==0.6.9
|
||||
|
||||
# homeassistant.components.ambient_station
|
||||
aioambient==2023.04.0
|
||||
@ -231,7 +231,7 @@ aioecowitt==2023.5.0
|
||||
aioemonitor==1.0.5
|
||||
|
||||
# homeassistant.components.esphome
|
||||
aioesphomeapi==17.0.1
|
||||
aioesphomeapi==18.0.7
|
||||
|
||||
# homeassistant.components.flo
|
||||
aioflo==2021.11.0
|
||||
@ -372,7 +372,7 @@ aiovlc==0.1.0
|
||||
aiovodafone==0.3.1
|
||||
|
||||
# homeassistant.components.waqi
|
||||
aiowaqi==2.0.0
|
||||
aiowaqi==2.1.0
|
||||
|
||||
# homeassistant.components.watttime
|
||||
aiowatttime==0.1.1
|
||||
@ -512,7 +512,7 @@ beautifulsoup4==4.12.2
|
||||
# beewi-smartclim==0.0.10
|
||||
|
||||
# homeassistant.components.zha
|
||||
bellows==0.36.5
|
||||
bellows==0.36.7
|
||||
|
||||
# homeassistant.components.bmw_connected_drive
|
||||
bimmer-connected==0.14.1
|
||||
@ -553,7 +553,7 @@ bluetooth-auto-recovery==1.2.3
|
||||
# homeassistant.components.ld2410_ble
|
||||
# homeassistant.components.led_ble
|
||||
# homeassistant.components.private_ble_device
|
||||
bluetooth-data-tools==1.12.0
|
||||
bluetooth-data-tools==1.13.0
|
||||
|
||||
# homeassistant.components.bond
|
||||
bond-async==0.2.1
|
||||
@ -645,7 +645,7 @@ datadog==0.15.0
|
||||
datapoint==0.9.8
|
||||
|
||||
# homeassistant.components.bluetooth
|
||||
dbus-fast==2.11.1
|
||||
dbus-fast==2.12.0
|
||||
|
||||
# homeassistant.components.debugpy
|
||||
debugpy==1.8.0
|
||||
@ -1383,7 +1383,7 @@ openwrt-luci-rpc==1.1.16
|
||||
openwrt-ubus-rpc==0.0.2
|
||||
|
||||
# homeassistant.components.opower
|
||||
opower==0.0.35
|
||||
opower==0.0.36
|
||||
|
||||
# homeassistant.components.oralb
|
||||
oralb-ble==0.17.6
|
||||
@ -1538,7 +1538,7 @@ pyCEC==0.5.2
|
||||
pyControl4==1.1.0
|
||||
|
||||
# homeassistant.components.duotecno
|
||||
pyDuotecno==2023.10.0
|
||||
pyDuotecno==2023.10.1
|
||||
|
||||
# homeassistant.components.eight_sleep
|
||||
pyEight==0.3.2
|
||||
@ -2372,7 +2372,7 @@ satel-integra==0.3.7
|
||||
scapy==2.5.0
|
||||
|
||||
# homeassistant.components.screenlogic
|
||||
screenlogicpy==0.9.2
|
||||
screenlogicpy==0.9.3
|
||||
|
||||
# homeassistant.components.scsgate
|
||||
scsgate==0.1.0
|
||||
@ -2648,10 +2648,10 @@ uvcclient==0.11.0
|
||||
vallox-websocket-api==3.3.0
|
||||
|
||||
# homeassistant.components.rdw
|
||||
vehicle==1.0.1
|
||||
vehicle==2.0.0
|
||||
|
||||
# homeassistant.components.velbus
|
||||
velbus-aio==2023.2.0
|
||||
velbus-aio==2023.10.1
|
||||
|
||||
# homeassistant.components.venstar
|
||||
venstarcolortouch==0.19
|
||||
@ -2787,7 +2787,7 @@ zeroconf==0.115.2
|
||||
zeversolar==0.3.1
|
||||
|
||||
# homeassistant.components.zha
|
||||
zha-quirks==0.0.104
|
||||
zha-quirks==0.0.105
|
||||
|
||||
# homeassistant.components.zhong_hong
|
||||
zhong-hong-hvac==1.0.9
|
||||
@ -2805,7 +2805,7 @@ zigpy-xbee==0.18.3
|
||||
zigpy-zigate==0.11.0
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy-znp==0.11.5
|
||||
zigpy-znp==0.11.6
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy==0.57.2
|
||||
|
@ -70,7 +70,7 @@ PyMetno==0.11.0
|
||||
PyMicroBot==0.0.9
|
||||
|
||||
# homeassistant.components.nina
|
||||
PyNINA==0.3.2
|
||||
PyNINA==0.3.3
|
||||
|
||||
# homeassistant.components.mobile_app
|
||||
# homeassistant.components.owntracks
|
||||
@ -170,7 +170,7 @@ aioairq==0.2.4
|
||||
aioairzone-cloud==0.2.3
|
||||
|
||||
# homeassistant.components.airzone
|
||||
aioairzone==0.6.8
|
||||
aioairzone==0.6.9
|
||||
|
||||
# homeassistant.components.ambient_station
|
||||
aioambient==2023.04.0
|
||||
@ -212,7 +212,7 @@ aioecowitt==2023.5.0
|
||||
aioemonitor==1.0.5
|
||||
|
||||
# homeassistant.components.esphome
|
||||
aioesphomeapi==17.0.1
|
||||
aioesphomeapi==18.0.7
|
||||
|
||||
# homeassistant.components.flo
|
||||
aioflo==2021.11.0
|
||||
@ -347,7 +347,7 @@ aiovlc==0.1.0
|
||||
aiovodafone==0.3.1
|
||||
|
||||
# homeassistant.components.waqi
|
||||
aiowaqi==2.0.0
|
||||
aiowaqi==2.1.0
|
||||
|
||||
# homeassistant.components.watttime
|
||||
aiowatttime==0.1.1
|
||||
@ -436,7 +436,7 @@ base36==0.1.1
|
||||
beautifulsoup4==4.12.2
|
||||
|
||||
# homeassistant.components.zha
|
||||
bellows==0.36.5
|
||||
bellows==0.36.7
|
||||
|
||||
# homeassistant.components.bmw_connected_drive
|
||||
bimmer-connected==0.14.1
|
||||
@ -467,7 +467,7 @@ bluetooth-auto-recovery==1.2.3
|
||||
# homeassistant.components.ld2410_ble
|
||||
# homeassistant.components.led_ble
|
||||
# homeassistant.components.private_ble_device
|
||||
bluetooth-data-tools==1.12.0
|
||||
bluetooth-data-tools==1.13.0
|
||||
|
||||
# homeassistant.components.bond
|
||||
bond-async==0.2.1
|
||||
@ -528,7 +528,7 @@ datadog==0.15.0
|
||||
datapoint==0.9.8
|
||||
|
||||
# homeassistant.components.bluetooth
|
||||
dbus-fast==2.11.1
|
||||
dbus-fast==2.12.0
|
||||
|
||||
# homeassistant.components.debugpy
|
||||
debugpy==1.8.0
|
||||
@ -1061,7 +1061,7 @@ openerz-api==0.2.0
|
||||
openhomedevice==2.2.0
|
||||
|
||||
# homeassistant.components.opower
|
||||
opower==0.0.35
|
||||
opower==0.0.36
|
||||
|
||||
# homeassistant.components.oralb
|
||||
oralb-ble==0.17.6
|
||||
@ -1171,7 +1171,7 @@ pyCEC==0.5.2
|
||||
pyControl4==1.1.0
|
||||
|
||||
# homeassistant.components.duotecno
|
||||
pyDuotecno==2023.10.0
|
||||
pyDuotecno==2023.10.1
|
||||
|
||||
# homeassistant.components.eight_sleep
|
||||
pyEight==0.3.2
|
||||
@ -1759,7 +1759,7 @@ samsungtvws[async,encrypted]==2.6.0
|
||||
scapy==2.5.0
|
||||
|
||||
# homeassistant.components.screenlogic
|
||||
screenlogicpy==0.9.2
|
||||
screenlogicpy==0.9.3
|
||||
|
||||
# homeassistant.components.backup
|
||||
securetar==2023.3.0
|
||||
@ -1966,10 +1966,10 @@ uvcclient==0.11.0
|
||||
vallox-websocket-api==3.3.0
|
||||
|
||||
# homeassistant.components.rdw
|
||||
vehicle==1.0.1
|
||||
vehicle==2.0.0
|
||||
|
||||
# homeassistant.components.velbus
|
||||
velbus-aio==2023.2.0
|
||||
velbus-aio==2023.10.1
|
||||
|
||||
# homeassistant.components.venstar
|
||||
venstarcolortouch==0.19
|
||||
@ -2081,7 +2081,7 @@ zeroconf==0.115.2
|
||||
zeversolar==0.3.1
|
||||
|
||||
# homeassistant.components.zha
|
||||
zha-quirks==0.0.104
|
||||
zha-quirks==0.0.105
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy-deconz==0.21.1
|
||||
@ -2093,7 +2093,7 @@ zigpy-xbee==0.18.3
|
||||
zigpy-zigate==0.11.0
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy-znp==0.11.5
|
||||
zigpy-znp==0.11.6
|
||||
|
||||
# homeassistant.components.zha
|
||||
zigpy==0.57.2
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Define tests for the AEMET OpenData init."""
|
||||
import asyncio
|
||||
from unittest.mock import patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
@ -70,3 +71,29 @@ async def test_init_town_not_found(
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id) is False
|
||||
|
||||
|
||||
async def test_init_api_timeout(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test API timeouts when loading the AEMET integration."""
|
||||
|
||||
hass.config.set_time_zone("UTC")
|
||||
freezer.move_to("2021-01-09 12:00:00+00:00")
|
||||
with patch(
|
||||
"homeassistant.components.aemet.AEMET.api_call",
|
||||
side_effect=asyncio.TimeoutError,
|
||||
):
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={
|
||||
CONF_API_KEY: "api-key",
|
||||
CONF_LATITUDE: "0.0",
|
||||
CONF_LONGITUDE: "0.0",
|
||||
CONF_NAME: "AEMET",
|
||||
},
|
||||
)
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id) is False
|
||||
|
@ -129,6 +129,7 @@ def mock_client(mock_device_info) -> APIClient:
|
||||
mock_client.connect = AsyncMock()
|
||||
mock_client.disconnect = AsyncMock()
|
||||
mock_client.list_entities_services = AsyncMock(return_value=([], []))
|
||||
mock_client.address = "127.0.0.1"
|
||||
mock_client.api_version = APIVersion(99, 99)
|
||||
|
||||
with patch("homeassistant.components.esphome.APIClient", mock_client), patch(
|
||||
|
@ -959,6 +959,17 @@ async def test_subscribe_topic(
|
||||
unsub()
|
||||
|
||||
|
||||
async def test_subscribe_topic_not_initialize(
|
||||
hass: HomeAssistant,
|
||||
mqtt_mock_entry: MqttMockHAClientGenerator,
|
||||
) -> None:
|
||||
"""Test the subscription of a topic when MQTT was not initialized."""
|
||||
with pytest.raises(
|
||||
HomeAssistantError, match=r".*make sure MQTT is set up correctly"
|
||||
):
|
||||
await mqtt.async_subscribe(hass, "test-topic", record_calls)
|
||||
|
||||
|
||||
@patch("homeassistant.components.mqtt.client.INITIAL_SUBSCRIBE_COOLDOWN", 0.0)
|
||||
@patch("homeassistant.components.mqtt.client.UNSUBSCRIBE_COOLDOWN", 0.2)
|
||||
async def test_subscribe_and_resubscribe(
|
||||
|
@ -662,6 +662,12 @@ def logic_group_zdb5100_state_fixture():
|
||||
return json.loads(load_fixture("zwave_js/logic_group_zdb5100_state.json"))
|
||||
|
||||
|
||||
@pytest.fixture(name="climate_intermatic_pe653_state", scope="session")
|
||||
def climate_intermatic_pe653_state_fixture():
|
||||
"""Load Intermatic PE653 Pool Control node state fixture data."""
|
||||
return json.loads(load_fixture("zwave_js/climate_intermatic_pe653_state.json"))
|
||||
|
||||
|
||||
# model fixtures
|
||||
|
||||
|
||||
@ -1290,3 +1296,11 @@ def logic_group_zdb5100_fixture(client, logic_group_zdb5100_state):
|
||||
node = Node(client, copy.deepcopy(logic_group_zdb5100_state))
|
||||
client.driver.controller.nodes[node.node_id] = node
|
||||
return node
|
||||
|
||||
|
||||
@pytest.fixture(name="climate_intermatic_pe653")
|
||||
def climate_intermatic_pe653_fixture(client, climate_intermatic_pe653_state):
|
||||
"""Mock an Intermatic PE653 node."""
|
||||
node = Node(client, copy.deepcopy(climate_intermatic_pe653_state))
|
||||
client.driver.controller.nodes[node.node_id] = node
|
||||
return node
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -792,3 +792,196 @@ async def test_thermostat_raise_repair_issue_and_warning_when_setting_fan_preset
|
||||
"Dry and Fan preset modes are deprecated and will be removed in Home Assistant 2024.2. Please use the corresponding Dry and Fan HVAC modes instead"
|
||||
in caplog.text
|
||||
)
|
||||
|
||||
|
||||
async def test_multi_setpoint_thermostat(
|
||||
hass: HomeAssistant, client, climate_intermatic_pe653, integration
|
||||
) -> None:
|
||||
"""Test a thermostat with multiple setpoints."""
|
||||
node = climate_intermatic_pe653
|
||||
|
||||
heating_entity_id = "climate.pool_control_2"
|
||||
heating = hass.states.get(heating_entity_id)
|
||||
assert heating
|
||||
assert heating.state == HVACMode.HEAT
|
||||
assert heating.attributes[ATTR_TEMPERATURE] == 3.9
|
||||
assert heating.attributes[ATTR_HVAC_MODES] == [HVACMode.HEAT]
|
||||
assert (
|
||||
heating.attributes[ATTR_SUPPORTED_FEATURES]
|
||||
== ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
)
|
||||
|
||||
furnace_entity_id = "climate.pool_control"
|
||||
furnace = hass.states.get(furnace_entity_id)
|
||||
assert furnace
|
||||
assert furnace.state == HVACMode.HEAT
|
||||
assert furnace.attributes[ATTR_TEMPERATURE] == 15.6
|
||||
assert furnace.attributes[ATTR_HVAC_MODES] == [HVACMode.HEAT]
|
||||
assert (
|
||||
furnace.attributes[ATTR_SUPPORTED_FEATURES]
|
||||
== ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
)
|
||||
|
||||
client.async_send_command_no_wait.reset_mock()
|
||||
|
||||
# Test setting temperature of heating setpoint
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_TEMPERATURE,
|
||||
{
|
||||
ATTR_ENTITY_ID: heating_entity_id,
|
||||
ATTR_TEMPERATURE: 20.0,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
# Test setting temperature of furnace setpoint
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_TEMPERATURE,
|
||||
{
|
||||
ATTR_ENTITY_ID: furnace_entity_id,
|
||||
ATTR_TEMPERATURE: 2.0,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
# Test setting illegal mode raises an error
|
||||
with pytest.raises(ValueError):
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_HVAC_MODE,
|
||||
{
|
||||
ATTR_ENTITY_ID: heating_entity_id,
|
||||
ATTR_HVAC_MODE: HVACMode.COOL,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_HVAC_MODE,
|
||||
{
|
||||
ATTR_ENTITY_ID: furnace_entity_id,
|
||||
ATTR_HVAC_MODE: HVACMode.COOL,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
# this is a no-op since there's no mode
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_HVAC_MODE,
|
||||
{
|
||||
ATTR_ENTITY_ID: heating_entity_id,
|
||||
ATTR_HVAC_MODE: HVACMode.HEAT,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
# this is a no-op since there's no mode
|
||||
await hass.services.async_call(
|
||||
CLIMATE_DOMAIN,
|
||||
SERVICE_SET_HVAC_MODE,
|
||||
{
|
||||
ATTR_ENTITY_ID: furnace_entity_id,
|
||||
ATTR_HVAC_MODE: HVACMode.HEAT,
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert len(client.async_send_command.call_args_list) == 2
|
||||
args = client.async_send_command.call_args_list[0][0][0]
|
||||
assert args["command"] == "node.set_value"
|
||||
assert args["nodeId"] == 19
|
||||
assert args["valueId"] == {
|
||||
"endpoint": 1,
|
||||
"commandClass": 67,
|
||||
"property": "setpoint",
|
||||
"propertyKey": 1,
|
||||
}
|
||||
assert args["value"] == 68.0
|
||||
|
||||
args = client.async_send_command.call_args_list[1][0][0]
|
||||
assert args["command"] == "node.set_value"
|
||||
assert args["nodeId"] == 19
|
||||
assert args["valueId"] == {
|
||||
"endpoint": 0,
|
||||
"commandClass": 67,
|
||||
"property": "setpoint",
|
||||
"propertyKey": 7,
|
||||
}
|
||||
assert args["value"] == 35.6
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
# Test heating setpoint value update from value updated event
|
||||
event = Event(
|
||||
type="value updated",
|
||||
data={
|
||||
"source": "node",
|
||||
"event": "value updated",
|
||||
"nodeId": 19,
|
||||
"args": {
|
||||
"commandClassName": "Thermostat Setpoint",
|
||||
"commandClass": 67,
|
||||
"endpoint": 1,
|
||||
"property": "setpoint",
|
||||
"propertyKey": 1,
|
||||
"propertyKeyName": "Heating",
|
||||
"propertyName": "setpoint",
|
||||
"newValue": 23,
|
||||
"prevValue": 21.5,
|
||||
},
|
||||
},
|
||||
)
|
||||
node.receive_event(event)
|
||||
|
||||
state = hass.states.get(heating_entity_id)
|
||||
assert state
|
||||
assert state.state == HVACMode.HEAT
|
||||
assert state.attributes[ATTR_TEMPERATURE] == -5
|
||||
|
||||
# furnace not changed
|
||||
state = hass.states.get(furnace_entity_id)
|
||||
assert state
|
||||
assert state.state == HVACMode.HEAT
|
||||
assert state.attributes[ATTR_TEMPERATURE] == 15.6
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
||||
# Test furnace setpoint value update from value updated event
|
||||
event = Event(
|
||||
type="value updated",
|
||||
data={
|
||||
"source": "node",
|
||||
"event": "value updated",
|
||||
"nodeId": 19,
|
||||
"args": {
|
||||
"commandClassName": "Thermostat Setpoint",
|
||||
"commandClass": 67,
|
||||
"endpoint": 0,
|
||||
"property": "setpoint",
|
||||
"propertyKey": 7,
|
||||
"propertyKeyName": "Furnace",
|
||||
"propertyName": "setpoint",
|
||||
"newValue": 68,
|
||||
"prevValue": 21.5,
|
||||
},
|
||||
},
|
||||
)
|
||||
node.receive_event(event)
|
||||
|
||||
# heating not changed
|
||||
state = hass.states.get(heating_entity_id)
|
||||
assert state
|
||||
assert state.state == HVACMode.HEAT
|
||||
assert state.attributes[ATTR_TEMPERATURE] == -5
|
||||
|
||||
state = hass.states.get(furnace_entity_id)
|
||||
assert state
|
||||
assert state.state == HVACMode.HEAT
|
||||
assert state.attributes[ATTR_TEMPERATURE] == 20
|
||||
|
||||
client.async_send_command.reset_mock()
|
||||
|
Loading…
x
Reference in New Issue
Block a user