mirror of
https://github.com/home-assistant/core.git
synced 2025-07-08 05:47:10 +00:00
Re-configuration possibilities for Synology DSM (#53285)
* add automated host/ip reconfig via SSDP * add reconfig of existing entry * adjust tests * adjust tests again * use self._async_current_entries() * _async_get_existing_entry() * apply suggestions
This commit is contained in:
parent
979797136a
commit
13cc671844
@ -228,14 +228,14 @@ class SynologyDSMFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||||||
if user_input.get(CONF_VOLUMES):
|
if user_input.get(CONF_VOLUMES):
|
||||||
config_data[CONF_VOLUMES] = user_input[CONF_VOLUMES]
|
config_data[CONF_VOLUMES] = user_input[CONF_VOLUMES]
|
||||||
|
|
||||||
if existing_entry and self.reauth_conf:
|
if existing_entry:
|
||||||
self.hass.config_entries.async_update_entry(
|
self.hass.config_entries.async_update_entry(
|
||||||
existing_entry, data=config_data
|
existing_entry, data=config_data
|
||||||
)
|
)
|
||||||
await self.hass.config_entries.async_reload(existing_entry.entry_id)
|
await self.hass.config_entries.async_reload(existing_entry.entry_id)
|
||||||
return self.async_abort(reason="reauth_successful")
|
if self.reauth_conf:
|
||||||
if existing_entry:
|
return self.async_abort(reason="reauth_successful")
|
||||||
return self.async_abort(reason="already_configured")
|
return self.async_abort(reason="reconfigure_successful")
|
||||||
|
|
||||||
return self.async_create_entry(title=host, data=config_data)
|
return self.async_create_entry(title=host, data=config_data)
|
||||||
|
|
||||||
@ -246,14 +246,26 @@ class SynologyDSMFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||||||
discovery_info[ssdp.ATTR_UPNP_FRIENDLY_NAME].split("(", 1)[0].strip()
|
discovery_info[ssdp.ATTR_UPNP_FRIENDLY_NAME].split("(", 1)[0].strip()
|
||||||
)
|
)
|
||||||
|
|
||||||
mac = discovery_info[ssdp.ATTR_UPNP_SERIAL].upper()
|
discovered_mac = discovery_info[ssdp.ATTR_UPNP_SERIAL].upper()
|
||||||
# Synology NAS can broadcast on multiple IP addresses, since they can be connected to multiple ethernets.
|
# Synology NAS can broadcast on multiple IP addresses, since they can be connected to multiple ethernets.
|
||||||
# The serial of the NAS is actually its MAC address.
|
# The serial of the NAS is actually its MAC address.
|
||||||
if self._mac_already_configured(mac):
|
|
||||||
return self.async_abort(reason="already_configured")
|
|
||||||
|
|
||||||
await self.async_set_unique_id(mac)
|
existing_entry = self._async_get_existing_entry(discovered_mac)
|
||||||
self._abort_if_unique_id_configured()
|
|
||||||
|
if existing_entry and existing_entry.data[CONF_HOST] != parsed_url.hostname:
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Update host from '%s' to '%s' for NAS '%s' via SSDP discovery",
|
||||||
|
existing_entry.data[CONF_HOST],
|
||||||
|
parsed_url.hostname,
|
||||||
|
existing_entry.unique_id,
|
||||||
|
)
|
||||||
|
self.hass.config_entries.async_update_entry(
|
||||||
|
existing_entry,
|
||||||
|
data={**existing_entry.data, CONF_HOST: parsed_url.hostname},
|
||||||
|
)
|
||||||
|
return self.async_abort(reason="reconfigure_successful")
|
||||||
|
if existing_entry:
|
||||||
|
return self.async_abort(reason="already_configured")
|
||||||
|
|
||||||
self.discovered_conf = {
|
self.discovered_conf = {
|
||||||
CONF_NAME: friendly_name,
|
CONF_NAME: friendly_name,
|
||||||
@ -295,14 +307,14 @@ class SynologyDSMFlowHandler(ConfigFlow, domain=DOMAIN):
|
|||||||
|
|
||||||
return await self.async_step_user(user_input)
|
return await self.async_step_user(user_input)
|
||||||
|
|
||||||
def _mac_already_configured(self, mac: str) -> bool:
|
def _async_get_existing_entry(self, discovered_mac: str) -> ConfigEntry | None:
|
||||||
"""See if we already have configured a NAS with this MAC address."""
|
"""See if we already have a configured NAS with this MAC address."""
|
||||||
existing_macs = [
|
for entry in self._async_current_entries():
|
||||||
mac.replace("-", "")
|
if discovered_mac in [
|
||||||
for entry in self._async_current_entries()
|
mac.replace("-", "") for mac in entry.data.get(CONF_MAC, [])
|
||||||
for mac in entry.data.get(CONF_MAC, [])
|
]:
|
||||||
]
|
return entry
|
||||||
return mac in existing_macs
|
return None
|
||||||
|
|
||||||
|
|
||||||
class SynologyDSMOptionsFlowHandler(OptionsFlow):
|
class SynologyDSMOptionsFlowHandler(OptionsFlow):
|
||||||
|
@ -48,7 +48,8 @@
|
|||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
|
||||||
|
"reconfigure_successful": "Re-configuration was successful"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
"config": {
|
"config": {
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "Ger\u00e4t ist bereits konfiguriert",
|
"already_configured": "Ger\u00e4t ist bereits konfiguriert",
|
||||||
"reauth_successful": "Die erneute Authentifizierung war erfolgreich"
|
"reauth_successful": "Die erneute Authentifizierung war erfolgreich",
|
||||||
|
"reconfigure_successful": "Die Anpassung der Konfiguration war erfolgreich"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"cannot_connect": "Verbindung fehlgeschlagen",
|
"cannot_connect": "Verbindung fehlgeschlagen",
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
"config": {
|
"config": {
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "Device is already configured",
|
"already_configured": "Device is already configured",
|
||||||
"reauth_successful": "Re-authentication was successful"
|
"reauth_successful": "Re-authentication was successful",
|
||||||
|
"reconfigure_successful": "Re-configuration was successful"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"cannot_connect": "Failed to connect",
|
"cannot_connect": "Failed to connect",
|
||||||
|
@ -305,22 +305,29 @@ async def test_reauth(hass: HomeAssistant, service: MagicMock):
|
|||||||
assert result["reason"] == "reauth_successful"
|
assert result["reason"] == "reauth_successful"
|
||||||
|
|
||||||
|
|
||||||
async def test_abort_if_already_setup(hass: HomeAssistant, service: MagicMock):
|
async def test_reconfig_user(hass: HomeAssistant, service: MagicMock):
|
||||||
"""Test we abort if the account is already setup."""
|
"""Test re-configuration of already existing entry by user."""
|
||||||
MockConfigEntry(
|
MockConfigEntry(
|
||||||
domain=DOMAIN,
|
domain=DOMAIN,
|
||||||
data={CONF_HOST: HOST, CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD},
|
data={
|
||||||
|
CONF_HOST: "wrong_host",
|
||||||
|
CONF_USERNAME: USERNAME,
|
||||||
|
CONF_PASSWORD: PASSWORD,
|
||||||
|
},
|
||||||
unique_id=SERIAL,
|
unique_id=SERIAL,
|
||||||
).add_to_hass(hass)
|
).add_to_hass(hass)
|
||||||
|
|
||||||
# Should fail, same HOST:PORT (flow)
|
with patch(
|
||||||
result = await hass.config_entries.flow.async_init(
|
"homeassistant.config_entries.ConfigEntries.async_reload",
|
||||||
DOMAIN,
|
return_value=True,
|
||||||
context={"source": SOURCE_USER},
|
):
|
||||||
data={CONF_HOST: HOST, CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD},
|
result = await hass.config_entries.flow.async_init(
|
||||||
)
|
DOMAIN,
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
context={"source": SOURCE_USER},
|
||||||
assert result["reason"] == "already_configured"
|
data={CONF_HOST: HOST, CONF_USERNAME: USERNAME, CONF_PASSWORD: PASSWORD},
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
|
assert result["reason"] == "reconfigure_successful"
|
||||||
|
|
||||||
|
|
||||||
async def test_login_failed(hass: HomeAssistant, service: MagicMock):
|
async def test_login_failed(hass: HomeAssistant, service: MagicMock):
|
||||||
@ -379,33 +386,6 @@ async def test_missing_data_after_login(hass: HomeAssistant, service_failed: Mag
|
|||||||
assert result["errors"] == {"base": "missing_data"}
|
assert result["errors"] == {"base": "missing_data"}
|
||||||
|
|
||||||
|
|
||||||
async def test_form_ssdp_already_configured(hass: HomeAssistant, service: MagicMock):
|
|
||||||
"""Test ssdp abort when the serial number is already configured."""
|
|
||||||
await setup.async_setup_component(hass, "persistent_notification", {})
|
|
||||||
|
|
||||||
MockConfigEntry(
|
|
||||||
domain=DOMAIN,
|
|
||||||
data={
|
|
||||||
CONF_HOST: HOST,
|
|
||||||
CONF_USERNAME: USERNAME,
|
|
||||||
CONF_PASSWORD: PASSWORD,
|
|
||||||
CONF_MAC: MACS,
|
|
||||||
},
|
|
||||||
unique_id=SERIAL,
|
|
||||||
).add_to_hass(hass)
|
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
|
||||||
DOMAIN,
|
|
||||||
context={"source": SOURCE_SSDP},
|
|
||||||
data={
|
|
||||||
ssdp.ATTR_SSDP_LOCATION: "http://192.168.1.5:5000",
|
|
||||||
ssdp.ATTR_UPNP_FRIENDLY_NAME: "mydsm",
|
|
||||||
ssdp.ATTR_UPNP_SERIAL: "001132XXXX59", # Existing in MACS[0], but SSDP does not have `-`
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
|
||||||
|
|
||||||
|
|
||||||
async def test_form_ssdp(hass: HomeAssistant, service: MagicMock):
|
async def test_form_ssdp(hass: HomeAssistant, service: MagicMock):
|
||||||
"""Test we can setup from ssdp."""
|
"""Test we can setup from ssdp."""
|
||||||
await setup.async_setup_component(hass, "persistent_notification", {})
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
@ -442,6 +422,62 @@ async def test_form_ssdp(hass: HomeAssistant, service: MagicMock):
|
|||||||
assert result["data"].get(CONF_VOLUMES) is None
|
assert result["data"].get(CONF_VOLUMES) is None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_reconfig_ssdp(hass: HomeAssistant, service: MagicMock):
|
||||||
|
"""Test re-configuration of already existing entry by ssdp."""
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={
|
||||||
|
CONF_HOST: "wrong_host",
|
||||||
|
CONF_USERNAME: USERNAME,
|
||||||
|
CONF_PASSWORD: PASSWORD,
|
||||||
|
CONF_MAC: MACS,
|
||||||
|
},
|
||||||
|
unique_id=SERIAL,
|
||||||
|
).add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_SSDP},
|
||||||
|
data={
|
||||||
|
ssdp.ATTR_SSDP_LOCATION: "http://192.168.1.5:5000",
|
||||||
|
ssdp.ATTR_UPNP_FRIENDLY_NAME: "mydsm",
|
||||||
|
ssdp.ATTR_UPNP_SERIAL: "001132XXXX59", # Existing in MACS[0], but SSDP does not have `-`
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
|
assert result["reason"] == "reconfigure_successful"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_existing_ssdp(hass: HomeAssistant, service: MagicMock):
|
||||||
|
"""Test abort of already existing entry by ssdp."""
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
|
||||||
|
MockConfigEntry(
|
||||||
|
domain=DOMAIN,
|
||||||
|
data={
|
||||||
|
CONF_HOST: "192.168.1.5",
|
||||||
|
CONF_USERNAME: USERNAME,
|
||||||
|
CONF_PASSWORD: PASSWORD,
|
||||||
|
CONF_MAC: MACS,
|
||||||
|
},
|
||||||
|
unique_id=SERIAL,
|
||||||
|
).add_to_hass(hass)
|
||||||
|
|
||||||
|
result = await hass.config_entries.flow.async_init(
|
||||||
|
DOMAIN,
|
||||||
|
context={"source": SOURCE_SSDP},
|
||||||
|
data={
|
||||||
|
ssdp.ATTR_SSDP_LOCATION: "http://192.168.1.5:5000",
|
||||||
|
ssdp.ATTR_UPNP_FRIENDLY_NAME: "mydsm",
|
||||||
|
ssdp.ATTR_UPNP_SERIAL: "001132XXXX59", # Existing in MACS[0], but SSDP does not have `-`
|
||||||
|
},
|
||||||
|
)
|
||||||
|
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
|
||||||
|
assert result["reason"] == "already_configured"
|
||||||
|
|
||||||
|
|
||||||
async def test_options_flow(hass: HomeAssistant, service: MagicMock):
|
async def test_options_flow(hass: HomeAssistant, service: MagicMock):
|
||||||
"""Test config flow options."""
|
"""Test config flow options."""
|
||||||
config_entry = MockConfigEntry(
|
config_entry = MockConfigEntry(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user