mirror of
https://github.com/home-assistant/core.git
synced 2025-07-08 22:07:10 +00:00
Add prices service calls for easyEnergy (#105175)
This commit is contained in:
parent
d50b79ba84
commit
e59e1d7f8e
@ -8,6 +8,7 @@ from homeassistant.exceptions import ConfigEntryNotReady
|
|||||||
|
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .coordinator import EasyEnergyDataUpdateCoordinator
|
from .coordinator import EasyEnergyDataUpdateCoordinator
|
||||||
|
from .services import async_setup_services
|
||||||
|
|
||||||
PLATFORMS = [Platform.SENSOR]
|
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
|
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
|
||||||
|
|
||||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||||
|
|
||||||
|
await async_setup_services(hass, coordinator)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
142
homeassistant/components/easyenergy/services.py
Normal file
142
homeassistant/components/easyenergy/services.py
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
"""Services for easyEnergy integration."""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import date, datetime
|
||||||
|
from enum import Enum
|
||||||
|
from functools import partial
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
|
from easyenergy import Electricity, Gas, VatOption
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.core import (
|
||||||
|
HomeAssistant,
|
||||||
|
ServiceCall,
|
||||||
|
ServiceResponse,
|
||||||
|
SupportsResponse,
|
||||||
|
)
|
||||||
|
from homeassistant.exceptions import ServiceValidationError
|
||||||
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
from .coordinator import EasyEnergyDataUpdateCoordinator
|
||||||
|
|
||||||
|
ATTR_START: Final = "start"
|
||||||
|
ATTR_END: Final = "end"
|
||||||
|
ATTR_INCL_VAT: Final = "incl_vat"
|
||||||
|
|
||||||
|
GAS_SERVICE_NAME: Final = "get_gas_prices"
|
||||||
|
ENERGY_USAGE_SERVICE_NAME: Final = "get_energy_usage_prices"
|
||||||
|
ENERGY_RETURN_SERVICE_NAME: Final = "get_energy_return_prices"
|
||||||
|
SERVICE_SCHEMA: Final = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Optional(ATTR_START): str,
|
||||||
|
vol.Optional(ATTR_END): str,
|
||||||
|
vol.Required(ATTR_INCL_VAT, default=True): bool,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PriceType(str, Enum):
|
||||||
|
"""Type of price."""
|
||||||
|
|
||||||
|
ENERGY_USAGE = "energy_usage"
|
||||||
|
ENERGY_RETURN = "energy_return"
|
||||||
|
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(
|
||||||
|
translation_domain=DOMAIN,
|
||||||
|
translation_key="invalid_date",
|
||||||
|
translation_placeholders={
|
||||||
|
"date": date_input,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def __serialize_prices(prices: list[dict[str, float | datetime]]) -> ServiceResponse:
|
||||||
|
"""Serialize prices to service response."""
|
||||||
|
return {
|
||||||
|
"prices": [
|
||||||
|
{
|
||||||
|
key: str(value) if isinstance(value, datetime) else value
|
||||||
|
for key, value in timestamp_price.items()
|
||||||
|
}
|
||||||
|
for timestamp_price in prices
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def __get_prices(
|
||||||
|
call: ServiceCall,
|
||||||
|
*,
|
||||||
|
coordinator: EasyEnergyDataUpdateCoordinator,
|
||||||
|
price_type: PriceType,
|
||||||
|
) -> ServiceResponse:
|
||||||
|
"""Get prices from easyEnergy."""
|
||||||
|
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.easyenergy.gas_prices(
|
||||||
|
start_date=start,
|
||||||
|
end_date=end,
|
||||||
|
vat=vat,
|
||||||
|
)
|
||||||
|
return __serialize_prices(data.timestamp_prices)
|
||||||
|
data = await coordinator.easyenergy.energy_prices(
|
||||||
|
start_date=start,
|
||||||
|
end_date=end,
|
||||||
|
vat=vat,
|
||||||
|
)
|
||||||
|
|
||||||
|
if price_type == PriceType.ENERGY_USAGE:
|
||||||
|
return __serialize_prices(data.timestamp_usage_prices)
|
||||||
|
return __serialize_prices(data.timestamp_return_prices)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup_services(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
coordinator: EasyEnergyDataUpdateCoordinator,
|
||||||
|
) -> None:
|
||||||
|
"""Set up services for easyEnergy integration."""
|
||||||
|
|
||||||
|
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_USAGE_SERVICE_NAME,
|
||||||
|
partial(
|
||||||
|
__get_prices, coordinator=coordinator, price_type=PriceType.ENERGY_USAGE
|
||||||
|
),
|
||||||
|
schema=SERVICE_SCHEMA,
|
||||||
|
supports_response=SupportsResponse.ONLY,
|
||||||
|
)
|
||||||
|
hass.services.async_register(
|
||||||
|
DOMAIN,
|
||||||
|
ENERGY_RETURN_SERVICE_NAME,
|
||||||
|
partial(
|
||||||
|
__get_prices, coordinator=coordinator, price_type=PriceType.ENERGY_RETURN
|
||||||
|
),
|
||||||
|
schema=SERVICE_SCHEMA,
|
||||||
|
supports_response=SupportsResponse.ONLY,
|
||||||
|
)
|
46
homeassistant/components/easyenergy/services.yaml
Normal file
46
homeassistant/components/easyenergy/services.yaml
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
get_gas_prices:
|
||||||
|
fields:
|
||||||
|
incl_vat:
|
||||||
|
required: true
|
||||||
|
default: true
|
||||||
|
selector:
|
||||||
|
boolean:
|
||||||
|
start:
|
||||||
|
required: false
|
||||||
|
example: "2024-01-01 00:00:00"
|
||||||
|
selector:
|
||||||
|
datetime:
|
||||||
|
end:
|
||||||
|
required: false
|
||||||
|
example: "2024-01-01 00:00:00"
|
||||||
|
selector:
|
||||||
|
datetime:
|
||||||
|
get_energy_usage_prices:
|
||||||
|
fields:
|
||||||
|
incl_vat:
|
||||||
|
required: true
|
||||||
|
default: true
|
||||||
|
selector:
|
||||||
|
boolean:
|
||||||
|
start:
|
||||||
|
required: false
|
||||||
|
example: "2024-01-01 00:00:00"
|
||||||
|
selector:
|
||||||
|
datetime:
|
||||||
|
end:
|
||||||
|
required: false
|
||||||
|
example: "2024-01-01 00:00:00"
|
||||||
|
selector:
|
||||||
|
datetime:
|
||||||
|
get_energy_return_prices:
|
||||||
|
fields:
|
||||||
|
start:
|
||||||
|
required: false
|
||||||
|
example: "2024-01-01 00:00:00"
|
||||||
|
selector:
|
||||||
|
datetime:
|
||||||
|
end:
|
||||||
|
required: false
|
||||||
|
example: "2024-01-01 00:00:00"
|
||||||
|
selector:
|
||||||
|
datetime:
|
@ -9,6 +9,11 @@
|
|||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"exceptions": {
|
||||||
|
"invalid_date": {
|
||||||
|
"message": "Invalid date provided. Got {date}"
|
||||||
|
}
|
||||||
|
},
|
||||||
"entity": {
|
"entity": {
|
||||||
"sensor": {
|
"sensor": {
|
||||||
"current_hour_price": {
|
"current_hour_price": {
|
||||||
@ -42,5 +47,57 @@
|
|||||||
"name": "Hours priced equal or higher than current - today"
|
"name": "Hours priced equal or higher than current - today"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"services": {
|
||||||
|
"get_gas_prices": {
|
||||||
|
"name": "Get gas prices",
|
||||||
|
"description": "Request gas prices from easyEnergy.",
|
||||||
|
"fields": {
|
||||||
|
"incl_vat": {
|
||||||
|
"name": "VAT Included",
|
||||||
|
"description": "Include or exclude VAT in the prices, default is true."
|
||||||
|
},
|
||||||
|
"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_usage_prices": {
|
||||||
|
"name": "Get energy usage prices",
|
||||||
|
"description": "Request usage energy prices from easyEnergy.",
|
||||||
|
"fields": {
|
||||||
|
"incl_vat": {
|
||||||
|
"name": "[%key:component::easyenergy::services::get_gas_prices::fields::incl_vat::name%]",
|
||||||
|
"description": "[%key:component::easyenergy::services::get_gas_prices::fields::incl_vat::description%]"
|
||||||
|
},
|
||||||
|
"start": {
|
||||||
|
"name": "[%key:component::easyenergy::services::get_gas_prices::fields::start::name%]",
|
||||||
|
"description": "[%key:component::easyenergy::services::get_gas_prices::fields::start::description%]"
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"name": "[%key:component::easyenergy::services::get_gas_prices::fields::end::name%]",
|
||||||
|
"description": "[%key:component::easyenergy::services::get_gas_prices::fields::end::description%]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"get_energy_return_prices": {
|
||||||
|
"name": "Get energy return prices",
|
||||||
|
"description": "Request return energy prices from easyEnergy.",
|
||||||
|
"fields": {
|
||||||
|
"start": {
|
||||||
|
"name": "[%key:component::easyenergy::services::get_gas_prices::fields::start::name%]",
|
||||||
|
"description": "[%key:component::easyenergy::services::get_gas_prices::fields::start::description%]"
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"name": "[%key:component::easyenergy::services::get_gas_prices::fields::end::name%]",
|
||||||
|
"description": "[%key:component::easyenergy::services::get_gas_prices::fields::end::description%]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3808
tests/components/easyenergy/snapshots/test_services.ambr
Normal file
3808
tests/components/easyenergy/snapshots/test_services.ambr
Normal file
File diff suppressed because it is too large
Load Diff
59
tests/components/easyenergy/test_services.py
Normal file
59
tests/components/easyenergy/test_services.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
"""Tests for the services provided by the easyEnergy integration."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from syrupy.assertion import SnapshotAssertion
|
||||||
|
|
||||||
|
from homeassistant.components.easyenergy.const import DOMAIN
|
||||||
|
from homeassistant.components.easyenergy.services import (
|
||||||
|
ENERGY_RETURN_SERVICE_NAME,
|
||||||
|
ENERGY_USAGE_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 easyEnergy Service."""
|
||||||
|
assert hass.services.has_service(DOMAIN, GAS_SERVICE_NAME)
|
||||||
|
assert hass.services.has_service(DOMAIN, ENERGY_USAGE_SERVICE_NAME)
|
||||||
|
assert hass.services.has_service(DOMAIN, ENERGY_RETURN_SERVICE_NAME)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("init_integration")
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"service", [GAS_SERVICE_NAME, ENERGY_USAGE_SERVICE_NAME, ENERGY_RETURN_SERVICE_NAME]
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize("incl_vat", [{"incl_vat": False}, {"incl_vat": True}, {}])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"start", [{"start": "2023-01-01 00:00:00"}, {"start": "incorrect date"}, {}]
|
||||||
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"end", [{"end": "2023-01-01 00:00:00"}, {"end": "incorrect date"}, {}]
|
||||||
|
)
|
||||||
|
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 easyEnergy Service."""
|
||||||
|
|
||||||
|
data = incl_vat | start | end
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
service,
|
||||||
|
data,
|
||||||
|
blocking=True,
|
||||||
|
return_response=True,
|
||||||
|
)
|
||||||
|
assert response == snapshot
|
||||||
|
except ServiceValidationError as e:
|
||||||
|
assert e == snapshot
|
Loading…
x
Reference in New Issue
Block a user