Add chart service to LaMetric (#80554)

This commit is contained in:
Franck Nijhof 2022-10-19 03:36:19 +02:00 committed by GitHub
parent ddba653158
commit e3919babb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 187 additions and 37 deletions

View File

@ -19,10 +19,12 @@ LOGGER = logging.getLogger(__package__)
SCAN_INTERVAL = timedelta(seconds=30)
CONF_CYCLES: Final = "cycles"
CONF_DATA: Final = "data"
CONF_ICON_TYPE: Final = "icon_type"
CONF_LIFETIME: Final = "lifetime"
CONF_MESSAGE: Final = "message"
CONF_PRIORITY: Final = "priority"
CONF_SOUND: Final = "sound"
CONF_MESSAGE: Final = "message"
SERVICE_MESSAGE: Final = "message"
SERVICE_CHART: Final = "chart"

View File

@ -1,6 +1,11 @@
"""Support for LaMetric time services."""
from __future__ import annotations
from collections.abc import Sequence
from demetriek import (
AlarmSound,
Chart,
LaMetricError,
Model,
Notification,
@ -19,20 +24,21 @@ from homeassistant.helpers import config_validation as cv
from .const import (
CONF_CYCLES,
CONF_DATA,
CONF_ICON_TYPE,
CONF_MESSAGE,
CONF_PRIORITY,
CONF_SOUND,
DOMAIN,
SERVICE_CHART,
SERVICE_MESSAGE,
)
from .coordinator import LaMetricDataUpdateCoordinator
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_MESSAGE): cv.string,
vol.Optional(CONF_CYCLES, default=1): cv.positive_int,
vol.Optional(CONF_ICON_TYPE, default=NotificationIconType.NONE): vol.Coerce(
NotificationIconType
@ -43,34 +49,73 @@ SERVICE_MESSAGE_SCHEMA = vol.Schema(
vol.Optional(CONF_SOUND): vol.Any(
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,
}
)
SERVICE_CHART_SCHEMA = SERVICE_BASE_SCHEMA.extend(
{
vol.Required(CONF_DATA): vol.All(cv.ensure_list, [vol.Coerce(int)]),
}
)
@callback
def async_setup_services(hass: HomeAssistant) -> None:
"""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."""
coordinator = async_get_coordinator_by_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(
DOMAIN,
SERVICE_MESSAGE,
_async_service_text,
_async_service_message,
schema=SERVICE_MESSAGE_SCHEMA,
)
async def async_service_text(
coordinator: LaMetricDataUpdateCoordinator, call: ServiceCall
async def async_send_notification(
coordinator: LaMetricDataUpdateCoordinator,
call: ServiceCall,
frames: Sequence[Chart | Simple],
) -> None:
"""Send a message to an LaMetric device."""
"""Send a notification to an LaMetric device."""
sound = None
if CONF_SOUND in call.data:
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]),
priority=NotificationPriority(call.data.get(CONF_PRIORITY)),
model=Model(
frames=[
Simple(
icon=call.data.get(CONF_ICON),
text=call.data[CONF_MESSAGE],
)
],
frames=frames,
cycles=call.data[CONF_CYCLES],
sound=sound,
),

View File

@ -1,29 +1,22 @@
message:
name: Display a message
description: Display a message with an optional icon on a LaMetric device.
chart:
name: Display a chart
description: Display a chart on a LaMetric device.
fields:
device_id:
device_id: &device_id
name: Device
description: The LaMetric device to display the message on.
description: The LaMetric device to display the chart on.
required: true
selector:
device:
integration: lametric
message:
name: Message
description: The message to display.
data:
name: Data
description: The list of data points in the chart
required: true
example: "[1,2,3,4,5,4,3,2,1]"
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:
object:
sound: &sound
name: Sound
description: The notification sound to play.
required: false
@ -126,7 +119,7 @@ message:
value: "wind"
- label: "Wind short"
value: "wind_short"
cycles:
cycles: &cycles
name: Cycles
description: >-
The number of times to display the message. When set to 0, the message
@ -138,7 +131,7 @@ message:
min: 0
max: 10
mode: slider
icon_type:
icon_type: &icon_type
name: Icon type
description: >-
The type of icon to display, indicating the nature of the notification.
@ -154,7 +147,7 @@ message:
value: "info"
- label: "Alert"
value: "alert"
priority:
priority: &priority
name: Priority
description: >-
The priority of the notification. When the device is running in
@ -172,3 +165,27 @@ message:
value: "warning"
- label: "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

View File

@ -2,6 +2,7 @@
from unittest.mock import MagicMock
from demetriek import (
Chart,
LaMetricError,
Notification,
NotificationIconType,
@ -14,11 +15,13 @@ import pytest
from homeassistant.components.lametric.const import (
CONF_CYCLES,
CONF_DATA,
CONF_ICON_TYPE,
CONF_MESSAGE,
CONF_PRIORITY,
CONF_SOUND,
DOMAIN,
SERVICE_CHART,
SERVICE_MESSAGE,
)
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
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(
hass: HomeAssistant,
init_integration: MockConfigEntry,
mock_lametric: MagicMock,
) -> None:
"""Test the LaMetric text service."""
"""Test the LaMetric message service."""
entity_registry = er.async_get(hass)
entry = entity_registry.async_get("button.frenck_s_lametric_next_app")