1 """Support for Velbus devices."""
3 from __future__
import annotations
5 from contextlib
import suppress
10 from velbusaio.controller
import Velbus
11 import voluptuous
as vol
26 SERVICE_SET_MEMO_TEXT,
30 _LOGGER = logging.getLogger(__name__)
33 Platform.BINARY_SENSOR,
45 controller: Velbus, hass: HomeAssistant, entry_id: str
47 """Task to offload the long running connect."""
49 await controller.connect()
50 except ConnectionError
as ex:
52 f
"Connection error while connecting to Velbus {entry_id}: {ex}"
57 """Migrate old device identifiers."""
58 dev_reg = dr.async_get(hass)
59 devices: list[dr.DeviceEntry] = dr.async_entries_for_config_entry(dev_reg, entry_id)
60 for device
in devices:
61 old_identifier =
list(next(iter(device.identifiers)))
62 if len(old_identifier) > 2:
63 new_identifier = {(old_identifier.pop(0), old_identifier.pop(0))}
65 "migrate identifier '%s' to '%s'", device.identifiers, new_identifier
67 dev_reg.async_update_device(device.id, new_identifiers=new_identifier)
71 """Establish connection with velbus."""
72 hass.data.setdefault(DOMAIN, {})
75 entry.data[CONF_PORT],
76 cache_dir=hass.config.path(STORAGE_DIR, f
"velbuscache-{entry.entry_id}"),
78 hass.data[DOMAIN][entry.entry_id] = {}
79 hass.data[DOMAIN][entry.entry_id][
"cntrl"] = controller
80 hass.data[DOMAIN][entry.entry_id][
"tsk"] = hass.async_create_task(
86 await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
88 if hass.services.has_service(DOMAIN, SERVICE_SCAN):
91 def check_entry_id(interface: str) -> str:
92 for config_entry
in hass.config_entries.async_entries(DOMAIN):
93 if "port" in config_entry.data
and config_entry.data[
"port"] == interface:
94 return config_entry.entry_id
96 "The interface provided is not defined as a port in a Velbus integration"
99 async
def scan(call: ServiceCall) ->
None:
100 await hass.data[DOMAIN][call.data[CONF_INTERFACE]][
"cntrl"].scan()
102 hass.services.async_register(
106 vol.Schema({vol.Required(CONF_INTERFACE): vol.All(cv.string, check_entry_id)}),
109 async
def syn_clock(call: ServiceCall) ->
None:
110 await hass.data[DOMAIN][call.data[CONF_INTERFACE]][
"cntrl"].sync_clock()
112 hass.services.async_register(
116 vol.Schema({vol.Required(CONF_INTERFACE): vol.All(cv.string, check_entry_id)}),
119 async
def set_memo_text(call: ServiceCall) ->
None:
120 """Handle Memo Text service call."""
121 memo_text = call.data[CONF_MEMO_TEXT]
123 hass.data[DOMAIN][call.data[CONF_INTERFACE]][
"cntrl"]
124 .get_module(call.data[CONF_ADDRESS])
125 .set_memo_text(memo_text)
128 hass.services.async_register(
130 SERVICE_SET_MEMO_TEXT,
134 vol.Required(CONF_INTERFACE): vol.All(cv.string, check_entry_id),
135 vol.Required(CONF_ADDRESS): vol.All(
136 vol.Coerce(int), vol.Range(min=0, max=255)
138 vol.Optional(CONF_MEMO_TEXT, default=
""): cv.string,
143 async
def clear_cache(call: ServiceCall) ->
None:
144 """Handle a clear cache service call."""
146 with suppress(FileNotFoundError):
147 if call.data.get(CONF_ADDRESS):
148 await hass.async_add_executor_job(
152 f
"velbuscache-{call.data[CONF_INTERFACE]}/{call.data[CONF_ADDRESS]}.p",
156 await hass.async_add_executor_job(
159 STORAGE_DIR, f
"velbuscache-{call.data[CONF_INTERFACE]}/"
165 hass.services.async_register(
171 vol.Required(CONF_INTERFACE): vol.All(cv.string, check_entry_id),
172 vol.Optional(CONF_ADDRESS): vol.All(
173 vol.Coerce(int), vol.Range(min=0, max=255)
183 """Unload (close) the velbus connection."""
184 unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
185 await hass.data[DOMAIN][entry.entry_id][
"cntrl"].stop()
186 hass.data[DOMAIN].pop(entry.entry_id)
187 if not hass.data[DOMAIN]:
188 hass.data.pop(DOMAIN)
189 hass.services.async_remove(DOMAIN, SERVICE_SCAN)
190 hass.services.async_remove(DOMAIN, SERVICE_SYNC)
191 hass.services.async_remove(DOMAIN, SERVICE_SET_MEMO_TEXT)
192 hass.services.async_remove(DOMAIN, SERVICE_CLEAR_CACHE)
197 """Remove the velbus entry, so we also have to cleanup the cache dir."""
198 await hass.async_add_executor_job(
200 hass.config.path(STORAGE_DIR, f
"velbuscache-{entry.entry_id}"),
205 """Migrate old entry."""
206 _LOGGER.debug(
"Migrating from version %s", config_entry.version)
207 cache_path = hass.config.path(STORAGE_DIR, f
"velbuscache-{config_entry.entry_id}/")
208 if config_entry.version == 1:
211 if os.path.isdir(cache_path):
212 await hass.async_add_executor_job(shutil.rmtree, cache_path)
214 hass.config_entries.async_update_entry(config_entry, version=2)
216 _LOGGER.debug(
"Migration to version %s successful", config_entry.version)
bool async_migrate_entry(HomeAssistant hass, ConfigEntry config_entry)
None velbus_connect_task(Velbus controller, HomeAssistant hass, str entry_id)
None async_remove_entry(HomeAssistant hass, ConfigEntry entry)
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
None _migrate_device_identifiers(HomeAssistant hass, str entry_id)
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)