Avoid importing MQTT into core for ServiceInfo dataclass (#74418)

* Avoid importing MQTT into core for discovery dataclass

Likely fixes #73863

* relo

* adjust

* rename

* rename

* rename

* adjust missed imports

* drop compat

* fix conflict correctly

* Update homeassistant/helpers/config_entry_flow.py

* fix black from trying to fix the conflict in github
This commit is contained in:
J. Nick Koston 2022-07-14 18:09:09 +02:00 committed by GitHub
parent 1725948d4a
commit 666f715e76
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 43 additions and 32 deletions

View File

@ -3,8 +3,6 @@ from __future__ import annotations
import asyncio import asyncio
from collections.abc import Callable from collections.abc import Callable
from dataclasses import dataclass
import datetime as dt
import logging import logging
from typing import Any, cast from typing import Any, cast
@ -23,7 +21,6 @@ from homeassistant.const import (
SERVICE_RELOAD, SERVICE_RELOAD,
) )
from homeassistant.core import HassJob, HomeAssistant, ServiceCall, callback from homeassistant.core import HassJob, HomeAssistant, ServiceCall, callback
from homeassistant.data_entry_flow import BaseServiceInfo
from homeassistant.exceptions import TemplateError, Unauthorized from homeassistant.exceptions import TemplateError, Unauthorized
from homeassistant.helpers import config_validation as cv, event, template from homeassistant.helpers import config_validation as cv, event, template
from homeassistant.helpers.device_registry import DeviceEntry from homeassistant.helpers.device_registry import DeviceEntry
@ -145,18 +142,6 @@ MQTT_PUBLISH_SCHEMA = vol.All(
) )
@dataclass
class MqttServiceInfo(BaseServiceInfo):
"""Prepared info from mqtt entries."""
topic: str
payload: ReceivePayloadType
qos: int
retain: bool
subscribed_topic: str
timestamp: dt.datetime
async def _async_setup_discovery( async def _async_setup_discovery(
hass: HomeAssistant, conf: ConfigType, config_entry hass: HomeAssistant, conf: ConfigType, config_entry
) -> None: ) -> None:

View File

