mirror of
https://github.com/home-assistant/core.git
synced 2025-08-11 08:35:15 +02:00
Add device action to mobile app to notify (#43814)
This commit is contained in:
@@ -541,7 +541,6 @@ omit =
|
|||||||
homeassistant/components/minio/*
|
homeassistant/components/minio/*
|
||||||
homeassistant/components/mitemp_bt/sensor.py
|
homeassistant/components/mitemp_bt/sensor.py
|
||||||
homeassistant/components/mjpeg/camera.py
|
homeassistant/components/mjpeg/camera.py
|
||||||
homeassistant/components/mobile_app/*
|
|
||||||
homeassistant/components/mochad/*
|
homeassistant/components/mochad/*
|
||||||
homeassistant/components/modbus/climate.py
|
homeassistant/components/modbus/climate.py
|
||||||
homeassistant/components/modbus/cover.py
|
homeassistant/components/modbus/cover.py
|
||||||
|
@@ -123,6 +123,7 @@ async def async_unload_entry(hass, entry):
|
|||||||
|
|
||||||
webhook_unregister(hass, webhook_id)
|
webhook_unregister(hass, webhook_id)
|
||||||
del hass.data[DOMAIN][DATA_CONFIG_ENTRIES][webhook_id]
|
del hass.data[DOMAIN][DATA_CONFIG_ENTRIES][webhook_id]
|
||||||
|
del hass.data[DOMAIN][DATA_DEVICES][webhook_id]
|
||||||
await hass_notify.async_reload(hass, DOMAIN)
|
await hass_notify.async_reload(hass, DOMAIN)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
@@ -15,6 +15,7 @@ DATA_DELETED_IDS = "deleted_ids"
|
|||||||
DATA_DEVICES = "devices"
|
DATA_DEVICES = "devices"
|
||||||
DATA_SENSOR = "sensor"
|
DATA_SENSOR = "sensor"
|
||||||
DATA_STORE = "store"
|
DATA_STORE = "store"
|
||||||
|
DATA_NOTIFY = "notify"
|
||||||
|
|
||||||
ATTR_APP_DATA = "app_data"
|
ATTR_APP_DATA = "app_data"
|
||||||
ATTR_APP_ID = "app_id"
|
ATTR_APP_ID = "app_id"
|
||||||
|
87
homeassistant/components/mobile_app/device_action.py
Normal file
87
homeassistant/components/mobile_app/device_action.py
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
"""Provides device actions for Mobile App."""
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.components import notify
|
||||||
|
from homeassistant.components.device_automation import InvalidDeviceAutomationConfig
|
||||||
|
from homeassistant.const import CONF_DEVICE_ID, CONF_DOMAIN, CONF_TYPE
|
||||||
|
from homeassistant.core import Context, HomeAssistant
|
||||||
|
from homeassistant.helpers import config_validation as cv, template
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
from .util import get_notify_service, supports_push, webhook_id_from_device_id
|
||||||
|
|
||||||
|
ACTION_SCHEMA = cv.DEVICE_ACTION_BASE_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
vol.Required(CONF_TYPE): "notify",
|
||||||
|
vol.Required(notify.ATTR_MESSAGE): cv.template,
|
||||||
|
vol.Optional(notify.ATTR_TITLE): cv.template,
|
||||||
|
vol.Optional(notify.ATTR_DATA): cv.template_complex,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_get_actions(hass: HomeAssistant, device_id: str) -> List[dict]:
|
||||||
|
"""List device actions for Mobile App devices."""
|
||||||
|
webhook_id = webhook_id_from_device_id(hass, device_id)
|
||||||
|
|
||||||
|
if webhook_id is None or not supports_push(hass, webhook_id):
|
||||||
|
return []
|
||||||
|
|
||||||
|
return [{CONF_DEVICE_ID: device_id, CONF_DOMAIN: DOMAIN, CONF_TYPE: "notify"}]
|
||||||
|
|
||||||
|
|
||||||
|
async def async_call_action_from_config(
|
||||||
|
hass: HomeAssistant, config: dict, variables: dict, context: Optional[Context]
|
||||||
|
) -> None:
|
||||||
|
"""Execute a device action."""
|
||||||
|
webhook_id = webhook_id_from_device_id(hass, config[CONF_DEVICE_ID])
|
||||||
|
|
||||||
|
if webhook_id is None:
|
||||||
|
raise InvalidDeviceAutomationConfig(
|
||||||
|
"Unable to resolve webhook ID from the device ID"
|
||||||
|
)
|
||||||
|
|
||||||
|
service_name = get_notify_service(hass, webhook_id)
|
||||||
|
|
||||||
|
if service_name is None:
|
||||||
|
raise InvalidDeviceAutomationConfig(
|
||||||
|
"Unable to find notify service for webhook ID"
|
||||||
|
)
|
||||||
|
|
||||||
|
service_data = {notify.ATTR_TARGET: webhook_id}
|
||||||
|
|
||||||
|
# Render it here because we have access to variables here.
|
||||||
|
for key in (notify.ATTR_MESSAGE, notify.ATTR_TITLE, notify.ATTR_DATA):
|
||||||
|
if key not in config:
|
||||||
|
continue
|
||||||
|
|
||||||
|
value_template = config[key]
|
||||||
|
template.attach(hass, value_template)
|
||||||
|
|
||||||
|
try:
|
||||||
|
service_data[key] = template.render_complex(value_template, variables)
|
||||||
|
except template.TemplateError as err:
|
||||||
|
raise InvalidDeviceAutomationConfig(
|
||||||
|
f"Error rendering {key}: {err}"
|
||||||
|
) from err
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
notify.DOMAIN, service_name, service_data, blocking=True, context=context
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_get_action_capabilities(hass, config):
|
||||||
|
"""List action capabilities."""
|
||||||
|
if config[CONF_TYPE] != "notify":
|
||||||
|
return {}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"extra_fields": vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(notify.ATTR_MESSAGE): str,
|
||||||
|
vol.Optional(notify.ATTR_TITLE): str,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
@@ -35,8 +35,10 @@ from .const import (
|
|||||||
ATTR_PUSH_TOKEN,
|
ATTR_PUSH_TOKEN,
|
||||||
ATTR_PUSH_URL,
|
ATTR_PUSH_URL,
|
||||||
DATA_CONFIG_ENTRIES,
|
DATA_CONFIG_ENTRIES,
|
||||||
|
DATA_NOTIFY,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
)
|
)
|
||||||
|
from .util import supports_push
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -44,15 +46,13 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
def push_registrations(hass):
|
def push_registrations(hass):
|
||||||
"""Return a dictionary of push enabled registrations."""
|
"""Return a dictionary of push enabled registrations."""
|
||||||
targets = {}
|
targets = {}
|
||||||
|
|
||||||
for webhook_id, entry in hass.data[DOMAIN][DATA_CONFIG_ENTRIES].items():
|
for webhook_id, entry in hass.data[DOMAIN][DATA_CONFIG_ENTRIES].items():
|
||||||
data = entry.data
|
if not supports_push(hass, webhook_id):
|
||||||
app_data = data[ATTR_APP_DATA]
|
continue
|
||||||
if ATTR_PUSH_TOKEN in app_data and ATTR_PUSH_URL in app_data:
|
|
||||||
device_name = data[ATTR_DEVICE_NAME]
|
targets[entry.data[ATTR_DEVICE_NAME]] = webhook_id
|
||||||
if device_name in targets:
|
|
||||||
_LOGGER.warning("Found duplicate device name %s", device_name)
|
|
||||||
continue
|
|
||||||
targets[device_name] = webhook_id
|
|
||||||
return targets
|
return targets
|
||||||
|
|
||||||
|
|
||||||
@@ -84,7 +84,8 @@ def log_rate_limits(hass, device_name, resp, level=logging.INFO):
|
|||||||
async def async_get_service(hass, config, discovery_info=None):
|
async def async_get_service(hass, config, discovery_info=None):
|
||||||
"""Get the mobile_app notification service."""
|
"""Get the mobile_app notification service."""
|
||||||
session = async_get_clientsession(hass)
|
session = async_get_clientsession(hass)
|
||||||
return MobileAppNotificationService(session)
|
service = hass.data[DOMAIN][DATA_NOTIFY] = MobileAppNotificationService(session)
|
||||||
|
return service
|
||||||
|
|
||||||
|
|
||||||
class MobileAppNotificationService(BaseNotificationService):
|
class MobileAppNotificationService(BaseNotificationService):
|
||||||
|
@@ -8,5 +8,10 @@
|
|||||||
"abort": {
|
"abort": {
|
||||||
"install_app": "Open the mobile app to set up the integration with Home Assistant. See [the docs]({apps_url}) for a list of compatible apps."
|
"install_app": "Open the mobile app to set up the integration with Home Assistant. See [the docs]({apps_url}) for a list of compatible apps."
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"device_automation": {
|
||||||
|
"action_type": {
|
||||||
|
"notify": "Send a notification"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,5 +8,10 @@
|
|||||||
"description": "Do you want to set up the Mobile App component?"
|
"description": "Do you want to set up the Mobile App component?"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"device_automation": {
|
||||||
|
"action_type": {
|
||||||
|
"notify": "Send a notification"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
47
homeassistant/components/mobile_app/util.py
Normal file
47
homeassistant/components/mobile_app/util.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
"""Mobile app utility functions."""
|
||||||
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
|
from homeassistant.core import callback
|
||||||
|
|
||||||
|
from .const import (
|
||||||
|
ATTR_APP_DATA,
|
||||||
|
ATTR_PUSH_TOKEN,
|
||||||
|
ATTR_PUSH_URL,
|
||||||
|
DATA_CONFIG_ENTRIES,
|
||||||
|
DATA_DEVICES,
|
||||||
|
DATA_NOTIFY,
|
||||||
|
DOMAIN,
|
||||||
|
)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .notify import MobileAppNotificationService
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def webhook_id_from_device_id(hass, device_id: str) -> Optional[str]:
|
||||||
|
"""Get webhook ID from device ID."""
|
||||||
|
for cur_webhook_id, cur_device in hass.data[DOMAIN][DATA_DEVICES].items():
|
||||||
|
if cur_device.id == device_id:
|
||||||
|
return cur_webhook_id
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def supports_push(hass, webhook_id: str) -> bool:
|
||||||
|
"""Return if push notifications is supported."""
|
||||||
|
config_entry = hass.data[DOMAIN][DATA_CONFIG_ENTRIES][webhook_id]
|
||||||
|
app_data = config_entry.data[ATTR_APP_DATA]
|
||||||
|
return ATTR_PUSH_TOKEN in app_data and ATTR_PUSH_URL in app_data
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def get_notify_service(hass, webhook_id: str) -> Optional[str]:
|
||||||
|
"""Return the notify service for this webhook ID."""
|
||||||
|
notify_service: "MobileAppNotificationService" = hass.data[DOMAIN][DATA_NOTIFY]
|
||||||
|
|
||||||
|
for target_service, target_webhook_id in notify_service.registered_targets.items():
|
||||||
|
if target_webhook_id == webhook_id:
|
||||||
|
return target_service
|
||||||
|
|
||||||
|
return None
|
@@ -110,6 +110,8 @@ class BaseNotificationService:
|
|||||||
"""An abstract class for notification services."""
|
"""An abstract class for notification services."""
|
||||||
|
|
||||||
hass: Optional[HomeAssistantType] = None
|
hass: Optional[HomeAssistantType] = None
|
||||||
|
# Name => target
|
||||||
|
registered_targets: Dict[str, str]
|
||||||
|
|
||||||
def send_message(self, message, **kwargs):
|
def send_message(self, message, **kwargs):
|
||||||
"""Send a message.
|
"""Send a message.
|
||||||
@@ -135,8 +137,8 @@ class BaseNotificationService:
|
|||||||
title.hass = self.hass
|
title.hass = self.hass
|
||||||
kwargs[ATTR_TITLE] = title.async_render(parse_result=False)
|
kwargs[ATTR_TITLE] = title.async_render(parse_result=False)
|
||||||
|
|
||||||
if self._registered_targets.get(service.service) is not None:
|
if self.registered_targets.get(service.service) is not None:
|
||||||
kwargs[ATTR_TARGET] = [self._registered_targets[service.service]]
|
kwargs[ATTR_TARGET] = [self.registered_targets[service.service]]
|
||||||
elif service.data.get(ATTR_TARGET) is not None:
|
elif service.data.get(ATTR_TARGET) is not None:
|
||||||
kwargs[ATTR_TARGET] = service.data.get(ATTR_TARGET)
|
kwargs[ATTR_TARGET] = service.data.get(ATTR_TARGET)
|
||||||
|
|
||||||
@@ -157,23 +159,23 @@ class BaseNotificationService:
|
|||||||
self.hass = hass
|
self.hass = hass
|
||||||
self._service_name = service_name
|
self._service_name = service_name
|
||||||
self._target_service_name_prefix = target_service_name_prefix
|
self._target_service_name_prefix = target_service_name_prefix
|
||||||
self._registered_targets: Dict = {}
|
self.registered_targets = {}
|
||||||
|
|
||||||
async def async_register_services(self) -> None:
|
async def async_register_services(self) -> None:
|
||||||
"""Create or update the notify services."""
|
"""Create or update the notify services."""
|
||||||
assert self.hass
|
assert self.hass
|
||||||
|
|
||||||
if hasattr(self, "targets"):
|
if hasattr(self, "targets"):
|
||||||
stale_targets = set(self._registered_targets)
|
stale_targets = set(self.registered_targets)
|
||||||
|
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
for name, target in self.targets.items(): # type: ignore
|
for name, target in self.targets.items(): # type: ignore
|
||||||
target_name = slugify(f"{self._target_service_name_prefix}_{name}")
|
target_name = slugify(f"{self._target_service_name_prefix}_{name}")
|
||||||
if target_name in stale_targets:
|
if target_name in stale_targets:
|
||||||
stale_targets.remove(target_name)
|
stale_targets.remove(target_name)
|
||||||
if target_name in self._registered_targets:
|
if target_name in self.registered_targets:
|
||||||
continue
|
continue
|
||||||
self._registered_targets[target_name] = target
|
self.registered_targets[target_name] = target
|
||||||
self.hass.services.async_register(
|
self.hass.services.async_register(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
target_name,
|
target_name,
|
||||||
@@ -182,7 +184,7 @@ class BaseNotificationService:
|
|||||||
)
|
)
|
||||||
|
|
||||||
for stale_target_name in stale_targets:
|
for stale_target_name in stale_targets:
|
||||||
del self._registered_targets[stale_target_name]
|
del self.registered_targets[stale_target_name]
|
||||||
self.hass.services.async_remove(
|
self.hass.services.async_remove(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
stale_target_name,
|
stale_target_name,
|
||||||
@@ -202,10 +204,10 @@ class BaseNotificationService:
|
|||||||
"""Unregister the notify services."""
|
"""Unregister the notify services."""
|
||||||
assert self.hass
|
assert self.hass
|
||||||
|
|
||||||
if self._registered_targets:
|
if self.registered_targets:
|
||||||
remove_targets = set(self._registered_targets)
|
remove_targets = set(self.registered_targets)
|
||||||
for remove_target_name in remove_targets:
|
for remove_target_name in remove_targets:
|
||||||
del self._registered_targets[remove_target_name]
|
del self.registered_targets[remove_target_name]
|
||||||
self.hass.services.async_remove(
|
self.hass.services.async_remove(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
remove_target_name,
|
remove_target_name,
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
"""Provides device automations for NEW_NAME."""
|
"""Provides device actions for NEW_NAME."""
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
@@ -72,8 +72,6 @@ async def async_call_action_from_config(
|
|||||||
hass: HomeAssistant, config: dict, variables: dict, context: Optional[Context]
|
hass: HomeAssistant, config: dict, variables: dict, context: Optional[Context]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Execute a device action."""
|
"""Execute a device action."""
|
||||||
config = ACTION_SCHEMA(config)
|
|
||||||
|
|
||||||
service_data = {ATTR_ENTITY_ID: config[CONF_ENTITY_ID]}
|
service_data = {ATTR_ENTITY_ID: config[CONF_ENTITY_ID]}
|
||||||
|
|
||||||
if config[CONF_TYPE] == "turn_on":
|
if config[CONF_TYPE] == "turn_on":
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
"""The tests for NEW_NAME device actions."""
|
"""The tests for NEW_NAME device actions."""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components import automation
|
||||||
from homeassistant.components.NEW_DOMAIN import DOMAIN
|
from homeassistant.components.NEW_DOMAIN import DOMAIN
|
||||||
import homeassistant.components.automation as automation
|
|
||||||
from homeassistant.helpers import device_registry
|
from homeassistant.helpers import device_registry
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
"""Provide the device automations for NEW_NAME."""
|
"""Provide the device conditions for NEW_NAME."""
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
"""The tests for NEW_NAME device conditions."""
|
"""The tests for NEW_NAME device conditions."""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components import automation
|
||||||
from homeassistant.components.NEW_DOMAIN import DOMAIN
|
from homeassistant.components.NEW_DOMAIN import DOMAIN
|
||||||
import homeassistant.components.automation as automation
|
|
||||||
from homeassistant.const import STATE_OFF, STATE_ON
|
from homeassistant.const import STATE_OFF, STATE_ON
|
||||||
from homeassistant.helpers import device_registry
|
from homeassistant.helpers import device_registry
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
"""Provides device automations for NEW_NAME."""
|
"""Provides device triggers for NEW_NAME."""
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
@@ -80,11 +80,8 @@ async def async_attach_trigger(
|
|||||||
automation_info: dict,
|
automation_info: dict,
|
||||||
) -> CALLBACK_TYPE:
|
) -> CALLBACK_TYPE:
|
||||||
"""Attach a trigger."""
|
"""Attach a trigger."""
|
||||||
config = TRIGGER_SCHEMA(config)
|
|
||||||
|
|
||||||
# TODO Implement your own logic to attach triggers.
|
# TODO Implement your own logic to attach triggers.
|
||||||
# Generally we suggest to re-use the existing state or event
|
# Use the existing state or event triggers from the automation integration.
|
||||||
# triggers from the automation integration.
|
|
||||||
|
|
||||||
if config[CONF_TYPE] == "turned_on":
|
if config[CONF_TYPE] == "turned_on":
|
||||||
from_state = STATE_OFF
|
from_state = STATE_OFF
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
"""The tests for NEW_NAME device triggers."""
|
"""The tests for NEW_NAME device triggers."""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from homeassistant.components import automation
|
||||||
from homeassistant.components.NEW_DOMAIN import DOMAIN
|
from homeassistant.components.NEW_DOMAIN import DOMAIN
|
||||||
import homeassistant.components.automation as automation
|
|
||||||
from homeassistant.const import STATE_OFF, STATE_ON
|
from homeassistant.const import STATE_OFF, STATE_ON
|
||||||
from homeassistant.helpers import device_registry
|
from homeassistant.helpers import device_registry
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
@@ -40,6 +40,26 @@ async def create_registrations(hass, authed_api_client):
|
|||||||
return (enc_reg_json, clear_reg_json)
|
return (enc_reg_json, clear_reg_json)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
async def push_registration(hass, authed_api_client):
|
||||||
|
"""Return registration with push notifications enabled."""
|
||||||
|
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
||||||
|
|
||||||
|
enc_reg = await authed_api_client.post(
|
||||||
|
"/api/mobile_app/registrations",
|
||||||
|
json={
|
||||||
|
**REGISTER,
|
||||||
|
"app_data": {
|
||||||
|
"push_url": "http://localhost/mock-push",
|
||||||
|
"push_token": "abcd",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert enc_reg.status == 201
|
||||||
|
return await enc_reg.json()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
async def webhook_client(hass, authed_api_client, aiohttp_client):
|
async def webhook_client(hass, authed_api_client, aiohttp_client):
|
||||||
"""mobile_app mock client."""
|
"""mobile_app mock client."""
|
||||||
|
68
tests/components/mobile_app/test_device_action.py
Normal file
68
tests/components/mobile_app/test_device_action.py
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
"""The tests for Mobile App device actions."""
|
||||||
|
from homeassistant.components import automation, device_automation
|
||||||
|
from homeassistant.components.mobile_app import DATA_DEVICES, DOMAIN, util
|
||||||
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
|
from tests.common import async_get_device_automations, patch
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_actions(hass, push_registration):
|
||||||
|
"""Test we get the expected actions from a mobile_app."""
|
||||||
|
webhook_id = push_registration["webhook_id"]
|
||||||
|
device_id = hass.data[DOMAIN][DATA_DEVICES][webhook_id].id
|
||||||
|
|
||||||
|
assert await async_get_device_automations(hass, "action", device_id) == [
|
||||||
|
{"domain": DOMAIN, "device_id": device_id, "type": "notify"}
|
||||||
|
]
|
||||||
|
|
||||||
|
capabilitites = await device_automation._async_get_device_automation_capabilities(
|
||||||
|
hass, "action", {"domain": DOMAIN, "device_id": device_id, "type": "notify"}
|
||||||
|
)
|
||||||
|
assert "extra_fields" in capabilitites
|
||||||
|
|
||||||
|
|
||||||
|
async def test_action(hass, push_registration):
|
||||||
|
"""Test for turn_on and turn_off actions."""
|
||||||
|
webhook_id = push_registration["webhook_id"]
|
||||||
|
|
||||||
|
assert await async_setup_component(
|
||||||
|
hass,
|
||||||
|
automation.DOMAIN,
|
||||||
|
{
|
||||||
|
automation.DOMAIN: [
|
||||||
|
{
|
||||||
|
"trigger": {
|
||||||
|
"platform": "event",
|
||||||
|
"event_type": "test_notify",
|
||||||
|
},
|
||||||
|
"action": [
|
||||||
|
{"variables": {"name": "Paulus"}},
|
||||||
|
{
|
||||||
|
"domain": DOMAIN,
|
||||||
|
"device_id": hass.data[DOMAIN]["devices"][webhook_id].id,
|
||||||
|
"type": "notify",
|
||||||
|
"message": "Hello {{ name }}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
service_name = util.get_notify_service(hass, webhook_id)
|
||||||
|
|
||||||
|
# Make sure it was actually registered
|
||||||
|
assert hass.services.has_service("notify", service_name)
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"homeassistant.components.mobile_app.notify.MobileAppNotificationService.async_send_message"
|
||||||
|
) as mock_send_message:
|
||||||
|
hass.bus.async_fire("test_notify")
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
assert len(mock_send_message.mock_calls) == 1
|
||||||
|
|
||||||
|
assert mock_send_message.mock_calls[0][2] == {
|
||||||
|
"target": [webhook_id],
|
||||||
|
"message": "Hello Paulus",
|
||||||
|
"data": None,
|
||||||
|
}
|
Reference in New Issue
Block a user