Skip to content

Commit

Permalink
Deprecate sensors in Habitica integration (home-assistant#134036)
Browse files Browse the repository at this point in the history
* Deprecate sensors

* move to setup, remove disabled

* changes

* add breaking version to string

* fixes

* fix entity id in tests
  • Loading branch information
tr4nt0r authored Jan 13, 2025
1 parent 8d38279 commit cdcc7db
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 19 deletions.
77 changes: 69 additions & 8 deletions homeassistant/components/habitica/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,26 @@
deserialize_task,
)

from homeassistant.components.automation import automations_with_entity
from homeassistant.components.script import scripts_with_entity
from homeassistant.components.sensor import (
DOMAIN as SENSOR_DOMAIN,
SensorDeviceClass,
SensorEntity,
SensorEntityDescription,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.issue_registry import (
IssueSeverity,
async_create_issue,
async_delete_issue,
)
from homeassistant.helpers.typing import StateType

from .const import ASSETS_URL
from .const import ASSETS_URL, DOMAIN
from .coordinator import HabiticaDataUpdateCoordinator
from .entity import HabiticaBase
from .types import HabiticaConfigEntry
from .util import get_attribute_points, get_attributes_total, inventory_list
Expand Down Expand Up @@ -269,6 +279,13 @@ class HabiticaSensorEntity(StrEnum):
)


def entity_used_in(hass: HomeAssistant, entity_id: str) -> list[str]:
"""Get list of related automations and scripts."""
used_in = automations_with_entity(hass, entity_id)
used_in += scripts_with_entity(hass, entity_id)
return used_in


async def async_setup_entry(
hass: HomeAssistant,
config_entry: HabiticaConfigEntry,
Expand All @@ -277,14 +294,58 @@ async def async_setup_entry(
"""Set up the habitica sensors."""

coordinator = config_entry.runtime_data
ent_reg = er.async_get(hass)
entities: list[SensorEntity] = []
description: SensorEntityDescription

def add_deprecated_entity(
description: SensorEntityDescription,
entity_cls: Callable[
[HabiticaDataUpdateCoordinator, SensorEntityDescription], SensorEntity
],
) -> None:
"""Add deprecated entities."""
if entity_id := ent_reg.async_get_entity_id(
SENSOR_DOMAIN,
DOMAIN,
f"{config_entry.unique_id}_{description.key}",
):
entity_entry = ent_reg.async_get(entity_id)
if entity_entry and entity_entry.disabled:
ent_reg.async_remove(entity_id)
async_delete_issue(
hass,
DOMAIN,
f"deprecated_entity_{description.key}",
)
elif entity_entry:
entities.append(entity_cls(coordinator, description))
if entity_used_in(hass, entity_id):
async_create_issue(
hass,
DOMAIN,
f"deprecated_entity_{description.key}",
breaks_in_ha_version="2025.8.0",
is_fixable=False,
severity=IssueSeverity.WARNING,
translation_key="deprecated_entity",
translation_placeholders={
"name": str(
entity_entry.name or entity_entry.original_name
),
"entity": entity_id,
},
)

for description in SENSOR_DESCRIPTIONS:
if description.key is HabiticaSensorEntity.HEALTH_MAX:
add_deprecated_entity(description, HabiticaSensor)
else:
entities.append(HabiticaSensor(coordinator, description))

for description in TASK_SENSOR_DESCRIPTION:
add_deprecated_entity(description, HabiticaTaskSensor)

entities: list[SensorEntity] = [
HabiticaSensor(coordinator, description) for description in SENSOR_DESCRIPTIONS
]
entities.extend(
HabiticaTaskSensor(coordinator, description)
for description in TASK_SENSOR_DESCRIPTION
)
async_add_entities(entities, True)


Expand Down
4 changes: 4 additions & 0 deletions homeassistant/components/habitica/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,10 @@
}
},
"issues": {
"deprecated_entity": {
"title": "The Habitica {name} entity is deprecated",
"description": "The Habitica entity `{entity}` is deprecated and will be removed in a future release.\nPlease update your automations and scripts, disable `{entity}` and reload the integration/restart Home Assistant to fix this issue."
},
"deprecated_api_call": {
"title": "The Habitica action habitica.api_call is deprecated",
"description": "The Habitica action `habitica.api_call` is deprecated and will be removed in Home Assistant 2025.5.0.\n\nPlease update your automations and scripts to use other Habitica actions and entities."
Expand Down
10 changes: 0 additions & 10 deletions homeassistant/components/habitica/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@
)
from habiticalib import ContentData, Frequency, TaskData, UserData

from homeassistant.components.automation import automations_with_entity
from homeassistant.components.script import scripts_with_entity
from homeassistant.core import HomeAssistant
from homeassistant.util import dt as dt_util


Expand Down Expand Up @@ -59,13 +56,6 @@ def next_due_date(task: TaskData, today: datetime.datetime) -> datetime.date | N
return dt_util.as_local(task.nextDue[0]).date()


def entity_used_in(hass: HomeAssistant, entity_id: str) -> list[str]:
"""Get list of related automations and scripts."""
used_in = automations_with_entity(hass, entity_id)
used_in += scripts_with_entity(hass, entity_id)
return used_in


FREQUENCY_MAP = {"daily": DAILY, "weekly": WEEKLY, "monthly": MONTHLY, "yearly": YEARLY}
WEEKDAY_MAP = {"m": MO, "t": TU, "w": WE, "th": TH, "f": FR, "s": SA, "su": SU}

Expand Down
111 changes: 110 additions & 1 deletion tests/components/habitica/test_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
import pytest
from syrupy.assertion import SnapshotAssertion

from homeassistant.components.habitica.const import DOMAIN
from homeassistant.components.habitica.sensor import HabiticaSensorEntity
from homeassistant.components.sensor.const import DOMAIN as SENSOR_DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers import entity_registry as er, issue_registry as ir

from tests.common import MockConfigEntry, snapshot_platform

Expand All @@ -33,10 +36,116 @@ async def test_sensors(
) -> None:
"""Test setup of the Habitica sensor platform."""

for entity in (
("test_user_habits", "habits"),
("test_user_rewards", "rewards"),
("test_user_max_health", "health_max"),
):
entity_registry.async_get_or_create(
SENSOR_DOMAIN,
DOMAIN,
f"a380546a-94be-4b8e-8a0b-23e0d5c03303_{entity[1]}",
suggested_object_id=entity[0],
disabled_by=None,
)

config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()

assert config_entry.state is ConfigEntryState.LOADED

await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id)


