mirror of
https://github.com/home-assistant/core.git
synced 2025-12-21 15:28:19 +00:00
Compare commits
3 Commits
dev
...
knx-text-u
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c383081f1 | ||
|
|
c5418b4849 | ||
|
|
1f53b9183c |
@@ -170,6 +170,7 @@ SUPPORTED_PLATFORMS_UI: Final = {
|
|||||||
Platform.LIGHT,
|
Platform.LIGHT,
|
||||||
Platform.SENSOR,
|
Platform.SENSOR,
|
||||||
Platform.SWITCH,
|
Platform.SWITCH,
|
||||||
|
Platform.TEXT,
|
||||||
Platform.TIME,
|
Platform.TIME,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,3 +74,6 @@ CONF_GA_SATURATION: Final = "ga_saturation"
|
|||||||
|
|
||||||
# Sensor
|
# Sensor
|
||||||
CONF_ALWAYS_CALLBACK: Final = "always_callback"
|
CONF_ALWAYS_CALLBACK: Final = "always_callback"
|
||||||
|
|
||||||
|
# Text
|
||||||
|
CONF_GA_TEXT: Final = "ga_text"
|
||||||
|
|||||||
@@ -13,10 +13,12 @@ from homeassistant.components.sensor import (
|
|||||||
SensorDeviceClass,
|
SensorDeviceClass,
|
||||||
SensorStateClass,
|
SensorStateClass,
|
||||||
)
|
)
|
||||||
|
from homeassistant.components.text import TextMode
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_DEVICE_CLASS,
|
CONF_DEVICE_CLASS,
|
||||||
CONF_ENTITY_CATEGORY,
|
CONF_ENTITY_CATEGORY,
|
||||||
CONF_ENTITY_ID,
|
CONF_ENTITY_ID,
|
||||||
|
CONF_MODE,
|
||||||
CONF_NAME,
|
CONF_NAME,
|
||||||
CONF_PLATFORM,
|
CONF_PLATFORM,
|
||||||
CONF_UNIT_OF_MEASUREMENT,
|
CONF_UNIT_OF_MEASUREMENT,
|
||||||
@@ -90,6 +92,7 @@ from .const import (
|
|||||||
CONF_GA_SWITCH,
|
CONF_GA_SWITCH,
|
||||||
CONF_GA_TEMPERATURE_CURRENT,
|
CONF_GA_TEMPERATURE_CURRENT,
|
||||||
CONF_GA_TEMPERATURE_TARGET,
|
CONF_GA_TEMPERATURE_TARGET,
|
||||||
|
CONF_GA_TEXT,
|
||||||
CONF_GA_TIME,
|
CONF_GA_TIME,
|
||||||
CONF_GA_UP_DOWN,
|
CONF_GA_UP_DOWN,
|
||||||
CONF_GA_VALVE,
|
CONF_GA_VALVE,
|
||||||
@@ -428,6 +431,20 @@ SWITCH_KNX_SCHEMA = vol.Schema(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
TEXT_KNX_SCHEMA = vol.Schema(
|
||||||
|
{
|
||||||
|
vol.Required(CONF_GA_TEXT): GASelector(write_required=True, dpt=["string"]),
|
||||||
|
vol.Required(CONF_MODE, default=TextMode.TEXT): selector.SelectSelector(
|
||||||
|
selector.SelectSelectorConfig(
|
||||||
|
options=list(TextMode),
|
||||||
|
translation_key="component.knx.config_panel.entities.create.text.knx.mode",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
vol.Optional(CONF_RESPOND_TO_READ, default=False): selector.BooleanSelector(),
|
||||||
|
vol.Optional(CONF_SYNC_STATE, default=True): SyncStateSelector(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
TIME_KNX_SCHEMA = vol.Schema(
|
TIME_KNX_SCHEMA = vol.Schema(
|
||||||
{
|
{
|
||||||
vol.Required(CONF_GA_TIME): GASelector(write_required=True, valid_dpt="10.001"),
|
vol.Required(CONF_GA_TIME): GASelector(write_required=True, valid_dpt="10.001"),
|
||||||
@@ -696,6 +713,7 @@ KNX_SCHEMA_FOR_PLATFORM = {
|
|||||||
Platform.LIGHT: LIGHT_KNX_SCHEMA,
|
Platform.LIGHT: LIGHT_KNX_SCHEMA,
|
||||||
Platform.SENSOR: SENSOR_KNX_SCHEMA,
|
Platform.SENSOR: SENSOR_KNX_SCHEMA,
|
||||||
Platform.SWITCH: SWITCH_KNX_SCHEMA,
|
Platform.SWITCH: SWITCH_KNX_SCHEMA,
|
||||||
|
Platform.TEXT: TEXT_KNX_SCHEMA,
|
||||||
Platform.TIME: TIME_KNX_SCHEMA,
|
Platform.TIME: TIME_KNX_SCHEMA,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -816,6 +816,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"text": {
|
||||||
|
"description": "The KNX text platform is used as an interface to text objects.",
|
||||||
|
"knx": {
|
||||||
|
"ga_text": {
|
||||||
|
"description": "The group address of the text object.",
|
||||||
|
"label": "Text"
|
||||||
|
},
|
||||||
|
"mode": {
|
||||||
|
"description": "Select how the entity is displayed in Home Assistant.",
|
||||||
|
"label": "[%common::config_flow::data::mode%]",
|
||||||
|
"options": {
|
||||||
|
"password": "[%common::config_flow::data::password%]",
|
||||||
|
"text": "[%key:component::text::entity_component::_::state_attributes::mode::state::text%]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"time": {
|
"time": {
|
||||||
"description": "The KNX time platform is used as an interface to time objects.",
|
"description": "The KNX time platform is used as an interface to time objects.",
|
||||||
"knx": {
|
"knx": {
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from xknx import XKNX
|
from propcache.api import cached_property
|
||||||
from xknx.devices import Notification as XknxNotification
|
from xknx.devices import Notification as XknxNotification
|
||||||
from xknx.dpt import DPTLatin1
|
from xknx.dpt import DPTLatin1
|
||||||
|
|
||||||
from homeassistant import config_entries
|
from homeassistant import config_entries
|
||||||
from homeassistant.components.text import TextEntity
|
from homeassistant.components.text import TextEntity, TextMode
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
CONF_ENTITY_CATEGORY,
|
CONF_ENTITY_CATEGORY,
|
||||||
CONF_MODE,
|
CONF_MODE,
|
||||||
@@ -18,13 +18,25 @@ from homeassistant.const import (
|
|||||||
Platform,
|
Platform,
|
||||||
)
|
)
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
from homeassistant.helpers.entity_platform import (
|
||||||
|
AddConfigEntryEntitiesCallback,
|
||||||
|
async_get_current_platform,
|
||||||
|
)
|
||||||
from homeassistant.helpers.restore_state import RestoreEntity
|
from homeassistant.helpers.restore_state import RestoreEntity
|
||||||
from homeassistant.helpers.typing import ConfigType
|
from homeassistant.helpers.typing import ConfigType
|
||||||
|
|
||||||
from .const import CONF_RESPOND_TO_READ, CONF_STATE_ADDRESS, KNX_ADDRESS, KNX_MODULE_KEY
|
from .const import (
|
||||||
from .entity import KnxYamlEntity
|
CONF_RESPOND_TO_READ,
|
||||||
|
CONF_STATE_ADDRESS,
|
||||||
|
CONF_SYNC_STATE,
|
||||||
|
DOMAIN,
|
||||||
|
KNX_ADDRESS,
|
||||||
|
KNX_MODULE_KEY,
|
||||||
|
)
|
||||||
|
from .entity import KnxUiEntity, KnxUiEntityPlatformController, KnxYamlEntity
|
||||||
from .knx_module import KNXModule
|
from .knx_module import KNXModule
|
||||||
|
from .storage.const import CONF_ENTITY, CONF_GA_TEXT
|
||||||
|
from .storage.util import ConfigExtractor
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
@@ -32,46 +44,39 @@ async def async_setup_entry(
|
|||||||
config_entry: config_entries.ConfigEntry,
|
config_entry: config_entries.ConfigEntry,
|
||||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up sensor(s) for KNX platform."""
|
"""Set up text(s) for KNX platform."""
|
||||||
knx_module = hass.data[KNX_MODULE_KEY]
|
knx_module = hass.data[KNX_MODULE_KEY]
|
||||||
config: list[ConfigType] = knx_module.config_yaml[Platform.TEXT]
|
platform = async_get_current_platform()
|
||||||
|
knx_module.config_store.add_platform(
|
||||||
async_add_entities(KNXText(knx_module, entity_config) for entity_config in config)
|
platform=Platform.TEXT,
|
||||||
|
controller=KnxUiEntityPlatformController(
|
||||||
|
knx_module=knx_module,
|
||||||
def _create_notification(xknx: XKNX, config: ConfigType) -> XknxNotification:
|
entity_platform=platform,
|
||||||
"""Return a KNX Notification to be used within XKNX."""
|
entity_class=KnxUiText,
|
||||||
return XknxNotification(
|
),
|
||||||
xknx,
|
|
||||||
name=config[CONF_NAME],
|
|
||||||
group_address=config[KNX_ADDRESS],
|
|
||||||
group_address_state=config.get(CONF_STATE_ADDRESS),
|
|
||||||
respond_to_read=config[CONF_RESPOND_TO_READ],
|
|
||||||
value_type=config[CONF_TYPE],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
entities: list[KnxYamlEntity | KnxUiEntity] = []
|
||||||
|
if yaml_platform_config := knx_module.config_yaml.get(Platform.TEXT):
|
||||||
|
entities.extend(
|
||||||
|
KnxYamlText(knx_module, entity_config)
|
||||||
|
for entity_config in yaml_platform_config
|
||||||
|
)
|
||||||
|
if ui_config := knx_module.config_store.data["entities"].get(Platform.TEXT):
|
||||||
|
entities.extend(
|
||||||
|
KnxUiText(knx_module, unique_id, config)
|
||||||
|
for unique_id, config in ui_config.items()
|
||||||
|
)
|
||||||
|
if entities:
|
||||||
|
async_add_entities(entities)
|
||||||
|
|
||||||
class KNXText(KnxYamlEntity, TextEntity, RestoreEntity):
|
|
||||||
|
class _KnxText(TextEntity, RestoreEntity):
|
||||||
"""Representation of a KNX text."""
|
"""Representation of a KNX text."""
|
||||||
|
|
||||||
_device: XknxNotification
|
_device: XknxNotification
|
||||||
_attr_native_max = 14
|
_attr_native_max = 14
|
||||||
|
|
||||||
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
|
|
||||||
"""Initialize a KNX text."""
|
|
||||||
super().__init__(
|
|
||||||
knx_module=knx_module,
|
|
||||||
device=_create_notification(knx_module.xknx, config),
|
|
||||||
)
|
|
||||||
self._attr_mode = config[CONF_MODE]
|
|
||||||
self._attr_pattern = (
|
|
||||||
r"[\u0000-\u00ff]*" # Latin-1
|
|
||||||
if issubclass(self._device.remote_value.dpt_class, DPTLatin1)
|
|
||||||
else r"[\u0000-\u007f]*" # ASCII
|
|
||||||
)
|
|
||||||
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
|
||||||
self._attr_unique_id = str(self._device.remote_value.group_address)
|
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
async def async_added_to_hass(self) -> None:
|
||||||
"""Restore last state."""
|
"""Restore last state."""
|
||||||
await super().async_added_to_hass()
|
await super().async_added_to_hass()
|
||||||
@@ -81,6 +86,15 @@ class KNXText(KnxYamlEntity, TextEntity, RestoreEntity):
|
|||||||
if last_state.state not in (STATE_UNKNOWN, STATE_UNAVAILABLE):
|
if last_state.state not in (STATE_UNKNOWN, STATE_UNAVAILABLE):
|
||||||
self._device.remote_value.value = last_state.state
|
self._device.remote_value.value = last_state.state
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def pattern(self) -> str | None:
|
||||||
|
"""Return the regex pattern that the value must match."""
|
||||||
|
return (
|
||||||
|
r"[\u0000-\u00ff]*" # Latin-1
|
||||||
|
if issubclass(self._device.remote_value.dpt_class, DPTLatin1)
|
||||||
|
else r"[\u0000-\u007f]*" # ASCII
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def native_value(self) -> str | None:
|
def native_value(self) -> str | None:
|
||||||
"""Return the value reported by the text."""
|
"""Return the value reported by the text."""
|
||||||
@@ -89,3 +103,56 @@ class KNXText(KnxYamlEntity, TextEntity, RestoreEntity):
|
|||||||
async def async_set_value(self, value: str) -> None:
|
async def async_set_value(self, value: str) -> None:
|
||||||
"""Change the value."""
|
"""Change the value."""
|
||||||
await self._device.set(value)
|
await self._device.set(value)
|
||||||
|
|
||||||
|
|
||||||
|
class KnxYamlText(_KnxText, KnxYamlEntity):
|
||||||
|
"""Representation of a KNX text configured from YAML."""
|
||||||
|
|
||||||
|
_device: XknxNotification
|
||||||
|
|
||||||
|
def __init__(self, knx_module: KNXModule, config: ConfigType) -> None:
|
||||||
|
"""Initialize a KNX text."""
|
||||||
|
super().__init__(
|
||||||
|
knx_module=knx_module,
|
||||||
|
device=XknxNotification(
|
||||||
|
knx_module.xknx,
|
||||||
|
name=config[CONF_NAME],
|
||||||
|
group_address=config[KNX_ADDRESS],
|
||||||
|
group_address_state=config.get(CONF_STATE_ADDRESS),
|
||||||
|
respond_to_read=config[CONF_RESPOND_TO_READ],
|
||||||
|
value_type=config[CONF_TYPE],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
self._attr_mode = config[CONF_MODE]
|
||||||
|
self._attr_entity_category = config.get(CONF_ENTITY_CATEGORY)
|
||||||
|
self._attr_unique_id = str(self._device.remote_value.group_address)
|
||||||
|
|
||||||
|
|
||||||
|
class KnxUiText(_KnxText, KnxUiEntity):
|
||||||
|
"""Representation of a KNX text configured from UI."""
|
||||||
|
|
||||||
|
_device: XknxNotification
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
knx_module: KNXModule,
|
||||||
|
unique_id: str,
|
||||||
|
config: ConfigType,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize a KNX text."""
|
||||||
|
super().__init__(
|
||||||
|
knx_module=knx_module,
|
||||||
|
unique_id=unique_id,
|
||||||
|
entity_config=config[CONF_ENTITY],
|
||||||
|
)
|
||||||
|
knx_conf = ConfigExtractor(config[DOMAIN])
|
||||||
|
self._device = XknxNotification(
|
||||||
|
knx_module.xknx,
|
||||||
|
name=config[CONF_ENTITY][CONF_NAME],
|
||||||
|
group_address=knx_conf.get_write(CONF_GA_TEXT),
|
||||||
|
group_address_state=knx_conf.get_state_and_passive(CONF_GA_TEXT),
|
||||||
|
respond_to_read=knx_conf.get(CONF_RESPOND_TO_READ),
|
||||||
|
sync_state=knx_conf.get(CONF_SYNC_STATE),
|
||||||
|
value_type=knx_conf.get_dpt(CONF_GA_TEXT),
|
||||||
|
)
|
||||||
|
self._attr_mode = TextMode(knx_conf.get(CONF_MODE))
|
||||||
|
|||||||
29
tests/components/knx/fixtures/config_store_text.json
Normal file
29
tests/components/knx/fixtures/config_store_text.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"minor_version": 2,
|
||||||
|
"key": "knx/config_store.json",
|
||||||
|
"data": {
|
||||||
|
"entities": {
|
||||||
|
"text": {
|
||||||
|
"knx_es_01KCXYT0WEJEQQ13SGCY1NHCYV": {
|
||||||
|
"entity": {
|
||||||
|
"name": "test",
|
||||||
|
"device_info": null,
|
||||||
|
"entity_category": null
|
||||||
|
},
|
||||||
|
"knx": {
|
||||||
|
"ga_text": {
|
||||||
|
"write": "1/1/1",
|
||||||
|
"dpt": "16.001",
|
||||||
|
"state": "2/2/2",
|
||||||
|
"passive": []
|
||||||
|
},
|
||||||
|
"respond_to_read": false,
|
||||||
|
"sync_state": true,
|
||||||
|
"mode": "text"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1967,6 +1967,69 @@
|
|||||||
'type': 'result',
|
'type': 'result',
|
||||||
})
|
})
|
||||||
# ---
|
# ---
|
||||||
|
# name: test_knx_get_schema[text]
|
||||||
|
dict({
|
||||||
|
'id': 1,
|
||||||
|
'result': list([
|
||||||
|
dict({
|
||||||
|
'name': 'ga_text',
|
||||||
|
'options': dict({
|
||||||
|
'dptClasses': list([
|
||||||
|
'string',
|
||||||
|
]),
|
||||||
|
'passive': True,
|
||||||
|
'state': dict({
|
||||||
|
'required': False,
|
||||||
|
}),
|
||||||
|
'write': dict({
|
||||||
|
'required': True,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'required': True,
|
||||||
|
'type': 'knx_group_address',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'default': 'text',
|
||||||
|
'name': 'mode',
|
||||||
|
'required': True,
|
||||||
|
'selector': dict({
|
||||||
|
'select': dict({
|
||||||
|
'custom_value': False,
|
||||||
|
'multiple': False,
|
||||||
|
'options': list([
|
||||||
|
'password',
|
||||||
|
'text',
|
||||||
|
]),
|
||||||
|
'sort': False,
|
||||||
|
'translation_key': 'component.knx.config_panel.entities.create.text.knx.mode',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'type': 'ha_selector',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'default': False,
|
||||||
|
'name': 'respond_to_read',
|
||||||
|
'optional': True,
|
||||||
|
'required': False,
|
||||||
|
'selector': dict({
|
||||||
|
'boolean': dict({
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
'type': 'ha_selector',
|
||||||
|
}),
|
||||||
|
dict({
|
||||||
|
'allow_false': False,
|
||||||
|
'default': True,
|
||||||
|
'name': 'sync_state',
|
||||||
|
'optional': True,
|
||||||
|
'required': False,
|
||||||
|
'type': 'knx_sync_state',
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
'success': True,
|
||||||
|
'type': 'result',
|
||||||
|
})
|
||||||
|
# ---
|
||||||
# name: test_knx_get_schema[time]
|
# name: test_knx_get_schema[time]
|
||||||
dict({
|
dict({
|
||||||
'id': 1,
|
'id': 1,
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
|
|
||||||
from homeassistant.components.knx.const import CONF_RESPOND_TO_READ, KNX_ADDRESS
|
from homeassistant.components.knx.const import CONF_RESPOND_TO_READ, KNX_ADDRESS
|
||||||
from homeassistant.components.knx.schema import TextSchema
|
from homeassistant.components.knx.schema import TextSchema
|
||||||
from homeassistant.const import CONF_NAME
|
from homeassistant.components.text import TextMode
|
||||||
|
from homeassistant.const import CONF_NAME, Platform
|
||||||
from homeassistant.core import HomeAssistant, State
|
from homeassistant.core import HomeAssistant, State
|
||||||
|
|
||||||
|
from . import KnxEntityGenerator
|
||||||
from .conftest import KNXTestKit
|
from .conftest import KNXTestKit
|
||||||
|
|
||||||
from tests.common import mock_restore_cache
|
from tests.common import mock_restore_cache
|
||||||
@@ -99,3 +101,44 @@ async def test_text_restore_and_respond(hass: HomeAssistant, knx: KNXTestKit) ->
|
|||||||
)
|
)
|
||||||
state = hass.states.get("text.test")
|
state = hass.states.get("text.test")
|
||||||
assert state.state == "hallo"
|
assert state.state == "hallo"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_text_ui_create(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
knx: KNXTestKit,
|
||||||
|
create_ui_entity: KnxEntityGenerator,
|
||||||
|
) -> None:
|
||||||
|
"""Test creating a text."""
|
||||||
|
await knx.setup_integration()
|
||||||
|
await create_ui_entity(
|
||||||
|
platform=Platform.TEXT,
|
||||||
|
entity_data={"name": "test"},
|
||||||
|
knx_data={
|
||||||
|
"ga_text": {"write": "1/1/1", "dpt": "16.000"},
|
||||||
|
"mode": TextMode.PASSWORD,
|
||||||
|
"sync_state": True,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
await hass.services.async_call(
|
||||||
|
"text",
|
||||||
|
"set_value",
|
||||||
|
{"entity_id": "text.test", "value": "hallo"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
await knx.assert_write(
|
||||||
|
"1/1/1",
|
||||||
|
(0x68, 0x61, 0x6C, 0x6C, 0x6F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0),
|
||||||
|
)
|
||||||
|
knx.assert_state("text.test", "hallo", mode=TextMode.PASSWORD)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_text_ui_load(knx: KNXTestKit) -> None:
|
||||||
|
"""Test loading a text from storage."""
|
||||||
|
await knx.setup_integration(config_store_fixture="config_store_text.json")
|
||||||
|
|
||||||
|
await knx.assert_read("2/2/2")
|
||||||
|
await knx.receive_response(
|
||||||
|
"2/2/2",
|
||||||
|
(0x68, 0x61, 0x6C, 0x6C, 0x6F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0),
|
||||||
|
)
|
||||||
|
knx.assert_state("text.test", "hallo", mode=TextMode.TEXT)
|
||||||
|
|||||||
Reference in New Issue
Block a user