Skip to content

Commit 41e5329

Browse files
authored
Add update entity to immich integration (home-assistant#147273)
* add update entity * remove unneccessary entity description * rename update entity to version * simplify test * define static attribute outside of the constructor * move min version check into coordinator
1 parent d4e7667 commit 41e5329

File tree

8 files changed

+209
-20
lines changed

8 files changed

+209
-20
lines changed

homeassistant/components/immich/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
from .coordinator import ImmichConfigEntry, ImmichDataUpdateCoordinator
2222

23-
PLATFORMS: list[Platform] = [Platform.SENSOR]
23+
PLATFORMS: list[Platform] = [Platform.SENSOR, Platform.UPDATE]
2424

2525

2626
async def async_setup_entry(hass: HomeAssistant, entry: ImmichConfigEntry) -> bool:

homeassistant/components/immich/coordinator.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
ImmichServerAbout,
1414
ImmichServerStatistics,
1515
ImmichServerStorage,
16+
ImmichServerVersionCheck,
1617
)
18+
from awesomeversion import AwesomeVersion
1719

1820
from homeassistant.config_entries import ConfigEntry
1921
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_SSL
@@ -33,6 +35,7 @@ class ImmichData:
3335
server_about: ImmichServerAbout
3436
server_storage: ImmichServerStorage
3537
server_usage: ImmichServerStatistics | None
38+
server_version_check: ImmichServerVersionCheck | None
3639

3740

3841
type ImmichConfigEntry = ConfigEntry[ImmichDataUpdateCoordinator]
@@ -71,9 +74,16 @@ async def _async_update_data(self) -> ImmichData:
7174
if self.is_admin
7275
else None
7376
)
77+
server_version_check = (
78+
await self.api.server.async_get_version_check()
79+
if AwesomeVersion(server_about.version) >= AwesomeVersion("v1.134.0")
80+
else None
81+
)
7482
except ImmichUnauthorizedError as err:
7583
raise ConfigEntryAuthFailed from err
7684
except CONNECT_ERRORS as err:
7785
raise UpdateFailed from err
7886

79-
return ImmichData(server_about, server_storage, server_usage)
87+
return ImmichData(
88+
server_about, server_storage, server_usage, server_version_check
89+
)

homeassistant/components/immich/strings.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@
6868
"usage_by_videos": {
6969
"name": "Disk used by videos"
7070
}
71+
},
72+
"update": {
73+
"update": {
74+
"name": "Version"
75+
}
7176
}
7277
}
7378
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
"""Update platform for the Immich integration."""
2+
3+
from __future__ import annotations
4+
5+
from homeassistant.components.update import UpdateEntity
6+
from homeassistant.core import HomeAssistant
7+
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
8+
9+
from .coordinator import ImmichConfigEntry, ImmichDataUpdateCoordinator
10+
from .entity import ImmichEntity
11+
12+
# Coordinator is used to centralize the data updates
13+
PARALLEL_UPDATES = 0
14+
15+
16+
async def async_setup_entry(
17+
hass: HomeAssistant,
18+
entry: ImmichConfigEntry,
19+
async_add_entities: AddConfigEntryEntitiesCallback,
20+
) -> None:
21+
"""Add immich server update entity."""
22+
coordinator = entry.runtime_data
23+
24+
if coordinator.data.server_version_check is not None:
25+
async_add_entities([ImmichUpdateEntity(coordinator)])
26+
27+
28+
class ImmichUpdateEntity(ImmichEntity, UpdateEntity):
29+
"""Define Immich update entity."""
30+
31+
_attr_translation_key = "update"
32+
33+
def __init__(
34+
self,
35+
coordinator: ImmichDataUpdateCoordinator,
36+
) -> None:
37+
"""Initialize."""
38+
super().__init__(coordinator)
39+
self._attr_unique_id = f"{coordinator.config_entry.unique_id}_update"
40+
41+
@property
42+
def installed_version(self) -> str:
43+
"""Current installed immich server version."""
44+
return self.coordinator.data.server_about.version
45+
46+
@property
47+
def latest_version(self) -> str:
48+
"""Available new immich server version."""
49+
assert self.coordinator.data.server_version_check
50+
return self.coordinator.data.server_version_check.release_version
51+
52+
@property
53+
def release_url(self) -> str | None:
54+
"""URL to the full release notes of the new immich server version."""
55+
return (
56+
f"https://github.com/immich-app/immich/releases/tag/{self.latest_version}"
57+
)

