diff --git a/homeassistant/components/airvisual/manifest.json b/homeassistant/components/airvisual/manifest.json index f7f509e2593..7934d809287 100644 --- a/homeassistant/components/airvisual/manifest.json +++ b/homeassistant/components/airvisual/manifest.json @@ -8,5 +8,5 @@ "integration_type": "service", "iot_class": "cloud_polling", "loggers": ["pyairvisual", "pysmb"], - "requirements": ["pyairvisual==2022.12.1"] + "requirements": ["pyairvisual==2023.08.1"] } diff --git a/homeassistant/components/airvisual_pro/manifest.json b/homeassistant/components/airvisual_pro/manifest.json index 0859754ba18..32dbc23a421 100644 --- a/homeassistant/components/airvisual_pro/manifest.json +++ b/homeassistant/components/airvisual_pro/manifest.json @@ -7,5 +7,5 @@ "integration_type": "device", "iot_class": "local_polling", "loggers": ["pyairvisual", "pysmb"], - "requirements": ["pyairvisual==2022.12.1"] + "requirements": ["pyairvisual==2023.08.1"] } diff --git a/homeassistant/components/airzone/manifest.json b/homeassistant/components/airzone/manifest.json index 88b918f699c..39adf08236e 100644 --- a/homeassistant/components/airzone/manifest.json +++ b/homeassistant/components/airzone/manifest.json @@ -11,5 +11,5 @@ "documentation": "https://www.home-assistant.io/integrations/airzone", "iot_class": "local_polling", "loggers": ["aioairzone"], - "requirements": ["aioairzone==0.6.4"] + "requirements": ["aioairzone==0.6.5"] } diff --git a/homeassistant/components/aladdin_connect/manifest.json b/homeassistant/components/aladdin_connect/manifest.json index 2702f2e8dec..3f31a833f1a 100644 --- a/homeassistant/components/aladdin_connect/manifest.json +++ b/homeassistant/components/aladdin_connect/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/aladdin_connect", "iot_class": "cloud_polling", "loggers": ["aladdin_connect"], - "requirements": ["AIOAladdinConnect==0.1.56"] + "requirements": ["AIOAladdinConnect==0.1.57"] } diff --git a/homeassistant/components/alert/__init__.py b/homeassistant/components/alert/__init__.py index 9b3fb0f29c8..721ed0d0c21 100644 --- a/homeassistant/components/alert/__init__.py +++ b/homeassistant/components/alert/__init__.py @@ -26,6 +26,7 @@ from homeassistant.const import ( STATE_ON, ) from homeassistant.core import HassJob, HomeAssistant +from homeassistant.exceptions import ServiceNotFound import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_component import EntityComponent @@ -293,9 +294,15 @@ class Alert(Entity): LOGGER.debug(msg_payload) for target in self._notifiers: - await self.hass.services.async_call( - DOMAIN_NOTIFY, target, msg_payload, context=self._context - ) + try: + await self.hass.services.async_call( + DOMAIN_NOTIFY, target, msg_payload, context=self._context + ) + except ServiceNotFound: + LOGGER.error( + "Failed to call notify.%s, retrying at next notification interval", + target, + ) async def async_turn_on(self, **kwargs: Any) -> None: """Async Unacknowledge alert.""" diff --git a/homeassistant/components/androidtv_remote/config_flow.py b/homeassistant/components/androidtv_remote/config_flow.py index b8399fd7ba2..d5c361674bd 100644 --- a/homeassistant/components/androidtv_remote/config_flow.py +++ b/homeassistant/components/androidtv_remote/config_flow.py @@ -58,6 +58,7 @@ class AndroidTVRemoteConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): assert self.host api = create_api(self.hass, self.host, enable_ime=False) try: + await api.async_generate_cert_if_missing() self.name, self.mac = await api.async_get_name_and_mac() assert self.mac await self.async_set_unique_id(format_mac(self.mac)) diff --git a/homeassistant/components/apple_tv/manifest.json b/homeassistant/components/apple_tv/manifest.json index 1d1c26b5fcd..a22687c0fb5 100644 --- a/homeassistant/components/apple_tv/manifest.json +++ b/homeassistant/components/apple_tv/manifest.json @@ -7,7 +7,7 @@ "documentation": "https://www.home-assistant.io/integrations/apple_tv", "iot_class": "local_push", "loggers": ["pyatv", "srptools"], - "requirements": ["pyatv==0.13.3"], + "requirements": ["pyatv==0.13.4"], "zeroconf": [ "_mediaremotetv._tcp.local.", "_companion-link._tcp.local.", diff --git a/homeassistant/components/bmw_connected_drive/manifest.json b/homeassistant/components/bmw_connected_drive/manifest.json index 82426fbce08..ff2804a8c04 100644 --- a/homeassistant/components/bmw_connected_drive/manifest.json +++ b/homeassistant/components/bmw_connected_drive/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive", "iot_class": "cloud_polling", "loggers": ["bimmer_connected"], - "requirements": ["bimmer-connected==0.13.8"] + "requirements": ["bimmer-connected==0.13.9"] } diff --git a/homeassistant/components/broadlink/light.py b/homeassistant/components/broadlink/light.py index d42e2b76b99..796698c6a4c 100644 --- a/homeassistant/components/broadlink/light.py +++ b/homeassistant/components/broadlink/light.py @@ -45,6 +45,7 @@ class BroadlinkLight(BroadlinkEntity, LightEntity): """Representation of a Broadlink light.""" _attr_has_entity_name = True + _attr_name = None def __init__(self, device): """Initialize the light.""" diff --git a/homeassistant/components/command_line/sensor.py b/homeassistant/components/command_line/sensor.py index dd5ad2d5190..2ccbdbc4785 100644 --- a/homeassistant/components/command_line/sensor.py +++ b/homeassistant/components/command_line/sensor.py @@ -207,7 +207,8 @@ class CommandSensor(ManualTriggerEntity, SensorEntity): self._process_manual_data(value) return - if self._value_template is not None: + self._attr_native_value = None + if self._value_template is not None and value is not None: value = self._value_template.async_render_with_possible_json_value( value, None, @@ -221,7 +222,6 @@ class CommandSensor(ManualTriggerEntity, SensorEntity): self._process_manual_data(value) return - self._attr_native_value = None if value is not None: self._attr_native_value = async_parse_date_datetime( value, self.entity_id, self.device_class diff --git a/homeassistant/components/fritz/common.py b/homeassistant/components/fritz/common.py index 8dfe5be9308..531c05eea4a 100644 --- a/homeassistant/components/fritz/common.py +++ b/homeassistant/components/fritz/common.py @@ -160,6 +160,15 @@ HostAttributes = TypedDict( ) +class HostInfo(TypedDict): + """FRITZ!Box host info class.""" + + mac: str + name: str + ip: str + status: bool + + class UpdateCoordinatorDataType(TypedDict): """Update coordinator data type.""" @@ -380,16 +389,86 @@ class FritzBoxTools( """Event specific per FRITZ!Box entry to signal updates in devices.""" return f"{DOMAIN}-device-update-{self._unique_id}" - async def _async_update_hosts_info(self) -> list[HostAttributes]: - """Retrieve latest hosts information from the FRITZ!Box.""" + async def _async_get_wan_access(self, ip_address: str) -> bool | None: + """Get WAN access rule for given IP address.""" try: - return await self.hass.async_add_executor_job( - self.fritz_hosts.get_hosts_attributes + wan_access = await self.hass.async_add_executor_job( + partial( + self.connection.call_action, + "X_AVM-DE_HostFilter:1", + "GetWANAccessByIP", + NewIPv4Address=ip_address, + ) ) + return not wan_access.get("NewDisallow") + except FRITZ_EXCEPTIONS as ex: + _LOGGER.debug( + ( + "could not get WAN access rule for client device with IP '%s'," + " error: %s" + ), + ip_address, + ex, + ) + return None + + async def _async_update_hosts_info(self) -> dict[str, Device]: + """Retrieve latest hosts information from the FRITZ!Box.""" + hosts_attributes: list[HostAttributes] = [] + hosts_info: list[HostInfo] = [] + try: + try: + hosts_attributes = await self.hass.async_add_executor_job( + self.fritz_hosts.get_hosts_attributes + ) + except FritzActionError: + hosts_info = await self.hass.async_add_executor_job( + self.fritz_hosts.get_hosts_info + ) except Exception as ex: # pylint: disable=[broad-except] if not self.hass.is_stopping: raise HomeAssistantError("Error refreshing hosts info") from ex - return [] + + hosts: dict[str, Device] = {} + if hosts_attributes: + for attributes in hosts_attributes: + if not attributes.get("MACAddress"): + continue + + if (wan_access := attributes.get("X_AVM-DE_WANAccess")) is not None: + wan_access_result = "granted" in wan_access + else: + wan_access_result = None + + hosts[attributes["MACAddress"]] = Device( + name=attributes["HostName"], + connected=attributes["Active"], + connected_to="", + connection_type="", + ip_address=attributes["IPAddress"], + ssid=None, + wan_access=wan_access_result, + ) + else: + for info in hosts_info: + if not info.get("mac"): + continue + + if info["ip"]: + wan_access_result = await self._async_get_wan_access(info["ip"]) + else: + wan_access_result = None + + hosts[info["mac"]] = Device( + name=info["name"], + connected=info["status"], + connected_to="", + connection_type="", + ip_address=info["ip"], + ssid=None, + wan_access=wan_access_result, + ) + return hosts def _update_device_info(self) -> tuple[bool, str | None, str | None]: """Retrieve latest device information from the FRITZ!Box.""" @@ -464,25 +543,7 @@ class FritzBoxTools( consider_home = _default_consider_home new_device = False - hosts = {} - for host in await self._async_update_hosts_info(): - if not host.get("MACAddress"): - continue - - if (wan_access := host.get("X_AVM-DE_WANAccess")) is not None: - wan_access_result = "granted" in wan_access - else: - wan_access_result = None - - hosts[host["MACAddress"]] = Device( - name=host["HostName"], - connected=host["Active"], - connected_to="", - connection_type="", - ip_address=host["IPAddress"], - ssid=None, - wan_access=wan_access_result, - ) + hosts = await self._async_update_hosts_info() if not self.fritz_status.device_has_mesh_support or ( self._options @@ -584,9 +645,7 @@ class FritzBoxTools( self, config_entry: ConfigEntry | None = None ) -> None: """Trigger device trackers cleanup.""" - device_hosts_list = await self.hass.async_add_executor_job( - self.fritz_hosts.get_hosts_attributes - ) + device_hosts = await self._async_update_hosts_info() entity_reg: er.EntityRegistry = er.async_get(self.hass) if config_entry is None: @@ -601,9 +660,9 @@ class FritzBoxTools( device_hosts_macs = set() device_hosts_names = set() - for device in device_hosts_list: - device_hosts_macs.add(device["MACAddress"]) - device_hosts_names.add(device["HostName"]) + for mac, device in device_hosts.items(): + device_hosts_macs.add(mac) + device_hosts_names.add(device.name) for entry in ha_entity_reg_list: if entry.original_name is None: diff --git a/homeassistant/components/gardena_bluetooth/number.py b/homeassistant/components/gardena_bluetooth/number.py index c425d17621d..ec887458586 100644 --- a/homeassistant/components/gardena_bluetooth/number.py +++ b/homeassistant/components/gardena_bluetooth/number.py @@ -62,11 +62,11 @@ DESCRIPTIONS = ( GardenaBluetoothNumberEntityDescription( key=DeviceConfiguration.rain_pause.uuid, translation_key="rain_pause", - native_unit_of_measurement=UnitOfTime.DAYS, + native_unit_of_measurement=UnitOfTime.MINUTES, mode=NumberMode.BOX, native_min_value=0.0, - native_max_value=127.0, - native_step=1.0, + native_max_value=7 * 24 * 60, + native_step=6 * 60.0, entity_category=EntityCategory.CONFIG, char=DeviceConfiguration.rain_pause, ), diff --git a/homeassistant/components/gardena_bluetooth/sensor.py b/homeassistant/components/gardena_bluetooth/sensor.py index eaa44d9d4fb..ebc83ae88af 100644 --- a/homeassistant/components/gardena_bluetooth/sensor.py +++ b/homeassistant/components/gardena_bluetooth/sensor.py @@ -117,3 +117,8 @@ class GardenaBluetoothRemainSensor(GardenaBluetoothEntity, SensorEntity): self._attr_native_value = time super()._handle_coordinator_update() return + + @property + def available(self) -> bool: + """Sensor only available when open.""" + return super().available and self._attr_native_value is not None diff --git a/homeassistant/components/gardena_bluetooth/strings.json b/homeassistant/components/gardena_bluetooth/strings.json index 1d9a281fdbc..1fc6e10b5a6 100644 --- a/homeassistant/components/gardena_bluetooth/strings.json +++ b/homeassistant/components/gardena_bluetooth/strings.json @@ -15,7 +15,8 @@ "cannot_connect": "Failed to connect: {error}" }, "abort": { - "already_configured": "[%key:common::config_flow::abort::already_configured_device%]" + "already_configured": "[%key:common::config_flow::abort::already_configured_device%]", + "no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]" } }, "entity": { diff --git a/homeassistant/components/homekit_controller/device_trigger.py b/homeassistant/components/homekit_controller/device_trigger.py index bbc56ddd4a4..9eab0fbb098 100644 --- a/homeassistant/components/homekit_controller/device_trigger.py +++ b/homeassistant/components/homekit_controller/device_trigger.py @@ -80,11 +80,11 @@ class TriggerSource: self._iid_trigger_keys.setdefault(iid, set()).add(trigger_key) await connection.add_watchable_characteristics([(aid, iid)]) - def fire(self, iid: int, value: dict[str, Any]) -> None: + def fire(self, iid: int, ev: dict[str, Any]) -> None: """Process events that have been received from a HomeKit accessory.""" for trigger_key in self._iid_trigger_keys.get(iid, set()): for event_handler in self._callbacks.get(trigger_key, []): - event_handler(value) + event_handler(ev) def async_get_triggers(self) -> Generator[tuple[str, str], None, None]: """List device triggers for HomeKit devices.""" @@ -99,20 +99,23 @@ class TriggerSource: ) -> CALLBACK_TYPE: """Attach a trigger.""" trigger_data = trigger_info["trigger_data"] - trigger_key = (config[CONF_TYPE], config[CONF_SUBTYPE]) + type_: str = config[CONF_TYPE] + sub_type: str = config[CONF_SUBTYPE] + trigger_key = (type_, sub_type) job = HassJob(action) + trigger_callbacks = self._callbacks.setdefault(trigger_key, []) + hass = self._hass @callback - def event_handler(char: dict[str, Any]) -> None: - if config[CONF_SUBTYPE] != HK_TO_HA_INPUT_EVENT_VALUES[char["value"]]: + def event_handler(ev: dict[str, Any]) -> None: + if sub_type != HK_TO_HA_INPUT_EVENT_VALUES[ev["value"]]: return - self._hass.async_run_hass_job(job, {"trigger": {**trigger_data, **config}}) + hass.async_run_hass_job(job, {"trigger": {**trigger_data, **config}}) - self._callbacks.setdefault(trigger_key, []).append(event_handler) + trigger_callbacks.append(event_handler) def async_remove_handler(): - if trigger_key in self._callbacks: - self._callbacks[trigger_key].remove(event_handler) + trigger_callbacks.remove(event_handler) return async_remove_handler @@ -262,7 +265,10 @@ def async_fire_triggers(conn: HKDevice, events: dict[tuple[int, int], dict[str, if aid in conn.devices: device_id = conn.devices[aid] if source := trigger_sources.get(device_id): - source.fire(iid, ev) + # If the value is None, we received the event via polling + # and we don't want to trigger on that + if ev.get("value") is not None: + source.fire(iid, ev) async def async_get_triggers( diff --git a/homeassistant/components/homekit_controller/entity.py b/homeassistant/components/homekit_controller/entity.py index 046dc9f17ec..6ebe777d5f8 100644 --- a/homeassistant/components/homekit_controller/entity.py +++ b/homeassistant/components/homekit_controller/entity.py @@ -5,6 +5,7 @@ from typing import Any from aiohomekit.model import Accessory from aiohomekit.model.characteristics import ( + EVENT_CHARACTERISTICS, Characteristic, CharacteristicPermissions, CharacteristicsTypes, @@ -111,7 +112,10 @@ class HomeKitEntity(Entity): def _setup_characteristic(self, char: Characteristic) -> None: """Configure an entity based on a HomeKit characteristics metadata.""" # Build up a list of (aid, iid) tuples to poll on update() - if CharacteristicPermissions.paired_read in char.perms: + if ( + CharacteristicPermissions.paired_read in char.perms + and char.type not in EVENT_CHARACTERISTICS + ): self.pollable_characteristics.append((self._aid, char.iid)) # Build up a list of (aid, iid) tuples to subscribe to diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 8cc80ef864e..52a91d42e67 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -14,6 +14,6 @@ "documentation": "https://www.home-assistant.io/integrations/homekit_controller", "iot_class": "local_push", "loggers": ["aiohomekit", "commentjson"], - "requirements": ["aiohomekit==2.6.12"], + "requirements": ["aiohomekit==2.6.15"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."] } diff --git a/homeassistant/components/mazda/manifest.json b/homeassistant/components/mazda/manifest.json index dd29d02d655..881120a0677 100644 --- a/homeassistant/components/mazda/manifest.json +++ b/homeassistant/components/mazda/manifest.json @@ -7,5 +7,5 @@ "iot_class": "cloud_polling", "loggers": ["pymazda"], "quality_scale": "platinum", - "requirements": ["pymazda==0.3.10"] + "requirements": ["pymazda==0.3.11"] } diff --git a/homeassistant/components/melcloud/strings.json b/homeassistant/components/melcloud/strings.json index bef65e28880..e5447952c5e 100644 --- a/homeassistant/components/melcloud/strings.json +++ b/homeassistant/components/melcloud/strings.json @@ -26,7 +26,7 @@ "fields": { "position": { "name": "Position", - "description": "Horizontal vane position. Possible options can be found in the vane_horizontal_positions state attribute.\n." + "description": "Horizontal vane position. Possible options can be found in the vane_horizontal_positions state attribute." } } }, @@ -36,7 +36,7 @@ "fields": { "position": { "name": "Position", - "description": "Vertical vane position. Possible options can be found in the vane_vertical_positions state attribute.\n." + "description": "Vertical vane position. Possible options can be found in the vane_vertical_positions state attribute." } } } diff --git a/homeassistant/components/metoffice/sensor.py b/homeassistant/components/metoffice/sensor.py index 3bf50525ca9..fcb8e5b134e 100644 --- a/homeassistant/components/metoffice/sensor.py +++ b/homeassistant/components/metoffice/sensor.py @@ -51,14 +51,12 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="name", name="Station name", - device_class=None, icon="mdi:label-outline", entity_registry_enabled_default=False, ), SensorEntityDescription( key="weather", name="Weather", - device_class=None, icon="mdi:weather-sunny", # but will adapt to current conditions entity_registry_enabled_default=True, ), @@ -107,7 +105,6 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="visibility", name="Visibility", - device_class=None, icon="mdi:eye", entity_registry_enabled_default=False, ), @@ -115,14 +112,12 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( key="visibility_distance", name="Visibility distance", native_unit_of_measurement=UnitOfLength.KILOMETERS, - device_class=SensorDeviceClass.DISTANCE, icon="mdi:eye", entity_registry_enabled_default=False, ), SensorEntityDescription( key="uv", name="UV index", - device_class=None, native_unit_of_measurement=UV_INDEX, icon="mdi:weather-sunny-alert", entity_registry_enabled_default=True, @@ -130,7 +125,6 @@ SENSOR_TYPES: tuple[SensorEntityDescription, ...] = ( SensorEntityDescription( key="precipitation", name="Probability of precipitation", - device_class=None, native_unit_of_measurement=PERCENTAGE, icon="mdi:weather-rainy", entity_registry_enabled_default=True, diff --git a/homeassistant/components/modbus/sensor.py b/homeassistant/components/modbus/sensor.py index ca8246577fd..a1c89677e4f 100644 --- a/homeassistant/components/modbus/sensor.py +++ b/homeassistant/components/modbus/sensor.py @@ -49,7 +49,7 @@ async def async_setup_platform( hub = get_hub(hass, discovery_info[CONF_NAME]) for entry in discovery_info[CONF_SENSORS]: slave_count = entry.get(CONF_SLAVE_COUNT, 0) - sensor = ModbusRegisterSensor(hub, entry) + sensor = ModbusRegisterSensor(hub, entry, slave_count) if slave_count > 0: sensors.extend(await sensor.async_setup_slaves(hass, slave_count, entry)) sensors.append(sensor) @@ -63,9 +63,12 @@ class ModbusRegisterSensor(BaseStructPlatform, RestoreSensor, SensorEntity): self, hub: ModbusHub, entry: dict[str, Any], + slave_count: int, ) -> None: """Initialize the modbus register sensor.""" super().__init__(hub, entry) + if slave_count: + self._count = self._count * slave_count self._coordinator: DataUpdateCoordinator[list[int] | None] | None = None self._attr_native_unit_of_measurement = entry.get(CONF_UNIT_OF_MEASUREMENT) self._attr_state_class = entry.get(CONF_STATE_CLASS) diff --git a/homeassistant/components/nina/manifest.json b/homeassistant/components/nina/manifest.json index d1897b53e04..df09d168827 100644 --- a/homeassistant/components/nina/manifest.json +++ b/homeassistant/components/nina/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/nina", "iot_class": "cloud_polling", "loggers": ["pynina"], - "requirements": ["PyNINA==0.3.1"] + "requirements": ["PyNINA==0.3.2"] } diff --git a/homeassistant/components/nws/manifest.json b/homeassistant/components/nws/manifest.json index 7f5d01f9897..05194d85a26 100644 --- a/homeassistant/components/nws/manifest.json +++ b/homeassistant/components/nws/manifest.json @@ -7,5 +7,5 @@ "iot_class": "cloud_polling", "loggers": ["metar", "pynws"], "quality_scale": "platinum", - "requirements": ["pynws==1.5.0"] + "requirements": ["pynws==1.5.1"] } diff --git a/homeassistant/components/opower/coordinator.py b/homeassistant/components/opower/coordinator.py index c331f45bc49..b346df1211c 100644 --- a/homeassistant/components/opower/coordinator.py +++ b/homeassistant/components/opower/coordinator.py @@ -69,12 +69,12 @@ class OpowerCoordinator(DataUpdateCoordinator[dict[str, Forecast]]): raise ConfigEntryAuthFailed from err forecasts: list[Forecast] = await self.api.async_get_forecast() _LOGGER.debug("Updating sensor data with: %s", forecasts) - await self._insert_statistics([forecast.account for forecast in forecasts]) + await self._insert_statistics() return {forecast.account.utility_account_id: forecast for forecast in forecasts} - async def _insert_statistics(self, accounts: list[Account]) -> None: + async def _insert_statistics(self) -> None: """Insert Opower statistics.""" - for account in accounts: + for account in await self.api.async_get_accounts(): id_prefix = "_".join( ( self.api.utility.subdomain(), diff --git a/homeassistant/components/opower/manifest.json b/homeassistant/components/opower/manifest.json index 94758720722..73942231b40 100644 --- a/homeassistant/components/opower/manifest.json +++ b/homeassistant/components/opower/manifest.json @@ -6,5 +6,5 @@ "dependencies": ["recorder"], "documentation": "https://www.home-assistant.io/integrations/opower", "iot_class": "cloud_polling", - "requirements": ["opower==0.0.20"] + "requirements": ["opower==0.0.26"] } diff --git a/homeassistant/components/opower/sensor.py b/homeassistant/components/opower/sensor.py index 36f88a36e8a..ad94d8cafb6 100644 --- a/homeassistant/components/opower/sensor.py +++ b/homeassistant/components/opower/sensor.py @@ -188,7 +188,7 @@ async def async_setup_entry( sensors = ELEC_SENSORS elif ( forecast.account.meter_type == MeterType.GAS - and forecast.unit_of_measure == UnitOfMeasure.THERM + and forecast.unit_of_measure in [UnitOfMeasure.THERM, UnitOfMeasure.CCF] ): sensors = GAS_SENSORS for sensor in sensors: diff --git a/homeassistant/components/roborock/manifest.json b/homeassistant/components/roborock/manifest.json index eda6a5609a2..05fff332c67 100644 --- a/homeassistant/components/roborock/manifest.json +++ b/homeassistant/components/roborock/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/roborock", "iot_class": "local_polling", "loggers": ["roborock"], - "requirements": ["python-roborock==0.31.1"] + "requirements": ["python-roborock==0.32.2"] } diff --git a/homeassistant/components/sensibo/manifest.json b/homeassistant/components/sensibo/manifest.json index 26182102442..42964ddce8f 100644 --- a/homeassistant/components/sensibo/manifest.json +++ b/homeassistant/components/sensibo/manifest.json @@ -15,5 +15,5 @@ "iot_class": "cloud_polling", "loggers": ["pysensibo"], "quality_scale": "platinum", - "requirements": ["pysensibo==1.0.32"] + "requirements": ["pysensibo==1.0.33"] } diff --git a/homeassistant/components/syncthru/__init__.py b/homeassistant/components/syncthru/__init__.py index f77f68450a4..424b81ac800 100644 --- a/homeassistant/components/syncthru/__init__.py +++ b/homeassistant/components/syncthru/__init__.py @@ -67,7 +67,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: config_entry_id=entry.entry_id, configuration_url=printer.url, connections=device_connections(printer), - default_manufacturer="Samsung", + manufacturer="Samsung", identifiers=device_identifiers(printer), model=printer.model(), name=printer.hostname(), diff --git a/homeassistant/components/tado/__init__.py b/homeassistant/components/tado/__init__.py index 1cd21634c8e..b57d384124c 100644 --- a/homeassistant/components/tado/__init__.py +++ b/homeassistant/components/tado/__init__.py @@ -163,12 +163,11 @@ class TadoConnector: def setup(self): """Connect to Tado and fetch the zones.""" - self.tado = Tado(self._username, self._password) - self.tado.setDebugging(True) + self.tado = Tado(self._username, self._password, None, True) # Load zones and devices - self.zones = self.tado.getZones() - self.devices = self.tado.getDevices() - tado_home = self.tado.getMe()["homes"][0] + self.zones = self.tado.get_zones() + self.devices = self.tado.get_devices() + tado_home = self.tado.get_me()["homes"][0] self.home_id = tado_home["id"] self.home_name = tado_home["name"] @@ -181,7 +180,7 @@ class TadoConnector: def update_devices(self): """Update the device data from Tado.""" - devices = self.tado.getDevices() + devices = self.tado.get_devices() for device in devices: device_short_serial_no = device["shortSerialNo"] _LOGGER.debug("Updating device %s", device_short_serial_no) @@ -190,7 +189,7 @@ class TadoConnector: INSIDE_TEMPERATURE_MEASUREMENT in device["characteristics"]["capabilities"] ): - device[TEMP_OFFSET] = self.tado.getDeviceInfo( + device[TEMP_OFFSET] = self.tado.get_device_info( device_short_serial_no, TEMP_OFFSET ) except RuntimeError: @@ -218,7 +217,7 @@ class TadoConnector: def update_zones(self): """Update the zone data from Tado.""" try: - zone_states = self.tado.getZoneStates()["zoneStates"] + zone_states = self.tado.get_zone_states()["zoneStates"] except RuntimeError: _LOGGER.error("Unable to connect to Tado while updating zones") return @@ -230,7 +229,7 @@ class TadoConnector: """Update the internal data from Tado.""" _LOGGER.debug("Updating zone %s", zone_id) try: - data = self.tado.getZoneState(zone_id) + data = self.tado.get_zone_state(zone_id) except RuntimeError: _LOGGER.error("Unable to connect to Tado while updating zone %s", zone_id) return @@ -251,8 +250,8 @@ class TadoConnector: def update_home(self): """Update the home data from Tado.""" try: - self.data["weather"] = self.tado.getWeather() - self.data["geofence"] = self.tado.getHomeState() + self.data["weather"] = self.tado.get_weather() + self.data["geofence"] = self.tado.get_home_state() dispatcher_send( self.hass, SIGNAL_TADO_UPDATE_RECEIVED.format(self.home_id, "home", "data"), @@ -265,15 +264,15 @@ class TadoConnector: def get_capabilities(self, zone_id): """Return the capabilities of the devices.""" - return self.tado.getCapabilities(zone_id) + return self.tado.get_capabilities(zone_id) def get_auto_geofencing_supported(self): """Return whether the Tado Home supports auto geofencing.""" - return self.tado.getAutoGeofencingSupported() + return self.tado.get_auto_geofencing_supported() def reset_zone_overlay(self, zone_id): """Reset the zone back to the default operation.""" - self.tado.resetZoneOverlay(zone_id) + self.tado.reset_zone_overlay(zone_id) self.update_zone(zone_id) def set_presence( @@ -282,11 +281,11 @@ class TadoConnector: ): """Set the presence to home, away or auto.""" if presence == PRESET_AWAY: - self.tado.setAway() + self.tado.set_away() elif presence == PRESET_HOME: - self.tado.setHome() + self.tado.set_home() elif presence == PRESET_AUTO: - self.tado.setAuto() + self.tado.set_auto() # Update everything when changing modes self.update_zones() @@ -320,7 +319,7 @@ class TadoConnector: ) try: - self.tado.setZoneOverlay( + self.tado.set_zone_overlay( zone_id, overlay_mode, temperature, @@ -340,7 +339,7 @@ class TadoConnector: def set_zone_off(self, zone_id, overlay_mode, device_type="HEATING"): """Set a zone to off.""" try: - self.tado.setZoneOverlay( + self.tado.set_zone_overlay( zone_id, overlay_mode, None, None, device_type, "OFF" ) except RequestException as exc: @@ -351,6 +350,6 @@ class TadoConnector: def set_temperature_offset(self, device_id, offset): """Set temperature offset of device.""" try: - self.tado.setTempOffset(device_id, offset) + self.tado.set_temp_offset(device_id, offset) except RequestException as exc: _LOGGER.error("Could not set temperature offset: %s", exc) diff --git a/homeassistant/components/tado/manifest.json b/homeassistant/components/tado/manifest.json index 62f7a377239..bea608514bd 100644 --- a/homeassistant/components/tado/manifest.json +++ b/homeassistant/components/tado/manifest.json @@ -14,5 +14,5 @@ }, "iot_class": "cloud_polling", "loggers": ["PyTado"], - "requirements": ["python-tado==0.15.0"] + "requirements": ["python-tado==0.16.0"] } diff --git a/homeassistant/components/tomorrowio/config_flow.py b/homeassistant/components/tomorrowio/config_flow.py index cdb0032431c..d6855f42c0a 100644 --- a/homeassistant/components/tomorrowio/config_flow.py +++ b/homeassistant/components/tomorrowio/config_flow.py @@ -102,7 +102,7 @@ class TomorrowioOptionsConfigFlow(config_entries.OptionsFlow): vol.Required( CONF_TIMESTEP, default=self._config_entry.options[CONF_TIMESTEP], - ): vol.In([1, 5, 15, 30]), + ): vol.In([1, 5, 15, 30, 60]), } return self.async_show_form( diff --git a/homeassistant/components/tomorrowio/const.py b/homeassistant/components/tomorrowio/const.py index 51d8d5f31cc..7ad6ea60836 100644 --- a/homeassistant/components/tomorrowio/const.py +++ b/homeassistant/components/tomorrowio/const.py @@ -25,7 +25,7 @@ LOGGER = logging.getLogger(__package__) CONF_TIMESTEP = "timestep" FORECAST_TYPES = [DAILY, HOURLY, NOWCAST] -DEFAULT_TIMESTEP = 15 +DEFAULT_TIMESTEP = 60 DEFAULT_FORECAST_TYPE = DAILY DOMAIN = "tomorrowio" INTEGRATION_NAME = "Tomorrow.io" diff --git a/homeassistant/components/tplink_omada/manifest.json b/homeassistant/components/tplink_omada/manifest.json index 795e6adf5b7..9c303b24661 100644 --- a/homeassistant/components/tplink_omada/manifest.json +++ b/homeassistant/components/tplink_omada/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/tplink_omada", "integration_type": "hub", "iot_class": "local_polling", - "requirements": ["tplink_omada_client==1.2.4"] + "requirements": ["tplink_omada_client==1.3.2"] } diff --git a/homeassistant/components/tractive/device_tracker.py b/homeassistant/components/tractive/device_tracker.py index e9739819734..a97ea963362 100644 --- a/homeassistant/components/tractive/device_tracker.py +++ b/homeassistant/components/tractive/device_tracker.py @@ -43,7 +43,7 @@ class TractiveDeviceTracker(TractiveEntity, TrackerEntity): """Initialize tracker entity.""" super().__init__(user_id, item.trackable, item.tracker_details) - self._battery_level: int = item.hw_info["battery_level"] + self._battery_level: int | None = item.hw_info.get("battery_level") self._latitude: float = item.pos_report["latlong"][0] self._longitude: float = item.pos_report["latlong"][1] self._accuracy: int = item.pos_report["pos_uncertainty"] @@ -75,7 +75,7 @@ class TractiveDeviceTracker(TractiveEntity, TrackerEntity): return self._accuracy @property - def battery_level(self) -> int: + def battery_level(self) -> int | None: """Return the battery level of the device.""" return self._battery_level diff --git a/homeassistant/components/tuya/cover.py b/homeassistant/components/tuya/cover.py index 3505bbf9f22..da9f7d29eb2 100644 --- a/homeassistant/components/tuya/cover.py +++ b/homeassistant/components/tuya/cover.py @@ -44,6 +44,7 @@ COVERS: dict[str, tuple[TuyaCoverEntityDescription, ...]] = { "cl": ( TuyaCoverEntityDescription( key=DPCode.CONTROL, + translation_key="curtain", current_state=DPCode.SITUATION_SET, current_position=(DPCode.PERCENT_CONTROL, DPCode.PERCENT_STATE), set_position=DPCode.PERCENT_CONTROL, @@ -65,6 +66,7 @@ COVERS: dict[str, tuple[TuyaCoverEntityDescription, ...]] = { ), TuyaCoverEntityDescription( key=DPCode.MACH_OPERATE, + translation_key="curtain", current_position=DPCode.POSITION, set_position=DPCode.POSITION, device_class=CoverDeviceClass.CURTAIN, @@ -76,6 +78,7 @@ COVERS: dict[str, tuple[TuyaCoverEntityDescription, ...]] = { # It is used by the Kogan Smart Blinds Driver TuyaCoverEntityDescription( key=DPCode.SWITCH_1, + translation_key="blind", current_position=DPCode.PERCENT_CONTROL, set_position=DPCode.PERCENT_CONTROL, device_class=CoverDeviceClass.BLIND, @@ -111,6 +114,7 @@ COVERS: dict[str, tuple[TuyaCoverEntityDescription, ...]] = { "clkg": ( TuyaCoverEntityDescription( key=DPCode.CONTROL, + translation_key="curtain", current_position=DPCode.PERCENT_CONTROL, set_position=DPCode.PERCENT_CONTROL, device_class=CoverDeviceClass.CURTAIN, @@ -128,6 +132,7 @@ COVERS: dict[str, tuple[TuyaCoverEntityDescription, ...]] = { "jdcljqr": ( TuyaCoverEntityDescription( key=DPCode.CONTROL, + translation_key="curtain", current_position=DPCode.PERCENT_STATE, set_position=DPCode.PERCENT_CONTROL, device_class=CoverDeviceClass.CURTAIN, diff --git a/homeassistant/components/tuya/strings.json b/homeassistant/components/tuya/strings.json index db16015ba56..1ea58f5029f 100644 --- a/homeassistant/components/tuya/strings.json +++ b/homeassistant/components/tuya/strings.json @@ -71,6 +71,12 @@ } }, "cover": { + "blind": { + "name": "[%key:component::cover::entity_component::blind::name%]" + }, + "curtain": { + "name": "[%key:component::cover::entity_component::curtain::name%]" + }, "curtain_2": { "name": "Curtain 2" }, diff --git a/homeassistant/components/tuya/switch.py b/homeassistant/components/tuya/switch.py index 676991fe167..a48d797555c 100644 --- a/homeassistant/components/tuya/switch.py +++ b/homeassistant/components/tuya/switch.py @@ -105,11 +105,11 @@ SWITCHES: dict[str, tuple[SwitchEntityDescription, ...]] = { translation_key="plug", ), ), - # Cirquit Breaker + # Circuit Breaker "dlq": ( SwitchEntityDescription( key=DPCode.CHILD_LOCK, - translation_key="asd", + translation_key="child_lock", icon="mdi:account-lock", entity_category=EntityCategory.CONFIG, ), diff --git a/homeassistant/components/unifi/manifest.json b/homeassistant/components/unifi/manifest.json index 4cc45ddb6b8..3b1fa68638b 100644 --- a/homeassistant/components/unifi/manifest.json +++ b/homeassistant/components/unifi/manifest.json @@ -8,7 +8,7 @@ "iot_class": "local_push", "loggers": ["aiounifi"], "quality_scale": "platinum", - "requirements": ["aiounifi==51"], + "requirements": ["aiounifi==52"], "ssdp": [ { "manufacturer": "Ubiquiti Networks", diff --git a/homeassistant/components/unifi/sensor.py b/homeassistant/components/unifi/sensor.py index 8cdc0dcbb71..ff5c58d7d6c 100644 --- a/homeassistant/components/unifi/sensor.py +++ b/homeassistant/components/unifi/sensor.py @@ -79,6 +79,8 @@ def async_wlan_client_value_fn(controller: UniFiController, wlan: Wlan) -> int: client.mac for client in controller.api.clients.values() if client.essid == wlan.name + and dt_util.utcnow() - dt_util.utc_from_timestamp(client.last_seen or 0) + < controller.option_detection_time ] ) diff --git a/homeassistant/components/webhook/__init__.py b/homeassistant/components/webhook/__init__.py index 9711c30b19e..5f82ca54283 100644 --- a/homeassistant/components/webhook/__init__.py +++ b/homeassistant/components/webhook/__init__.py @@ -145,16 +145,26 @@ async def async_handle_webhook( return Response(status=HTTPStatus.METHOD_NOT_ALLOWED) if webhook["local_only"] in (True, None) and not isinstance(request, MockRequest): - if TYPE_CHECKING: - assert isinstance(request, Request) - assert request.remote is not None - try: - remote = ip_address(request.remote) - except ValueError: - _LOGGER.debug("Unable to parse remote ip %s", request.remote) - return Response(status=HTTPStatus.OK) + if has_cloud := "cloud" in hass.config.components: + from hass_nabucasa import remote # pylint: disable=import-outside-toplevel - if not network.is_local(remote): + is_local = True + if has_cloud and remote.is_cloud_request.get(): + is_local = False + else: + if TYPE_CHECKING: + assert isinstance(request, Request) + assert request.remote is not None + + try: + request_remote = ip_address(request.remote) + except ValueError: + _LOGGER.debug("Unable to parse remote ip %s", request.remote) + return Response(status=HTTPStatus.OK) + + is_local = network.is_local(request_remote) + + if not is_local: _LOGGER.warning("Received remote request for local webhook %s", webhook_id) if webhook["local_only"]: return Response(status=HTTPStatus.OK) diff --git a/homeassistant/components/yeelight/light.py b/homeassistant/components/yeelight/light.py index 35739b0f596..f5f39e9997d 100644 --- a/homeassistant/components/yeelight/light.py +++ b/homeassistant/components/yeelight/light.py @@ -297,7 +297,7 @@ async def async_setup_entry( _lights_setup_helper(YeelightColorLightWithNightlightSwitch) _lights_setup_helper(YeelightNightLightModeWithoutBrightnessControl) else: - _lights_setup_helper(YeelightColorLightWithoutNightlightSwitch) + _lights_setup_helper(YeelightColorLightWithoutNightlightSwitchLight) elif device_type == BulbType.WhiteTemp: if nl_switch_light and device.is_nightlight_supported: _lights_setup_helper(YeelightWithNightLight) @@ -931,6 +931,14 @@ class YeelightColorLightWithoutNightlightSwitch( """Representation of a Color Yeelight light.""" +class YeelightColorLightWithoutNightlightSwitchLight( + YeelightColorLightWithoutNightlightSwitch +): + """Representation of a Color Yeelight light.""" + + _attr_name = None + + class YeelightColorLightWithNightlightSwitch( YeelightNightLightSupport, YeelightColorLightSupport, YeelightGenericLight ): diff --git a/homeassistant/components/yeelight/manifest.json b/homeassistant/components/yeelight/manifest.json index 7f5a67f4220..766ac0700e5 100644 --- a/homeassistant/components/yeelight/manifest.json +++ b/homeassistant/components/yeelight/manifest.json @@ -17,7 +17,7 @@ "iot_class": "local_push", "loggers": ["async_upnp_client", "yeelight"], "quality_scale": "platinum", - "requirements": ["yeelight==0.7.12", "async-upnp-client==0.34.1"], + "requirements": ["yeelight==0.7.13", "async-upnp-client==0.34.1"], "zeroconf": [ { "type": "_miio._udp.local.", diff --git a/homeassistant/components/zha/manifest.json b/homeassistant/components/zha/manifest.json index 041a93a8ead..29fed3a3c9f 100644 --- a/homeassistant/components/zha/manifest.json +++ b/homeassistant/components/zha/manifest.json @@ -20,7 +20,7 @@ "zigpy_znp" ], "requirements": [ - "bellows==0.35.8", + "bellows==0.35.9", "pyserial==3.5", "pyserial-asyncio==0.6", "zha-quirks==0.0.102", diff --git a/homeassistant/const.py b/homeassistant/const.py index 0568650deb5..bcc9586e54b 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -7,7 +7,7 @@ from typing import Final APPLICATION_NAME: Final = "HomeAssistant" MAJOR_VERSION: Final = 2023 MINOR_VERSION: Final = 8 -PATCH_VERSION: Final = "1" +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, 11, 0) diff --git a/pyproject.toml b/pyproject.toml index fdcd2788b58..0710b813402 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2023.8.1" +version = "2023.8.2" license = {text = "Apache-2.0"} description = "Open-source home automation platform running on Python 3." readme = "README.rst" diff --git a/requirements_all.txt b/requirements_all.txt index 21d10628ecb..c0a87e85810 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -5,7 +5,7 @@ AEMET-OpenData==0.2.2 # homeassistant.components.aladdin_connect -AIOAladdinConnect==0.1.56 +AIOAladdinConnect==0.1.57 # homeassistant.components.honeywell AIOSomecomfort==0.0.15 @@ -79,7 +79,7 @@ PyMetno==0.10.0 PyMicroBot==0.0.9 # homeassistant.components.nina -PyNINA==0.3.1 +PyNINA==0.3.2 # homeassistant.components.mobile_app # homeassistant.components.owntracks @@ -191,7 +191,7 @@ aioairq==0.2.4 aioairzone-cloud==0.2.1 # homeassistant.components.airzone -aioairzone==0.6.4 +aioairzone==0.6.5 # homeassistant.components.ambient_station aioambient==2023.04.0 @@ -249,7 +249,7 @@ aioguardian==2022.07.0 aioharmony==0.2.10 # homeassistant.components.homekit_controller -aiohomekit==2.6.12 +aiohomekit==2.6.15 # homeassistant.components.emulated_hue # homeassistant.components.http @@ -360,7 +360,7 @@ aiosyncthing==0.5.1 aiotractive==0.5.5 # homeassistant.components.unifi -aiounifi==51 +aiounifi==52 # homeassistant.components.vlc_telnet aiovlc==0.1.0 @@ -500,10 +500,10 @@ beautifulsoup4==4.11.1 # beewi-smartclim==0.0.10 # homeassistant.components.zha -bellows==0.35.8 +bellows==0.35.9 # homeassistant.components.bmw_connected_drive -bimmer-connected==0.13.8 +bimmer-connected==0.13.9 # homeassistant.components.bizkaibus bizkaibus==0.1.1 @@ -1368,7 +1368,7 @@ openwrt-luci-rpc==1.1.16 openwrt-ubus-rpc==0.0.2 # homeassistant.components.opower -opower==0.0.20 +opower==0.0.26 # homeassistant.components.oralb oralb-ble==0.17.6 @@ -1563,7 +1563,7 @@ pyairnow==1.2.1 # homeassistant.components.airvisual # homeassistant.components.airvisual_pro -pyairvisual==2022.12.1 +pyairvisual==2023.08.1 # homeassistant.components.atag pyatag==0.3.5.3 @@ -1572,7 +1572,7 @@ pyatag==0.3.5.3 pyatmo==7.5.0 # homeassistant.components.apple_tv -pyatv==0.13.3 +pyatv==0.13.4 # homeassistant.components.aussie_broadband pyaussiebb==0.0.15 @@ -1824,7 +1824,7 @@ pymailgunner==1.4 pymata-express==1.19 # homeassistant.components.mazda -pymazda==0.3.10 +pymazda==0.3.11 # homeassistant.components.mediaroom pymediaroom==0.6.5.4 @@ -1872,7 +1872,7 @@ pynuki==1.6.2 pynut2==2.1.2 # homeassistant.components.nws -pynws==1.5.0 +pynws==1.5.1 # homeassistant.components.nx584 pynx584==0.5 @@ -1982,7 +1982,7 @@ pysabnzbd==1.1.1 pysaj==0.0.16 # homeassistant.components.sensibo -pysensibo==1.0.32 +pysensibo==1.0.33 # homeassistant.components.serial # homeassistant.components.zha @@ -2150,7 +2150,7 @@ python-qbittorrent==0.4.3 python-ripple-api==0.0.3 # homeassistant.components.roborock -python-roborock==0.31.1 +python-roborock==0.32.2 # homeassistant.components.smarttub python-smarttub==0.0.33 @@ -2159,7 +2159,7 @@ python-smarttub==0.0.33 python-songpal==0.15.2 # homeassistant.components.tado -python-tado==0.15.0 +python-tado==0.16.0 # homeassistant.components.telegram_bot python-telegram-bot==13.1 @@ -2563,7 +2563,7 @@ total-connect-client==2023.2 tp-connected==0.0.4 # homeassistant.components.tplink_omada -tplink_omada_client==1.2.4 +tplink_omada_client==1.3.2 # homeassistant.components.transmission transmission-rpc==4.1.5 @@ -2725,7 +2725,7 @@ yalexs-ble==2.2.3 yalexs==1.5.1 # homeassistant.components.yeelight -yeelight==0.7.12 +yeelight==0.7.13 # homeassistant.components.yeelightsunflower yeelightsunflower==0.0.10 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 5cb970c7132..a9cedeabbac 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -7,7 +7,7 @@ AEMET-OpenData==0.2.2 # homeassistant.components.aladdin_connect -AIOAladdinConnect==0.1.56 +AIOAladdinConnect==0.1.57 # homeassistant.components.honeywell AIOSomecomfort==0.0.15 @@ -69,7 +69,7 @@ PyMetno==0.10.0 PyMicroBot==0.0.9 # homeassistant.components.nina -PyNINA==0.3.1 +PyNINA==0.3.2 # homeassistant.components.mobile_app # homeassistant.components.owntracks @@ -172,7 +172,7 @@ aioairq==0.2.4 aioairzone-cloud==0.2.1 # homeassistant.components.airzone -aioairzone==0.6.4 +aioairzone==0.6.5 # homeassistant.components.ambient_station aioambient==2023.04.0 @@ -227,7 +227,7 @@ aioguardian==2022.07.0 aioharmony==0.2.10 # homeassistant.components.homekit_controller -aiohomekit==2.6.12 +aiohomekit==2.6.15 # homeassistant.components.emulated_hue # homeassistant.components.http @@ -335,7 +335,7 @@ aiosyncthing==0.5.1 aiotractive==0.5.5 # homeassistant.components.unifi -aiounifi==51 +aiounifi==52 # homeassistant.components.vlc_telnet aiovlc==0.1.0 @@ -424,10 +424,10 @@ base36==0.1.1 beautifulsoup4==4.11.1 # homeassistant.components.zha -bellows==0.35.8 +bellows==0.35.9 # homeassistant.components.bmw_connected_drive -bimmer-connected==0.13.8 +bimmer-connected==0.13.9 # homeassistant.components.bluetooth bleak-retry-connector==3.1.1 @@ -1037,7 +1037,7 @@ openerz-api==0.2.0 openhomedevice==2.2.0 # homeassistant.components.opower -opower==0.0.20 +opower==0.0.26 # homeassistant.components.oralb oralb-ble==0.17.6 @@ -1169,7 +1169,7 @@ pyairnow==1.2.1 # homeassistant.components.airvisual # homeassistant.components.airvisual_pro -pyairvisual==2022.12.1 +pyairvisual==2023.08.1 # homeassistant.components.atag pyatag==0.3.5.3 @@ -1178,7 +1178,7 @@ pyatag==0.3.5.3 pyatmo==7.5.0 # homeassistant.components.apple_tv -pyatv==0.13.3 +pyatv==0.13.4 # homeassistant.components.aussie_broadband pyaussiebb==0.0.15 @@ -1352,7 +1352,7 @@ pymailgunner==1.4 pymata-express==1.19 # homeassistant.components.mazda -pymazda==0.3.10 +pymazda==0.3.11 # homeassistant.components.melcloud pymelcloud==2.5.8 @@ -1388,7 +1388,7 @@ pynuki==1.6.2 pynut2==2.1.2 # homeassistant.components.nws -pynws==1.5.0 +pynws==1.5.1 # homeassistant.components.nx584 pynx584==0.5 @@ -1474,7 +1474,7 @@ pyrympro==0.0.7 pysabnzbd==1.1.1 # homeassistant.components.sensibo -pysensibo==1.0.32 +pysensibo==1.0.33 # homeassistant.components.serial # homeassistant.components.zha @@ -1579,7 +1579,7 @@ python-picnic-api==1.1.0 python-qbittorrent==0.4.3 # homeassistant.components.roborock -python-roborock==0.31.1 +python-roborock==0.32.2 # homeassistant.components.smarttub python-smarttub==0.0.33 @@ -1588,7 +1588,7 @@ python-smarttub==0.0.33 python-songpal==0.15.2 # homeassistant.components.tado -python-tado==0.15.0 +python-tado==0.16.0 # homeassistant.components.telegram_bot python-telegram-bot==13.1 @@ -1869,7 +1869,7 @@ toonapi==0.2.1 total-connect-client==2023.2 # homeassistant.components.tplink_omada -tplink_omada_client==1.2.4 +tplink_omada_client==1.3.2 # homeassistant.components.transmission transmission-rpc==4.1.5 @@ -2007,7 +2007,7 @@ yalexs-ble==2.2.3 yalexs==1.5.1 # homeassistant.components.yeelight -yeelight==0.7.12 +yeelight==0.7.13 # homeassistant.components.yolink yolink-api==0.3.0 diff --git a/tests/components/airzone/test_climate.py b/tests/components/airzone/test_climate.py index f7cc7806bcb..3e68c056566 100644 --- a/tests/components/airzone/test_climate.py +++ b/tests/components/airzone/test_climate.py @@ -329,7 +329,7 @@ async def test_airzone_climate_set_hvac_mode(hass: HomeAssistant) -> None: await async_init_integration(hass) - HVAC_MOCK = { + HVAC_MOCK_1 = { API_DATA: [ { API_SYSTEM_ID: 1, @@ -340,7 +340,7 @@ async def test_airzone_climate_set_hvac_mode(hass: HomeAssistant) -> None: } with patch( "homeassistant.components.airzone.AirzoneLocalApi.put_hvac", - return_value=HVAC_MOCK, + return_value=HVAC_MOCK_1, ): await hass.services.async_call( CLIMATE_DOMAIN, @@ -407,6 +407,51 @@ async def test_airzone_climate_set_hvac_mode(hass: HomeAssistant) -> None: state = hass.states.get("climate.airzone_2_1") assert state.state == HVACMode.HEAT_COOL + HVAC_MOCK_4 = { + API_DATA: [ + { + API_SYSTEM_ID: 1, + API_ZONE_ID: 1, + API_ON: 1, + } + ] + } + with patch( + "homeassistant.components.airzone.AirzoneLocalApi.put_hvac", + return_value=HVAC_MOCK_4, + ): + await hass.services.async_call( + CLIMATE_DOMAIN, + SERVICE_SET_HVAC_MODE, + { + ATTR_ENTITY_ID: "climate.salon", + ATTR_HVAC_MODE: HVACMode.FAN_ONLY, + }, + blocking=True, + ) + + state = hass.states.get("climate.salon") + assert state.state == HVACMode.FAN_ONLY + + HVAC_MOCK_NO_SET_POINT = {**HVAC_MOCK} + del HVAC_MOCK_NO_SET_POINT[API_SYSTEMS][0][API_DATA][0][API_SET_POINT] + + with patch( + "homeassistant.components.airzone.AirzoneLocalApi.get_hvac", + return_value=HVAC_MOCK_NO_SET_POINT, + ), patch( + "homeassistant.components.airzone.AirzoneLocalApi.get_hvac_systems", + return_value=HVAC_SYSTEMS_MOCK, + ), patch( + "homeassistant.components.airzone.AirzoneLocalApi.get_webserver", + return_value=HVAC_WEBSERVER_MOCK, + ): + async_fire_time_changed(hass, utcnow() + SCAN_INTERVAL) + await hass.async_block_till_done() + + state = hass.states.get("climate.salon") + assert state.attributes.get(ATTR_TEMPERATURE) == 19.1 + async def test_airzone_climate_set_hvac_slave_error(hass: HomeAssistant) -> None: """Test setting the HVAC mode for a slave zone.""" diff --git a/tests/components/alert/test_init.py b/tests/components/alert/test_init.py index 550727a2a22..8dfbb437646 100644 --- a/tests/components/alert/test_init.py +++ b/tests/components/alert/test_init.py @@ -36,6 +36,7 @@ from tests.common import async_mock_service NAME = "alert_test" DONE_MESSAGE = "alert_gone" NOTIFIER = "test" +BAD_NOTIFIER = "bad_notifier" TEMPLATE = "{{ states.sensor.test.entity_id }}" TEST_ENTITY = "sensor.test" TITLE = "{{ states.sensor.test.entity_id }}" @@ -199,6 +200,26 @@ async def test_notification( assert len(mock_notifier) == 2 +async def test_bad_notifier( + hass: HomeAssistant, mock_notifier: list[ServiceCall] +) -> None: + """Test a broken notifier does not break the alert.""" + config = deepcopy(TEST_CONFIG) + config[DOMAIN][NAME][CONF_NOTIFIERS] = [BAD_NOTIFIER, NOTIFIER] + assert await async_setup_component(hass, DOMAIN, config) + assert len(mock_notifier) == 0 + + hass.states.async_set("sensor.test", STATE_ON) + await hass.async_block_till_done() + assert len(mock_notifier) == 1 + assert hass.states.get(ENTITY_ID).state == STATE_ON + + hass.states.async_set("sensor.test", STATE_OFF) + await hass.async_block_till_done() + assert len(mock_notifier) == 2 + assert hass.states.get(ENTITY_ID).state == STATE_IDLE + + async def test_no_notifiers( hass: HomeAssistant, mock_notifier: list[ServiceCall] ) -> None: diff --git a/tests/components/androidtv_remote/test_config_flow.py b/tests/components/androidtv_remote/test_config_flow.py index 4e0067152e7..a2792efb0f3 100644 --- a/tests/components/androidtv_remote/test_config_flow.py +++ b/tests/components/androidtv_remote/test_config_flow.py @@ -90,6 +90,7 @@ async def test_user_flow_cannot_connect( host = "1.2.3.4" + mock_api.async_generate_cert_if_missing = AsyncMock(return_value=True) mock_api.async_get_name_and_mac = AsyncMock(side_effect=CannotConnect()) result = await hass.config_entries.flow.async_configure( @@ -101,6 +102,7 @@ async def test_user_flow_cannot_connect( assert "host" in result["data_schema"].schema assert result["errors"] == {"base": "cannot_connect"} + mock_api.async_generate_cert_if_missing.assert_called() mock_api.async_get_name_and_mac.assert_called() mock_api.async_start_pairing.assert_not_called() @@ -329,6 +331,7 @@ async def test_user_flow_already_configured_host_changed_reloads_entry( assert "host" in result["data_schema"].schema assert not result["errors"] + mock_api.async_generate_cert_if_missing = AsyncMock(return_value=True) mock_api.async_get_name_and_mac = AsyncMock(return_value=(name, mac)) result = await hass.config_entries.flow.async_configure( @@ -338,6 +341,7 @@ async def test_user_flow_already_configured_host_changed_reloads_entry( assert result.get("type") == FlowResultType.ABORT assert result.get("reason") == "already_configured" + mock_api.async_generate_cert_if_missing.assert_called() mock_api.async_get_name_and_mac.assert_called() mock_api.async_start_pairing.assert_not_called() @@ -386,6 +390,7 @@ async def test_user_flow_already_configured_host_not_changed_no_reload_entry( assert "host" in result["data_schema"].schema assert not result["errors"] + mock_api.async_generate_cert_if_missing = AsyncMock(return_value=True) mock_api.async_get_name_and_mac = AsyncMock(return_value=(name, mac)) result = await hass.config_entries.flow.async_configure( @@ -395,6 +400,7 @@ async def test_user_flow_already_configured_host_not_changed_no_reload_entry( assert result.get("type") == FlowResultType.ABORT assert result.get("reason") == "already_configured" + mock_api.async_generate_cert_if_missing.assert_called() mock_api.async_get_name_and_mac.assert_called() mock_api.async_start_pairing.assert_not_called() diff --git a/tests/components/command_line/test_sensor.py b/tests/components/command_line/test_sensor.py index bc24ff5419f..db6dd277e1f 100644 --- a/tests/components/command_line/test_sensor.py +++ b/tests/components/command_line/test_sensor.py @@ -3,6 +3,7 @@ from __future__ import annotations import asyncio from datetime import timedelta +import subprocess from typing import Any from unittest.mock import patch @@ -16,7 +17,7 @@ from homeassistant.components.homeassistant import ( SERVICE_UPDATE_ENTITY, ) from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN -from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er import homeassistant.helpers.issue_registry as ir @@ -697,3 +698,40 @@ async def test_scrape_sensor_device_date( entity_state = hass.states.get("sensor.test") assert entity_state assert entity_state.state == "2022-01-17" + + +async def test_template_not_error_when_data_is_none( + hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Test command sensor with template not logging error when data is None.""" + + with patch( + "homeassistant.components.command_line.utils.subprocess.check_output", + side_effect=subprocess.CalledProcessError, + ): + await setup.async_setup_component( + hass, + DOMAIN, + { + "command_line": [ + { + "sensor": { + "name": "Test", + "command": "failed command", + "unit_of_measurement": "MB", + "value_template": "{{ (value.split('\t')[0]|int(0)/1000)|round(3) }}", + } + } + ] + }, + ) + await hass.async_block_till_done() + + entity_state = hass.states.get("sensor.test") + assert entity_state + assert entity_state.state == STATE_UNKNOWN + + assert ( + "Template variable error: 'None' has no attribute 'split' when rendering" + not in caplog.text + ) diff --git a/tests/components/gardena_bluetooth/snapshots/test_sensor.ambr b/tests/components/gardena_bluetooth/snapshots/test_sensor.ambr index 14135cb390c..8df37b40abc 100644 --- a/tests/components/gardena_bluetooth/snapshots/test_sensor.ambr +++ b/tests/components/gardena_bluetooth/snapshots/test_sensor.ambr @@ -35,7 +35,7 @@ 'entity_id': 'sensor.mock_title_valve_closing', 'last_changed': , 'last_updated': , - 'state': 'unknown', + 'state': 'unavailable', }) # --- # name: test_setup[98bd2a19-0b0e-421a-84e5-ddbf75dc6de4-raw0-sensor.mock_title_battery] diff --git a/tests/components/homekit_controller/test_device_trigger.py b/tests/components/homekit_controller/test_device_trigger.py index 757823aba9b..c7e5005446f 100644 --- a/tests/components/homekit_controller/test_device_trigger.py +++ b/tests/components/homekit_controller/test_device_trigger.py @@ -425,6 +425,14 @@ async def test_handle_events_late_setup(hass: HomeAssistant, utcnow, calls) -> N assert len(calls) == 1 assert calls[0].data["some"] == "device - button1 - single_press - 0" + # Make sure automation doesn't trigger for a polled None + helper.pairing.testing.update_named_service( + "Button 1", {CharacteristicsTypes.INPUT_EVENT: None} + ) + + await hass.async_block_till_done() + assert len(calls) == 1 + # Make sure automation doesn't trigger for long press helper.pairing.testing.update_named_service( "Button 1", {CharacteristicsTypes.INPUT_EVENT: 1} diff --git a/tests/components/roborock/conftest.py b/tests/components/roborock/conftest.py index eb281076825..ef841769f8d 100644 --- a/tests/components/roborock/conftest.py +++ b/tests/components/roborock/conftest.py @@ -67,6 +67,10 @@ async def setup_entry( return_value=PROP, ), patch( "homeassistant.components.roborock.coordinator.RoborockLocalClient.send_message" + ), patch( + "homeassistant.components.roborock.RoborockMqttClient._wait_response" + ), patch( + "homeassistant.components.roborock.coordinator.RoborockLocalClient._wait_response" ): assert await async_setup_component(hass, DOMAIN, {}) await hass.async_block_till_done() diff --git a/tests/components/sensibo/fixtures/data.json b/tests/components/sensibo/fixtures/data.json index 7db7b4a7c4a..8be6d1e173a 100644 --- a/tests/components/sensibo/fixtures/data.json +++ b/tests/components/sensibo/fixtures/data.json @@ -547,6 +547,163 @@ "autoOffEnabled": false, "antiMoldTimer": null, "antiMoldConfig": null + }, + { + "isGeofenceOnEnterEnabledForThisUser": false, + "isClimateReactGeofenceOnEnterEnabledForThisUser": false, + "isMotionGeofenceOnEnterEnabled": false, + "isOwner": true, + "id": "BBZZBBZZ", + "qrId": "AAAAAAAACC", + "temperatureUnit": "C", + "room": { + "uid": "99YY99YY", + "name": "Bedroom", + "icon": "Diningroom" + }, + "acState": { + "timestamp": { + "time": "2022-04-30T11:23:30.067312Z", + "secondsAgo": -1 + }, + "on": false + }, + "lastStateChange": { + "time": "2022-04-30T11:21:41Z", + "secondsAgo": 108 + }, + "lastStateChangeToOn": { + "time": "2022-04-30T09:43:26Z", + "secondsAgo": 6003 + }, + "lastStateChangeToOff": { + "time": "2022-04-30T11:21:37Z", + "secondsAgo": 112 + }, + "location": { + "id": "ZZZZZZZZZZYY", + "name": "Home", + "latLon": [58.9806976, 20.5864297], + "address": ["Sealand 99", "Some county"], + "country": "United Country", + "createTime": { + "time": "2020-03-21T15:44:15Z", + "secondsAgo": 66543240 + } + }, + "connectionStatus": { + "isAlive": true, + "lastSeen": { + "time": "2022-04-30T11:23:20.642798Z", + "secondsAgo": 9 + } + }, + "firmwareVersion": "PUR00111", + "firmwareType": "pure-esp32", + "productModel": "pure", + "configGroup": "stable", + "currentlyAvailableFirmwareVersion": "PUR00111", + "cleanFiltersNotificationEnabled": false, + "shouldShowFilterCleaningNotification": false, + "isGeofenceOnExitEnabled": false, + "isClimateReactGeofenceOnExitEnabled": false, + "isMotionGeofenceOnExitEnabled": false, + "serial": "0987654321", + "sensorsCalibration": { + "temperature": 0.0, + "humidity": 0.0 + }, + "motionSensors": [], + "tags": [], + "timer": null, + "schedules": [], + "motionConfig": null, + "filtersCleaning": { + "acOnSecondsSinceLastFiltersClean": 415560, + "filtersCleanSecondsThreshold": 14256000, + "lastFiltersCleanTime": { + "time": "2022-04-23T15:58:45Z", + "secondsAgo": 588284 + }, + "shouldCleanFilters": false + }, + "serviceSubscriptions": [], + "roomIsOccupied": null, + "mainMeasurementsSensor": null, + "pureBoostConfig": null, + "warrantyEligible": "no", + "warrantyEligibleUntil": { + "time": "2022-04-10T09:58:58Z", + "secondsAgo": 1733071 + }, + "features": ["optimusTrial", "softShowPlus"], + "runningHealthcheck": null, + "lastHealthcheck": null, + "lastACStateChange": { + "id": "AA22", + "time": { + "time": "2022-04-30T11:21:37Z", + "secondsAgo": 112 + }, + "status": "Success", + "acState": { + "timestamp": { + "time": "2022-04-30T11:23:30.090144Z", + "secondsAgo": -1 + }, + "on": false, + "mode": "fan", + "fanLevel": "low", + "light": "on" + }, + "resultingAcState": { + "timestamp": { + "time": "2022-04-30T11:23:30.090185Z", + "secondsAgo": -1 + }, + "on": false, + "mode": "fan", + "fanLevel": "low", + "light": "on" + }, + "changedProperties": ["on"], + "reason": "UserRequest", + "failureReason": null, + "resolveTime": { + "time": "2022-04-30T11:21:37Z", + "secondsAgo": 112 + }, + "causedByScheduleId": null, + "causedByScheduleType": null + }, + "homekitSupported": true, + "remoteCapabilities": null, + "remote": { + "toggle": false, + "window": false + }, + "remoteFlavor": "Eccentric Eagle", + "remoteAlternatives": [], + "smartMode": null, + "measurements": { + "time": { + "time": "2022-04-30T11:23:20.642798Z", + "secondsAgo": 9 + }, + "rssi": -58, + "pm25": 1, + "motion": false, + "roomIsOccupied": null + }, + "accessPoint": { + "ssid": "Sensibo-09876", + "password": null + }, + "macAddress": "00:01:00:01:00:01", + "autoOffMinutes": null, + "autoOffEnabled": false, + "antiMoldTimer": null, + "antiMoldConfig": null } ] } diff --git a/tests/components/sensibo/test_climate.py b/tests/components/sensibo/test_climate.py index 4e856d396c1..56a7a8c902c 100644 --- a/tests/components/sensibo/test_climate.py +++ b/tests/components/sensibo/test_climate.py @@ -76,12 +76,16 @@ async def test_climate_find_valid_targets() -> None: async def test_climate( - hass: HomeAssistant, load_int: ConfigEntry, get_data: SensiboData + hass: HomeAssistant, + caplog: pytest.LogCaptureFixture, + get_data: SensiboData, + load_int: ConfigEntry, ) -> None: """Test the Sensibo climate.""" state1 = hass.states.get("climate.hallway") state2 = hass.states.get("climate.kitchen") + state3 = hass.states.get("climate.bedroom") assert state1.state == "heat" assert state1.attributes == { @@ -113,6 +117,19 @@ async def test_climate( assert state2.state == "off" + assert not state3 + found_log = False + logs = caplog.get_records("setup") + for log in logs: + if ( + log.message + == "Device Bedroom not correctly registered with Sensibo cloud. Skipping device" + ): + found_log = True + break + + assert found_log + async def test_climate_fan( hass: HomeAssistant, diff --git a/tests/components/unifi/test_sensor.py b/tests/components/unifi/test_sensor.py index d619cd4c3c9..3d50df8ada9 100644 --- a/tests/components/unifi/test_sensor.py +++ b/tests/components/unifi/test_sensor.py @@ -470,6 +470,7 @@ async def test_wlan_client_sensors( wireless_client_1 = { "essid": "SSID 1", "is_wired": False, + "last_seen": dt_util.as_timestamp(dt_util.utcnow()), "mac": "00:00:00:00:00:01", "name": "Wireless client", "oui": "Producer", @@ -479,6 +480,7 @@ async def test_wlan_client_sensors( wireless_client_2 = { "essid": "SSID 2", "is_wired": False, + "last_seen": dt_util.as_timestamp(dt_util.utcnow()), "mac": "00:00:00:00:00:02", "name": "Wireless client2", "oui": "Producer2", @@ -526,9 +528,17 @@ async def test_wlan_client_sensors( # Verify state update - decreasing number wireless_client_1["essid"] = "SSID" - wireless_client_2["essid"] = "SSID" - mock_unifi_websocket(message=MessageKey.CLIENT, data=wireless_client_1) + + async_fire_time_changed(hass, datetime.utcnow() + DEFAULT_SCAN_INTERVAL) + await hass.async_block_till_done() + + ssid_1 = hass.states.get("sensor.ssid_1") + assert ssid_1.state == "1" + + # Verify state update - decreasing number + + wireless_client_2["last_seen"] = 0 mock_unifi_websocket(message=MessageKey.CLIENT, data=wireless_client_2) async_fire_time_changed(hass, datetime.utcnow() + DEFAULT_SCAN_INTERVAL) diff --git a/tests/components/webhook/test_init.py b/tests/components/webhook/test_init.py index ff0346a3d8b..fbe0da15853 100644 --- a/tests/components/webhook/test_init.py +++ b/tests/components/webhook/test_init.py @@ -1,7 +1,7 @@ """Test the webhook component.""" from http import HTTPStatus from ipaddress import ip_address -from unittest.mock import patch +from unittest.mock import Mock, patch from aiohttp import web import pytest @@ -206,6 +206,8 @@ async def test_webhook_not_allowed_method(hass: HomeAssistant) -> None: async def test_webhook_local_only(hass: HomeAssistant, mock_client) -> None: """Test posting a webhook with local only.""" + hass.config.components.add("cloud") + hooks = [] webhook_id = webhook.async_generate_id() @@ -234,6 +236,16 @@ async def test_webhook_local_only(hass: HomeAssistant, mock_client) -> None: # No hook received assert len(hooks) == 1 + # Request from Home Assistant Cloud remote UI + with patch( + "hass_nabucasa.remote.is_cloud_request", Mock(get=Mock(return_value=True)) + ): + resp = await mock_client.post(f"/api/webhook/{webhook_id}", json={"data": True}) + + # No hook received + assert resp.status == HTTPStatus.OK + assert len(hooks) == 1 + async def test_listing_webhook( hass: HomeAssistant, diff --git a/tests/components/webhook/test_trigger.py b/tests/components/webhook/test_trigger.py index 392ab58a30f..990482c500e 100644 --- a/tests/components/webhook/test_trigger.py +++ b/tests/components/webhook/test_trigger.py @@ -1,6 +1,6 @@ """The tests for the webhook automation trigger.""" from ipaddress import ip_address -from unittest.mock import patch +from unittest.mock import Mock, patch import pytest @@ -68,6 +68,9 @@ async def test_webhook_post( hass: HomeAssistant, hass_client_no_auth: ClientSessionGenerator ) -> None: """Test triggering with a POST webhook.""" + # Set up fake cloud + hass.config.components.add("cloud") + events = [] @callback @@ -114,6 +117,16 @@ async def test_webhook_post( await hass.async_block_till_done() assert len(events) == 1 + # Request from Home Assistant Cloud remote UI + with patch( + "hass_nabucasa.remote.is_cloud_request", Mock(get=Mock(return_value=True)) + ): + await client.post("/api/webhook/post_webhook", data={"hello": "world"}) + + # No hook received + await hass.async_block_till_done() + assert len(events) == 1 + async def test_webhook_allowed_methods_internet( hass: HomeAssistant, hass_client_no_auth: ClientSessionGenerator @@ -141,7 +154,6 @@ async def test_webhook_allowed_methods_internet( }, "action": { "event": "test_success", - "event_data_template": {"hello": "yo {{ trigger.data.hello }}"}, }, } }, @@ -150,7 +162,7 @@ async def test_webhook_allowed_methods_internet( client = await hass_client_no_auth() - await client.post("/api/webhook/post_webhook", data={"hello": "world"}) + await client.post("/api/webhook/post_webhook") await hass.async_block_till_done() assert len(events) == 0 @@ -160,7 +172,7 @@ async def test_webhook_allowed_methods_internet( "homeassistant.components.webhook.ip_address", return_value=ip_address("123.123.123.123"), ): - await client.put("/api/webhook/post_webhook", data={"hello": "world"}) + await client.put("/api/webhook/post_webhook") await hass.async_block_till_done() assert len(events) == 1