mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Add Energyzero get_gas_prices and get_energy_price services (#101374)
Co-authored-by: Klaas Schoute <klaas_schoute@hotmail.com> Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
parent
0534b0dee4
commit
d5c7ae5b34
@ -8,6 +8,7 @@ from homeassistant.exceptions import ConfigEntryNotReady
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import EnergyZeroDataUpdateCoordinator
|
||||
from .services import async_register_services
|
||||
|
||||
PLATFORMS = [Platform.SENSOR]
|
||||
|
||||
@ -25,6 +26,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
||||
async_register_services(hass, coordinator)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
|
129
homeassistant/components/energyzero/services.py
Normal file
129
homeassistant/components/energyzero/services.py
Normal file
@ -0,0 +1,129 @@
|
||||
"""The EnergyZero services."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date, datetime
|
||||
from enum import Enum
|
||||
from functools import partial
|
||||
from typing import Final
|
||||
|
||||
from energyzero import Electricity, Gas, VatOption
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.core import (
|
||||
HomeAssistant,
|
||||
ServiceCall,
|
||||
ServiceResponse,
|
||||
SupportsResponse,
|
||||
callback,
|
||||
)
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import EnergyZeroDataUpdateCoordinator
|
||||
|
||||
ATTR_START: Final = "start"
|
||||
ATTR_END: Final = "end"
|
||||
ATTR_INCL_VAT: Final = "incl_vat"
|
||||
|
||||
GAS_SERVICE_NAME: Final = "get_gas_prices"
|
||||
ENERGY_SERVICE_NAME: Final = "get_energy_prices"
|
||||
SERVICE_SCHEMA: Final = vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_INCL_VAT): bool,
|
||||
vol.Optional(ATTR_START): str,
|
||||
vol.Optional(ATTR_END): str,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class PriceType(Enum):
|
||||
"""Type of price."""
|
||||
|
||||
ENERGY = "energy"
|
||||
GAS = "gas"
|
||||
|
||||
|
||||
def __get_date(date_input: str | None) -> date | datetime:
|
||||
"""Get date."""
|
||||
if not date_input:
|
||||
return dt_util.now().date()
|
||||
|
||||
if value := dt_util.parse_datetime(date_input):
|
||||
return value
|
||||
|
||||
raise ServiceValidationError(
|
||||
"Invalid datetime provided.",
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="invalid_date",
|
||||
translation_placeholders={
|
||||
"date": date_input,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def __serialize_prices(prices: Electricity | Gas) -> ServiceResponse:
|
||||
"""Serialize prices."""
|
||||
return {
|
||||
"prices": [
|
||||
{
|
||||
key: str(value) if isinstance(value, datetime) else value
|
||||
for key, value in timestamp_price.items()
|
||||
}
|
||||
for timestamp_price in prices.timestamp_prices
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
async def __get_prices(
|
||||
call: ServiceCall,
|
||||
*,
|
||||
coordinator: EnergyZeroDataUpdateCoordinator,
|
||||
price_type: PriceType,
|
||||
) -> ServiceResponse:
|
||||
start = __get_date(call.data.get(ATTR_START))
|
||||
end = __get_date(call.data.get(ATTR_END))
|
||||
|
||||
vat = VatOption.INCLUDE
|
||||
|
||||
if call.data.get(ATTR_INCL_VAT) is False:
|
||||
vat = VatOption.EXCLUDE
|
||||
|
||||
data: Electricity | Gas
|
||||
|
||||
if price_type == PriceType.GAS:
|
||||
data = await coordinator.energyzero.gas_prices(
|
||||
start_date=start,
|
||||
end_date=end,
|
||||
vat=vat,
|
||||
)
|
||||
else:
|
||||
data = await coordinator.energyzero.energy_prices(
|
||||
start_date=start,
|
||||
end_date=end,
|
||||
vat=vat,
|
||||
)
|
||||
|
||||
return __serialize_prices(data)
|
||||
|
||||
|
||||
@callback
|
||||
def async_register_services(
|
||||
hass: HomeAssistant, coordinator: EnergyZeroDataUpdateCoordinator
|
||||
):
|
||||
"""Set up EnergyZero services."""
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN,
|
||||
GAS_SERVICE_NAME,
|
||||
partial(__get_prices, coordinator=coordinator, price_type=PriceType.GAS),
|
||||
schema=SERVICE_SCHEMA,
|
||||
supports_response=SupportsResponse.ONLY,
|
||||
)
|
||||
hass.services.async_register(
|
||||
DOMAIN,
|
||||
ENERGY_SERVICE_NAME,
|
||||
partial(__get_prices, coordinator=coordinator, price_type=PriceType.ENERGY),
|
||||
schema=SERVICE_SCHEMA,
|
||||
supports_response=SupportsResponse.ONLY,
|
||||
)
|
34
homeassistant/components/energyzero/services.yaml
Normal file
34
homeassistant/components/energyzero/services.yaml
Normal file
@ -0,0 +1,34 @@
|
||||
get_gas_prices:
|
||||
fields:
|
||||
incl_vat:
|
||||
required: true
|
||||
default: true
|
||||
selector:
|
||||
boolean:
|
||||
start:
|
||||
required: false
|
||||
example: "2023-01-01 00:00:00"
|
||||
selector:
|
||||
datetime:
|
||||
end:
|
||||
required: false
|
||||
example: "2023-01-01 00:00:00"
|
||||
selector:
|
||||
datetime:
|
||||
get_energy_prices:
|
||||
fields:
|
||||
incl_vat:
|
||||
required: true
|
||||
default: true
|
||||
selector:
|
||||
boolean:
|
||||
start:
|
||||
required: false
|
||||
example: "2023-01-01 00:00:00"
|
||||
selector:
|
||||
datetime:
|
||||
end:
|
||||
required: false
|
||||
example: "2023-01-01 00:00:00"
|
||||
selector:
|
||||
datetime:
|
@ -9,6 +9,11 @@
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
"invalid_date": {
|
||||
"message": "Invalid date provided. Got {date}"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"current_hour_price": {
|
||||
@ -39,5 +44,43 @@
|
||||
"name": "Hours priced equal or lower than current - today"
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"get_gas_prices": {
|
||||
"name": "Get gas prices",
|
||||
"description": "Request gas prices from EnergyZero.",
|
||||
"fields": {
|
||||
"incl_vat": {
|
||||
"name": "Including VAT",
|
||||
"description": "Include VAT in the prices."
|
||||
},
|
||||
"start": {
|
||||
"name": "Start",
|
||||
"description": "Specifies the date and time from which to retrieve prices. Defaults to today if omitted."
|
||||
},
|
||||
"end": {
|
||||
"name": "End",
|
||||
"description": "Specifies the date and time until which to retrieve prices. Defaults to today if omitted."
|
||||
}
|
||||
}
|
||||
},
|
||||
"get_energy_prices": {
|
||||
"name": "Get energy prices",
|
||||
"description": "Request energy prices from EnergyZero.",
|
||||
"fields": {
|
||||
"incl_vat": {
|
||||
"name": "[%key:component::energyzero::services::get_gas_prices::fields::incl_vat::name%]",
|
||||
"description": "[%key:component::energyzero::services::get_gas_prices::fields::incl_vat::description%]"
|
||||
},
|
||||
"start": {
|
||||
"name": "[%key:component::energyzero::services::get_gas_prices::fields::start::name%]",
|
||||
"description": "[%key:component::energyzero::services::get_gas_prices::fields::start::description%]"
|
||||
},
|
||||
"end": {
|
||||
"name": "[%key:component::energyzero::services::get_gas_prices::fields::end::name%]",
|
||||
"description": "[%key:component::energyzero::services::get_gas_prices::fields::end::description%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
2401
tests/components/energyzero/snapshots/test_services.ambr
Normal file
2401
tests/components/energyzero/snapshots/test_services.ambr
Normal file
File diff suppressed because it is too large
Load Diff
90
tests/components/energyzero/test_services.py
Normal file
90
tests/components/energyzero/test_services.py
Normal file
@ -0,0 +1,90 @@
|
||||
"""Tests for the services provided by the EnergyZero integration."""
|
||||
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.energyzero.const import DOMAIN
|
||||
from homeassistant.components.energyzero.services import (
|
||||
ENERGY_SERVICE_NAME,
|
||||
GAS_SERVICE_NAME,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_integration")
|
||||
async def test_has_services(
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test the existence of the EnergyZero Service."""
|
||||
assert hass.services.has_service(DOMAIN, GAS_SERVICE_NAME)
|
||||
assert hass.services.has_service(DOMAIN, ENERGY_SERVICE_NAME)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_integration")
|
||||
@pytest.mark.parametrize("service", [GAS_SERVICE_NAME, ENERGY_SERVICE_NAME])
|
||||
@pytest.mark.parametrize("incl_vat", [{"incl_vat": False}, {"incl_vat": True}])
|
||||
@pytest.mark.parametrize("start", [{"start": "2023-01-01 00:00:00"}, {}])
|
||||
@pytest.mark.parametrize("end", [{"end": "2023-01-01 00:00:00"}, {}])
|
||||
async def test_service(
|
||||
hass: HomeAssistant,
|
||||
snapshot: SnapshotAssertion,
|
||||
service: str,
|
||||
incl_vat: dict[str, bool],
|
||||
start: dict[str, str],
|
||||
end: dict[str, str],
|
||||
) -> None:
|
||||
"""Test the EnergyZero Service."""
|
||||
|
||||
data = incl_vat | start | end
|
||||
|
||||
assert snapshot == await hass.services.async_call(
|
||||
DOMAIN,
|
||||
service,
|
||||
data,
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_integration")
|
||||
@pytest.mark.parametrize("service", [GAS_SERVICE_NAME, ENERGY_SERVICE_NAME])
|
||||
@pytest.mark.parametrize(
|
||||
("service_data", "error", "error_message"),
|
||||
[
|
||||
({}, vol.er.Error, "required key not provided .+"),
|
||||
(
|
||||
{"incl_vat": "incorrect vat"},
|
||||
vol.er.Error,
|
||||
"expected bool for dictionary value .+",
|
||||
),
|
||||
(
|
||||
{"incl_vat": True, "start": "incorrect date"},
|
||||
ServiceValidationError,
|
||||
"Invalid datetime provided.",
|
||||
),
|
||||
(
|
||||
{"incl_vat": True, "end": "incorrect date"},
|
||||
ServiceValidationError,
|
||||
"Invalid datetime provided.",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_service_validation(
|
||||
hass: HomeAssistant,
|
||||
service: str,
|
||||
service_data: dict[str, str],
|
||||
error: type[Exception],
|
||||
error_message: str,
|
||||
) -> None:
|
||||
"""Test the EnergyZero Service validation."""
|
||||
|
||||
with pytest.raises(error, match=error_message):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
service,
|
||||
service_data,
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user