tests/components/immich/conftest.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
ImmichServerAbout,
99
ImmichServerStatistics,
1010
ImmichServerStorage,
11+
ImmichServerVersionCheck,
1112
)
1213
from aioimmich.users.models import ImmichUserObject
1314
import pytest
@@ -79,21 +80,21 @@ def mock_immich_server() -> AsyncMock:
7980
mock = AsyncMock(spec=ImmichServer)
8081
mock.async_get_about_info.return_value = ImmichServerAbout.from_dict(
8182
{
82-
"version": "v1.132.3",
83-
"versionUrl": "https://github.com/immich-app/immich/releases/tag/v1.132.3",
83+
"version": "v1.134.0",
84+
"versionUrl": "https://github.com/immich-app/immich/releases/tag/v1.134.0",
8485
"licensed": False,
85-
"build": "14709928600",
86-
"buildUrl": "https://github.com/immich-app/immich/actions/runs/14709928600",
87-
"buildImage": "v1.132.3",
86+
"build": "15281783550",
87+
"buildUrl": "https://github.com/immich-app/immich/actions/runs/15281783550",
88+
"buildImage": "v1.134.0",
8889
"buildImageUrl": "https://github.com/immich-app/immich/pkgs/container/immich-server",
8990
"repository": "immich-app/immich",
9091
"repositoryUrl": "https://github.com/immich-app/immich",
91-
"sourceRef": "v1.132.3",
92-
"sourceCommit": "02994883fe3f3972323bb6759d0170a4062f5236",
93-
"sourceUrl": "https://github.com/immich-app/immich/commit/02994883fe3f3972323bb6759d0170a4062f5236",
92+
"sourceRef": "v1.134.0",
93+
"sourceCommit": "58ae77ec9204a2e43a8cb2f1fd27482af40d0891",
94+
"sourceUrl": "https://github.com/immich-app/immich/commit/58ae77ec9204a2e43a8cb2f1fd27482af40d0891",
9495
"nodejs": "v22.14.0",
9596
"exiftool": "13.00",
96-
"ffmpeg": "7.0.2-7",
97+
"ffmpeg": "7.0.2-9",
9798
"libvips": "8.16.1",
9899
"imagemagick": "7.1.1-47",
99100
}
@@ -130,6 +131,12 @@ def mock_immich_server() -> AsyncMock:
130131
],
131132
}
132133
)
134+
mock.async_get_version_check.return_value = ImmichServerVersionCheck.from_dict(
135+
{
136+
"checkedAt": "2025-06-21T16:35:10.352Z",
137+
"releaseVersion": "v1.135.3",
138+
}
139+
)
133140
return mock
134141

135142

