mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Add slot list service to ohme (#134170)
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
parent
65147f8d4c
commit
88d366b0c5
@ -8,9 +8,14 @@ from homeassistant.config_entries import ConfigEntry
|
|||||||
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
|
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||||
|
from homeassistant.helpers import config_validation as cv
|
||||||
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
from .const import DOMAIN, PLATFORMS
|
from .const import DOMAIN, PLATFORMS
|
||||||
from .coordinator import OhmeAdvancedSettingsCoordinator, OhmeChargeSessionCoordinator
|
from .coordinator import OhmeAdvancedSettingsCoordinator, OhmeChargeSessionCoordinator
|
||||||
|
from .services import async_setup_services
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
|
||||||
|
|
||||||
type OhmeConfigEntry = ConfigEntry[OhmeRuntimeData]
|
type OhmeConfigEntry = ConfigEntry[OhmeRuntimeData]
|
||||||
|
|
||||||
@ -23,6 +28,13 @@ class OhmeRuntimeData:
|
|||||||
advanced_settings_coordinator: OhmeAdvancedSettingsCoordinator
|
advanced_settings_coordinator: OhmeAdvancedSettingsCoordinator
|
||||||
|
|
||||||
|
|
||||||
|
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||||
|
"""Set up Ohme integration."""
|
||||||
|
async_setup_services(hass)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass: HomeAssistant, entry: OhmeConfigEntry) -> bool:
|
async def async_setup_entry(hass: HomeAssistant, entry: OhmeConfigEntry) -> bool:
|
||||||
"""Set up Ohme from a config entry."""
|
"""Set up Ohme from a config entry."""
|
||||||
|
|
||||||
|
@ -19,5 +19,10 @@
|
|||||||
"default": "mdi:gauge"
|
"default": "mdi:gauge"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"services": {
|
||||||
|
"list_charge_slots": {
|
||||||
|
"service": "mdi:clock-start"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,13 @@
|
|||||||
rules:
|
rules:
|
||||||
# Bronze
|
# Bronze
|
||||||
action-setup:
|
action-setup: done
|
||||||
status: exempt
|
|
||||||
comment: |
|
|
||||||
This integration has no custom actions.
|
|
||||||
appropriate-polling: done
|
appropriate-polling: done
|
||||||
brands: done
|
brands: done
|
||||||
common-modules: done
|
common-modules: done
|
||||||
config-flow-test-coverage: done
|
config-flow-test-coverage: done
|
||||||
config-flow: done
|
config-flow: done
|
||||||
dependency-transparency: done
|
dependency-transparency: done
|
||||||
docs-actions:
|
docs-actions: done
|
||||||
status: exempt
|
|
||||||
comment: |
|
|
||||||
This integration has no custom actions.
|
|
||||||
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
|
||||||
|
75
homeassistant/components/ohme/services.py
Normal file
75
homeassistant/components/ohme/services.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
"""Ohme services."""
|
||||||
|
|
||||||
|
from typing import Final
|
||||||
|
|
||||||
|
from ohme import OhmeApiClient
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
||||||
|
from homeassistant.core import (
|
||||||
|
HomeAssistant,
|
||||||
|
ServiceCall,
|
||||||
|
ServiceResponse,
|
||||||
|
SupportsResponse,
|
||||||
|
)
|
||||||
|
from homeassistant.exceptions import ServiceValidationError
|
||||||
|
from homeassistant.helpers import selector
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
SERVICE_LIST_CHARGE_SLOTS = "list_charge_slots"
|
||||||
|
ATTR_CONFIG_ENTRY: Final = "config_entry"
|
||||||
|
SERVICE_SCHEMA: Final = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(ATTR_CONFIG_ENTRY): selector.ConfigEntrySelector(
|
||||||
|
{
|
||||||
|
"integration": DOMAIN,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def __get_client(call: ServiceCall) -> OhmeApiClient:
|
||||||
|
"""Get the client from the config entry."""
|
||||||
|
entry_id: str = call.data[ATTR_CONFIG_ENTRY]
|
||||||
|
entry: ConfigEntry | None = call.hass.config_entries.async_get_entry(entry_id)
|
||||||
|
|
||||||
|
if not entry:
|
||||||
|
raise ServiceValidationError(
|
||||||
|
translation_domain=DOMAIN,
|
||||||
|
translation_key="invalid_config_entry",
|
||||||
|
translation_placeholders={
|
||||||
|
"config_entry": entry_id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if entry.state != ConfigEntryState.LOADED:
|
||||||
|
raise ServiceValidationError(
|
||||||
|
translation_domain=DOMAIN,
|
||||||
|
translation_key="unloaded_config_entry",
|
||||||
|
translation_placeholders={
|
||||||
|
"config_entry": entry.title,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
return entry.runtime_data.charge_session_coordinator.client
|
||||||
|
|
||||||
|
|
||||||
|
def async_setup_services(hass: HomeAssistant) -> None:
|
||||||
|
"""Register services."""
|
||||||
|
|
||||||
|
async def list_charge_slots(
|
||||||
|
service_call: ServiceCall,
|
||||||
|
) -> ServiceResponse:
|
||||||
|
"""List of charge slots."""
|
||||||
|
client = __get_client(service_call)
|
||||||
|
|
||||||
|
return {"slots": client.slots}
|
||||||
|
|
||||||
|
hass.services.async_register(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_LIST_CHARGE_SLOTS,
|
||||||
|
list_charge_slots,
|
||||||
|
schema=SERVICE_SCHEMA,
|
||||||
|
supports_response=SupportsResponse.ONLY,
|
||||||
|
)
|
7
homeassistant/components/ohme/services.yaml
Normal file
7
homeassistant/components/ohme/services.yaml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
list_charge_slots:
|
||||||
|
fields:
|
||||||
|
config_entry:
|
||||||
|
required: true
|
||||||
|
selector:
|
||||||
|
config_entry:
|
||||||
|
integration: ohme
|
@ -32,6 +32,18 @@
|
|||||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"services": {
|
||||||
|
"list_charge_slots": {
|
||||||
|
"name": "List charge slots",
|
||||||
|
"description": "Return a list of charge slots.",
|
||||||
|
"fields": {
|
||||||
|
"config_entry": {
|
||||||
|
"name": "Ohme account",
|
||||||
|
"description": "The Ohme config entry for which to return charge slots."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"entity": {
|
"entity": {
|
||||||
"button": {
|
"button": {
|
||||||
"approve": {
|
"approve": {
|
||||||
@ -62,6 +74,12 @@
|
|||||||
},
|
},
|
||||||
"api_failed": {
|
"api_failed": {
|
||||||
"message": "Error communicating with Ohme API"
|
"message": "Error communicating with Ohme API"
|
||||||
|
},
|
||||||
|
"invalid_config_entry": {
|
||||||
|
"message": "Invalid config entry provided. Got {config_entry}"
|
||||||
|
},
|
||||||
|
"unloaded_config_entry": {
|
||||||
|
"message": "Invalid config entry provided. {config_entry} is not loaded."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,7 @@ def mock_client():
|
|||||||
client.async_login.return_value = True
|
client.async_login.return_value = True
|
||||||
client.status = ChargerStatus.CHARGING
|
client.status = ChargerStatus.CHARGING
|
||||||
client.power = ChargerPower(0, 0, 0, 0)
|
client.power = ChargerPower(0, 0, 0, 0)
|
||||||
|
|
||||||
client.serial = "chargerid"
|
client.serial = "chargerid"
|
||||||
client.ct_connected = True
|
client.ct_connected = True
|
||||||
client.energy = 1000
|
client.energy = 1000
|
||||||
|
12
tests/components/ohme/snapshots/test_services.ambr
Normal file
12
tests/components/ohme/snapshots/test_services.ambr
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# serializer version: 1
|
||||||
|
# name: test_list_charge_slots
|
||||||
|
dict({
|
||||||
|
'slots': list([
|
||||||
|
dict({
|
||||||
|
'end': '2024-12-30T04:30:39+00:00',
|
||||||
|
'energy': 2.042,
|
||||||
|
'start': '2024-12-30T04:00:00+00:00',
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
# ---
|
70
tests/components/ohme/test_services.py
Normal file
70
tests/components/ohme/test_services.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
"""Tests for services."""
|
||||||
|
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from syrupy.assertion import SnapshotAssertion
|
||||||
|
|
||||||
|
from homeassistant.components.ohme.const import DOMAIN
|
||||||
|
from homeassistant.components.ohme.services import (
|
||||||
|
ATTR_CONFIG_ENTRY,
|
||||||
|
SERVICE_LIST_CHARGE_SLOTS,
|
||||||
|
)
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import ServiceValidationError
|
||||||
|
|
||||||
|
from . import setup_integration
|
||||||
|
|
||||||
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
|
async def test_list_charge_slots(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_client: MagicMock,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
) -> None:
|
||||||
|
"""Test list charge slots service."""
|
||||||
|
|
||||||
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
|
mock_client.slots = [
|
||||||
|
{
|
||||||
|
"start": "2024-12-30T04:00:00+00:00",
|
||||||
|
"end": "2024-12-30T04:30:39+00:00",
|
||||||
|
"energy": 2.042,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
assert snapshot == await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
"list_charge_slots",
|
||||||
|
{
|
||||||
|
ATTR_CONFIG_ENTRY: mock_config_entry.entry_id,
|
||||||
|
},
|
||||||
|
blocking=True,
|
||||||
|
return_response=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_list_charge_slots_exception(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_config_entry: MockConfigEntry,
|
||||||
|
mock_client: MagicMock,
|
||||||
|
snapshot: SnapshotAssertion,
|
||||||
|
) -> None:
|
||||||
|
"""Test list charge slots service."""
|
||||||
|
|
||||||
|
await setup_integration(hass, mock_config_entry)
|
||||||
|
|
||||||
|
# Test error
|
||||||
|
with pytest.raises(
|
||||||
|
ServiceValidationError, match="Invalid config entry provided. Got invalid"
|
||||||
|
):
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_LIST_CHARGE_SLOTS,
|
||||||
|
{ATTR_CONFIG_ENTRY: "invalid"},
|
||||||
|
blocking=True,
|
||||||
|
return_response=True,
|
||||||
|
)
|
Loading…
x
Reference in New Issue
Block a user