mirror of
https://github.com/home-assistant/core.git
synced 2025-07-18 10:47:10 +00:00
Merge branch 'dev' into esphome_bronze
This commit is contained in:
commit
03f6eda8b1
@ -29,6 +29,8 @@ from .entity import (
|
|||||||
)
|
)
|
||||||
from .enum_mapper import EsphomeEnumMapper
|
from .enum_mapper import EsphomeEnumMapper
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
_ESPHOME_ACP_STATE_TO_HASS_STATE: EsphomeEnumMapper[
|
_ESPHOME_ACP_STATE_TO_HASS_STATE: EsphomeEnumMapper[
|
||||||
ESPHomeAlarmControlPanelState, AlarmControlPanelState
|
ESPHomeAlarmControlPanelState, AlarmControlPanelState
|
||||||
] = EsphomeEnumMapper(
|
] = EsphomeEnumMapper(
|
||||||
|
@ -47,6 +47,8 @@ from .entry_data import ESPHomeConfigEntry, RuntimeEntryData
|
|||||||
from .enum_mapper import EsphomeEnumMapper
|
from .enum_mapper import EsphomeEnumMapper
|
||||||
from .ffmpeg_proxy import async_create_proxy_url
|
from .ffmpeg_proxy import async_create_proxy_url
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
_VOICE_ASSISTANT_EVENT_TYPES: EsphomeEnumMapper[
|
_VOICE_ASSISTANT_EVENT_TYPES: EsphomeEnumMapper[
|
||||||
|
@ -20,6 +20,8 @@ from .const import DOMAIN
|
|||||||
from .entity import EsphomeAssistEntity, EsphomeEntity, platform_async_setup_entry
|
from .entity import EsphomeAssistEntity, EsphomeEntity, platform_async_setup_entry
|
||||||
from .entry_data import ESPHomeConfigEntry
|
from .entry_data import ESPHomeConfigEntry
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
@ -16,6 +16,8 @@ from .entity import (
|
|||||||
platform_async_setup_entry,
|
platform_async_setup_entry,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
class EsphomeButton(EsphomeEntity[ButtonInfo, EntityState], ButtonEntity):
|
class EsphomeButton(EsphomeEntity[ButtonInfo, EntityState], ButtonEntity):
|
||||||
"""A button implementation for ESPHome."""
|
"""A button implementation for ESPHome."""
|
||||||
|
@ -16,6 +16,8 @@ from homeassistant.core import callback
|
|||||||
|
|
||||||
from .entity import EsphomeEntity, platform_async_setup_entry
|
from .entity import EsphomeEntity, platform_async_setup_entry
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
class EsphomeCamera(Camera, EsphomeEntity[CameraInfo, CameraState]):
|
class EsphomeCamera(Camera, EsphomeEntity[CameraInfo, CameraState]):
|
||||||
"""A camera implementation for ESPHome."""
|
"""A camera implementation for ESPHome."""
|
||||||
|
@ -65,6 +65,8 @@ from .entity import (
|
|||||||
)
|
)
|
||||||
from .enum_mapper import EsphomeEnumMapper
|
from .enum_mapper import EsphomeEnumMapper
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
FAN_QUIET = "quiet"
|
FAN_QUIET = "quiet"
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@ from .entity import (
|
|||||||
platform_async_setup_entry,
|
platform_async_setup_entry,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
class EsphomeCover(EsphomeEntity[CoverInfo, CoverState], CoverEntity):
|
class EsphomeCover(EsphomeEntity[CoverInfo, CoverState], CoverEntity):
|
||||||
"""A cover implementation for ESPHome."""
|
"""A cover implementation for ESPHome."""
|
||||||
|
@ -11,6 +11,8 @@ from homeassistant.components.date import DateEntity
|
|||||||
|
|
||||||
from .entity import EsphomeEntity, esphome_state_property, platform_async_setup_entry
|
from .entity import EsphomeEntity, esphome_state_property, platform_async_setup_entry
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
class EsphomeDate(EsphomeEntity[DateInfo, DateState], DateEntity):
|
class EsphomeDate(EsphomeEntity[DateInfo, DateState], DateEntity):
|
||||||
"""A date implementation for esphome."""
|
"""A date implementation for esphome."""
|
||||||
|
@ -12,6 +12,8 @@ from homeassistant.util import dt as dt_util
|
|||||||
|
|
||||||
from .entity import EsphomeEntity, esphome_state_property, platform_async_setup_entry
|
from .entity import EsphomeEntity, esphome_state_property, platform_async_setup_entry
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
class EsphomeDateTime(EsphomeEntity[DateTimeInfo, DateTimeState], DateTimeEntity):
|
class EsphomeDateTime(EsphomeEntity[DateTimeInfo, DateTimeState], DateTimeEntity):
|
||||||
"""A datetime implementation for esphome."""
|
"""A datetime implementation for esphome."""
|
||||||
|
@ -12,6 +12,8 @@ from homeassistant.util.enum import try_parse_enum
|
|||||||
|
|
||||||
from .entity import EsphomeEntity, platform_async_setup_entry
|
from .entity import EsphomeEntity, platform_async_setup_entry
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
class EsphomeEvent(EsphomeEntity[EventInfo, Event], EventEntity):
|
class EsphomeEvent(EsphomeEntity[EventInfo, Event], EventEntity):
|
||||||
"""An event implementation for ESPHome."""
|
"""An event implementation for ESPHome."""
|
||||||
|
@ -30,6 +30,8 @@ from .entity import (
|
|||||||
)
|
)
|
||||||
from .enum_mapper import EsphomeEnumMapper
|
from .enum_mapper import EsphomeEnumMapper
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
ORDERED_NAMED_FAN_SPEEDS = [FanSpeed.LOW, FanSpeed.MEDIUM, FanSpeed.HIGH]
|
ORDERED_NAMED_FAN_SPEEDS = [FanSpeed.LOW, FanSpeed.MEDIUM, FanSpeed.HIGH]
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,6 +38,8 @@ from .entity import (
|
|||||||
platform_async_setup_entry,
|
platform_async_setup_entry,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
FLASH_LENGTHS = {FLASH_SHORT: 2, FLASH_LONG: 10}
|
FLASH_LENGTHS = {FLASH_SHORT: 2, FLASH_LONG: 10}
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@ from .entity import (
|
|||||||
platform_async_setup_entry,
|
platform_async_setup_entry,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
class EsphomeLock(EsphomeEntity[LockInfo, LockEntityState], LockEntity):
|
class EsphomeLock(EsphomeEntity[LockInfo, LockEntityState], LockEntity):
|
||||||
"""A lock implementation for ESPHome."""
|
"""A lock implementation for ESPHome."""
|
||||||
|
@ -44,7 +44,7 @@ from homeassistant.core import (
|
|||||||
State,
|
State,
|
||||||
callback,
|
callback,
|
||||||
)
|
)
|
||||||
from homeassistant.exceptions import TemplateError
|
from homeassistant.exceptions import HomeAssistantError, TemplateError
|
||||||
from homeassistant.helpers import (
|
from homeassistant.helpers import (
|
||||||
config_validation as cv,
|
config_validation as cv,
|
||||||
device_registry as dr,
|
device_registry as dr,
|
||||||
@ -827,7 +827,18 @@ def execute_service(
|
|||||||
entry_data: RuntimeEntryData, service: UserService, call: ServiceCall
|
entry_data: RuntimeEntryData, service: UserService, call: ServiceCall
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Execute a service on a node."""
|
"""Execute a service on a node."""
|
||||||
|
try:
|
||||||
entry_data.client.execute_service(service, call.data)
|
entry_data.client.execute_service(service, call.data)
|
||||||
|
except APIConnectionError as err:
|
||||||
|
raise HomeAssistantError(
|
||||||
|
translation_domain=DOMAIN,
|
||||||
|
translation_key="action_call_failed",
|
||||||
|
translation_placeholders={
|
||||||
|
"call_name": service.name,
|
||||||
|
"device_name": entry_data.name,
|
||||||
|
"error": str(err),
|
||||||
|
},
|
||||||
|
) from err
|
||||||
|
|
||||||
|
|
||||||
def build_service_name(device_info: EsphomeDeviceInfo, service: UserService) -> str:
|
def build_service_name(device_info: EsphomeDeviceInfo, service: UserService) -> str:
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
"loggers": ["aioesphomeapi", "noiseprotocol", "bleak_esphome"],
|
"loggers": ["aioesphomeapi", "noiseprotocol", "bleak_esphome"],
|
||||||
"mqtt": ["esphome/discover/#"],
|
"mqtt": ["esphome/discover/#"],
|
||||||
"requirements": [
|
"requirements": [
|
||||||
"aioesphomeapi==29.10.0",
|
"aioesphomeapi==30.0.1",
|
||||||
"esphome-dashboard-api==1.2.3",
|
"esphome-dashboard-api==1.2.3",
|
||||||
"bleak-esphome==2.13.1"
|
"bleak-esphome==2.13.1"
|
||||||
],
|
],
|
||||||
|
@ -41,6 +41,8 @@ from .entity import (
|
|||||||
from .enum_mapper import EsphomeEnumMapper
|
from .enum_mapper import EsphomeEnumMapper
|
||||||
from .ffmpeg_proxy import async_create_proxy_url
|
from .ffmpeg_proxy import async_create_proxy_url
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
_STATES: EsphomeEnumMapper[EspMediaPlayerState, MediaPlayerState] = EsphomeEnumMapper(
|
_STATES: EsphomeEnumMapper[EspMediaPlayerState, MediaPlayerState] = EsphomeEnumMapper(
|
||||||
|
@ -23,6 +23,8 @@ from .entity import (
|
|||||||
)
|
)
|
||||||
from .enum_mapper import EsphomeEnumMapper
|
from .enum_mapper import EsphomeEnumMapper
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
NUMBER_MODES: EsphomeEnumMapper[EsphomeNumberMode, NumberMode] = EsphomeEnumMapper(
|
NUMBER_MODES: EsphomeEnumMapper[EsphomeNumberMode, NumberMode] = EsphomeEnumMapper(
|
||||||
{
|
{
|
||||||
EsphomeNumberMode.AUTO: NumberMode.AUTO,
|
EsphomeNumberMode.AUTO: NumberMode.AUTO,
|
||||||
|
@ -25,6 +25,8 @@ from .entity import (
|
|||||||
)
|
)
|
||||||
from .entry_data import ESPHomeConfigEntry, RuntimeEntryData
|
from .entry_data import ESPHomeConfigEntry, RuntimeEntryData
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
@ -29,6 +29,8 @@ from homeassistant.util.enum import try_parse_enum
|
|||||||
from .entity import EsphomeEntity, platform_async_setup_entry
|
from .entity import EsphomeEntity, platform_async_setup_entry
|
||||||
from .enum_mapper import EsphomeEnumMapper
|
from .enum_mapper import EsphomeEnumMapper
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
|
@ -180,5 +180,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"exceptions": {
|
||||||
|
"action_call_failed": {
|
||||||
|
"message": "Failed to execute the action call {call_name} on {device_name}: {error}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,8 @@ from .entity import (
|
|||||||
platform_async_setup_entry,
|
platform_async_setup_entry,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
class EsphomeSwitch(EsphomeEntity[SwitchInfo, SwitchState], SwitchEntity):
|
class EsphomeSwitch(EsphomeEntity[SwitchInfo, SwitchState], SwitchEntity):
|
||||||
"""A switch implementation for ESPHome."""
|
"""A switch implementation for ESPHome."""
|
||||||
|
@ -17,6 +17,8 @@ from .entity import (
|
|||||||
)
|
)
|
||||||
from .enum_mapper import EsphomeEnumMapper
|
from .enum_mapper import EsphomeEnumMapper
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
TEXT_MODES: EsphomeEnumMapper[EsphomeTextMode, TextMode] = EsphomeEnumMapper(
|
TEXT_MODES: EsphomeEnumMapper[EsphomeTextMode, TextMode] = EsphomeEnumMapper(
|
||||||
{
|
{
|
||||||
EsphomeTextMode.TEXT: TextMode.TEXT,
|
EsphomeTextMode.TEXT: TextMode.TEXT,
|
||||||
|
@ -11,6 +11,8 @@ from homeassistant.components.time import TimeEntity
|
|||||||
|
|
||||||
from .entity import EsphomeEntity, esphome_state_property, platform_async_setup_entry
|
from .entity import EsphomeEntity, esphome_state_property, platform_async_setup_entry
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
class EsphomeTime(EsphomeEntity[TimeInfo, TimeState], TimeEntity):
|
class EsphomeTime(EsphomeEntity[TimeInfo, TimeState], TimeEntity):
|
||||||
"""A time implementation for esphome."""
|
"""A time implementation for esphome."""
|
||||||
|
@ -38,6 +38,8 @@ from .entity import (
|
|||||||
)
|
)
|
||||||
from .entry_data import RuntimeEntryData
|
from .entry_data import RuntimeEntryData
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
KEY_UPDATE_LOCK = "esphome_update_lock"
|
KEY_UPDATE_LOCK = "esphome_update_lock"
|
||||||
|
|
||||||
NO_FEATURES = UpdateEntityFeature(0)
|
NO_FEATURES = UpdateEntityFeature(0)
|
||||||
|
@ -22,6 +22,8 @@ from .entity import (
|
|||||||
platform_async_setup_entry,
|
platform_async_setup_entry,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
PARALLEL_UPDATES = 0
|
||||||
|
|
||||||
|
|
||||||
class EsphomeValve(EsphomeEntity[ValveInfo, ValveState], ValveEntity):
|
class EsphomeValve(EsphomeEntity[ValveInfo, ValveState], ValveEntity):
|
||||||
"""A valve implementation for ESPHome."""
|
"""A valve implementation for ESPHome."""
|
||||||
|
@ -197,7 +197,12 @@ class OAuth2FlowHandler(
|
|||||||
"Error reading primary calendar, make sure Google Calendar API is enabled: %s",
|
"Error reading primary calendar, make sure Google Calendar API is enabled: %s",
|
||||||
err,
|
err,
|
||||||
)
|
)
|
||||||
return self.async_abort(reason="api_disabled")
|
return self.async_abort(
|
||||||
|
reason="calendar_api_disabled",
|
||||||
|
description_placeholders={
|
||||||
|
"calendar_api_url": "https://console.cloud.google.com/apis/library/calendar-json.googleapis.com"
|
||||||
|
},
|
||||||
|
)
|
||||||
except ApiException as err:
|
except ApiException as err:
|
||||||
_LOGGER.error("Error reading primary calendar: %s", err)
|
_LOGGER.error("Error reading primary calendar: %s", err)
|
||||||
return self.async_abort(reason="cannot_connect")
|
return self.async_abort(reason="cannot_connect")
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||||
"timeout_connect": "[%key:common::config_flow::error::timeout_connect%]",
|
"timeout_connect": "[%key:common::config_flow::error::timeout_connect%]",
|
||||||
"code_expired": "Authentication code expired or credential setup is invalid, please try again.",
|
"code_expired": "Authentication code expired or credential setup is invalid, please try again.",
|
||||||
"api_disabled": "You must enable the Google Calendar API in the Google Cloud Console"
|
"calendar_api_disabled": "You must [enable the Google Calendar API]({calendar_api_url}) in the Google Cloud Console"
|
||||||
},
|
},
|
||||||
"create_entry": {
|
"create_entry": {
|
||||||
"default": "[%key:common::config_flow::create_entry::authenticated%]"
|
"default": "[%key:common::config_flow::create_entry::authenticated%]"
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
},
|
},
|
||||||
"data_description": {
|
"data_description": {
|
||||||
"host": "The IP address or hostname of your LaMetric TIME on your network.",
|
"host": "The IP address or hostname of your LaMetric TIME on your network.",
|
||||||
"api_key": "You can find this API key in [devices page in your LaMetric developer account](https://developer.lametric.com/user/devices)."
|
"api_key": "You can find this API key in the [devices page in your LaMetric developer account](https://developer.lametric.com/user/devices)."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cloud_select_device": {
|
"cloud_select_device": {
|
||||||
@ -83,8 +83,8 @@
|
|||||||
"brightness_mode": {
|
"brightness_mode": {
|
||||||
"name": "Brightness mode",
|
"name": "Brightness mode",
|
||||||
"state": {
|
"state": {
|
||||||
"auto": "Automatic",
|
"auto": "[%key:common::state::auto%]",
|
||||||
"manual": "Manual"
|
"manual": "[%key:common::state::manual%]"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
2
requirements_all.txt
generated
2
requirements_all.txt
generated
@ -243,7 +243,7 @@ aioelectricitymaps==0.4.0
|
|||||||
aioemonitor==1.0.5
|
aioemonitor==1.0.5
|
||||||
|
|
||||||
# homeassistant.components.esphome
|
# homeassistant.components.esphome
|
||||||
aioesphomeapi==29.10.0
|
aioesphomeapi==30.0.1
|
||||||
|
|
||||||
# homeassistant.components.flo
|
# homeassistant.components.flo
|
||||||
aioflo==2021.11.0
|
aioflo==2021.11.0
|
||||||
|
2
requirements_test_all.txt
generated
2
requirements_test_all.txt
generated
@ -231,7 +231,7 @@ aioelectricitymaps==0.4.0
|
|||||||
aioemonitor==1.0.5
|
aioemonitor==1.0.5
|
||||||
|
|
||||||
# homeassistant.components.esphome
|
# homeassistant.components.esphome
|
||||||
aioesphomeapi==29.10.0
|
aioesphomeapi==30.0.1
|
||||||
|
|
||||||
# homeassistant.components.flo
|
# homeassistant.components.flo
|
||||||
aioflo==2021.11.0
|
aioflo==2021.11.0
|
||||||
|
@ -88,6 +88,7 @@ OSI_APPROVED_LICENSES_SPDX = {
|
|||||||
"MPL-1.1",
|
"MPL-1.1",
|
||||||
"MPL-2.0",
|
"MPL-2.0",
|
||||||
"PSF-2.0",
|
"PSF-2.0",
|
||||||
|
"Python-2.0",
|
||||||
"Unlicense",
|
"Unlicense",
|
||||||
"Zlib",
|
"Zlib",
|
||||||
"ZPL-2.1",
|
"ZPL-2.1",
|
||||||
|
@ -106,6 +106,7 @@ async def test_restore_dashboard_storage_skipped_if_addon_uninstalled(
|
|||||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert mock_config_entry.state is ConfigEntryState.LOADED
|
assert mock_config_entry.state is ConfigEntryState.LOADED
|
||||||
|
await hass.async_block_till_done() # wait for dashboard setup
|
||||||
assert "test-slug is no longer installed" in caplog.text
|
assert "test-slug is no longer installed" in caplog.text
|
||||||
assert not mock_dashboard_api.called
|
assert not mock_dashboard_api.called
|
||||||
|
|
||||||
|
@ -42,6 +42,7 @@ from homeassistant.const import (
|
|||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant, ServiceCall
|
from homeassistant.core import HomeAssistant, ServiceCall
|
||||||
from homeassistant.data_entry_flow import FlowResultType
|
from homeassistant.data_entry_flow import FlowResultType
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers import device_registry as dr, issue_registry as ir
|
from homeassistant.helpers import device_registry as dr, issue_registry as ir
|
||||||
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
|
from homeassistant.helpers.service_info.dhcp import DhcpServiceInfo
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
@ -1123,6 +1124,65 @@ async def test_esphome_user_services_ignores_invalid_arg_types(
|
|||||||
assert not hass.services.has_service(DOMAIN, "with_dash_bad_service")
|
assert not hass.services.has_service(DOMAIN, "with_dash_bad_service")
|
||||||
|
|
||||||
|
|
||||||
|
async def test_esphome_user_service_fails(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_client: APIClient,
|
||||||
|
mock_esphome_device: Callable[
|
||||||
|
[APIClient, list[EntityInfo], list[UserService], list[EntityState]],
|
||||||
|
Awaitable[MockESPHomeDevice],
|
||||||
|
],
|
||||||
|
) -> None:
|
||||||
|
"""Test executing a user service fails due to disconnect."""
|
||||||
|
entity_info = []
|
||||||
|
states = []
|
||||||
|
service1 = UserService(
|
||||||
|
name="simple_service",
|
||||||
|
key=2,
|
||||||
|
args=[
|
||||||
|
UserServiceArg(name="arg1", type=UserServiceArgType.BOOL),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
await mock_esphome_device(
|
||||||
|
mock_client=mock_client,
|
||||||
|
entity_info=entity_info,
|
||||||
|
user_service=[service1],
|
||||||
|
device_info={"name": "with-dash"},
|
||||||
|
states=states,
|
||||||
|
)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert hass.services.has_service(DOMAIN, "with_dash_simple_service")
|
||||||
|
|
||||||
|
mock_client.execute_service = Mock(side_effect=APIConnectionError("fail"))
|
||||||
|
with pytest.raises(HomeAssistantError) as exc:
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN, "with_dash_simple_service", {"arg1": True}, blocking=True
|
||||||
|
)
|
||||||
|
assert exc.value.translation_domain == DOMAIN
|
||||||
|
assert exc.value.translation_key == "action_call_failed"
|
||||||
|
assert exc.value.translation_placeholders == {
|
||||||
|
"call_name": "simple_service",
|
||||||
|
"device_name": "with-dash",
|
||||||
|
"error": "fail",
|
||||||
|
}
|
||||||
|
assert (
|
||||||
|
str(exc.value)
|
||||||
|
== "Failed to execute the action call simple_service on with-dash: fail"
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_client.execute_service.assert_has_calls(
|
||||||
|
[
|
||||||
|
call(
|
||||||
|
UserService(
|
||||||
|
name="simple_service",
|
||||||
|
key=2,
|
||||||
|
args=[UserServiceArg(name="arg1", type=UserServiceArgType.BOOL)],
|
||||||
|
),
|
||||||
|
{"arg1": True},
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_esphome_user_services_changes(
|
async def test_esphome_user_services_changes(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_client: APIClient,
|
mock_client: APIClient,
|
||||||
|
@ -570,7 +570,7 @@ async def test_reauth_flow(
|
|||||||
("primary_calendar_error", "primary_calendar_status", "reason"),
|
("primary_calendar_error", "primary_calendar_status", "reason"),
|
||||||
[
|
[
|
||||||
(ClientError(), None, "cannot_connect"),
|
(ClientError(), None, "cannot_connect"),
|
||||||
(None, HTTPStatus.FORBIDDEN, "api_disabled"),
|
(None, HTTPStatus.FORBIDDEN, "calendar_api_disabled"),
|
||||||
(None, HTTPStatus.SERVICE_UNAVAILABLE, "cannot_connect"),
|
(None, HTTPStatus.SERVICE_UNAVAILABLE, "cannot_connect"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user