mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Centralize knx config and update xknx to 0.12.0 (#39219)
* Refactor KNX integration to centralize configuration yaml (#39189) * Updates for xknx 0.12.0 (#38880)
This commit is contained in:
parent
79f4b6eb6b
commit
a2651845f3
@ -223,7 +223,7 @@ homeassistant/components/keba/* @dannerph
|
||||
homeassistant/components/keenetic_ndms2/* @foxel
|
||||
homeassistant/components/kef/* @basnijholt
|
||||
homeassistant/components/keyboard_remote/* @bendavid
|
||||
homeassistant/components/knx/* @Julius2342
|
||||
homeassistant/components/knx/* @Julius2342 @farmio @marvin-w
|
||||
homeassistant/components/kodi/* @armills @OnFreund
|
||||
homeassistant/components/konnected/* @heythisisnate @kit-klein
|
||||
homeassistant/components/lametric/* @robbiet480
|
||||
|
@ -3,8 +3,8 @@ import logging
|
||||
|
||||
import voluptuous as vol
|
||||
from xknx import XKNX
|
||||
from xknx.devices import ActionCallback, DateTime, DateTimeBroadcastType, ExposeSensor
|
||||
from xknx.dpt import DPTArray, DPTBinary
|
||||
from xknx.devices import DateTime, ExposeSensor
|
||||
from xknx.dpt import DPTArray, DPTBase, DPTBinary
|
||||
from xknx.exceptions import XKNXException
|
||||
from xknx.io import DEFAULT_MCAST_PORT, ConnectionConfig, ConnectionType
|
||||
from xknx.telegram import AddressFilter, GroupAddress, Telegram
|
||||
@ -23,60 +23,61 @@ from homeassistant.core import callback
|
||||
from homeassistant.helpers import discovery
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.event import async_track_state_change_event
|
||||
from homeassistant.helpers.script import Script
|
||||
|
||||
from .const import DATA_KNX, DOMAIN, DeviceTypes
|
||||
from .factory import create_knx_device
|
||||
from .schema import (
|
||||
BinarySensorSchema,
|
||||
ClimateSchema,
|
||||
ConnectionSchema,
|
||||
CoverSchema,
|
||||
ExposeSchema,
|
||||
LightSchema,
|
||||
NotifySchema,
|
||||
SceneSchema,
|
||||
SensorSchema,
|
||||
SwitchSchema,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DOMAIN = "knx"
|
||||
DATA_KNX = "data_knx"
|
||||
CONF_KNX_CONFIG = "config_file"
|
||||
|
||||
CONF_KNX_ROUTING = "routing"
|
||||
CONF_KNX_TUNNELING = "tunneling"
|
||||
CONF_KNX_LOCAL_IP = "local_ip"
|
||||
CONF_KNX_FIRE_EVENT = "fire_event"
|
||||
CONF_KNX_FIRE_EVENT_FILTER = "fire_event_filter"
|
||||
CONF_KNX_STATE_UPDATER = "state_updater"
|
||||
CONF_KNX_RATE_LIMIT = "rate_limit"
|
||||
CONF_KNX_EXPOSE = "expose"
|
||||
CONF_KNX_EXPOSE_TYPE = "type"
|
||||
CONF_KNX_EXPOSE_ATTRIBUTE = "attribute"
|
||||
CONF_KNX_EXPOSE_DEFAULT = "default"
|
||||
CONF_KNX_EXPOSE_ADDRESS = "address"
|
||||
|
||||
CONF_KNX_LIGHT = "light"
|
||||
CONF_KNX_COVER = "cover"
|
||||
CONF_KNX_BINARY_SENSOR = "binary_sensor"
|
||||
CONF_KNX_SCENE = "scene"
|
||||
CONF_KNX_SENSOR = "sensor"
|
||||
CONF_KNX_SWITCH = "switch"
|
||||
CONF_KNX_NOTIFY = "notify"
|
||||
CONF_KNX_CLIMATE = "climate"
|
||||
|
||||
SERVICE_KNX_SEND = "send"
|
||||
SERVICE_KNX_ATTR_ADDRESS = "address"
|
||||
SERVICE_KNX_ATTR_PAYLOAD = "payload"
|
||||
SERVICE_KNX_ATTR_TYPE = "type"
|
||||
|
||||
ATTR_DISCOVER_DEVICES = "devices"
|
||||
|
||||
TUNNELING_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Optional(CONF_KNX_LOCAL_IP): cv.string,
|
||||
vol.Optional(CONF_PORT): cv.port,
|
||||
}
|
||||
)
|
||||
|
||||
ROUTING_SCHEMA = vol.Schema({vol.Optional(CONF_KNX_LOCAL_IP): cv.string})
|
||||
|
||||
EXPOSE_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_KNX_EXPOSE_TYPE): cv.string,
|
||||
vol.Optional(CONF_ENTITY_ID): cv.entity_id,
|
||||
vol.Optional(CONF_KNX_EXPOSE_ATTRIBUTE): cv.string,
|
||||
vol.Optional(CONF_KNX_EXPOSE_DEFAULT): cv.match_all,
|
||||
vol.Required(CONF_KNX_EXPOSE_ADDRESS): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
DOMAIN: vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_KNX_CONFIG): cv.string,
|
||||
vol.Exclusive(CONF_KNX_ROUTING, "connection_type"): ROUTING_SCHEMA,
|
||||
vol.Exclusive(CONF_KNX_TUNNELING, "connection_type"): TUNNELING_SCHEMA,
|
||||
vol.Exclusive(
|
||||
CONF_KNX_ROUTING, "connection_type"
|
||||
): ConnectionSchema.ROUTING_SCHEMA,
|
||||
vol.Exclusive(
|
||||
CONF_KNX_TUNNELING, "connection_type"
|
||||
): ConnectionSchema.TUNNELING_SCHEMA,
|
||||
vol.Inclusive(CONF_KNX_FIRE_EVENT, "fire_ev"): cv.boolean,
|
||||
vol.Inclusive(CONF_KNX_FIRE_EVENT_FILTER, "fire_ev"): vol.All(
|
||||
cv.ensure_list, [cv.string]
|
||||
@ -85,7 +86,33 @@ CONFIG_SCHEMA = vol.Schema(
|
||||
vol.Optional(CONF_KNX_RATE_LIMIT, default=20): vol.All(
|
||||
vol.Coerce(int), vol.Range(min=1, max=100)
|
||||
),
|
||||
vol.Optional(CONF_KNX_EXPOSE): vol.All(cv.ensure_list, [EXPOSE_SCHEMA]),
|
||||
vol.Optional(CONF_KNX_EXPOSE): vol.All(
|
||||
cv.ensure_list, [ExposeSchema.SCHEMA]
|
||||
),
|
||||
vol.Optional(CONF_KNX_COVER): vol.All(
|
||||
cv.ensure_list, [CoverSchema.SCHEMA]
|
||||
),
|
||||
vol.Optional(CONF_KNX_BINARY_SENSOR): vol.All(
|
||||
cv.ensure_list, [BinarySensorSchema.SCHEMA]
|
||||
),
|
||||
vol.Optional(CONF_KNX_LIGHT): vol.All(
|
||||
cv.ensure_list, [LightSchema.SCHEMA]
|
||||
),
|
||||
vol.Optional(CONF_KNX_CLIMATE): vol.All(
|
||||
cv.ensure_list, [ClimateSchema.SCHEMA]
|
||||
),
|
||||
vol.Optional(CONF_KNX_NOTIFY): vol.All(
|
||||
cv.ensure_list, [NotifySchema.SCHEMA]
|
||||
),
|
||||
vol.Optional(CONF_KNX_SWITCH): vol.All(
|
||||
cv.ensure_list, [SwitchSchema.SCHEMA]
|
||||
),
|
||||
vol.Optional(CONF_KNX_SENSOR): vol.All(
|
||||
cv.ensure_list, [SensorSchema.SCHEMA]
|
||||
),
|
||||
vol.Optional(CONF_KNX_SCENE): vol.All(
|
||||
cv.ensure_list, [SceneSchema.SCHEMA]
|
||||
),
|
||||
}
|
||||
)
|
||||
},
|
||||
@ -98,9 +125,21 @@ SERVICE_KNX_SEND_SCHEMA = vol.Schema(
|
||||
vol.Required(SERVICE_KNX_ATTR_PAYLOAD): vol.Any(
|
||||
cv.positive_int, [cv.positive_int]
|
||||
),
|
||||
vol.Optional(SERVICE_KNX_ATTR_TYPE): vol.Any(int, float, str),
|
||||
}
|
||||
)
|
||||
|
||||
KNX_CONFIG_PLATFORM_MAPPING = {
|
||||
CONF_KNX_COVER: DeviceTypes.cover,
|
||||
CONF_KNX_SWITCH: DeviceTypes.switch,
|
||||
CONF_KNX_LIGHT: DeviceTypes.light,
|
||||
CONF_KNX_SENSOR: DeviceTypes.sensor,
|
||||
CONF_KNX_NOTIFY: DeviceTypes.notify,
|
||||
CONF_KNX_SCENE: DeviceTypes.scene,
|
||||
CONF_KNX_BINARY_SENSOR: DeviceTypes.binary_sensor,
|
||||
CONF_KNX_CLIMATE: DeviceTypes.climate,
|
||||
}
|
||||
|
||||
|
||||
async def async_setup(hass, config):
|
||||
"""Set up the KNX component."""
|
||||
@ -114,6 +153,15 @@ async def async_setup(hass, config):
|
||||
f"Can't connect to KNX interface: <br><b>{ex}</b>", title="KNX"
|
||||
)
|
||||
|
||||
for platform_config, device_type in KNX_CONFIG_PLATFORM_MAPPING.items():
|
||||
if platform_config in config[DOMAIN]:
|
||||
for device_config in config[DOMAIN][platform_config]:
|
||||
hass.data[DATA_KNX].xknx.devices.add(
|
||||
create_knx_device(
|
||||
hass, device_type, hass.data[DATA_KNX].xknx, device_config
|
||||
)
|
||||
)
|
||||
|
||||
for component, discovery_type in (
|
||||
("switch", "Switch"),
|
||||
("climate", "Climate"),
|
||||
@ -203,11 +251,15 @@ class KNXModule:
|
||||
return self.connection_config_tunneling()
|
||||
if CONF_KNX_ROUTING in self.config[DOMAIN]:
|
||||
return self.connection_config_routing()
|
||||
return self.connection_config_auto()
|
||||
# return None to let xknx use config from xknx.yaml connection block if given
|
||||
# otherwise it will use default ConnectionConfig (Automatic)
|
||||
return None
|
||||
|
||||
def connection_config_routing(self):
|
||||
"""Return the connection_config if routing is configured."""
|
||||
local_ip = self.config[DOMAIN][CONF_KNX_ROUTING].get(CONF_KNX_LOCAL_IP)
|
||||
local_ip = self.config[DOMAIN][CONF_KNX_ROUTING].get(
|
||||
ConnectionSchema.CONF_KNX_LOCAL_IP
|
||||
)
|
||||
return ConnectionConfig(
|
||||
connection_type=ConnectionType.ROUTING, local_ip=local_ip
|
||||
)
|
||||
@ -216,7 +268,9 @@ class KNXModule:
|
||||
"""Return the connection_config if tunneling is configured."""
|
||||
gateway_ip = self.config[DOMAIN][CONF_KNX_TUNNELING][CONF_HOST]
|
||||
gateway_port = self.config[DOMAIN][CONF_KNX_TUNNELING].get(CONF_PORT)
|
||||
local_ip = self.config[DOMAIN][CONF_KNX_TUNNELING].get(CONF_KNX_LOCAL_IP)
|
||||
local_ip = self.config[DOMAIN][CONF_KNX_TUNNELING].get(
|
||||
ConnectionSchema.CONF_KNX_LOCAL_IP
|
||||
)
|
||||
if gateway_port is None:
|
||||
gateway_port = DEFAULT_MCAST_PORT
|
||||
return ConnectionConfig(
|
||||
@ -227,11 +281,6 @@ class KNXModule:
|
||||
auto_reconnect=True,
|
||||
)
|
||||
|
||||
def connection_config_auto(self):
|
||||
"""Return the connection_config if auto is configured."""
|
||||
# pylint: disable=no-self-use
|
||||
return ConnectionConfig()
|
||||
|
||||
def register_callbacks(self):
|
||||
"""Register callbacks within XKNX object."""
|
||||
if (
|
||||
@ -251,11 +300,11 @@ class KNXModule:
|
||||
if CONF_KNX_EXPOSE not in self.config[DOMAIN]:
|
||||
return
|
||||
for to_expose in self.config[DOMAIN][CONF_KNX_EXPOSE]:
|
||||
expose_type = to_expose.get(CONF_KNX_EXPOSE_TYPE)
|
||||
expose_type = to_expose.get(ExposeSchema.CONF_KNX_EXPOSE_TYPE)
|
||||
entity_id = to_expose.get(CONF_ENTITY_ID)
|
||||
attribute = to_expose.get(CONF_KNX_EXPOSE_ATTRIBUTE)
|
||||
default = to_expose.get(CONF_KNX_EXPOSE_DEFAULT)
|
||||
address = to_expose.get(CONF_KNX_EXPOSE_ADDRESS)
|
||||
attribute = to_expose.get(ExposeSchema.CONF_KNX_EXPOSE_ATTRIBUTE)
|
||||
default = to_expose.get(ExposeSchema.CONF_KNX_EXPOSE_DEFAULT)
|
||||
address = to_expose.get(ExposeSchema.CONF_KNX_EXPOSE_ADDRESS)
|
||||
if expose_type in ["time", "date", "datetime"]:
|
||||
exposure = KNXExposeTime(self.xknx, expose_type, address)
|
||||
exposure.async_register()
|
||||
@ -286,9 +335,15 @@ class KNXModule:
|
||||
"""Service for sending an arbitrary KNX message to the KNX bus."""
|
||||
attr_payload = call.data.get(SERVICE_KNX_ATTR_PAYLOAD)
|
||||
attr_address = call.data.get(SERVICE_KNX_ATTR_ADDRESS)
|
||||
attr_type = call.data.get(SERVICE_KNX_ATTR_TYPE)
|
||||
|
||||
def calculate_payload(attr_payload):
|
||||
"""Calculate payload depending on type of attribute."""
|
||||
if attr_type is not None:
|
||||
transcoder = DPTBase.parse_transcoder(attr_type)
|
||||
if transcoder is None:
|
||||
raise ValueError(f"Invalid type for knx.send service: {attr_type}")
|
||||
return DPTArray(transcoder.to_knx(attr_payload))
|
||||
if isinstance(attr_payload, int):
|
||||
return DPTBinary(attr_payload)
|
||||
return DPTArray(attr_payload)
|
||||
@ -302,22 +357,6 @@ class KNXModule:
|
||||
await self.xknx.telegrams.put(telegram)
|
||||
|
||||
|
||||
class KNXAutomation:
|
||||
"""Wrapper around xknx.devices.ActionCallback object.."""
|
||||
|
||||
def __init__(self, hass, device, hook, action, counter=1):
|
||||
"""Initialize Automation class."""
|
||||
self.hass = hass
|
||||
self.device = device
|
||||
script_name = f"{device.get_name()} turn ON script"
|
||||
self.script = Script(hass, action, script_name, DOMAIN)
|
||||
|
||||
self.action = ActionCallback(
|
||||
hass.data[DATA_KNX].xknx, self.script.async_run, hook=hook, counter=counter
|
||||
)
|
||||
device.actions.append(self.action)
|
||||
|
||||
|
||||
class KNXExposeTime:
|
||||
"""Object to Expose Time/Date object to KNX bus."""
|
||||
|
||||
@ -332,7 +371,7 @@ class KNXExposeTime:
|
||||
def async_register(self):
|
||||
"""Register listener."""
|
||||
broadcast_type_string = self.type.upper()
|
||||
broadcast_type = DateTimeBroadcastType[broadcast_type_string]
|
||||
broadcast_type = broadcast_type_string
|
||||
self.device = DateTime(
|
||||
self.xknx, "Time", broadcast_type=broadcast_type, group_address=self.address
|
||||
)
|
||||
|
@ -1,60 +1,16 @@
|
||||
"""Support for KNX/IP binary sensors."""
|
||||
import voluptuous as vol
|
||||
from xknx.devices import BinarySensor
|
||||
from xknx.devices import BinarySensor as XknxBinarySensor
|
||||
|
||||
from homeassistant.components.binary_sensor import PLATFORM_SCHEMA, BinarySensorEntity
|
||||
from homeassistant.const import CONF_DEVICE_CLASS, CONF_NAME
|
||||
from homeassistant.components.binary_sensor import BinarySensorEntity
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from . import ATTR_DISCOVER_DEVICES, DATA_KNX, KNXAutomation
|
||||
|
||||
CONF_STATE_ADDRESS = "state_address"
|
||||
CONF_SIGNIFICANT_BIT = "significant_bit"
|
||||
CONF_DEFAULT_SIGNIFICANT_BIT = 1
|
||||
CONF_SYNC_STATE = "sync_state"
|
||||
CONF_AUTOMATION = "automation"
|
||||
CONF_HOOK = "hook"
|
||||
CONF_DEFAULT_HOOK = "on"
|
||||
CONF_COUNTER = "counter"
|
||||
CONF_DEFAULT_COUNTER = 1
|
||||
CONF_ACTION = "action"
|
||||
CONF_RESET_AFTER = "reset_after"
|
||||
|
||||
CONF__ACTION = "turn_off_action"
|
||||
|
||||
DEFAULT_NAME = "KNX Binary Sensor"
|
||||
AUTOMATION_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_HOOK, default=CONF_DEFAULT_HOOK): cv.string,
|
||||
vol.Optional(CONF_COUNTER, default=CONF_DEFAULT_COUNTER): cv.port,
|
||||
vol.Required(CONF_ACTION): cv.SCRIPT_SCHEMA,
|
||||
}
|
||||
)
|
||||
|
||||
AUTOMATIONS_SCHEMA = vol.All(cv.ensure_list, [AUTOMATION_SCHEMA])
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(
|
||||
CONF_SIGNIFICANT_BIT, default=CONF_DEFAULT_SIGNIFICANT_BIT
|
||||
): cv.positive_int,
|
||||
vol.Optional(CONF_SYNC_STATE, default=True): cv.boolean,
|
||||
vol.Required(CONF_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_DEVICE_CLASS): cv.string,
|
||||
vol.Optional(CONF_RESET_AFTER): cv.positive_int,
|
||||
vol.Optional(CONF_AUTOMATION): AUTOMATIONS_SCHEMA,
|
||||
}
|
||||
)
|
||||
from . import ATTR_DISCOVER_DEVICES, DATA_KNX
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up binary sensor(s) for KNX platform."""
|
||||
if discovery_info is not None:
|
||||
async_add_entities_discovery(hass, discovery_info, async_add_entities)
|
||||
else:
|
||||
async_add_entities_config(hass, config, async_add_entities)
|
||||
|
||||
|
||||
@callback
|
||||
@ -67,48 +23,12 @@ def async_add_entities_discovery(hass, discovery_info, async_add_entities):
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
@callback
|
||||
def async_add_entities_config(hass, config, async_add_entities):
|
||||
"""Set up binary senor for KNX platform configured within platform."""
|
||||
name = config[CONF_NAME]
|
||||
|
||||
binary_sensor = BinarySensor(
|
||||
hass.data[DATA_KNX].xknx,
|
||||
name=name,
|
||||
group_address_state=config[CONF_STATE_ADDRESS],
|
||||
sync_state=config[CONF_SYNC_STATE],
|
||||
device_class=config.get(CONF_DEVICE_CLASS),
|
||||
significant_bit=config[CONF_SIGNIFICANT_BIT],
|
||||
reset_after=config.get(CONF_RESET_AFTER),
|
||||
)
|
||||
hass.data[DATA_KNX].xknx.devices.add(binary_sensor)
|
||||
|
||||
entity = KNXBinarySensor(binary_sensor)
|
||||
automations = config.get(CONF_AUTOMATION)
|
||||
if automations is not None:
|
||||
for automation in automations:
|
||||
counter = automation[CONF_COUNTER]
|
||||
hook = automation[CONF_HOOK]
|
||||
action = automation[CONF_ACTION]
|
||||
entity.automations.append(
|
||||
KNXAutomation(
|
||||
hass=hass,
|
||||
device=binary_sensor,
|
||||
hook=hook,
|
||||
action=action,
|
||||
counter=counter,
|
||||
)
|
||||
)
|
||||
async_add_entities([entity])
|
||||
|
||||
|
||||
class KNXBinarySensor(BinarySensorEntity):
|
||||
"""Representation of a KNX binary sensor."""
|
||||
|
||||
def __init__(self, device):
|
||||
def __init__(self, device: XknxBinarySensor):
|
||||
"""Initialize of KNX binary sensor."""
|
||||
self.device = device
|
||||
self.automations = []
|
||||
|
||||
@callback
|
||||
def async_register_callbacks(self):
|
||||
|
@ -1,127 +1,31 @@
|
||||
"""Support for KNX/IP climate devices."""
|
||||
from typing import List, Optional
|
||||
|
||||
import voluptuous as vol
|
||||
from xknx.devices import Climate as XknxClimate, ClimateMode as XknxClimateMode
|
||||
from xknx.devices import Climate as XknxClimate
|
||||
from xknx.dpt import HVACOperationMode
|
||||
|
||||
from homeassistant.components.climate import PLATFORM_SCHEMA, ClimateEntity
|
||||
from homeassistant.components.climate import ClimateEntity
|
||||
from homeassistant.components.climate.const import (
|
||||
HVAC_MODE_AUTO,
|
||||
HVAC_MODE_COOL,
|
||||
HVAC_MODE_DRY,
|
||||
HVAC_MODE_FAN_ONLY,
|
||||
HVAC_MODE_HEAT,
|
||||
HVAC_MODE_OFF,
|
||||
PRESET_AWAY,
|
||||
PRESET_COMFORT,
|
||||
PRESET_ECO,
|
||||
PRESET_SLEEP,
|
||||
SUPPORT_PRESET_MODE,
|
||||
SUPPORT_TARGET_TEMPERATURE,
|
||||
)
|
||||
from homeassistant.const import ATTR_TEMPERATURE, CONF_NAME, TEMP_CELSIUS
|
||||
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from . import ATTR_DISCOVER_DEVICES, DATA_KNX
|
||||
|
||||
CONF_SETPOINT_SHIFT_ADDRESS = "setpoint_shift_address"
|
||||
CONF_SETPOINT_SHIFT_STATE_ADDRESS = "setpoint_shift_state_address"
|
||||
CONF_SETPOINT_SHIFT_STEP = "setpoint_shift_step"
|
||||
CONF_SETPOINT_SHIFT_MAX = "setpoint_shift_max"
|
||||
CONF_SETPOINT_SHIFT_MIN = "setpoint_shift_min"
|
||||
CONF_TEMPERATURE_ADDRESS = "temperature_address"
|
||||
CONF_TARGET_TEMPERATURE_ADDRESS = "target_temperature_address"
|
||||
CONF_TARGET_TEMPERATURE_STATE_ADDRESS = "target_temperature_state_address"
|
||||
CONF_OPERATION_MODE_ADDRESS = "operation_mode_address"
|
||||
CONF_OPERATION_MODE_STATE_ADDRESS = "operation_mode_state_address"
|
||||
CONF_CONTROLLER_STATUS_ADDRESS = "controller_status_address"
|
||||
CONF_CONTROLLER_STATUS_STATE_ADDRESS = "controller_status_state_address"
|
||||
CONF_CONTROLLER_MODE_ADDRESS = "controller_mode_address"
|
||||
CONF_CONTROLLER_MODE_STATE_ADDRESS = "controller_mode_state_address"
|
||||
CONF_OPERATION_MODE_FROST_PROTECTION_ADDRESS = "operation_mode_frost_protection_address"
|
||||
CONF_OPERATION_MODE_NIGHT_ADDRESS = "operation_mode_night_address"
|
||||
CONF_OPERATION_MODE_COMFORT_ADDRESS = "operation_mode_comfort_address"
|
||||
CONF_OPERATION_MODES = "operation_modes"
|
||||
CONF_ON_OFF_ADDRESS = "on_off_address"
|
||||
CONF_ON_OFF_STATE_ADDRESS = "on_off_state_address"
|
||||
CONF_ON_OFF_INVERT = "on_off_invert"
|
||||
CONF_MIN_TEMP = "min_temp"
|
||||
CONF_MAX_TEMP = "max_temp"
|
||||
|
||||
DEFAULT_NAME = "KNX Climate"
|
||||
DEFAULT_SETPOINT_SHIFT_STEP = 0.5
|
||||
DEFAULT_SETPOINT_SHIFT_MAX = 6
|
||||
DEFAULT_SETPOINT_SHIFT_MIN = -6
|
||||
DEFAULT_ON_OFF_INVERT = False
|
||||
# Map KNX operation modes to HA modes. This list might not be full.
|
||||
OPERATION_MODES = {
|
||||
# Map DPT 201.105 HVAC control modes
|
||||
"Auto": HVAC_MODE_AUTO,
|
||||
"Heat": HVAC_MODE_HEAT,
|
||||
"Cool": HVAC_MODE_COOL,
|
||||
"Off": HVAC_MODE_OFF,
|
||||
"Fan only": HVAC_MODE_FAN_ONLY,
|
||||
"Dry": HVAC_MODE_DRY,
|
||||
}
|
||||
from .const import OPERATION_MODES, PRESET_MODES
|
||||
|
||||
OPERATION_MODES_INV = dict(reversed(item) for item in OPERATION_MODES.items())
|
||||
|
||||
PRESET_MODES = {
|
||||
# Map DPT 201.100 HVAC operating modes to HA presets
|
||||
"Frost Protection": PRESET_ECO,
|
||||
"Night": PRESET_SLEEP,
|
||||
"Standby": PRESET_AWAY,
|
||||
"Comfort": PRESET_COMFORT,
|
||||
}
|
||||
|
||||
PRESET_MODES_INV = dict(reversed(item) for item in PRESET_MODES.items())
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(
|
||||
CONF_SETPOINT_SHIFT_STEP, default=DEFAULT_SETPOINT_SHIFT_STEP
|
||||
): vol.All(float, vol.Range(min=0, max=2)),
|
||||
vol.Optional(
|
||||
CONF_SETPOINT_SHIFT_MAX, default=DEFAULT_SETPOINT_SHIFT_MAX
|
||||
): vol.All(int, vol.Range(min=0, max=32)),
|
||||
vol.Optional(
|
||||
CONF_SETPOINT_SHIFT_MIN, default=DEFAULT_SETPOINT_SHIFT_MIN
|
||||
): vol.All(int, vol.Range(min=-32, max=0)),
|
||||
vol.Required(CONF_TEMPERATURE_ADDRESS): cv.string,
|
||||
vol.Required(CONF_TARGET_TEMPERATURE_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_TARGET_TEMPERATURE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_SETPOINT_SHIFT_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_SETPOINT_SHIFT_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_OPERATION_MODE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_OPERATION_MODE_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_CONTROLLER_STATUS_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_CONTROLLER_STATUS_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_CONTROLLER_MODE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_CONTROLLER_MODE_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_OPERATION_MODE_FROST_PROTECTION_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_OPERATION_MODE_NIGHT_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_OPERATION_MODE_COMFORT_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_ON_OFF_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_ON_OFF_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_ON_OFF_INVERT, default=DEFAULT_ON_OFF_INVERT): cv.boolean,
|
||||
vol.Optional(CONF_OPERATION_MODES): vol.All(
|
||||
cv.ensure_list, [vol.In({**OPERATION_MODES, **PRESET_MODES})]
|
||||
),
|
||||
vol.Optional(CONF_MIN_TEMP): vol.Coerce(float),
|
||||
vol.Optional(CONF_MAX_TEMP): vol.Coerce(float),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up climate(s) for KNX platform."""
|
||||
if discovery_info is not None:
|
||||
async_add_entities_discovery(hass, discovery_info, async_add_entities)
|
||||
else:
|
||||
async_add_entities_config(hass, config, async_add_entities)
|
||||
|
||||
|
||||
@callback
|
||||
@ -134,68 +38,10 @@ def async_add_entities_discovery(hass, discovery_info, async_add_entities):
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
@callback
|
||||
def async_add_entities_config(hass, config, async_add_entities):
|
||||
"""Set up climate for KNX platform configured within platform."""
|
||||
climate_mode = XknxClimateMode(
|
||||
hass.data[DATA_KNX].xknx,
|
||||
name=f"{config[CONF_NAME]} Mode",
|
||||
group_address_operation_mode=config.get(CONF_OPERATION_MODE_ADDRESS),
|
||||
group_address_operation_mode_state=config.get(
|
||||
CONF_OPERATION_MODE_STATE_ADDRESS
|
||||
),
|
||||
group_address_controller_status=config.get(CONF_CONTROLLER_STATUS_ADDRESS),
|
||||
group_address_controller_status_state=config.get(
|
||||
CONF_CONTROLLER_STATUS_STATE_ADDRESS
|
||||
),
|
||||
group_address_controller_mode=config.get(CONF_CONTROLLER_MODE_ADDRESS),
|
||||
group_address_controller_mode_state=config.get(
|
||||
CONF_CONTROLLER_MODE_STATE_ADDRESS
|
||||
),
|
||||
group_address_operation_mode_protection=config.get(
|
||||
CONF_OPERATION_MODE_FROST_PROTECTION_ADDRESS
|
||||
),
|
||||
group_address_operation_mode_night=config.get(
|
||||
CONF_OPERATION_MODE_NIGHT_ADDRESS
|
||||
),
|
||||
group_address_operation_mode_comfort=config.get(
|
||||
CONF_OPERATION_MODE_COMFORT_ADDRESS
|
||||
),
|
||||
operation_modes=config.get(CONF_OPERATION_MODES),
|
||||
)
|
||||
hass.data[DATA_KNX].xknx.devices.add(climate_mode)
|
||||
|
||||
climate = XknxClimate(
|
||||
hass.data[DATA_KNX].xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address_temperature=config[CONF_TEMPERATURE_ADDRESS],
|
||||
group_address_target_temperature=config.get(CONF_TARGET_TEMPERATURE_ADDRESS),
|
||||
group_address_target_temperature_state=config[
|
||||
CONF_TARGET_TEMPERATURE_STATE_ADDRESS
|
||||
],
|
||||
group_address_setpoint_shift=config.get(CONF_SETPOINT_SHIFT_ADDRESS),
|
||||
group_address_setpoint_shift_state=config.get(
|
||||
CONF_SETPOINT_SHIFT_STATE_ADDRESS
|
||||
),
|
||||
setpoint_shift_step=config[CONF_SETPOINT_SHIFT_STEP],
|
||||
setpoint_shift_max=config[CONF_SETPOINT_SHIFT_MAX],
|
||||
setpoint_shift_min=config[CONF_SETPOINT_SHIFT_MIN],
|
||||
group_address_on_off=config.get(CONF_ON_OFF_ADDRESS),
|
||||
group_address_on_off_state=config.get(CONF_ON_OFF_STATE_ADDRESS),
|
||||
min_temp=config.get(CONF_MIN_TEMP),
|
||||
max_temp=config.get(CONF_MAX_TEMP),
|
||||
mode=climate_mode,
|
||||
on_off_invert=config[CONF_ON_OFF_INVERT],
|
||||
)
|
||||
hass.data[DATA_KNX].xknx.devices.add(climate)
|
||||
|
||||
async_add_entities([KNXClimate(climate)])
|
||||
|
||||
|
||||
class KNXClimate(ClimateEntity):
|
||||
"""Representation of a KNX climate device."""
|
||||
|
||||
def __init__(self, device):
|
||||
def __init__(self, device: XknxClimate):
|
||||
"""Initialize of a KNX climate device."""
|
||||
self.device = device
|
||||
self._unit_of_measurement = TEMP_CELSIUS
|
||||
@ -278,8 +124,6 @@ class KNXClimate(ClimateEntity):
|
||||
"""Return current operation ie. heat, cool, idle."""
|
||||
if self.device.supports_on_off and not self.device.is_on:
|
||||
return HVAC_MODE_OFF
|
||||
if self.device.supports_on_off and self.device.is_on:
|
||||
return HVAC_MODE_HEAT
|
||||
if self.device.mode.supports_operation_mode:
|
||||
return OPERATION_MODES.get(
|
||||
self.device.mode.operation_mode.value, HVAC_MODE_HEAT
|
||||
@ -296,10 +140,11 @@ class KNXClimate(ClimateEntity):
|
||||
]
|
||||
|
||||
if self.device.supports_on_off:
|
||||
if not _operations:
|
||||
_operations.append(HVAC_MODE_HEAT)
|
||||
_operations.append(HVAC_MODE_OFF)
|
||||
|
||||
_modes = list(filter(None, _operations))
|
||||
_modes = list(set(filter(None, _operations)))
|
||||
# default to ["heat"]
|
||||
return _modes if _modes else [HVAC_MODE_HEAT]
|
||||
|
||||
@ -307,10 +152,13 @@ class KNXClimate(ClimateEntity):
|
||||
"""Set operation mode."""
|
||||
if self.device.supports_on_off and hvac_mode == HVAC_MODE_OFF:
|
||||
await self.device.turn_off()
|
||||
elif self.device.supports_on_off and hvac_mode == HVAC_MODE_HEAT:
|
||||
else:
|
||||
if self.device.supports_on_off and not self.device.is_on:
|
||||
await self.device.turn_on()
|
||||
elif self.device.mode.supports_operation_mode:
|
||||
knx_operation_mode = HVACOperationMode(OPERATION_MODES_INV.get(hvac_mode))
|
||||
if self.device.mode.supports_operation_mode:
|
||||
knx_operation_mode = HVACOperationMode(
|
||||
OPERATION_MODES_INV.get(hvac_mode)
|
||||
)
|
||||
await self.device.mode.set_operation_mode(knx_operation_mode)
|
||||
self.async_write_ha_state()
|
||||
|
||||
|
61
homeassistant/components/knx/const.py
Normal file
61
homeassistant/components/knx/const.py
Normal file
@ -0,0 +1,61 @@
|
||||
"""Constants for the KNX integration."""
|
||||
from enum import Enum
|
||||
|
||||
from homeassistant.components.climate.const import (
|
||||
HVAC_MODE_AUTO,
|
||||
HVAC_MODE_COOL,
|
||||
HVAC_MODE_DRY,
|
||||
HVAC_MODE_FAN_ONLY,
|
||||
HVAC_MODE_HEAT,
|
||||
HVAC_MODE_OFF,
|
||||
PRESET_AWAY,
|
||||
PRESET_COMFORT,
|
||||
PRESET_ECO,
|
||||
PRESET_SLEEP,
|
||||
)
|
||||
|
||||
DOMAIN = "knx"
|
||||
DATA_KNX = "data_knx"
|
||||
|
||||
CONF_STATE_ADDRESS = "state_address"
|
||||
CONF_SYNC_STATE = "sync_state"
|
||||
|
||||
|
||||
class ColorTempModes(Enum):
|
||||
"""Color temperature modes for config validation."""
|
||||
|
||||
absolute = "DPT-7.600"
|
||||
relative = "DPT-5.001"
|
||||
|
||||
|
||||
class DeviceTypes(Enum):
|
||||
"""KNX device types."""
|
||||
|
||||
cover = "cover"
|
||||
light = "light"
|
||||
binary_sensor = "binary_sensor"
|
||||
climate = "climate"
|
||||
switch = "switch"
|
||||
notify = "notify"
|
||||
scene = "scene"
|
||||
sensor = "sensor"
|
||||
|
||||
|
||||
# Map KNX operation modes to HA modes. This list might not be complete.
|
||||
OPERATION_MODES = {
|
||||
# Map DPT 20.105 HVAC control modes
|
||||
"Auto": HVAC_MODE_AUTO,
|
||||
"Heat": HVAC_MODE_HEAT,
|
||||
"Cool": HVAC_MODE_COOL,
|
||||
"Off": HVAC_MODE_OFF,
|
||||
"Fan only": HVAC_MODE_FAN_ONLY,
|
||||
"Dry": HVAC_MODE_DRY,
|
||||
}
|
||||
|
||||
PRESET_MODES = {
|
||||
# Map DPT 20.102 HVAC operating modes to HA presets
|
||||
"Frost Protection": PRESET_ECO,
|
||||
"Night": PRESET_SLEEP,
|
||||
"Standby": PRESET_AWAY,
|
||||
"Comfort": PRESET_COMFORT,
|
||||
}
|
@ -1,11 +1,10 @@
|
||||
"""Support for KNX/IP covers."""
|
||||
import voluptuous as vol
|
||||
from xknx.devices import Cover as XknxCover
|
||||
|
||||
from homeassistant.components.cover import (
|
||||
ATTR_POSITION,
|
||||
ATTR_TILT_POSITION,
|
||||
PLATFORM_SCHEMA,
|
||||
DEVICE_CLASS_BLIND,
|
||||
SUPPORT_CLOSE,
|
||||
SUPPORT_OPEN,
|
||||
SUPPORT_SET_POSITION,
|
||||
@ -13,53 +12,16 @@ from homeassistant.components.cover import (
|
||||
SUPPORT_STOP,
|
||||
CoverEntity,
|
||||
)
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.event import async_track_utc_time_change
|
||||
|
||||
from . import ATTR_DISCOVER_DEVICES, DATA_KNX
|
||||
|
||||
CONF_MOVE_LONG_ADDRESS = "move_long_address"
|
||||
CONF_MOVE_SHORT_ADDRESS = "move_short_address"
|
||||
CONF_POSITION_ADDRESS = "position_address"
|
||||
CONF_POSITION_STATE_ADDRESS = "position_state_address"
|
||||
CONF_ANGLE_ADDRESS = "angle_address"
|
||||
CONF_ANGLE_STATE_ADDRESS = "angle_state_address"
|
||||
CONF_TRAVELLING_TIME_DOWN = "travelling_time_down"
|
||||
CONF_TRAVELLING_TIME_UP = "travelling_time_up"
|
||||
CONF_INVERT_POSITION = "invert_position"
|
||||
CONF_INVERT_ANGLE = "invert_angle"
|
||||
|
||||
DEFAULT_TRAVEL_TIME = 25
|
||||
DEFAULT_NAME = "KNX Cover"
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_MOVE_LONG_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_MOVE_SHORT_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_POSITION_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_POSITION_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_ANGLE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_ANGLE_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(
|
||||
CONF_TRAVELLING_TIME_DOWN, default=DEFAULT_TRAVEL_TIME
|
||||
): cv.positive_int,
|
||||
vol.Optional(
|
||||
CONF_TRAVELLING_TIME_UP, default=DEFAULT_TRAVEL_TIME
|
||||
): cv.positive_int,
|
||||
vol.Optional(CONF_INVERT_POSITION, default=False): cv.boolean,
|
||||
vol.Optional(CONF_INVERT_ANGLE, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up cover(s) for KNX platform."""
|
||||
if discovery_info is not None:
|
||||
async_add_entities_discovery(hass, discovery_info, async_add_entities)
|
||||
else:
|
||||
async_add_entities_config(hass, config, async_add_entities)
|
||||
|
||||
|
||||
@callback
|
||||
@ -72,32 +34,10 @@ def async_add_entities_discovery(hass, discovery_info, async_add_entities):
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
@callback
|
||||
def async_add_entities_config(hass, config, async_add_entities):
|
||||
"""Set up cover for KNX platform configured within platform."""
|
||||
cover = XknxCover(
|
||||
hass.data[DATA_KNX].xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address_long=config.get(CONF_MOVE_LONG_ADDRESS),
|
||||
group_address_short=config.get(CONF_MOVE_SHORT_ADDRESS),
|
||||
group_address_position_state=config.get(CONF_POSITION_STATE_ADDRESS),
|
||||
group_address_angle=config.get(CONF_ANGLE_ADDRESS),
|
||||
group_address_angle_state=config.get(CONF_ANGLE_STATE_ADDRESS),
|
||||
group_address_position=config.get(CONF_POSITION_ADDRESS),
|
||||
travel_time_down=config[CONF_TRAVELLING_TIME_DOWN],
|
||||
travel_time_up=config[CONF_TRAVELLING_TIME_UP],
|
||||
invert_position=config[CONF_INVERT_POSITION],
|
||||
invert_angle=config[CONF_INVERT_ANGLE],
|
||||
)
|
||||
|
||||
hass.data[DATA_KNX].xknx.devices.add(cover)
|
||||
async_add_entities([KNXCover(cover)])
|
||||
|
||||
|
||||
class KNXCover(CoverEntity):
|
||||
"""Representation of a KNX cover."""
|
||||
|
||||
def __init__(self, device):
|
||||
def __init__(self, device: XknxCover):
|
||||
"""Initialize the cover."""
|
||||
self.device = device
|
||||
self._unsubscribe_auto_updater = None
|
||||
@ -109,6 +49,8 @@ class KNXCover(CoverEntity):
|
||||
async def after_update_callback(device):
|
||||
"""Call after device was updated."""
|
||||
self.async_write_ha_state()
|
||||
if self.device.is_traveling():
|
||||
self.start_auto_updater()
|
||||
|
||||
self.device.register_device_updated_cb(after_update_callback)
|
||||
|
||||
@ -135,44 +77,62 @@ class KNXCover(CoverEntity):
|
||||
"""No polling needed within KNX."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return the class of this device, from component DEVICE_CLASSES."""
|
||||
if self.device.supports_angle:
|
||||
return DEVICE_CLASS_BLIND
|
||||
return None
|
||||
|
||||
@property
|
||||
def supported_features(self):
|
||||
"""Flag supported features."""
|
||||
supported_features = (
|
||||
SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_SET_POSITION | SUPPORT_STOP
|
||||
)
|
||||
supported_features = SUPPORT_OPEN | SUPPORT_CLOSE | SUPPORT_SET_POSITION
|
||||
if self.device.supports_stop:
|
||||
supported_features |= SUPPORT_STOP
|
||||
if self.device.supports_angle:
|
||||
supported_features |= SUPPORT_SET_TILT_POSITION
|
||||
return supported_features
|
||||
|
||||
@property
|
||||
def current_cover_position(self):
|
||||
"""Return the current position of the cover."""
|
||||
return self.device.current_position()
|
||||
"""Return the current position of the cover.
|
||||
|
||||
None is unknown, 0 is closed, 100 is fully open.
|
||||
"""
|
||||
# In KNX 0 is open, 100 is closed.
|
||||
try:
|
||||
return 100 - self.device.current_position()
|
||||
except TypeError:
|
||||
return None
|
||||
|
||||
@property
|
||||
def is_closed(self):
|
||||
"""Return if the cover is closed."""
|
||||
return self.device.is_closed()
|
||||
|
||||
@property
|
||||
def is_opening(self):
|
||||
"""Return if the cover is opening or not."""
|
||||
return self.device.is_opening()
|
||||
|
||||
@property
|
||||
def is_closing(self):
|
||||
"""Return if the cover is closing or not."""
|
||||
return self.device.is_closing()
|
||||
|
||||
async def async_close_cover(self, **kwargs):
|
||||
"""Close the cover."""
|
||||
if not self.device.is_closed():
|
||||
await self.device.set_down()
|
||||
self.start_auto_updater()
|
||||
|
||||
async def async_open_cover(self, **kwargs):
|
||||
"""Open the cover."""
|
||||
if not self.device.is_open():
|
||||
await self.device.set_up()
|
||||
self.start_auto_updater()
|
||||
|
||||
async def async_set_cover_position(self, **kwargs):
|
||||
"""Move the cover to a specific position."""
|
||||
if ATTR_POSITION in kwargs:
|
||||
position = kwargs[ATTR_POSITION]
|
||||
await self.device.set_position(position)
|
||||
self.start_auto_updater()
|
||||
knx_position = 100 - kwargs[ATTR_POSITION]
|
||||
await self.device.set_position(knx_position)
|
||||
|
||||
async def async_stop_cover(self, **kwargs):
|
||||
"""Stop the cover."""
|
||||
@ -184,13 +144,15 @@ class KNXCover(CoverEntity):
|
||||
"""Return current tilt position of cover."""
|
||||
if not self.device.supports_angle:
|
||||
return None
|
||||
return self.device.current_angle()
|
||||
try:
|
||||
return 100 - self.device.current_angle()
|
||||
except TypeError:
|
||||
return None
|
||||
|
||||
async def async_set_cover_tilt_position(self, **kwargs):
|
||||
"""Move the cover tilt to a specific position."""
|
||||
if ATTR_TILT_POSITION in kwargs:
|
||||
tilt_position = kwargs[ATTR_TILT_POSITION]
|
||||
await self.device.set_angle(tilt_position)
|
||||
knx_tilt_position = 100 - kwargs[ATTR_TILT_POSITION]
|
||||
await self.device.set_angle(knx_tilt_position)
|
||||
|
||||
def start_auto_updater(self):
|
||||
"""Start the autoupdater to update Home Assistant while cover is moving."""
|
||||
|
263
homeassistant/components/knx/factory.py
Normal file
263
homeassistant/components/knx/factory.py
Normal file
@ -0,0 +1,263 @@
|
||||
"""Factory function to initialize KNX devices from config."""
|
||||
from xknx import XKNX
|
||||
from xknx.devices import (
|
||||
ActionCallback as XknxActionCallback,
|
||||
BinarySensor as XknxBinarySensor,
|
||||
Climate as XknxClimate,
|
||||
ClimateMode as XknxClimateMode,
|
||||
Cover as XknxCover,
|
||||
Device as XknxDevice,
|
||||
Light as XknxLight,
|
||||
Notification as XknxNotification,
|
||||
Scene as XknxScene,
|
||||
Sensor as XknxSensor,
|
||||
Switch as XknxSwitch,
|
||||
)
|
||||
|
||||
from homeassistant.const import CONF_ADDRESS, CONF_DEVICE_CLASS, CONF_NAME, CONF_TYPE
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.script import Script
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import DATA_KNX, DOMAIN, ColorTempModes, DeviceTypes
|
||||
from .schema import (
|
||||
BinarySensorSchema,
|
||||
ClimateSchema,
|
||||
CoverSchema,
|
||||
LightSchema,
|
||||
SceneSchema,
|
||||
SensorSchema,
|
||||
SwitchSchema,
|
||||
)
|
||||
|
||||
|
||||
def create_knx_device(
|
||||
hass: HomeAssistant, device_type: DeviceTypes, knx_module: XKNX, config: ConfigType
|
||||
) -> XknxDevice:
|
||||
"""Return the requested XKNX device."""
|
||||
if device_type is DeviceTypes.light:
|
||||
return _create_light(knx_module, config)
|
||||
|
||||
if device_type is DeviceTypes.cover:
|
||||
return _create_cover(knx_module, config)
|
||||
|
||||
if device_type is DeviceTypes.climate:
|
||||
return _create_climate(hass, knx_module, config)
|
||||
|
||||
if device_type is DeviceTypes.switch:
|
||||
return _create_switch(knx_module, config)
|
||||
|
||||
if device_type is DeviceTypes.sensor:
|
||||
return _create_sensor(knx_module, config)
|
||||
|
||||
if device_type is DeviceTypes.notify:
|
||||
return _create_notify(knx_module, config)
|
||||
|
||||
if device_type is DeviceTypes.scene:
|
||||
return _create_scene(knx_module, config)
|
||||
|
||||
if device_type is DeviceTypes.binary_sensor:
|
||||
return _create_binary_sensor(hass, knx_module, config)
|
||||
|
||||
|
||||
def _create_cover(knx_module: XKNX, config: ConfigType) -> XknxCover:
|
||||
"""Return a KNX Cover device to be used within XKNX."""
|
||||
return XknxCover(
|
||||
knx_module,
|
||||
name=config[CONF_NAME],
|
||||
group_address_long=config.get(CoverSchema.CONF_MOVE_LONG_ADDRESS),
|
||||
group_address_short=config.get(CoverSchema.CONF_MOVE_SHORT_ADDRESS),
|
||||
group_address_stop=config.get(CoverSchema.CONF_STOP_ADDRESS),
|
||||
group_address_position_state=config.get(
|
||||
CoverSchema.CONF_POSITION_STATE_ADDRESS
|
||||
),
|
||||
group_address_angle=config.get(CoverSchema.CONF_ANGLE_ADDRESS),
|
||||
group_address_angle_state=config.get(CoverSchema.CONF_ANGLE_STATE_ADDRESS),
|
||||
group_address_position=config.get(CoverSchema.CONF_POSITION_ADDRESS),
|
||||
travel_time_down=config[CoverSchema.CONF_TRAVELLING_TIME_DOWN],
|
||||
travel_time_up=config[CoverSchema.CONF_TRAVELLING_TIME_UP],
|
||||
invert_position=config[CoverSchema.CONF_INVERT_POSITION],
|
||||
invert_angle=config[CoverSchema.CONF_INVERT_ANGLE],
|
||||
)
|
||||
|
||||
|
||||
def _create_light(knx_module: XKNX, config: ConfigType) -> XknxLight:
|
||||
"""Return a KNX Light device to be used within XKNX."""
|
||||
group_address_tunable_white = None
|
||||
group_address_tunable_white_state = None
|
||||
group_address_color_temp = None
|
||||
group_address_color_temp_state = None
|
||||
if config[LightSchema.CONF_COLOR_TEMP_MODE] == ColorTempModes.absolute:
|
||||
group_address_color_temp = config.get(LightSchema.CONF_COLOR_TEMP_ADDRESS)
|
||||
group_address_color_temp_state = config.get(
|
||||
LightSchema.CONF_COLOR_TEMP_STATE_ADDRESS
|
||||
)
|
||||
elif config[LightSchema.CONF_COLOR_TEMP_MODE] == ColorTempModes.relative:
|
||||
group_address_tunable_white = config.get(LightSchema.CONF_COLOR_TEMP_ADDRESS)
|
||||
group_address_tunable_white_state = config.get(
|
||||
LightSchema.CONF_COLOR_TEMP_STATE_ADDRESS
|
||||
)
|
||||
|
||||
return XknxLight(
|
||||
knx_module,
|
||||
name=config[CONF_NAME],
|
||||
group_address_switch=config[CONF_ADDRESS],
|
||||
group_address_switch_state=config.get(LightSchema.CONF_STATE_ADDRESS),
|
||||
group_address_brightness=config.get(LightSchema.CONF_BRIGHTNESS_ADDRESS),
|
||||
group_address_brightness_state=config.get(
|
||||
LightSchema.CONF_BRIGHTNESS_STATE_ADDRESS
|
||||
),
|
||||
group_address_color=config.get(LightSchema.CONF_COLOR_ADDRESS),
|
||||
group_address_color_state=config.get(LightSchema.CONF_COLOR_STATE_ADDRESS),
|
||||
group_address_rgbw=config.get(LightSchema.CONF_RGBW_ADDRESS),
|
||||
group_address_rgbw_state=config.get(LightSchema.CONF_RGBW_STATE_ADDRESS),
|
||||
group_address_tunable_white=group_address_tunable_white,
|
||||
group_address_tunable_white_state=group_address_tunable_white_state,
|
||||
group_address_color_temperature=group_address_color_temp,
|
||||
group_address_color_temperature_state=group_address_color_temp_state,
|
||||
min_kelvin=config[LightSchema.CONF_MIN_KELVIN],
|
||||
max_kelvin=config[LightSchema.CONF_MAX_KELVIN],
|
||||
)
|
||||
|
||||
|
||||
def _create_climate(
|
||||
hass: HomeAssistant, knx_module: XKNX, config: ConfigType
|
||||
) -> XknxClimate:
|
||||
"""Return a KNX Climate device to be used within XKNX."""
|
||||
climate_mode = XknxClimateMode(
|
||||
knx_module,
|
||||
name=f"{config[CONF_NAME]} Mode",
|
||||
group_address_operation_mode=config.get(
|
||||
ClimateSchema.CONF_OPERATION_MODE_ADDRESS
|
||||
),
|
||||
group_address_operation_mode_state=config.get(
|
||||
ClimateSchema.CONF_OPERATION_MODE_STATE_ADDRESS
|
||||
),
|
||||
group_address_controller_status=config.get(
|
||||
ClimateSchema.CONF_CONTROLLER_STATUS_ADDRESS
|
||||
),
|
||||
group_address_controller_status_state=config.get(
|
||||
ClimateSchema.CONF_CONTROLLER_STATUS_STATE_ADDRESS
|
||||
),
|
||||
group_address_controller_mode=config.get(
|
||||
ClimateSchema.CONF_CONTROLLER_MODE_ADDRESS
|
||||
),
|
||||
group_address_controller_mode_state=config.get(
|
||||
ClimateSchema.CONF_CONTROLLER_MODE_STATE_ADDRESS
|
||||
),
|
||||
group_address_operation_mode_protection=config.get(
|
||||
ClimateSchema.CONF_OPERATION_MODE_FROST_PROTECTION_ADDRESS
|
||||
),
|
||||
group_address_operation_mode_night=config.get(
|
||||
ClimateSchema.CONF_OPERATION_MODE_NIGHT_ADDRESS
|
||||
),
|
||||
group_address_operation_mode_comfort=config.get(
|
||||
ClimateSchema.CONF_OPERATION_MODE_COMFORT_ADDRESS
|
||||
),
|
||||
group_address_operation_mode_standby=config.get(
|
||||
ClimateSchema.CONF_OPERATION_MODE_STANDBY_ADDRESS
|
||||
),
|
||||
group_address_heat_cool=config.get(ClimateSchema.CONF_HEAT_COOL_ADDRESS),
|
||||
group_address_heat_cool_state=config.get(
|
||||
ClimateSchema.CONF_HEAT_COOL_STATE_ADDRESS
|
||||
),
|
||||
operation_modes=config.get(ClimateSchema.CONF_OPERATION_MODES),
|
||||
)
|
||||
hass.data[DATA_KNX].xknx.devices.add(climate_mode)
|
||||
|
||||
return XknxClimate(
|
||||
knx_module,
|
||||
name=config[CONF_NAME],
|
||||
group_address_temperature=config[ClimateSchema.CONF_TEMPERATURE_ADDRESS],
|
||||
group_address_target_temperature=config.get(
|
||||
ClimateSchema.CONF_TARGET_TEMPERATURE_ADDRESS
|
||||
),
|
||||
group_address_target_temperature_state=config[
|
||||
ClimateSchema.CONF_TARGET_TEMPERATURE_STATE_ADDRESS
|
||||
],
|
||||
group_address_setpoint_shift=config.get(
|
||||
ClimateSchema.CONF_SETPOINT_SHIFT_ADDRESS
|
||||
),
|
||||
group_address_setpoint_shift_state=config.get(
|
||||
ClimateSchema.CONF_SETPOINT_SHIFT_STATE_ADDRESS
|
||||
),
|
||||
setpoint_shift_mode=config[ClimateSchema.CONF_SETPOINT_SHIFT_MODE],
|
||||
setpoint_shift_max=config[ClimateSchema.CONF_SETPOINT_SHIFT_MAX],
|
||||
setpoint_shift_min=config[ClimateSchema.CONF_SETPOINT_SHIFT_MIN],
|
||||
temperature_step=config[ClimateSchema.CONF_TEMPERATURE_STEP],
|
||||
group_address_on_off=config.get(ClimateSchema.CONF_ON_OFF_ADDRESS),
|
||||
group_address_on_off_state=config.get(ClimateSchema.CONF_ON_OFF_STATE_ADDRESS),
|
||||
min_temp=config.get(ClimateSchema.CONF_MIN_TEMP),
|
||||
max_temp=config.get(ClimateSchema.CONF_MAX_TEMP),
|
||||
mode=climate_mode,
|
||||
on_off_invert=config[ClimateSchema.CONF_ON_OFF_INVERT],
|
||||
)
|
||||
|
||||
|
||||
def _create_switch(knx_module: XKNX, config: ConfigType) -> XknxSwitch:
|
||||
"""Return a KNX switch to be used within XKNX."""
|
||||
return XknxSwitch(
|
||||
knx_module,
|
||||
name=config[CONF_NAME],
|
||||
group_address=config[CONF_ADDRESS],
|
||||
group_address_state=config.get(SwitchSchema.CONF_STATE_ADDRESS),
|
||||
)
|
||||
|
||||
|
||||
def _create_sensor(knx_module: XKNX, config: ConfigType) -> XknxSensor:
|
||||
"""Return a KNX sensor to be used within XKNX."""
|
||||
return XknxSensor(
|
||||
knx_module,
|
||||
name=config[CONF_NAME],
|
||||
group_address_state=config[SensorSchema.CONF_STATE_ADDRESS],
|
||||
sync_state=config[SensorSchema.CONF_SYNC_STATE],
|
||||
value_type=config[CONF_TYPE],
|
||||
)
|
||||
|
||||
|
||||
def _create_notify(knx_module: XKNX, config: ConfigType) -> XknxNotification:
|
||||
"""Return a KNX notification to be used within XKNX."""
|
||||
return XknxNotification(
|
||||
knx_module, name=config[CONF_NAME], group_address=config[CONF_ADDRESS],
|
||||
)
|
||||
|
||||
|
||||
def _create_scene(knx_module: XKNX, config: ConfigType) -> XknxScene:
|
||||
"""Return a KNX scene to be used within XKNX."""
|
||||
return XknxScene(
|
||||
knx_module,
|
||||
name=config[CONF_NAME],
|
||||
group_address=config[CONF_ADDRESS],
|
||||
scene_number=config[SceneSchema.CONF_SCENE_NUMBER],
|
||||
)
|
||||
|
||||
|
||||
def _create_binary_sensor(
|
||||
hass: HomeAssistant, knx_module: XKNX, config: ConfigType
|
||||
) -> XknxBinarySensor:
|
||||
"""Return a KNX binary sensor to be used within XKNX."""
|
||||
device_name = config[CONF_NAME]
|
||||
actions = []
|
||||
automations = config.get(BinarySensorSchema.CONF_AUTOMATION)
|
||||
if automations is not None:
|
||||
for automation in automations:
|
||||
counter = automation[BinarySensorSchema.CONF_COUNTER]
|
||||
hook = automation[BinarySensorSchema.CONF_HOOK]
|
||||
action = automation[BinarySensorSchema.CONF_ACTION]
|
||||
script_name = f"{device_name} turn ON script"
|
||||
script = Script(hass, action, script_name, DOMAIN)
|
||||
action = XknxActionCallback(
|
||||
knx_module, script.async_run, hook=hook, counter=counter
|
||||
)
|
||||
actions.append(action)
|
||||
|
||||
return XknxBinarySensor(
|
||||
knx_module,
|
||||
name=device_name,
|
||||
group_address_state=config[BinarySensorSchema.CONF_STATE_ADDRESS],
|
||||
sync_state=config[BinarySensorSchema.CONF_SYNC_STATE],
|
||||
device_class=config.get(CONF_DEVICE_CLASS),
|
||||
ignore_internal_state=config[BinarySensorSchema.CONF_IGNORE_INTERNAL_STATE],
|
||||
reset_after=config.get(BinarySensorSchema.CONF_RESET_AFTER),
|
||||
actions=actions,
|
||||
)
|
@ -1,7 +1,4 @@
|
||||
"""Support for KNX/IP lights."""
|
||||
from enum import Enum
|
||||
|
||||
import voluptuous as vol
|
||||
from xknx.devices import Light as XknxLight
|
||||
|
||||
from homeassistant.components.light import (
|
||||
@ -9,81 +6,26 @@ from homeassistant.components.light import (
|
||||
ATTR_COLOR_TEMP,
|
||||
ATTR_HS_COLOR,
|
||||
ATTR_WHITE_VALUE,
|
||||
PLATFORM_SCHEMA,
|
||||
SUPPORT_BRIGHTNESS,
|
||||
SUPPORT_COLOR,
|
||||
SUPPORT_COLOR_TEMP,
|
||||
SUPPORT_WHITE_VALUE,
|
||||
LightEntity,
|
||||
)
|
||||
from homeassistant.const import CONF_ADDRESS, CONF_NAME
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
import homeassistant.util.color as color_util
|
||||
|
||||
from . import ATTR_DISCOVER_DEVICES, DATA_KNX
|
||||
|
||||
CONF_STATE_ADDRESS = "state_address"
|
||||
CONF_BRIGHTNESS_ADDRESS = "brightness_address"
|
||||
CONF_BRIGHTNESS_STATE_ADDRESS = "brightness_state_address"
|
||||
CONF_COLOR_ADDRESS = "color_address"
|
||||
CONF_COLOR_STATE_ADDRESS = "color_state_address"
|
||||
CONF_COLOR_TEMP_ADDRESS = "color_temperature_address"
|
||||
CONF_COLOR_TEMP_STATE_ADDRESS = "color_temperature_state_address"
|
||||
CONF_COLOR_TEMP_MODE = "color_temperature_mode"
|
||||
CONF_RGBW_ADDRESS = "rgbw_address"
|
||||
CONF_RGBW_STATE_ADDRESS = "rgbw_state_address"
|
||||
CONF_MIN_KELVIN = "min_kelvin"
|
||||
CONF_MAX_KELVIN = "max_kelvin"
|
||||
|
||||
DEFAULT_NAME = "KNX Light"
|
||||
DEFAULT_COLOR = (0.0, 0.0)
|
||||
DEFAULT_BRIGHTNESS = 255
|
||||
DEFAULT_COLOR_TEMP_MODE = "absolute"
|
||||
DEFAULT_WHITE_VALUE = 255
|
||||
DEFAULT_MIN_KELVIN = 2700 # 370 mireds
|
||||
DEFAULT_MAX_KELVIN = 6000 # 166 mireds
|
||||
|
||||
|
||||
class ColorTempModes(Enum):
|
||||
"""Color temperature modes for config validation."""
|
||||
|
||||
absolute = "DPT-7.600"
|
||||
relative = "DPT-5.001"
|
||||
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_BRIGHTNESS_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_BRIGHTNESS_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_COLOR_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_COLOR_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_COLOR_TEMP_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_COLOR_TEMP_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_COLOR_TEMP_MODE, default=DEFAULT_COLOR_TEMP_MODE): cv.enum(
|
||||
ColorTempModes
|
||||
),
|
||||
vol.Optional(CONF_RGBW_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_RGBW_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_MIN_KELVIN, default=DEFAULT_MIN_KELVIN): vol.All(
|
||||
vol.Coerce(int), vol.Range(min=1)
|
||||
),
|
||||
vol.Optional(CONF_MAX_KELVIN, default=DEFAULT_MAX_KELVIN): vol.All(
|
||||
vol.Coerce(int), vol.Range(min=1)
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up lights for KNX platform."""
|
||||
if discovery_info is not None:
|
||||
async_add_entities_discovery(hass, discovery_info, async_add_entities)
|
||||
else:
|
||||
async_add_entities_config(hass, config, async_add_entities)
|
||||
|
||||
|
||||
@callback
|
||||
@ -96,46 +38,10 @@ def async_add_entities_discovery(hass, discovery_info, async_add_entities):
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
@callback
|
||||
def async_add_entities_config(hass, config, async_add_entities):
|
||||
"""Set up light for KNX platform configured within platform."""
|
||||
group_address_tunable_white = None
|
||||
group_address_tunable_white_state = None
|
||||
group_address_color_temp = None
|
||||
group_address_color_temp_state = None
|
||||
if config[CONF_COLOR_TEMP_MODE] == ColorTempModes.absolute:
|
||||
group_address_color_temp = config.get(CONF_COLOR_TEMP_ADDRESS)
|
||||
group_address_color_temp_state = config.get(CONF_COLOR_TEMP_STATE_ADDRESS)
|
||||
elif config[CONF_COLOR_TEMP_MODE] == ColorTempModes.relative:
|
||||
group_address_tunable_white = config.get(CONF_COLOR_TEMP_ADDRESS)
|
||||
group_address_tunable_white_state = config.get(CONF_COLOR_TEMP_STATE_ADDRESS)
|
||||
|
||||
light = XknxLight(
|
||||
hass.data[DATA_KNX].xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address_switch=config[CONF_ADDRESS],
|
||||
group_address_switch_state=config.get(CONF_STATE_ADDRESS),
|
||||
group_address_brightness=config.get(CONF_BRIGHTNESS_ADDRESS),
|
||||
group_address_brightness_state=config.get(CONF_BRIGHTNESS_STATE_ADDRESS),
|
||||
group_address_color=config.get(CONF_COLOR_ADDRESS),
|
||||
group_address_color_state=config.get(CONF_COLOR_STATE_ADDRESS),
|
||||
group_address_rgbw=config.get(CONF_RGBW_ADDRESS),
|
||||
group_address_rgbw_state=config.get(CONF_RGBW_STATE_ADDRESS),
|
||||
group_address_tunable_white=group_address_tunable_white,
|
||||
group_address_tunable_white_state=group_address_tunable_white_state,
|
||||
group_address_color_temperature=group_address_color_temp,
|
||||
group_address_color_temperature_state=group_address_color_temp_state,
|
||||
min_kelvin=config[CONF_MIN_KELVIN],
|
||||
max_kelvin=config[CONF_MAX_KELVIN],
|
||||
)
|
||||
hass.data[DATA_KNX].xknx.devices.add(light)
|
||||
async_add_entities([KNXLight(light)])
|
||||
|
||||
|
||||
class KNXLight(LightEntity):
|
||||
"""Representation of a KNX light."""
|
||||
|
||||
def __init__(self, device):
|
||||
def __init__(self, device: XknxLight):
|
||||
"""Initialize of KNX light."""
|
||||
self.device = device
|
||||
|
||||
|
@ -2,6 +2,6 @@
|
||||
"domain": "knx",
|
||||
"name": "KNX",
|
||||
"documentation": "https://www.home-assistant.io/integrations/knx",
|
||||
"requirements": ["xknx==0.11.3"],
|
||||
"codeowners": ["@Julius2342"]
|
||||
"requirements": ["xknx==0.12.0"],
|
||||
"codeowners": ["@Julius2342", "@farmio", "@marvin-w"]
|
||||
}
|
||||
|
@ -1,31 +1,16 @@
|
||||
"""Support for KNX/IP notification services."""
|
||||
import voluptuous as vol
|
||||
from xknx.devices import Notification as XknxNotification
|
||||
|
||||
from homeassistant.components.notify import PLATFORM_SCHEMA, BaseNotificationService
|
||||
from homeassistant.const import CONF_ADDRESS, CONF_NAME
|
||||
from homeassistant.components.notify import BaseNotificationService
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from . import ATTR_DISCOVER_DEVICES, DATA_KNX
|
||||
|
||||
DEFAULT_NAME = "KNX Notify"
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def async_get_service(hass, config, discovery_info=None):
|
||||
"""Get the KNX notification service."""
|
||||
return (
|
||||
if discovery_info is not None:
|
||||
async_get_service_discovery(hass, discovery_info)
|
||||
if discovery_info is not None
|
||||
else async_get_service_config(hass, config)
|
||||
)
|
||||
|
||||
|
||||
@callback
|
||||
@ -40,22 +25,10 @@ def async_get_service_discovery(hass, discovery_info):
|
||||
)
|
||||
|
||||
|
||||
@callback
|
||||
def async_get_service_config(hass, config):
|
||||
"""Set up notification for KNX platform configured within platform."""
|
||||
notification = XknxNotification(
|
||||
hass.data[DATA_KNX].xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address=config[CONF_ADDRESS],
|
||||
)
|
||||
hass.data[DATA_KNX].xknx.devices.add(notification)
|
||||
return KNXNotificationService([notification])
|
||||
|
||||
|
||||
class KNXNotificationService(BaseNotificationService):
|
||||
"""Implement demo notification service."""
|
||||
|
||||
def __init__(self, devices):
|
||||
def __init__(self, devices: XknxNotification):
|
||||
"""Initialize the service."""
|
||||
self.devices = devices
|
||||
|
||||
|
@ -1,35 +1,18 @@
|
||||
"""Support for KNX scenes."""
|
||||
from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
from xknx.devices import Scene as XknxScene
|
||||
|
||||
from homeassistant.components.scene import CONF_PLATFORM, Scene
|
||||
from homeassistant.const import CONF_ADDRESS, CONF_NAME
|
||||
from homeassistant.components.scene import Scene
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from . import ATTR_DISCOVER_DEVICES, DATA_KNX
|
||||
|
||||
CONF_SCENE_NUMBER = "scene_number"
|
||||
|
||||
DEFAULT_NAME = "KNX SCENE"
|
||||
PLATFORM_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_PLATFORM): "knx",
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Required(CONF_ADDRESS): cv.string,
|
||||
vol.Required(CONF_SCENE_NUMBER): cv.positive_int,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up the scenes for KNX platform."""
|
||||
if discovery_info is not None:
|
||||
async_add_entities_discovery(hass, discovery_info, async_add_entities)
|
||||
else:
|
||||
async_add_entities_config(hass, config, async_add_entities)
|
||||
|
||||
|
||||
@callback
|
||||
@ -42,23 +25,10 @@ def async_add_entities_discovery(hass, discovery_info, async_add_entities):
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
@callback
|
||||
def async_add_entities_config(hass, config, async_add_entities):
|
||||
"""Set up scene for KNX platform configured within platform."""
|
||||
scene = XknxScene(
|
||||
hass.data[DATA_KNX].xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address=config[CONF_ADDRESS],
|
||||
scene_number=config[CONF_SCENE_NUMBER],
|
||||
)
|
||||
hass.data[DATA_KNX].xknx.devices.add(scene)
|
||||
async_add_entities([KNXScene(scene)])
|
||||
|
||||
|
||||
class KNXScene(Scene):
|
||||
"""Representation of a KNX scene."""
|
||||
|
||||
def __init__(self, scene):
|
||||
def __init__(self, scene: XknxScene):
|
||||
"""Init KNX scene."""
|
||||
self.scene = scene
|
||||
|
||||
|
342
homeassistant/components/knx/schema.py
Normal file
342
homeassistant/components/knx/schema.py
Normal file
@ -0,0 +1,342 @@
|
||||
"""Voluptuous schemas for the KNX integration."""
|
||||
import voluptuous as vol
|
||||
from xknx.devices.climate import SetpointShiftMode
|
||||
|
||||
from homeassistant.const import (
|
||||
CONF_ADDRESS,
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_ENTITY_ID,
|
||||
CONF_HOST,
|
||||
CONF_NAME,
|
||||
CONF_PORT,
|
||||
CONF_TYPE,
|
||||
)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from .const import (
|
||||
CONF_STATE_ADDRESS,
|
||||
CONF_SYNC_STATE,
|
||||
OPERATION_MODES,
|
||||
PRESET_MODES,
|
||||
ColorTempModes,
|
||||
)
|
||||
|
||||
|
||||
class ConnectionSchema:
|
||||
"""Voluptuous schema for KNX connection."""
|
||||
|
||||
CONF_KNX_LOCAL_IP = "local_ip"
|
||||
|
||||
TUNNELING_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_HOST): cv.string,
|
||||
vol.Optional(CONF_KNX_LOCAL_IP): cv.string,
|
||||
vol.Optional(CONF_PORT): cv.port,
|
||||
}
|
||||
)
|
||||
|
||||
ROUTING_SCHEMA = vol.Schema({vol.Optional(CONF_KNX_LOCAL_IP): cv.string})
|
||||
|
||||
|
||||
class CoverSchema:
|
||||
"""Voluptuous schema for KNX covers."""
|
||||
|
||||
CONF_MOVE_LONG_ADDRESS = "move_long_address"
|
||||
CONF_MOVE_SHORT_ADDRESS = "move_short_address"
|
||||
CONF_STOP_ADDRESS = "stop_address"
|
||||
CONF_POSITION_ADDRESS = "position_address"
|
||||
CONF_POSITION_STATE_ADDRESS = "position_state_address"
|
||||
CONF_ANGLE_ADDRESS = "angle_address"
|
||||
CONF_ANGLE_STATE_ADDRESS = "angle_state_address"
|
||||
CONF_TRAVELLING_TIME_DOWN = "travelling_time_down"
|
||||
CONF_TRAVELLING_TIME_UP = "travelling_time_up"
|
||||
CONF_INVERT_POSITION = "invert_position"
|
||||
CONF_INVERT_ANGLE = "invert_angle"
|
||||
|
||||
DEFAULT_TRAVEL_TIME = 25
|
||||
DEFAULT_NAME = "KNX Cover"
|
||||
|
||||
SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_MOVE_LONG_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_MOVE_SHORT_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_STOP_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_POSITION_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_POSITION_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_ANGLE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_ANGLE_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(
|
||||
CONF_TRAVELLING_TIME_DOWN, default=DEFAULT_TRAVEL_TIME
|
||||
): cv.positive_int,
|
||||
vol.Optional(
|
||||
CONF_TRAVELLING_TIME_UP, default=DEFAULT_TRAVEL_TIME
|
||||
): cv.positive_int,
|
||||
vol.Optional(CONF_INVERT_POSITION, default=False): cv.boolean,
|
||||
vol.Optional(CONF_INVERT_ANGLE, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class BinarySensorSchema:
|
||||
"""Voluptuous schema for KNX binary sensors."""
|
||||
|
||||
CONF_STATE_ADDRESS = CONF_STATE_ADDRESS
|
||||
CONF_SYNC_STATE = CONF_SYNC_STATE
|
||||
CONF_IGNORE_INTERNAL_STATE = "ignore_internal_state"
|
||||
CONF_AUTOMATION = "automation"
|
||||
CONF_HOOK = "hook"
|
||||
CONF_DEFAULT_HOOK = "on"
|
||||
CONF_COUNTER = "counter"
|
||||
CONF_DEFAULT_COUNTER = 1
|
||||
CONF_ACTION = "action"
|
||||
CONF_RESET_AFTER = "reset_after"
|
||||
|
||||
DEFAULT_NAME = "KNX Binary Sensor"
|
||||
AUTOMATION_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_HOOK, default=CONF_DEFAULT_HOOK): cv.string,
|
||||
vol.Optional(CONF_COUNTER, default=CONF_DEFAULT_COUNTER): cv.port,
|
||||
vol.Required(CONF_ACTION): cv.SCRIPT_SCHEMA,
|
||||
}
|
||||
)
|
||||
|
||||
AUTOMATIONS_SCHEMA = vol.All(cv.ensure_list, [AUTOMATION_SCHEMA])
|
||||
|
||||
SCHEMA = vol.All(
|
||||
cv.deprecated("significant_bit"),
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_SYNC_STATE, default=True): vol.Any(
|
||||
vol.All(vol.Coerce(int), vol.Range(min=2, max=1440)),
|
||||
cv.boolean,
|
||||
cv.string,
|
||||
),
|
||||
vol.Optional(CONF_IGNORE_INTERNAL_STATE, default=False): cv.boolean,
|
||||
vol.Required(CONF_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_DEVICE_CLASS): cv.string,
|
||||
vol.Optional(CONF_RESET_AFTER): cv.positive_int,
|
||||
vol.Optional(CONF_AUTOMATION): AUTOMATIONS_SCHEMA,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class LightSchema:
|
||||
"""Voluptuous schema for KNX lights."""
|
||||
|
||||
CONF_STATE_ADDRESS = CONF_STATE_ADDRESS
|
||||
CONF_BRIGHTNESS_ADDRESS = "brightness_address"
|
||||
CONF_BRIGHTNESS_STATE_ADDRESS = "brightness_state_address"
|
||||
CONF_COLOR_ADDRESS = "color_address"
|
||||
CONF_COLOR_STATE_ADDRESS = "color_state_address"
|
||||
CONF_COLOR_TEMP_ADDRESS = "color_temperature_address"
|
||||
CONF_COLOR_TEMP_STATE_ADDRESS = "color_temperature_state_address"
|
||||
CONF_COLOR_TEMP_MODE = "color_temperature_mode"
|
||||
CONF_RGBW_ADDRESS = "rgbw_address"
|
||||
CONF_RGBW_STATE_ADDRESS = "rgbw_state_address"
|
||||
CONF_MIN_KELVIN = "min_kelvin"
|
||||
CONF_MAX_KELVIN = "max_kelvin"
|
||||
|
||||
DEFAULT_NAME = "KNX Light"
|
||||
DEFAULT_COLOR_TEMP_MODE = "absolute"
|
||||
DEFAULT_MIN_KELVIN = 2700 # 370 mireds
|
||||
DEFAULT_MAX_KELVIN = 6000 # 166 mireds
|
||||
|
||||
SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_BRIGHTNESS_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_BRIGHTNESS_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_COLOR_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_COLOR_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_COLOR_TEMP_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_COLOR_TEMP_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(
|
||||
CONF_COLOR_TEMP_MODE, default=DEFAULT_COLOR_TEMP_MODE
|
||||
): cv.enum(ColorTempModes),
|
||||
vol.Optional(CONF_RGBW_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_RGBW_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_MIN_KELVIN, default=DEFAULT_MIN_KELVIN): vol.All(
|
||||
vol.Coerce(int), vol.Range(min=1)
|
||||
),
|
||||
vol.Optional(CONF_MAX_KELVIN, default=DEFAULT_MAX_KELVIN): vol.All(
|
||||
vol.Coerce(int), vol.Range(min=1)
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class ClimateSchema:
|
||||
"""Voluptuous schema for KNX climate devices."""
|
||||
|
||||
CONF_SETPOINT_SHIFT_ADDRESS = "setpoint_shift_address"
|
||||
CONF_SETPOINT_SHIFT_STATE_ADDRESS = "setpoint_shift_state_address"
|
||||
CONF_SETPOINT_SHIFT_MODE = "setpoint_shift_mode"
|
||||
CONF_SETPOINT_SHIFT_MAX = "setpoint_shift_max"
|
||||
CONF_SETPOINT_SHIFT_MIN = "setpoint_shift_min"
|
||||
CONF_TEMPERATURE_ADDRESS = "temperature_address"
|
||||
CONF_TEMPERATURE_STEP = "temperature_step"
|
||||
CONF_TARGET_TEMPERATURE_ADDRESS = "target_temperature_address"
|
||||
CONF_TARGET_TEMPERATURE_STATE_ADDRESS = "target_temperature_state_address"
|
||||
CONF_OPERATION_MODE_ADDRESS = "operation_mode_address"
|
||||
CONF_OPERATION_MODE_STATE_ADDRESS = "operation_mode_state_address"
|
||||
CONF_CONTROLLER_STATUS_ADDRESS = "controller_status_address"
|
||||
CONF_CONTROLLER_STATUS_STATE_ADDRESS = "controller_status_state_address"
|
||||
CONF_CONTROLLER_MODE_ADDRESS = "controller_mode_address"
|
||||
CONF_CONTROLLER_MODE_STATE_ADDRESS = "controller_mode_state_address"
|
||||
CONF_HEAT_COOL_ADDRESS = "heat_cool_address"
|
||||
CONF_HEAT_COOL_STATE_ADDRESS = "heat_cool_state_address"
|
||||
CONF_OPERATION_MODE_FROST_PROTECTION_ADDRESS = (
|
||||
"operation_mode_frost_protection_address"
|
||||
)
|
||||
CONF_OPERATION_MODE_NIGHT_ADDRESS = "operation_mode_night_address"
|
||||
CONF_OPERATION_MODE_COMFORT_ADDRESS = "operation_mode_comfort_address"
|
||||
CONF_OPERATION_MODE_STANDBY_ADDRESS = "operation_mode_standby_address"
|
||||
CONF_OPERATION_MODES = "operation_modes"
|
||||
CONF_ON_OFF_ADDRESS = "on_off_address"
|
||||
CONF_ON_OFF_STATE_ADDRESS = "on_off_state_address"
|
||||
CONF_ON_OFF_INVERT = "on_off_invert"
|
||||
CONF_MIN_TEMP = "min_temp"
|
||||
CONF_MAX_TEMP = "max_temp"
|
||||
|
||||
DEFAULT_NAME = "KNX Climate"
|
||||
DEFAULT_SETPOINT_SHIFT_MODE = "DPT6010"
|
||||
DEFAULT_SETPOINT_SHIFT_MAX = 6
|
||||
DEFAULT_SETPOINT_SHIFT_MIN = -6
|
||||
DEFAULT_TEMPERATURE_STEP = 0.1
|
||||
DEFAULT_ON_OFF_INVERT = False
|
||||
|
||||
SCHEMA = vol.All(
|
||||
cv.deprecated("setpoint_shift_step", replacement_key=CONF_TEMPERATURE_STEP),
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(
|
||||
CONF_SETPOINT_SHIFT_MODE, default=DEFAULT_SETPOINT_SHIFT_MODE
|
||||
): cv.enum(SetpointShiftMode),
|
||||
vol.Optional(
|
||||
CONF_SETPOINT_SHIFT_MAX, default=DEFAULT_SETPOINT_SHIFT_MAX
|
||||
): vol.All(int, vol.Range(min=0, max=32)),
|
||||
vol.Optional(
|
||||
CONF_SETPOINT_SHIFT_MIN, default=DEFAULT_SETPOINT_SHIFT_MIN
|
||||
): vol.All(int, vol.Range(min=-32, max=0)),
|
||||
vol.Optional(
|
||||
CONF_TEMPERATURE_STEP, default=DEFAULT_TEMPERATURE_STEP
|
||||
): vol.All(float, vol.Range(min=0, max=2)),
|
||||
vol.Required(CONF_TEMPERATURE_ADDRESS): cv.string,
|
||||
vol.Required(CONF_TARGET_TEMPERATURE_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_TARGET_TEMPERATURE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_SETPOINT_SHIFT_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_SETPOINT_SHIFT_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_OPERATION_MODE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_OPERATION_MODE_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_CONTROLLER_STATUS_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_CONTROLLER_STATUS_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_CONTROLLER_MODE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_CONTROLLER_MODE_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_HEAT_COOL_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_HEAT_COOL_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_OPERATION_MODE_FROST_PROTECTION_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_OPERATION_MODE_NIGHT_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_OPERATION_MODE_COMFORT_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_OPERATION_MODE_STANDBY_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_ON_OFF_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_ON_OFF_STATE_ADDRESS): cv.string,
|
||||
vol.Optional(
|
||||
CONF_ON_OFF_INVERT, default=DEFAULT_ON_OFF_INVERT
|
||||
): cv.boolean,
|
||||
vol.Optional(CONF_OPERATION_MODES): vol.All(
|
||||
cv.ensure_list, [vol.In({**OPERATION_MODES, **PRESET_MODES})]
|
||||
),
|
||||
vol.Optional(CONF_MIN_TEMP): vol.Coerce(float),
|
||||
vol.Optional(CONF_MAX_TEMP): vol.Coerce(float),
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class SwitchSchema:
|
||||
"""Voluptuous schema for KNX switches."""
|
||||
|
||||
CONF_STATE_ADDRESS = CONF_STATE_ADDRESS
|
||||
|
||||
DEFAULT_NAME = "KNX Switch"
|
||||
SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_STATE_ADDRESS): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class ExposeSchema:
|
||||
"""Voluptuous schema for KNX exposures."""
|
||||
|
||||
CONF_KNX_EXPOSE_TYPE = CONF_TYPE
|
||||
CONF_KNX_EXPOSE_ATTRIBUTE = "attribute"
|
||||
CONF_KNX_EXPOSE_DEFAULT = "default"
|
||||
CONF_KNX_EXPOSE_ADDRESS = CONF_ADDRESS
|
||||
|
||||
SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_KNX_EXPOSE_TYPE): vol.Any(int, float, str),
|
||||
vol.Optional(CONF_ENTITY_ID): cv.entity_id,
|
||||
vol.Optional(CONF_KNX_EXPOSE_ATTRIBUTE): cv.string,
|
||||
vol.Optional(CONF_KNX_EXPOSE_DEFAULT): cv.match_all,
|
||||
vol.Required(CONF_KNX_EXPOSE_ADDRESS): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class NotifySchema:
|
||||
"""Voluptuous schema for KNX notifications."""
|
||||
|
||||
DEFAULT_NAME = "KNX Notify"
|
||||
|
||||
SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class SensorSchema:
|
||||
"""Voluptuous schema for KNX sensors."""
|
||||
|
||||
CONF_STATE_ADDRESS = CONF_STATE_ADDRESS
|
||||
CONF_SYNC_STATE = CONF_SYNC_STATE
|
||||
DEFAULT_NAME = "KNX Sensor"
|
||||
|
||||
SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_SYNC_STATE, default=True): vol.Any(
|
||||
vol.All(vol.Coerce(int), vol.Range(min=2, max=1440)),
|
||||
cv.boolean,
|
||||
cv.string,
|
||||
),
|
||||
vol.Required(CONF_STATE_ADDRESS): cv.string,
|
||||
vol.Required(CONF_TYPE): vol.Any(int, float, str),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class SceneSchema:
|
||||
"""Voluptuous schema for KNX scenes."""
|
||||
|
||||
CONF_SCENE_NUMBER = "scene_number"
|
||||
|
||||
DEFAULT_NAME = "KNX SCENE"
|
||||
SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Required(CONF_ADDRESS): cv.string,
|
||||
vol.Required(CONF_SCENE_NUMBER): cv.positive_int,
|
||||
}
|
||||
)
|
@ -1,35 +1,16 @@
|
||||
"""Support for KNX/IP sensors."""
|
||||
import voluptuous as vol
|
||||
from xknx.devices import Sensor as XknxSensor
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||
from homeassistant.const import CONF_NAME, CONF_TYPE
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.entity import Entity
|
||||
|
||||
from . import ATTR_DISCOVER_DEVICES, DATA_KNX
|
||||
|
||||
CONF_STATE_ADDRESS = "state_address"
|
||||
CONF_SYNC_STATE = "sync_state"
|
||||
DEFAULT_NAME = "KNX Sensor"
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_SYNC_STATE, default=True): cv.boolean,
|
||||
vol.Required(CONF_STATE_ADDRESS): cv.string,
|
||||
vol.Required(CONF_TYPE): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up sensor(s) for KNX platform."""
|
||||
if discovery_info is not None:
|
||||
async_add_entities_discovery(hass, discovery_info, async_add_entities)
|
||||
else:
|
||||
async_add_entities_config(hass, config, async_add_entities)
|
||||
|
||||
|
||||
@callback
|
||||
@ -42,24 +23,10 @@ def async_add_entities_discovery(hass, discovery_info, async_add_entities):
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
@callback
|
||||
def async_add_entities_config(hass, config, async_add_entities):
|
||||
"""Set up sensor for KNX platform configured within platform."""
|
||||
sensor = XknxSensor(
|
||||
hass.data[DATA_KNX].xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address_state=config[CONF_STATE_ADDRESS],
|
||||
sync_state=config[CONF_SYNC_STATE],
|
||||
value_type=config[CONF_TYPE],
|
||||
)
|
||||
hass.data[DATA_KNX].xknx.devices.add(sensor)
|
||||
async_add_entities([KNXSensor(sensor)])
|
||||
|
||||
|
||||
class KNXSensor(Entity):
|
||||
"""Representation of a KNX sensor."""
|
||||
|
||||
def __init__(self, device):
|
||||
def __init__(self, device: XknxSensor):
|
||||
"""Initialize of a KNX sensor."""
|
||||
self.device = device
|
||||
|
||||
|
@ -7,3 +7,6 @@ send:
|
||||
payload:
|
||||
description: "Payload to send to the bus. Integers are treated as DPT 1/2/3 payloads. For DPTs > 6 bits send a list. Each value represents 1 octet (0-255). Pad with 0 to DPT byte length."
|
||||
example: "[0, 4]"
|
||||
type:
|
||||
description: "Optional. If set, the payload will not be sent as raw bytes, but encoded as given DPT. Knx sensor types are valid values (see https://www.home-assistant.io/integrations/sensor.knx)."
|
||||
example: "temperature"
|
||||
|
@ -1,32 +1,16 @@
|
||||
"""Support for KNX/IP switches."""
|
||||
import voluptuous as vol
|
||||
from xknx.devices import Switch as XknxSwitch
|
||||
|
||||
from homeassistant.components.switch import PLATFORM_SCHEMA, SwitchEntity
|
||||
from homeassistant.const import CONF_ADDRESS, CONF_NAME
|
||||
from homeassistant.components.switch import SwitchEntity
|
||||
from homeassistant.core import callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from . import ATTR_DISCOVER_DEVICES, DATA_KNX
|
||||
|
||||
CONF_STATE_ADDRESS = "state_address"
|
||||
|
||||
DEFAULT_NAME = "KNX Switch"
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_ADDRESS): cv.string,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_STATE_ADDRESS): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up switch(es) for KNX platform."""
|
||||
if discovery_info is not None:
|
||||
async_add_entities_discovery(hass, discovery_info, async_add_entities)
|
||||
else:
|
||||
async_add_entities_config(hass, config, async_add_entities)
|
||||
|
||||
|
||||
@callback
|
||||
@ -39,23 +23,10 @@ def async_add_entities_discovery(hass, discovery_info, async_add_entities):
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
@callback
|
||||
def async_add_entities_config(hass, config, async_add_entities):
|
||||
"""Set up switch for KNX platform configured within platform."""
|
||||
switch = XknxSwitch(
|
||||
hass.data[DATA_KNX].xknx,
|
||||
name=config[CONF_NAME],
|
||||
group_address=config[CONF_ADDRESS],
|
||||
group_address_state=config.get(CONF_STATE_ADDRESS),
|
||||
)
|
||||
hass.data[DATA_KNX].xknx.devices.add(switch)
|
||||
async_add_entities([KNXSwitch(switch)])
|
||||
|
||||
|
||||
class KNXSwitch(SwitchEntity):
|
||||
"""Representation of a KNX switch."""
|
||||
|
||||
def __init__(self, device):
|
||||
def __init__(self, device: XknxSwitch):
|
||||
"""Initialize of KNX switch."""
|
||||
self.device = device
|
||||
|
||||
|
@ -2252,7 +2252,7 @@ xboxapi==2.0.1
|
||||
xfinity-gateway==0.0.4
|
||||
|
||||
# homeassistant.components.knx
|
||||
xknx==0.11.3
|
||||
xknx==0.12.0
|
||||
|
||||
# homeassistant.components.bluesound
|
||||
# homeassistant.components.rest
|
||||
|
Loading…
x
Reference in New Issue
Block a user