Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions changelog/1.3.17.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# 1.3.17 (04/11/2026)

**Title:** Fix blank Add Hub board menu labels (#6)

**Author:** master3395
**GitHub Release:** [v1.3.17](https://github.com/master3395/veista-nilan/releases/tag/v1.3.17)

Upgrade via **HACS** (or git pull), then reload the Nilan integration / restart Home Assistant.

## What changed

- Config-flow menus (interface, board, confirm) pass **explicit option labels** so the five board choices never render as empty rows (issue #6)
- Added Danish (`da`) config-flow translations for Add Hub setup
- Board step descriptions clarify Compact P GEO / NCS-700 vs Nordic XL vs 2018+ Compact P
- Filled board menu strings for German, Italian, and Finnish translations

## Operator notes

- If a YAML Modbus hub is still named bare `nilan`, rename it (for example `nilan_compactpc`) and reload Modbus. Labels should already be visible in 1.3.17; renaming still helps other strings and coexistence.
- Compact P GEO on NCS-700: start with **Auto-detect**, or pick Nordic XL vs 2018+ Compact P using the fan register hints in the menu

## Scope

`config_flow.py`, `config_flow_menus.py` (new), `strings.json`, `translations/*`, `manifest.json`, `docs/faq.md`, changelog.
1 change: 1 addition & 0 deletions changelog/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ GitHub Releases: https://github.com/master3395/veista-nilan/releases

| Version | Date | Summary | Notes |
|---|---|---|---|
| [1.3.17](1.3.17.md) | 04/11/2026 | Fix blank Add Hub board menu labels (#6) | [Release](https://github.com/master3395/veista-nilan/releases/tag/v1.3.17) |
| [1.3.16](1.3.16.md) | 04/10/2026 | Compact P 20464 electrical supplement switch | [Release](https://github.com/master3395/veista-nilan/releases/tag/v1.3.16) |
| [1.3.15](1.3.15.md) | 03/09/2026 | Filter alarm logging + binary/switch last-good | [Release](https://github.com/master3395/veista-nilan/releases/tag/v1.3.15) |
| [1.3.14](1.3.14.md) | 02/09/2026 | CTS700 Nordic Modbus read stability + manual links | [Release](https://github.com/master3395/veista-nilan/releases/tag/v1.3.14) |
Expand Down
14 changes: 5 additions & 9 deletions custom_components/nilan/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
BOARD_TYPE_CTS700_NORDIC,
DOMAIN,
)
from .config_flow_menus import async_menu_options
from .modbus_hub_util import coexistence_warning, find_conflicting_modbus_hub
from .modbus_probe import (
async_detect_board,
Expand Down Expand Up @@ -108,7 +109,7 @@ async def async_step_menu(self, user_input: Optional[dict[str, Any]] = None):
"""Show Communications Selection."""
return self.async_show_menu(
step_id="menu",
menu_options=["tcp", "serial"],
menu_options=await async_menu_options(self.hass, "menu"),
description_placeholders=self._flow_placeholders(),
)

Expand All @@ -124,15 +125,10 @@ async def async_step_serial(self, user_input: Optional[dict[str, Any]] = None):

async def async_step_board(self, user_input: Optional[dict[str, Any]] = None):
"""Choose auto-detect, CTS602, or a CTS700 era map."""
# Dict labels stay visible if YAML hub ``nilan`` blanks translations (#6).
return self.async_show_menu(
step_id="board",
menu_options=[
"auto_detect",
"cts602",
"cts700",
"cts700_nordic",
"cts700_legacy",
],
menu_options=await async_menu_options(self.hass, "board"),
description_placeholders=self._flow_placeholders(),
)

Expand Down Expand Up @@ -231,7 +227,7 @@ async def async_step_confirm(self, user_input: Optional[dict[str, Any]] = None):
)
return self.async_show_menu(
step_id="confirm",
menu_options=["accept", "manual"],
menu_options=await async_menu_options(self.hass, "confirm"),
description_placeholders=placeholders,
)

Expand Down
85 changes: 85 additions & 0 deletions custom_components/nilan/config_flow_menus.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
"""Config-flow menu labels that stay visible even when translations blank.

A YAML Modbus hub named bare ``nilan`` can collide with this integration
domain and blank ``async_show_menu`` labels that rely only on translation
lookups (issue #6). Passing ``menu_options`` as a dict of id -> label keeps
the five board choices readable. Prefer translated strings when available.
"""

from __future__ import annotations

import logging
from typing import Any

from homeassistant.core import HomeAssistant

from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)

# English fallbacks: never return empty labels to the UI.
MENU_LABELS: dict[str, dict[str, str]] = {
"menu": {
"tcp": "TCP",
"serial": "Serial",
},
"board": {
"auto_detect": "Auto-detect (recommended)",
"cts602": "CTS602 (Comfort / Compact / VP)",
"cts700": "CTS700 2018+ Compact P (fan percent 21771)",
"cts700_nordic": "CTS700 Nordic/Polar/Arctic XL (fan steps 101-104)",
"cts700_legacy": "CTS700 2015 legacy map",
},
"confirm": {
"accept": "Accept and create entry",
"manual": "Choose board manually",
},
}


def _translation_candidates(step_id: str, option_id: str) -> list[str]:
"""Possible keys used by Home Assistant translation helpers."""
base = f"config.step.{step_id}.menu_options.{option_id}"
return [
f"component.{DOMAIN}.{base}",
f"{DOMAIN}.{base}",
base,
]


async def async_menu_options(
hass: HomeAssistant,
step_id: str,
) -> dict[str, str]:
"""Return menu option id -> non-empty label for ``async_show_menu``."""
defaults = MENU_LABELS.get(step_id)
if not defaults:
return {}

language = getattr(hass.config, "language", None) or "en"
translations: dict[str, Any] = {}
try:
from homeassistant.helpers.translation import async_get_translations

translations = await async_get_translations(
hass, language, "config", {DOMAIN}
)
except Exception: # noqa: BLE001 - keep English labels if lookup fails
_LOGGER.debug(
"Nilan menu translation lookup failed for %s/%s; using fallbacks",
language,
step_id,
exc_info=True,
)
return dict(defaults)

resolved: dict[str, str] = {}
for option_id, fallback in defaults.items():
label = fallback
for key in _translation_candidates(step_id, option_id):
value = translations.get(key)
if isinstance(value, str) and value.strip():
label = value.strip()
break
resolved[option_id] = label
return resolved
4 changes: 2 additions & 2 deletions custom_components/nilan/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
"config_flow": true,
"dependencies": ["modbus"],
"documentation": "https://github.com/veista/nilan",
"iot_class": "local_polling",
"integration_type": "hub",
"iot_class": "local_polling",
"issue_tracker": "https://github.com/veista/nilan/issues",
"requirements": ["pymodbus>=3.11.1"],
"version": "1.3.16"
"version": "1.3.17"
}
11 changes: 9 additions & 2 deletions custom_components/nilan/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,21 @@
},
"board": {
"title": "Select Controller Board",
"description": "Prefer Auto-detect when unsure. Pause YAML Modbus for this unit first so probes do not fight for the bus. {coexistence_warning}",
"description": "Prefer Auto-detect when unsure. Pause YAML Modbus for this unit first so probes do not fight for the bus. Compact P GEO / NCS-700: try Auto-detect, or Nordic XL (fan steps 101-104) vs 2018+ Compact P (fan percent 21771). {coexistence_warning}",
"menu_options": {
"auto_detect": "Auto-detect (recommended)",
"cts602": "CTS602 (Comfort / Compact / VP)",
"cts700": "CTS700 2018+ Compact P (fan percent 21771)",
"cts700_nordic": "CTS700 Nordic/Polar/Arctic XL (fan steps 101-104)",
"cts700_legacy": "CTS700 2015 legacy map"
}
},
"menu_option_descriptions": {
"auto_detect": "Probes CTS602 then CTS700 Nordic / 2018+ / 2015. Best when unsure (for example Compact P GEO on NCS-700).",
"cts602": "Classic Comfort / Compact / VP boards. Typical unit id 30.",
"cts700": "2018+ Compact P Ethernet map. Fan holding 21771 as percent. Typical unit id 1.",
"cts700_nordic": "Nordic / Polar / Arctic XL hybrid. Fan holding 4747 steps 101-104. Typical unit id 1.",
"cts700_legacy": "2015 PDF-era map (registers mostly under 10000)."
}
},
"tcp": {
"title": "TCP Configuration",
Expand Down
105 changes: 105 additions & 0 deletions custom_components/nilan/translations/da.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
{
"config": {
"abort": {
"already_configured": "Enheden er allerede konfigureret"
},
"error": {
"cannot_connect": "Kunne ikke oprette forbindelse",
"invalid_response": "Ugyldigt svar fra enheden",
"unsupported_device": "Enheden understøttes ikke",
"cannot_detect": "Kunne ikke registrere CTS602 eller CTS700 (Nordic / 2018+ / 2015). Tjek host, port og unit id, eller vælg kort manuelt.",
"unknown": "Uventet fejl"
},
"step": {
"menu": {
"title": "Vælg grænsefladetype",
"description": "{coexistence_warning}",
"menu_options": {
"tcp": "TCP",
"serial": "Seriel"
}
},
"board": {
"title": "Vælg styrekort",
"description": "Vælg Auto-registrering hvis du er usikker. Sæt YAML Modbus for samme aggregat på pause først. Compact P GEO / NCS-700: prøv Auto-registrering, eller Nordic XL (viftetrin 101-104) vs 2018+ Compact P (vifte procent 21771). {coexistence_warning}",
"menu_options": {
"auto_detect": "Auto-registrering (anbefalet)",
"cts602": "CTS602 (Comfort / Compact / VP)",
"cts700": "CTS700 2018+ Compact P (vifte procent 21771)",
"cts700_nordic": "CTS700 Nordic/Polar/Arctic XL (viftetrin 101-104)",
"cts700_legacy": "CTS700 2015 ældre kort"
},
"menu_option_descriptions": {
"auto_detect": "Søger CTS602 derefter CTS700 Nordic / 2018+ / 2015. Bedst når du er usikker (fx Compact P GEO på NCS-700).",
"cts602": "Klassiske Comfort / Compact / VP-kort. Typisk unit id 30.",
"cts700": "2018+ Compact P Ethernet-kort. Vifte holding 21771 som procent. Typisk unit id 1.",
"cts700_nordic": "Nordic / Polar / Arctic XL hybrid. Vifte holding 4747 trin 101-104. Typisk unit id 1.",
"cts700_legacy": "2015 PDF-era kort (registre mest under 10000)."
}
},
"tcp": {
"title": "TCP-konfiguration",
"data": {
"name": "Enhedsnavn",
"host_ip": "IP-adresse",
"host_port": "Port",
"unit_id": "Unit ID"
}
},
"tcp_config": {
"title": "TCP-konfiguration",
"description": "{coexistence_warning}",
"data": {
"name": "Enhedsnavn",
"host_ip": "IP-adresse",
"host_port": "Port",
"unit_id": "Unit ID"
}
},
"tcp_detect": {
"title": "TCP auto-registrering",
"description": "Lad Unit ID stå tom for at prøve 1 derefter 30. {coexistence_warning}",
"data": {
"name": "Enhedsnavn",
"host_ip": "IP-adresse",
"host_port": "Port",
"unit_id": "Unit ID (valgfri)"
}
},
"serial_detect": {
"title": "Seriel auto-registrering",
"description": "Lad Unit ID stå tom for at prøve 1 derefter 30. {coexistence_warning}",
"data": {
"name": "Enhedsnavn",
"host_port": "Sti",
"unit_id": "Unit ID (valgfri)"
}
},
"confirm": {
"title": "Bekræft registreret Nilan",
"description": "Registreret kort: {board_type}. Model: {model}. Unit ID: {unit_id}. {coexistence_warning}",
"menu_options": {
"accept": "Acceptér og opret",
"manual": "Vælg kort manuelt"
}
},
"serial": {
"title": "Seriel konfiguration",
"data": {
"name": "Enhedsnavn",
"host_port": "Sti",
"unit_id": "Unit ID"
}
},
"serial_config": {
"title": "Seriel konfiguration",
"description": "{coexistence_warning}",
"data": {
"name": "Enhedsnavn",
"host_port": "Sti",
"unit_id": "Unit ID"
}
}
}
}
}
18 changes: 18 additions & 0 deletions custom_components/nilan/translations/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,24 @@
"serial": "Serial"
}
},
"board": {
"title": "Steuerplatine wählen",
"description": "Auto-Erkennung bevorzugen wenn unsicher. YAML-Modbus für dieses Gerät zuerst pausieren. {coexistence_warning}",
"menu_options": {
"auto_detect": "Auto-Erkennung (empfohlen)",
"cts602": "CTS602 (Comfort / Compact / VP)",
"cts700": "CTS700 2018+ Compact P (Lüfter Prozent 21771)",
"cts700_nordic": "CTS700 Nordic/Polar/Arctic XL (Lüfterstufen 101-104)",
"cts700_legacy": "CTS700 2015 Legacy-Map"
},
"menu_option_descriptions": {
"auto_detect": "Prüft CTS602, dann CTS700 Nordic / 2018+ / 2015. Beste Wahl bei Unsicherheit (z. B. Compact P GEO auf NCS-700).",
"cts602": "Klassische Comfort / Compact / VP. Typische Unit-ID 30.",
"cts700": "2018+ Compact P Ethernet-Map. Lüfter Holding 21771 als Prozent. Typische Unit-ID 1.",
"cts700_nordic": "Nordic / Polar / Arctic XL Hybrid. Lüfter Holding 4747 Stufen 101-104. Typische Unit-ID 1.",
"cts700_legacy": "2015 PDF-Map (Register meist unter 10000)."
}
},
"tcp": {
"title": "TCP Konfiguration",
"data": {
Expand Down
11 changes: 9 additions & 2 deletions custom_components/nilan/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,21 @@
},
"board": {
"title": "Select Controller Board",
"description": "Prefer Auto-detect when unsure. Pause YAML Modbus for this unit first so probes do not fight for the bus. {coexistence_warning}",
"description": "Prefer Auto-detect when unsure. Pause YAML Modbus for this unit first so probes do not fight for the bus. Compact P GEO / NCS-700: try Auto-detect, or Nordic XL (fan steps 101-104) vs 2018+ Compact P (fan percent 21771). {coexistence_warning}",
"menu_options": {
"auto_detect": "Auto-detect (recommended)",
"cts602": "CTS602 (Comfort / Compact / VP)",
"cts700": "CTS700 2018+ Compact P (fan percent 21771)",
"cts700_nordic": "CTS700 Nordic/Polar/Arctic XL (fan steps 101-104)",
"cts700_legacy": "CTS700 2015 legacy map"
}
},
"menu_option_descriptions": {
"auto_detect": "Probes CTS602 then CTS700 Nordic / 2018+ / 2015. Best when unsure (for example Compact P GEO on NCS-700).",
"cts602": "Classic Comfort / Compact / VP boards. Typical unit id 30.",
"cts700": "2018+ Compact P Ethernet map. Fan holding 21771 as percent. Typical unit id 1.",
"cts700_nordic": "Nordic / Polar / Arctic XL hybrid. Fan holding 4747 steps 101-104. Typical unit id 1.",
"cts700_legacy": "2015 PDF-era map (registers mostly under 10000)."
}
},
"tcp": {
"title": "TCP Configuration",
Expand Down
18 changes: 18 additions & 0 deletions custom_components/nilan/translations/fi.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,24 @@
"serial": "Sarjaportti"
}
},
"board": {
"title": "Valitse ohjainkortti",
"description": "Valitse Automaattinen tunnistus jos et ole varma. Keskeytä YAML Modbus tälle laitteelle ensin. {coexistence_warning}",
"menu_options": {
"auto_detect": "Automaattinen tunnistus (suositus)",
"cts602": "CTS602 (Comfort / Compact / VP)",
"cts700": "CTS700 2018+ Compact P (tuuletin prosentti 21771)",
"cts700_nordic": "CTS700 Nordic/Polar/Arctic XL (tuuletinvaiheet 101-104)",
"cts700_legacy": "CTS700 2015 vanha kartta"
},
"menu_option_descriptions": {
"auto_detect": "Tutkii CTS602 sitten CTS700 Nordic / 2018+ / 2015. Paras kun et ole varma (esim. Compact P GEO / NCS-700).",
"cts602": "Perinteiset Comfort / Compact / VP. Tyypillinen unit id 30.",
"cts700": "2018+ Compact P Ethernet-kartta. Tuuletin holding 21771 prosenttina. Tyypillinen unit id 1.",
"cts700_nordic": "Nordic / Polar / Arctic XL -hybridi. Tuuletin holding 4747 vaiheet 101-104. Tyypillinen unit id 1.",
"cts700_legacy": "2015 PDF-kartta (rekisterit yleensä alle 10000)."
}
},
"tcp": {
"title": "TCP-asetukset",
"data": {
Expand Down
Loading
Loading