2019-05-29 22:59:38 +00:00
|
|
|
"""Service calls related dependencies for LCN component."""
|
|
|
|
import pypck
|
|
|
|
import voluptuous as vol
|
|
|
|
|
|
|
|
from homeassistant.const import (
|
2019-07-31 19:25:30 +00:00
|
|
|
CONF_ADDRESS,
|
|
|
|
CONF_BRIGHTNESS,
|
|
|
|
CONF_STATE,
|
|
|
|
CONF_UNIT_OF_MEASUREMENT,
|
2020-02-23 20:09:24 +00:00
|
|
|
TIME_SECONDS,
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
2019-12-09 13:22:30 +00:00
|
|
|
import homeassistant.helpers.config_validation as cv
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
from .const import (
|
2019-07-31 19:25:30 +00:00
|
|
|
CONF_CONNECTIONS,
|
|
|
|
CONF_KEYS,
|
|
|
|
CONF_LED,
|
|
|
|
CONF_OUTPUT,
|
|
|
|
CONF_PCK,
|
|
|
|
CONF_RELVARREF,
|
|
|
|
CONF_ROW,
|
|
|
|
CONF_SETPOINT,
|
|
|
|
CONF_TABLE,
|
|
|
|
CONF_TEXT,
|
|
|
|
CONF_TIME,
|
|
|
|
CONF_TIME_UNIT,
|
|
|
|
CONF_TRANSITION,
|
|
|
|
CONF_VALUE,
|
|
|
|
CONF_VARIABLE,
|
|
|
|
DATA_LCN,
|
|
|
|
LED_PORTS,
|
|
|
|
LED_STATUS,
|
|
|
|
OUTPUT_PORTS,
|
|
|
|
RELVARREF,
|
|
|
|
SENDKEYCOMMANDS,
|
|
|
|
SETPOINTS,
|
|
|
|
THRESHOLDS,
|
|
|
|
TIME_UNITS,
|
|
|
|
VAR_UNITS,
|
|
|
|
VARIABLES,
|
|
|
|
)
|
2019-05-29 22:59:38 +00:00
|
|
|
from .helpers import (
|
2019-07-31 19:25:30 +00:00
|
|
|
get_connection,
|
|
|
|
is_address,
|
|
|
|
is_key_lock_states_string,
|
|
|
|
is_relays_states_string,
|
|
|
|
)
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
class LcnServiceCall:
|
2019-05-29 22:59:38 +00:00
|
|
|
"""Parent class for all LCN service calls."""
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
schema = vol.Schema({vol.Required(CONF_ADDRESS): is_address})
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
def __init__(self, hass):
|
|
|
|
"""Initialize service call."""
|
|
|
|
self.connections = hass.data[DATA_LCN][CONF_CONNECTIONS]
|
|
|
|
|
|
|
|
def get_address_connection(self, call):
|
|
|
|
"""Get address connection object."""
|
|
|
|
addr, connection_id = call.data[CONF_ADDRESS]
|
|
|
|
addr = pypck.lcn_addr.LcnAddr(*addr)
|
|
|
|
if connection_id is None:
|
|
|
|
connection = self.connections[0]
|
|
|
|
else:
|
|
|
|
connection = get_connection(self.connections, connection_id)
|
|
|
|
|
|
|
|
return connection.get_address_conn(addr)
|
|
|
|
|
|
|
|
|
|
|
|
class OutputAbs(LcnServiceCall):
|
|
|
|
"""Set absolute brightness of output port in percent."""
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
schema = LcnServiceCall.schema.extend(
|
|
|
|
{
|
|
|
|
vol.Required(CONF_OUTPUT): vol.All(vol.Upper, vol.In(OUTPUT_PORTS)),
|
|
|
|
vol.Required(CONF_BRIGHTNESS): vol.All(
|
|
|
|
vol.Coerce(int), vol.Range(min=0, max=100)
|
|
|
|
),
|
|
|
|
vol.Optional(CONF_TRANSITION, default=0): vol.All(
|
|
|
|
vol.Coerce(float), vol.Range(min=0.0, max=486.0)
|
|
|
|
),
|
|
|
|
}
|
|
|
|
)
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
def __call__(self, call):
|
|
|
|
"""Execute service call."""
|
|
|
|
output = pypck.lcn_defs.OutputPort[call.data[CONF_OUTPUT]]
|
|
|
|
brightness = call.data[CONF_BRIGHTNESS]
|
|
|
|
transition = pypck.lcn_defs.time_to_ramp_value(
|
2019-07-31 19:25:30 +00:00
|
|
|
call.data[CONF_TRANSITION] * 1000
|
|
|
|
)
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
address_connection = self.get_address_connection(call)
|
|
|
|
address_connection.dim_output(output.value, brightness, transition)
|
|
|
|
|
|
|
|
|
|
|
|
class OutputRel(LcnServiceCall):
|
|
|
|
"""Set relative brightness of output port in percent."""
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
schema = LcnServiceCall.schema.extend(
|
|
|
|
{
|
|
|
|
vol.Required(CONF_OUTPUT): vol.All(vol.Upper, vol.In(OUTPUT_PORTS)),
|
|
|
|
vol.Required(CONF_BRIGHTNESS): vol.All(
|
|
|
|
vol.Coerce(int), vol.Range(min=-100, max=100)
|
|
|
|
),
|
|
|
|
}
|
|
|
|
)
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
def __call__(self, call):
|
|
|
|
"""Execute service call."""
|
|
|
|
output = pypck.lcn_defs.OutputPort[call.data[CONF_OUTPUT]]
|
|
|
|
brightness = call.data[CONF_BRIGHTNESS]
|
|
|
|
|
|
|
|
address_connection = self.get_address_connection(call)
|
|
|
|
address_connection.rel_output(output.value, brightness)
|
|
|
|
|
|
|
|
|
|
|
|
class OutputToggle(LcnServiceCall):
|
|
|
|
"""Toggle output port."""
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
schema = LcnServiceCall.schema.extend(
|
|
|
|
{
|
|
|
|
vol.Required(CONF_OUTPUT): vol.All(vol.Upper, vol.In(OUTPUT_PORTS)),
|
|
|
|
vol.Optional(CONF_TRANSITION, default=0): vol.All(
|
|
|
|
vol.Coerce(float), vol.Range(min=0.0, max=486.0)
|
|
|
|
),
|
|
|
|
}
|
|
|
|
)
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
def __call__(self, call):
|
|
|
|
"""Execute service call."""
|
|
|
|
output = pypck.lcn_defs.OutputPort[call.data[CONF_OUTPUT]]
|
|
|
|
transition = pypck.lcn_defs.time_to_ramp_value(
|
2019-07-31 19:25:30 +00:00
|
|
|
call.data[CONF_TRANSITION] * 1000
|
|
|
|
)
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
address_connection = self.get_address_connection(call)
|
|
|
|
address_connection.toggle_output(output.value, transition)
|
|
|
|
|
|
|
|
|
|
|
|
class Relays(LcnServiceCall):
|
|
|
|
"""Set the relays status."""
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
schema = LcnServiceCall.schema.extend(
|
|
|
|
{vol.Required(CONF_STATE): is_relays_states_string}
|
|
|
|
)
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
def __call__(self, call):
|
|
|
|
"""Execute service call."""
|
2019-07-31 19:25:30 +00:00
|
|
|
states = [
|
|
|
|
pypck.lcn_defs.RelayStateModifier[state] for state in call.data[CONF_STATE]
|
|
|
|
]
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
address_connection = self.get_address_connection(call)
|
|
|
|
address_connection.control_relays(states)
|
|
|
|
|
|
|
|
|
|
|
|
class Led(LcnServiceCall):
|
|
|
|
"""Set the led state."""
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
schema = LcnServiceCall.schema.extend(
|
|
|
|
{
|
|
|
|
vol.Required(CONF_LED): vol.All(vol.Upper, vol.In(LED_PORTS)),
|
|
|
|
vol.Required(CONF_STATE): vol.All(vol.Upper, vol.In(LED_STATUS)),
|
|
|
|
}
|
|
|
|
)
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
def __call__(self, call):
|
|
|
|
"""Execute service call."""
|
|
|
|
led = pypck.lcn_defs.LedPort[call.data[CONF_LED]]
|
2019-07-31 19:25:30 +00:00
|
|
|
led_state = pypck.lcn_defs.LedStatus[call.data[CONF_STATE]]
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
address_connection = self.get_address_connection(call)
|
|
|
|
address_connection.control_led(led, led_state)
|
|
|
|
|
|
|
|
|
|
|
|
class VarAbs(LcnServiceCall):
|
|
|
|
"""Set absolute value of a variable or setpoint.
|
|
|
|
|
|
|
|
Variable has to be set as counter!
|
2020-01-31 16:33:00 +00:00
|
|
|
Regulator setpoints can also be set using R1VARSETPOINT, R2VARSETPOINT.
|
2019-05-29 22:59:38 +00:00
|
|
|
"""
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
schema = LcnServiceCall.schema.extend(
|
|
|
|
{
|
|
|
|
vol.Required(CONF_VARIABLE): vol.All(
|
|
|
|
vol.Upper, vol.In(VARIABLES + SETPOINTS)
|
|
|
|
),
|
|
|
|
vol.Optional(CONF_VALUE, default=0): vol.All(
|
|
|
|
vol.Coerce(int), vol.Range(min=0)
|
|
|
|
),
|
|
|
|
vol.Optional(CONF_UNIT_OF_MEASUREMENT, default="native"): vol.All(
|
|
|
|
vol.Upper, vol.In(VAR_UNITS)
|
|
|
|
),
|
|
|
|
}
|
|
|
|
)
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
def __call__(self, call):
|
|
|
|
"""Execute service call."""
|
|
|
|
var = pypck.lcn_defs.Var[call.data[CONF_VARIABLE]]
|
|
|
|
value = call.data[CONF_VALUE]
|
2019-07-31 19:25:30 +00:00
|
|
|
unit = pypck.lcn_defs.VarUnit.parse(call.data[CONF_UNIT_OF_MEASUREMENT])
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
address_connection = self.get_address_connection(call)
|
|
|
|
address_connection.var_abs(var, value, unit)
|
|
|
|
|
|
|
|
|
|
|
|
class VarReset(LcnServiceCall):
|
|
|
|
"""Reset value of variable or setpoint."""
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
schema = LcnServiceCall.schema.extend(
|
|
|
|
{vol.Required(CONF_VARIABLE): vol.All(vol.Upper, vol.In(VARIABLES + SETPOINTS))}
|
|
|
|
)
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
def __call__(self, call):
|
|
|
|
"""Execute service call."""
|
|
|
|
var = pypck.lcn_defs.Var[call.data[CONF_VARIABLE]]
|
|
|
|
|
|
|
|
address_connection = self.get_address_connection(call)
|
|
|
|
address_connection.var_reset(var)
|
|
|
|
|
|
|
|
|
|
|
|
class VarRel(LcnServiceCall):
|
|
|
|
"""Shift value of a variable, setpoint or threshold."""
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
schema = LcnServiceCall.schema.extend(
|
|
|
|
{
|
|
|
|
vol.Required(CONF_VARIABLE): vol.All(
|
|
|
|
vol.Upper, vol.In(VARIABLES + SETPOINTS + THRESHOLDS)
|
|
|
|
),
|
|
|
|
vol.Optional(CONF_VALUE, default=0): int,
|
|
|
|
vol.Optional(CONF_UNIT_OF_MEASUREMENT, default="native"): vol.All(
|
|
|
|
vol.Upper, vol.In(VAR_UNITS)
|
|
|
|
),
|
|
|
|
vol.Optional(CONF_RELVARREF, default="current"): vol.All(
|
|
|
|
vol.Upper, vol.In(RELVARREF)
|
|
|
|
),
|
|
|
|
}
|
|
|
|
)
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
def __call__(self, call):
|
|
|
|
"""Execute service call."""
|
|
|
|
var = pypck.lcn_defs.Var[call.data[CONF_VARIABLE]]
|
|
|
|
value = call.data[CONF_VALUE]
|
2019-07-31 19:25:30 +00:00
|
|
|
unit = pypck.lcn_defs.VarUnit.parse(call.data[CONF_UNIT_OF_MEASUREMENT])
|
|
|
|
value_ref = pypck.lcn_defs.RelVarRef[call.data[CONF_RELVARREF]]
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
address_connection = self.get_address_connection(call)
|
|
|
|
address_connection.var_rel(var, value, unit, value_ref)
|
|
|
|
|
|
|
|
|
|
|
|
class LockRegulator(LcnServiceCall):
|
|
|
|
"""Locks a regulator setpoint."""
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
schema = LcnServiceCall.schema.extend(
|
|
|
|
{
|
|
|
|
vol.Required(CONF_SETPOINT): vol.All(vol.Upper, vol.In(SETPOINTS)),
|
|
|
|
vol.Optional(CONF_STATE, default=False): bool,
|
|
|
|
}
|
|
|
|
)
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
def __call__(self, call):
|
|
|
|
"""Execute service call."""
|
|
|
|
setpoint = pypck.lcn_defs.Var[call.data[CONF_SETPOINT]]
|
|
|
|
state = call.data[CONF_STATE]
|
|
|
|
|
|
|
|
reg_id = pypck.lcn_defs.Var.to_set_point_id(setpoint)
|
|
|
|
address_connection = self.get_address_connection(call)
|
|
|
|
address_connection.lock_regulator(reg_id, state)
|
|
|
|
|
|
|
|
|
|
|
|
class SendKeys(LcnServiceCall):
|
|
|
|
"""Sends keys (which executes bound commands)."""
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
schema = LcnServiceCall.schema.extend(
|
|
|
|
{
|
|
|
|
vol.Required(CONF_KEYS): vol.All(
|
|
|
|
vol.Upper, cv.matches_regex(r"^([A-D][1-8])+$")
|
|
|
|
),
|
|
|
|
vol.Optional(CONF_STATE, default="hit"): vol.All(
|
|
|
|
vol.Upper, vol.In(SENDKEYCOMMANDS)
|
|
|
|
),
|
|
|
|
vol.Optional(CONF_TIME, default=0): vol.All(int, vol.Range(min=0)),
|
2020-02-23 20:09:24 +00:00
|
|
|
vol.Optional(CONF_TIME_UNIT, default=TIME_SECONDS): vol.All(
|
2019-07-31 19:25:30 +00:00
|
|
|
vol.Upper, vol.In(TIME_UNITS)
|
|
|
|
),
|
|
|
|
}
|
|
|
|
)
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
def __call__(self, call):
|
|
|
|
"""Execute service call."""
|
|
|
|
address_connection = self.get_address_connection(call)
|
|
|
|
|
|
|
|
keys = [[False] * 8 for i in range(4)]
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
key_strings = zip(call.data[CONF_KEYS][::2], call.data[CONF_KEYS][1::2])
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
for table, key in key_strings:
|
|
|
|
table_id = ord(table) - 65
|
|
|
|
key_id = int(key) - 1
|
|
|
|
keys[table_id][key_id] = True
|
|
|
|
|
|
|
|
delay_time = call.data[CONF_TIME]
|
|
|
|
if delay_time != 0:
|
|
|
|
hit = pypck.lcn_defs.SendKeyCommand.HIT
|
2019-07-31 19:25:30 +00:00
|
|
|
if pypck.lcn_defs.SendKeyCommand[call.data[CONF_STATE]] != hit:
|
|
|
|
raise ValueError(
|
2020-01-02 19:17:10 +00:00
|
|
|
"Only hit command is allowed when sending deferred keys."
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
|
|
|
delay_unit = pypck.lcn_defs.TimeUnit.parse(call.data[CONF_TIME_UNIT])
|
|
|
|
address_connection.send_keys_hit_deferred(keys, delay_time, delay_unit)
|
2019-05-29 22:59:38 +00:00
|
|
|
else:
|
2019-07-31 19:25:30 +00:00
|
|
|
state = pypck.lcn_defs.SendKeyCommand[call.data[CONF_STATE]]
|
2019-05-29 22:59:38 +00:00
|
|
|
address_connection.send_keys(keys, state)
|
|
|
|
|
|
|
|
|
|
|
|
class LockKeys(LcnServiceCall):
|
|
|
|
"""Lock keys."""
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
schema = LcnServiceCall.schema.extend(
|
|
|
|
{
|
|
|
|
vol.Optional(CONF_TABLE, default="a"): vol.All(
|
|
|
|
vol.Upper, cv.matches_regex(r"^[A-D]$")
|
|
|
|
),
|
|
|
|
vol.Required(CONF_STATE): is_key_lock_states_string,
|
|
|
|
vol.Optional(CONF_TIME, default=0): vol.All(int, vol.Range(min=0)),
|
2020-02-23 20:09:24 +00:00
|
|
|
vol.Optional(CONF_TIME_UNIT, default=TIME_SECONDS): vol.All(
|
2019-07-31 19:25:30 +00:00
|
|
|
vol.Upper, vol.In(TIME_UNITS)
|
|
|
|
),
|
|
|
|
}
|
|
|
|
)
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
def __call__(self, call):
|
|
|
|
"""Execute service call."""
|
|
|
|
address_connection = self.get_address_connection(call)
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
states = [
|
|
|
|
pypck.lcn_defs.KeyLockStateModifier[state]
|
|
|
|
for state in call.data[CONF_STATE]
|
|
|
|
]
|
2019-05-29 22:59:38 +00:00
|
|
|
table_id = ord(call.data[CONF_TABLE]) - 65
|
|
|
|
|
|
|
|
delay_time = call.data[CONF_TIME]
|
|
|
|
if delay_time != 0:
|
|
|
|
if table_id != 0:
|
2019-07-31 19:25:30 +00:00
|
|
|
raise ValueError(
|
2020-01-02 19:17:10 +00:00
|
|
|
"Only table A is allowed when locking keys for a specific time."
|
2019-07-31 19:25:30 +00:00
|
|
|
)
|
|
|
|
delay_unit = pypck.lcn_defs.TimeUnit.parse(call.data[CONF_TIME_UNIT])
|
|
|
|
address_connection.lock_keys_tab_a_temporary(delay_time, delay_unit, states)
|
2019-05-29 22:59:38 +00:00
|
|
|
else:
|
|
|
|
address_connection.lock_keys(table_id, states)
|
|
|
|
|
|
|
|
address_connection.request_status_locked_keys_timeout()
|
|
|
|
|
|
|
|
|
|
|
|
class DynText(LcnServiceCall):
|
|
|
|
"""Send dynamic text to LCN-GTxD displays."""
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
schema = LcnServiceCall.schema.extend(
|
|
|
|
{
|
|
|
|
vol.Required(CONF_ROW): vol.All(int, vol.Range(min=1, max=4)),
|
|
|
|
vol.Required(CONF_TEXT): vol.All(str, vol.Length(max=60)),
|
|
|
|
}
|
|
|
|
)
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
def __call__(self, call):
|
|
|
|
"""Execute service call."""
|
|
|
|
row_id = call.data[CONF_ROW] - 1
|
|
|
|
text = call.data[CONF_TEXT]
|
|
|
|
|
|
|
|
address_connection = self.get_address_connection(call)
|
|
|
|
address_connection.dyn_text(row_id, text)
|
|
|
|
|
|
|
|
|
|
|
|
class Pck(LcnServiceCall):
|
|
|
|
"""Send arbitrary PCK command."""
|
|
|
|
|
2019-07-31 19:25:30 +00:00
|
|
|
schema = LcnServiceCall.schema.extend({vol.Required(CONF_PCK): str})
|
2019-05-29 22:59:38 +00:00
|
|
|
|
|
|
|
def __call__(self, call):
|
|
|
|
"""Execute service call."""
|
|
|
|
pck = call.data[CONF_PCK]
|
|
|
|
address_connection = self.get_address_connection(call)
|
|
|
|
address_connection.pck(pck)
|