Skip to content
Snippets Groups Projects
Unverified Commit e6c94d78 authored by Martin Hjelmare's avatar Martin Hjelmare Committed by GitHub
Browse files

Remove mysensors notify (#90402)

parent b6a0ac6f
No related branches found
No related tags found
No related merge requests found
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Callable from collections.abc import Callable
from functools import partial
import logging import logging
from mysensors import BaseAsyncGateway from mysensors import BaseAsyncGateway
...@@ -12,24 +11,19 @@ from homeassistant.const import Platform ...@@ -12,24 +11,19 @@ from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant, callback
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.device_registry import DeviceEntry from homeassistant.helpers.device_registry import DeviceEntry
from homeassistant.helpers.discovery import async_load_platform
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.typing import ConfigType
from .const import ( from .const import (
ATTR_DEVICES, ATTR_DEVICES,
DOMAIN, DOMAIN,
MYSENSORS_DISCOVERY,
MYSENSORS_GATEWAYS, MYSENSORS_GATEWAYS,
MYSENSORS_ON_UNLOAD, MYSENSORS_ON_UNLOAD,
PLATFORMS_WITH_ENTRY_SUPPORT, PLATFORMS,
DevId, DevId,
DiscoveryInfo, DiscoveryInfo,
SensorType, SensorType,
) )
from .device import MySensorsDevice, get_mysensors_devices from .device import MySensorsDevice, get_mysensors_devices
from .gateway import finish_setup, gw_stop, setup_gateway from .gateway import finish_setup, gw_stop, setup_gateway
from .helpers import on_unload
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
...@@ -39,14 +33,6 @@ DATA_HASS_CONFIG = "hass_config" ...@@ -39,14 +33,6 @@ DATA_HASS_CONFIG = "hass_config"
CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False) CONFIG_SCHEMA = cv.removed(DOMAIN, raise_if_present=False)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the MySensors component."""
# This is needed to set up the notify platform via discovery.
hass.data[DOMAIN] = {DATA_HASS_CONFIG: config}
return True
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up an instance of the MySensors integration. """Set up an instance of the MySensors integration.
...@@ -58,33 +44,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ...@@ -58,33 +44,12 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
_LOGGER.error("Gateway setup failed for %s", entry.data) _LOGGER.error("Gateway setup failed for %s", entry.data)
return False return False
if MYSENSORS_GATEWAYS not in hass.data[DOMAIN]: mysensors_data = hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][MYSENSORS_GATEWAYS] = {} if MYSENSORS_GATEWAYS not in mysensors_data:
hass.data[DOMAIN][MYSENSORS_GATEWAYS][entry.entry_id] = gateway mysensors_data[MYSENSORS_GATEWAYS] = {}
mysensors_data[MYSENSORS_GATEWAYS][entry.entry_id] = gateway
# Connect notify discovery as that integration doesn't support entry forwarding.
load_discovery_platform = partial( await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
async_load_platform,
hass,
Platform.NOTIFY,
DOMAIN,
hass_config=hass.data[DOMAIN][DATA_HASS_CONFIG],
)
on_unload(
hass,
entry.entry_id,
async_dispatcher_connect(
hass,
MYSENSORS_DISCOVERY.format(entry.entry_id, Platform.NOTIFY),
load_discovery_platform,
),
)
await hass.config_entries.async_forward_entry_setups(
entry, PLATFORMS_WITH_ENTRY_SUPPORT
)
await finish_setup(hass, entry, gateway) await finish_setup(hass, entry, gateway)
return True return True
...@@ -95,9 +60,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ...@@ -95,9 +60,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
gateway: BaseAsyncGateway = hass.data[DOMAIN][MYSENSORS_GATEWAYS][entry.entry_id] gateway: BaseAsyncGateway = hass.data[DOMAIN][MYSENSORS_GATEWAYS][entry.entry_id]
unload_ok = await hass.config_entries.async_unload_platforms( unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
entry, PLATFORMS_WITH_ENTRY_SUPPORT
)
if not unload_ok: if not unload_ok:
return False return False
......
...@@ -40,7 +40,6 @@ class DiscoveryInfo(TypedDict): ...@@ -40,7 +40,6 @@ class DiscoveryInfo(TypedDict):
"""Represent the discovery info type for mysensors platforms.""" """Represent the discovery info type for mysensors platforms."""
devices: list[DevId] devices: list[DevId]
name: str # CONF_NAME is used in the notify base integration.
gateway_id: GatewayId gateway_id: GatewayId
...@@ -92,8 +91,6 @@ LIGHT_TYPES: dict[SensorType, set[ValueType]] = { ...@@ -92,8 +91,6 @@ LIGHT_TYPES: dict[SensorType, set[ValueType]] = {
"S_RGBW_LIGHT": {"V_RGBW"}, "S_RGBW_LIGHT": {"V_RGBW"},
} }
NOTIFY_TYPES: dict[SensorType, set[ValueType]] = {"S_INFO": {"V_TEXT"}}
REMOTE_TYPES: dict[SensorType, set[ValueType]] = {"S_IR": {"V_IR_SEND"}} REMOTE_TYPES: dict[SensorType, set[ValueType]] = {"S_IR": {"V_IR_SEND"}}
SENSOR_TYPES: dict[SensorType, set[ValueType]] = { SENSOR_TYPES: dict[SensorType, set[ValueType]] = {
...@@ -148,7 +145,6 @@ PLATFORM_TYPES: dict[Platform, dict[SensorType, set[ValueType]]] = { ...@@ -148,7 +145,6 @@ PLATFORM_TYPES: dict[Platform, dict[SensorType, set[ValueType]]] = {
Platform.COVER: COVER_TYPES, Platform.COVER: COVER_TYPES,
Platform.DEVICE_TRACKER: DEVICE_TRACKER_TYPES, Platform.DEVICE_TRACKER: DEVICE_TRACKER_TYPES,
Platform.LIGHT: LIGHT_TYPES, Platform.LIGHT: LIGHT_TYPES,
Platform.NOTIFY: NOTIFY_TYPES,
Platform.REMOTE: REMOTE_TYPES, Platform.REMOTE: REMOTE_TYPES,
Platform.SENSOR: SENSOR_TYPES, Platform.SENSOR: SENSOR_TYPES,
Platform.SWITCH: SWITCH_TYPES, Platform.SWITCH: SWITCH_TYPES,
...@@ -167,6 +163,4 @@ for platform, platform_types in PLATFORM_TYPES.items(): ...@@ -167,6 +163,4 @@ for platform, platform_types in PLATFORM_TYPES.items():
for s_type_name in platform_types: for s_type_name in platform_types:
TYPE_TO_PLATFORMS[s_type_name].append(platform) TYPE_TO_PLATFORMS[s_type_name].append(platform)
PLATFORMS_WITH_ENTRY_SUPPORT = set(PLATFORM_TYPES.keys()) - { PLATFORMS = tuple(PLATFORM_TYPES)
Platform.NOTIFY,
}
"""MySensors notification service."""
from __future__ import annotations
from typing import Any, cast
from homeassistant.components.notify import ATTR_TARGET, BaseNotificationService
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import slugify
from .. import mysensors
from .const import DOMAIN, DevId, DiscoveryInfo
async def async_get_service(
hass: HomeAssistant,
config: ConfigType,
discovery_info: DiscoveryInfoType | None = None,
) -> BaseNotificationService | None:
"""Get the MySensors notification service."""
if not discovery_info:
return None
new_devices = mysensors.setup_mysensors_platform(
hass,
Platform.NOTIFY,
cast(DiscoveryInfo, discovery_info),
MySensorsNotificationDevice,
)
if not new_devices:
return None
return MySensorsNotificationService(hass)
class MySensorsNotificationDevice(mysensors.device.MySensorsDevice):
"""Represent a MySensors Notification device."""
@callback
def _async_update_callback(self) -> None:
"""Update the device."""
self._async_update()
def send_msg(self, msg: str) -> None:
"""Send a message."""
for sub_msg in [msg[i : i + 25] for i in range(0, len(msg), 25)]:
# Max mysensors payload is 25 bytes.
self.gateway.set_child_value(
self.node_id, self.child_id, self.value_type, sub_msg
)
def __repr__(self) -> str:
"""Return the representation."""
return f"<MySensorsNotificationDevice {self.name}>"
class MySensorsNotificationService(BaseNotificationService):
"""Implement a MySensors notification service."""
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize the service."""
self.devices: dict[
DevId, MySensorsNotificationDevice
] = mysensors.get_mysensors_devices(
hass, Platform.NOTIFY
) # type: ignore[assignment]
self.hass = hass
async def async_send_message(self, message: str = "", **kwargs: Any) -> None:
"""Send a message to a user."""
target_devices = kwargs.get(ATTR_TARGET)
devices = [
device
for device in self.devices.values()
if target_devices is None or device.name in target_devices
]
placeholders = {
"alternate_service": "text.set_value",
"deprecated_service": f"notify.{self._service_name}",
"alternate_target": str(
[f"text.{slugify(device.name)}" for device in devices]
),
}
async_create_issue(
self.hass,
DOMAIN,
"deprecated_notify_service",
breaks_in_ha_version="2023.4.0",
is_fixable=True,
is_persistent=True,
severity=IssueSeverity.WARNING,
translation_key="deprecated_service",
translation_placeholders=placeholders,
)
for device in devices:
device.send_msg(message)
...@@ -61,8 +61,6 @@ async def test_config_mqtt(hass: HomeAssistant, mqtt: None) -> None: ...@@ -61,8 +61,6 @@ async def test_config_mqtt(hass: HomeAssistant, mqtt: None) -> None:
flow_id = step["flow_id"] flow_id = step["flow_id"]
with patch( with patch(
"homeassistant.components.mysensors.async_setup", return_value=True
) as mock_setup, patch(
"homeassistant.components.mysensors.async_setup_entry", "homeassistant.components.mysensors.async_setup_entry",
return_value=True, return_value=True,
) as mock_setup_entry: ) as mock_setup_entry:
...@@ -89,7 +87,6 @@ async def test_config_mqtt(hass: HomeAssistant, mqtt: None) -> None: ...@@ -89,7 +87,6 @@ async def test_config_mqtt(hass: HomeAssistant, mqtt: None) -> None:
CONF_VERSION: "2.4", CONF_VERSION: "2.4",
CONF_GATEWAY_TYPE: "MQTT", CONF_GATEWAY_TYPE: "MQTT",
} }
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1
...@@ -121,8 +118,6 @@ async def test_config_serial(hass: HomeAssistant) -> None: ...@@ -121,8 +118,6 @@ async def test_config_serial(hass: HomeAssistant) -> None:
), patch( ), patch(
"homeassistant.components.mysensors.config_flow.try_connect", return_value=True "homeassistant.components.mysensors.config_flow.try_connect", return_value=True
), patch( ), patch(
"homeassistant.components.mysensors.async_setup", return_value=True
) as mock_setup, patch(
"homeassistant.components.mysensors.async_setup_entry", "homeassistant.components.mysensors.async_setup_entry",
return_value=True, return_value=True,
) as mock_setup_entry: ) as mock_setup_entry:
...@@ -146,7 +141,6 @@ async def test_config_serial(hass: HomeAssistant) -> None: ...@@ -146,7 +141,6 @@ async def test_config_serial(hass: HomeAssistant) -> None:
CONF_VERSION: "2.4", CONF_VERSION: "2.4",
CONF_GATEWAY_TYPE: "Serial", CONF_GATEWAY_TYPE: "Serial",
} }
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1
...@@ -158,8 +152,6 @@ async def test_config_tcp(hass: HomeAssistant) -> None: ...@@ -158,8 +152,6 @@ async def test_config_tcp(hass: HomeAssistant) -> None:
with patch( with patch(
"homeassistant.components.mysensors.config_flow.try_connect", return_value=True "homeassistant.components.mysensors.config_flow.try_connect", return_value=True
), patch( ), patch(
"homeassistant.components.mysensors.async_setup", return_value=True
) as mock_setup, patch(
"homeassistant.components.mysensors.async_setup_entry", "homeassistant.components.mysensors.async_setup_entry",
return_value=True, return_value=True,
) as mock_setup_entry: ) as mock_setup_entry:
...@@ -183,7 +175,6 @@ async def test_config_tcp(hass: HomeAssistant) -> None: ...@@ -183,7 +175,6 @@ async def test_config_tcp(hass: HomeAssistant) -> None:
CONF_VERSION: "2.4", CONF_VERSION: "2.4",
CONF_GATEWAY_TYPE: "TCP", CONF_GATEWAY_TYPE: "TCP",
} }
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1
...@@ -195,8 +186,6 @@ async def test_fail_to_connect(hass: HomeAssistant) -> None: ...@@ -195,8 +186,6 @@ async def test_fail_to_connect(hass: HomeAssistant) -> None:
with patch( with patch(
"homeassistant.components.mysensors.config_flow.try_connect", return_value=False "homeassistant.components.mysensors.config_flow.try_connect", return_value=False
), patch( ), patch(
"homeassistant.components.mysensors.async_setup", return_value=True
) as mock_setup, patch(
"homeassistant.components.mysensors.async_setup_entry", "homeassistant.components.mysensors.async_setup_entry",
return_value=True, return_value=True,
) as mock_setup_entry: ) as mock_setup_entry:
...@@ -215,7 +204,6 @@ async def test_fail_to_connect(hass: HomeAssistant) -> None: ...@@ -215,7 +204,6 @@ async def test_fail_to_connect(hass: HomeAssistant) -> None:
errors = result["errors"] errors = result["errors"]
assert errors assert errors
assert errors.get("base") == "cannot_connect" assert errors.get("base") == "cannot_connect"
assert len(mock_setup.mock_calls) == 0
assert len(mock_setup_entry.mock_calls) == 0 assert len(mock_setup_entry.mock_calls) == 0
...@@ -358,8 +346,6 @@ async def test_config_invalid( ...@@ -358,8 +346,6 @@ async def test_config_invalid(
"homeassistant.components.mysensors.gateway.socket.getaddrinfo", "homeassistant.components.mysensors.gateway.socket.getaddrinfo",
side_effect=OSError, side_effect=OSError,
), patch( ), patch(
"homeassistant.components.mysensors.async_setup", return_value=True
) as mock_setup, patch(
"homeassistant.components.mysensors.async_setup_entry", "homeassistant.components.mysensors.async_setup_entry",
return_value=True, return_value=True,
) as mock_setup_entry: ) as mock_setup_entry:
...@@ -375,7 +361,6 @@ async def test_config_invalid( ...@@ -375,7 +361,6 @@ async def test_config_invalid(
assert errors assert errors
assert err_field in errors assert err_field in errors
assert errors[err_field] == err_string assert errors[err_field] == err_string
assert len(mock_setup.mock_calls) == 0
assert len(mock_setup_entry.mock_calls) == 0 assert len(mock_setup_entry.mock_calls) == 0
......
"""Provide tests for mysensors notify platform."""
from __future__ import annotations
from collections.abc import Callable
from unittest.mock import MagicMock, call
from mysensors.sensor import Sensor
from homeassistant.components.notify import DOMAIN as NOTIFY_DOMAIN
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
async def test_text_type(
hass: HomeAssistant,
text_node: Sensor,
transport_write: MagicMock,
integration: MockConfigEntry,
) -> None:
"""Test a text type child."""
# Test without target.
await hass.services.async_call(
NOTIFY_DOMAIN, "mysensors", {"message": "Hello World"}, blocking=True
)
assert transport_write.call_count == 1
assert transport_write.call_args == call("1;1;1;0;47;Hello World\n")
# Test with target.
await hass.services.async_call(
NOTIFY_DOMAIN,
"mysensors",
{"message": "Hello", "target": "Text Node 1 1"},
blocking=True,
)
assert transport_write.call_count == 2
assert transport_write.call_args == call("1;1;1;0;47;Hello\n")
transport_write.reset_mock()
# Test a message longer than 25 characters.
await hass.services.async_call(
NOTIFY_DOMAIN,
"mysensors",
{
"message": "This is a long message that will be split",
"target": "Text Node 1 1",
},
blocking=True,
)
assert transport_write.call_count == 2
assert transport_write.call_args_list == [
call("1;1;1;0;47;This is a long message th\n"),
call("1;1;1;0;47;at will be split\n"),
]
async def test_text_type_discovery(
hass: HomeAssistant,
text_node: Sensor,
transport_write: MagicMock,
receive_message: Callable[[str], None],
) -> None:
"""Test text type discovery."""
receive_message("1;2;0;0;36;\n")
receive_message("1;2;1;0;47;test\n")
receive_message("1;2;1;0;47;test2\n") # Test that more than one set message works.
await hass.async_block_till_done()
# Test targeting the discovered child.
await hass.services.async_call(
NOTIFY_DOMAIN,
"mysensors",
{"message": "Hello", "target": "Text Node 1 2"},
blocking=True,
)
assert transport_write.call_count == 1
assert transport_write.call_args == call("1;2;1;0;47;Hello\n")
transport_write.reset_mock()
# Test targeting all notify children.
await hass.services.async_call(
NOTIFY_DOMAIN, "mysensors", {"message": "Hello World"}, blocking=True
)
assert transport_write.call_count == 2
assert transport_write.call_args_list == [
call("1;1;1;0;47;Hello World\n"),
call("1;2;1;0;47;Hello World\n"),
]
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment