Skip to content

Commit 5f15eb0

Browse files
0xAHAclaude
andcommitted
refactor: move the four already-conformant entity classes onto GrowattEntity
GrowattGenericNumber, GrowattGenericSelect, GrowattGenericTime and GrowattModTouTime already set has_entity_name = True and do not bake the device name into _attr_name, so they are on the same convention GrowattEntity provides. Moving them removes the duplicated unique_id assignment and device_info property with no user-visible change. The other 16 classes set has_entity_name = False and build names like f'{entry_name} Work Mode'. GrowattEntity sets the flag True, which makes Home Assistant prefix the device name itself -- so migrating those unchanged would render 'Growatt Growatt Work Mode'. They need the baked-in prefixes stripped, which changes displayed names, so they are deliberately left for a separate decision. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent e38bd4f commit 5f15eb0

3 files changed

Lines changed: 33 additions & 42 deletions

File tree

custom_components/growatt_modbus/number.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
get_device_type_for_control,
1818
)
1919
from .coordinator import GrowattModbusCoordinator
20+
from .entity import GrowattEntity
2021
from .growatt_modbus import ModbusWriteError
2122

2223
_LOGGER = logging.getLogger(__name__)
@@ -132,10 +133,10 @@ async def async_setup_entry(
132133
async_add_entities(entities)
133134

134135

135-
class GrowattGenericNumber(CoordinatorEntity, NumberEntity):
136+
class GrowattGenericNumber(GrowattEntity, NumberEntity):
136137
"""Generic number entity for any numeric control."""
137138

138-
_attr_has_entity_name = True
139+
# has_entity_name comes from GrowattEntity, as do unique_id and device_info.
139140
_attr_mode = NumberMode.SLIDER
140141
_attr_entity_category = EntityCategory.CONFIG
141142

@@ -147,9 +148,13 @@ def __init__(
147148
control_config: dict,
148149
) -> None:
149150
"""Initialize the number entity."""
150-
super().__init__(coordinator)
151+
super().__init__(
152+
coordinator,
153+
config_entry,
154+
control_name,
155+
get_device_type_for_control(control_name),
156+
)
151157

152-
self._config_entry = config_entry
153158
self._control_name = control_name
154159
self._control_config = control_config
155160

@@ -170,7 +175,6 @@ def __init__(
170175
}
171176
friendly_name = friendly_overrides.get(control_name, control_name.replace('_', ' ').title())
172177
self._attr_name = friendly_name
173-
self._attr_unique_id = f"{config_entry.entry_id}_{control_name}"
174178

175179
# Set icon
176180
self._attr_icon = self._get_icon(control_name)
@@ -236,12 +240,6 @@ def _configure_range_and_unit(self):
236240

237241
self._attr_native_unit_of_measurement = unit
238242

239-
@property
240-
def device_info(self) -> dict[str, Any]:
241-
"""Return device information."""
242-
device_type = get_device_type_for_control(self._control_name)
243-
return self.coordinator.get_device_info(device_type)
244-
245243
@property
246244
def native_value(self) -> float | None:
247245
"""Return the current value."""

custom_components/growatt_modbus/select.py

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
MOD_TOU_PERIODS,
2020
)
2121
from .coordinator import GrowattModbusCoordinator
22+
from .entity import GrowattEntity
2223
from .growatt_modbus import ModbusWriteError
2324

2425
_LOGGER = logging.getLogger(__name__)
@@ -194,10 +195,10 @@ def _async_check_deferred_vpp() -> None:
194195
config_entry.async_on_unload(_remove_vpp_listener)
195196

196197

197-
class GrowattGenericSelect(CoordinatorEntity, SelectEntity):
198+
class GrowattGenericSelect(GrowattEntity, SelectEntity):
198199
"""Generic select entity for any control with options."""
199200

200-
_attr_has_entity_name = True
201+
# has_entity_name comes from GrowattEntity, as do unique_id and device_info.
201202
_attr_entity_category = EntityCategory.CONFIG
202203

203204
def __init__(
@@ -208,16 +209,19 @@ def __init__(
208209
control_config: dict,
209210
) -> None:
210211
"""Initialize the select entity."""
211-
super().__init__(coordinator)
212+
super().__init__(
213+
coordinator,
214+
config_entry,
215+
control_name,
216+
get_device_type_for_control(control_name),
217+
)
212218

213-
self._config_entry = config_entry
214219
self._control_name = control_name
215220
self._control_config = control_config
216221

217222
# Generate friendly name (e.g., "output_config" -> "Output Config")
218223
friendly_name = control_name.replace('_', ' ').title()
219224
self._attr_name = friendly_name
220-
self._attr_unique_id = f"{config_entry.entry_id}_{control_name}"
221225

222226
# Set icon based on control type
223227
self._attr_icon = self._get_icon(control_name)
@@ -236,13 +240,6 @@ def _get_icon(self, control_name: str) -> str:
236240
}
237241
return icon_map.get(control_name, 'mdi:tune')
238242

239-
@property
240-
def device_info(self) -> dict[str, Any]:
241-
"""Return device information."""
242-
# Determine device based on control type
243-
device_type = get_device_type_for_control(self._control_name)
244-
return self.coordinator.get_device_info(device_type)
245-
246243
@property
247244
def current_option(self) -> str | None:
248245
"""Return the current selected option."""

custom_components/growatt_modbus/time.py

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
from .const import DOMAIN, WRITABLE_REGISTERS, CONF_REGISTER_MAP, get_device_type_for_control, DEVICE_TYPE_BATTERY, MOD_TOU_PERIODS
1414
from .coordinator import GrowattModbusCoordinator
15+
from .entity import GrowattEntity
1516
from .growatt_modbus import ModbusWriteError
1617

1718
_LOGGER = logging.getLogger(__name__)
@@ -137,14 +138,14 @@ async def async_set_value(self, value: dt_time) -> None:
137138
_LOGGER.exception("[WIT-TOU] Period %d %s write error: %s", self._period, slot, err)
138139

139140

140-
class GrowattGenericTime(CoordinatorEntity, TimeEntity):
141+
class GrowattGenericTime(GrowattEntity, TimeEntity):
141142
"""Time entity for inverter time period start/end controls.
142143
143144
Hardware stores time as hex-packed bytes: hours*256 + minutes.
144145
e.g. 06:00 = 0x0600 = 1536, 22:00 = 0x1600 = 5632.
145146
"""
146147

147-
_attr_has_entity_name = True
148+
# has_entity_name comes from GrowattEntity, as do unique_id and device_info.
148149
_attr_entity_category = EntityCategory.CONFIG
149150
_attr_icon = "mdi:clock-outline"
150151

@@ -156,19 +157,17 @@ def __init__(
156157
control_config: dict,
157158
) -> None:
158159
"""Initialize the time entity."""
159-
super().__init__(coordinator)
160-
self._config_entry = config_entry
160+
super().__init__(
161+
coordinator,
162+
config_entry,
163+
control_name,
164+
get_device_type_for_control(control_name),
165+
)
161166
self._control_name = control_name
162167
self._control_config = control_config
163168

164169
friendly_name = control_config.get('label') or control_name.replace('_', ' ').title()
165170
self._attr_name = friendly_name
166-
self._attr_unique_id = f"{config_entry.entry_id}_{control_name}"
167-
168-
@property
169-
def device_info(self) -> dict[str, Any]:
170-
"""Return device information."""
171-
return self.coordinator.get_device_info(get_device_type_for_control(self._control_name))
172171

173172
@property
174173
def native_value(self) -> dt_time | None:
@@ -336,8 +335,12 @@ def __init__(
336335
is_start: bool,
337336
) -> None:
338337
"""Initialize the MOD TOU time entity."""
339-
super().__init__(coordinator)
340-
self._config_entry = config_entry
338+
super().__init__(
339+
coordinator,
340+
config_entry,
341+
f"mod_tou_{period_def['period']}_{'start' if is_start else 'end'}",
342+
DEVICE_TYPE_BATTERY,
343+
)
341344
self._period_def = period_def
342345
self._is_start = is_start
343346
self._period = period_def["period"]
@@ -351,13 +354,6 @@ def __init__(
351354
self._data_field = period_def["end_field"]
352355
self._attr_name = f"TOU Period {self._period} End"
353356

354-
self._attr_unique_id = f"{config_entry.entry_id}_mod_tou_{self._period}_{'start' if is_start else 'end'}"
355-
356-
@property
357-
def device_info(self) -> dict[str, Any]:
358-
"""Return device information."""
359-
return self.coordinator.get_device_info(DEVICE_TYPE_BATTERY)
360-
361357
@property
362358
def native_value(self) -> dt_time | None:
363359
"""Return the current time value decoded from the packed register."""

0 commit comments

Comments
 (0)