@ -17,6 +17,7 @@ from homeassistant.helpers.dispatcher import (
async_dispatcher_send, async_dispatcher_send,
) )
from homeassistant.helpers.json import json_loads from homeassistant.helpers.json import json_loads
from homeassistant.helpers.service_info.mqtt import MqttServiceInfo
from homeassistant.loader import async_get_mqtt from homeassistant.loader import async_get_mqtt
from .. import mqtt from .. import mqtt
@ -267,7 +268,7 @@ async def async_start( # noqa: C901
if key not in hass.data[INTEGRATION_UNSUBSCRIBE]: if key not in hass.data[INTEGRATION_UNSUBSCRIBE]:
return return
data = mqtt.MqttServiceInfo( data = MqttServiceInfo(
topic=msg.topic, topic=msg.topic,
payload=msg.payload, payload=msg.payload,
qos=msg.qos, qos=msg.qos,

View File

@ -12,12 +12,12 @@ from homeassistant.const import ATTR_ENTITY_ID, ATTR_NAME
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import template from homeassistant.helpers import template
from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity import Entity
from homeassistant.helpers.service_info.mqtt import ReceivePayloadType
from homeassistant.helpers.typing import TemplateVarsType from homeassistant.helpers.typing import TemplateVarsType
_SENTINEL = object() _SENTINEL = object()
PublishPayloadType = Union[str, bytes, int, float, None] PublishPayloadType = Union[str, bytes, int, float, None]
ReceivePayloadType = Union[str, bytes]
@attr.s(slots=True, frozen=True) @attr.s(slots=True, frozen=True)

View File

@ -6,8 +6,9 @@ from typing import Any
import voluptuous as vol import voluptuous as vol
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.components.mqtt import MqttServiceInfo, valid_subscribe_topic from homeassistant.components.mqtt import valid_subscribe_topic
from homeassistant.data_entry_flow import FlowResult from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers.service_info.mqtt import MqttServiceInfo
from .const import CONF_DISCOVERY_PREFIX, DEFAULT_PREFIX, DOMAIN from .const import CONF_DISCOVERY_PREFIX, DEFAULT_PREFIX, DOMAIN

View File

@ -31,10 +31,10 @@ if TYPE_CHECKING:
from .components.bluetooth import BluetoothServiceInfo from .components.bluetooth import BluetoothServiceInfo
from .components.dhcp import DhcpServiceInfo from .components.dhcp import DhcpServiceInfo
from .components.hassio import HassioServiceInfo from .components.hassio import HassioServiceInfo
from .components.mqtt import MqttServiceInfo
from .components.ssdp import SsdpServiceInfo from .components.ssdp import SsdpServiceInfo
from .components.usb import UsbServiceInfo from .components.usb import UsbServiceInfo
from .components.zeroconf import ZeroconfServiceInfo from .components.zeroconf import ZeroconfServiceInfo
from .helpers.service_info.mqtt import MqttServiceInfo
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)

View File

@ -6,7 +6,7 @@ import logging
from typing import TYPE_CHECKING, Any, Generic, TypeVar, Union, cast from typing import TYPE_CHECKING, Any, Generic, TypeVar, Union, cast
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.components import bluetooth, dhcp, onboarding, ssdp, zeroconf from homeassistant.components import onboarding
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResult from homeassistant.data_entry_flow import FlowResult
@ -15,8 +15,12 @@ from .typing import UNDEFINED, DiscoveryInfoType, UndefinedType
if TYPE_CHECKING: if TYPE_CHECKING:
import asyncio import asyncio
from homeassistant.components import mqtt from homeassistant.components.bluetooth import BluetoothServiceInfo
from homeassistant.components.dhcp import DhcpServiceInfo
from homeassistant.components.ssdp import SsdpServiceInfo
from homeassistant.components.zeroconf import ZeroconfServiceInfo
from .service_info.mqtt import MqttServiceInfo
_R = TypeVar("_R", bound="Awaitable[bool] | bool") _R = TypeVar("_R", bound="Awaitable[bool] | bool")
DiscoveryFunctionType = Callable[[HomeAssistant], _R] DiscoveryFunctionType = Callable[[HomeAssistant], _R]
@ -93,7 +97,7 @@ class DiscoveryFlowHandler(config_entries.ConfigFlow, Generic[_R]):
return await self.async_step_confirm() return await self.async_step_confirm()
async def async_step_bluetooth( async def async_step_bluetooth(
self, discovery_info: bluetooth.BluetoothServiceInfo self, discovery_info: BluetoothServiceInfo
) -> FlowResult: ) -> FlowResult:
"""Handle a flow initialized by bluetooth discovery.""" """Handle a flow initialized by bluetooth discovery."""
if self._async_in_progress() or self._async_current_entries(): if self._async_in_progress() or self._async_current_entries():
@ -103,7 +107,7 @@ class DiscoveryFlowHandler(config_entries.ConfigFlow, Generic[_R]):
return await self.async_step_confirm() return await self.async_step_confirm()
async def async_step_dhcp(self, discovery_info: dhcp.DhcpServiceInfo) -> FlowResult: async def async_step_dhcp(self, discovery_info: DhcpServiceInfo) -> FlowResult:
"""Handle a flow initialized by dhcp discovery.""" """Handle a flow initialized by dhcp discovery."""
if self._async_in_progress() or self._async_current_entries(): if self._async_in_progress() or self._async_current_entries():
return self.async_abort(reason="single_instance_allowed") return self.async_abort(reason="single_instance_allowed")
@ -113,7 +117,7 @@ class DiscoveryFlowHandler(config_entries.ConfigFlow, Generic[_R]):
return await self.async_step_confirm() return await self.async_step_confirm()
async def async_step_homekit( async def async_step_homekit(
self, discovery_info: zeroconf.ZeroconfServiceInfo self, discovery_info: ZeroconfServiceInfo
) -> FlowResult: ) -> FlowResult:
"""Handle a flow initialized by Homekit discovery.""" """Handle a flow initialized by Homekit discovery."""
if self._async_in_progress() or self._async_current_entries(): if self._async_in_progress() or self._async_current_entries():
@ -123,7 +127,7 @@ class DiscoveryFlowHandler(config_entries.ConfigFlow, Generic[_R]):
return await self.async_step_confirm() return await self.async_step_confirm()
async def async_step_mqtt(self, discovery_info: mqtt.MqttServiceInfo) -> FlowResult: async def async_step_mqtt(self, discovery_info: MqttServiceInfo) -> FlowResult:
"""Handle a flow initialized by mqtt discovery.""" """Handle a flow initialized by mqtt discovery."""
if self._async_in_progress() or self._async_current_entries(): if self._async_in_progress() or self._async_current_entries():
return self.async_abort(reason="single_instance_allowed") return self.async_abort(reason="single_instance_allowed")
@ -133,7 +137,7 @@ class DiscoveryFlowHandler(config_entries.ConfigFlow, Generic[_R]):
return await self.async_step_confirm() return await self.async_step_confirm()
async def async_step_zeroconf( async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo self, discovery_info: ZeroconfServiceInfo
) -> FlowResult: ) -> FlowResult:
"""Handle a flow initialized by Zeroconf discovery.""" """Handle a flow initialized by Zeroconf discovery."""
if self._async_in_progress() or self._async_current_entries(): if self._async_in_progress() or self._async_current_entries():
@ -143,7 +147,7 @@ class DiscoveryFlowHandler(config_entries.ConfigFlow, Generic[_R]):
return await self.async_step_confirm() return await self.async_step_confirm()
async def async_step_ssdp(self, discovery_info: ssdp.SsdpServiceInfo) -> FlowResult: async def async_step_ssdp(self, discovery_info: SsdpServiceInfo) -> FlowResult:
"""Handle a flow initialized by Ssdp discovery.""" """Handle a flow initialized by Ssdp discovery."""
if self._async_in_progress() or self._async_current_entries(): if self._async_in_progress() or self._async_current_entries():
return self.async_abort(reason="single_instance_allowed") return self.async_abort(reason="single_instance_allowed")

View File

@ -0,0 +1,20 @@
"""MQTT Discovery data."""
from dataclasses import dataclass
import datetime as dt
from typing import Union
from homeassistant.data_entry_flow import BaseServiceInfo
ReceivePayloadType = Union[str, bytes]
@dataclass
class MqttServiceInfo(BaseServiceInfo):
"""Prepared info from mqtt entries."""
topic: str
payload: ReceivePayloadType
qos: int
retain: bool
subscribed_topic: str
timestamp: dt.datetime

View File

@ -1,6 +1,6 @@
"""Test config flow.""" """Test config flow."""
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.components import mqtt from homeassistant.helpers.service_info.mqtt import MqttServiceInfo
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
@ -19,7 +19,7 @@ async def test_mqtt_abort_if_existing_entry(hass, mqtt_mock):
async def test_mqtt_abort_invalid_topic(hass, mqtt_mock): async def test_mqtt_abort_invalid_topic(hass, mqtt_mock):
"""Check MQTT flow aborts if discovery topic is invalid.""" """Check MQTT flow aborts if discovery topic is invalid."""
discovery_info = mqtt.MqttServiceInfo( discovery_info = MqttServiceInfo(
topic="tasmota/discovery/DC4F220848A2/bla", topic="tasmota/discovery/DC4F220848A2/bla",
payload=( payload=(
'{"ip":"192.168.0.136","dn":"Tasmota","fn":["Tasmota",null,null,null,null,' '{"ip":"192.168.0.136","dn":"Tasmota","fn":["Tasmota",null,null,null,null,'
@ -42,7 +42,7 @@ async def test_mqtt_abort_invalid_topic(hass, mqtt_mock):
assert result["type"] == "abort" assert result["type"] == "abort"
assert result["reason"] == "invalid_discovery_info" assert result["reason"] == "invalid_discovery_info"
discovery_info = mqtt.MqttServiceInfo( discovery_info = MqttServiceInfo(
topic="tasmota/discovery/DC4F220848A2/config", topic="tasmota/discovery/DC4F220848A2/config",
payload="", payload="",
qos=0, qos=0,
@ -56,7 +56,7 @@ async def test_mqtt_abort_invalid_topic(hass, mqtt_mock):
assert result["type"] == "abort" assert result["type"] == "abort"
assert result["reason"] == "invalid_discovery_info" assert result["reason"] == "invalid_discovery_info"
discovery_info = mqtt.MqttServiceInfo( discovery_info = MqttServiceInfo(
topic="tasmota/discovery/DC4F220848A2/config", topic="tasmota/discovery/DC4F220848A2/config",
payload=( payload=(
'{"ip":"192.168.0.136","dn":"Tasmota","fn":["Tasmota",null,null,null,null,' '{"ip":"192.168.0.136","dn":"Tasmota","fn":["Tasmota",null,null,null,null,'
@ -81,7 +81,7 @@ async def test_mqtt_abort_invalid_topic(hass, mqtt_mock):
async def test_mqtt_setup(hass, mqtt_mock) -> None: async def test_mqtt_setup(hass, mqtt_mock) -> None:
"""Test we can finish a config flow through MQTT with custom prefix.""" """Test we can finish a config flow through MQTT with custom prefix."""
discovery_info = mqtt.MqttServiceInfo( discovery_info = MqttServiceInfo(
topic="tasmota/discovery/DC4F220848A2/config", topic="tasmota/discovery/DC4F220848A2/config",
payload=( payload=(
'{"ip":"192.168.0.136","dn":"Tasmota","fn":["Tasmota",null,null,null,null,' '{"ip":"192.168.0.136","dn":"Tasmota","fn":["Tasmota",null,null,null,null,'