Skip to content

Commit c1a882e

Browse files
LuckyTriple7claude
andcommitted
v0.2.2: per-domain devices, shorter entity names, diagnostic categories
- Each domain is now its own HA device (linked to account via via_device) - Entity names shortened: device name + short suffix instead of one long string - Resolved IP and Public IP sensors marked as EntityCategory.DIAGNOSTIC - Account problem sensor renamed from Error to Problem - CHANGELOG fully translated to English Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 4aac1b5 commit c1a882e

5 files changed

Lines changed: 72 additions & 31 deletions

File tree

CHANGELOG.md

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,32 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [0.2.2] - 2026-06-21
6+
### Changed
7+
- Each domain is now its own HA device — entities are grouped per domain instead of all under one account device
8+
- Entity names shortened: `website.de Resolved IP` instead of `mein_strato Domain website.de Resolved IP`
9+
- Resolved IP and Public IP sensors are now categorized as `DIAGNOSTIC` (appear under the Diagnostics tab in the integration overview)
10+
- Account-level sensor renamed from "Error" to "Problem" (matches device class)
11+
- "Update Now" button now correctly grouped under the account device
12+
513
## [0.2.1] - 2026-06-10
614
### Fixed
7-
- Domain-Binärsensoren zeigen jetzt den letzten Strato-Rückgabecode in den Attributen an (`last_update_status`, `last_update_response`), auch wenn kein Update gesendet wurde
15+
- Domain binary sensors now persistently show the last Strato response code in attributes (`last_update_status`, `last_update_response`), even when no update was sent
816

917
## [0.2.0] - 2026-06-09
1018
### Fixed
11-
- manifest.json: `issue_tracker` und `codeowners` ergänzt (HACS-Pflichtfelder für Store-Aufnahme)
19+
- manifest.json: added `issue_tracker` and `codeowners` (required fields for HACS Default Store submission)
1220

1321
## [0.1.9] - 2026-06-09
1422
### Changed
15-
- Globaler Problem-Sensor zeigt nur noch `ON`, wenn DNS-Resolver-IP von der öffentlichen IP abweicht (IPv4 oder IPv6)
16-
- Update-Fehler einzelner Domains werden im globalen Sensor nicht mehr berücksichtigt — die Domain-Sensoren zeigen das weiterhin separat
23+
- Global problem sensor now only turns `ON` when a DNS resolver IP differs from the public IP (IPv4 or IPv6)
24+
- Update errors on individual domains are no longer considered by the global sensor — the per-domain sensors still show them separately
1725

1826
## [0.1.8] - 2026-06-09
1927
### Fixed
20-
- Kein unnötiger Strato-Update mehr beim HA-Neustart
21-
- Nach Neustart ist der In-Memory-Zustand leer → DNS-Check entscheidet (wie bisher, sicher)
22-
- Innerhalb einer laufenden Sessiongesendete IP entscheidet (verhindert Re-Send während DNS-Propagierung)
28+
- No more unnecessary Strato update on HA restart
29+
- After restart, in-memory state is empty → DNS check decides (safe, same as before)
30+
- Within a running sessionlast sent IP decides (prevents re-send while DNS is propagating)
2331

2432
## [0.1.7] - 2026-06-09
2533
### Changed

