Remove deprecated notify platform from Mastodon (#149735)

This commit is contained in:
Andrew Jackson
2025-08-09 00:17:48 +01:00
committed by GitHub
parent 1af0282091
commit 775701133d
5 changed files with 7 additions and 253 deletions

View File

@@ -8,12 +8,11 @@ from homeassistant.const import (
CONF_ACCESS_TOKEN,
CONF_CLIENT_ID,
CONF_CLIENT_SECRET,
CONF_NAME,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import config_validation as cv, discovery
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.typing import ConfigType
from homeassistant.util import slugify
@@ -22,7 +21,7 @@ from .coordinator import MastodonConfigEntry, MastodonCoordinator, MastodonData
from .services import setup_services
from .utils import construct_mastodon_username, create_mastodon_client
PLATFORMS: list[Platform] = [Platform.NOTIFY, Platform.SENSOR]
PLATFORMS: list[Platform] = [Platform.SENSOR]
CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
@@ -53,26 +52,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: MastodonConfigEntry) ->
entry.runtime_data = MastodonData(client, instance, account, coordinator)
await discovery.async_load_platform(
hass,
Platform.NOTIFY,
DOMAIN,
{CONF_NAME: entry.title, "client": client},
{},
)
await hass.config_entries.async_forward_entry_setups(
entry, [platform for platform in PLATFORMS if platform != Platform.NOTIFY]
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: MastodonConfigEntry) -> bool:
"""Unload a config entry."""
return await hass.config_entries.async_unload_platforms(
entry, [platform for platform in PLATFORMS if platform != Platform.NOTIFY]
)
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
async def async_migrate_entry(hass: HomeAssistant, entry: MastodonConfigEntry) -> bool:

View File

@@ -1,152 +0,0 @@
"""Mastodon platform for notify component."""
from __future__ import annotations
from typing import Any, cast
from mastodon import Mastodon
from mastodon.Mastodon import MastodonAPIError, MediaAttachment
import voluptuous as vol
from homeassistant.components.notify import (
ATTR_DATA,
PLATFORM_SCHEMA as NOTIFY_PLATFORM_SCHEMA,
BaseNotificationService,
)
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_CLIENT_ID, CONF_CLIENT_SECRET
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv, issue_registry as ir
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from .const import (
ATTR_CONTENT_WARNING,
ATTR_MEDIA_WARNING,
CONF_BASE_URL,
DEFAULT_URL,
DOMAIN,
)
from .utils import get_media_type
ATTR_MEDIA = "media"
ATTR_TARGET = "target"
PLATFORM_SCHEMA = NOTIFY_PLATFORM_SCHEMA.extend(
{
vol.Required(CONF_ACCESS_TOKEN): cv.string,
vol.Required(CONF_CLIENT_ID): cv.string,
vol.Required(CONF_CLIENT_SECRET): cv.string,
vol.Optional(CONF_BASE_URL, default=DEFAULT_URL): cv.string,
}
)
INTEGRATION_TITLE = "Mastodon"
async def async_get_service(
hass: HomeAssistant,
config: ConfigType,
discovery_info: DiscoveryInfoType | None = None,
) -> MastodonNotificationService | None:
"""Get the Mastodon notification service."""
if discovery_info is None:
return None
client = cast(Mastodon, discovery_info.get("client"))
return MastodonNotificationService(hass, client)
class MastodonNotificationService(BaseNotificationService):
"""Implement the notification service for Mastodon."""
def __init__(
self,
hass: HomeAssistant,
client: Mastodon,
) -> None:
"""Initialize the service."""
self.client = client
def send_message(self, message: str = "", **kwargs: Any) -> None:
"""Toot a message, with media perhaps."""
ir.create_issue(
self.hass,
DOMAIN,
"deprecated_notify_action_mastodon",
breaks_in_ha_version="2025.9.0",
is_fixable=False,
issue_domain=DOMAIN,
severity=ir.IssueSeverity.WARNING,
translation_key="deprecated_notify_action",
)
target = None
if (target_list := kwargs.get(ATTR_TARGET)) is not None:
target = cast(list[str], target_list)[0]
data = kwargs.get(ATTR_DATA)
media = None
mediadata = None
sensitive = False
content_warning = None
if data:
media = data.get(ATTR_MEDIA)
if media:
if not self.hass.config.is_allowed_path(media):
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="not_whitelisted_directory",
translation_placeholders={"media": media},
)
mediadata = self._upload_media(media)
sensitive = data.get(ATTR_MEDIA_WARNING)
content_warning = data.get(ATTR_CONTENT_WARNING)
if mediadata:
try:
self.client.status_post(
message,
visibility=target,
spoiler_text=content_warning,
media_ids=mediadata.id,
sensitive=sensitive,
)
except MastodonAPIError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="unable_to_send_message",
) from err
else:
try:
self.client.status_post(
message, visibility=target, spoiler_text=content_warning
)
except MastodonAPIError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="unable_to_send_message",
) from err
def _upload_media(self, media_path: Any = None) -> MediaAttachment:
"""Upload media."""
with open(media_path, "rb"):
media_type = get_media_type(media_path)
try:
mediadata: MediaAttachment = self.client.media_post(
media_path, mime_type=media_type
)
except MastodonAPIError as err:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="unable_to_upload_image",
translation_placeholders={"media_path": media_path},
) from err
return mediadata

