mirror of
https://github.com/home-assistant/core.git
synced 2025-07-24 21:57:51 +00:00
Add Tado add meter readings service (#111552)
This commit is contained in:
parent
4de56c1751
commit
951743551a
@ -10,10 +10,11 @@ from homeassistant.components.climate import PRESET_AWAY, PRESET_HOME
|
|||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
|
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, Platform
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady, HomeAssistantError
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.dispatcher import dispatcher_send
|
from homeassistant.helpers.dispatcher import dispatcher_send
|
||||||
from homeassistant.helpers.event import async_track_time_interval
|
from homeassistant.helpers.event import async_track_time_interval
|
||||||
|
from homeassistant.helpers.typing import ConfigType
|
||||||
from homeassistant.util import Throttle
|
from homeassistant.util import Throttle
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
@ -33,6 +34,7 @@ from .const import (
|
|||||||
UPDATE_MOBILE_DEVICE_TRACK,
|
UPDATE_MOBILE_DEVICE_TRACK,
|
||||||
UPDATE_TRACK,
|
UPDATE_TRACK,
|
||||||
)
|
)
|
||||||
|
from .services import setup_services
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -52,6 +54,14 @@ SCAN_MOBILE_DEVICE_INTERVAL = timedelta(seconds=30)
|
|||||||
CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)
|
CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||||
|
"""Set up Tado."""
|
||||||
|
|
||||||
|
setup_services(hass)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
"""Set up Tado from a config entry."""
|
"""Set up Tado from a config entry."""
|
||||||
|
|
||||||
@ -425,3 +435,10 @@ class TadoConnector:
|
|||||||
self.tado.set_temp_offset(device_id, offset)
|
self.tado.set_temp_offset(device_id, offset)
|
||||||
except RequestException as exc:
|
except RequestException as exc:
|
||||||
_LOGGER.error("Could not set temperature offset: %s", exc)
|
_LOGGER.error("Could not set temperature offset: %s", exc)
|
||||||
|
|
||||||
|
def set_meter_reading(self, reading: int) -> dict[str, str]:
|
||||||
|
"""Send meter reading to Tado."""
|
||||||
|
try:
|
||||||
|
return self.tado.set_eiq_meter_readings(reading=reading)
|
||||||
|
except RequestException as exc:
|
||||||
|
raise HomeAssistantError("Could not set meter reading") from exc
|
||||||
|
@ -204,3 +204,9 @@ TADO_TO_HA_OFFSET_MAP = {
|
|||||||
# Constants for Overlay Default settings
|
# Constants for Overlay Default settings
|
||||||
HA_TERMINATION_TYPE = "default_overlay_type"
|
HA_TERMINATION_TYPE = "default_overlay_type"
|
||||||
HA_TERMINATION_DURATION = "default_overlay_seconds"
|
HA_TERMINATION_DURATION = "default_overlay_seconds"
|
||||||
|
|
||||||
|
# Constants for service calls
|
||||||
|
SERVICE_ADD_METER_READING = "add_meter_reading"
|
||||||
|
CONF_CONFIG_ENTRY = "config_entry"
|
||||||
|
CONF_READING = "reading"
|
||||||
|
ATTR_MESSAGE = "message"
|
||||||
|
52
homeassistant/components/tado/services.py
Normal file
52
homeassistant/components/tado/services.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
"""Services for the Tado integration."""
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.core import HomeAssistant, ServiceCall, callback
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
from homeassistant.helpers import selector
|
||||||
|
|
||||||
|
from .const import (
|
||||||
|
ATTR_MESSAGE,
|
||||||
|
CONF_CONFIG_ENTRY,
|
||||||
|
CONF_READING,
|
||||||
|
DATA,
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_ADD_METER_READING,
|
||||||
|
)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
SCHEMA_ADD_METER_READING = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(CONF_CONFIG_ENTRY): selector.ConfigEntrySelector(
|
||||||
|
{
|
||||||
|
"integration": DOMAIN,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
vol.Required(CONF_READING): vol.Coerce(int),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def setup_services(hass: HomeAssistant) -> None:
|
||||||
|
"""Set up the services for the Tado integration."""
|
||||||
|
|
||||||
|
async def add_meter_reading(call: ServiceCall) -> None:
|
||||||
|
"""Send meter reading to Tado."""
|
||||||
|
entry_id: str = call.data[CONF_CONFIG_ENTRY]
|
||||||
|
reading: int = call.data[CONF_READING]
|
||||||
|
_LOGGER.debug("Add meter reading %s", reading)
|
||||||
|
|
||||||
|
tadoconnector = hass.data[DOMAIN][entry_id][DATA]
|
||||||
|
response: dict = await hass.async_add_executor_job(
|
||||||
|
tadoconnector.set_meter_reading, call.data[CONF_READING]
|
||||||
|
)
|
||||||
|
|
||||||
|
if ATTR_MESSAGE in response:
|
||||||
|
raise HomeAssistantError(response[ATTR_MESSAGE])
|
||||||
|
|
||||||
|
hass.services.async_register(
|
||||||
|
DOMAIN, SERVICE_ADD_METER_READING, add_meter_reading, SCHEMA_ADD_METER_READING
|
||||||
|
)
|
@ -61,3 +61,18 @@ set_climate_temperature_offset:
|
|||||||
max: 10
|
max: 10
|
||||||
step: 0.01
|
step: 0.01
|
||||||
unit_of_measurement: "°"
|
unit_of_measurement: "°"
|
||||||
|
|
||||||
|
add_meter_reading:
|
||||||
|
fields:
|
||||||
|
config_entry:
|
||||||
|
required: true
|
||||||
|
selector:
|
||||||
|
config_entry:
|
||||||
|
integration: tado
|
||||||
|
reading:
|
||||||
|
required: true
|
||||||
|
selector:
|
||||||
|
number:
|
||||||
|
mode: box
|
||||||
|
min: 0
|
||||||
|
step: 1
|
||||||
|
@ -122,6 +122,20 @@
|
|||||||
"description": "Offset you would like (depending on your device)."
|
"description": "Offset you would like (depending on your device)."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"add_meter_reading": {
|
||||||
|
"name": "Add meter readings",
|
||||||
|
"description": "Add meter readings to Tado Energy IQ.",
|
||||||
|
"fields": {
|
||||||
|
"config_entry": {
|
||||||
|
"name": "Config Entry",
|
||||||
|
"description": "Config entry to add meter readings to."
|
||||||
|
},
|
||||||
|
"reading": {
|
||||||
|
"name": "Reading",
|
||||||
|
"description": "Reading in m³ or kWh without decimals."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"issues": {
|
"issues": {
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"code": "duplicated_meter_reading",
|
||||||
|
"message": "reading already exists for date [2024-01-01]"
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
{ "code": "invalid_meter_reading", "message": "invalid new reading" }
|
6
tests/components/tado/fixtures/add_readings_success.json
Normal file
6
tests/components/tado/fixtures/add_readings_success.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"id": "12345a6b-7c8d-9e01-2fa3-4b5c67890def",
|
||||||
|
"homeId": 123456,
|
||||||
|
"date": "2024-01-01",
|
||||||
|
"reading": 1234
|
||||||
|
}
|
140
tests/components/tado/test_service.py
Normal file
140
tests/components/tado/test_service.py
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
"""The serive tests for the tado platform."""
|
||||||
|
import json
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from requests.exceptions import RequestException
|
||||||
|
|
||||||
|
from homeassistant.components.tado.const import (
|
||||||
|
CONF_CONFIG_ENTRY,
|
||||||
|
CONF_READING,
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_ADD_METER_READING,
|
||||||
|
)
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
|
||||||
|
from .util import async_init_integration
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry, load_fixture
|
||||||
|
|
||||||
|
|
||||||
|
async def test_has_services(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
) -> None:
|
||||||
|
"""Test the existence of the Tado Service."""
|
||||||
|
|
||||||
|
await async_init_integration(hass)
|
||||||
|
|
||||||
|
assert hass.services.has_service(DOMAIN, SERVICE_ADD_METER_READING)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_add_meter_readings(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
) -> None:
|
||||||
|
"""Test the add_meter_readings service."""
|
||||||
|
|
||||||
|
await async_init_integration(hass)
|
||||||
|
|
||||||
|
config_entry: MockConfigEntry = hass.config_entries.async_entries(DOMAIN)[0]
|
||||||
|
fixture: str = load_fixture("tado/add_readings_success.json")
|
||||||
|
with patch(
|
||||||
|
"PyTado.interface.Tado.set_eiq_meter_readings",
|
||||||
|
return_value=json.loads(fixture),
|
||||||
|
):
|
||||||
|
response: None = await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_ADD_METER_READING,
|
||||||
|
service_data={
|
||||||
|
CONF_CONFIG_ENTRY: config_entry.entry_id,
|
||||||
|
CONF_READING: 1234,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
assert response is None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_add_meter_readings_exception(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
) -> None:
|
||||||
|
"""Test the add_meter_readings service with a RequestException."""
|
||||||
|
|
||||||
|
await async_init_integration(hass)
|
||||||
|
|
||||||
|
config_entry: MockConfigEntry = hass.config_entries.async_entries(DOMAIN)[0]
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"PyTado.interface.Tado.set_eiq_meter_readings",
|
||||||
|
side_effect=RequestException("Error"),
|
||||||
|
),
|
||||||
|
pytest.raises(HomeAssistantError) as exc,
|
||||||
|
):
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_ADD_METER_READING,
|
||||||
|
service_data={
|
||||||
|
CONF_CONFIG_ENTRY: config_entry.entry_id,
|
||||||
|
CONF_READING: 1234,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "Could not set meter reading" in str(exc)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_add_meter_readings_invalid(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
) -> None:
|
||||||
|
"""Test the add_meter_readings service with an invalid_meter_reading response."""
|
||||||
|
|
||||||
|
await async_init_integration(hass)
|
||||||
|
|
||||||
|
config_entry: MockConfigEntry = hass.config_entries.async_entries(DOMAIN)[0]
|
||||||
|
fixture: str = load_fixture("tado/add_readings_invalid_meter_reading.json")
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"PyTado.interface.Tado.set_eiq_meter_readings",
|
||||||
|
return_value=json.loads(fixture),
|
||||||
|
),
|
||||||
|
pytest.raises(HomeAssistantError) as exc,
|
||||||
|
):
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_ADD_METER_READING,
|
||||||
|
service_data={
|
||||||
|
CONF_CONFIG_ENTRY: config_entry.entry_id,
|
||||||
|
CONF_READING: 1234,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "invalid new reading" in str(exc)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_add_meter_readings_duplicate(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
) -> None:
|
||||||
|
"""Test the add_meter_readings service with a duplicated_meter_reading response."""
|
||||||
|
|
||||||
|
await async_init_integration(hass)
|
||||||
|
|
||||||
|
config_entry: MockConfigEntry = hass.config_entries.async_entries(DOMAIN)[0]
|
||||||
|
fixture: str = load_fixture("tado/add_readings_duplicated_meter_reading.json")
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"PyTado.interface.Tado.set_eiq_meter_readings",
|
||||||
|
return_value=json.loads(fixture),
|
||||||
|
),
|
||||||
|
pytest.raises(HomeAssistantError) as exc,
|
||||||
|
):
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_ADD_METER_READING,
|
||||||
|
service_data={
|
||||||
|
CONF_CONFIG_ENTRY: config_entry.entry_id,
|
||||||
|
CONF_READING: 1234,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "reading already exists for date" in str(exc)
|
Loading…
x
Reference in New Issue
Block a user