tests/components/immich/snapshots/test_diagnostics.ambr

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,23 @@
33
dict({
44
'data': dict({
55
'server_about': dict({
6-
'build': '14709928600',
7-
'build_image': 'v1.132.3',
6+
'build': '15281783550',
7+
'build_image': 'v1.134.0',
88
'build_image_url': 'https://github.com/immich-app/immich/pkgs/container/immich-server',
9-
'build_url': 'https://github.com/immich-app/immich/actions/runs/14709928600',
9+
'build_url': 'https://github.com/immich-app/immich/actions/runs/15281783550',
1010
'exiftool': '13.00',
11-
'ffmpeg': '7.0.2-7',
11+
'ffmpeg': '7.0.2-9',
1212
'imagemagick': '7.1.1-47',
1313
'libvips': '8.16.1',
1414
'licensed': False,
1515
'nodejs': 'v22.14.0',
1616
'repository': 'immich-app/immich',
1717
'repository_url': 'https://github.com/immich-app/immich',
18-
'source_commit': '02994883fe3f3972323bb6759d0170a4062f5236',
19-
'source_ref': 'v1.132.3',
20-
'source_url': 'https://github.com/immich-app/immich/commit/02994883fe3f3972323bb6759d0170a4062f5236',
21-
'version': 'v1.132.3',
22-
'version_url': 'https://github.com/immich-app/immich/releases/tag/v1.132.3',
18+
'source_commit': '58ae77ec9204a2e43a8cb2f1fd27482af40d0891',
19+
'source_ref': 'v1.134.0',
20+
'source_url': 'https://github.com/immich-app/immich/commit/58ae77ec9204a2e43a8cb2f1fd27482af40d0891',
21+
'version': 'v1.134.0',
22+
'version_url': 'https://github.com/immich-app/immich/releases/tag/v1.134.0',
2323
}),
2424
'server_storage': dict({
2525
'disk_available': '136.3 GiB',
@@ -49,6 +49,10 @@
4949
'usage_videos': 65234281361,
5050
'videos': 1836,
5151
}),
52+
'server_version_check': dict({
53+
'checked_at': '2025-06-21T16:35:10.352000+00:00',
54+
'release_version': 'v1.135.3',
55+
}),
5256
}),
5357
'entry': dict({
5458
'data': dict({
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# serializer version: 1
2+
# name: test_update[update.someone_version-entry]
3+
EntityRegistryEntrySnapshot({
4+
'aliases': set({
5+
}),
6+
'area_id': None,
7+
'capabilities': None,
8+
'config_entry_id': <ANY>,
9+
'config_subentry_id': <ANY>,
10+
'device_class': None,
11+
'device_id': <ANY>,
12+
'disabled_by': None,
13+
'domain': 'update',
14+
'entity_category': <EntityCategory.DIAGNOSTIC: 'diagnostic'>,
15+
'entity_id': 'update.someone_version',
16+
'has_entity_name': True,
17+
'hidden_by': None,
18+
'icon': None,
19+
'id': <ANY>,
20+
'labels': set({
21+
}),
22+
'name': None,
23+
'options': dict({
24+
}),
25+
'original_device_class': None,
26+
'original_icon': None,
27+
'original_name': 'Version',
28+
'platform': 'immich',
29+
'previous_unique_id': None,
30+
'suggested_object_id': None,
31+
'supported_features': 0,
32+
'translation_key': 'update',
33+
'unique_id': 'e7ef5713-9dab-4bd4-b899-715b0ca4379e_update',
34+
'unit_of_measurement': None,
35+
})
36+
# ---
37+
# name: test_update[update.someone_version-state]
38+
StateSnapshot({
39+
'attributes': ReadOnlyDict({
40+
'auto_update': False,
41+
'display_precision': 0,
42+
'entity_picture': 'https://brands.home-assistant.io/_/immich/icon.png',
43+
'friendly_name': 'Someone Version',
44+
'in_progress': False,
45+
'installed_version': 'v1.134.0',
46+
'latest_version': 'v1.135.3',
47+
'release_summary': None,
48+
'release_url': 'https://github.com/immich-app/immich/releases/tag/v1.135.3',
49+
'skipped_version': None,
50+
'supported_features': <UpdateEntityFeature: 0>,
51+
'title': None,
52+
'update_percentage': None,
53+
}),
54+
'context': <ANY>,
55+
'entity_id': 'update.someone_version',
56+
'last_changed': <ANY>,
57+
'last_reported': <ANY>,
58+
'last_updated': <ANY>,
59+
'state': 'on',
60+
})
61+
# ---
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
"""Test the Immich update platform."""
2+
3+
from unittest.mock import Mock, patch
4+
5+
from syrupy.assertion import SnapshotAssertion
6+
7+
from homeassistant.const import Platform
8+
from homeassistant.core import HomeAssistant
9+
from homeassistant.helpers import entity_registry as er
10+
11+
from . import setup_integration
12+
13+
from tests.common import MockConfigEntry, snapshot_platform
14+
15+
16+
async def test_update(
17+
hass: HomeAssistant,
18+
entity_registry: er.EntityRegistry,
19+
snapshot: SnapshotAssertion,
20+
mock_immich: Mock,
21+
mock_config_entry: MockConfigEntry,
22+
) -> None:
23+
"""Test the Immich update platform."""
24+
25+
with patch("homeassistant.components.immich.PLATFORMS", [Platform.UPDATE]):
26+
await setup_integration(hass, mock_config_entry)
27+
28+
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
29+
30+
31+
async def test_update_min_version(
32+
hass: HomeAssistant,
33+
entity_registry: er.EntityRegistry,
34+
snapshot: SnapshotAssertion,
35+
mock_immich: Mock,
36+
mock_config_entry: MockConfigEntry,
37+
) -> None:
38+
"""Test the Immich update platform with min version not installed."""
39+
40+
mock_immich.server.async_get_about_info.return_value.version = "v1.132.3"
41+
42+
with patch("homeassistant.components.immich.PLATFORMS", [Platform.UPDATE]):
43+
await setup_integration(hass, mock_config_entry)
44+
45+
assert not hass.states.async_all()

0 commit comments

Comments
 (0)