mirror of
https://github.com/home-assistant/core.git
synced 2025-07-29 08:07:45 +00:00
Merge pull request #76835 from home-assistant/rc
This commit is contained in:
commit
795ed9fbf6
@ -36,6 +36,7 @@ from homeassistant.core import (
|
|||||||
HomeAssistant,
|
HomeAssistant,
|
||||||
callback,
|
callback,
|
||||||
split_entity_id,
|
split_entity_id,
|
||||||
|
valid_entity_id,
|
||||||
)
|
)
|
||||||
from homeassistant.exceptions import (
|
from homeassistant.exceptions import (
|
||||||
ConditionError,
|
ConditionError,
|
||||||
@ -361,7 +362,7 @@ class AutomationEntity(ToggleEntity, RestoreEntity):
|
|||||||
referenced |= condition.async_extract_devices(conf)
|
referenced |= condition.async_extract_devices(conf)
|
||||||
|
|
||||||
for conf in self._trigger_config:
|
for conf in self._trigger_config:
|
||||||
referenced |= set(_trigger_extract_device(conf))
|
referenced |= set(_trigger_extract_devices(conf))
|
||||||
|
|
||||||
self._referenced_devices = referenced
|
self._referenced_devices = referenced
|
||||||
return referenced
|
return referenced
|
||||||
@ -763,7 +764,7 @@ async def _async_process_if(hass, name, config, p_config):
|
|||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _trigger_extract_device(trigger_conf: dict) -> list[str]:
|
def _trigger_extract_devices(trigger_conf: dict) -> list[str]:
|
||||||
"""Extract devices from a trigger config."""
|
"""Extract devices from a trigger config."""
|
||||||
if trigger_conf[CONF_PLATFORM] == "device":
|
if trigger_conf[CONF_PLATFORM] == "device":
|
||||||
return [trigger_conf[CONF_DEVICE_ID]]
|
return [trigger_conf[CONF_DEVICE_ID]]
|
||||||
@ -772,6 +773,7 @@ def _trigger_extract_device(trigger_conf: dict) -> list[str]:
|
|||||||
trigger_conf[CONF_PLATFORM] == "event"
|
trigger_conf[CONF_PLATFORM] == "event"
|
||||||
and CONF_EVENT_DATA in trigger_conf
|
and CONF_EVENT_DATA in trigger_conf
|
||||||
and CONF_DEVICE_ID in trigger_conf[CONF_EVENT_DATA]
|
and CONF_DEVICE_ID in trigger_conf[CONF_EVENT_DATA]
|
||||||
|
and isinstance(trigger_conf[CONF_EVENT_DATA][CONF_DEVICE_ID], str)
|
||||||
):
|
):
|
||||||
return [trigger_conf[CONF_EVENT_DATA][CONF_DEVICE_ID]]
|
return [trigger_conf[CONF_EVENT_DATA][CONF_DEVICE_ID]]
|
||||||
|
|
||||||
@ -803,6 +805,8 @@ def _trigger_extract_entities(trigger_conf: dict) -> list[str]:
|
|||||||
trigger_conf[CONF_PLATFORM] == "event"
|
trigger_conf[CONF_PLATFORM] == "event"
|
||||||
and CONF_EVENT_DATA in trigger_conf
|
and CONF_EVENT_DATA in trigger_conf
|
||||||
and CONF_ENTITY_ID in trigger_conf[CONF_EVENT_DATA]
|
and CONF_ENTITY_ID in trigger_conf[CONF_EVENT_DATA]
|
||||||
|
and isinstance(trigger_conf[CONF_EVENT_DATA][CONF_ENTITY_ID], str)
|
||||||
|
and valid_entity_id(trigger_conf[CONF_EVENT_DATA][CONF_ENTITY_ID])
|
||||||
):
|
):
|
||||||
return [trigger_conf[CONF_EVENT_DATA][CONF_ENTITY_ID]]
|
return [trigger_conf[CONF_EVENT_DATA][CONF_ENTITY_ID]]
|
||||||
|
|
||||||
|
@ -358,13 +358,13 @@ class BluetoothManager:
|
|||||||
async with async_timeout.timeout(START_TIMEOUT):
|
async with async_timeout.timeout(START_TIMEOUT):
|
||||||
await self.scanner.start() # type: ignore[no-untyped-call]
|
await self.scanner.start() # type: ignore[no-untyped-call]
|
||||||
except InvalidMessageError as ex:
|
except InvalidMessageError as ex:
|
||||||
self._cancel_device_detected()
|
self._async_cancel_scanner_callback()
|
||||||
_LOGGER.debug("Invalid DBus message received: %s", ex, exc_info=True)
|
_LOGGER.debug("Invalid DBus message received: %s", ex, exc_info=True)
|
||||||
raise ConfigEntryNotReady(
|
raise ConfigEntryNotReady(
|
||||||
f"Invalid DBus message received: {ex}; try restarting `dbus`"
|
f"Invalid DBus message received: {ex}; try restarting `dbus`"
|
||||||
) from ex
|
) from ex
|
||||||
except BrokenPipeError as ex:
|
except BrokenPipeError as ex:
|
||||||
self._cancel_device_detected()
|
self._async_cancel_scanner_callback()
|
||||||
_LOGGER.debug("DBus connection broken: %s", ex, exc_info=True)
|
_LOGGER.debug("DBus connection broken: %s", ex, exc_info=True)
|
||||||
if is_docker_env():
|
if is_docker_env():
|
||||||
raise ConfigEntryNotReady(
|
raise ConfigEntryNotReady(
|
||||||
@ -374,7 +374,7 @@ class BluetoothManager:
|
|||||||
f"DBus connection broken: {ex}; try restarting `bluetooth` and `dbus`"
|
f"DBus connection broken: {ex}; try restarting `bluetooth` and `dbus`"
|
||||||
) from ex
|
) from ex
|
||||||
except FileNotFoundError as ex:
|
except FileNotFoundError as ex:
|
||||||
self._cancel_device_detected()
|
self._async_cancel_scanner_callback()
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"FileNotFoundError while starting bluetooth: %s", ex, exc_info=True
|
"FileNotFoundError while starting bluetooth: %s", ex, exc_info=True
|
||||||
)
|
)
|
||||||
@ -386,12 +386,12 @@ class BluetoothManager:
|
|||||||
f"DBus service not found; make sure the DBus socket is available to Home Assistant: {ex}"
|
f"DBus service not found; make sure the DBus socket is available to Home Assistant: {ex}"
|
||||||
) from ex
|
) from ex
|
||||||
except asyncio.TimeoutError as ex:
|
except asyncio.TimeoutError as ex:
|
||||||
self._cancel_device_detected()
|
self._async_cancel_scanner_callback()
|
||||||
raise ConfigEntryNotReady(
|
raise ConfigEntryNotReady(
|
||||||
f"Timed out starting Bluetooth after {START_TIMEOUT} seconds"
|
f"Timed out starting Bluetooth after {START_TIMEOUT} seconds"
|
||||||
) from ex
|
) from ex
|
||||||
except BleakError as ex:
|
except BleakError as ex:
|
||||||
self._cancel_device_detected()
|
self._async_cancel_scanner_callback()
|
||||||
_LOGGER.debug("BleakError while starting bluetooth: %s", ex, exc_info=True)
|
_LOGGER.debug("BleakError while starting bluetooth: %s", ex, exc_info=True)
|
||||||
raise ConfigEntryNotReady(f"Failed to start Bluetooth: {ex}") from ex
|
raise ConfigEntryNotReady(f"Failed to start Bluetooth: {ex}") from ex
|
||||||
self.async_setup_unavailable_tracking()
|
self.async_setup_unavailable_tracking()
|
||||||
@ -573,15 +573,20 @@ class BluetoothManager:
|
|||||||
self._cancel_stop = None
|
self._cancel_stop = None
|
||||||
await self.async_stop()
|
await self.async_stop()
|
||||||
|
|
||||||
|
@hass_callback
|
||||||
|
def _async_cancel_scanner_callback(self) -> None:
|
||||||
|
"""Cancel the scanner callback."""
|
||||||
|
if self._cancel_device_detected:
|
||||||
|
self._cancel_device_detected()
|
||||||
|
self._cancel_device_detected = None
|
||||||
|
|
||||||
async def async_stop(self) -> None:
|
async def async_stop(self) -> None:
|
||||||
"""Stop bluetooth discovery."""
|
"""Stop bluetooth discovery."""
|
||||||
_LOGGER.debug("Stopping bluetooth discovery")
|
_LOGGER.debug("Stopping bluetooth discovery")
|
||||||
if self._cancel_watchdog:
|
if self._cancel_watchdog:
|
||||||
self._cancel_watchdog()
|
self._cancel_watchdog()
|
||||||
self._cancel_watchdog = None
|
self._cancel_watchdog = None
|
||||||
if self._cancel_device_detected:
|
self._async_cancel_scanner_callback()
|
||||||
self._cancel_device_detected()
|
|
||||||
self._cancel_device_detected = None
|
|
||||||
if self._cancel_unavailable_tracking:
|
if self._cancel_unavailable_tracking:
|
||||||
self._cancel_unavailable_tracking()
|
self._cancel_unavailable_tracking()
|
||||||
self._cancel_unavailable_tracking = None
|
self._cancel_unavailable_tracking = None
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"domain": "bmw_connected_drive",
|
"domain": "bmw_connected_drive",
|
||||||
"name": "BMW Connected Drive",
|
"name": "BMW Connected Drive",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive",
|
"documentation": "https://www.home-assistant.io/integrations/bmw_connected_drive",
|
||||||
"requirements": ["bimmer_connected==0.10.1"],
|
"requirements": ["bimmer_connected==0.10.2"],
|
||||||
"codeowners": ["@gerard33", "@rikroe"],
|
"codeowners": ["@gerard33", "@rikroe"],
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"iot_class": "cloud_polling",
|
"iot_class": "cloud_polling",
|
||||||
|
@ -192,7 +192,6 @@ async def async_setup_entry(
|
|||||||
calendar_id,
|
calendar_id,
|
||||||
data.get(CONF_SEARCH),
|
data.get(CONF_SEARCH),
|
||||||
)
|
)
|
||||||
await coordinator.async_config_entry_first_refresh()
|
|
||||||
entities.append(
|
entities.append(
|
||||||
GoogleCalendarEntity(
|
GoogleCalendarEntity(
|
||||||
coordinator,
|
coordinator,
|
||||||
@ -342,6 +341,9 @@ class GoogleCalendarEntity(CoordinatorEntity, CalendarEntity):
|
|||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""When entity is added to hass."""
|
"""When entity is added to hass."""
|
||||||
await super().async_added_to_hass()
|
await super().async_added_to_hass()
|
||||||
|
# We do not ask for an update with async_add_entities()
|
||||||
|
# because it will update disabled entities
|
||||||
|
await self.coordinator.async_request_refresh()
|
||||||
self._apply_coordinator_update()
|
self._apply_coordinator_update()
|
||||||
|
|
||||||
async def async_get_events(
|
async def async_get_events(
|
||||||
|
@ -138,11 +138,18 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
|||||||
"""Execute calls to services taking a program."""
|
"""Execute calls to services taking a program."""
|
||||||
program = call.data[ATTR_PROGRAM]
|
program = call.data[ATTR_PROGRAM]
|
||||||
device_id = call.data[ATTR_DEVICE_ID]
|
device_id = call.data[ATTR_DEVICE_ID]
|
||||||
options = {
|
|
||||||
ATTR_KEY: call.data.get(ATTR_KEY),
|
options = []
|
||||||
ATTR_VALUE: call.data.get(ATTR_VALUE),
|
|
||||||
ATTR_UNIT: call.data.get(ATTR_UNIT),
|
option_key = call.data.get(ATTR_KEY)
|
||||||
}
|
if option_key is not None:
|
||||||
|
option = {ATTR_KEY: option_key, ATTR_VALUE: call.data[ATTR_VALUE]}
|
||||||
|
|
||||||
|
option_unit = call.data.get(ATTR_UNIT)
|
||||||
|
if option_unit is not None:
|
||||||
|
option[ATTR_UNIT] = option_unit
|
||||||
|
|
||||||
|
options.append(option)
|
||||||
|
|
||||||
appliance = _get_appliance_by_device_id(hass, device_id)
|
appliance = _get_appliance_by_device_id(hass, device_id)
|
||||||
await hass.async_add_executor_job(getattr(appliance, method), program, options)
|
await hass.async_add_executor_job(getattr(appliance, method), program, options)
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/home_connect",
|
"documentation": "https://www.home-assistant.io/integrations/home_connect",
|
||||||
"dependencies": ["application_credentials"],
|
"dependencies": ["application_credentials"],
|
||||||
"codeowners": ["@DavidMStraub"],
|
"codeowners": ["@DavidMStraub"],
|
||||||
"requirements": ["homeconnect==0.7.1"],
|
"requirements": ["homeconnect==0.7.2"],
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"iot_class": "cloud_push",
|
"iot_class": "cloud_push",
|
||||||
"loggers": ["homeconnect"]
|
"loggers": ["homeconnect"]
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "HomeKit Controller",
|
"name": "HomeKit Controller",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/homekit_controller",
|
"documentation": "https://www.home-assistant.io/integrations/homekit_controller",
|
||||||
"requirements": ["aiohomekit==1.2.9"],
|
"requirements": ["aiohomekit==1.2.11"],
|
||||||
"zeroconf": ["_hap._tcp.local.", "_hap._udp.local."],
|
"zeroconf": ["_hap._tcp.local.", "_hap._udp.local."],
|
||||||
"bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }],
|
"bluetooth": [{ "manufacturer_id": 76, "manufacturer_data_start": [6] }],
|
||||||
"dependencies": ["bluetooth", "zeroconf"],
|
"dependencies": ["bluetooth", "zeroconf"],
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "Philips Hue",
|
"name": "Philips Hue",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/hue",
|
"documentation": "https://www.home-assistant.io/integrations/hue",
|
||||||
"requirements": ["aiohue==4.4.2"],
|
"requirements": ["aiohue==4.5.0"],
|
||||||
"ssdp": [
|
"ssdp": [
|
||||||
{
|
{
|
||||||
"manufacturer": "Royal Philips Electronics",
|
"manufacturer": "Royal Philips Electronics",
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
{ "local_name": "xBBQ*" },
|
{ "local_name": "xBBQ*" },
|
||||||
{ "local_name": "tps" }
|
{ "local_name": "tps" }
|
||||||
],
|
],
|
||||||
"requirements": ["inkbird-ble==0.5.2"],
|
"requirements": ["inkbird-ble==0.5.5"],
|
||||||
"dependencies": ["bluetooth"],
|
"dependencies": ["bluetooth"],
|
||||||
"codeowners": ["@bdraco"],
|
"codeowners": ["@bdraco"],
|
||||||
"iot_class": "local_push"
|
"iot_class": "local_push"
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "KNX",
|
"name": "KNX",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/knx",
|
"documentation": "https://www.home-assistant.io/integrations/knx",
|
||||||
"requirements": ["xknx==0.22.1"],
|
"requirements": ["xknx==1.0.0"],
|
||||||
"codeowners": ["@Julius2342", "@farmio", "@marvin-w"],
|
"codeowners": ["@Julius2342", "@farmio", "@marvin-w"],
|
||||||
"quality_scale": "platinum",
|
"quality_scale": "platinum",
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
|
@ -119,18 +119,20 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
"""Confirm discovery."""
|
"""Confirm discovery."""
|
||||||
assert self._discovered_device is not None
|
assert self._discovered_device is not None
|
||||||
|
discovered = self._discovered_device
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Confirming discovery: %s with serial %s",
|
"Confirming discovery: %s with serial %s",
|
||||||
self._discovered_device.label,
|
discovered.label,
|
||||||
self.unique_id,
|
self.unique_id,
|
||||||
)
|
)
|
||||||
if user_input is not None or self._async_discovered_pending_migration():
|
if user_input is not None or self._async_discovered_pending_migration():
|
||||||
return self._async_create_entry_from_device(self._discovered_device)
|
return self._async_create_entry_from_device(discovered)
|
||||||
|
|
||||||
|
self._abort_if_unique_id_configured(updates={CONF_HOST: discovered.ip_addr})
|
||||||
self._set_confirm_only()
|
self._set_confirm_only()
|
||||||
placeholders = {
|
placeholders = {
|
||||||
"label": self._discovered_device.label,
|
"label": discovered.label,
|
||||||
"host": self._discovered_device.ip_addr,
|
"host": discovered.ip_addr,
|
||||||
"serial": self.unique_id,
|
"serial": self.unique_id,
|
||||||
}
|
}
|
||||||
self.context["title_placeholders"] = placeholders
|
self.context["title_placeholders"] = placeholders
|
||||||
|
@ -121,8 +121,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
multicast_interface = entry.data.get(CONF_INTERFACE, DEFAULT_INTERFACE)
|
multicast_interface = entry.data.get(CONF_INTERFACE, DEFAULT_INTERFACE)
|
||||||
wait_for_push = entry.options.get(CONF_WAIT_FOR_PUSH, DEFAULT_WAIT_FOR_PUSH)
|
wait_for_push = entry.options.get(CONF_WAIT_FOR_PUSH, DEFAULT_WAIT_FOR_PUSH)
|
||||||
|
|
||||||
entry.async_on_unload(entry.add_update_listener(update_listener))
|
|
||||||
|
|
||||||
# Create multicast Listener
|
# Create multicast Listener
|
||||||
async with setup_lock:
|
async with setup_lock:
|
||||||
if KEY_MULTICAST_LISTENER not in hass.data[DOMAIN]:
|
if KEY_MULTICAST_LISTENER not in hass.data[DOMAIN]:
|
||||||
@ -213,6 +211,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
|
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
|
entry.async_on_unload(entry.add_update_listener(update_listener))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"local_name": "SensorPush*"
|
"local_name": "SensorPush*"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"requirements": ["sensorpush-ble==1.5.1"],
|
"requirements": ["sensorpush-ble==1.5.2"],
|
||||||
"dependencies": ["bluetooth"],
|
"dependencies": ["bluetooth"],
|
||||||
"codeowners": ["@bdraco"],
|
"codeowners": ["@bdraco"],
|
||||||
"iot_class": "local_push"
|
"iot_class": "local_push"
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"domain": "switchbot",
|
"domain": "switchbot",
|
||||||
"name": "SwitchBot",
|
"name": "SwitchBot",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/switchbot",
|
"documentation": "https://www.home-assistant.io/integrations/switchbot",
|
||||||
"requirements": ["PySwitchbot==0.18.4"],
|
"requirements": ["PySwitchbot==0.18.10"],
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"dependencies": ["bluetooth"],
|
"dependencies": ["bluetooth"],
|
||||||
"codeowners": [
|
"codeowners": [
|
||||||
|
@ -10,6 +10,10 @@ from systembridgeconnector.exceptions import (
|
|||||||
ConnectionClosedException,
|
ConnectionClosedException,
|
||||||
ConnectionErrorException,
|
ConnectionErrorException,
|
||||||
)
|
)
|
||||||
|
from systembridgeconnector.models.keyboard_key import KeyboardKey
|
||||||
|
from systembridgeconnector.models.keyboard_text import KeyboardText
|
||||||
|
from systembridgeconnector.models.open_path import OpenPath
|
||||||
|
from systembridgeconnector.models.open_url import OpenUrl
|
||||||
from systembridgeconnector.version import SUPPORTED_VERSION, Version
|
from systembridgeconnector.version import SUPPORTED_VERSION, Version
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
@ -149,7 +153,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||||
call.data[CONF_BRIDGE]
|
call.data[CONF_BRIDGE]
|
||||||
]
|
]
|
||||||
await coordinator.websocket_client.open_path(call.data[CONF_PATH])
|
await coordinator.websocket_client.open_path(
|
||||||
|
OpenPath(path=call.data[CONF_PATH])
|
||||||
|
)
|
||||||
|
|
||||||
async def handle_open_url(call: ServiceCall) -> None:
|
async def handle_open_url(call: ServiceCall) -> None:
|
||||||
"""Handle the open url service call."""
|
"""Handle the open url service call."""
|
||||||
@ -157,21 +163,25 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||||
call.data[CONF_BRIDGE]
|
call.data[CONF_BRIDGE]
|
||||||
]
|
]
|
||||||
await coordinator.websocket_client.open_url(call.data[CONF_URL])
|
await coordinator.websocket_client.open_url(OpenUrl(url=call.data[CONF_URL]))
|
||||||
|
|
||||||
async def handle_send_keypress(call: ServiceCall) -> None:
|
async def handle_send_keypress(call: ServiceCall) -> None:
|
||||||
"""Handle the send_keypress service call."""
|
"""Handle the send_keypress service call."""
|
||||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||||
call.data[CONF_BRIDGE]
|
call.data[CONF_BRIDGE]
|
||||||
]
|
]
|
||||||
await coordinator.websocket_client.keyboard_keypress(call.data[CONF_KEY])
|
await coordinator.websocket_client.keyboard_keypress(
|
||||||
|
KeyboardKey(key=call.data[CONF_KEY])
|
||||||
|
)
|
||||||
|
|
||||||
async def handle_send_text(call: ServiceCall) -> None:
|
async def handle_send_text(call: ServiceCall) -> None:
|
||||||
"""Handle the send_keypress service call."""
|
"""Handle the send_keypress service call."""
|
||||||
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
coordinator: SystemBridgeDataUpdateCoordinator = hass.data[DOMAIN][
|
||||||
call.data[CONF_BRIDGE]
|
call.data[CONF_BRIDGE]
|
||||||
]
|
]
|
||||||
await coordinator.websocket_client.keyboard_text(call.data[CONF_TEXT])
|
await coordinator.websocket_client.keyboard_text(
|
||||||
|
KeyboardText(text=call.data[CONF_TEXT])
|
||||||
|
)
|
||||||
|
|
||||||
hass.services.async_register(
|
hass.services.async_register(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
@ -7,12 +7,13 @@ import logging
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import async_timeout
|
import async_timeout
|
||||||
from systembridgeconnector.const import EVENT_MODULE, EVENT_TYPE, TYPE_DATA_UPDATE
|
|
||||||
from systembridgeconnector.exceptions import (
|
from systembridgeconnector.exceptions import (
|
||||||
AuthenticationException,
|
AuthenticationException,
|
||||||
ConnectionClosedException,
|
ConnectionClosedException,
|
||||||
ConnectionErrorException,
|
ConnectionErrorException,
|
||||||
)
|
)
|
||||||
|
from systembridgeconnector.models.get_data import GetData
|
||||||
|
from systembridgeconnector.models.system import System
|
||||||
from systembridgeconnector.websocket_client import WebSocketClient
|
from systembridgeconnector.websocket_client import WebSocketClient
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def validate_input(
|
async def _validate_input(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
data: dict[str, Any],
|
data: dict[str, Any],
|
||||||
) -> dict[str, str]:
|
) -> dict[str, str]:
|
||||||
@ -56,15 +57,12 @@ async def validate_input(
|
|||||||
try:
|
try:
|
||||||
async with async_timeout.timeout(30):
|
async with async_timeout.timeout(30):
|
||||||
await websocket_client.connect(session=async_get_clientsession(hass))
|
await websocket_client.connect(session=async_get_clientsession(hass))
|
||||||
await websocket_client.get_data(["system"])
|
hass.async_create_task(websocket_client.listen())
|
||||||
while True:
|
response = await websocket_client.get_data(GetData(modules=["system"]))
|
||||||
message = await websocket_client.receive_message()
|
_LOGGER.debug("Got response: %s", response.json())
|
||||||
_LOGGER.debug("Message: %s", message)
|
if response.data is None or not isinstance(response.data, System):
|
||||||
if (
|
raise CannotConnect("No data received")
|
||||||
message[EVENT_TYPE] == TYPE_DATA_UPDATE
|
system: System = response.data
|
||||||
and message[EVENT_MODULE] == "system"
|
|
||||||
):
|
|
||||||
break
|
|
||||||
except AuthenticationException as exception:
|
except AuthenticationException as exception:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Authentication error when connecting to %s: %s", data[CONF_HOST], exception
|
"Authentication error when connecting to %s: %s", data[CONF_HOST], exception
|
||||||
@ -81,14 +79,12 @@ async def validate_input(
|
|||||||
except asyncio.TimeoutError as exception:
|
except asyncio.TimeoutError as exception:
|
||||||
_LOGGER.warning("Timed out connecting to %s: %s", data[CONF_HOST], exception)
|
_LOGGER.warning("Timed out connecting to %s: %s", data[CONF_HOST], exception)
|
||||||
raise CannotConnect from exception
|
raise CannotConnect from exception
|
||||||
|
except ValueError as exception:
|
||||||
|
raise CannotConnect from exception
|
||||||
|
|
||||||
_LOGGER.debug("%s Message: %s", TYPE_DATA_UPDATE, message)
|
_LOGGER.debug("Got System data: %s", system.json())
|
||||||
|
|
||||||
if "uuid" not in message["data"]:
|
return {"hostname": host, "uuid": system.uuid}
|
||||||
error = "No UUID in result!"
|
|
||||||
raise CannotConnect(error)
|
|
||||||
|
|
||||||
return {"hostname": host, "uuid": message["data"]["uuid"]}
|
|
||||||
|
|
||||||
|
|
||||||
async def _async_get_info(
|
async def _async_get_info(
|
||||||
@ -98,7 +94,7 @@ async def _async_get_info(
|
|||||||
errors = {}
|
errors = {}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
info = await validate_input(hass, user_input)
|
info = await _validate_input(hass, user_input)
|
||||||
except CannotConnect:
|
except CannotConnect:
|
||||||
errors["base"] = "cannot_connect"
|
errors["base"] = "cannot_connect"
|
||||||
except InvalidAuth:
|
except InvalidAuth:
|
||||||
|
@ -5,6 +5,7 @@ import asyncio
|
|||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import async_timeout
|
import async_timeout
|
||||||
from pydantic import BaseModel # pylint: disable=no-name-in-module
|
from pydantic import BaseModel # pylint: disable=no-name-in-module
|
||||||
@ -17,8 +18,10 @@ from systembridgeconnector.models.battery import Battery
|
|||||||
from systembridgeconnector.models.cpu import Cpu
|
from systembridgeconnector.models.cpu import Cpu
|
||||||
from systembridgeconnector.models.disk import Disk
|
from systembridgeconnector.models.disk import Disk
|
||||||
from systembridgeconnector.models.display import Display
|
from systembridgeconnector.models.display import Display
|
||||||
|
from systembridgeconnector.models.get_data import GetData
|
||||||
from systembridgeconnector.models.gpu import Gpu
|
from systembridgeconnector.models.gpu import Gpu
|
||||||
from systembridgeconnector.models.memory import Memory
|
from systembridgeconnector.models.memory import Memory
|
||||||
|
from systembridgeconnector.models.register_data_listener import RegisterDataListener
|
||||||
from systembridgeconnector.models.system import System
|
from systembridgeconnector.models.system import System
|
||||||
from systembridgeconnector.websocket_client import WebSocketClient
|
from systembridgeconnector.websocket_client import WebSocketClient
|
||||||
|
|
||||||
@ -93,12 +96,14 @@ class SystemBridgeDataUpdateCoordinator(
|
|||||||
if not self.websocket_client.connected:
|
if not self.websocket_client.connected:
|
||||||
await self._setup_websocket()
|
await self._setup_websocket()
|
||||||
|
|
||||||
self.hass.async_create_task(self.websocket_client.get_data(modules))
|
self.hass.async_create_task(
|
||||||
|
self.websocket_client.get_data(GetData(modules=modules))
|
||||||
|
)
|
||||||
|
|
||||||
async def async_handle_module(
|
async def async_handle_module(
|
||||||
self,
|
self,
|
||||||
module_name: str,
|
module_name: str,
|
||||||
module,
|
module: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Handle data from the WebSocket client."""
|
"""Handle data from the WebSocket client."""
|
||||||
self.logger.debug("Set new data for: %s", module_name)
|
self.logger.debug("Set new data for: %s", module_name)
|
||||||
@ -174,7 +179,9 @@ class SystemBridgeDataUpdateCoordinator(
|
|||||||
|
|
||||||
self.hass.async_create_task(self._listen_for_data())
|
self.hass.async_create_task(self._listen_for_data())
|
||||||
|
|
||||||
await self.websocket_client.register_data_listener(MODULES)
|
await self.websocket_client.register_data_listener(
|
||||||
|
RegisterDataListener(modules=MODULES)
|
||||||
|
)
|
||||||
|
|
||||||
self.last_update_success = True
|
self.last_update_success = True
|
||||||
self.async_update_listeners()
|
self.async_update_listeners()
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"name": "System Bridge",
|
"name": "System Bridge",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/system_bridge",
|
"documentation": "https://www.home-assistant.io/integrations/system_bridge",
|
||||||
"requirements": ["systembridgeconnector==3.3.2"],
|
"requirements": ["systembridgeconnector==3.4.4"],
|
||||||
"codeowners": ["@timmo001"],
|
"codeowners": ["@timmo001"],
|
||||||
"zeroconf": ["_system-bridge._tcp.local."],
|
"zeroconf": ["_system-bridge._tcp.local."],
|
||||||
"after_dependencies": ["zeroconf"],
|
"after_dependencies": ["zeroconf"],
|
||||||
|
@ -7,7 +7,7 @@ from .backports.enum import StrEnum
|
|||||||
|
|
||||||
MAJOR_VERSION: Final = 2022
|
MAJOR_VERSION: Final = 2022
|
||||||
MINOR_VERSION: Final = 8
|
MINOR_VERSION: Final = 8
|
||||||
PATCH_VERSION: Final = "4"
|
PATCH_VERSION: Final = "5"
|
||||||
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
__short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}"
|
||||||
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
__version__: Final = f"{__short_version__}.{PATCH_VERSION}"
|
||||||
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0)
|
REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 9, 0)
|
||||||
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "homeassistant"
|
name = "homeassistant"
|
||||||
version = "2022.8.4"
|
version = "2022.8.5"
|
||||||
license = {text = "Apache-2.0"}
|
license = {text = "Apache-2.0"}
|
||||||
description = "Open-source home automation platform running on Python 3."
|
description = "Open-source home automation platform running on Python 3."
|
||||||
readme = "README.rst"
|
readme = "README.rst"
|
||||||
|
@ -37,7 +37,7 @@ PyRMVtransport==0.3.3
|
|||||||
PySocks==1.7.1
|
PySocks==1.7.1
|
||||||
|
|
||||||
# homeassistant.components.switchbot
|
# homeassistant.components.switchbot
|
||||||
PySwitchbot==0.18.4
|
PySwitchbot==0.18.10
|
||||||
|
|
||||||
# homeassistant.components.transport_nsw
|
# homeassistant.components.transport_nsw
|
||||||
PyTransportNSW==0.1.1
|
PyTransportNSW==0.1.1
|
||||||
@ -168,14 +168,14 @@ aioguardian==2022.07.0
|
|||||||
aioharmony==0.2.9
|
aioharmony==0.2.9
|
||||||
|
|
||||||
# homeassistant.components.homekit_controller
|
# homeassistant.components.homekit_controller
|
||||||
aiohomekit==1.2.9
|
aiohomekit==1.2.11
|
||||||
|
|
||||||
# homeassistant.components.emulated_hue
|
# homeassistant.components.emulated_hue
|
||||||
# homeassistant.components.http
|
# homeassistant.components.http
|
||||||
aiohttp_cors==0.7.0
|
aiohttp_cors==0.7.0
|
||||||
|
|
||||||
# homeassistant.components.hue
|
# homeassistant.components.hue
|
||||||
aiohue==4.4.2
|
aiohue==4.5.0
|
||||||
|
|
||||||
# homeassistant.components.imap
|
# homeassistant.components.imap
|
||||||
aioimaplib==1.0.0
|
aioimaplib==1.0.0
|
||||||
@ -399,7 +399,7 @@ beautifulsoup4==4.11.1
|
|||||||
bellows==0.32.0
|
bellows==0.32.0
|
||||||
|
|
||||||
# homeassistant.components.bmw_connected_drive
|
# homeassistant.components.bmw_connected_drive
|
||||||
bimmer_connected==0.10.1
|
bimmer_connected==0.10.2
|
||||||
|
|
||||||
# homeassistant.components.bizkaibus
|
# homeassistant.components.bizkaibus
|
||||||
bizkaibus==0.1.1
|
bizkaibus==0.1.1
|
||||||
@ -842,7 +842,7 @@ holidays==0.14.2
|
|||||||
home-assistant-frontend==20220802.0
|
home-assistant-frontend==20220802.0
|
||||||
|
|
||||||
# homeassistant.components.home_connect
|
# homeassistant.components.home_connect
|
||||||
homeconnect==0.7.1
|
homeconnect==0.7.2
|
||||||
|
|
||||||
# homeassistant.components.homematicip_cloud
|
# homeassistant.components.homematicip_cloud
|
||||||
homematicip==1.0.7
|
homematicip==1.0.7
|
||||||
@ -902,7 +902,7 @@ influxdb-client==1.24.0
|
|||||||
influxdb==5.3.1
|
influxdb==5.3.1
|
||||||
|
|
||||||
# homeassistant.components.inkbird
|
# homeassistant.components.inkbird
|
||||||
inkbird-ble==0.5.2
|
inkbird-ble==0.5.5
|
||||||
|
|
||||||
# homeassistant.components.insteon
|
# homeassistant.components.insteon
|
||||||
insteon-frontend-home-assistant==0.2.0
|
insteon-frontend-home-assistant==0.2.0
|
||||||
@ -2160,7 +2160,7 @@ sendgrid==6.8.2
|
|||||||
sense_energy==0.10.4
|
sense_energy==0.10.4
|
||||||
|
|
||||||
# homeassistant.components.sensorpush
|
# homeassistant.components.sensorpush
|
||||||
sensorpush-ble==1.5.1
|
sensorpush-ble==1.5.2
|
||||||
|
|
||||||
# homeassistant.components.sentry
|
# homeassistant.components.sentry
|
||||||
sentry-sdk==1.8.0
|
sentry-sdk==1.8.0
|
||||||
@ -2281,7 +2281,7 @@ swisshydrodata==0.1.0
|
|||||||
synology-srm==0.2.0
|
synology-srm==0.2.0
|
||||||
|
|
||||||
# homeassistant.components.system_bridge
|
# homeassistant.components.system_bridge
|
||||||
systembridgeconnector==3.3.2
|
systembridgeconnector==3.4.4
|
||||||
|
|
||||||
# homeassistant.components.tailscale
|
# homeassistant.components.tailscale
|
||||||
tailscale==0.2.0
|
tailscale==0.2.0
|
||||||
@ -2473,7 +2473,7 @@ xboxapi==2.0.1
|
|||||||
xiaomi-ble==0.6.4
|
xiaomi-ble==0.6.4
|
||||||
|
|
||||||
# homeassistant.components.knx
|
# homeassistant.components.knx
|
||||||
xknx==0.22.1
|
xknx==1.0.0
|
||||||
|
|
||||||
# homeassistant.components.bluesound
|
# homeassistant.components.bluesound
|
||||||
# homeassistant.components.fritz
|
# homeassistant.components.fritz
|
||||||
|
@ -33,7 +33,7 @@ PyRMVtransport==0.3.3
|
|||||||
PySocks==1.7.1
|
PySocks==1.7.1
|
||||||
|
|
||||||
# homeassistant.components.switchbot
|
# homeassistant.components.switchbot
|
||||||
PySwitchbot==0.18.4
|
PySwitchbot==0.18.10
|
||||||
|
|
||||||
# homeassistant.components.transport_nsw
|
# homeassistant.components.transport_nsw
|
||||||
PyTransportNSW==0.1.1
|
PyTransportNSW==0.1.1
|
||||||
@ -152,14 +152,14 @@ aioguardian==2022.07.0
|
|||||||
aioharmony==0.2.9
|
aioharmony==0.2.9
|
||||||
|
|
||||||
# homeassistant.components.homekit_controller
|
# homeassistant.components.homekit_controller
|
||||||
aiohomekit==1.2.9
|
aiohomekit==1.2.11
|
||||||
|
|
||||||
# homeassistant.components.emulated_hue
|
# homeassistant.components.emulated_hue
|
||||||
# homeassistant.components.http
|
# homeassistant.components.http
|
||||||
aiohttp_cors==0.7.0
|
aiohttp_cors==0.7.0
|
||||||
|
|
||||||
# homeassistant.components.hue
|
# homeassistant.components.hue
|
||||||
aiohue==4.4.2
|
aiohue==4.5.0
|
||||||
|
|
||||||
# homeassistant.components.apache_kafka
|
# homeassistant.components.apache_kafka
|
||||||
aiokafka==0.7.2
|
aiokafka==0.7.2
|
||||||
@ -323,7 +323,7 @@ beautifulsoup4==4.11.1
|
|||||||
bellows==0.32.0
|
bellows==0.32.0
|
||||||
|
|
||||||
# homeassistant.components.bmw_connected_drive
|
# homeassistant.components.bmw_connected_drive
|
||||||
bimmer_connected==0.10.1
|
bimmer_connected==0.10.2
|
||||||
|
|
||||||
# homeassistant.components.bluetooth
|
# homeassistant.components.bluetooth
|
||||||
bleak==0.15.1
|
bleak==0.15.1
|
||||||
@ -619,7 +619,7 @@ holidays==0.14.2
|
|||||||
home-assistant-frontend==20220802.0
|
home-assistant-frontend==20220802.0
|
||||||
|
|
||||||
# homeassistant.components.home_connect
|
# homeassistant.components.home_connect
|
||||||
homeconnect==0.7.1
|
homeconnect==0.7.2
|
||||||
|
|
||||||
# homeassistant.components.homematicip_cloud
|
# homeassistant.components.homematicip_cloud
|
||||||
homematicip==1.0.7
|
homematicip==1.0.7
|
||||||
@ -655,7 +655,7 @@ influxdb-client==1.24.0
|
|||||||
influxdb==5.3.1
|
influxdb==5.3.1
|
||||||
|
|
||||||
# homeassistant.components.inkbird
|
# homeassistant.components.inkbird
|
||||||
inkbird-ble==0.5.2
|
inkbird-ble==0.5.5
|
||||||
|
|
||||||
# homeassistant.components.insteon
|
# homeassistant.components.insteon
|
||||||
insteon-frontend-home-assistant==0.2.0
|
insteon-frontend-home-assistant==0.2.0
|
||||||
@ -1451,7 +1451,7 @@ securetar==2022.2.0
|
|||||||
sense_energy==0.10.4
|
sense_energy==0.10.4
|
||||||
|
|
||||||
# homeassistant.components.sensorpush
|
# homeassistant.components.sensorpush
|
||||||
sensorpush-ble==1.5.1
|
sensorpush-ble==1.5.2
|
||||||
|
|
||||||
# homeassistant.components.sentry
|
# homeassistant.components.sentry
|
||||||
sentry-sdk==1.8.0
|
sentry-sdk==1.8.0
|
||||||
@ -1539,7 +1539,7 @@ sunwatcher==0.2.1
|
|||||||
surepy==0.7.2
|
surepy==0.7.2
|
||||||
|
|
||||||
# homeassistant.components.system_bridge
|
# homeassistant.components.system_bridge
|
||||||
systembridgeconnector==3.3.2
|
systembridgeconnector==3.4.4
|
||||||
|
|
||||||
# homeassistant.components.tailscale
|
# homeassistant.components.tailscale
|
||||||
tailscale==0.2.0
|
tailscale==0.2.0
|
||||||
@ -1665,7 +1665,7 @@ xbox-webapi==2.0.11
|
|||||||
xiaomi-ble==0.6.4
|
xiaomi-ble==0.6.4
|
||||||
|
|
||||||
# homeassistant.components.knx
|
# homeassistant.components.knx
|
||||||
xknx==0.22.1
|
xknx==1.0.0
|
||||||
|
|
||||||
# homeassistant.components.bluesound
|
# homeassistant.components.bluesound
|
||||||
# homeassistant.components.fritz
|
# homeassistant.components.fritz
|
||||||
|
@ -1103,6 +1103,24 @@ async def test_extraction_functions(hass):
|
|||||||
"event_type": "state_changed",
|
"event_type": "state_changed",
|
||||||
"event_data": {"entity_id": "sensor.trigger_event"},
|
"event_data": {"entity_id": "sensor.trigger_event"},
|
||||||
},
|
},
|
||||||
|
# entity_id is a list of strings (not supported)
|
||||||
|
{
|
||||||
|
"platform": "event",
|
||||||
|
"event_type": "state_changed",
|
||||||
|
"event_data": {"entity_id": ["sensor.trigger_event2"]},
|
||||||
|
},
|
||||||
|
# entity_id is not a valid entity ID
|
||||||
|
{
|
||||||
|
"platform": "event",
|
||||||
|
"event_type": "state_changed",
|
||||||
|
"event_data": {"entity_id": "abc"},
|
||||||
|
},
|
||||||
|
# entity_id is not a string
|
||||||
|
{
|
||||||
|
"platform": "event",
|
||||||
|
"event_type": "state_changed",
|
||||||
|
"event_data": {"entity_id": 123},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
"condition": {
|
"condition": {
|
||||||
"condition": "state",
|
"condition": "state",
|
||||||
@ -1151,6 +1169,18 @@ async def test_extraction_functions(hass):
|
|||||||
"event_type": "esphome.button_pressed",
|
"event_type": "esphome.button_pressed",
|
||||||
"event_data": {"device_id": "device-trigger-event"},
|
"event_data": {"device_id": "device-trigger-event"},
|
||||||
},
|
},
|
||||||
|
# device_id is a list of strings (not supported)
|
||||||
|
{
|
||||||
|
"platform": "event",
|
||||||
|
"event_type": "esphome.button_pressed",
|
||||||
|
"event_data": {"device_id": ["device-trigger-event"]},
|
||||||
|
},
|
||||||
|
# device_id is not a string
|
||||||
|
{
|
||||||
|
"platform": "event",
|
||||||
|
"event_type": "esphome.button_pressed",
|
||||||
|
"event_data": {"device_id": 123},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
"condition": {
|
"condition": {
|
||||||
"condition": "device",
|
"condition": "device",
|
||||||
|
@ -164,6 +164,43 @@ async def test_setup_and_retry_adapter_not_yet_available(hass, caplog):
|
|||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_no_race_during_manual_reload_in_retry_state(hass, caplog):
|
||||||
|
"""Test we can successfully reload when the entry is in a retry state."""
|
||||||
|
mock_bt = []
|
||||||
|
with patch("homeassistant.components.bluetooth.HaBleakScanner.async_setup"), patch(
|
||||||
|
"homeassistant.components.bluetooth.HaBleakScanner.start",
|
||||||
|
side_effect=BleakError,
|
||||||
|
), patch(
|
||||||
|
"homeassistant.components.bluetooth.async_get_bluetooth", return_value=mock_bt
|
||||||
|
):
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass, bluetooth.DOMAIN, {bluetooth.DOMAIN: {}}
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
entry = hass.config_entries.async_entries(bluetooth.DOMAIN)[0]
|
||||||
|
|
||||||
|
assert "Failed to start Bluetooth" in caplog.text
|
||||||
|
assert len(bluetooth.async_discovered_service_info(hass)) == 0
|
||||||
|
assert entry.state == ConfigEntryState.SETUP_RETRY
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.bluetooth.HaBleakScanner.start",
|
||||||
|
):
|
||||||
|
await hass.config_entries.async_reload(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert entry.state == ConfigEntryState.LOADED
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.bluetooth.HaBleakScanner.stop",
|
||||||
|
):
|
||||||
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STOP)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
|
||||||
async def test_calling_async_discovered_devices_no_bluetooth(hass, caplog):
|
async def test_calling_async_discovered_devices_no_bluetooth(hass, caplog):
|
||||||
"""Test we fail gracefully when asking for discovered devices and there is no blueooth."""
|
"""Test we fail gracefully when asking for discovered devices and there is no blueooth."""
|
||||||
mock_bt = []
|
mock_bt = []
|
||||||
@ -828,6 +865,66 @@ async def test_register_callback_by_address(
|
|||||||
assert service_info.manufacturer_id == 89
|
assert service_info.manufacturer_id == 89
|
||||||
|
|
||||||
|
|
||||||
|
async def test_register_callback_survives_reload(
|
||||||
|
hass, mock_bleak_scanner_start, enable_bluetooth
|
||||||
|
):
|
||||||
|
"""Test registering a callback by address survives bluetooth being reloaded."""
|
||||||
|
mock_bt = []
|
||||||
|
callbacks = []
|
||||||
|
|
||||||
|
def _fake_subscriber(
|
||||||
|
service_info: BluetoothServiceInfo, change: BluetoothChange
|
||||||
|
) -> None:
|
||||||
|
"""Fake subscriber for the BleakScanner."""
|
||||||
|
callbacks.append((service_info, change))
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.bluetooth.async_get_bluetooth", return_value=mock_bt
|
||||||
|
):
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass, bluetooth.DOMAIN, {bluetooth.DOMAIN: {}}
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
hass.bus.async_fire(EVENT_HOMEASSISTANT_STARTED)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
bluetooth.async_register_callback(
|
||||||
|
hass,
|
||||||
|
_fake_subscriber,
|
||||||
|
{"address": "44:44:33:11:23:45"},
|
||||||
|
BluetoothScanningMode.ACTIVE,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(mock_bleak_scanner_start.mock_calls) == 1
|
||||||
|
|
||||||
|
switchbot_device = BLEDevice("44:44:33:11:23:45", "wohand")
|
||||||
|
switchbot_adv = AdvertisementData(
|
||||||
|
local_name="wohand",
|
||||||
|
service_uuids=["cba20d00-224d-11e6-9fb8-0002a5d5c51b"],
|
||||||
|
manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"},
|
||||||
|
service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10c"},
|
||||||
|
)
|
||||||
|
|
||||||
|
_get_underlying_scanner()._callback(switchbot_device, switchbot_adv)
|
||||||
|
assert len(callbacks) == 1
|
||||||
|
service_info: BluetoothServiceInfo = callbacks[0][0]
|
||||||
|
assert service_info.name == "wohand"
|
||||||
|
assert service_info.manufacturer == "Nordic Semiconductor ASA"
|
||||||
|
assert service_info.manufacturer_id == 89
|
||||||
|
|
||||||
|
entry = hass.config_entries.async_entries(bluetooth.DOMAIN)[0]
|
||||||
|
await hass.config_entries.async_reload(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
_get_underlying_scanner()._callback(switchbot_device, switchbot_adv)
|
||||||
|
assert len(callbacks) == 2
|
||||||
|
service_info: BluetoothServiceInfo = callbacks[1][0]
|
||||||
|
assert service_info.name == "wohand"
|
||||||
|
assert service_info.manufacturer == "Nordic Semiconductor ASA"
|
||||||
|
assert service_info.manufacturer_id == 89
|
||||||
|
|
||||||
|
|
||||||
async def test_process_advertisements_bail_on_good_advertisement(
|
async def test_process_advertisements_bail_on_good_advertisement(
|
||||||
hass: HomeAssistant, mock_bleak_scanner_start, enable_bluetooth
|
hass: HomeAssistant, mock_bleak_scanner_start, enable_bluetooth
|
||||||
):
|
):
|
||||||
|
@ -25,7 +25,7 @@ async def test_async_step_bluetooth_valid_device(hass):
|
|||||||
result["flow_id"], user_input={}
|
result["flow_id"], user_input={}
|
||||||
)
|
)
|
||||||
assert result2["type"] == FlowResultType.CREATE_ENTRY
|
assert result2["type"] == FlowResultType.CREATE_ENTRY
|
||||||
assert result2["title"] == "iBBQ 6AADDD4CAC3D"
|
assert result2["title"] == "iBBQ AC3D"
|
||||||
assert result2["data"] == {}
|
assert result2["data"] == {}
|
||||||
assert result2["result"].unique_id == "4125DDBA-2774-4851-9889-6AADDD4CAC3D"
|
assert result2["result"].unique_id == "4125DDBA-2774-4851-9889-6AADDD4CAC3D"
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ async def test_async_step_user_with_found_devices(hass):
|
|||||||
user_input={"address": "61DE521B-F0BF-9F44-64D4-75BBE1738105"},
|
user_input={"address": "61DE521B-F0BF-9F44-64D4-75BBE1738105"},
|
||||||
)
|
)
|
||||||
assert result2["type"] == FlowResultType.CREATE_ENTRY
|
assert result2["type"] == FlowResultType.CREATE_ENTRY
|
||||||
assert result2["title"] == "IBS-TH 75BBE1738105"
|
assert result2["title"] == "IBS-TH 8105"
|
||||||
assert result2["data"] == {}
|
assert result2["data"] == {}
|
||||||
assert result2["result"].unique_id == "61DE521B-F0BF-9F44-64D4-75BBE1738105"
|
assert result2["result"].unique_id == "61DE521B-F0BF-9F44-64D4-75BBE1738105"
|
||||||
|
|
||||||
@ -184,7 +184,7 @@ async def test_async_step_user_takes_precedence_over_discovery(hass):
|
|||||||
user_input={"address": "61DE521B-F0BF-9F44-64D4-75BBE1738105"},
|
user_input={"address": "61DE521B-F0BF-9F44-64D4-75BBE1738105"},
|
||||||
)
|
)
|
||||||
assert result2["type"] == FlowResultType.CREATE_ENTRY
|
assert result2["type"] == FlowResultType.CREATE_ENTRY
|
||||||
assert result2["title"] == "IBS-TH 75BBE1738105"
|
assert result2["title"] == "IBS-TH 8105"
|
||||||
assert result2["data"] == {}
|
assert result2["data"] == {}
|
||||||
assert result2["result"].unique_id == "61DE521B-F0BF-9F44-64D4-75BBE1738105"
|
assert result2["result"].unique_id == "61DE521B-F0BF-9F44-64D4-75BBE1738105"
|
||||||
|
|
||||||
|
@ -39,10 +39,10 @@ async def test_sensors(hass):
|
|||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert len(hass.states.async_all()) == 3
|
assert len(hass.states.async_all()) == 3
|
||||||
|
|
||||||
temp_sensor = hass.states.get("sensor.ibs_th_75bbe1738105_battery")
|
temp_sensor = hass.states.get("sensor.ibs_th_8105_battery")
|
||||||
temp_sensor_attribtes = temp_sensor.attributes
|
temp_sensor_attribtes = temp_sensor.attributes
|
||||||
assert temp_sensor.state == "87"
|
assert temp_sensor.state == "87"
|
||||||
assert temp_sensor_attribtes[ATTR_FRIENDLY_NAME] == "IBS-TH 75BBE1738105 Battery"
|
assert temp_sensor_attribtes[ATTR_FRIENDLY_NAME] == "IBS-TH 8105 Battery"
|
||||||
assert temp_sensor_attribtes[ATTR_UNIT_OF_MEASUREMENT] == "%"
|
assert temp_sensor_attribtes[ATTR_UNIT_OF_MEASUREMENT] == "%"
|
||||||
assert temp_sensor_attribtes[ATTR_STATE_CLASS] == "measurement"
|
assert temp_sensor_attribtes[ATTR_STATE_CLASS] == "measurement"
|
||||||
|
|
||||||
|
@ -466,21 +466,38 @@ async def test_discovered_by_dhcp_or_discovery_failed_to_get_device(hass, source
|
|||||||
assert result["reason"] == "cannot_connect"
|
assert result["reason"] == "cannot_connect"
|
||||||
|
|
||||||
|
|
||||||
async def test_discovered_by_dhcp_updates_ip(hass):
|
@pytest.mark.parametrize(
|
||||||
|
"source, data",
|
||||||
|
[
|
||||||
|
(
|
||||||
|
config_entries.SOURCE_DHCP,
|
||||||
|
dhcp.DhcpServiceInfo(ip=IP_ADDRESS, macaddress=MAC_ADDRESS, hostname=LABEL),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
config_entries.SOURCE_HOMEKIT,
|
||||||
|
zeroconf.ZeroconfServiceInfo(
|
||||||
|
host=IP_ADDRESS,
|
||||||
|
addresses=[IP_ADDRESS],
|
||||||
|
hostname=LABEL,
|
||||||
|
name=LABEL,
|
||||||
|
port=None,
|
||||||
|
properties={zeroconf.ATTR_PROPERTIES_ID: "any"},
|
||||||
|
type="mock_type",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_discovered_by_dhcp_or_homekit_updates_ip(hass, source, data):
|
||||||
"""Update host from dhcp."""
|
"""Update host from dhcp."""
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
domain=DOMAIN, data={CONF_HOST: "127.0.0.2"}, unique_id=SERIAL
|
domain=DOMAIN, data={CONF_HOST: "127.0.0.2"}, unique_id=SERIAL
|
||||||
)
|
)
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
with _patch_discovery(no_device=True), _patch_config_flow_try_connect(
|
with _patch_discovery(), _patch_config_flow_try_connect():
|
||||||
no_device=True
|
|
||||||
):
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
context={"source": config_entries.SOURCE_DHCP},
|
context={"source": source},
|
||||||
data=dhcp.DhcpServiceInfo(
|
data=data,
|
||||||
ip=IP_ADDRESS, macaddress=MAC_ADDRESS, hostname=LABEL
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert result["type"] == RESULT_TYPE_ABORT
|
assert result["type"] == RESULT_TYPE_ABORT
|
||||||
|
@ -2,21 +2,14 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
from systembridgeconnector.const import (
|
from systembridgeconnector.const import MODEL_SYSTEM, TYPE_DATA_UPDATE
|
||||||
EVENT_DATA,
|
|
||||||
EVENT_MESSAGE,
|
|
||||||
EVENT_MODULE,
|
|
||||||
EVENT_SUBTYPE,
|
|
||||||
EVENT_TYPE,
|
|
||||||
SUBTYPE_BAD_API_KEY,
|
|
||||||
TYPE_DATA_UPDATE,
|
|
||||||
TYPE_ERROR,
|
|
||||||
)
|
|
||||||
from systembridgeconnector.exceptions import (
|
from systembridgeconnector.exceptions import (
|
||||||
AuthenticationException,
|
AuthenticationException,
|
||||||
ConnectionClosedException,
|
ConnectionClosedException,
|
||||||
ConnectionErrorException,
|
ConnectionErrorException,
|
||||||
)
|
)
|
||||||
|
from systembridgeconnector.models.response import Response
|
||||||
|
from systembridgeconnector.models.system import LastUpdated, System
|
||||||
|
|
||||||
from homeassistant import config_entries, data_entry_flow
|
from homeassistant import config_entries, data_entry_flow
|
||||||
from homeassistant.components import zeroconf
|
from homeassistant.components import zeroconf
|
||||||
@ -48,8 +41,8 @@ FIXTURE_ZEROCONF = zeroconf.ZeroconfServiceInfo(
|
|||||||
addresses=["1.1.1.1"],
|
addresses=["1.1.1.1"],
|
||||||
port=9170,
|
port=9170,
|
||||||
hostname="test-bridge.local.",
|
hostname="test-bridge.local.",
|
||||||
type="_system-bridge._udp.local.",
|
type="_system-bridge._tcp.local.",
|
||||||
name="System Bridge - test-bridge._system-bridge._udp.local.",
|
name="System Bridge - test-bridge._system-bridge._tcp.local.",
|
||||||
properties={
|
properties={
|
||||||
"address": "http://test-bridge:9170",
|
"address": "http://test-bridge:9170",
|
||||||
"fqdn": "test-bridge",
|
"fqdn": "test-bridge",
|
||||||
@ -66,34 +59,70 @@ FIXTURE_ZEROCONF_BAD = zeroconf.ZeroconfServiceInfo(
|
|||||||
addresses=["1.1.1.1"],
|
addresses=["1.1.1.1"],
|
||||||
port=9170,
|
port=9170,
|
||||||
hostname="test-bridge.local.",
|
hostname="test-bridge.local.",
|
||||||
type="_system-bridge._udp.local.",
|
type="_system-bridge._tcp.local.",
|
||||||
name="System Bridge - test-bridge._system-bridge._udp.local.",
|
name="System Bridge - test-bridge._system-bridge._tcp.local.",
|
||||||
properties={
|
properties={
|
||||||
"something": "bad",
|
"something": "bad",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
FIXTURE_DATA_SYSTEM = {
|
|
||||||
EVENT_TYPE: TYPE_DATA_UPDATE,
|
|
||||||
EVENT_MESSAGE: "Data changed",
|
|
||||||
EVENT_MODULE: "system",
|
|
||||||
EVENT_DATA: {
|
|
||||||
"uuid": FIXTURE_UUID,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
FIXTURE_DATA_SYSTEM_BAD = {
|
FIXTURE_SYSTEM = System(
|
||||||
EVENT_TYPE: TYPE_DATA_UPDATE,
|
id=FIXTURE_UUID,
|
||||||
EVENT_MESSAGE: "Data changed",
|
boot_time=1,
|
||||||
EVENT_MODULE: "system",
|
fqdn="",
|
||||||
EVENT_DATA: {},
|
hostname="1.1.1.1",
|
||||||
}
|
ip_address_4="1.1.1.1",
|
||||||
|
mac_address=FIXTURE_MAC_ADDRESS,
|
||||||
|
platform="",
|
||||||
|
platform_version="",
|
||||||
|
uptime=1,
|
||||||
|
uuid=FIXTURE_UUID,
|
||||||
|
version="",
|
||||||
|
version_latest="",
|
||||||
|
version_newer_available=False,
|
||||||
|
last_updated=LastUpdated(
|
||||||
|
boot_time=1,
|
||||||
|
fqdn=1,
|
||||||
|
hostname=1,
|
||||||
|
ip_address_4=1,
|
||||||
|
mac_address=1,
|
||||||
|
platform=1,
|
||||||
|
platform_version=1,
|
||||||
|
uptime=1,
|
||||||
|
uuid=1,
|
||||||
|
version=1,
|
||||||
|
version_latest=1,
|
||||||
|
version_newer_available=1,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
FIXTURE_DATA_AUTH_ERROR = {
|
FIXTURE_DATA_RESPONSE = Response(
|
||||||
EVENT_TYPE: TYPE_ERROR,
|
id="1234",
|
||||||
EVENT_SUBTYPE: SUBTYPE_BAD_API_KEY,
|
type=TYPE_DATA_UPDATE,
|
||||||
EVENT_MESSAGE: "Invalid api-key",
|
subtype=None,
|
||||||
}
|
message="Data received",
|
||||||
|
module=MODEL_SYSTEM,
|
||||||
|
data=FIXTURE_SYSTEM,
|
||||||
|
)
|
||||||
|
|
||||||
|
FIXTURE_DATA_RESPONSE_BAD = Response(
|
||||||
|
id="1234",
|
||||||
|
type=TYPE_DATA_UPDATE,
|
||||||
|
subtype=None,
|
||||||
|
message="Data received",
|
||||||
|
module=MODEL_SYSTEM,
|
||||||
|
data={},
|
||||||
|
)
|
||||||
|
|
||||||
|
FIXTURE_DATA_RESPONSE_BAD = Response(
|
||||||
|
id="1234",
|
||||||
|
type=TYPE_DATA_UPDATE,
|
||||||
|
subtype=None,
|
||||||
|
message="Data received",
|
||||||
|
module=MODEL_SYSTEM,
|
||||||
|
data={},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_show_user_form(hass: HomeAssistant) -> None:
|
async def test_show_user_form(hass: HomeAssistant) -> None:
|
||||||
@ -117,9 +146,11 @@ async def test_user_flow(hass: HomeAssistant) -> None:
|
|||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"homeassistant.components.system_bridge.config_flow.WebSocketClient.connect"
|
"homeassistant.components.system_bridge.config_flow.WebSocketClient.connect"
|
||||||
), patch("systembridgeconnector.websocket_client.WebSocketClient.get_data"), patch(
|
), patch(
|
||||||
"systembridgeconnector.websocket_client.WebSocketClient.receive_message",
|
"systembridgeconnector.websocket_client.WebSocketClient.get_data",
|
||||||
return_value=FIXTURE_DATA_SYSTEM,
|
return_value=FIXTURE_DATA_RESPONSE,
|
||||||
|
), patch(
|
||||||
|
"systembridgeconnector.websocket_client.WebSocketClient.listen"
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.system_bridge.async_setup_entry",
|
"homeassistant.components.system_bridge.async_setup_entry",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
@ -167,11 +198,13 @@ async def test_form_connection_closed_cannot_connect(hass: HomeAssistant) -> Non
|
|||||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||||
assert result["errors"] is None
|
assert result["errors"] is None
|
||||||
|
|
||||||
with patch("systembridgeconnector.websocket_client.WebSocketClient.connect"), patch(
|
with patch(
|
||||||
"systembridgeconnector.websocket_client.WebSocketClient.get_data"
|
"homeassistant.components.system_bridge.config_flow.WebSocketClient.connect"
|
||||||
), patch(
|
), patch(
|
||||||
"systembridgeconnector.websocket_client.WebSocketClient.receive_message",
|
"systembridgeconnector.websocket_client.WebSocketClient.get_data",
|
||||||
side_effect=ConnectionClosedException,
|
side_effect=ConnectionClosedException,
|
||||||
|
), patch(
|
||||||
|
"systembridgeconnector.websocket_client.WebSocketClient.listen",
|
||||||
):
|
):
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], FIXTURE_USER_INPUT
|
result["flow_id"], FIXTURE_USER_INPUT
|
||||||
@ -192,11 +225,13 @@ async def test_form_timeout_cannot_connect(hass: HomeAssistant) -> None:
|
|||||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||||
assert result["errors"] is None
|
assert result["errors"] is None
|
||||||
|
|
||||||
with patch("systembridgeconnector.websocket_client.WebSocketClient.connect"), patch(
|
with patch(
|
||||||
"systembridgeconnector.websocket_client.WebSocketClient.get_data"
|
"homeassistant.components.system_bridge.config_flow.WebSocketClient.connect"
|
||||||
), patch(
|
), patch(
|
||||||
"systembridgeconnector.websocket_client.WebSocketClient.receive_message",
|
"systembridgeconnector.websocket_client.WebSocketClient.get_data",
|
||||||
side_effect=asyncio.TimeoutError,
|
side_effect=asyncio.TimeoutError,
|
||||||
|
), patch(
|
||||||
|
"systembridgeconnector.websocket_client.WebSocketClient.listen",
|
||||||
):
|
):
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], FIXTURE_USER_INPUT
|
result["flow_id"], FIXTURE_USER_INPUT
|
||||||
@ -217,11 +252,13 @@ async def test_form_invalid_auth(hass: HomeAssistant) -> None:
|
|||||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||||
assert result["errors"] is None
|
assert result["errors"] is None
|
||||||
|
|
||||||
with patch("systembridgeconnector.websocket_client.WebSocketClient.connect"), patch(
|
with patch(
|
||||||
"systembridgeconnector.websocket_client.WebSocketClient.get_data"
|
"homeassistant.components.system_bridge.config_flow.WebSocketClient.connect"
|
||||||
), patch(
|
), patch(
|
||||||
"systembridgeconnector.websocket_client.WebSocketClient.receive_message",
|
"systembridgeconnector.websocket_client.WebSocketClient.get_data",
|
||||||
side_effect=AuthenticationException,
|
side_effect=AuthenticationException,
|
||||||
|
), patch(
|
||||||
|
"systembridgeconnector.websocket_client.WebSocketClient.listen",
|
||||||
):
|
):
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], FIXTURE_USER_INPUT
|
result["flow_id"], FIXTURE_USER_INPUT
|
||||||
@ -242,11 +279,40 @@ async def test_form_uuid_error(hass: HomeAssistant) -> None:
|
|||||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||||
assert result["errors"] is None
|
assert result["errors"] is None
|
||||||
|
|
||||||
with patch("systembridgeconnector.websocket_client.WebSocketClient.connect"), patch(
|
with patch(
|
||||||
"systembridgeconnector.websocket_client.WebSocketClient.get_data"
|
"homeassistant.components.system_bridge.config_flow.WebSocketClient.connect"
|
||||||
), patch(
|
), patch(
|
||||||
"systembridgeconnector.websocket_client.WebSocketClient.receive_message",
|
"systembridgeconnector.websocket_client.WebSocketClient.get_data",
|
||||||
return_value=FIXTURE_DATA_SYSTEM_BAD,
|
side_effect=ValueError,
|
||||||
|
), patch(
|
||||||
|
"systembridgeconnector.websocket_client.WebSocketClient.listen",
|
||||||
|
):
|
||||||
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
|
result["flow_id"], FIXTURE_USER_INPUT
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert result2["type"] == data_entry_flow.FlowResultType.FORM
|
||||||
|
assert result2["step_id"] == "user"
|
||||||
|
assert result2["errors"] == {"base": "cannot_connect"}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_form_value_error(hass: HomeAssistant) -> None:
|
||||||
|
"""Test we handle error from bad value."""
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||||
|
assert result["errors"] is None
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.system_bridge.config_flow.WebSocketClient.connect"
|
||||||
|
), patch(
|
||||||
|
"systembridgeconnector.websocket_client.WebSocketClient.get_data",
|
||||||
|
return_value=FIXTURE_DATA_RESPONSE_BAD,
|
||||||
|
), patch(
|
||||||
|
"systembridgeconnector.websocket_client.WebSocketClient.listen",
|
||||||
):
|
):
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], FIXTURE_USER_INPUT
|
result["flow_id"], FIXTURE_USER_INPUT
|
||||||
@ -267,11 +333,13 @@ async def test_form_unknown_error(hass: HomeAssistant) -> None:
|
|||||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||||
assert result["errors"] is None
|
assert result["errors"] is None
|
||||||
|
|
||||||
with patch("systembridgeconnector.websocket_client.WebSocketClient.connect"), patch(
|
with patch(
|
||||||
"systembridgeconnector.websocket_client.WebSocketClient.get_data"
|
"homeassistant.components.system_bridge.config_flow.WebSocketClient.connect"
|
||||||
), patch(
|
), patch(
|
||||||
"systembridgeconnector.websocket_client.WebSocketClient.receive_message",
|
"systembridgeconnector.websocket_client.WebSocketClient.get_data",
|
||||||
side_effect=Exception,
|
side_effect=Exception,
|
||||||
|
), patch(
|
||||||
|
"systembridgeconnector.websocket_client.WebSocketClient.listen",
|
||||||
):
|
):
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], FIXTURE_USER_INPUT
|
result["flow_id"], FIXTURE_USER_INPUT
|
||||||
@ -292,11 +360,13 @@ async def test_reauth_authorization_error(hass: HomeAssistant) -> None:
|
|||||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||||
assert result["step_id"] == "authenticate"
|
assert result["step_id"] == "authenticate"
|
||||||
|
|
||||||
with patch("systembridgeconnector.websocket_client.WebSocketClient.connect"), patch(
|
with patch(
|
||||||
"systembridgeconnector.websocket_client.WebSocketClient.get_data"
|
"homeassistant.components.system_bridge.config_flow.WebSocketClient.connect"
|
||||||
), patch(
|
), patch(
|
||||||
"systembridgeconnector.websocket_client.WebSocketClient.receive_message",
|
"systembridgeconnector.websocket_client.WebSocketClient.get_data",
|
||||||
side_effect=AuthenticationException,
|
side_effect=AuthenticationException,
|
||||||
|
), patch(
|
||||||
|
"systembridgeconnector.websocket_client.WebSocketClient.listen",
|
||||||
):
|
):
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], FIXTURE_AUTH_INPUT
|
result["flow_id"], FIXTURE_AUTH_INPUT
|
||||||
@ -340,11 +410,13 @@ async def test_reauth_connection_closed_error(hass: HomeAssistant) -> None:
|
|||||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||||
assert result["step_id"] == "authenticate"
|
assert result["step_id"] == "authenticate"
|
||||||
|
|
||||||
with patch("systembridgeconnector.websocket_client.WebSocketClient.connect"), patch(
|
with patch(
|
||||||
"systembridgeconnector.websocket_client.WebSocketClient.get_data"
|
"homeassistant.components.system_bridge.config_flow.WebSocketClient.connect"
|
||||||
), patch(
|
), patch(
|
||||||
"systembridgeconnector.websocket_client.WebSocketClient.receive_message",
|
"systembridgeconnector.websocket_client.WebSocketClient.get_data",
|
||||||
side_effect=ConnectionClosedException,
|
side_effect=ConnectionClosedException,
|
||||||
|
), patch(
|
||||||
|
"systembridgeconnector.websocket_client.WebSocketClient.listen",
|
||||||
):
|
):
|
||||||
result2 = await hass.config_entries.flow.async_configure(
|
result2 = await hass.config_entries.flow.async_configure(
|
||||||
result["flow_id"], FIXTURE_AUTH_INPUT
|
result["flow_id"], FIXTURE_AUTH_INPUT
|
||||||
@ -370,11 +442,13 @@ async def test_reauth_flow(hass: HomeAssistant) -> None:
|
|||||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||||
assert result["step_id"] == "authenticate"
|
assert result["step_id"] == "authenticate"
|
||||||
|
|
||||||
with patch("systembridgeconnector.websocket_client.WebSocketClient.connect"), patch(
|
with patch(
|
||||||
"systembridgeconnector.websocket_client.WebSocketClient.get_data"
|
"homeassistant.components.system_bridge.config_flow.WebSocketClient.connect"
|
||||||
), patch(
|
), patch(
|
||||||
"systembridgeconnector.websocket_client.WebSocketClient.receive_message",
|
"systembridgeconnector.websocket_client.WebSocketClient.get_data",
|
||||||
return_value=FIXTURE_DATA_SYSTEM,
|
return_value=FIXTURE_DATA_RESPONSE,
|
||||||
|
), patch(
|
||||||
|
"systembridgeconnector.websocket_client.WebSocketClient.listen"
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.system_bridge.async_setup_entry",
|
"homeassistant.components.system_bridge.async_setup_entry",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
@ -402,11 +476,13 @@ async def test_zeroconf_flow(hass: HomeAssistant) -> None:
|
|||||||
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
assert result["type"] == data_entry_flow.FlowResultType.FORM
|
||||||
assert not result["errors"]
|
assert not result["errors"]
|
||||||
|
|
||||||
with patch("systembridgeconnector.websocket_client.WebSocketClient.connect"), patch(
|
with patch(
|
||||||
"systembridgeconnector.websocket_client.WebSocketClient.get_data"
|
"homeassistant.components.system_bridge.config_flow.WebSocketClient.connect"
|
||||||
), patch(
|
), patch(
|
||||||
"systembridgeconnector.websocket_client.WebSocketClient.receive_message",
|
"systembridgeconnector.websocket_client.WebSocketClient.get_data",
|
||||||
return_value=FIXTURE_DATA_SYSTEM,
|
return_value=FIXTURE_DATA_RESPONSE,
|
||||||
|
), patch(
|
||||||
|
"systembridgeconnector.websocket_client.WebSocketClient.listen"
|
||||||
), patch(
|
), patch(
|
||||||
"homeassistant.components.system_bridge.async_setup_entry",
|
"homeassistant.components.system_bridge.async_setup_entry",
|
||||||
return_value=True,
|
return_value=True,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user