Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions homeassistant/components/dnsip/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ async def async_step_init(
self, user_input: dict[str, Any] | None = None
) -> ConfigFlowResult:
"""Manage the options."""
if self.config_entry.data[CONF_HOSTNAME] == DEFAULT_HOSTNAME:
return self.async_abort(reason="no_options")

errors = {}
if user_input is not None:
resolver = user_input.get(CONF_RESOLVER, DEFAULT_RESOLVER)
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/dnsip/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
}
},
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]",
"no_options": "The myip hostname requires the default resolvers and therefore cannot be configured."
},
"error": {
"invalid_resolver": "Invalid IP address or port for resolver"
Expand Down
1 change: 1 addition & 0 deletions homeassistant/components/nordpool/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
DEFAULT_NAME = "Nord Pool"

CONF_AREAS = "areas"
ATTR_RESOLUTION = "resolution"
3 changes: 3 additions & 0 deletions homeassistant/components/nordpool/icons.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
"services": {
"get_prices_for_date": {
"service": "mdi:cash-multiple"
},
"get_price_indices_for_date": {
"service": "mdi:cash-multiple"
}
}
}
79 changes: 71 additions & 8 deletions homeassistant/components/nordpool/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,21 @@

from __future__ import annotations

from collections.abc import Callable
from datetime import date, datetime
from functools import partial
import logging
from typing import TYPE_CHECKING

from pynordpool import (
AREAS,
Currency,
DeliveryPeriodData,
NordPoolAuthenticationError,
NordPoolClient,
NordPoolEmptyResponseError,
NordPoolError,
PriceIndicesData,
)
import voluptuous as vol

Expand All @@ -32,14 +37,15 @@

if TYPE_CHECKING:
from . import NordPoolConfigEntry
from .const import DOMAIN
from .const import ATTR_RESOLUTION, 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_PRICE_INDICES_FOR_DATE = "get_price_indices_for_date"
SERVICE_GET_PRICES_SCHEMA = vol.Schema(
{
vol.Required(ATTR_CONFIG_ENTRY): ConfigEntrySelector({"integration": DOMAIN}),
Expand All @@ -50,6 +56,13 @@
),
}
)
SERVICE_GET_PRICE_INDICES_SCHEMA = SERVICE_GET_PRICES_SCHEMA.extend(
{
vol.Optional(ATTR_RESOLUTION, default=60): vol.All(
cv.positive_int, vol.All(vol.Coerce(int), vol.In((15, 30, 60)))
),
}
)


