Ensure yeelight model is set in the config entry (#55281)

* Ensure yeelight model is set in the config entry

- If the model was not set in the config entry the light could
  be sent commands it could not handle

* update tests

* fix test
pull/55278/head
J. Nick Koston 2021-08-26 13:02:59 -05:00 committed by GitHub
parent f6bb5c77a0
commit 089dfad78a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 58 additions and 17 deletions

View File

@ -196,7 +196,6 @@ async def _async_initialize(
entry_data = hass.data[DOMAIN][DATA_CONFIG_ENTRIES][entry.entry_id] = {
DATA_PLATFORMS_LOADED: False
}
entry.async_on_unload(entry.add_update_listener(_async_update_listener))
@callback
def _async_load_platforms():
@ -212,6 +211,15 @@ async def _async_initialize(
await device.async_setup()
entry_data[DATA_DEVICE] = device
if (
device.capabilities
and entry.options.get(CONF_MODEL) != device.capabilities["model"]
):
hass.config_entries.async_update_entry(
entry, options={**entry.options, CONF_MODEL: device.capabilities["model"]}
)
entry.async_on_unload(entry.add_update_listener(_async_update_listener))
entry.async_on_unload(
async_dispatcher_connect(
hass, DEVICE_INITIALIZED.format(host), _async_load_platforms
@ -540,7 +548,7 @@ class YeelightDevice:
self._config = config
self._host = host
self._bulb_device = bulb
self._capabilities = {}
self.capabilities = {}
self._device_type = None
self._available = False
self._initialized = False
@ -574,12 +582,12 @@ class YeelightDevice:
@property
def model(self):
"""Return configured/autodetected device model."""
return self._bulb_device.model or self._capabilities.get("model")
return self._bulb_device.model or self.capabilities.get("model")
@property
def fw_version(self):
"""Return the firmware version."""
return self._capabilities.get("fw_ver")
return self.capabilities.get("fw_ver")
@property
def is_nightlight_supported(self) -> bool:
@ -674,13 +682,13 @@ class YeelightDevice:
async def async_setup(self):
"""Fetch capabilities and setup name if available."""
scanner = YeelightScanner.async_get(self._hass)
self._capabilities = await scanner.async_get_capabilities(self._host) or {}
self.capabilities = await scanner.async_get_capabilities(self._host) or {}
if name := self._config.get(CONF_NAME):
# Override default name when name is set in config
self._name = name
elif self._capabilities:
elif self.capabilities:
# Generate name from model and id when capabilities is available
self._name = _async_unique_name(self._capabilities)
self._name = _async_unique_name(self.capabilities)
else:
self._name = self._host # Default name is host

View File

@ -96,7 +96,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
if user_input is not None:
return self.async_create_entry(
title=async_format_model_id(self._discovered_model, self.unique_id),
data={CONF_ID: self.unique_id, CONF_HOST: self._discovered_ip},
data={
CONF_ID: self.unique_id,
CONF_HOST: self._discovered_ip,
CONF_MODEL: self._discovered_model,
},
)
self._set_confirm_only()
@ -129,6 +133,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
data={
CONF_HOST: user_input[CONF_HOST],
CONF_ID: self.unique_id,
CONF_MODEL: model,
},
)
@ -151,7 +156,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
host = urlparse(capabilities["location"]).hostname
return self.async_create_entry(
title=_async_unique_name(capabilities),
data={CONF_ID: unique_id, CONF_HOST: host},
data={
CONF_ID: unique_id,
CONF_HOST: host,
CONF_MODEL: capabilities["model"],
},
)
configured_devices = {

View File

@ -19,7 +19,7 @@ from homeassistant.components.yeelight import (
DOMAIN,
NIGHTLIGHT_SWITCH_TYPE_LIGHT,
)
from homeassistant.components.yeelight.config_flow import CannotConnect
from homeassistant.components.yeelight.config_flow import MODEL_UNKNOWN, CannotConnect
from homeassistant.const import CONF_DEVICE, CONF_HOST, CONF_ID, CONF_NAME
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import RESULT_TYPE_ABORT, RESULT_TYPE_FORM
@ -28,6 +28,7 @@ from . import (
CAPABILITIES,
ID,
IP_ADDRESS,
MODEL,
MODULE,
MODULE_CONFIG_FLOW,
NAME,
@ -87,7 +88,7 @@ async def test_discovery(hass: HomeAssistant):
)
assert result3["type"] == "create_entry"
assert result3["title"] == UNIQUE_FRIENDLY_NAME
assert result3["data"] == {CONF_ID: ID, CONF_HOST: IP_ADDRESS}
assert result3["data"] == {CONF_ID: ID, CONF_HOST: IP_ADDRESS, CONF_MODEL: MODEL}
await hass.async_block_till_done()
mock_setup.assert_called_once()
mock_setup_entry.assert_called_once()
@ -160,7 +161,11 @@ async def test_discovery_with_existing_device_present(hass: HomeAssistant):
)
assert result3["type"] == "create_entry"
assert result3["title"] == UNIQUE_FRIENDLY_NAME
assert result3["data"] == {CONF_ID: ID, CONF_HOST: IP_ADDRESS}
assert result3["data"] == {
CONF_ID: ID,
CONF_HOST: IP_ADDRESS,
CONF_MODEL: MODEL,
}
await hass.async_block_till_done()
await hass.async_block_till_done()
@ -300,7 +305,11 @@ async def test_manual(hass: HomeAssistant):
await hass.async_block_till_done()
assert result4["type"] == "create_entry"
assert result4["title"] == "Color 0x15243f"
assert result4["data"] == {CONF_HOST: IP_ADDRESS, CONF_ID: "0x000000000015243f"}
assert result4["data"] == {
CONF_HOST: IP_ADDRESS,
CONF_ID: "0x000000000015243f",
CONF_MODEL: MODEL,
}
# Duplicate
result = await hass.config_entries.flow.async_init(
@ -333,7 +342,7 @@ async def test_options(hass: HomeAssistant):
config = {
CONF_NAME: NAME,
CONF_MODEL: "",
CONF_MODEL: MODEL,
CONF_TRANSITION: DEFAULT_TRANSITION,
CONF_MODE_MUSIC: DEFAULT_MODE_MUSIC,
CONF_SAVE_ON_CHANGE: DEFAULT_SAVE_ON_CHANGE,
@ -383,7 +392,11 @@ async def test_manual_no_capabilities(hass: HomeAssistant):
result["flow_id"], {CONF_HOST: IP_ADDRESS}
)
assert result["type"] == "create_entry"
assert result["data"] == {CONF_HOST: IP_ADDRESS, CONF_ID: None}
assert result["data"] == {
CONF_HOST: IP_ADDRESS,
CONF_ID: None,
CONF_MODEL: MODEL_UNKNOWN,
}
async def test_discovered_by_homekit_and_dhcp(hass):
@ -480,7 +493,11 @@ async def test_discovered_by_dhcp_or_homekit(hass, source, data):
await hass.async_block_till_done()
assert result2["type"] == "create_entry"
assert result2["data"] == {CONF_HOST: IP_ADDRESS, CONF_ID: "0x000000000015243f"}
assert result2["data"] == {
CONF_HOST: IP_ADDRESS,
CONF_ID: "0x000000000015243f",
CONF_MODEL: MODEL,
}
assert mock_async_setup.called
assert mock_async_setup_entry.called
@ -540,7 +557,11 @@ async def test_discovered_ssdp(hass):
await hass.async_block_till_done()
assert result2["type"] == "create_entry"
assert result2["data"] == {CONF_HOST: IP_ADDRESS, CONF_ID: "0x000000000015243f"}
assert result2["data"] == {
CONF_HOST: IP_ADDRESS,
CONF_ID: "0x000000000015243f",
CONF_MODEL: MODEL,
}
assert mock_async_setup.called
assert mock_async_setup_entry.called

View File

@ -5,6 +5,7 @@ from unittest.mock import AsyncMock, patch
from yeelight import BulbException, BulbType
from homeassistant.components.yeelight import (
CONF_MODEL,
CONF_NIGHTLIGHT_SWITCH,
CONF_NIGHTLIGHT_SWITCH_TYPE,
DATA_CONFIG_ENTRIES,
@ -35,6 +36,7 @@ from . import (
FAIL_TO_BIND_IP,
ID,
IP_ADDRESS,
MODEL,
MODULE,
SHORT_ID,
_mocked_bulb,
@ -360,6 +362,7 @@ async def test_async_listen_error_late_discovery(hass, caplog):
assert "Failed to connect to bulb at" not in caplog.text
assert config_entry.state is ConfigEntryState.LOADED
assert config_entry.options[CONF_MODEL] == MODEL
async def test_async_listen_error_has_host_with_id(hass: HomeAssistant):