Improve IPP Config Flow (#34212)

pull/35205/head
Chris Talkington 2020-04-16 16:12:26 -05:00 committed by GitHub
parent ede432ba71
commit 9d794b820a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 79 additions and 26 deletions

View File

@ -134,13 +134,18 @@ class IPPEntity(Entity):
enabled_default: bool = True,
) -> None:
"""Initialize the IPP entity."""
self._device_id = None
self._enabled_default = enabled_default
self._entry_id = entry_id
self._icon = icon
self._name = name
self._unsub_dispatcher = None
self.coordinator = coordinator
if coordinator.data.info.uuid is not None:
self._device_id = coordinator.data.info.uuid
elif coordinator.data.info.serial is not None:
self._device_id = coordinator.data.info.serial
@property
def name(self) -> str:
"""Return the name of the entity."""
@ -179,8 +184,11 @@ class IPPEntity(Entity):
@property
def device_info(self) -> Dict[str, Any]:
"""Return device information about this IPP device."""
if self._device_id is None:
return None
return {
ATTR_IDENTIFIERS: {(DOMAIN, self.coordinator.data.info.uuid)},
ATTR_IDENTIFIERS: {(DOMAIN, self._device_id)},
ATTR_NAME: self.coordinator.data.info.name,
ATTR_MANUFACTURER: self.coordinator.data.info.manufacturer,
ATTR_MODEL: self.coordinator.data.info.model,

View File

@ -24,7 +24,7 @@ from homeassistant.const import (
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.typing import ConfigType, HomeAssistantType
from .const import CONF_BASE_PATH, CONF_UUID
from .const import CONF_BASE_PATH, CONF_SERIAL, CONF_UUID
from .const import DOMAIN # pylint: disable=unused-import
_LOGGER = logging.getLogger(__name__)
@ -47,7 +47,7 @@ async def validate_input(hass: HomeAssistantType, data: dict) -> Dict[str, Any]:
printer = await ipp.printer()
return {CONF_UUID: printer.info.uuid}
return {CONF_SERIAL: printer.info.serial, CONF_UUID: printer.info.uuid}
class IPPFlowHandler(ConfigFlow, domain=DOMAIN):
@ -83,20 +83,28 @@ class IPPFlowHandler(ConfigFlow, domain=DOMAIN):
_LOGGER.debug("IPP Error", exc_info=True)
return self.async_abort(reason="ipp_error")
user_input[CONF_UUID] = info[CONF_UUID]
unique_id = user_input[CONF_UUID] = info[CONF_UUID]
await self.async_set_unique_id(user_input[CONF_UUID])
if unique_id is None and info[CONF_SERIAL] is not None:
_LOGGER.debug(
"Printer UUID is missing from IPP response. Falling back to IPP serial number"
)
unique_id = info[CONF_SERIAL]
elif unique_id is None:
_LOGGER.debug("Unable to determine unique id from IPP response")
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured(updates={CONF_HOST: user_input[CONF_HOST]})
return self.async_create_entry(title=user_input[CONF_HOST], data=user_input)
async def async_step_zeroconf(self, discovery_info: ConfigType) -> Dict[str, Any]:
"""Handle zeroconf discovery."""
# Hostname is format: EPSON123456.local.
host = discovery_info["hostname"].rstrip(".")
port = discovery_info["port"]
name, _ = host.rsplit(".")
tls = discovery_info["type"] == "_ipps._tcp.local."
port = discovery_info[CONF_PORT]
zctype = discovery_info["type"]
name = discovery_info[CONF_NAME].replace(f".{zctype}", "")
tls = zctype == "_ipps._tcp.local."
base_path = discovery_info["properties"].get("rp", "ipp/print")
# pylint: disable=no-member # https://github.com/PyCQA/pylint/issues/3167
self.context.update({"title_placeholders": {"name": name}})
@ -107,8 +115,7 @@ class IPPFlowHandler(ConfigFlow, domain=DOMAIN):
CONF_PORT: port,
CONF_SSL: tls,
CONF_VERIFY_SSL: False,
CONF_BASE_PATH: "/"
+ discovery_info["properties"].get("rp", "ipp/print"),
CONF_BASE_PATH: f"/{base_path}",
CONF_NAME: name,
CONF_UUID: discovery_info["properties"].get("UUID"),
}
@ -130,12 +137,28 @@ class IPPFlowHandler(ConfigFlow, domain=DOMAIN):
_LOGGER.debug("IPP Error", exc_info=True)
return self.async_abort(reason="ipp_error")
if info[CONF_UUID] is not None:
self.discovery_info[CONF_UUID] = info[CONF_UUID]
unique_id = self.discovery_info[CONF_UUID]
if unique_id is None and info[CONF_UUID] is not None:
_LOGGER.debug(
"Printer UUID is missing from discovery info. Falling back to IPP UUID"
)
unique_id = self.discovery_info[CONF_UUID] = info[CONF_UUID]
elif unique_id is None and info[CONF_SERIAL] is not None:
_LOGGER.debug(
"Printer UUID is missing from discovery info and IPP response. Falling back to IPP serial number"
)
unique_id = info[CONF_SERIAL]
elif unique_id is None:
_LOGGER.debug(
"Unable to determine unique id from discovery info and IPP response"
)
await self.async_set_unique_id(self.discovery_info[CONF_UUID])
await self.async_set_unique_id(unique_id)
self._abort_if_unique_id_configured(
updates={CONF_HOST: self.discovery_info[CONF_HOST]}
updates={
CONF_HOST: self.discovery_info[CONF_HOST],
CONF_NAME: self.discovery_info[CONF_NAME],
},
)
return await self.async_step_zeroconf_confirm()

View File

@ -21,5 +21,6 @@ ATTR_URI_SUPPORTED = "uri_supported"
# Config Keys
CONF_BASE_PATH = "base_path"
CONF_SERIAL = "serial"
CONF_TLS = "tls"
CONF_UUID = "uuid"

View File

@ -60,6 +60,12 @@ class IPPSensor(IPPEntity):
"""Initialize IPP sensor."""
self._unit_of_measurement = unit_of_measurement
self._key = key
self._unique_id = None
if coordinator.data.info.uuid is not None:
self._unique_id = f"{coordinator.data.info.uuid}_{key}"
elif coordinator.data.info.serial is not None:
self._unique_id = f"{coordinator.data.info.serial}_{key}"
super().__init__(
entry_id=entry_id,
@ -72,7 +78,7 @@ class IPPSensor(IPPEntity):
@property
def unique_id(self) -> str:
"""Return the unique ID for this sensor."""
return f"{self.coordinator.data.info.uuid}_{self._key}"
return self._unique_id
@property
def unit_of_measurement(self) -> str:

View File

@ -21,7 +21,7 @@ ATTR_PROPERTIES = "properties"
IPP_ZEROCONF_SERVICE_TYPE = "_ipp._tcp.local."
IPPS_ZEROCONF_SERVICE_TYPE = "_ipps._tcp.local."
ZEROCONF_NAME = "EPSON123456"
ZEROCONF_NAME = "EPSON XP-6000 Series"
ZEROCONF_HOST = "192.168.1.31"
ZEROCONF_HOSTNAME = "EPSON123456.local."
ZEROCONF_PORT = 631

View File

@ -50,7 +50,7 @@ async def test_show_zeroconf_form(
assert result["step_id"] == "zeroconf_confirm"
assert result["type"] == RESULT_TYPE_FORM
assert result["description_placeholders"] == {CONF_NAME: "EPSON123456"}
assert result["description_placeholders"] == {CONF_NAME: "EPSON XP-6000 Series"}
async def test_connection_error(
@ -276,8 +276,13 @@ async def test_zeroconf_with_uuid_device_exists_abort(
"""Test we abort zeroconf flow if printer already configured."""
await init_integration(hass, aioclient_mock)
discovery_info = MOCK_ZEROCONF_IPP_SERVICE_INFO.copy()
discovery_info["properties"]["UUID"] = "cfe92100-67c4-11d4-a45f-f8d027761251"
discovery_info = {
**MOCK_ZEROCONF_IPP_SERVICE_INFO,
"properties": {
**MOCK_ZEROCONF_IPP_SERVICE_INFO["properties"],
"UUID": "cfe92100-67c4-11d4-a45f-f8d027761251",
},
}
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_ZEROCONF}, data=discovery_info,
)
@ -315,6 +320,9 @@ async def test_full_user_flow_implementation(
assert result["data"][CONF_HOST] == "192.168.1.31"
assert result["data"][CONF_UUID] == "cfe92100-67c4-11d4-a45f-f8d027761251"
assert result["result"]
assert result["result"].unique_id == "cfe92100-67c4-11d4-a45f-f8d027761251"
async def test_full_zeroconf_flow_implementation(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
@ -339,13 +347,17 @@ async def test_full_zeroconf_flow_implementation(
)
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
assert result["title"] == "EPSON123456"
assert result["title"] == "EPSON XP-6000 Series"
assert result["data"]
assert result["data"][CONF_HOST] == "192.168.1.31"
assert result["data"][CONF_NAME] == "EPSON XP-6000 Series"
assert result["data"][CONF_UUID] == "cfe92100-67c4-11d4-a45f-f8d027761251"
assert not result["data"][CONF_SSL]
assert result["result"]
assert result["result"].unique_id == "cfe92100-67c4-11d4-a45f-f8d027761251"
async def test_full_zeroconf_tls_flow_implementation(
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker
@ -364,17 +376,20 @@ async def test_full_zeroconf_tls_flow_implementation(
assert result["step_id"] == "zeroconf_confirm"
assert result["type"] == RESULT_TYPE_FORM
assert result["description_placeholders"] == {CONF_NAME: "EPSON123456"}
assert result["description_placeholders"] == {CONF_NAME: "EPSON XP-6000 Series"}
result = await hass.config_entries.flow.async_configure(
result["flow_id"], user_input={}
)
assert result["type"] == RESULT_TYPE_CREATE_ENTRY
assert result["title"] == "EPSON123456"
assert result["title"] == "EPSON XP-6000 Series"
assert result["data"]
assert result["data"][CONF_HOST] == "192.168.1.31"
assert result["data"][CONF_NAME] == "EPSON123456"
assert result["data"][CONF_NAME] == "EPSON XP-6000 Series"
assert result["data"][CONF_UUID] == "cfe92100-67c4-11d4-a45f-f8d027761251"
assert result["data"][CONF_SSL]
assert result["result"]
assert result["result"].unique_id == "cfe92100-67c4-11d4-a45f-f8d027761251"