diff --git a/homeassistant/components/flux_led/__init__.py b/homeassistant/components/flux_led/__init__.py index 2b1e983cc92..6c126ec633a 100644 --- a/homeassistant/components/flux_led/__init__.py +++ b/homeassistant/components/flux_led/__init__.py @@ -32,6 +32,7 @@ from .const import ( ) from .coordinator import FluxLedUpdateCoordinator from .discovery import ( + async_build_cached_discovery, async_clear_discovery_cache, async_discover_device, async_discover_devices, @@ -84,11 +85,14 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Flux LED/MagicLight from a config entry.""" host = entry.data[CONF_HOST] - directed_discovery = None + discovery_cached = True if discovery := async_get_discovery(hass, host): - directed_discovery = False + discovery_cached = False + else: + discovery = async_build_cached_discovery(entry) device: AIOWifiLedBulb = async_wifi_bulb_for_host(host, discovery=discovery) signal = SIGNAL_STATE_UPDATED.format(device.ipaddr) + device.discovery = discovery @callback def _async_state_changed(*_: Any) -> None: @@ -103,23 +107,23 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ) from ex # UDP probe after successful connect only - if not discovery and (discovery := await async_discover_device(hass, host)): - directed_discovery = True + if discovery_cached: + if directed_discovery := await async_discover_device(hass, host): + device.discovery = discovery = directed_discovery + discovery_cached = False - if discovery: - if entry.unique_id: - assert discovery[ATTR_ID] is not None - mac = dr.format_mac(cast(str, discovery[ATTR_ID])) - if mac != entry.unique_id: - # The device is offline and another flux_led device is now using the ip address - raise ConfigEntryNotReady( - f"Unexpected device found at {host}; Expected {entry.unique_id}, found {mac}" - ) - if directed_discovery: - # Only update the entry once we have verified the unique id - # is either missing or we have verified it matches - async_update_entry_from_discovery(hass, entry, discovery) - device.discovery = discovery + if entry.unique_id and discovery.get(ATTR_ID): + mac = dr.format_mac(cast(str, discovery[ATTR_ID])) + if mac != entry.unique_id: + # The device is offline and another flux_led device is now using the ip address + raise ConfigEntryNotReady( + f"Unexpected device found at {host}; Expected {entry.unique_id}, found {mac}" + ) + + if not discovery_cached: + # Only update the entry once we have verified the unique id + # is either missing or we have verified it matches + async_update_entry_from_discovery(hass, entry, discovery, device.model_num) coordinator = FluxLedUpdateCoordinator(hass, device, entry) hass.data[DOMAIN][entry.entry_id] = coordinator diff --git a/homeassistant/components/flux_led/config_flow.py b/homeassistant/components/flux_led/config_flow.py index 101e3a55d17..c6f14e929d6 100644 --- a/homeassistant/components/flux_led/config_flow.py +++ b/homeassistant/components/flux_led/config_flow.py @@ -1,9 +1,17 @@ """Config flow for Flux LED/MagicLight.""" from __future__ import annotations +import contextlib from typing import Any, Final, cast -from flux_led.const import ATTR_ID, ATTR_IPADDR, ATTR_MODEL, ATTR_MODEL_DESCRIPTION +from flux_led.const import ( + ATTR_ID, + ATTR_IPADDR, + ATTR_MODEL, + ATTR_MODEL_DESCRIPTION, + ATTR_MODEL_INFO, + ATTR_VERSION_NUM, +) from flux_led.scanner import FluxLEDDiscovery import voluptuous as vol @@ -35,6 +43,7 @@ from .discovery import ( async_populate_data_from_discovery, async_update_entry_from_discovery, ) +from .util import format_as_flux_mac CONF_DEVICE: Final = "device" @@ -60,7 +69,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): self._discovered_device = FluxLEDDiscovery( ipaddr=discovery_info.ip, model=None, - id=discovery_info.macaddress.replace(":", ""), + id=format_as_flux_mac(discovery_info.macaddress), model_num=None, version_num=None, firmware_date=None, @@ -90,7 +99,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): await self.async_set_unique_id(mac) for entry in self._async_current_entries(include_ignore=False): if entry.unique_id == mac or entry.data[CONF_HOST] == host: - if async_update_entry_from_discovery(self.hass, entry, device): + if async_update_entry_from_discovery(self.hass, entry, device, None): self.hass.async_create_task( self.hass.config_entries.async_reload(entry.entry_id) ) @@ -101,9 +110,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): return self.async_abort(reason="already_in_progress") if not device[ATTR_MODEL_DESCRIPTION]: try: - device = await self._async_try_connect( - host, device[ATTR_ID], device[ATTR_MODEL] - ) + device = await self._async_try_connect(host, device) except FLUX_LED_EXCEPTIONS: return self.async_abort(reason="cannot_connect") else: @@ -157,7 +164,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): if not (host := user_input[CONF_HOST]): return await self.async_step_pick_device() try: - device = await self._async_try_connect(host, None, None) + device = await self._async_try_connect(host, None) except FLUX_LED_EXCEPTIONS: errors["base"] = "cannot_connect" else: @@ -182,7 +189,11 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): if user_input is not None: mac = user_input[CONF_DEVICE] await self.async_set_unique_id(mac, raise_on_progress=False) - return self._async_create_entry_from_device(self._discovered_devices[mac]) + device = self._discovered_devices[mac] + if not device.get(ATTR_MODEL_DESCRIPTION): + with contextlib.suppress(*FLUX_LED_EXCEPTIONS): + device = await self._async_try_connect(device[ATTR_IPADDR], device) + return self._async_create_entry_from_device(device) current_unique_ids = self._async_current_ids() current_hosts = { @@ -212,7 +223,7 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): ) async def _async_try_connect( - self, host: str, mac_address: str | None, model: str | None + self, host: str, discovery: FluxLEDDiscovery | None ) -> FluxLEDDiscovery: """Try to connect.""" self._async_abort_entries_match({CONF_HOST: host}) @@ -226,18 +237,19 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): # AKA `HF-LPB100-ZJ200` return device bulb = async_wifi_bulb_for_host(host, discovery=device) + bulb.discovery = discovery try: await bulb.async_setup(lambda: None) finally: await bulb.async_stop() return FluxLEDDiscovery( ipaddr=host, - model=model, - id=mac_address, + model=discovery[ATTR_MODEL] if discovery else None, + id=discovery[ATTR_ID] if discovery else None, model_num=bulb.model_num, - version_num=None, # This is the minor version number + version_num=discovery[ATTR_VERSION_NUM] if discovery else None, firmware_date=None, - model_info=None, + model_info=discovery[ATTR_MODEL_INFO] if discovery else None, model_description=bulb.model_data.description, remote_access_enabled=None, remote_access_host=None, diff --git a/homeassistant/components/flux_led/const.py b/homeassistant/components/flux_led/const.py index 430c5a0e38a..ec81f7ba087 100644 --- a/homeassistant/components/flux_led/const.py +++ b/homeassistant/components/flux_led/const.py @@ -50,6 +50,9 @@ STARTUP_SCAN_TIMEOUT: Final = 5 DISCOVER_SCAN_TIMEOUT: Final = 10 CONF_MODEL: Final = "model" +CONF_MODEL_NUM: Final = "model_num" +CONF_MODEL_INFO: Final = "model_info" +CONF_MODEL_DESCRIPTION: Final = "model_description" CONF_MINOR_VERSION: Final = "minor_version" CONF_REMOTE_ACCESS_ENABLED: Final = "remote_access_enabled" CONF_REMOTE_ACCESS_HOST: Final = "remote_access_host" diff --git a/homeassistant/components/flux_led/discovery.py b/homeassistant/components/flux_led/discovery.py index d707af8ac9e..32b0d0ed9df 100644 --- a/homeassistant/components/flux_led/discovery.py +++ b/homeassistant/components/flux_led/discovery.py @@ -12,6 +12,8 @@ from flux_led.const import ( ATTR_IPADDR, ATTR_MODEL, ATTR_MODEL_DESCRIPTION, + ATTR_MODEL_INFO, + ATTR_MODEL_NUM, ATTR_REMOTE_ACCESS_ENABLED, ATTR_REMOTE_ACCESS_HOST, ATTR_REMOTE_ACCESS_PORT, @@ -21,6 +23,7 @@ from flux_led.scanner import FluxLEDDiscovery from homeassistant import config_entries from homeassistant.components import network +from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST, CONF_NAME from homeassistant.core import HomeAssistant, callback from homeassistant.helpers import device_registry as dr @@ -29,6 +32,9 @@ from homeassistant.util.network import is_ip_address from .const import ( CONF_MINOR_VERSION, CONF_MODEL, + CONF_MODEL_DESCRIPTION, + CONF_MODEL_INFO, + CONF_MODEL_NUM, CONF_REMOTE_ACCESS_ENABLED, CONF_REMOTE_ACCESS_HOST, CONF_REMOTE_ACCESS_PORT, @@ -36,6 +42,7 @@ from .const import ( DOMAIN, FLUX_LED_DISCOVERY, ) +from .util import format_as_flux_mac _LOGGER = logging.getLogger(__name__) @@ -47,9 +54,31 @@ CONF_TO_DISCOVERY: Final = { CONF_REMOTE_ACCESS_PORT: ATTR_REMOTE_ACCESS_PORT, CONF_MINOR_VERSION: ATTR_VERSION_NUM, CONF_MODEL: ATTR_MODEL, + CONF_MODEL_NUM: ATTR_MODEL_NUM, + CONF_MODEL_INFO: ATTR_MODEL_INFO, + CONF_MODEL_DESCRIPTION: ATTR_MODEL_DESCRIPTION, } +@callback +def async_build_cached_discovery(entry: ConfigEntry) -> FluxLEDDiscovery: + """When discovery is unavailable, load it from the config entry.""" + data = entry.data + return FluxLEDDiscovery( + ipaddr=data[CONF_HOST], + model=data.get(CONF_MODEL), + id=format_as_flux_mac(entry.unique_id), + model_num=data.get(CONF_MODEL_NUM), + version_num=data.get(CONF_MINOR_VERSION), + firmware_date=None, + model_info=data.get(CONF_MODEL_INFO), + model_description=data.get(CONF_MODEL_DESCRIPTION), + remote_access_enabled=data.get(CONF_REMOTE_ACCESS_ENABLED), + remote_access_host=data.get(CONF_REMOTE_ACCESS_HOST), + remote_access_port=data.get(CONF_REMOTE_ACCESS_PORT), + ) + + @callback def async_name_from_discovery(device: FluxLEDDiscovery) -> str: """Convert a flux_led discovery to a human readable name.""" @@ -72,6 +101,8 @@ def async_populate_data_from_discovery( for conf_key, discovery_key in CONF_TO_DISCOVERY.items(): if ( device.get(discovery_key) is not None + and conf_key + not in data_updates # Prefer the model num from TCP instead of UDP and current_data.get(conf_key) != device[discovery_key] # type: ignore[misc] ): data_updates[conf_key] = device[discovery_key] # type: ignore[misc] @@ -79,7 +110,10 @@ def async_populate_data_from_discovery( @callback def async_update_entry_from_discovery( - hass: HomeAssistant, entry: config_entries.ConfigEntry, device: FluxLEDDiscovery + hass: HomeAssistant, + entry: config_entries.ConfigEntry, + device: FluxLEDDiscovery, + model_num: int | None, ) -> bool: """Update a config entry from a flux_led discovery.""" data_updates: dict[str, Any] = {} @@ -88,6 +122,8 @@ def async_update_entry_from_discovery( updates: dict[str, Any] = {} if not entry.unique_id: updates["unique_id"] = dr.format_mac(mac_address) + if model_num and entry.data.get(CONF_MODEL_NUM) != model_num: + data_updates[CONF_MODEL_NUM] = model_num async_populate_data_from_discovery(entry.data, data_updates, device) if not entry.data.get(CONF_NAME) or is_ip_address(entry.data[CONF_NAME]): updates["title"] = data_updates[CONF_NAME] = async_name_from_discovery(device) diff --git a/homeassistant/components/flux_led/entity.py b/homeassistant/components/flux_led/entity.py index 6296f11b21a..a5807adbca8 100644 --- a/homeassistant/components/flux_led/entity.py +++ b/homeassistant/components/flux_led/entity.py @@ -24,7 +24,7 @@ def _async_device_info( version_num = device.version_num if minor_version := entry.data.get(CONF_MINOR_VERSION): sw_version = version_num + int(hex(minor_version)[2:]) / 100 - sw_version_str = f"{sw_version:0.3f}" + sw_version_str = f"{sw_version:0.2f}" else: sw_version_str = str(device.version_num) return DeviceInfo( diff --git a/homeassistant/components/flux_led/util.py b/homeassistant/components/flux_led/util.py index 8e1e387cafb..9a1d19c66a1 100644 --- a/homeassistant/components/flux_led/util.py +++ b/homeassistant/components/flux_led/util.py @@ -18,6 +18,11 @@ def _hass_color_modes(device: AIOWifiLedBulb) -> set[str]: return {_flux_color_mode_to_hass(mode, color_modes) for mode in color_modes} +def format_as_flux_mac(mac: str | None) -> str | None: + """Convert a device registry formatted mac to flux mac.""" + return None if mac is None else mac.replace(":", "").upper() + + def _flux_color_mode_to_hass( flux_color_mode: str | None, flux_color_modes: set[str] ) -> str: diff --git a/tests/components/flux_led/__init__.py b/tests/components/flux_led/__init__.py index 1a44dd949aa..82518619166 100644 --- a/tests/components/flux_led/__init__.py +++ b/tests/components/flux_led/__init__.py @@ -24,11 +24,12 @@ MODULE = "homeassistant.components.flux_led" MODULE_CONFIG_FLOW = "homeassistant.components.flux_led.config_flow" IP_ADDRESS = "127.0.0.1" MODEL_NUM_HEX = "0x35" +MODEL_NUM = 0x35 MODEL = "AZ120444" MODEL_DESCRIPTION = "Bulb RGBCW" MAC_ADDRESS = "aa:bb:cc:dd:ee:ff" -FLUX_MAC_ADDRESS = "aabbccddeeff" -SHORT_MAC_ADDRESS = "ddeeff" +FLUX_MAC_ADDRESS = "AABBCCDDEEFF" +SHORT_MAC_ADDRESS = "DDEEFF" DEFAULT_ENTRY_TITLE = f"{MODEL_DESCRIPTION} {SHORT_MAC_ADDRESS}" @@ -52,7 +53,7 @@ FLUX_DISCOVERY = FluxLEDDiscovery( ipaddr=IP_ADDRESS, model=MODEL, id=FLUX_MAC_ADDRESS, - model_num=0x25, + model_num=MODEL_NUM, version_num=0x04, firmware_date=datetime.date(2021, 5, 5), model_info=MODEL, @@ -80,6 +81,16 @@ def _mocked_bulb() -> AIOWifiLedBulb: bulb.async_set_effect = AsyncMock() bulb.async_set_white_temp = AsyncMock() bulb.async_set_brightness = AsyncMock() + bulb.pixels_per_segment = 300 + bulb.segments = 2 + bulb.music_pixels_per_segment = 150 + bulb.music_segments = 4 + bulb.operating_mode = "RGB&W" + bulb.operating_modes = ["RGB&W", "RGB/W"] + bulb.wirings = ["RGBW", "GRBW", "BGRW"] + bulb.wiring = "BGRW" + bulb.ic_types = ["WS2812B", "UCS1618"] + bulb.ic_type = "WS2812B" bulb.async_stop = AsyncMock() bulb.async_update = AsyncMock() bulb.async_turn_off = AsyncMock() @@ -102,8 +113,8 @@ def _mocked_bulb() -> AIOWifiLedBulb: bulb.color_temp = 2700 bulb.getWhiteTemperature = MagicMock(return_value=(2700, 128)) bulb.brightness = 128 - bulb.model_num = 0x35 - bulb.model_data = MODEL_MAP[0x35] + bulb.model_num = MODEL_NUM + bulb.model_data = MODEL_MAP[MODEL_NUM] bulb.effect = None bulb.speed = 50 bulb.model = "Bulb RGBCW (0x35)" diff --git a/tests/components/flux_led/test_config_flow.py b/tests/components/flux_led/test_config_flow.py index 32e1707f7eb..dcab5cc01ad 100644 --- a/tests/components/flux_led/test_config_flow.py +++ b/tests/components/flux_led/test_config_flow.py @@ -13,6 +13,9 @@ from homeassistant.components.flux_led.const import ( CONF_CUSTOM_EFFECT_TRANSITION, CONF_MINOR_VERSION, CONF_MODEL, + CONF_MODEL_DESCRIPTION, + CONF_MODEL_INFO, + CONF_MODEL_NUM, CONF_REMOTE_ACCESS_ENABLED, CONF_REMOTE_ACCESS_HOST, CONF_REMOTE_ACCESS_PORT, @@ -32,6 +35,8 @@ from . import ( IP_ADDRESS, MAC_ADDRESS, MODEL, + MODEL_DESCRIPTION, + MODEL_NUM, MODULE, _patch_discovery, _patch_wifibulb, @@ -91,6 +96,85 @@ async def test_discovery(hass: HomeAssistant): CONF_HOST: IP_ADDRESS, CONF_NAME: DEFAULT_ENTRY_TITLE, CONF_MODEL: MODEL, + CONF_MODEL_NUM: MODEL_NUM, + CONF_MODEL_INFO: MODEL, + CONF_MODEL_DESCRIPTION: MODEL_DESCRIPTION, + CONF_REMOTE_ACCESS_ENABLED: True, + CONF_REMOTE_ACCESS_HOST: "the.cloud", + CONF_REMOTE_ACCESS_PORT: 8816, + CONF_MINOR_VERSION: 0x04, + } + mock_setup.assert_called_once() + mock_setup_entry.assert_called_once() + + # ignore configured devices + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == "form" + assert result["step_id"] == "user" + assert not result["errors"] + + with _patch_discovery(), _patch_wifibulb(): + result2 = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + await hass.async_block_till_done() + + assert result2["type"] == "abort" + assert result2["reason"] == "no_devices_found" + + +async def test_discovery_legacy(hass: HomeAssistant): + """Test setting up discovery with a legacy device.""" + with _patch_discovery(device=FLUX_DISCOVERY_PARTIAL), _patch_wifibulb(): + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + await hass.async_block_till_done() + assert result["type"] == "form" + assert result["step_id"] == "user" + assert not result["errors"] + + result2 = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + await hass.async_block_till_done() + assert result2["type"] == "form" + assert result2["step_id"] == "pick_device" + assert not result2["errors"] + + # test we can try again + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": config_entries.SOURCE_USER} + ) + assert result["type"] == "form" + assert result["step_id"] == "user" + assert not result["errors"] + + result2 = await hass.config_entries.flow.async_configure(result["flow_id"], {}) + await hass.async_block_till_done() + assert result2["type"] == "form" + assert result2["step_id"] == "pick_device" + assert not result2["errors"] + + with _patch_discovery(), _patch_wifibulb(), patch( + f"{MODULE}.async_setup", return_value=True + ) as mock_setup, patch( + f"{MODULE}.async_setup_entry", return_value=True + ) as mock_setup_entry: + result3 = await hass.config_entries.flow.async_configure( + result["flow_id"], + {CONF_DEVICE: MAC_ADDRESS}, + ) + await hass.async_block_till_done() + + assert result3["type"] == "create_entry" + assert result3["title"] == DEFAULT_ENTRY_TITLE + assert result3["data"] == { + CONF_MINOR_VERSION: 4, + CONF_HOST: IP_ADDRESS, + CONF_NAME: DEFAULT_ENTRY_TITLE, + CONF_MODEL: MODEL, + CONF_MODEL_NUM: MODEL_NUM, + CONF_MODEL_INFO: MODEL, + CONF_MODEL_DESCRIPTION: MODEL_DESCRIPTION, CONF_REMOTE_ACCESS_ENABLED: True, CONF_REMOTE_ACCESS_HOST: "the.cloud", CONF_REMOTE_ACCESS_PORT: 8816, @@ -171,6 +255,9 @@ async def test_discovery_with_existing_device_present(hass: HomeAssistant): CONF_HOST: IP_ADDRESS, CONF_NAME: DEFAULT_ENTRY_TITLE, CONF_MODEL: MODEL, + CONF_MODEL_NUM: MODEL_NUM, + CONF_MODEL_INFO: MODEL, + CONF_MODEL_DESCRIPTION: MODEL_DESCRIPTION, CONF_REMOTE_ACCESS_ENABLED: True, CONF_REMOTE_ACCESS_HOST: "the.cloud", CONF_REMOTE_ACCESS_PORT: 8816, @@ -245,6 +332,9 @@ async def test_manual_working_discovery(hass: HomeAssistant): CONF_HOST: IP_ADDRESS, CONF_NAME: DEFAULT_ENTRY_TITLE, CONF_MODEL: MODEL, + CONF_MODEL_NUM: MODEL_NUM, + CONF_MODEL_INFO: MODEL, + CONF_MODEL_DESCRIPTION: MODEL_DESCRIPTION, CONF_REMOTE_ACCESS_ENABLED: True, CONF_REMOTE_ACCESS_HOST: "the.cloud", CONF_REMOTE_ACCESS_PORT: 8816, @@ -283,7 +373,12 @@ async def test_manual_no_discovery_data(hass: HomeAssistant): await hass.async_block_till_done() assert result["type"] == "create_entry" - assert result["data"] == {CONF_HOST: IP_ADDRESS, CONF_NAME: IP_ADDRESS} + assert result["data"] == { + CONF_HOST: IP_ADDRESS, + CONF_MODEL_NUM: MODEL_NUM, + CONF_MODEL_DESCRIPTION: MODEL_DESCRIPTION, + CONF_NAME: IP_ADDRESS, + } async def test_discovered_by_discovery_and_dhcp(hass): @@ -352,6 +447,9 @@ async def test_discovered_by_discovery(hass): CONF_HOST: IP_ADDRESS, CONF_NAME: DEFAULT_ENTRY_TITLE, CONF_MODEL: MODEL, + CONF_MODEL_NUM: MODEL_NUM, + CONF_MODEL_INFO: MODEL, + CONF_MODEL_DESCRIPTION: MODEL_DESCRIPTION, CONF_REMOTE_ACCESS_ENABLED: True, CONF_REMOTE_ACCESS_HOST: "the.cloud", CONF_REMOTE_ACCESS_PORT: 8816, @@ -387,6 +485,9 @@ async def test_discovered_by_dhcp_udp_responds(hass): CONF_HOST: IP_ADDRESS, CONF_NAME: DEFAULT_ENTRY_TITLE, CONF_MODEL: MODEL, + CONF_MODEL_NUM: MODEL_NUM, + CONF_MODEL_INFO: MODEL, + CONF_MODEL_DESCRIPTION: MODEL_DESCRIPTION, CONF_REMOTE_ACCESS_ENABLED: True, CONF_REMOTE_ACCESS_HOST: "the.cloud", CONF_REMOTE_ACCESS_PORT: 8816, @@ -419,6 +520,8 @@ async def test_discovered_by_dhcp_no_udp_response(hass): assert result2["type"] == "create_entry" assert result2["data"] == { CONF_HOST: IP_ADDRESS, + CONF_MODEL_NUM: MODEL_NUM, + CONF_MODEL_DESCRIPTION: MODEL_DESCRIPTION, CONF_NAME: DEFAULT_ENTRY_TITLE, } assert mock_async_setup.called @@ -448,6 +551,8 @@ async def test_discovered_by_dhcp_partial_udp_response_fallback_tcp(hass): assert result2["type"] == "create_entry" assert result2["data"] == { CONF_HOST: IP_ADDRESS, + CONF_MODEL_NUM: MODEL_NUM, + CONF_MODEL_DESCRIPTION: MODEL_DESCRIPTION, CONF_NAME: DEFAULT_ENTRY_TITLE, } assert mock_async_setup.called