@pytest.mark.parametrize(
("entity_id", "key"),
[
("test_user_habits", HabiticaSensorEntity.HABITS),
("test_user_rewards", HabiticaSensorEntity.REWARDS),
("test_user_max_health", HabiticaSensorEntity.HEALTH_MAX),
],
)
@pytest.mark.usefixtures("habitica", "entity_registry_enabled_by_default")
async def test_sensor_deprecation_issue(
hass: HomeAssistant,
config_entry: MockConfigEntry,
issue_registry: ir.IssueRegistry,
entity_registry: er.EntityRegistry,
entity_id: str,
key: HabiticaSensorEntity,
) -> None:
"""Test sensor deprecation issue."""
entity_registry.async_get_or_create(
SENSOR_DOMAIN,
DOMAIN,
f"a380546a-94be-4b8e-8a0b-23e0d5c03303_{key}",
suggested_object_id=entity_id,
disabled_by=None,
)

assert entity_registry is not None
with patch(
"homeassistant.components.habitica.sensor.entity_used_in", return_value=True
):
config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id)

await hass.async_block_till_done()

assert config_entry.state is ConfigEntryState.LOADED

assert entity_registry.async_get(f"sensor.{entity_id}") is not None
assert issue_registry.async_get_issue(
domain=DOMAIN,
issue_id=f"deprecated_entity_{key}",
)


@pytest.mark.parametrize(
("entity_id", "key"),
[
("test_user_habits", HabiticaSensorEntity.HABITS),
("test_user_rewards", HabiticaSensorEntity.REWARDS),
("test_user_max_health", HabiticaSensorEntity.HEALTH_MAX),
],
)
@pytest.mark.usefixtures("habitica", "entity_registry_enabled_by_default")
async def test_sensor_deprecation_delete_disabled(
hass: HomeAssistant,
config_entry: MockConfigEntry,
issue_registry: ir.IssueRegistry,
entity_registry: er.EntityRegistry,
entity_id: str,
key: HabiticaSensorEntity,
) -> None:
"""Test sensor deletion ."""

entity_registry.async_get_or_create(
SENSOR_DOMAIN,
DOMAIN,
f"a380546a-94be-4b8e-8a0b-23e0d5c03303_{key}",
suggested_object_id=entity_id,
disabled_by=er.RegistryEntryDisabler.USER,
)

assert entity_registry is not None
with patch(
"homeassistant.components.habitica.sensor.entity_used_in", return_value=True
):
config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id)

await hass.async_block_till_done()

assert config_entry.state is ConfigEntryState.LOADED

assert (
issue_registry.async_get_issue(
domain=DOMAIN,
issue_id=f"deprecated_entity_{key}",
)
is None
)

assert entity_registry.async_get(f"sensor.{entity_id}") is None

0 comments on commit cdcc7db

Please sign in to comment.