Skip to content

Commit

Permalink
Avoid importing MQTT into core for ServiceInfo dataclass (#74418)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
bdraco committed Jul 14, 2022
1 parent 1725948 commit 666f715
Show file tree
Hide file tree
Showing 8 changed files with 43 additions and 32 deletions.
15 changes: 0 additions & 15 deletions homeassistant/components/mqtt/__init__.py
Expand Up @@ -3,8 +3,6 @@

import asyncio
from collections.abc import Callable
from dataclasses import dataclass
import datetime as dt
import logging
from typing import Any, cast

Expand All @@ -23,7 +21,6 @@
SERVICE_RELOAD,
)
from homeassistant.core import HassJob, HomeAssistant, ServiceCall, callback
from homeassistant.data_entry_flow import BaseServiceInfo
from homeassistant.exceptions import TemplateError, Unauthorized
from homeassistant.helpers import config_validation as cv, event, template
from homeassistant.helpers.device_registry import DeviceEntry
Expand Down Expand Up @@ -145,18 +142,6 @@
)


@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(
hass: HomeAssistant, conf: ConfigType, config_entry
) -> None:
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/mqtt/discovery.py
Expand Up @@ -17,6 +17,7 @@
async_dispatcher_send,
)
from homeassistant.helpers.json import json_loads
from homeassistant.helpers.service_info.mqtt import MqttServiceInfo
from homeassistant.loader import async_get_mqtt

from .. import mqtt
Expand Down Expand Up @@ -267,7 +268,7 @@ async def async_integration_message_received(integration, msg):
if key not in hass.data[INTEGRATION_UNSUBSCRIBE]:
return

