Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 27516de

Browse files
niraclerNoRi2909joostlek
authored
Add DALI Center integration (home-assistant#151479)
Co-authored-by: Norbert Rittel <norbert@rittel.de> Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
1 parent 40c9e53 commit 27516de

File tree

19 files changed

+1425
-0
lines changed

19 files changed

+1425
-0
lines changed

‎CODEOWNERS‎

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
"""The DALI Center integration."""
2+
3+
from __future__ import annotations
4+
5+
import logging
6+
7+
from PySrDaliGateway import DaliGateway
8+
from PySrDaliGateway.exceptions import DaliGatewayError
9+
10+
from homeassistant.const import (
11+
CONF_HOST,
12+
CONF_NAME,
13+
CONF_PASSWORD,
14+
CONF_PORT,
15+
CONF_USERNAME,
16+
Platform,
17+
)
18+
from homeassistant.core import HomeAssistant
19+
from homeassistant.exceptions import ConfigEntryNotReady
20+
from homeassistant.helpers import device_registry as dr
21+
from homeassistant.helpers.dispatcher import async_dispatcher_send
22+
23+
from .const import CONF_SERIAL_NUMBER, DOMAIN, MANUFACTURER
24+
from .types import DaliCenterConfigEntry, DaliCenterData
25+
26+
_PLATFORMS: list[Platform] = [Platform.LIGHT]
27+
_LOGGER = logging.getLogger(__name__)
28+
29+
30+
async def async_setup_entry(hass: HomeAssistant, entry: DaliCenterConfigEntry) -> bool:
31+
"""Set up DALI Center from a config entry."""
32+
33+
gateway = DaliGateway(
34+
entry.data[CONF_SERIAL_NUMBER],
35+
entry.data[CONF_HOST],
36+
entry.data[CONF_PORT],
37+
entry.data[CONF_USERNAME],
38+
entry.data[CONF_PASSWORD],
39+
name=entry.data[CONF_NAME],
40+
)
41+
gw_sn = gateway.gw_sn
42+
43+
try:
44+
await gateway.connect()
45+
except DaliGatewayError as exc:
46+
raise ConfigEntryNotReady(
47+
"You can try to delete the gateway and add it again"
48+
) from exc
49+
50+
def on_online_status(dev_id: str, available: bool) -> None:
51+
signal = f"{DOMAIN}_update_available_{dev_id}"
52+
hass.add_job(async_dispatcher_send, hass, signal, available)
53+
54+
gateway.on_online_status = on_online_status
55+
56+
try:
57+
devices = await gateway.discover_devices()
58+
except DaliGatewayError as exc:
59+
raise ConfigEntryNotReady(
60+
"Unable to discover devices from the gateway"
61+
) from exc
62+
63+
_LOGGER.debug("Discovered %d devices on gateway %s", len(devices), gw_sn)
64+
65+
dev_reg = dr.async_get(hass)
66+
dev_reg.async_get_or_create(
67+
config_entry_id=entry.entry_id,
68+
identifiers={(DOMAIN, gw_sn)},
69+
manufacturer=MANUFACTURER,
70+
name=gateway.name,
71+
model="SR-GW-EDA",
72+
serial_number=gw_sn,
73+
)
74+
75+
entry.runtime_data = DaliCenterData(
76+
gateway=gateway,
77+
devices=devices,
78+
)
79+
await hass.config_entries.async_forward_entry_setups(entry, _PLATFORMS)
80+
81+
return True
82+
83+
84+
async def async_unload_entry(hass: HomeAssistant, entry: DaliCenterConfigEntry) -> bool:
85+
"""Unload a config entry."""
86+
if unload_ok := await hass.config_entries.async_unload_platforms(entry, _PLATFORMS):
87+
await entry.runtime_data.gateway.disconnect()
88+
return unload_ok
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
"""Config flow for the DALI Center integration."""
2+
3+
from __future__ import annotations
4+
5+
import logging
6+
from typing import Any
7+
8+
from PySrDaliGateway import DaliGateway
9+
from PySrDaliGateway.discovery import DaliGatewayDiscovery
10+
from PySrDaliGateway.exceptions import DaliGatewayError
11+
import voluptuous as vol
12+
13+
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
14+
from homeassistant.const import (
15+
CONF_HOST,
16+
CONF_NAME,
17+
CONF_PASSWORD,
18+
CONF_PORT,
19+
CONF_USERNAME,
20+
)
21+
from homeassistant.helpers.selector import (
22+
SelectOptionDict,
23+
SelectSelector,
24+
SelectSelectorConfig,
25+
)
26+
27+
from .const import CONF_SERIAL_NUMBER, DOMAIN
28+
29+
_LOGGER = logging.getLogger(__name__)
30+
31+
32+
class DaliCenterConfigFlow(ConfigFlow, domain=DOMAIN):
33+
"""Handle a config flow for DALI Center."""
34+
35+
VERSION = 1
36+
37+
def __init__(self) -> None:
38+
"""Initialize the config flow."""
39+
self._discovered_gateways: dict[str, DaliGateway] = {}
40+
41+
async def async_step_user(
42+
self, user_input: dict[str, Any] | None = None
43+
) -> ConfigFlowResult:
44+
"""Handle the initial step."""
45+
if user_input is not None:
46+
return await self.async_step_select_gateway()
47+
48+
return self.async_show_form(
49+
step_id="user",
50+
data_schema=vol.Schema({}),
51+
)
52+
53+
async def async_step_select_gateway(
54+
self, discovery_info: dict[str, Any] | None = None
55+
) -> ConfigFlowResult:
56+
"""Handle gateway discovery."""
57+
errors: dict[str, str] = {}
58+
59+
if discovery_info and "selected_gateway" in discovery_info:
60+
selected_sn = discovery_info["selected_gateway"]
61+
selected_gateway = self._discovered_gateways[selected_sn]
62+
63+
await self.async_set_unique_id(selected_gateway.gw_sn)
64+
self._abort_if_unique_id_configured()
65+
66+
try:
67+
await selected_gateway.connect()
68+
except DaliGatewayError as err:
69+
_LOGGER.debug(
70+
"Failed to connect to gateway %s during config flow",
71+
selected_gateway.gw_sn,
72+
exc_info=err,
73+
)
74+
errors["base"] = "cannot_connect"
75+
else:
76+
await selected_gateway.disconnect()
77+
return self.async_create_entry(
78+
title=selected_gateway.name,
79+
data={
80+
CONF_SERIAL_NUMBER: selected_gateway.gw_sn,
81+
CONF_HOST: selected_gateway.gw_ip,
82+
CONF_PORT: selected_gateway.port,
83+
CONF_NAME: selected_gateway.name,
84+
CONF_USERNAME: selected_gateway.username,
85+
CONF_PASSWORD: selected_gateway.passwd,
86+
},
87+
)
88+
89+
if not self._discovered_gateways:
90+
_LOGGER.debug("Starting gateway discovery")
91+
discovery = DaliGatewayDiscovery()
92+
try:
93+
discovered = await discovery.discover_gateways()
94+
except DaliGatewayError as err:
95+
_LOGGER.debug("Gateway discovery failed", exc_info=err)
96+
errors["base"] = "discovery_failed"
97+
else:
98+
configured_gateways = {
99+
entry.data[CONF_SERIAL_NUMBER]
100+
for entry in self.hass.config_entries.async_entries(DOMAIN)
101+
}
102+
103+
self._discovered_gateways = {
104+
gw.gw_sn: gw
105+
for gw in discovered
106+
if gw.gw_sn not in configured_gateways
107+
}
108+
109+
if not self._discovered_gateways:
110+
return self.async_show_form(
111+
step_id="select_gateway",
112+
errors=errors if errors else {"base": "no_devices_found"},
113+
data_schema=vol.Schema({}),
114+
)
115+
116+
gateway_options = [
117+
SelectOptionDict(
118+
value=sn,
119+
label=f"{gateway.name} [SN {sn}, IP {gateway.gw_ip}]",
120+
)
121+
for sn, gateway in self._discovered_gateways.items()
122+
]
123+
124+
return self.async_show_form(
125+
step_id="select_gateway",
126+
data_schema=vol.Schema(
127+
{
128+
vol.Optional("selected_gateway"): SelectSelector(
129+
SelectSelectorConfig(options=gateway_options, sort=True)
130+
),
131+
}
132+
),
133+
errors=errors,
134+
)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
"""Constants for the DALI Center integration."""
2+
3+
DOMAIN = "sunricher_dali_center"
4+
MANUFACTURER = "Sunricher"
5+
CONF_SERIAL_NUMBER = "serial_number"

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /