parent
0826f7b897
commit
bd1af21ce8
|
|
@ -1,16 +1,27 @@
|
||||||
"""Config flow for XT211 HAN integration."""
|
"""Config flow for XT211 HAN integration.
|
||||||
|
|
||||||
|
Discovery order:
|
||||||
|
1. DHCP discovery – automatic, triggered by HA when USR-DR134 appears on network
|
||||||
|
2. Network scan – user clicks "Search network" in the UI
|
||||||
|
3. Manual entry – user types IP + port manually (always available as fallback)
|
||||||
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
from ipaddress import IPv4Network, IPv4Address
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
|
from homeassistant.components import dhcp
|
||||||
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_NAME
|
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_NAME
|
||||||
from homeassistant.data_entry_flow import FlowResult
|
from homeassistant.data_entry_flow import FlowResult
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
from .const import (
|
from .const import (
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
|
|
@ -32,6 +43,9 @@ from .const import (
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# Known MAC prefixes for USR IOT devices (USR-DR134)
|
||||||
|
USR_IOT_MAC_PREFIXES = ("d8b04c", "b4e62d")
|
||||||
|
|
||||||
STEP_CONNECTION_SCHEMA = vol.Schema(
|
STEP_CONNECTION_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_HOST): str,
|
vol.Required(CONF_HOST): str,
|
||||||
|
|
@ -64,12 +78,16 @@ STEP_METER_SCHEMA = vol.Schema(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def _test_connection(host: str, port: int) -> str | None:
|
# ---------------------------------------------------------------------------
|
||||||
"""Try to open a TCP connection. Returns error string or None on success."""
|
# Helpers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def _test_connection(host: str, port: int, timeout: float = 5.0) -> str | None:
|
||||||
|
"""Try TCP connection. Returns error key or None on success."""
|
||||||
try:
|
try:
|
||||||
reader, writer = await asyncio.wait_for(
|
reader, writer = await asyncio.wait_for(
|
||||||
asyncio.open_connection(host, port),
|
asyncio.open_connection(host, port),
|
||||||
timeout=5,
|
timeout=timeout,
|
||||||
)
|
)
|
||||||
writer.close()
|
writer.close()
|
||||||
await writer.wait_closed()
|
await writer.wait_closed()
|
||||||
|
|
@ -82,26 +100,226 @@ async def _test_connection(host: str, port: int) -> str | None:
|
||||||
return "unknown"
|
return "unknown"
|
||||||
|
|
||||||
|
|
||||||
|
async def _scan_network(port: int, timeout: float = 0.5) -> list[str]:
|
||||||
|
"""
|
||||||
|
Scan the local network for open TCP port (default 8899).
|
||||||
|
Returns list of IP addresses that responded.
|
||||||
|
"""
|
||||||
|
# Determine local subnet from hostname
|
||||||
|
try:
|
||||||
|
local_ip = socket.gethostbyname(socket.gethostname())
|
||||||
|
except OSError:
|
||||||
|
local_ip = "192.168.1.1"
|
||||||
|
|
||||||
|
try:
|
||||||
|
network = IPv4Network(f"{local_ip}/24", strict=False)
|
||||||
|
except ValueError:
|
||||||
|
network = IPv4Network("192.168.1.0/24", strict=False)
|
||||||
|
|
||||||
|
found: list[str] = []
|
||||||
|
|
||||||
|
async def _probe(ip: str) -> None:
|
||||||
|
try:
|
||||||
|
_, writer = await asyncio.wait_for(
|
||||||
|
asyncio.open_connection(ip, port),
|
||||||
|
timeout=timeout,
|
||||||
|
)
|
||||||
|
writer.close()
|
||||||
|
await writer.wait_closed()
|
||||||
|
found.append(ip)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Probe all hosts in /24 concurrently (skip network and broadcast)
|
||||||
|
hosts = [str(h) for h in network.hosts()]
|
||||||
|
await asyncio.gather(*[_probe(ip) for ip in hosts])
|
||||||
|
return sorted(found)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Config Flow
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
class XT211HANConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
class XT211HANConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
"""Handle the config flow for XT211 HAN – two steps."""
|
"""
|
||||||
|
Three-path config flow:
|
||||||
|
- DHCP discovery (automatic)
|
||||||
|
- Network scan (semi-automatic)
|
||||||
|
- Manual entry (always available)
|
||||||
|
"""
|
||||||
|
|
||||||
VERSION = 1
|
VERSION = 1
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self._connection_data: dict[str, Any] = {}
|
self._connection_data: dict[str, Any] = {}
|
||||||
|
self._discovered_host: str | None = None
|
||||||
|
self._discovered_port: int = DEFAULT_PORT
|
||||||
|
self._scan_results: list[str] = []
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Step 1 – connection (host / port / name)
|
# Path 1 – DHCP discovery (triggered automatically by HA)
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def async_step_dhcp(self, discovery_info: dhcp.DhcpServiceInfo) -> FlowResult:
|
||||||
|
"""Handle DHCP discovery of a USR IOT device."""
|
||||||
|
mac = discovery_info.macaddress.replace(":", "").lower()
|
||||||
|
if not any(mac.startswith(prefix) for prefix in USR_IOT_MAC_PREFIXES):
|
||||||
|
return self.async_abort(reason="not_supported")
|
||||||
|
|
||||||
|
ip = discovery_info.ip
|
||||||
|
_LOGGER.info("XT211 HAN: DHCP discovered USR IOT device at %s (MAC %s)", ip, mac)
|
||||||
|
|
||||||
|
# Check not already configured
|
||||||
|
await self.async_set_unique_id(f"{ip}:{DEFAULT_PORT}")
|
||||||
|
self._abort_if_unique_id_configured(updates={CONF_HOST: ip})
|
||||||
|
|
||||||
|
self._discovered_host = ip
|
||||||
|
self._discovered_port = DEFAULT_PORT
|
||||||
|
return await self.async_step_dhcp_confirm()
|
||||||
|
|
||||||
|
async def async_step_dhcp_confirm(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
|
"""Ask user to confirm the DHCP-discovered device."""
|
||||||
|
if user_input is not None:
|
||||||
|
error = await _test_connection(self._discovered_host, self._discovered_port)
|
||||||
|
if error:
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="dhcp_confirm",
|
||||||
|
errors={"base": error},
|
||||||
|
description_placeholders={
|
||||||
|
"host": self._discovered_host,
|
||||||
|
"port": str(self._discovered_port),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
self._connection_data = {
|
||||||
|
CONF_HOST: self._discovered_host,
|
||||||
|
CONF_PORT: self._discovered_port,
|
||||||
|
CONF_NAME: DEFAULT_NAME,
|
||||||
|
}
|
||||||
|
return await self.async_step_meter()
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="dhcp_confirm",
|
||||||
|
description_placeholders={
|
||||||
|
"host": self._discovered_host,
|
||||||
|
"port": str(self._discovered_port),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Path 2 + 3 – User-initiated: scan or manual
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
async def async_step_user(
|
async def async_step_user(
|
||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
|
"""First screen: choose between scan or manual entry."""
|
||||||
|
if user_input is not None:
|
||||||
|
if user_input.get("method") == "scan":
|
||||||
|
return await self.async_step_scan()
|
||||||
|
else:
|
||||||
|
return await self.async_step_manual()
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="user",
|
||||||
|
data_schema=vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required("method", default="scan"): vol.In(
|
||||||
|
{
|
||||||
|
"scan": "🔍 Automaticky vyhledat v síti",
|
||||||
|
"manual": "✏️ Zadat IP adresu ručně",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Path 2 – Network scan
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def async_step_scan(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
|
"""Scan the local network for devices with the configured port open."""
|
||||||
|
if user_input is not None:
|
||||||
|
host = user_input[CONF_HOST]
|
||||||
|
port = user_input.get(CONF_PORT, DEFAULT_PORT)
|
||||||
|
name = user_input.get(CONF_NAME, DEFAULT_NAME)
|
||||||
|
|
||||||
|
await self.async_set_unique_id(f"{host}:{port}")
|
||||||
|
self._abort_if_unique_id_configured()
|
||||||
|
|
||||||
|
error = await _test_connection(host, port)
|
||||||
|
if error:
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="scan",
|
||||||
|
data_schema=self._scan_schema(port),
|
||||||
|
errors={"base": error},
|
||||||
|
)
|
||||||
|
|
||||||
|
self._connection_data = {
|
||||||
|
CONF_HOST: host,
|
||||||
|
CONF_PORT: port,
|
||||||
|
CONF_NAME: name,
|
||||||
|
}
|
||||||
|
return await self.async_step_meter()
|
||||||
|
|
||||||
|
# Run the scan
|
||||||
|
_LOGGER.debug("XT211 HAN: scanning network for port %d", DEFAULT_PORT)
|
||||||
|
self._scan_results = await _scan_network(DEFAULT_PORT)
|
||||||
|
_LOGGER.debug("XT211 HAN: scan found %d device(s): %s", len(self._scan_results), self._scan_results)
|
||||||
|
|
||||||
|
if not self._scan_results:
|
||||||
|
# Nothing found – fall through to manual with a warning
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="scan",
|
||||||
|
data_schema=self._scan_schema(DEFAULT_PORT),
|
||||||
|
errors={"base": "no_devices_found"},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Build selector: found IPs + option to type manually
|
||||||
|
choices = {ip: f"{ip}:{DEFAULT_PORT}" for ip in self._scan_results}
|
||||||
|
choices["manual"] = "✏️ Zadat jinak ručně"
|
||||||
|
|
||||||
|
return self.async_show_form(
|
||||||
|
step_id="scan",
|
||||||
|
data_schema=self._scan_schema(DEFAULT_PORT, choices),
|
||||||
|
)
|
||||||
|
|
||||||
|
def _scan_schema(
|
||||||
|
self, port: int, choices: dict | None = None
|
||||||
|
) -> vol.Schema:
|
||||||
|
if choices:
|
||||||
|
return vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(CONF_HOST): vol.In(choices),
|
||||||
|
vol.Optional(CONF_NAME, default=DEFAULT_NAME): str,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(CONF_HOST): str,
|
||||||
|
vol.Required(CONF_PORT, default=port): int,
|
||||||
|
vol.Optional(CONF_NAME, default=DEFAULT_NAME): str,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Path 3 – Manual entry
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
async def async_step_manual(
|
||||||
|
self, user_input: dict[str, Any] | None = None
|
||||||
|
) -> FlowResult:
|
||||||
|
"""Manual IP + port entry."""
|
||||||
errors: dict[str, str] = {}
|
errors: dict[str, str] = {}
|
||||||
|
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
host = user_input[CONF_HOST]
|
host = user_input[CONF_HOST]
|
||||||
port = user_input[CONF_PORT]
|
port = user_input[CONF_PORT]
|
||||||
|
name = user_input.get(CONF_NAME, DEFAULT_NAME)
|
||||||
|
|
||||||
await self.async_set_unique_id(f"{host}:{port}")
|
await self.async_set_unique_id(f"{host}:{port}")
|
||||||
self._abort_if_unique_id_configured()
|
self._abort_if_unique_id_configured()
|
||||||
|
|
@ -110,22 +328,27 @@ class XT211HANConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
if error:
|
if error:
|
||||||
errors["base"] = error
|
errors["base"] = error
|
||||||
else:
|
else:
|
||||||
self._connection_data = user_input
|
self._connection_data = {
|
||||||
|
CONF_HOST: host,
|
||||||
|
CONF_PORT: port,
|
||||||
|
CONF_NAME: name,
|
||||||
|
}
|
||||||
return await self.async_step_meter()
|
return await self.async_step_meter()
|
||||||
|
|
||||||
return self.async_show_form(
|
return self.async_show_form(
|
||||||
step_id="user",
|
step_id="manual",
|
||||||
data_schema=STEP_CONNECTION_SCHEMA,
|
data_schema=STEP_CONNECTION_SCHEMA,
|
||||||
errors=errors,
|
errors=errors,
|
||||||
)
|
)
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Step 2 – meter configuration
|
# Step: meter configuration (shared by all paths)
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
async def async_step_meter(
|
async def async_step_meter(
|
||||||
self, user_input: dict[str, Any] | None = None
|
self, user_input: dict[str, Any] | None = None
|
||||||
) -> FlowResult:
|
) -> FlowResult:
|
||||||
|
"""Meter type, FVE, tariffs, relays."""
|
||||||
if user_input is not None:
|
if user_input is not None:
|
||||||
data = {**self._connection_data, **user_input}
|
data = {**self._connection_data, **user_input}
|
||||||
name = data.get(CONF_NAME, DEFAULT_NAME)
|
name = data.get(CONF_NAME, DEFAULT_NAME)
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,16 @@
|
||||||
{
|
{
|
||||||
"domain": "xt211_han",
|
"domain": "xt211_han",
|
||||||
"name": "XT211 HAN (RS485 via Ethernet)",
|
"name": "XT211 HAN (RS485 via Ethernet)",
|
||||||
"version": "0.6.0",
|
"version": "0.7.0",
|
||||||
"documentation": "https://github.com/nero150/xt211-han-ha",
|
"documentation": "https://github.com/nero150/xt211-han-ha",
|
||||||
"issue_tracker": "https://github.com/nero150/xt211-han-ha/issues",
|
"issue_tracker": "https://github.com/nero150/xt211-han-ha/issues",
|
||||||
"dependencies": [],
|
"dependencies": [],
|
||||||
"codeowners": ["@nero150"],
|
"codeowners": ["@nero150"],
|
||||||
"requirements": [],
|
"requirements": [],
|
||||||
"iot_class": "local_push",
|
"iot_class": "local_push",
|
||||||
"config_flow": true
|
"config_flow": true,
|
||||||
|
"dhcp": [
|
||||||
|
{"macaddress": "D8B04C*"},
|
||||||
|
{"macaddress": "B4E62D*"}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,34 @@
|
||||||
"config": {
|
"config": {
|
||||||
"step": {
|
"step": {
|
||||||
"user": {
|
"user": {
|
||||||
"title": "Nastavení připojení",
|
"title": "Přidat XT211 HAN",
|
||||||
"description": "Zadej IP adresu a port RS485-to-Ethernet adaptéru (např. PUSR USR-DR134). Výchozí port pro TCP server mód je 8899.",
|
"description": "Jak chceš najít adaptér?",
|
||||||
|
"data": {
|
||||||
|
"method": "Metoda vyhledání"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scan": {
|
||||||
|
"title": "Vyhledávání v síti",
|
||||||
|
"description": "Prohledávám síť na portu 8899... Vyber nalezené zařízení nebo zadej IP ručně.",
|
||||||
"data": {
|
"data": {
|
||||||
"host": "IP adresa adaptéru",
|
"host": "IP adresa adaptéru",
|
||||||
"port": "TCP port",
|
"port": "TCP port",
|
||||||
"name": "Název zařízení"
|
"name": "Název zařízení"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"manual": {
|
||||||
|
"title": "Ruční zadání",
|
||||||
|
"description": "Zadej IP adresu a port RS485-to-Ethernet adaptéru (např. PUSR USR-DR134). Výchozí port je 8899.",
|
||||||
|
"data": {
|
||||||
|
"host": "IP adresa adaptéru",
|
||||||
|
"port": "TCP port",
|
||||||
|
"name": "Název zařízení"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dhcp_confirm": {
|
||||||
|
"title": "Nalezeno zařízení USR IOT",
|
||||||
|
"description": "Home Assistant automaticky nalezl adaptér na adrese **{host}:{port}**. Chceš nastavit integraci XT211 HAN pro toto zařízení?"
|
||||||
|
},
|
||||||
"meter": {
|
"meter": {
|
||||||
"title": "Konfigurace elektroměru",
|
"title": "Konfigurace elektroměru",
|
||||||
"description": "Upřesni parametry tvého elektroměru. Podle toho se zobrazí jen relevantní entity.",
|
"description": "Upřesni parametry tvého elektroměru. Podle toho se zobrazí jen relevantní entity.",
|
||||||
|
|
@ -23,10 +43,12 @@
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"cannot_connect": "Nepodařilo se připojit k adaptéru. Zkontroluj IP adresu, port a připojení.",
|
"cannot_connect": "Nepodařilo se připojit k adaptéru. Zkontroluj IP adresu, port a připojení.",
|
||||||
|
"no_devices_found": "V síti nebyla nalezena žádná zařízení na portu 8899. Zkus zadat IP adresu ručně.",
|
||||||
"unknown": "Neočekávaná chyba. Zkontroluj log."
|
"unknown": "Neočekávaná chyba. Zkontroluj log."
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "Toto zařízení je již nakonfigurováno."
|
"already_configured": "Toto zařízení je již nakonfigurováno.",
|
||||||
|
"not_supported": "Toto zařízení není podporováno."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,34 @@
|
||||||
"config": {
|
"config": {
|
||||||
"step": {
|
"step": {
|
||||||
"user": {
|
"user": {
|
||||||
"title": "Nastavení připojení",
|
"title": "Přidat XT211 HAN",
|
||||||
"description": "Zadej IP adresu a port RS485-to-Ethernet adaptéru (např. PUSR USR-DR134). Výchozí port pro TCP server mód je 8899.",
|
"description": "Jak chceš najít adaptér?",
|
||||||
|
"data": {
|
||||||
|
"method": "Metoda vyhledání"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scan": {
|
||||||
|
"title": "Vyhledávání v síti",
|
||||||
|
"description": "Prohledávám síť na portu 8899... Vyber nalezené zařízení nebo zadej IP ručně.",
|
||||||
"data": {
|
"data": {
|
||||||
"host": "IP adresa adaptéru",
|
"host": "IP adresa adaptéru",
|
||||||
"port": "TCP port",
|
"port": "TCP port",
|
||||||
"name": "Název zařízení"
|
"name": "Název zařízení"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"manual": {
|
||||||
|
"title": "Ruční zadání",
|
||||||
|
"description": "Zadej IP adresu a port RS485-to-Ethernet adaptéru (např. PUSR USR-DR134). Výchozí port je 8899.",
|
||||||
|
"data": {
|
||||||
|
"host": "IP adresa adaptéru",
|
||||||
|
"port": "TCP port",
|
||||||
|
"name": "Název zařízení"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dhcp_confirm": {
|
||||||
|
"title": "Nalezeno zařízení USR IOT",
|
||||||
|
"description": "Home Assistant automaticky nalezl adaptér na adrese **{host}:{port}**. Chceš nastavit integraci XT211 HAN pro toto zařízení?"
|
||||||
|
},
|
||||||
"meter": {
|
"meter": {
|
||||||
"title": "Konfigurace elektroměru",
|
"title": "Konfigurace elektroměru",
|
||||||
"description": "Upřesni parametry tvého elektroměru. Podle toho se zobrazí jen relevantní entity.",
|
"description": "Upřesni parametry tvého elektroměru. Podle toho se zobrazí jen relevantní entity.",
|
||||||
|
|
@ -23,10 +43,12 @@
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"cannot_connect": "Nepodařilo se připojit k adaptéru. Zkontroluj IP adresu, port a připojení.",
|
"cannot_connect": "Nepodařilo se připojit k adaptéru. Zkontroluj IP adresu, port a připojení.",
|
||||||
|
"no_devices_found": "V síti nebyla nalezena žádná zařízení na portu 8899. Zkus zadat IP adresu ručně.",
|
||||||
"unknown": "Neočekávaná chyba. Zkontroluj log."
|
"unknown": "Neočekávaná chyba. Zkontroluj log."
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "Toto zařízení je již nakonfigurováno."
|
"already_configured": "Toto zařízení je již nakonfigurováno.",
|
||||||
|
"not_supported": "Toto zařízení není podporováno."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,34 @@
|
||||||
"config": {
|
"config": {
|
||||||
"step": {
|
"step": {
|
||||||
"user": {
|
"user": {
|
||||||
"title": "Connection Setup",
|
"title": "Add XT211 HAN",
|
||||||
"description": "Enter the IP address and port of your RS485-to-Ethernet adapter (e.g. PUSR USR-DR134). The default TCP server port is 8899.",
|
"description": "How would you like to find the adapter?",
|
||||||
|
"data": {
|
||||||
|
"method": "Discovery method"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scan": {
|
||||||
|
"title": "Network Scan",
|
||||||
|
"description": "Scanning network on port 8899... Select a discovered device or enter IP manually.",
|
||||||
"data": {
|
"data": {
|
||||||
"host": "Adapter IP address",
|
"host": "Adapter IP address",
|
||||||
"port": "TCP port",
|
"port": "TCP port",
|
||||||
"name": "Device name"
|
"name": "Device name"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"manual": {
|
||||||
|
"title": "Manual Entry",
|
||||||
|
"description": "Enter the IP address and port of your RS485-to-Ethernet adapter (e.g. PUSR USR-DR134). Default port is 8899.",
|
||||||
|
"data": {
|
||||||
|
"host": "Adapter IP address",
|
||||||
|
"port": "TCP port",
|
||||||
|
"name": "Device name"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dhcp_confirm": {
|
||||||
|
"title": "USR IOT Device Found",
|
||||||
|
"description": "Home Assistant automatically discovered an adapter at **{host}:{port}**. Would you like to set up the XT211 HAN integration for this device?"
|
||||||
|
},
|
||||||
"meter": {
|
"meter": {
|
||||||
"title": "Meter Configuration",
|
"title": "Meter Configuration",
|
||||||
"description": "Specify your meter parameters. Only relevant entities will be shown.",
|
"description": "Specify your meter parameters. Only relevant entities will be shown.",
|
||||||
|
|
@ -23,10 +43,12 @@
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"cannot_connect": "Could not connect to the adapter. Check the IP address, port and network connection.",
|
"cannot_connect": "Could not connect to the adapter. Check the IP address, port and network connection.",
|
||||||
|
"no_devices_found": "No devices found on port 8899. Try entering the IP address manually.",
|
||||||
"unknown": "Unexpected error. Check the log."
|
"unknown": "Unexpected error. Check the log."
|
||||||
},
|
},
|
||||||
"abort": {
|
"abort": {
|
||||||
"already_configured": "This device is already configured."
|
"already_configured": "This device is already configured.",
|
||||||
|
"not_supported": "This device is not supported."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue