Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9580df558a | ||
|
|
932baef234 |
@@ -31,6 +31,11 @@ from .modbus_client import RDZModbusClient
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _format_zone_id(zone_id: int | str) -> str:
|
||||||
|
"""Format zone ID as zero-padded two-digit string for consistent ordering."""
|
||||||
|
return f"{int(zone_id):02d}"
|
||||||
|
|
||||||
|
|
||||||
class RDZHMIConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
class RDZHMIConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
"""Handle a config flow for RDZ HMI Control."""
|
"""Handle a config flow for RDZ HMI Control."""
|
||||||
|
|
||||||
@@ -68,8 +73,9 @@ class RDZHMIConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
|||||||
self._abort_if_unique_id_configured()
|
self._abort_if_unique_id_configured()
|
||||||
|
|
||||||
# Build initial zone config (all unconfigured)
|
# Build initial zone config (all unconfigured)
|
||||||
|
# Use zero-padded string keys for consistent ordering
|
||||||
zones_config = {
|
zones_config = {
|
||||||
zone_id: {
|
_format_zone_id(zone_id): {
|
||||||
CONF_ZONE_ID: zone_id,
|
CONF_ZONE_ID: zone_id,
|
||||||
CONF_ZONE_NAME: f"Zone {zone_id}",
|
CONF_ZONE_NAME: f"Zone {zone_id}",
|
||||||
CONF_ZONE_TYPE: THERMOSTAT_TYPE_UNCONFIGURED,
|
CONF_ZONE_TYPE: THERMOSTAT_TYPE_UNCONFIGURED,
|
||||||
@@ -127,6 +133,8 @@ class RDZHMIOptionsFlow(config_entries.OptionsFlow):
|
|||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
"""Handle the initial options step - select zone to configure."""
|
"""Handle the initial options step - select zone to configure."""
|
||||||
zones_config: dict = self._config_entry.data.get(CONF_ZONES, {})
|
zones_config: dict = self._config_entry.data.get(CONF_ZONES, {})
|
||||||
|
# Normalize keys to zero-padded strings for consistent ordering
|
||||||
|
zones_config = {_format_zone_id(k): v for k, v in zones_config.items()}
|
||||||
|
|
||||||
if not zones_config:
|
if not zones_config:
|
||||||
return self.async_abort(reason="no_zones")
|
return self.async_abort(reason="no_zones")
|
||||||
@@ -139,7 +147,7 @@ class RDZHMIOptionsFlow(config_entries.OptionsFlow):
|
|||||||
zone_options = []
|
zone_options = []
|
||||||
for zone_id_str, zone_data in zones_config.items():
|
for zone_id_str, zone_data in zones_config.items():
|
||||||
zone_id = int(zone_id_str) if isinstance(zone_id_str, str) else zone_id_str
|
zone_id = int(zone_id_str) if isinstance(zone_id_str, str) else zone_id_str
|
||||||
zone_name = zone_data.get(CONF_ZONE_NAME, f"Zone {zone_id}")
|
zone_name = f"[Zone {zone_id}] "+zone_data.get(CONF_ZONE_NAME, "")
|
||||||
zone_type = zone_data.get(CONF_ZONE_TYPE, THERMOSTAT_TYPE_UNCONFIGURED)
|
zone_type = zone_data.get(CONF_ZONE_TYPE, THERMOSTAT_TYPE_UNCONFIGURED)
|
||||||
type_label = {
|
type_label = {
|
||||||
THERMOSTAT_TYPE_REAL: "Real",
|
THERMOSTAT_TYPE_REAL: "Real",
|
||||||
@@ -148,7 +156,7 @@ class RDZHMIOptionsFlow(config_entries.OptionsFlow):
|
|||||||
}.get(zone_type, "Unknown")
|
}.get(zone_type, "Unknown")
|
||||||
zone_options.append(
|
zone_options.append(
|
||||||
selector.SelectOptionDict(
|
selector.SelectOptionDict(
|
||||||
value=str(zone_id),
|
value=_format_zone_id(zone_id),
|
||||||
label=f"{zone_name} ({type_label})",
|
label=f"{zone_name} ({type_label})",
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -172,7 +180,9 @@ class RDZHMIOptionsFlow(config_entries.OptionsFlow):
|
|||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
"""Handle zone configuration step."""
|
"""Handle zone configuration step."""
|
||||||
zones_config: dict = dict(self._config_entry.data.get(CONF_ZONES, {}))
|
zones_config: dict = dict(self._config_entry.data.get(CONF_ZONES, {}))
|
||||||
zone_id_str = str(self._selected_zone_id)
|
# Normalize keys to zero-padded strings for consistent ordering
|
||||||
|
zones_config = {_format_zone_id(k): v for k, v in zones_config.items()}
|
||||||
|
zone_id_str = _format_zone_id(self._selected_zone_id)
|
||||||
current_zone = zones_config.get(zone_id_str, {})
|
current_zone = zones_config.get(zone_id_str, {})
|
||||||
|
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
@@ -207,16 +217,28 @@ class RDZHMIOptionsFlow(config_entries.OptionsFlow):
|
|||||||
current_type = current_zone.get(CONF_ZONE_TYPE, THERMOSTAT_TYPE_UNCONFIGURED)
|
current_type = current_zone.get(CONF_ZONE_TYPE, THERMOSTAT_TYPE_UNCONFIGURED)
|
||||||
current_linked = current_zone.get(CONF_LINKED_VIRTUAL_ZONE)
|
current_linked = current_zone.get(CONF_LINKED_VIRTUAL_ZONE)
|
||||||
|
|
||||||
# Build virtual zone options for linking (exclude current zone)
|
# Collect virtual zones already linked to other real thermostats
|
||||||
|
already_linked_virtuals = set()
|
||||||
|
for zid_str, zone_data in zones_config.items():
|
||||||
|
zid = int(zid_str) if isinstance(zid_str, str) else zid_str
|
||||||
|
if zid != self._selected_zone_id and zone_data.get(CONF_ZONE_TYPE) == THERMOSTAT_TYPE_REAL:
|
||||||
|
linked = zone_data.get(CONF_LINKED_VIRTUAL_ZONE)
|
||||||
|
if linked is not None:
|
||||||
|
already_linked_virtuals.add(linked)
|
||||||
|
|
||||||
|
# Build virtual zone options for linking (exclude current zone and already-linked virtuals)
|
||||||
virtual_zone_options = [
|
virtual_zone_options = [
|
||||||
selector.SelectOptionDict(value="none", label="None")
|
selector.SelectOptionDict(value="none", label="None")
|
||||||
]
|
]
|
||||||
for zid_str, zone_data in zones_config.items():
|
for zid_str, zone_data in zones_config.items():
|
||||||
zid = int(zid_str) if isinstance(zid_str, str) else zid_str
|
zid = int(zid_str) if isinstance(zid_str, str) else zid_str
|
||||||
if zid != self._selected_zone_id and zone_data.get(CONF_ZONE_TYPE) == THERMOSTAT_TYPE_VIRTUAL:
|
if zid != self._selected_zone_id and zone_data.get(CONF_ZONE_TYPE) == THERMOSTAT_TYPE_VIRTUAL:
|
||||||
|
# Skip if already linked to another real thermostat
|
||||||
|
if zid in already_linked_virtuals:
|
||||||
|
continue
|
||||||
zone_name = zone_data.get(CONF_ZONE_NAME, f"Zone {zid}")
|
zone_name = zone_data.get(CONF_ZONE_NAME, f"Zone {zid}")
|
||||||
virtual_zone_options.append(
|
virtual_zone_options.append(
|
||||||
selector.SelectOptionDict(value=str(zid), label=zone_name)
|
selector.SelectOptionDict(value=_format_zone_id(zid), label=zone_name)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Build schema
|
# Build schema
|
||||||
@@ -240,11 +262,12 @@ class RDZHMIOptionsFlow(config_entries.OptionsFlow):
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Only show linked virtual zone option for real thermostats with available virtual zones
|
# Show linked virtual zone option when virtual zones are available
|
||||||
if current_type == THERMOSTAT_TYPE_REAL and len(virtual_zone_options) > 1:
|
# (selection is only used if user chooses "Real" type - handled in user_input processing)
|
||||||
|
if len(virtual_zone_options) > 1:
|
||||||
schema_dict[vol.Optional(
|
schema_dict[vol.Optional(
|
||||||
CONF_LINKED_VIRTUAL_ZONE,
|
CONF_LINKED_VIRTUAL_ZONE,
|
||||||
default=str(current_linked) if current_linked else "none"
|
default=_format_zone_id(current_linked) if current_linked else "none"
|
||||||
)] = selector.SelectSelector(
|
)] = selector.SelectSelector(
|
||||||
selector.SelectSelectorConfig(
|
selector.SelectSelectorConfig(
|
||||||
options=virtual_zone_options,
|
options=virtual_zone_options,
|
||||||
|
|||||||
Reference in New Issue
Block a user