mirror of
https://github.com/home-assistant/core.git
synced 2025-08-01 09:38:21 +00:00
Add action for set_program_oven to miele (#149620)
This commit is contained in:
parent
5930ac6425
commit
1eb6d5fe32
@ -110,6 +110,9 @@
|
|||||||
},
|
},
|
||||||
"set_program": {
|
"set_program": {
|
||||||
"service": "mdi:arrow-right-circle-outline"
|
"service": "mdi:arrow-right-circle-outline"
|
||||||
|
},
|
||||||
|
"set_program_oven": {
|
||||||
|
"service": "mdi:arrow-right-circle-outline"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
"""Services for Miele integration."""
|
"""Services for Miele integration."""
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
from typing import cast
|
from typing import cast
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.const import ATTR_DEVICE_ID
|
from homeassistant.const import ATTR_DEVICE_ID, ATTR_TEMPERATURE
|
||||||
from homeassistant.core import (
|
from homeassistant.core import (
|
||||||
HomeAssistant,
|
HomeAssistant,
|
||||||
ServiceCall,
|
ServiceCall,
|
||||||
@ -32,6 +33,19 @@ SERVICE_SET_PROGRAM_SCHEMA = vol.Schema(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
SERVICE_SET_PROGRAM_OVEN = "set_program_oven"
|
||||||
|
SERVICE_SET_PROGRAM_OVEN_SCHEMA = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(ATTR_DEVICE_ID): str,
|
||||||
|
vol.Required(ATTR_PROGRAM_ID): cv.positive_int,
|
||||||
|
vol.Optional(ATTR_TEMPERATURE): cv.positive_int,
|
||||||
|
vol.Optional(ATTR_DURATION): vol.All(
|
||||||
|
cv.time_period,
|
||||||
|
vol.Range(min=timedelta(minutes=1), max=timedelta(hours=12)),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
SERVICE_GET_PROGRAMS = "get_programs"
|
SERVICE_GET_PROGRAMS = "get_programs"
|
||||||
SERVICE_GET_PROGRAMS_SCHEMA = vol.Schema(
|
SERVICE_GET_PROGRAMS_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
@ -103,6 +117,36 @@ async def set_program(call: ServiceCall) -> None:
|
|||||||
) from ex
|
) from ex
|
||||||
|
|
||||||
|
|
||||||
|
async def set_program_oven(call: ServiceCall) -> None:
|
||||||
|
"""Set a program on a Miele oven."""
|
||||||
|
|
||||||
|
_LOGGER.debug("Set program call: %s", call)
|
||||||
|
config_entry = await _extract_config_entry(call)
|
||||||
|
api = config_entry.runtime_data.api
|
||||||
|
|
||||||
|
serial_number = await _get_serial_number(call)
|
||||||
|
data = {"programId": call.data[ATTR_PROGRAM_ID]}
|
||||||
|
if call.data.get(ATTR_DURATION) is not None:
|
||||||
|
td = call.data[ATTR_DURATION]
|
||||||
|
data["duration"] = [
|
||||||
|
td.seconds // 3600, # hours
|
||||||
|
(td.seconds // 60) % 60, # minutes
|
||||||
|
]
|
||||||
|
if call.data.get(ATTR_TEMPERATURE) is not None:
|
||||||
|
data["temperature"] = call.data[ATTR_TEMPERATURE]
|
||||||
|
try:
|
||||||
|
await api.set_program(serial_number, data)
|
||||||
|
except aiohttp.ClientResponseError as ex:
|
||||||
|
raise HomeAssistantError(
|
||||||
|
translation_domain=DOMAIN,
|
||||||
|
translation_key="set_program_oven_error",
|
||||||
|
translation_placeholders={
|
||||||
|
"status": str(ex.status),
|
||||||
|
"message": ex.message,
|
||||||
|
},
|
||||||
|
) from ex
|
||||||
|
|
||||||
|
|
||||||
async def get_programs(call: ServiceCall) -> ServiceResponse:
|
async def get_programs(call: ServiceCall) -> ServiceResponse:
|
||||||
"""Get available programs from appliance."""
|
"""Get available programs from appliance."""
|
||||||
|
|
||||||
@ -172,7 +216,17 @@ async def async_setup_services(hass: HomeAssistant) -> None:
|
|||||||
"""Set up services."""
|
"""Set up services."""
|
||||||
|
|
||||||
hass.services.async_register(
|
hass.services.async_register(
|
||||||
DOMAIN, SERVICE_SET_PROGRAM, set_program, SERVICE_SET_PROGRAM_SCHEMA
|
DOMAIN,
|
||||||
|
SERVICE_SET_PROGRAM,
|
||||||
|
set_program,
|
||||||
|
SERVICE_SET_PROGRAM_SCHEMA,
|
||||||
|
)
|
||||||
|
|
||||||
|
hass.services.async_register(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_SET_PROGRAM_OVEN,
|
||||||
|
set_program_oven,
|
||||||
|
SERVICE_SET_PROGRAM_OVEN_SCHEMA,
|
||||||
)
|
)
|
||||||
|
|
||||||
hass.services.async_register(
|
hass.services.async_register(
|
||||||
|
@ -23,3 +23,33 @@ set_program:
|
|||||||
max: 99999
|
max: 99999
|
||||||
mode: box
|
mode: box
|
||||||
example: 24
|
example: 24
|
||||||
|
|
||||||
|
set_program_oven:
|
||||||
|
fields:
|
||||||
|
device_id:
|
||||||
|
selector:
|
||||||
|
device:
|
||||||
|
integration: miele
|
||||||
|
required: true
|
||||||
|
program_id:
|
||||||
|
required: true
|
||||||
|
selector:
|
||||||
|
number:
|
||||||
|
min: 0
|
||||||
|
max: 99999
|
||||||
|
mode: box
|
||||||
|
example: 24
|
||||||
|
temperature:
|
||||||
|
required: false
|
||||||
|
selector:
|
||||||
|
number:
|
||||||
|
min: 30
|
||||||
|
max: 300
|
||||||
|
unit_of_measurement: "°C"
|
||||||
|
mode: box
|
||||||
|
example: 180
|
||||||
|
duration:
|
||||||
|
required: false
|
||||||
|
selector:
|
||||||
|
duration:
|
||||||
|
example: 1:15:00
|
||||||
|
@ -1068,6 +1068,9 @@
|
|||||||
"set_program_error": {
|
"set_program_error": {
|
||||||
"message": "'Set program' action failed {status} / {message}."
|
"message": "'Set program' action failed {status} / {message}."
|
||||||
},
|
},
|
||||||
|
"set_program_oven_error": {
|
||||||
|
"message": "'Set program on oven' action failed {status} / {message}."
|
||||||
|
},
|
||||||
"set_state_error": {
|
"set_state_error": {
|
||||||
"message": "Failed to set state for {entity}."
|
"message": "Failed to set state for {entity}."
|
||||||
}
|
}
|
||||||
@ -1096,6 +1099,28 @@
|
|||||||
"name": "Program ID"
|
"name": "Program ID"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"set_program_oven": {
|
||||||
|
"name": "Set program on oven",
|
||||||
|
"description": "[%key:component::miele::services::set_program::description%]",
|
||||||
|
"fields": {
|
||||||
|
"device_id": {
|
||||||
|
"description": "[%key:component::miele::services::set_program::fields::device_id::description%]",
|
||||||
|
"name": "[%key:component::miele::services::set_program::fields::device_id::name%]"
|
||||||
|
},
|
||||||
|
"program_id": {
|
||||||
|
"description": "[%key:component::miele::services::set_program::fields::program_id::description%]",
|
||||||
|
"name": "[%key:component::miele::services::set_program::fields::program_id::name%]"
|
||||||
|
},
|
||||||
|
"temperature": {
|
||||||
|
"description": "The target temperature for the oven program.",
|
||||||
|
"name": "[%key:component::sensor::entity_component::temperature::name%]"
|
||||||
|
},
|
||||||
|
"duration": {
|
||||||
|
"description": "The duration for the oven program.",
|
||||||
|
"name": "[%key:component::sensor::entity_component::duration::name%]"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
"""Tests the services provided by the miele integration."""
|
"""Tests the services provided by the miele integration."""
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
from aiohttp import ClientResponseError
|
from aiohttp import ClientResponseError
|
||||||
@ -9,11 +10,13 @@ from voluptuous import MultipleInvalid
|
|||||||
|
|
||||||
from homeassistant.components.miele.const import DOMAIN
|
from homeassistant.components.miele.const import DOMAIN
|
||||||
from homeassistant.components.miele.services import (
|
from homeassistant.components.miele.services import (
|
||||||
|
ATTR_DURATION,
|
||||||
ATTR_PROGRAM_ID,
|
ATTR_PROGRAM_ID,
|
||||||
SERVICE_GET_PROGRAMS,
|
SERVICE_GET_PROGRAMS,
|
||||||
SERVICE_SET_PROGRAM,
|
SERVICE_SET_PROGRAM,
|
||||||
|
SERVICE_SET_PROGRAM_OVEN,
|
||||||
)
|
)
|
||||||
from homeassistant.const import ATTR_DEVICE_ID
|
from homeassistant.const import ATTR_DEVICE_ID, ATTR_TEMPERATURE
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
||||||
from homeassistant.helpers.device_registry import DeviceRegistry
|
from homeassistant.helpers.device_registry import DeviceRegistry
|
||||||
@ -49,6 +52,50 @@ async def test_services(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("call_arguments", "miele_arguments"),
|
||||||
|
[
|
||||||
|
(
|
||||||
|
{ATTR_PROGRAM_ID: 24},
|
||||||
|
{"programId": 24},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{ATTR_PROGRAM_ID: 25, ATTR_DURATION: timedelta(minutes=75)},
|
||||||
|
{"programId": 25, "duration": [1, 15]},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
ATTR_PROGRAM_ID: 26,
|
||||||
|
ATTR_DURATION: timedelta(minutes=135),
|
||||||
|
ATTR_TEMPERATURE: 180,
|
||||||
|
},
|
||||||
|
{"programId": 26, "duration": [2, 15], "temperature": 180},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
async def test_services_oven(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
device_registry: DeviceRegistry,
|
||||||
|
mock_miele_client: MagicMock,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
call_arguments: dict,
|
||||||
|
miele_arguments: dict,
|
||||||
|
) -> None:
|
||||||
|
"""Tests that the custom services are correct for ovens."""
|
||||||
|
|
||||||
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
device = device_registry.async_get_device(identifiers={(DOMAIN, TEST_APPLIANCE)})
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_SET_PROGRAM_OVEN,
|
||||||
|
{ATTR_DEVICE_ID: device.id, **call_arguments},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
mock_miele_client.set_program.assert_called_once_with(
|
||||||
|
TEST_APPLIANCE, miele_arguments
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_services_with_response(
|
async def test_services_with_response(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
device_registry: DeviceRegistry,
|
device_registry: DeviceRegistry,
|
||||||
@ -71,11 +118,20 @@ async def test_services_with_response(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("service", "error"),
|
||||||
|
[
|
||||||
|
(SERVICE_SET_PROGRAM, "'Set program' action failed"),
|
||||||
|
(SERVICE_SET_PROGRAM_OVEN, "'Set program on oven' action failed"),
|
||||||
|
],
|
||||||
|
)
|
||||||
async def test_service_api_errors(
|
async def test_service_api_errors(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
device_registry: DeviceRegistry,
|
device_registry: DeviceRegistry,
|
||||||
mock_miele_client: MagicMock,
|
mock_miele_client: MagicMock,
|
||||||
mock_config_entry: MockConfigEntry,
|
mock_config_entry: MockConfigEntry,
|
||||||
|
service: str,
|
||||||
|
error: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test service api errors."""
|
"""Test service api errors."""
|
||||||
await setup_integration(hass, mock_config_entry)
|
await setup_integration(hass, mock_config_entry)
|
||||||
@ -83,10 +139,10 @@ async def test_service_api_errors(
|
|||||||
|
|
||||||
# Test http error
|
# Test http error
|
||||||
mock_miele_client.set_program.side_effect = ClientResponseError("TestInfo", "test")
|
mock_miele_client.set_program.side_effect = ClientResponseError("TestInfo", "test")
|
||||||
with pytest.raises(HomeAssistantError, match="'Set program' action failed"):
|
with pytest.raises(HomeAssistantError, match=error):
|
||||||
await hass.services.async_call(
|
await hass.services.async_call(
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
SERVICE_SET_PROGRAM,
|
service,
|
||||||
{ATTR_DEVICE_ID: device.id, ATTR_PROGRAM_ID: 1},
|
{ATTR_DEVICE_ID: device.id, ATTR_PROGRAM_ID: 1},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user