From e44cfa00afaa8f795719b29ae3f66fe8f9d01b15 Mon Sep 17 00:00:00 2001 From: G Johansson Date: Fri, 24 Jan 2025 08:43:18 +0100 Subject: [PATCH] Remove deprecated 17track package sensor (#136389) --- .../components/seventeentrack/const.py | 3 - .../components/seventeentrack/repairs.py | 49 ----- .../components/seventeentrack/sensor.py | 158 +--------------- .../components/seventeentrack/strings.json | 13 -- .../components/seventeentrack/test_repairs.py | 86 --------- .../components/seventeentrack/test_sensor.py | 170 +----------------- 6 files changed, 3 insertions(+), 476 deletions(-) delete mode 100644 homeassistant/components/seventeentrack/repairs.py delete mode 100644 tests/components/seventeentrack/test_repairs.py diff --git a/homeassistant/components/seventeentrack/const.py b/homeassistant/components/seventeentrack/const.py index 6b888590600..19e2d3083c9 100644 --- a/homeassistant/components/seventeentrack/const.py +++ b/homeassistant/components/seventeentrack/const.py @@ -47,6 +47,3 @@ SERVICE_ARCHIVE_PACKAGE = "archive_package" ATTR_PACKAGE_STATE = "package_state" ATTR_PACKAGE_TRACKING_NUMBER = "package_tracking_number" ATTR_CONFIG_ENTRY_ID = "config_entry_id" - - -DEPRECATED_KEY = "deprecated" diff --git a/homeassistant/components/seventeentrack/repairs.py b/homeassistant/components/seventeentrack/repairs.py deleted file mode 100644 index ce72960ea91..00000000000 --- a/homeassistant/components/seventeentrack/repairs.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Repairs for the SeventeenTrack integration.""" - -import voluptuous as vol - -from homeassistant.components.repairs import ConfirmRepairFlow, RepairsFlow -from homeassistant.config_entries import ConfigEntry -from homeassistant.core import HomeAssistant -from homeassistant.data_entry_flow import FlowResult - -from .const import DEPRECATED_KEY - - -class SensorDeprecationRepairFlow(RepairsFlow): - """Handler for an issue fixing flow.""" - - def __init__(self, entry: ConfigEntry) -> None: - """Create flow.""" - self.entry = entry - - async def async_step_init( - self, user_input: dict[str, str] | None = None - ) -> FlowResult: - """Handle the first step of a fix flow.""" - return await self.async_step_confirm() - - async def async_step_confirm( - self, user_input: dict[str, str] | None = None - ) -> FlowResult: - """Handle the confirm step of a fix flow.""" - if user_input is not None: - data = {**self.entry.data, DEPRECATED_KEY: True} - self.hass.config_entries.async_update_entry(self.entry, data=data) - return self.async_create_entry(data={}) - - return self.async_show_form( - step_id="confirm", - data_schema=vol.Schema({}), - ) - - -async def async_create_fix_flow( - hass: HomeAssistant, issue_id: str, data: dict -) -> RepairsFlow: - """Create flow.""" - if issue_id.startswith("deprecate_sensor_") and ( - entry := hass.config_entries.async_get_entry(data["entry_id"]) - ): - return SensorDeprecationRepairFlow(entry) - return ConfirmRepairFlow() diff --git a/homeassistant/components/seventeentrack/sensor.py b/homeassistant/components/seventeentrack/sensor.py index 4e561a87961..dade9efb67c 100644 --- a/homeassistant/components/seventeentrack/sensor.py +++ b/homeassistant/components/seventeentrack/sensor.py @@ -4,12 +4,10 @@ from __future__ import annotations from typing import Any -from homeassistant.components import persistent_notification from homeassistant.components.sensor import SensorEntity from homeassistant.config_entries import ConfigEntry from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_LOCATION -from homeassistant.core import HomeAssistant, callback -from homeassistant.helpers import entity_registry as er, issue_registry as ir +from homeassistant.core import HomeAssistant from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import StateType @@ -17,23 +15,13 @@ from homeassistant.helpers.update_coordinator import CoordinatorEntity from . import SeventeenTrackCoordinator from .const import ( - ATTR_DESTINATION_COUNTRY, ATTR_INFO_TEXT, - ATTR_ORIGIN_COUNTRY, - ATTR_PACKAGE_TYPE, ATTR_PACKAGES, ATTR_STATUS, ATTR_TIMESTAMP, - ATTR_TRACKING_INFO_LANGUAGE, ATTR_TRACKING_NUMBER, ATTRIBUTION, - DEPRECATED_KEY, DOMAIN, - LOGGER, - NOTIFICATION_DELIVERED_MESSAGE, - NOTIFICATION_DELIVERED_TITLE, - UNIQUE_ID_TEMPLATE, - VALUE_DELIVERED, ) @@ -45,63 +33,12 @@ async def async_setup_entry( """Set up a 17Track sensor entry.""" coordinator: SeventeenTrackCoordinator = hass.data[DOMAIN][config_entry.entry_id] - previous_tracking_numbers: set[str] = set() - - # This has been deprecated in 2024.8, will be removed in 2025.2 - @callback - def _async_create_remove_entities(): - if config_entry.data.get(DEPRECATED_KEY): - remove_packages(hass, coordinator.account_id, previous_tracking_numbers) - return - live_tracking_numbers = set(coordinator.data.live_packages.keys()) - - new_tracking_numbers = live_tracking_numbers - previous_tracking_numbers - old_tracking_numbers = previous_tracking_numbers - live_tracking_numbers - - previous_tracking_numbers.update(live_tracking_numbers) - - packages_to_add = [ - coordinator.data.live_packages[tracking_number] - for tracking_number in new_tracking_numbers - ] - - for package_data in coordinator.data.live_packages.values(): - if ( - package_data.status == VALUE_DELIVERED - and not coordinator.show_delivered - ): - old_tracking_numbers.add(package_data.tracking_number) - notify_delivered( - hass, - package_data.friendly_name, - package_data.tracking_number, - ) - - remove_packages(hass, coordinator.account_id, old_tracking_numbers) - - async_add_entities( - SeventeenTrackPackageSensor( - coordinator, - package_data.tracking_number, - ) - for package_data in packages_to_add - if not ( - not coordinator.show_delivered and package_data.status == "Delivered" - ) - ) async_add_entities( SeventeenTrackSummarySensor(status, coordinator) for status, summary_data in coordinator.data.summary.items() ) - if not config_entry.data.get(DEPRECATED_KEY): - deprecate_sensor_issue(hass, config_entry.entry_id) - _async_create_remove_entities() - config_entry.async_on_unload( - coordinator.async_add_listener(_async_create_remove_entities) - ) - class SeventeenTrackSensor(CoordinatorEntity[SeventeenTrackCoordinator], SensorEntity): """Define a 17Track sensor.""" @@ -163,96 +100,3 @@ class SeventeenTrackSummarySensor(SeventeenTrackSensor): for package in packages ] } - - -# The dynamic package sensors have been replaced by the seventeentrack.get_packages service -class SeventeenTrackPackageSensor(SeventeenTrackSensor): - """Define an individual package sensor.""" - - _attr_translation_key = "package" - - def __init__( - self, - coordinator: SeventeenTrackCoordinator, - tracking_number: str, - ) -> None: - """Initialize the sensor.""" - super().__init__(coordinator) - self._tracking_number = tracking_number - self._previous_status = coordinator.data.live_packages[tracking_number].status - self._attr_unique_id = UNIQUE_ID_TEMPLATE.format( - coordinator.account_id, tracking_number - ) - package = coordinator.data.live_packages[tracking_number] - if not (name := package.friendly_name): - name = tracking_number - self._attr_translation_placeholders = {"name": name} - - @property - def available(self) -> bool: - """Return whether the entity is available.""" - return self._tracking_number in self.coordinator.data.live_packages - - @property - def native_value(self) -> StateType: - """Return the state.""" - return self.coordinator.data.live_packages[self._tracking_number].status - - @property - def extra_state_attributes(self) -> dict[str, Any] | None: - """Return the state attributes.""" - package = self.coordinator.data.live_packages[self._tracking_number] - return { - ATTR_DESTINATION_COUNTRY: package.destination_country, - ATTR_INFO_TEXT: package.info_text, - ATTR_TIMESTAMP: package.timestamp, - ATTR_LOCATION: package.location, - ATTR_ORIGIN_COUNTRY: package.origin_country, - ATTR_PACKAGE_TYPE: package.package_type, - ATTR_TRACKING_INFO_LANGUAGE: package.tracking_info_language, - ATTR_TRACKING_NUMBER: package.tracking_number, - } - - -def remove_packages(hass: HomeAssistant, account_id: str, packages: set[str]) -> None: - """Remove entity itself.""" - reg = er.async_get(hass) - for package in packages: - entity_id = reg.async_get_entity_id( - "sensor", - "seventeentrack", - UNIQUE_ID_TEMPLATE.format(account_id, package), - ) - if entity_id: - reg.async_remove(entity_id) - - -def notify_delivered(hass: HomeAssistant, friendly_name: str, tracking_number: str): - """Notify when package is delivered.""" - LOGGER.debug("Package delivered: %s", tracking_number) - - identification = friendly_name if friendly_name else tracking_number - message = NOTIFICATION_DELIVERED_MESSAGE.format(identification, tracking_number) - title = NOTIFICATION_DELIVERED_TITLE.format(identification) - notification_id = NOTIFICATION_DELIVERED_TITLE.format(tracking_number) - - persistent_notification.create( - hass, message, title=title, notification_id=notification_id - ) - - -@callback -def deprecate_sensor_issue(hass: HomeAssistant, entry_id: str) -> None: - """Ensure an issue is registered.""" - ir.async_create_issue( - hass, - DOMAIN, - f"deprecate_sensor_{entry_id}", - breaks_in_ha_version="2025.2.0", - issue_domain=DOMAIN, - is_fixable=True, - is_persistent=True, - translation_key="deprecate_sensor", - severity=ir.IssueSeverity.WARNING, - data={"entry_id": entry_id}, - ) diff --git a/homeassistant/components/seventeentrack/strings.json b/homeassistant/components/seventeentrack/strings.json index bbd01ed3055..982b15ab629 100644 --- a/homeassistant/components/seventeentrack/strings.json +++ b/homeassistant/components/seventeentrack/strings.json @@ -37,19 +37,6 @@ } } }, - "issues": { - "deprecate_sensor": { - "title": "17Track package sensors are being deprecated", - "fix_flow": { - "step": { - "confirm": { - "title": "[%key:component::seventeentrack::issues::deprecate_sensor::title%]", - "description": "17Track package sensors are deprecated and will be removed.\nPlease update your automations and scripts to get data using the `seventeentrack.get_packages` action." - } - } - } - } - }, "entity": { "sensor": { "not_found": { diff --git a/tests/components/seventeentrack/test_repairs.py b/tests/components/seventeentrack/test_repairs.py deleted file mode 100644 index 44d1f078432..00000000000 --- a/tests/components/seventeentrack/test_repairs.py +++ /dev/null @@ -1,86 +0,0 @@ -"""Tests for the seventeentrack repair flow.""" - -from unittest.mock import AsyncMock - -from freezegun.api import FrozenDateTimeFactory - -from homeassistant.components.repairs import DOMAIN as REPAIRS_DOMAIN -from homeassistant.components.seventeentrack import DOMAIN -from homeassistant.core import HomeAssistant -from homeassistant.helpers import issue_registry as ir -from homeassistant.setup import async_setup_component - -from . import goto_future, init_integration -from .conftest import DEFAULT_SUMMARY_LENGTH, get_package - -from tests.common import MockConfigEntry -from tests.components.repairs import process_repair_fix_flow, start_repair_fix_flow -from tests.typing import ClientSessionGenerator - - -async def test_repair( - hass: HomeAssistant, - mock_seventeentrack: AsyncMock, - issue_registry: ir.IssueRegistry, - hass_client: ClientSessionGenerator, - mock_config_entry: MockConfigEntry, - freezer: FrozenDateTimeFactory, -) -> None: - """Ensure everything starts correctly.""" - await init_integration(hass, mock_config_entry) # 2 - assert len(hass.states.async_entity_ids()) == DEFAULT_SUMMARY_LENGTH - assert len(issue_registry.issues) == 1 - - package = get_package() - mock_seventeentrack.return_value.profile.packages.return_value = [package] - await goto_future(hass, freezer) - - assert hass.states.get("sensor.17track_package_friendly_name_1") - assert len(hass.states.async_entity_ids()) == DEFAULT_SUMMARY_LENGTH + 1 - - assert "deprecated" not in mock_config_entry.data - - repair_issue = issue_registry.async_get_issue( - domain=DOMAIN, issue_id=f"deprecate_sensor_{mock_config_entry.entry_id}" - ) - - assert await async_setup_component(hass, REPAIRS_DOMAIN, {REPAIRS_DOMAIN: {}}) - - client = await hass_client() - - data = await start_repair_fix_flow(client, DOMAIN, repair_issue.issue_id) - - flow_id = data["flow_id"] - assert data == { - "type": "form", - "flow_id": flow_id, - "handler": DOMAIN, - "step_id": "confirm", - "data_schema": [], - "errors": None, - "description_placeholders": None, - "last_step": None, - "preview": None, - } - - data = await process_repair_fix_flow(client, flow_id) - - flow_id = data["flow_id"] - assert data == { - "type": "create_entry", - "handler": DOMAIN, - "flow_id": flow_id, - "description": None, - "description_placeholders": None, - } - - assert mock_config_entry.data["deprecated"] - - repair_issue = issue_registry.async_get_issue( - domain=DOMAIN, issue_id="deprecate_sensor" - ) - - assert repair_issue is None - - await goto_future(hass, freezer) - assert len(hass.states.async_entity_ids()) == DEFAULT_SUMMARY_LENGTH diff --git a/tests/components/seventeentrack/test_sensor.py b/tests/components/seventeentrack/test_sensor.py index a631996b4eb..5367fabba9e 100644 --- a/tests/components/seventeentrack/test_sensor.py +++ b/tests/components/seventeentrack/test_sensor.py @@ -2,7 +2,7 @@ from __future__ import annotations -from unittest.mock import AsyncMock, patch +from unittest.mock import AsyncMock from freezegun.api import FrozenDateTimeFactory from pyseventeentrack.errors import SeventeenTrackError @@ -63,87 +63,6 @@ async def test_login_exception( assert not hass.states.async_entity_ids("sensor") -async def test_add_package( - hass: HomeAssistant, - freezer: FrozenDateTimeFactory, - mock_seventeentrack: AsyncMock, - mock_config_entry: MockConfigEntry, -) -> None: - """Ensure package is added correctly when user add a new package.""" - package = get_package() - mock_seventeentrack.return_value.profile.packages.return_value = [package] - - await init_integration(hass, mock_config_entry) - assert hass.states.get("sensor.17track_package_friendly_name_1") - assert len(hass.states.async_entity_ids()) == DEFAULT_SUMMARY_LENGTH + 1 - - package2 = get_package( - tracking_number="789", - friendly_name="friendly name 2", - info_text="info text 2", - location="location 2", - timestamp="2020-08-10 14:25", - ) - mock_seventeentrack.return_value.profile.packages.return_value = [package, package2] - - await goto_future(hass, freezer) - - assert hass.states.get("sensor.17track_package_friendly_name_1") is not None - assert len(hass.states.async_entity_ids()) == DEFAULT_SUMMARY_LENGTH + 2 - - -async def test_add_package_default_friendly_name( - hass: HomeAssistant, - mock_seventeentrack: AsyncMock, - mock_config_entry: MockConfigEntry, -) -> None: - """Ensure package is added correctly with default friendly name when user add a new package without his own friendly name.""" - package = get_package(friendly_name=None) - mock_seventeentrack.return_value.profile.packages.return_value = [package] - - await init_integration(hass, mock_config_entry) - state_456 = hass.states.get("sensor.17track_package_456") - assert state_456 is not None - assert state_456.attributes["friendly_name"] == "17Track Package 456" - assert len(hass.states.async_entity_ids()) == DEFAULT_SUMMARY_LENGTH + 1 - - -async def test_remove_package( - hass: HomeAssistant, - freezer: FrozenDateTimeFactory, - mock_seventeentrack: AsyncMock, - mock_config_entry: MockConfigEntry, -) -> None: - """Ensure entity is not there anymore if package is not there.""" - package1 = get_package() - package2 = get_package( - tracking_number="789", - friendly_name="friendly name 2", - info_text="info text 2", - location="location 2", - timestamp="2020-08-10 14:25", - ) - - mock_seventeentrack.return_value.profile.packages.return_value = [ - package1, - package2, - ] - - await init_integration(hass, mock_config_entry) - - assert hass.states.get("sensor.17track_package_friendly_name_1") is not None - assert hass.states.get("sensor.17track_package_friendly_name_2") is not None - assert len(hass.states.async_entity_ids()) == DEFAULT_SUMMARY_LENGTH + 2 - - mock_seventeentrack.return_value.profile.packages.return_value = [package2] - - await goto_future(hass, freezer) - - assert hass.states.get("sensor.17track_package_friendly_name_1") is None - assert hass.states.get("sensor.17track_package_friendly_name_2") is not None - assert len(hass.states.async_entity_ids()) == DEFAULT_SUMMARY_LENGTH + 1 - - async def test_package_error( hass: HomeAssistant, mock_seventeentrack: AsyncMock, @@ -159,72 +78,6 @@ async def test_package_error( assert hass.states.get("sensor.17track_package_friendly_name_1") is None -async def test_delivered_not_shown( - hass: HomeAssistant, - freezer: FrozenDateTimeFactory, - mock_seventeentrack: AsyncMock, - mock_config_entry_with_default_options: MockConfigEntry, -) -> None: - """Ensure delivered packages are not shown.""" - package = get_package(status=40) - mock_seventeentrack.return_value.profile.packages.return_value = [package] - - with patch( - "homeassistant.components.seventeentrack.sensor.persistent_notification" - ) as persistent_notification_mock: - await init_integration(hass, mock_config_entry_with_default_options) - await goto_future(hass, freezer) - - assert hass.states.get("sensor.17track_package_friendly_name_1") is None - persistent_notification_mock.create.assert_called() - - -async def test_delivered_shown( - hass: HomeAssistant, - mock_seventeentrack: AsyncMock, - mock_config_entry: MockConfigEntry, -) -> None: - """Ensure delivered packages are show when user choose to show them.""" - package = get_package(status=40) - mock_seventeentrack.return_value.profile.packages.return_value = [package] - - with patch( - "homeassistant.components.seventeentrack.sensor.persistent_notification" - ) as persistent_notification_mock: - await init_integration(hass, mock_config_entry) - - assert hass.states.get("sensor.17track_package_friendly_name_1") is not None - assert len(hass.states.async_entity_ids()) == DEFAULT_SUMMARY_LENGTH + 1 - persistent_notification_mock.create.assert_not_called() - - -async def test_becomes_delivered_not_shown_notification( - hass: HomeAssistant, - freezer: FrozenDateTimeFactory, - mock_seventeentrack: AsyncMock, - mock_config_entry_with_default_options: MockConfigEntry, -) -> None: - """Ensure notification is triggered when package becomes delivered.""" - package = get_package() - mock_seventeentrack.return_value.profile.packages.return_value = [package] - - await init_integration(hass, mock_config_entry_with_default_options) - - assert hass.states.get("sensor.17track_package_friendly_name_1") is not None - assert len(hass.states.async_entity_ids()) == DEFAULT_SUMMARY_LENGTH + 1 - - package_delivered = get_package(status=40) - mock_seventeentrack.return_value.profile.packages.return_value = [package_delivered] - - with patch( - "homeassistant.components.seventeentrack.sensor.persistent_notification" - ) as persistent_notification_mock: - await goto_future(hass, freezer) - - persistent_notification_mock.create.assert_called() - assert len(hass.states.async_entity_ids()) == DEFAULT_SUMMARY_LENGTH - - async def test_summary_correctly_updated( hass: HomeAssistant, freezer: FrozenDateTimeFactory, @@ -237,7 +90,7 @@ async def test_summary_correctly_updated( await init_integration(hass, mock_config_entry) - assert len(hass.states.async_entity_ids()) == DEFAULT_SUMMARY_LENGTH + 1 + assert len(hass.states.async_entity_ids()) == DEFAULT_SUMMARY_LENGTH state_ready_picked = hass.states.get("sensor.17track_ready_to_be_picked_up") assert state_ready_picked is not None @@ -278,25 +131,6 @@ async def test_summary_error( ) -async def test_utc_timestamp( - hass: HomeAssistant, - mock_seventeentrack: AsyncMock, - mock_config_entry: MockConfigEntry, -) -> None: - """Ensure package timestamp is converted correctly from HA-defined time zone to UTC.""" - - package = get_package(tz="Asia/Jakarta") - mock_seventeentrack.return_value.profile.packages.return_value = [package] - - await init_integration(hass, mock_config_entry) - - assert hass.states.get("sensor.17track_package_friendly_name_1") is not None - assert len(hass.states.async_entity_ids()) == DEFAULT_SUMMARY_LENGTH + 1 - state_456 = hass.states.get("sensor.17track_package_friendly_name_1") - assert state_456 is not None - assert str(state_456.attributes.get("timestamp")) == "2020-08-10 03:32:00+00:00" - - async def test_non_valid_platform_config( hass: HomeAssistant, mock_seventeentrack: AsyncMock ) -> None: