Split the velbus services code in its own file (#131375)

pull/132943/head
Maikel Punie 2024-12-11 16:41:48 +01:00 committed by GitHub
parent 0d71828def
commit 00ab5db661
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 130 additions and 109 deletions

View File

@ -2,30 +2,22 @@
from __future__ import annotations from __future__ import annotations
from contextlib import suppress
import logging import logging
import os import os
import shutil import shutil
from velbusaio.controller import Velbus from velbusaio.controller import Velbus
import voluptuous as vol
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_ADDRESS, CONF_PORT, Platform from homeassistant.const import CONF_PORT, Platform
from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.core import HomeAssistant
from homeassistant.exceptions import PlatformNotReady from homeassistant.exceptions import PlatformNotReady
from homeassistant.helpers import config_validation as cv, device_registry as dr from homeassistant.helpers import config_validation as cv, device_registry as dr
from homeassistant.helpers.storage import STORAGE_DIR from homeassistant.helpers.storage import STORAGE_DIR
from homeassistant.helpers.typing import ConfigType
from .const import ( from .const import DOMAIN
CONF_INTERFACE, from .services import setup_services
CONF_MEMO_TEXT,
DOMAIN,
SERVICE_CLEAR_CACHE,
SERVICE_SCAN,
SERVICE_SET_MEMO_TEXT,
SERVICE_SYNC,
)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -40,6 +32,8 @@ PLATFORMS = [
Platform.SWITCH, Platform.SWITCH,
] ]
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
async def velbus_connect_task( async def velbus_connect_task(
controller: Velbus, hass: HomeAssistant, entry_id: str controller: Velbus, hass: HomeAssistant, entry_id: str
@ -67,6 +61,12 @@ def _migrate_device_identifiers(hass: HomeAssistant, entry_id: str) -> None:
dev_reg.async_update_device(device.id, new_identifiers=new_identifier) dev_reg.async_update_device(device.id, new_identifiers=new_identifier)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the actions for the Velbus component."""
setup_services(hass)
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Establish connection with velbus.""" """Establish connection with velbus."""
hass.data.setdefault(DOMAIN, {}) hass.data.setdefault(DOMAIN, {})
@ -85,97 +85,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
if hass.services.has_service(DOMAIN, SERVICE_SCAN):
return True
def check_entry_id(interface: str) -> str:
for config_entry in hass.config_entries.async_entries(DOMAIN):
if "port" in config_entry.data and config_entry.data["port"] == interface:
return config_entry.entry_id
raise vol.Invalid(
"The interface provided is not defined as a port in a Velbus integration"
)
async def scan(call: ServiceCall) -> None:
await hass.data[DOMAIN][call.data[CONF_INTERFACE]]["cntrl"].scan()
hass.services.async_register(
DOMAIN,
SERVICE_SCAN,
scan,
vol.Schema({vol.Required(CONF_INTERFACE): vol.All(cv.string, check_entry_id)}),
)
async def syn_clock(call: ServiceCall) -> None:
await hass.data[DOMAIN][call.data[CONF_INTERFACE]]["cntrl"].sync_clock()
hass.services.async_register(
DOMAIN,
SERVICE_SYNC,
syn_clock,
vol.Schema({vol.Required(CONF_INTERFACE): vol.All(cv.string, check_entry_id)}),
)
async def set_memo_text(call: ServiceCall) -> None:
"""Handle Memo Text service call."""
memo_text = call.data[CONF_MEMO_TEXT]
await (
hass.data[DOMAIN][call.data[CONF_INTERFACE]]["cntrl"]
.get_module(call.data[CONF_ADDRESS])
.set_memo_text(memo_text)
)
hass.services.async_register(
DOMAIN,
SERVICE_SET_MEMO_TEXT,
set_memo_text,
vol.Schema(
{
vol.Required(CONF_INTERFACE): vol.All(cv.string, check_entry_id),
vol.Required(CONF_ADDRESS): vol.All(
vol.Coerce(int), vol.Range(min=0, max=255)
),
vol.Optional(CONF_MEMO_TEXT, default=""): cv.string,
}
),
)
async def clear_cache(call: ServiceCall) -> None:
"""Handle a clear cache service call."""
# clear the cache
with suppress(FileNotFoundError):
if call.data.get(CONF_ADDRESS):
await hass.async_add_executor_job(
os.unlink,
hass.config.path(
STORAGE_DIR,
f"velbuscache-{call.data[CONF_INTERFACE]}/{call.data[CONF_ADDRESS]}.p",
),
)
else:
await hass.async_add_executor_job(
shutil.rmtree,
hass.config.path(
STORAGE_DIR, f"velbuscache-{call.data[CONF_INTERFACE]}/"
),
)
# call a scan to repopulate
await scan(call)
hass.services.async_register(
DOMAIN,
SERVICE_CLEAR_CACHE,
clear_cache,
vol.Schema(
{
vol.Required(CONF_INTERFACE): vol.All(cv.string, check_entry_id),
vol.Optional(CONF_ADDRESS): vol.All(
vol.Coerce(int), vol.Range(min=0, max=255)
),
}
),
)
return True return True
@ -186,10 +95,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.data[DOMAIN].pop(entry.entry_id) hass.data[DOMAIN].pop(entry.entry_id)
if not hass.data[DOMAIN]: if not hass.data[DOMAIN]:
hass.data.pop(DOMAIN) hass.data.pop(DOMAIN)
hass.services.async_remove(DOMAIN, SERVICE_SCAN)
hass.services.async_remove(DOMAIN, SERVICE_SYNC)
hass.services.async_remove(DOMAIN, SERVICE_SET_MEMO_TEXT)
hass.services.async_remove(DOMAIN, SERVICE_CLEAR_CACHE)
return unload_ok return unload_ok

View File

@ -1,6 +1,6 @@
rules: rules:
# Bronze # Bronze
action-setup: todo action-setup: done
appropriate-polling: appropriate-polling:
status: exempt status: exempt
comment: | comment: |

View File

@ -0,0 +1,116 @@
"""Support for Velbus devices."""
from __future__ import annotations
from contextlib import suppress
import os
import shutil
import voluptuous as vol
from homeassistant.const import CONF_ADDRESS
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.storage import STORAGE_DIR
from .const import (
CONF_INTERFACE,
CONF_MEMO_TEXT,
DOMAIN,
SERVICE_CLEAR_CACHE,
SERVICE_SCAN,
SERVICE_SET_MEMO_TEXT,
SERVICE_SYNC,
)
def setup_services(hass: HomeAssistant) -> None:
"""Register the velbus services."""
def check_entry_id(interface: str) -> str:
for config_entry in hass.config_entries.async_entries(DOMAIN):
if "port" in config_entry.data and config_entry.data["port"] == interface:
return config_entry.entry_id
raise vol.Invalid(
"The interface provided is not defined as a port in a Velbus integration"
)
async def scan(call: ServiceCall) -> None:
await hass.data[DOMAIN][call.data[CONF_INTERFACE]]["cntrl"].scan()
async def syn_clock(call: ServiceCall) -> None:
await hass.data[DOMAIN][call.data[CONF_INTERFACE]]["cntrl"].sync_clock()
async def set_memo_text(call: ServiceCall) -> None:
"""Handle Memo Text service call."""
memo_text = call.data[CONF_MEMO_TEXT]
await (
hass.data[DOMAIN][call.data[CONF_INTERFACE]]["cntrl"]
.get_module(call.data[CONF_ADDRESS])
.set_memo_text(memo_text.async_render())
)
async def clear_cache(call: ServiceCall) -> None:
"""Handle a clear cache service call."""
# clear the cache
with suppress(FileNotFoundError):
if call.data.get(CONF_ADDRESS):
await hass.async_add_executor_job(
os.unlink,
hass.config.path(
STORAGE_DIR,
f"velbuscache-{call.data[CONF_INTERFACE]}/{call.data[CONF_ADDRESS]}.p",
),
)
else:
await hass.async_add_executor_job(
shutil.rmtree,
hass.config.path(
STORAGE_DIR, f"velbuscache-{call.data[CONF_INTERFACE]}/"
),
)
# call a scan to repopulate
await scan(call)
hass.services.async_register(
DOMAIN,
SERVICE_SCAN,
scan,
vol.Schema({vol.Required(CONF_INTERFACE): vol.All(cv.string, check_entry_id)}),
)
hass.services.async_register(
DOMAIN,
SERVICE_SYNC,
syn_clock,
vol.Schema({vol.Required(CONF_INTERFACE): vol.All(cv.string, check_entry_id)}),
)
hass.services.async_register(
DOMAIN,
SERVICE_SET_MEMO_TEXT,
set_memo_text,
vol.Schema(
{
vol.Required(CONF_INTERFACE): vol.All(cv.string, check_entry_id),
vol.Required(CONF_ADDRESS): vol.All(
vol.Coerce(int), vol.Range(min=0, max=255)
),
vol.Optional(CONF_MEMO_TEXT, default=""): cv.template,
}
),
)
hass.services.async_register(
DOMAIN,
SERVICE_CLEAR_CACHE,
clear_cache,
vol.Schema(
{
vol.Required(CONF_INTERFACE): vol.All(cv.string, check_entry_id),
vol.Optional(CONF_ADDRESS): vol.All(
vol.Coerce(int), vol.Range(min=0, max=255)
),
}
),
)