custom_components/strato_dyndns/binary_sensor.py

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,26 +28,37 @@ async def async_setup_entry(
2828
async_add_entities(entities)
2929

3030

31-
def _device_info(coordinator: StratoDynDNSCoordinator) -> DeviceInfo:
31+
def _account_device_info(coordinator: StratoDynDNSCoordinator) -> DeviceInfo:
3232
return DeviceInfo(
3333
identifiers={(DOMAIN, coordinator.account_name)},
34-
name=f"Strato DynDNS · {coordinator.account_name}",
34+
name=coordinator.account_name,
3535
manufacturer="Strato AG",
3636
model="DynDNS",
3737
)
3838

3939

40+
def _domain_device_info(coordinator: StratoDynDNSCoordinator, domain: str) -> DeviceInfo:
41+
return DeviceInfo(
42+
identifiers={(DOMAIN, f"{coordinator.account_name}_{domain}")},
43+
name=domain,
44+
manufacturer="Strato AG",
45+
model="DynDNS",
46+
via_device=(DOMAIN, coordinator.account_name),
47+
)
48+
49+
4050
class StratoAccountErrorSensor(CoordinatorEntity[StratoDynDNSCoordinator], BinarySensorEntity):
41-
"""ON when any domain's last update attempt returned an error."""
51+
"""ON when any domain's DNS-resolved IP differs from the public IP."""
4252

53+
_attr_has_entity_name = True
4354
_attr_device_class = BinarySensorDeviceClass.PROBLEM
4455
_attr_icon = "mdi:cloud-alert"
4556

4657
def __init__(self, coordinator: StratoDynDNSCoordinator, entry: ConfigEntry) -> None:
4758
super().__init__(coordinator)
4859
self._attr_unique_id = f"{entry.entry_id}_account_error"
49-
self._attr_name = f"{coordinator.account_name} Error"
50-
self._attr_device_info = _device_info(coordinator)
60+
self._attr_name = "Problem"
61+
self._attr_device_info = _account_device_info(coordinator)
5162

5263
@property
5364
def is_on(self) -> bool | None:
@@ -81,6 +92,7 @@ def extra_state_attributes(self) -> dict:
8192

8293

8394
class StratoDomainMismatchSensor(CoordinatorEntity[StratoDynDNSCoordinator], BinarySensorEntity):
95+
_attr_has_entity_name = True
8496
_attr_device_class = BinarySensorDeviceClass.PROBLEM
8597
_attr_icon = "mdi:alert-circle-outline"
8698

@@ -93,8 +105,8 @@ def __init__(
93105
super().__init__(coordinator)
94106
slug = domain.replace(".", "_").replace("-", "_")
95107
self._attr_unique_id = f"{entry.entry_id}_{slug}_mismatch"
96-
self._attr_name = f"{coordinator.account_name} Domain {domain} IP Mismatch"
97-
self._attr_device_info = _device_info(coordinator)
108+
self._attr_name = "IP Mismatch"
109+
self._attr_device_info = _domain_device_info(coordinator, domain)
98110
self._domain = domain
99111

100112
@property
@@ -117,6 +129,7 @@ def extra_state_attributes(self) -> dict:
117129

118130

119131
class StratoDomainIPv6MismatchSensor(CoordinatorEntity[StratoDynDNSCoordinator], BinarySensorEntity):
132+
_attr_has_entity_name = True
120133
_attr_device_class = BinarySensorDeviceClass.PROBLEM
121134
_attr_icon = "mdi:alert-circle-outline"
122135

@@ -129,8 +142,8 @@ def __init__(
129142
super().__init__(coordinator)
130143
slug = domain.replace(".", "_").replace("-", "_")
131144
self._attr_unique_id = f"{entry.entry_id}_{slug}_ipv6_mismatch"
132-
self._attr_name = f"{coordinator.account_name} Domain {domain} IPv6 Mismatch"
133-
self._attr_device_info = _device_info(coordinator)
145+
self._attr_name = "IPv6 Mismatch"
146+
self._attr_device_info = _domain_device_info(coordinator, domain)
134147
self._domain = domain
135148

136149
@property

custom_components/strato_dyndns/button.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,16 @@ async def async_setup_entry(
2121

2222

2323
class StratoUpdateNowButton(CoordinatorEntity[StratoDynDNSCoordinator], ButtonEntity):
24+
_attr_has_entity_name = True
2425
_attr_icon = "mdi:refresh"
2526

2627
def __init__(self, coordinator: StratoDynDNSCoordinator, entry: ConfigEntry) -> None:
2728
super().__init__(coordinator)
2829
self._attr_unique_id = f"{entry.entry_id}_update_now"
29-
self._attr_name = f"{coordinator.account_name} Update Now"
30+
self._attr_name = "Update Now"
3031
self._attr_device_info = DeviceInfo(
3132
identifiers={(DOMAIN, coordinator.account_name)},
32-
name=f"Strato DynDNS · {coordinator.account_name}",
33+
name=coordinator.account_name,
3334
manufacturer="Strato AG",
3435
model="DynDNS",
3536
)

custom_components/strato_dyndns/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88
"iot_class": "cloud_polling",
99
"issue_tracker": "https://github.com/LuckyTriple7/ha-strato-dyndns/issues",
1010
"requirements": ["dnspython==2.7.0"],
11-
"version": "0.2.1"
11+
"version": "0.2.2"
1212
}

custom_components/strato_dyndns/sensor.py

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from homeassistant.config_entries import ConfigEntry
77
from homeassistant.core import HomeAssistant
88
from homeassistant.helpers.device_registry import DeviceInfo
9+
from homeassistant.helpers.entity import EntityCategory
910
from homeassistant.helpers.entity_platform import AddEntitiesCallback
1011
from homeassistant.helpers.update_coordinator import CoordinatorEntity
1112

@@ -32,23 +33,35 @@ async def async_setup_entry(
3233
async_add_entities(entities)
3334

3435

35-
def _device_info(coordinator: StratoDynDNSCoordinator) -> DeviceInfo:
36+
def _account_device_info(coordinator: StratoDynDNSCoordinator) -> DeviceInfo:
3637
return DeviceInfo(
3738
identifiers={(DOMAIN, coordinator.account_name)},
38-
name=f"Strato DynDNS · {coordinator.account_name}",
39+
name=coordinator.account_name,
3940
manufacturer="Strato AG",
4041
model="DynDNS",
4142
)
4243

4344

45+
def _domain_device_info(coordinator: StratoDynDNSCoordinator, domain: str) -> DeviceInfo:
46+
return DeviceInfo(
47+
identifiers={(DOMAIN, f"{coordinator.account_name}_{domain}")},
48+
name=domain,
49+
manufacturer="Strato AG",
50+
model="DynDNS",
51+
via_device=(DOMAIN, coordinator.account_name),
52+
)
53+
54+
4455
class StratoPublicIPv4Sensor(CoordinatorEntity[StratoDynDNSCoordinator], SensorEntity):
56+
_attr_has_entity_name = True
4557
_attr_icon = "mdi:ip-network"
58+
_attr_entity_category = EntityCategory.DIAGNOSTIC
4659

4760
def __init__(self, coordinator: StratoDynDNSCoordinator, entry: ConfigEntry) -> None:
4861
super().__init__(coordinator)
4962
self._attr_unique_id = f"{entry.entry_id}_public_ipv4"
50-
self._attr_name = f"{coordinator.account_name} Public IPv4"
51-
self._attr_device_info = _device_info(coordinator)
63+
self._attr_name = "Public IPv4"
64+
self._attr_device_info = _account_device_info(coordinator)
5265

5366
@property
5467
def native_value(self) -> str | None:
@@ -64,14 +77,15 @@ def extra_state_attributes(self) -> dict:
6477

6578

6679
class StratoPublicIPv6Sensor(CoordinatorEntity[StratoDynDNSCoordinator], SensorEntity):
80+
_attr_has_entity_name = True
6781
_attr_icon = "mdi:ip-network-outline"
82+
_attr_entity_category = EntityCategory.DIAGNOSTIC
6883

6984
def __init__(self, coordinator: StratoDynDNSCoordinator, entry: ConfigEntry) -> None:
7085
super().__init__(coordinator)
7186
self._attr_unique_id = f"{entry.entry_id}_public_ipv6"
72-
# No account prefix — public IPv6 is network-wide, not per account
7387
self._attr_name = "Public IPv6"
74-
self._attr_device_info = _device_info(coordinator)
88+
self._attr_device_info = _account_device_info(coordinator)
7589

7690
@property
7791
def native_value(self) -> str | None:
@@ -87,7 +101,9 @@ def extra_state_attributes(self) -> dict:
87101

88102

89103
class StratoDomainResolvedIPSensor(CoordinatorEntity[StratoDynDNSCoordinator], SensorEntity):
104+
_attr_has_entity_name = True
90105
_attr_icon = "mdi:dns"
106+
_attr_entity_category = EntityCategory.DIAGNOSTIC
91107

92108
def __init__(
93109
self,
@@ -98,8 +114,8 @@ def __init__(
98114
super().__init__(coordinator)
99115
slug = domain.replace(".", "_").replace("-", "_")
100116
self._attr_unique_id = f"{entry.entry_id}_{slug}_resolved_ip"
101-
self._attr_name = f"{coordinator.account_name} Domain {domain} Resolved IP"
102-
self._attr_device_info = _device_info(coordinator)
117+
self._attr_name = "Resolved IP"
118+
self._attr_device_info = _domain_device_info(coordinator, domain)
103119
self._domain = domain
104120

105121
@property
@@ -128,7 +144,9 @@ def extra_state_attributes(self) -> dict:
128144

129145

130146
class StratoDomainResolvedIPv6Sensor(CoordinatorEntity[StratoDynDNSCoordinator], SensorEntity):
147+
_attr_has_entity_name = True
131148
_attr_icon = "mdi:dns-outline"
149+
_attr_entity_category = EntityCategory.DIAGNOSTIC
132150

133151
def __init__(
134152
self,
@@ -139,8 +157,8 @@ def __init__(
139157
super().__init__(coordinator)
140158
slug = domain.replace(".", "_").replace("-", "_")
141159
self._attr_unique_id = f"{entry.entry_id}_{slug}_resolved_ipv6"
142-
self._attr_name = f"{coordinator.account_name} Domain {domain} Resolved IPv6"
143-
self._attr_device_info = _device_info(coordinator)
160+
self._attr_name = "Resolved IPv6"
161+
self._attr_device_info = _domain_device_info(coordinator, domain)
144162
self._domain = domain
145163

146164
@property
@@ -158,6 +176,7 @@ def extra_state_attributes(self) -> dict:
158176

159177

160178
class StratoDomainLastUpdateSensor(CoordinatorEntity[StratoDynDNSCoordinator], SensorEntity):
179+
_attr_has_entity_name = True
161180
_attr_device_class = SensorDeviceClass.TIMESTAMP
162181
_attr_icon = "mdi:clock-check-outline"
163182

@@ -170,8 +189,8 @@ def __init__(
170189
super().__init__(coordinator)
171190
slug = domain.replace(".", "_").replace("-", "_")
172191
self._attr_unique_id = f"{entry.entry_id}_{slug}_last_update"
173-
self._attr_name = f"{coordinator.account_name} Domain {domain} Last Update"
174-
self._attr_device_info = _device_info(coordinator)
192+
self._attr_name = "Last Update"
193+
self._attr_device_info = _domain_device_info(coordinator, domain)
175194
self._domain = domain
176195

177196
@property

0 commit comments

Comments
 (0)