mirror of
https://github.com/home-assistant/core
synced 2024-07-27 18:58:57 +02:00
Add chart service to LaMetric (#80554)
This commit is contained in:
parent
ddba653158
commit
e3919babb2
@ -19,10 +19,12 @@ LOGGER = logging.getLogger(__package__)
|
|||||||
SCAN_INTERVAL = timedelta(seconds=30)
|
SCAN_INTERVAL = timedelta(seconds=30)
|
||||||
|
|
||||||
CONF_CYCLES: Final = "cycles"
|
CONF_CYCLES: Final = "cycles"
|
||||||
|
CONF_DATA: Final = "data"
|
||||||
CONF_ICON_TYPE: Final = "icon_type"
|
CONF_ICON_TYPE: Final = "icon_type"
|
||||||
CONF_LIFETIME: Final = "lifetime"
|
CONF_LIFETIME: Final = "lifetime"
|
||||||
|
CONF_MESSAGE: Final = "message"
|
||||||
CONF_PRIORITY: Final = "priority"
|
CONF_PRIORITY: Final = "priority"
|
||||||
CONF_SOUND: Final = "sound"
|
CONF_SOUND: Final = "sound"
|
||||||
CONF_MESSAGE: Final = "message"
|
|
||||||
|
|
||||||
SERVICE_MESSAGE: Final = "message"
|
SERVICE_MESSAGE: Final = "message"
|
||||||
|
SERVICE_CHART: Final = "chart"
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
"""Support for LaMetric time services."""
|
"""Support for LaMetric time services."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from demetriek import (
|
from demetriek import (
|
||||||
AlarmSound,
|
AlarmSound,
|
||||||
|
Chart,
|
||||||
LaMetricError,
|
LaMetricError,
|
||||||
Model,
|
Model,
|
||||||
Notification,
|
Notification,
|
||||||
@ -19,20 +24,21 @@ from homeassistant.helpers import config_validation as cv
|
|||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
CONF_CYCLES,
|
CONF_CYCLES,
|
||||||
|
CONF_DATA,
|
||||||
CONF_ICON_TYPE,
|
CONF_ICON_TYPE,
|
||||||
CONF_MESSAGE,
|
CONF_MESSAGE,
|
||||||
CONF_PRIORITY,
|
CONF_PRIORITY,
|
||||||
CONF_SOUND,
|
CONF_SOUND,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
SERVICE_CHART,
|
||||||
SERVICE_MESSAGE,
|
SERVICE_MESSAGE,
|
||||||
)
|
)
|
||||||
from .coordinator import LaMetricDataUpdateCoordinator
|
from .coordinator import LaMetricDataUpdateCoordinator
|
||||||
from .helpers import async_get_coordinator_by_device_id
|
from .helpers import async_get_coordinator_by_device_id
|
||||||
|
|
||||||
SERVICE_MESSAGE_SCHEMA = vol.Schema(
|
SERVICE_BASE_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_DEVICE_ID): cv.string,
|
vol.Required(CONF_DEVICE_ID): cv.string,
|
||||||
vol.Required(CONF_MESSAGE): cv.string,
|
|
||||||
vol.Optional(CONF_CYCLES, default=1): cv.positive_int,
|
vol.Optional(CONF_CYCLES, default=1): cv.positive_int,
|
||||||
vol.Optional(CONF_ICON_TYPE, default=NotificationIconType.NONE): vol.Coerce(
|
vol.Optional(CONF_ICON_TYPE, default=NotificationIconType.NONE): vol.Coerce(
|
||||||
NotificationIconType
|
NotificationIconType
|
||||||
@ -43,34 +49,73 @@ SERVICE_MESSAGE_SCHEMA = vol.Schema(
|
|||||||
vol.Optional(CONF_SOUND): vol.Any(
|
vol.Optional(CONF_SOUND): vol.Any(
|
||||||
vol.Coerce(AlarmSound), vol.Coerce(NotificationSound)
|
vol.Coerce(AlarmSound), vol.Coerce(NotificationSound)
|
||||||
),
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
SERVICE_MESSAGE_SCHEMA = SERVICE_BASE_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
vol.Required(CONF_MESSAGE): cv.string,
|
||||||
vol.Optional(CONF_ICON): cv.string,
|
vol.Optional(CONF_ICON): cv.string,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
SERVICE_CHART_SCHEMA = SERVICE_BASE_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
vol.Required(CONF_DATA): vol.All(cv.ensure_list, [vol.Coerce(int)]),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_setup_services(hass: HomeAssistant) -> None:
|
def async_setup_services(hass: HomeAssistant) -> None:
|
||||||
"""Set up services for the LaMetric integration."""
|
"""Set up services for the LaMetric integration."""
|
||||||
|
|
||||||
async def _async_service_text(call: ServiceCall) -> None:
|
async def _async_service_chart(call: ServiceCall) -> None:
|
||||||
|
"""Send a chart to a LaMetric device."""
|
||||||
|
coordinator = async_get_coordinator_by_device_id(
|
||||||
|
hass, call.data[CONF_DEVICE_ID]
|
||||||
|
)
|
||||||
|
await async_send_notification(
|
||||||
|
coordinator, call, [Chart(data=call.data[CONF_DATA])]
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _async_service_message(call: ServiceCall) -> None:
|
||||||
"""Send a message to a LaMetric device."""
|
"""Send a message to a LaMetric device."""
|
||||||
coordinator = async_get_coordinator_by_device_id(
|
coordinator = async_get_coordinator_by_device_id(
|
||||||
hass, call.data[CONF_DEVICE_ID]
|
hass, call.data[CONF_DEVICE_ID]
|
||||||
)
|
)
|
||||||
await async_service_text(coordinator, call)
|
await async_send_notification(
|
||||||
|
coordinator,
|
||||||
|
call,
|
||||||
|
[
|
||||||
|
Simple(
|
||||||
|
icon=call.data.get(CONF_ICON),
|
||||||
|
text=call.data[CONF_MESSAGE],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.services.async_register(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_CHART,
|
||||||
|
_async_service_chart,
|
||||||
|
schema=SERVICE_CHART_SCHEMA,
|
||||||
|
)
|
||||||
|
|
||||||
hass.services.async_register(
|
hass.services.async_register(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SERVICE_MESSAGE,
|
SERVICE_MESSAGE,
|
||||||
_async_service_text,
|
_async_service_message,
|
||||||
schema=SERVICE_MESSAGE_SCHEMA,
|
schema=SERVICE_MESSAGE_SCHEMA,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def async_service_text(
|
async def async_send_notification(
|
||||||
coordinator: LaMetricDataUpdateCoordinator, call: ServiceCall
|
coordinator: LaMetricDataUpdateCoordinator,
|
||||||
|
call: ServiceCall,
|
||||||
|
frames: Sequence[Chart | Simple],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Send a message to an LaMetric device."""
|
"""Send a notification to an LaMetric device."""
|
||||||
sound = None
|
sound = None
|
||||||
if CONF_SOUND in call.data:
|
if CONF_SOUND in call.data:
|
||||||
sound = Sound(id=call.data[CONF_SOUND], category=None)
|
sound = Sound(id=call.data[CONF_SOUND], category=None)
|
||||||
@ -79,12 +124,7 @@ async def async_service_text(
|
|||||||
icon_type=NotificationIconType(call.data[CONF_ICON_TYPE]),
|
icon_type=NotificationIconType(call.data[CONF_ICON_TYPE]),
|
||||||
priority=NotificationPriority(call.data.get(CONF_PRIORITY)),
|
priority=NotificationPriority(call.data.get(CONF_PRIORITY)),
|
||||||
model=Model(
|
model=Model(
|
||||||
frames=[
|
frames=frames,
|
||||||
Simple(
|
|
||||||
icon=call.data.get(CONF_ICON),
|
|
||||||
text=call.data[CONF_MESSAGE],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
cycles=call.data[CONF_CYCLES],
|
cycles=call.data[CONF_CYCLES],
|
||||||
sound=sound,
|
sound=sound,
|
||||||
),
|
),
|
||||||
|
@ -1,29 +1,22 @@
|
|||||||
message:
|
chart:
|
||||||
name: Display a message
|
name: Display a chart
|
||||||
description: Display a message with an optional icon on a LaMetric device.
|
description: Display a chart on a LaMetric device.
|
||||||
fields:
|
fields:
|
||||||
device_id:
|
device_id: &device_id
|
||||||
name: Device
|
name: Device
|
||||||
description: The LaMetric device to display the message on.
|
description: The LaMetric device to display the chart on.
|
||||||
required: true
|
required: true
|
||||||
selector:
|
selector:
|
||||||
device:
|
device:
|
||||||
integration: lametric
|
integration: lametric
|
||||||
message:
|
data:
|
||||||
name: Message
|
name: Data
|
||||||
description: The message to display.
|
description: The list of data points in the chart
|
||||||
required: true
|
required: true
|
||||||
|
example: "[1,2,3,4,5,4,3,2,1]"
|
||||||
selector:
|
selector:
|
||||||
text:
|
object:
|
||||||
icon:
|
sound: &sound
|
||||||
name: Icon
|
|
||||||
description: >-
|
|
||||||
The ID number of the icon or animation to display. List of all icons
|
|
||||||
and their IDs can be found at: https://developer.lametric.com/icons
|
|
||||||
required: false
|
|
||||||
selector:
|
|
||||||
text:
|
|
||||||
sound:
|
|
||||||
name: Sound
|
name: Sound
|
||||||
description: The notification sound to play.
|
description: The notification sound to play.
|
||||||
required: false
|
required: false
|
||||||
@ -126,7 +119,7 @@ message:
|
|||||||
value: "wind"
|
value: "wind"
|
||||||
- label: "Wind short"
|
- label: "Wind short"
|
||||||
value: "wind_short"
|
value: "wind_short"
|
||||||
cycles:
|
cycles: &cycles
|
||||||
name: Cycles
|
name: Cycles
|
||||||
description: >-
|
description: >-
|
||||||
The number of times to display the message. When set to 0, the message
|
The number of times to display the message. When set to 0, the message
|
||||||
@ -138,7 +131,7 @@ message:
|
|||||||
min: 0
|
min: 0
|
||||||
max: 10
|
max: 10
|
||||||
mode: slider
|
mode: slider
|
||||||
icon_type:
|
icon_type: &icon_type
|
||||||
name: Icon type
|
name: Icon type
|
||||||
description: >-
|
description: >-
|
||||||
The type of icon to display, indicating the nature of the notification.
|
The type of icon to display, indicating the nature of the notification.
|
||||||
@ -154,7 +147,7 @@ message:
|
|||||||
value: "info"
|
value: "info"
|
||||||
- label: "Alert"
|
- label: "Alert"
|
||||||
value: "alert"
|
value: "alert"
|
||||||
priority:
|
priority: &priority
|
||||||
name: Priority
|
name: Priority
|
||||||
description: >-
|
description: >-
|
||||||
The priority of the notification. When the device is running in
|
The priority of the notification. When the device is running in
|
||||||
@ -172,3 +165,27 @@ message:
|
|||||||
value: "warning"
|
value: "warning"
|
||||||
- label: "Critical"
|
- label: "Critical"
|
||||||
value: "critical"
|
value: "critical"
|
||||||
|
|
||||||
|
message:
|
||||||
|
name: Display a message
|
||||||
|
description: Display a message with an optional icon on a LaMetric device.
|
||||||
|
fields:
|
||||||
|
device_id: *device_id
|
||||||
|
message:
|
||||||
|
name: Message
|
||||||
|
description: The message to display.
|
||||||
|
required: true
|
||||||
|
selector:
|
||||||
|
text:
|
||||||
|
icon:
|
||||||
|
name: Icon
|
||||||
|
description: >-
|
||||||
|
The ID number of the icon or animation to display. List of all icons
|
||||||
|
and their IDs can be found at: https://developer.lametric.com/icons
|
||||||
|
required: false
|
||||||
|
selector:
|
||||||
|
text:
|
||||||
|
sound: *sound
|
||||||
|
cycles: *cycles
|
||||||
|
icon_type: *icon_type
|
||||||
|
priority: *priority
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
from demetriek import (
|
from demetriek import (
|
||||||
|
Chart,
|
||||||
LaMetricError,
|
LaMetricError,
|
||||||
Notification,
|
Notification,
|
||||||
NotificationIconType,
|
NotificationIconType,
|
||||||
@ -14,11 +15,13 @@ import pytest
|
|||||||
|
|
||||||
from homeassistant.components.lametric.const import (
|
from homeassistant.components.lametric.const import (
|
||||||
CONF_CYCLES,
|
CONF_CYCLES,
|
||||||
|
CONF_DATA,
|
||||||
CONF_ICON_TYPE,
|
CONF_ICON_TYPE,
|
||||||
CONF_MESSAGE,
|
CONF_MESSAGE,
|
||||||
CONF_PRIORITY,
|
CONF_PRIORITY,
|
||||||
CONF_SOUND,
|
CONF_SOUND,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
SERVICE_CHART,
|
||||||
SERVICE_MESSAGE,
|
SERVICE_MESSAGE,
|
||||||
)
|
)
|
||||||
from homeassistant.const import CONF_DEVICE_ID, CONF_ICON
|
from homeassistant.const import CONF_DEVICE_ID, CONF_ICON
|
||||||
@ -29,12 +32,100 @@ from homeassistant.helpers import entity_registry as er
|
|||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
|
async def test_service_chart(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
init_integration: MockConfigEntry,
|
||||||
|
mock_lametric: MagicMock,
|
||||||
|
) -> None:
|
||||||
|
"""Test the LaMetric chart service."""
|
||||||
|
entity_registry = er.async_get(hass)
|
||||||
|
|
||||||
|
entry = entity_registry.async_get("button.frenck_s_lametric_next_app")
|
||||||
|
assert entry
|
||||||
|
assert entry.device_id
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_CHART,
|
||||||
|
{
|
||||||
|
CONF_DEVICE_ID: entry.device_id,
|
||||||
|
CONF_DATA: [1, 2, 3, 4, 5, 4, 3, 2, 1],
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(mock_lametric.notify.mock_calls) == 1
|
||||||
|
|
||||||
|
notification: Notification = mock_lametric.notify.mock_calls[0][2]["notification"]
|
||||||
|
assert notification.icon_type is NotificationIconType.NONE
|
||||||
|
assert notification.life_time is None
|
||||||
|
assert notification.model.cycles == 1
|
||||||
|
assert notification.model.sound is None
|
||||||
|
assert notification.notification_id is None
|
||||||
|
assert notification.notification_type is None
|
||||||
|
assert notification.priority is NotificationPriority.INFO
|
||||||
|
|
||||||
|
assert len(notification.model.frames) == 1
|
||||||
|
frame = notification.model.frames[0]
|
||||||
|
assert type(frame) is Chart
|
||||||
|
assert frame.data == [1, 2, 3, 4, 5, 4, 3, 2, 1]
|
||||||
|
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_CHART,
|
||||||
|
{
|
||||||
|
CONF_DATA: [1, 2, 3, 4, 5, 4, 3, 2, 1],
|
||||||
|
CONF_DEVICE_ID: entry.device_id,
|
||||||
|
CONF_CYCLES: 3,
|
||||||
|
CONF_ICON_TYPE: "info",
|
||||||
|
CONF_PRIORITY: "critical",
|
||||||
|
CONF_SOUND: "cat",
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(mock_lametric.notify.mock_calls) == 2
|
||||||
|
|
||||||
|
notification: Notification = mock_lametric.notify.mock_calls[1][2]["notification"]
|
||||||
|
assert notification.icon_type is NotificationIconType.INFO
|
||||||
|
assert notification.life_time is None
|
||||||
|
assert notification.model.cycles == 3
|
||||||
|
assert notification.model.sound is not None
|
||||||
|
assert notification.model.sound.category is NotificationSoundCategory.NOTIFICATIONS
|
||||||
|
assert notification.model.sound.sound is NotificationSound.CAT
|
||||||
|
assert notification.model.sound.repeat == 1
|
||||||
|
assert notification.notification_id is None
|
||||||
|
assert notification.notification_type is None
|
||||||
|
assert notification.priority is NotificationPriority.CRITICAL
|
||||||
|
|
||||||
|
assert len(notification.model.frames) == 1
|
||||||
|
frame = notification.model.frames[0]
|
||||||
|
assert type(frame) is Chart
|
||||||
|
assert frame.data == [1, 2, 3, 4, 5, 4, 3, 2, 1]
|
||||||
|
|
||||||
|
mock_lametric.notify.side_effect = LaMetricError
|
||||||
|
with pytest.raises(
|
||||||
|
HomeAssistantError, match="Could not send LaMetric notification"
|
||||||
|
):
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_CHART,
|
||||||
|
{
|
||||||
|
CONF_DEVICE_ID: entry.device_id,
|
||||||
|
CONF_DATA: [1, 2, 3, 4, 5],
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert len(mock_lametric.notify.mock_calls) == 3
|
||||||
|
|
||||||
|
|
||||||
async def test_service_message(
|
async def test_service_message(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
init_integration: MockConfigEntry,
|
init_integration: MockConfigEntry,
|
||||||
mock_lametric: MagicMock,
|
mock_lametric: MagicMock,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test the LaMetric text service."""
|
"""Test the LaMetric message service."""
|
||||||
entity_registry = er.async_get(hass)
|
entity_registry = er.async_get(hass)
|
||||||
|
|
||||||
entry = entity_registry.async_get("button.frenck_s_lametric_next_app")
|
entry = entity_registry.async_get("button.frenck_s_lametric_next_app")
|
||||||
|
Loading…
Reference in New Issue
Block a user