data = mqtt.MqttServiceInfo(
data = MqttServiceInfo(
topic=msg.topic,
payload=msg.payload,
qos=msg.qos,
Expand Down
2 changes: 1 addition & 1 deletion homeassistant/components/mqtt/models.py
Expand Up @@ -12,12 +12,12 @@
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers import template
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.service_info.mqtt import ReceivePayloadType
from homeassistant.helpers.typing import TemplateVarsType

_SENTINEL = object()

PublishPayloadType = Union[str, bytes, int, float, None]
ReceivePayloadType = Union[str, bytes]


@attr.s(slots=True, frozen=True)
Expand Down
3 changes: 2 additions & 1 deletion homeassistant/components/tasmota/config_flow.py
Expand Up @@ -6,8 +6,9 @@
import voluptuous as vol

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.helpers.service_info.mqtt import MqttServiceInfo

from .const import CONF_DISCOVERY_PREFIX, DEFAULT_PREFIX, DOMAIN

Expand Down
2 changes: 1 addition & 1 deletion homeassistant/config_entries.py
Expand Up @@ -31,10 +31,10 @@
from .components.bluetooth import BluetoothServiceInfo
from .components.dhcp import DhcpServiceInfo
from .components.hassio import HassioServiceInfo
from .components.mqtt import MqttServiceInfo
from .components.ssdp import SsdpServiceInfo
from .components.usb import UsbServiceInfo
from .components.zeroconf import ZeroconfServiceInfo
from .helpers.service_info.mqtt import MqttServiceInfo

_LOGGER = logging.getLogger(__name__)

Expand Down
20 changes: 12 additions & 8 deletions homeassistant/helpers/config_entry_flow.py
Expand Up @@ -6,7 +6,7 @@
from typing import TYPE_CHECKING, Any, Generic, TypeVar, Union, cast

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.data_entry_flow import FlowResult

Expand All @@ -15,8 +15,12 @@
if TYPE_CHECKING:
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")
DiscoveryFunctionType = Callable[[HomeAssistant], _R]
Expand Down Expand Up @@ -93,7 +97,7 @@ async def async_step_discovery(
return await self.async_step_confirm()

async def async_step_bluetooth(
self, discovery_info: bluetooth.BluetoothServiceInfo
self, discovery_info: BluetoothServiceInfo
) -> FlowResult:
"""Handle a flow initialized by bluetooth discovery."""
if self._async_in_progress() or self._async_current_entries():
Expand All @@ -103,7 +107,7 @@ async def async_step_bluetooth(

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."""
if self._async_in_progress() or self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
Expand All @@ -113,7 +117,7 @@ async def async_step_dhcp(self, discovery_info: dhcp.DhcpServiceInfo) -> FlowRes
return await self.async_step_confirm()

async def async_step_homekit(
self, discovery_info: zeroconf.ZeroconfServiceInfo
self, discovery_info: ZeroconfServiceInfo
) -> FlowResult:
"""Handle a flow initialized by Homekit discovery."""
if self._async_in_progress() or self._async_current_entries():
Expand All @@ -123,7 +127,7 @@ async def async_step_homekit(

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."""
if self._async_in_progress() or self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
Expand All @@ -133,7 +137,7 @@ async def async_step_mqtt(self, discovery_info: mqtt.MqttServiceInfo) -> FlowRes
return await self.async_step_confirm()

async def async_step_zeroconf(
self, discovery_info: zeroconf.ZeroconfServiceInfo
self, discovery_info: ZeroconfServiceInfo
) -> FlowResult:
"""Handle a flow initialized by Zeroconf discovery."""
if self._async_in_progress() or self._async_current_entries():
Expand All @@ -143,7 +147,7 @@ async def async_step_zeroconf(

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."""
if self._async_in_progress() or self._async_current_entries():
return self.async_abort(reason="single_instance_allowed")
Expand Down
20 changes: 20 additions & 0 deletions homeassistant/helpers/service_info/mqtt.py
@@ -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
10 changes: 5 additions & 5 deletions tests/components/tasmota/test_config_flow.py
@@ -1,6 +1,6 @@
"""Test config flow."""
from homeassistant import config_entries
from homeassistant.components import mqtt
from homeassistant.helpers.service_info.mqtt import MqttServiceInfo

from tests.common import MockConfigEntry

Expand All @@ -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):
"""Check MQTT flow aborts if discovery topic is invalid."""
discovery_info = mqtt.MqttServiceInfo(
discovery_info = MqttServiceInfo(
topic="tasmota/discovery/DC4F220848A2/bla",
payload=(
'{"ip":"192.168.0.136","dn":"Tasmota","fn":["Tasmota",null,null,null,null,'
Expand All @@ -42,7 +42,7 @@ async def test_mqtt_abort_invalid_topic(hass, mqtt_mock):
assert result["type"] == "abort"
assert result["reason"] == "invalid_discovery_info"

discovery_info = mqtt.MqttServiceInfo(
discovery_info = MqttServiceInfo(
topic="tasmota/discovery/DC4F220848A2/config",
payload="",
qos=0,
Expand All @@ -56,7 +56,7 @@ async def test_mqtt_abort_invalid_topic(hass, mqtt_mock):
assert result["type"] == "abort"
assert result["reason"] == "invalid_discovery_info"

discovery_info = mqtt.MqttServiceInfo(
discovery_info = MqttServiceInfo(
topic="tasmota/discovery/DC4F220848A2/config",
payload=(
'{"ip":"192.168.0.136","dn":"Tasmota","fn":["Tasmota",null,null,null,null,'
Expand All @@ -81,7 +81,7 @@ async def test_mqtt_abort_invalid_topic(hass, mqtt_mock):

async def test_mqtt_setup(hass, mqtt_mock) -> None:
"""Test we can finish a config flow through MQTT with custom prefix."""
discovery_info = mqtt.MqttServiceInfo(
discovery_info = MqttServiceInfo(
topic="tasmota/discovery/DC4F220848A2/config",
payload=(
'{"ip":"192.168.0.136","dn":"Tasmota","fn":["Tasmota",null,null,null,null,'
Expand Down

0 comments on commit 666f715

Please sign in to comment.