def get_config_entry(hass: HomeAssistant, entry_id: str) -> NordPoolConfigEntry:
Expand All @@ -71,11 +84,13 @@ def get_config_entry(hass: HomeAssistant, entry_id: str) -> NordPoolConfigEntry:
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."""
def get_service_params(
call: ServiceCall,
) -> tuple[NordPoolClient, date, str, list[str], int]:
"""Return the parameters for the service."""
entry = get_config_entry(hass, call.data[ATTR_CONFIG_ENTRY])
asked_date: date = call.data[ATTR_DATE]
client = entry.runtime_data.client
asked_date: date = call.data[ATTR_DATE]

areas: list[str] = entry.data[ATTR_AREAS]
if _areas := call.data.get(ATTR_AREAS):
Expand All @@ -85,14 +100,55 @@ async def get_prices_for_date(call: ServiceCall) -> ServiceResponse:
if _currency := call.data.get(ATTR_CURRENCY):
currency = _currency

resolution: int = 60
if _resolution := call.data.get(ATTR_RESOLUTION):
resolution = _resolution

areas = [area.upper() for area in areas]
currency = currency.upper()

return (client, asked_date, currency, areas, resolution)

async def get_prices_for_date(
client: NordPoolClient,
asked_date: date,
currency: str,
areas: list[str],
resolution: int,
) -> DeliveryPeriodData:
"""Get prices."""
return await client.async_get_delivery_period(
datetime.combine(asked_date, dt_util.utcnow().time()),
Currency(currency),
areas,
)

async def get_price_indices_for_date(
client: NordPoolClient,
asked_date: date,
currency: str,
areas: list[str],
resolution: int,
) -> PriceIndicesData:
"""Get prices."""
return await client.async_get_price_indices(
datetime.combine(asked_date, dt_util.utcnow().time()),
Currency(currency),
areas,
resolution=resolution,
)

async def get_prices(func: Callable, call: ServiceCall) -> ServiceResponse:
"""Get price service."""
client, asked_date, currency, areas, resolution = get_service_params(call)

try:
price_data = await client.async_get_delivery_period(
datetime.combine(asked_date, dt_util.utcnow().time()),
Currency(currency),
price_data = await func(
client,
asked_date,
currency,
areas,
resolution,
)
except NordPoolAuthenticationError as error:
raise ServiceValidationError(
Expand Down Expand Up @@ -122,7 +178,14 @@ async def get_prices_for_date(call: ServiceCall) -> ServiceResponse:
hass.services.async_register(
DOMAIN,
SERVICE_GET_PRICES_FOR_DATE,
get_prices_for_date,
partial(get_prices, get_prices_for_date),
schema=SERVICE_GET_PRICES_SCHEMA,
supports_response=SupportsResponse.ONLY,
)
hass.services.async_register(
DOMAIN,
SERVICE_GET_PRICE_INDICES_FOR_DATE,
partial(get_prices, get_price_indices_for_date),
schema=SERVICE_GET_PRICE_INDICES_SCHEMA,
supports_response=SupportsResponse.ONLY,
)
56 changes: 56 additions & 0 deletions homeassistant/components/nordpool/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,59 @@ get_prices_for_date:
- "PLN"
- "SEK"
mode: dropdown
get_price_indices_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
resolution:
selector:
select:
options:
- "15"
- "30"
- "60"
mode: dropdown
26 changes: 26 additions & 0 deletions homeassistant/components/nordpool/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,32 @@
"description": "Currency to get prices in. If left empty it will use the currency already configured."
}
}
},
"get_price_indices_for_date": {
"name": "Get price indices for date",
"description": "Retrieves the price indices for a specific date.",
"fields": {
"config_entry": {
"name": "Config entry",
"description": "The Nord Pool configuration entry for this action."
},
"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."
},
"resolution": {
"name": "Resolution",
"description": "Resolution time for the prices, can be any of 15, 30 and 60 minutes."
}
}
}
},
"exceptions": {
Expand Down
6 changes: 5 additions & 1 deletion homeassistant/components/playstation_network/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@
from .coordinator import PlaystationNetworkConfigEntry, PlaystationNetworkCoordinator
from .helpers import PlaystationNetwork

PLATFORMS: list[Platform] = [Platform.MEDIA_PLAYER, Platform.SENSOR]
PLATFORMS: list[Platform] = [
Platform.BINARY_SENSOR,
Platform.MEDIA_PLAYER,
Platform.SENSOR,
]


async def async_setup_entry(
Expand Down
71 changes: 71 additions & 0 deletions homeassistant/components/playstation_network/binary_sensor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"""Binary Sensor platform for PlayStation Network integration."""

from __future__ import annotations

from collections.abc import Callable
from dataclasses import dataclass
from enum import StrEnum

from homeassistant.components.binary_sensor import (
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback

from .coordinator import PlaystationNetworkConfigEntry, PlaystationNetworkData
from .entity import PlaystationNetworkServiceEntity

PARALLEL_UPDATES = 0


@dataclass(kw_only=True, frozen=True)
class PlaystationNetworkBinarySensorEntityDescription(BinarySensorEntityDescription):
"""PlayStation Network binary sensor description."""

is_on_fn: Callable[[PlaystationNetworkData], bool]


class PlaystationNetworkBinarySensor(StrEnum):
"""PlayStation Network binary sensors."""

PS_PLUS_STATUS = "ps_plus_status"


BINARY_SENSOR_DESCRIPTIONS: tuple[
PlaystationNetworkBinarySensorEntityDescription, ...
] = (
PlaystationNetworkBinarySensorEntityDescription(
key=PlaystationNetworkBinarySensor.PS_PLUS_STATUS,
translation_key=PlaystationNetworkBinarySensor.PS_PLUS_STATUS,
is_on_fn=lambda psn: psn.profile["isPlus"],
),
)


async def async_setup_entry(
hass: HomeAssistant,
config_entry: PlaystationNetworkConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the binary sensor platform."""
coordinator = config_entry.runtime_data
async_add_entities(
PlaystationNetworkBinarySensorEntity(coordinator, description)
for description in BINARY_SENSOR_DESCRIPTIONS
)


class PlaystationNetworkBinarySensorEntity(
PlaystationNetworkServiceEntity,
BinarySensorEntity,
):
"""Representation of a PlayStation Network binary sensor entity."""

entity_description: PlaystationNetworkBinarySensorEntityDescription

@property
def is_on(self) -> bool:
"""Return the state of the binary sensor."""

return self.entity_description.is_on_fn(self.coordinator.data)
36 changes: 36 additions & 0 deletions homeassistant/components/playstation_network/entity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"""Base entity for PlayStation Network Integration."""

from typing import TYPE_CHECKING

from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
from homeassistant.helpers.entity import EntityDescription
from homeassistant.helpers.update_coordinator import CoordinatorEntity

from .const import DOMAIN
from .coordinator import PlaystationNetworkCoordinator


class PlaystationNetworkServiceEntity(CoordinatorEntity[PlaystationNetworkCoordinator]):
"""Common entity class for PlayStationNetwork Service entities."""

_attr_has_entity_name = True

def __init__(
self,
coordinator: PlaystationNetworkCoordinator,
entity_description: EntityDescription,
) -> None:
"""Initialize PlayStation Network Service Entity."""
super().__init__(coordinator)
if TYPE_CHECKING:
assert coordinator.config_entry.unique_id
self.entity_description = entity_description
self._attr_unique_id = (
f"{coordinator.config_entry.unique_id}_{entity_description.key}"
)
self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, coordinator.config_entry.unique_id)},
name=coordinator.data.username,
entry_type=DeviceEntryType.SERVICE,
manufacturer="Sony Interactive Entertainment",
)
Loading
Loading