core/homeassistant/components/insteon/api/properties.py

282 lines
8.9 KiB
Python
Raw Normal View History

2021-07-22 18:11:36 +00:00
"""Property update methods and schemas."""
from pyinsteon import devices
from pyinsteon.config import RADIO_BUTTON_GROUPS, RAMP_RATE_IN_SEC, get_usable_value
from pyinsteon.constants import (
RAMP_RATES_SEC,
PropertyType,
RelayMode,
ResponseStatus,
ToggleMode,
2021-07-22 18:11:36 +00:00
)
from pyinsteon.device_types.device_base import Device
2021-07-22 18:11:36 +00:00
import voluptuous as vol
import voluptuous_serialize
from homeassistant.components import websocket_api
from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv
from ..const import (
DEVICE_ADDRESS,
ID,
INSTEON_DEVICE_NOT_FOUND,
PROPERTY_NAME,
PROPERTY_VALUE,
TYPE,
)
from .device import notify_device_not_found
SHOW_ADVANCED = "show_advanced"
RAMP_RATE_SECONDS = list(dict.fromkeys(RAMP_RATES_SEC))
2021-07-22 18:11:36 +00:00
RAMP_RATE_SECONDS.sort()
RAMP_RATE_LIST = [str(seconds) for seconds in RAMP_RATE_SECONDS]
TOGGLE_MODES = [str(ToggleMode(v)).lower() for v in list(ToggleMode)]
RELAY_MODES = [str(RelayMode(v)).lower() for v in list(RelayMode)]
2021-07-22 18:11:36 +00:00
def _bool_schema(name):
return voluptuous_serialize.convert(vol.Schema({vol.Required(name): bool}))[0]
def _byte_schema(name):
return voluptuous_serialize.convert(vol.Schema({vol.Required(name): cv.byte}))[0]
def _float_schema(name):
return voluptuous_serialize.convert(vol.Schema({vol.Required(name): float}))[0]
def _list_schema(name, values):
2021-07-22 18:11:36 +00:00
return voluptuous_serialize.convert(
vol.Schema({vol.Required(name): vol.In(values)}),
2021-07-22 18:11:36 +00:00
custom_serializer=cv.custom_serializer,
)[0]
def _multi_select_schema(name, values):
return voluptuous_serialize.convert(
vol.Schema({vol.Optional(name): cv.multi_select(values)}),
custom_serializer=cv.custom_serializer,
)[0]
def _read_only_schema(name, value):
"""Return a constant value schema."""
return voluptuous_serialize.convert(vol.Schema({vol.Required(name): value}))[0]
def get_schema(prop, name, groups):
"""Return the correct shema type."""
if prop.is_read_only:
return _read_only_schema(name, prop.value)
if name == RAMP_RATE_IN_SEC:
return _list_schema(name, RAMP_RATE_LIST)
if name == RADIO_BUTTON_GROUPS:
button_list = {str(group): groups[group].name for group in groups if group != 1}
return _multi_select_schema(name, button_list)
if prop.value_type == bool:
return _bool_schema(name)
if prop.value_type == int:
return _byte_schema(name)
if prop.value_type == float:
return _float_schema(name)
if prop.value_type == ToggleMode:
return _list_schema(name, TOGGLE_MODES)
if prop.value_type == RelayMode:
return _list_schema(name, RELAY_MODES)
return None
def get_properties(device: Device, show_advanced=False):
2021-07-22 18:11:36 +00:00
"""Get the properties of an Insteon device and return the records and schema."""
properties = []
schema = {}
for name, prop in device.configuration.items():
if prop.is_read_only and not show_advanced:
2021-07-22 18:11:36 +00:00
continue
prop_schema = get_schema(prop, name, device.groups)
if name == "momentary_delay":
print(prop_schema)
if prop_schema is None:
continue
schema[name] = prop_schema
properties.append(property_to_dict(prop))
if show_advanced:
for name, prop in device.operating_flags.items():
if prop.property_type != PropertyType.ADVANCED:
continue
prop_schema = get_schema(prop, name, device.groups)
if prop_schema is not None:
schema[name] = prop_schema
properties.append(property_to_dict(prop))
for name, prop in device.properties.items():
if prop.property_type != PropertyType.ADVANCED:
continue
prop_schema = get_schema(prop, name, device.groups)
if prop_schema is not None:
schema[name] = prop_schema
properties.append(property_to_dict(prop))
2021-07-22 18:11:36 +00:00
return properties, schema
def property_to_dict(prop):
2021-07-22 18:11:36 +00:00
"""Return a property data row."""
value = get_usable_value(prop)
modified = value == prop.new_value
if prop.value_type in [ToggleMode, RelayMode] or prop.name == RAMP_RATE_IN_SEC:
value = str(value).lower()
2021-07-22 18:11:36 +00:00
prop_dict = {"name": prop.name, "value": value, "modified": modified}
return prop_dict
def update_property(device, prop_name, value):
"""Update the value of a device property."""
prop = device.configuration[prop_name]
if prop.value_type == ToggleMode:
toggle_mode = getattr(ToggleMode, value.upper())
prop.new_value = toggle_mode
elif prop.value_type == RelayMode:
relay_mode = getattr(RelayMode, value.upper())
prop.new_value = relay_mode
2021-07-22 18:11:36 +00:00
else:
prop.new_value = value
2021-07-22 18:11:36 +00:00
@websocket_api.websocket_command(
{
vol.Required(TYPE): "insteon/properties/get",
vol.Required(DEVICE_ADDRESS): str,
vol.Required(SHOW_ADVANCED): bool,
2021-07-22 18:11:36 +00:00
}
)
@websocket_api.require_admin
@websocket_api.async_response
async def websocket_get_properties(
hass: HomeAssistant,
connection: websocket_api.connection.ActiveConnection,
msg: dict,
) -> None:
"""Add the default All-Link Database records for an Insteon device."""
2021-10-18 16:36:35 +00:00
if not (device := devices[msg[DEVICE_ADDRESS]]):
2021-07-22 18:11:36 +00:00
notify_device_not_found(connection, msg, INSTEON_DEVICE_NOT_FOUND)
return
properties, schema = get_properties(device, msg[SHOW_ADVANCED])
2021-07-22 18:11:36 +00:00
connection.send_result(msg[ID], {"properties": properties, "schema": schema})
@websocket_api.websocket_command(
{
vol.Required(TYPE): "insteon/properties/change",
vol.Required(DEVICE_ADDRESS): str,
vol.Required(PROPERTY_NAME): str,
vol.Required(PROPERTY_VALUE): vol.Any(list, int, float, bool, str),
}
)
@websocket_api.require_admin
@websocket_api.async_response
async def websocket_change_properties_record(
hass: HomeAssistant,
connection: websocket_api.connection.ActiveConnection,
msg: dict,
) -> None:
"""Add the default All-Link Database records for an Insteon device."""
2021-10-18 16:36:35 +00:00
if not (device := devices[msg[DEVICE_ADDRESS]]):
2021-07-22 18:11:36 +00:00
notify_device_not_found(connection, msg, INSTEON_DEVICE_NOT_FOUND)
return
update_property(device, msg[PROPERTY_NAME], msg[PROPERTY_VALUE])
2021-07-22 18:11:36 +00:00
connection.send_result(msg[ID])
@websocket_api.websocket_command(
{
vol.Required(TYPE): "insteon/properties/write",
vol.Required(DEVICE_ADDRESS): str,
}
)
@websocket_api.require_admin
@websocket_api.async_response
async def websocket_write_properties(
hass: HomeAssistant,
connection: websocket_api.connection.ActiveConnection,
msg: dict,
) -> None:
"""Add the default All-Link Database records for an Insteon device."""
2021-10-18 16:36:35 +00:00
if not (device := devices[msg[DEVICE_ADDRESS]]):
2021-07-22 18:11:36 +00:00
notify_device_not_found(connection, msg, INSTEON_DEVICE_NOT_FOUND)
return
result = await device.async_write_config()
2021-07-22 18:11:36 +00:00
await devices.async_save(workdir=hass.config.config_dir)
if result != ResponseStatus.SUCCESS:
2021-07-22 18:11:36 +00:00
connection.send_message(
websocket_api.error_message(
msg[ID], "write_failed", "properties not written to device"
)
)
return
connection.send_result(msg[ID])
@websocket_api.websocket_command(
{
vol.Required(TYPE): "insteon/properties/load",
vol.Required(DEVICE_ADDRESS): str,
}
)
@websocket_api.require_admin
@websocket_api.async_response
async def websocket_load_properties(
hass: HomeAssistant,
connection: websocket_api.connection.ActiveConnection,
msg: dict,
) -> None:
"""Add the default All-Link Database records for an Insteon device."""
2021-10-18 16:36:35 +00:00
if not (device := devices[msg[DEVICE_ADDRESS]]):
2021-07-22 18:11:36 +00:00
notify_device_not_found(connection, msg, INSTEON_DEVICE_NOT_FOUND)
return
result, _ = await device.async_read_config(read_aldb=False)
2021-07-22 18:11:36 +00:00
await devices.async_save(workdir=hass.config.config_dir)
if result != ResponseStatus.SUCCESS:
2021-07-22 18:11:36 +00:00
connection.send_message(
websocket_api.error_message(
msg[ID], "load_failed", "properties not loaded from device"
)
)
return
connection.send_result(msg[ID])
@websocket_api.websocket_command(
{
vol.Required(TYPE): "insteon/properties/reset",
vol.Required(DEVICE_ADDRESS): str,
}
)
@websocket_api.require_admin
@websocket_api.async_response
async def websocket_reset_properties(
hass: HomeAssistant,
connection: websocket_api.connection.ActiveConnection,
msg: dict,
) -> None:
"""Add the default All-Link Database records for an Insteon device."""
2021-10-18 16:36:35 +00:00
if not (device := devices[msg[DEVICE_ADDRESS]]):
2021-07-22 18:11:36 +00:00
notify_device_not_found(connection, msg, INSTEON_DEVICE_NOT_FOUND)
return
for prop in device.operating_flags:
device.operating_flags[prop].new_value = None
for prop in device.properties:
device.properties[prop].new_value = None
connection.send_result(msg[ID])