This commit is contained in:
Franck Nijhof 2023-10-21 12:00:26 +02:00 committed by GitHub
commit aa5c4d8786
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 4879 additions and 102 deletions

View File

@ -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()

View File

@ -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"]
}

View File

@ -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"
]
}

View File

@ -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,

View File

@ -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"]
}

View File

@ -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

View File

@ -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

View File

@ -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."]

View File

@ -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()

View File

@ -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",

View File

@ -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"]
}

View File

@ -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"]
}

View File

@ -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:

View File

@ -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(

View File

@ -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"]
}

View File

@ -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"]
}

View File

@ -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"]
}

View File

@ -7,5 +7,5 @@
"integration_type": "service",
"iot_class": "cloud_polling",
"quality_scale": "platinum",
"requirements": ["vehicle==1.0.1"]
"requirements": ["vehicle==2.0.0"]
}

View File

@ -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"]
}

View File

@ -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 {}

View File

@ -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)])

View File

@ -13,7 +13,7 @@
"velbus-packet",
"velbus-protocol"
],
"requirements": ["velbus-aio==2023.2.0"],
"requirements": ["velbus-aio==2023.10.1"],
"usb": [
{
"vid": "10CF",

View File

@ -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

View File

@ -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."""

View File

@ -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"]
}

View File

@ -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"
],

View File

@ -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

View File

@ -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": {

View File

@ -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)

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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(

View File

@ -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(

View File

@ -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

View File

@ -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()