mirror of
https://github.com/home-assistant/core.git
synced 2025-12-20 14:58:50 +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.SENSOR,
|
||||
Platform.SWITCH,
|
||||
Platform.TEXT,
|
||||
Platform.TIME,
|
||||
}
|
||||
|
||||
|
||||
@@ -74,3 +74,6 @@ CONF_GA_SATURATION: Final = "ga_saturation"
|
||||
|
||||
# Sensor
|
||||
CONF_ALWAYS_CALLBACK: Final = "always_callback"
|
||||
|
||||
# Text
|
||||
CONF_GA_TEXT: Final = "ga_text"
|
||||
|
||||
@@ -13,10 +13,12 @@ from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.components.text import TextMode
|
||||
from homeassistant.const import (
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_ENTITY_ID,
|
||||
CONF_MODE,
|
||||
CONF_NAME,
|
||||
CONF_PLATFORM,
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
@@ -90,6 +92,7 @@ from .const import (
|
||||
CONF_GA_SWITCH,
|
||||
CONF_GA_TEMPERATURE_CURRENT,
|
||||
CONF_GA_TEMPERATURE_TARGET,
|
||||
CONF_GA_TEXT,
|
||||
CONF_GA_TIME,
|
||||
CONF_GA_UP_DOWN,
|
||||
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(
|
||||
{
|
||||
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.SENSOR: SENSOR_KNX_SCHEMA,
|
||||
Platform.SWITCH: SWITCH_KNX_SCHEMA,
|
||||
Platform.TEXT: TEXT_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": {
|
||||
"description": "The KNX time platform is used as an interface to time objects.",
|
||||
"knx": {
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from xknx import XKNX
|
||||
from propcache.api import cached_property
|
||||
from xknx.devices import Notification as XknxNotification
|
||||
from xknx.dpt import DPTLatin1
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.text import TextEntity
|
||||
from homeassistant.components.text import TextEntity, TextMode
|
||||
from homeassistant.const import (
|
||||
CONF_ENTITY_CATEGORY,
|
||||
CONF_MODE,
|
||||
@@ -18,13 +18,25 @@ from homeassistant.const import (
|
||||
Platform,
|
||||
)
|
||||
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.typing import ConfigType
|
||||
|
||||
from .const import CONF_RESPOND_TO_READ, CONF_STATE_ADDRESS, KNX_ADDRESS, KNX_MODULE_KEY
|
||||
from .entity import KnxYamlEntity
|
||||
from .const import (
|
||||
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 .storage.const import CONF_ENTITY, CONF_GA_TEXT
|
||||
from .storage.util import ConfigExtractor
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@@ -32,46 +44,39 @@ async def async_setup_entry(
|
||||
config_entry: config_entries.ConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up sensor(s) for KNX platform."""
|
||||
"""Set up text(s) for KNX platform."""
|
||||
knx_module = hass.data[KNX_MODULE_KEY]
|
||||
config: list[ConfigType] = knx_module.config_yaml[Platform.TEXT]
|
||||
|
||||
async_add_entities(KNXText(knx_module, entity_config) for entity_config in config)
|
||||
|
||||
|
||||
def _create_notification(xknx: XKNX, config: ConfigType) -> XknxNotification:
|
||||
"""Return a KNX Notification to be used within XKNX."""
|
||||
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],
|
||||
platform = async_get_current_platform()
|
||||
knx_module.config_store.add_platform(
|
||||
platform=Platform.TEXT,
|
||||
controller=KnxUiEntityPlatformController(
|
||||
knx_module=knx_module,
|
||||
entity_platform=platform,
|
||||
entity_class=KnxUiText,
|
||||
),
|
||||
)
|
||||
|
||||
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."""
|
||||
|
||||
_device: XknxNotification
|
||||
_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:
|
||||
"""Restore last state."""
|
||||
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):
|
||||
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
|
||||
def native_value(self) -> str | None:
|
||||
"""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:
|
||||
"""Change the 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',
|
||||
})
|
||||
# ---
|
||||
# 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]
|
||||
dict({
|
||||
'id': 1,
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
|
||||
from homeassistant.components.knx.const import CONF_RESPOND_TO_READ, KNX_ADDRESS
|
||||
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 . import KnxEntityGenerator
|
||||
from .conftest import KNXTestKit
|
||||
|
||||
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")
|
||||
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