mirror of
https://github.com/home-assistant/core.git
synced 2025-07-09 22:37:11 +00:00
Add Get price service to Nord Pool (#130185)
* Add get_price service to Nord Pool * Tests and fixes * Fixes * Not used fixtures * update qs * Fixes * docstring * Remove selector from strings * Mod service
This commit is contained in:
parent
f8cd6204ca
commit
4c60e36f4f
@ -5,13 +5,25 @@ from __future__ import annotations
|
|||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
|
from homeassistant.helpers import config_validation as cv
|
||||||
|
from homeassistant.helpers.typing import ConfigType
|
||||||
from homeassistant.util import dt as dt_util
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
from .const import DOMAIN, PLATFORMS
|
from .const import DOMAIN, PLATFORMS
|
||||||
from .coordinator import NordPoolDataUpdateCoordinator
|
from .coordinator import NordPoolDataUpdateCoordinator
|
||||||
|
from .services import async_setup_services
|
||||||
|
|
||||||
type NordPoolConfigEntry = ConfigEntry[NordPoolDataUpdateCoordinator]
|
type NordPoolConfigEntry = ConfigEntry[NordPoolDataUpdateCoordinator]
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||||
|
"""Set up the Nord Pool service."""
|
||||||
|
|
||||||
|
async_setup_services(hass)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: NordPoolConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: NordPoolConfigEntry) -> bool:
|
||||||
"""Set up Nord Pool from a config entry."""
|
"""Set up Nord Pool from a config entry."""
|
||||||
|
@ -38,5 +38,10 @@
|
|||||||
"default": "mdi:cash-multiple"
|
"default": "mdi:cash-multiple"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"services": {
|
||||||
|
"get_prices_for_date": {
|
||||||
|
"service": "mdi:cash-multiple"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,27 +14,18 @@ rules:
|
|||||||
comment: |
|
comment: |
|
||||||
Entities doesn't subscribe to events.
|
Entities doesn't subscribe to events.
|
||||||
dependency-transparency: done
|
dependency-transparency: done
|
||||||
action-setup:
|
action-setup: done
|
||||||
status: exempt
|
|
||||||
comment: |
|
|
||||||
This integration does not provide additional actions.
|
|
||||||
common-modules: done
|
common-modules: done
|
||||||
docs-high-level-description: done
|
docs-high-level-description: done
|
||||||
docs-installation-instructions: done
|
docs-installation-instructions: done
|
||||||
docs-removal-instructions: done
|
docs-removal-instructions: done
|
||||||
docs-actions:
|
docs-actions: done
|
||||||
status: exempt
|
|
||||||
comment: |
|
|
||||||
This integration does not provide additional actions.
|
|
||||||
brands: done
|
brands: done
|
||||||
# Silver
|
# Silver
|
||||||
config-entry-unloading: done
|
config-entry-unloading: done
|
||||||
log-when-unavailable: done
|
log-when-unavailable: done
|
||||||
entity-unavailable: done
|
entity-unavailable: done
|
||||||
action-exceptions:
|
action-exceptions: done
|
||||||
status: exempt
|
|
||||||
comment: |
|
|
||||||
No actions.
|
|
||||||
reauthentication-flow:
|
reauthentication-flow:
|
||||||
status: exempt
|
status: exempt
|
||||||
comment: |
|
comment: |
|
||||||
|
129
homeassistant/components/nordpool/services.py
Normal file
129
homeassistant/components/nordpool/services.py
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
"""Services for Nord Pool integration."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import date, datetime
|
||||||
|
import logging
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from pynordpool import (
|
||||||
|
AREAS,
|
||||||
|
Currency,
|
||||||
|
NordPoolAuthenticationError,
|
||||||
|
NordPoolEmptyResponseError,
|
||||||
|
NordPoolError,
|
||||||
|
)
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntryState
|
||||||
|
from homeassistant.const import ATTR_DATE
|
||||||
|
from homeassistant.core import (
|
||||||
|
HomeAssistant,
|
||||||
|
ServiceCall,
|
||||||
|
ServiceResponse,
|
||||||
|
SupportsResponse,
|
||||||
|
)
|
||||||
|
from homeassistant.exceptions import ServiceValidationError
|
||||||
|
from homeassistant.helpers import config_validation as cv
|
||||||
|
from homeassistant.helpers.selector import ConfigEntrySelector
|
||||||
|
from homeassistant.util import dt as dt_util
|
||||||
|
from homeassistant.util.json import JsonValueType
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from . import NordPoolConfigEntry
|
||||||
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
ATTR_CONFIG_ENTRY = "config_entry"
|
||||||
|
ATTR_AREAS = "areas"
|
||||||
|
ATTR_CURRENCY = "currency"
|
||||||
|
|
||||||
|
SERVICE_GET_PRICES_FOR_DATE = "get_prices_for_date"
|
||||||
|
SERVICE_GET_PRICES_SCHEMA = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(ATTR_CONFIG_ENTRY): ConfigEntrySelector(),
|
||||||
|
vol.Required(ATTR_DATE): cv.date,
|
||||||
|
vol.Optional(ATTR_AREAS): vol.All(vol.In(list(AREAS)), cv.ensure_list, [str]),
|
||||||
|
vol.Optional(ATTR_CURRENCY): vol.All(
|
||||||
|
cv.string, vol.In([currency.value for currency in Currency])
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_config_entry(hass: HomeAssistant, entry_id: str) -> NordPoolConfigEntry:
|
||||||
|
"""Return config entry."""
|
||||||
|
if not (entry := hass.config_entries.async_get_entry(entry_id)):
|
||||||
|
raise ServiceValidationError(
|
||||||
|
translation_domain=DOMAIN,
|
||||||
|
translation_key="entry_not_found",
|
||||||
|
)
|
||||||
|
if entry.state is not ConfigEntryState.LOADED:
|
||||||
|
raise ServiceValidationError(
|
||||||
|
translation_domain=DOMAIN,
|
||||||
|
translation_key="entry_not_loaded",
|
||||||
|
)
|
||||||
|
return entry
|
||||||
|
|
||||||
|
|
||||||
|
def async_setup_services(hass: HomeAssistant) -> None:
|
||||||
|
"""Set up services for Nord Pool integration."""
|
||||||
|
|
||||||
|
async def get_prices_for_date(call: ServiceCall) -> ServiceResponse:
|
||||||
|
"""Get price service."""
|
||||||
|
entry = get_config_entry(hass, call.data[ATTR_CONFIG_ENTRY])
|
||||||
|
asked_date: date = call.data[ATTR_DATE]
|
||||||
|
client = entry.runtime_data.client
|
||||||
|
|
||||||
|
areas: list[str] = entry.data[ATTR_AREAS]
|
||||||
|
if _areas := call.data.get(ATTR_AREAS):
|
||||||
|
areas = _areas
|
||||||
|
|
||||||
|
currency: str = entry.data[ATTR_CURRENCY]
|
||||||
|
if _currency := call.data.get(ATTR_CURRENCY):
|
||||||
|
currency = _currency
|
||||||
|
|
||||||
|
areas = [area.upper() for area in areas]
|
||||||
|
currency = currency.upper()
|
||||||
|
|
||||||
|
try:
|
||||||
|
price_data = await client.async_get_delivery_period(
|
||||||
|
datetime.combine(asked_date, dt_util.utcnow().time()),
|
||||||
|
Currency(currency),
|
||||||
|
areas,
|
||||||
|
)
|
||||||
|
except NordPoolAuthenticationError as error:
|
||||||
|
raise ServiceValidationError(
|
||||||
|
translation_domain=DOMAIN,
|
||||||
|
translation_key="authentication_error",
|
||||||
|
) from error
|
||||||
|
except NordPoolEmptyResponseError as error:
|
||||||
|
raise ServiceValidationError(
|
||||||
|
translation_domain=DOMAIN,
|
||||||
|
translation_key="empty_response",
|
||||||
|
) from error
|
||||||
|
except NordPoolError as error:
|
||||||
|
raise ServiceValidationError(
|
||||||
|
translation_domain=DOMAIN,
|
||||||
|
translation_key="connection_error",
|
||||||
|
) from error
|
||||||
|
|
||||||
|
result: dict[str, JsonValueType] = {}
|
||||||
|
for area in areas:
|
||||||
|
result[area] = [
|
||||||
|
{
|
||||||
|
"start": price_entry.start.isoformat(),
|
||||||
|
"end": price_entry.end.isoformat(),
|
||||||
|
"price": price_entry.entry[area],
|
||||||
|
}
|
||||||
|
for price_entry in price_data.entries
|
||||||
|
]
|
||||||
|
return result
|
||||||
|
|
||||||
|
hass.services.async_register(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_GET_PRICES_FOR_DATE,
|
||||||
|
get_prices_for_date,
|
||||||
|
schema=SERVICE_GET_PRICES_SCHEMA,
|
||||||
|
supports_response=SupportsResponse.ONLY,
|
||||||
|
)
|
48
homeassistant/components/nordpool/services.yaml
Normal file
48
homeassistant/components/nordpool/services.yaml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
get_prices_for_date:
|
||||||
|
fields:
|
||||||
|
config_entry:
|
||||||
|
required: true
|
||||||
|
selector:
|
||||||
|
config_entry:
|
||||||
|
integration: nordpool
|
||||||
|
date:
|
||||||
|
required: true
|
||||||
|
selector:
|
||||||
|
date:
|
||||||
|
areas:
|
||||||
|
selector:
|
||||||
|
select:
|
||||||
|
options:
|
||||||
|
- "EE"
|
||||||
|
- "LT"
|
||||||
|
- "LV"
|
||||||
|
- "AT"
|
||||||
|
- "BE"
|
||||||
|
- "FR"
|
||||||
|
- "GER"
|
||||||
|
- "NL"
|
||||||
|
- "PL"
|
||||||
|
- "DK1"
|
||||||
|
- "DK2"
|
||||||
|
- "FI"
|
||||||
|
- "NO1"
|
||||||
|
- "NO2"
|
||||||
|
- "NO3"
|
||||||
|
- "NO4"
|
||||||
|
- "NO5"
|
||||||
|
- "SE1"
|
||||||
|
- "SE2"
|
||||||
|
- "SE3"
|
||||||
|
- "SE4"
|
||||||
|
- "SYS"
|
||||||
|
mode: dropdown
|
||||||
|
currency:
|
||||||
|
selector:
|
||||||
|
select:
|
||||||
|
options:
|
||||||
|
- "DKK"
|
||||||
|
- "EUR"
|
||||||
|
- "NOK"
|
||||||
|
- "PLN"
|
||||||
|
- "SEK"
|
||||||
|
mode: dropdown
|
@ -70,9 +70,48 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"services": {
|
||||||
|
"get_prices_for_date": {
|
||||||
|
"name": "Get prices for date",
|
||||||
|
"description": "Retrieve the prices for a specific date.",
|
||||||
|
"fields": {
|
||||||
|
"config_entry": {
|
||||||
|
"name": "Select Nord Pool configuration entry",
|
||||||
|
"description": "Choose the configuration entry."
|
||||||
|
},
|
||||||
|
"date": {
|
||||||
|
"name": "Date",
|
||||||
|
"description": "Only dates two months in the past and one day in the future is allowed."
|
||||||
|
},
|
||||||
|
"areas": {
|
||||||
|
"name": "Areas",
|
||||||
|
"description": "One or multiple areas to get prices for. If left empty it will use the areas already configured."
|
||||||
|
},
|
||||||
|
"currency": {
|
||||||
|
"name": "Currency",
|
||||||
|
"description": "Currency to get prices in. If left empty it will use the currency already configured."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"exceptions": {
|
"exceptions": {
|
||||||
"initial_update_failed": {
|
"initial_update_failed": {
|
||||||
"message": "Initial update failed on startup with error {error}"
|
"message": "Initial update failed on startup with error {error}"
|
||||||
|
},
|
||||||
|
"entry_not_found": {
|
||||||
|
"message": "The Nord Pool integration is not configured in Home Assistant."
|
||||||
|
},
|
||||||
|
"entry_not_loaded": {
|
||||||
|
"message": "The Nord Pool integration is currently not loaded or disabled in Home Assistant."
|
||||||
|
},
|
||||||
|
"authentication_error": {
|
||||||
|
"message": "There was an authentication error as you tried to retrieve data too far in the past."
|
||||||
|
},
|
||||||
|
"empty_response": {
|
||||||
|
"message": "Nord Pool has not posted market prices for the provided date."
|
||||||
|
},
|
||||||
|
"connection_error": {
|
||||||
|
"message": "There was a connection error connecting to the API. Try again later."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
127
tests/components/nordpool/snapshots/test_services.ambr
Normal file
127
tests/components/nordpool/snapshots/test_services.ambr
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
# serializer version: 1
|
||||||
|
# name: test_service_call
|
||||||
|
dict({
|
||||||
|
'SE3': list([
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T00:00:00+00:00',
|
||||||
|
'price': 250.73,
|
||||||
|
'start': '2024-11-04T23:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T01:00:00+00:00',
|
||||||
|
'price': 76.36,
|
||||||
|
'start': '2024-11-05T00:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T02:00:00+00:00',
|
||||||
|
'price': 73.92,
|
||||||
|
'start': '2024-11-05T01:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T03:00:00+00:00',
|
||||||
|
'price': 61.69,
|
||||||
|
'start': '2024-11-05T02:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T04:00:00+00:00',
|
||||||
|
'price': 64.6,
|
||||||
|
'start': '2024-11-05T03:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T05:00:00+00:00',
|
||||||
|
'price': 453.27,
|
||||||
|
'start': '2024-11-05T04:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T06:00:00+00:00',
|
||||||
|
'price': 996.28,
|
||||||
|
'start': '2024-11-05T05:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T07:00:00+00:00',
|
||||||
|
'price': 1406.14,
|
||||||
|
'start': '2024-11-05T06:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T08:00:00+00:00',
|
||||||
|
'price': 1346.54,
|
||||||
|
'start': '2024-11-05T07:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T09:00:00+00:00',
|
||||||
|
'price': 1150.28,
|
||||||
|
'start': '2024-11-05T08:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T10:00:00+00:00',
|
||||||
|
'price': 1031.32,
|
||||||
|
'start': '2024-11-05T09:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T11:00:00+00:00',
|
||||||
|
'price': 927.37,
|
||||||
|
'start': '2024-11-05T10:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T12:00:00+00:00',
|
||||||
|
'price': 925.05,
|
||||||
|
'start': '2024-11-05T11:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T13:00:00+00:00',
|
||||||
|
'price': 949.49,
|
||||||
|
'start': '2024-11-05T12:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T14:00:00+00:00',
|
||||||
|
'price': 1042.03,
|
||||||
|
'start': '2024-11-05T13:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T15:00:00+00:00',
|
||||||
|
'price': 1258.89,
|
||||||
|
'start': '2024-11-05T14:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T16:00:00+00:00',
|
||||||
|
'price': 1816.45,
|
||||||
|
'start': '2024-11-05T15:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T17:00:00+00:00',
|
||||||
|
'price': 2512.65,
|
||||||
|
'start': '2024-11-05T16:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T18:00:00+00:00',
|
||||||
|
'price': 1819.83,
|
||||||
|
'start': '2024-11-05T17:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T19:00:00+00:00',
|
||||||
|
'price': 1011.77,
|
||||||
|
'start': '2024-11-05T18:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T20:00:00+00:00',
|
||||||
|
'price': 835.53,
|
||||||
|
'start': '2024-11-05T19:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T21:00:00+00:00',
|
||||||
|
'price': 796.19,
|
||||||
|
'start': '2024-11-05T20:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T22:00:00+00:00',
|
||||||
|
'price': 522.3,
|
||||||
|
'start': '2024-11-05T21:00:00+00:00',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'end': '2024-11-05T23:00:00+00:00',
|
||||||
|
'price': 289.14,
|
||||||
|
'start': '2024-11-05T22:00:00+00:00',
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
# ---
|
166
tests/components/nordpool/test_services.py
Normal file
166
tests/components/nordpool/test_services.py
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
"""Test services in Nord Pool."""
|
||||||
|
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from pynordpool import (
|
||||||
|
DeliveryPeriodData,
|
||||||
|
NordPoolAuthenticationError,
|
||||||
|
NordPoolEmptyResponseError,
|
||||||
|
NordPoolError,
|
||||||
|
)
|
||||||
|
import pytest
|
||||||
|
from syrupy.assertion import SnapshotAssertion
|
||||||
|
|
||||||
|
from homeassistant.components.nordpool.const import DOMAIN
|
||||||
|
from homeassistant.components.nordpool.services import (
|
||||||
|
ATTR_AREAS,
|
||||||
|
ATTR_CONFIG_ENTRY,
|
||||||
|
ATTR_CURRENCY,
|
||||||
|
SERVICE_GET_PRICES_FOR_DATE,
|
||||||
|
)
|
||||||
|
from homeassistant.const import ATTR_DATE
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import ServiceValidationError
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
TEST_SERVICE_DATA = {
|
||||||
|
ATTR_CONFIG_ENTRY: "to_replace",
|
||||||
|
ATTR_DATE: "2024-11-05",
|
||||||
|
ATTR_AREAS: "SE3",
|
||||||
|
ATTR_CURRENCY: "SEK",
|
||||||
|
}
|
||||||
|
TEST_SERVICE_DATA_USE_DEFAULTS = {
|
||||||
|
ATTR_CONFIG_ENTRY: "to_replace",
|
||||||
|
ATTR_DATE: "2024-11-05",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.freeze_time("2024-11-05T18:00:00+00:00")
|
||||||
|
async def test_service_call(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
load_int: MockConfigEntry,
|
||||||
|
get_data: DeliveryPeriodData,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
) -> None:
|
||||||
|
"""Test get_prices_for_date service call."""
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
||||||
|
return_value=get_data,
|
||||||
|
),
|
||||||
|
):
|
||||||
|
service_data = TEST_SERVICE_DATA.copy()
|
||||||
|
service_data[ATTR_CONFIG_ENTRY] = load_int.entry_id
|
||||||
|
response = await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_GET_PRICES_FOR_DATE,
|
||||||
|
service_data,
|
||||||
|
blocking=True,
|
||||||
|
return_response=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response == snapshot
|
||||||
|
price_value = response["SE3"][0]["price"]
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
||||||
|
return_value=get_data,
|
||||||
|
),
|
||||||
|
):
|
||||||
|
service_data = TEST_SERVICE_DATA_USE_DEFAULTS.copy()
|
||||||
|
service_data[ATTR_CONFIG_ENTRY] = load_int.entry_id
|
||||||
|
response = await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_GET_PRICES_FOR_DATE,
|
||||||
|
service_data,
|
||||||
|
blocking=True,
|
||||||
|
return_response=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert "SE3" in response
|
||||||
|
assert response["SE3"][0]["price"] == price_value
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("error", "key"),
|
||||||
|
[
|
||||||
|
(NordPoolAuthenticationError, "authentication_error"),
|
||||||
|
(NordPoolEmptyResponseError, "empty_response"),
|
||||||
|
(NordPoolError, "connection_error"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
@pytest.mark.freeze_time("2024-11-05T18:00:00+00:00")
|
||||||
|
async def test_service_call_failures(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
load_int: MockConfigEntry,
|
||||||
|
error: Exception,
|
||||||
|
key: str,
|
||||||
|
) -> None:
|
||||||
|
"""Test get_prices_for_date service call when it fails."""
|
||||||
|
service_data = TEST_SERVICE_DATA.copy()
|
||||||
|
service_data[ATTR_CONFIG_ENTRY] = load_int.entry_id
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
||||||
|
side_effect=error,
|
||||||
|
),
|
||||||
|
pytest.raises(ServiceValidationError) as err,
|
||||||
|
):
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_GET_PRICES_FOR_DATE,
|
||||||
|
service_data,
|
||||||
|
blocking=True,
|
||||||
|
return_response=True,
|
||||||
|
)
|
||||||
|
assert err.value.translation_key == key
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.freeze_time("2024-11-05T18:00:00+00:00")
|
||||||
|
async def test_service_call_config_entry_bad_state(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
load_int: MockConfigEntry,
|
||||||
|
get_data: DeliveryPeriodData,
|
||||||
|
) -> None:
|
||||||
|
"""Test get_prices_for_date service call when config entry bad state."""
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
||||||
|
return_value=get_data,
|
||||||
|
),
|
||||||
|
pytest.raises(ServiceValidationError) as err,
|
||||||
|
):
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_GET_PRICES_FOR_DATE,
|
||||||
|
TEST_SERVICE_DATA,
|
||||||
|
blocking=True,
|
||||||
|
return_response=True,
|
||||||
|
)
|
||||||
|
assert err.value.translation_key == "entry_not_found"
|
||||||
|
|
||||||
|
service_data = TEST_SERVICE_DATA.copy()
|
||||||
|
service_data[ATTR_CONFIG_ENTRY] = load_int.entry_id
|
||||||
|
await hass.config_entries.async_unload(load_int.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.nordpool.coordinator.NordPoolClient.async_get_delivery_period",
|
||||||
|
return_value=get_data,
|
||||||
|
),
|
||||||
|
pytest.raises(ServiceValidationError) as err,
|
||||||
|
):
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_GET_PRICES_FOR_DATE,
|
||||||
|
service_data,
|
||||||
|
blocking=True,
|
||||||
|
return_response=True,
|
||||||
|
)
|
||||||
|
assert err.value.translation_key == "entry_not_loaded"
|
Loading…
x
Reference in New Issue
Block a user