Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid importing MQTT into core for ServiceInfo dataclass #74418

Merged
merged 13 commits into from Jul 14, 2022
19 changes: 4 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 @@ -24,7 +22,6 @@
SERVICE_RELOAD,
)
from homeassistant.core import Event, 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 All @@ -34,6 +31,10 @@
async_reload_integration_platforms,
)
from homeassistant.helpers.service import async_register_admin_service

# MqttServiceInfo only for backwards compatibility, do not use this
# as you'll get the whole MQTT integration via import.
from homeassistant.helpers.service_info.mqtt import MqttServiceInfo # noqa: F401
bdraco marked this conversation as resolved.
Show resolved Hide resolved
from homeassistant.helpers.typing import ConfigType

# Loading the config flow file will register the flow
Expand Down Expand Up @@ -140,18 +141,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 @@ -268,7 +269,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 @@ -30,10 +30,10 @@
if TYPE_CHECKING:
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
18 changes: 12 additions & 6 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 dhcp, mqtt, onboarding, ssdp, zeroconf
from homeassistant.components import onboarding
bdraco marked this conversation as resolved.
Show resolved Hide resolved
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResult

Expand All @@ -15,6 +15,12 @@
if TYPE_CHECKING:
import asyncio

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 @@ -89,7 +95,7 @@ async def async_step_discovery(

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 @@ -99,7 +105,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 @@ -109,7 +115,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 @@ -119,7 +125,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 @@ -129,7 +135,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
bdraco marked this conversation as resolved.
Show resolved Hide resolved
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