View File

@@ -26,10 +26,7 @@ rules:
unique-config-entry: done
# Silver
action-exceptions:
status: todo
comment: |
Awaiting legacy Notify deprecation.
action-exceptions: done
config-entry-unloading: done
docs-configuration-parameters:
status: exempt
@@ -39,19 +36,12 @@ rules:
entity-unavailable: done
integration-owner: done
log-when-unavailable: done
parallel-updates:
status: todo
comment: |
Awaiting legacy Notify deprecation.
parallel-updates: done
reauthentication-flow:
status: todo
comment: |
Waiting to move to oAuth.
test-coverage:
status: todo
comment: |
Awaiting legacy Notify deprecation.
test-coverage: done
# Gold
devices: done
diagnostics: done

View File

@@ -42,12 +42,6 @@
"message": "{media} is not a whitelisted directory."
}
},
"issues": {
"deprecated_notify_action": {
"title": "Deprecated Notify action used for Mastodon",
"description": "The Notify action for Mastodon is deprecated.\n\nUse the `mastodon.post` action instead."
}
},
"entity": {
"sensor": {
"followers": {

View File

@@ -1,65 +0,0 @@
"""Tests for the Mastodon notify platform."""
from unittest.mock import AsyncMock
from mastodon.Mastodon import MastodonAPIError
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.notify import DOMAIN as NOTIFY_DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import entity_registry as er
from . import setup_integration
from tests.common import MockConfigEntry
async def test_notify(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
entity_registry: er.EntityRegistry,
mock_mastodon_client: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test sending a message."""
await setup_integration(hass, mock_config_entry)
assert hass.services.has_service(NOTIFY_DOMAIN, "trwnh_mastodon_social")
await hass.services.async_call(
NOTIFY_DOMAIN,
"trwnh_mastodon_social",
{
"message": "test toot",
},
blocking=True,
return_response=False,
)
assert mock_mastodon_client.status_post.assert_called_once
async def test_notify_failed(
hass: HomeAssistant,
mock_mastodon_client: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test the notify raising an error."""
mock_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
mock_mastodon_client.status_post.side_effect = MastodonAPIError
with pytest.raises(HomeAssistantError, match="Unable to send message"):
await hass.services.async_call(
NOTIFY_DOMAIN,
"trwnh_mastodon_social",
{
"message": "test toot",
},
blocking=True,
return_response=False,
)