"""Command line interface for Operation Flag and Extended Properites Management."""
from .. import devices
from ..config.device_flag import DeviceFlagBase
from ..constants import PropertyType, RelayMode, ResponseStatus
from .tools_base import ToolsBase
def _show_flag(flag: DeviceFlagBase, advanced: bool, hidden: bool):
if flag.property_type == PropertyType.STANDARD:
return True
if flag.property_type == PropertyType.ADVANCED and advanced:
return True
if flag.property_type == PropertyType.HIDDEN and hidden:
return True
return False
class ToolsConfig(ToolsBase):
"""Command line interface for Operation Flag and Extended Properites Management."""
async def do_read_config(self, address, log_stdout=None, background=False):
"""Read the operating flags and extended properties of a device.
Usage:
read_config
|all
"""
try:
addresses = await self._ensure_address(
address=address,
name="Address",
ask_value=not background,
log_stdout=log_stdout,
allow_all=True,
match_device=True,
)
if not addresses:
log_stdout("Address is required")
return
except ValueError:
log_stdout("Invalid device address or device not found")
return
for device_address in addresses:
device = devices[device_address]
if not device == devices.modem:
log_stdout(f"Reading configuration for: {device.address}")
if await device.async_read_op_flags() == ResponseStatus.SUCCESS:
log_stdout("Operating flags read")
else:
log_stdout("Operating flags read error")
if await device.async_read_ext_properties() == ResponseStatus.SUCCESS:
log_stdout("Extended properties read")
else:
log_stdout("Extended properties read error")
async def do_print_config(
self, address, advanced="n", hidden="n", log_stdout=None, background=False
):
"""Write the operating flags and extended properties to a device.
Usage:
read_ops_flags |all
advanced: y|n default 'n'
hidden: y|n default 'n'
"""
try:
addresses = await self._ensure_address(
address=address,
name="Address",
ask_value=not background,
log_stdout=log_stdout,
allow_all=False,
match_device=True,
)
if not addresses:
log_stdout("Address is required")
return
except ValueError:
log_stdout("Invalid device address or device not found")
return
advanced = await self._ensure_bool(
advanced, "Advanced", ask_value=True, log_stdout=log_stdout
)
hidden = await self._ensure_bool(
hidden, "Advanced", ask_value=True, log_stdout=log_stdout
)
for device_address in addresses:
device = devices[device_address]
if device and not device == devices.modem:
log_stdout("")
log_stdout(f"Device: {device_address}")
log_stdout("Property Value")
log_stdout("------------------------------ ----------")
for name in device.operating_flags:
op_flag = device.operating_flags[name]
if _show_flag(op_flag, advanced, hidden):
if op_flag.is_dirty:
name_out = f"{name}*"
else:
name_out = name
if op_flag.value_type == bool:
log_stdout(f"{name_out:30s} {bool(op_flag.value)}")
else:
prop_value = (
op_flag.value if op_flag.value is not None else 0
)
log_stdout(
f"{name_out:30s} 0x{prop_value:02x} ({prop_value})"
)
log_stdout("")
for name in device.properties:
prop = device.properties[name]
if _show_flag(prop, advanced, hidden):
if prop.is_dirty:
name_out = f"{name}*"
else:
name_out = name
if isinstance(prop.value, bool):
log_stdout(f"{name_out:30s} {prop.value}")
else:
prop_value = prop.value if prop.value is not None else 0
log_stdout(
f"{name_out:30s} 0x{prop_value:02x} ({prop_value:d})"
)
log_stdout("")
async def do_set_config_value(
self, address, name, value, log_stdout=None, background=False
):
"""Set an operating flag or extended property for a device.
Usage:
set_config_value flag y|n
"""
try:
addresses = await self._ensure_address(
address=address,
name="Address",
ask_value=not background,
log_stdout=log_stdout,
allow_all=False,
match_device=True,
)
if not addresses:
log_stdout("Address is required.")
return
address = addresses[0]
except ValueError:
log_stdout("Invalid device address or device not found")
return
device = devices[address]
prop_names = []
for prop_name in device.operating_flags:
prop_names.append(prop_name)
for prop_name in device.properties:
prop_names.append(prop_name)
if not prop_names:
log_stdout(f"Device {address} has no configurable settings.")
return
try:
name = await self._ensure_string(
name, prop_names, "Property name", not background, log_stdout
)
if name is None:
log_stdout("Property name is required.")
return
except ValueError:
log_stdout(f"Flag {name} not found in device {address}")
if device.operating_flags.get(name):
prop = device.operating_flags[name]
else:
prop = device.properties[name]
try:
if prop.value_type is bool:
value = await self._ensure_bool(
value, "Property value", not background, log_stdout
)
else:
value = await self._ensure_byte(
value, "Property value", not background, log_stdout
)
if value is None:
log_stdout("Property value is required.")
return
except ValueError:
log_stdout(f"Invalid value for {name}")
return
prop.new_value = value
log_stdout(f"Changed property {prop.name} value to {value}")
log_stdout("Use `write_config command to write changes to the device")
async def do_write_config(self, address, log_stdout=None, background=False):
"""Write the device config.
Usage:
write_config |all
"""
try:
addresses = await self._ensure_address(
address=address,
name="Address",
ask_value=not background,
log_stdout=log_stdout,
allow_all=True,
match_device=True,
)
if not addresses:
log_stdout("Address is required")
return
except ValueError:
log_stdout("Invalid device address or device not found")
return
for device_address in addresses:
device = devices[device_address]
if device != devices.modem:
if await device.async_write_op_flags() == ResponseStatus.SUCCESS:
log_stdout("Operating flags written")
else:
log_stdout("Operating flags write error")
if await device.async_write_ext_properties() == ResponseStatus.SUCCESS:
log_stdout("Extended properties written")
else:
log_stdout("Extended properties write error")
async def do_set_kpl_toggle_mode(
self, address, button, toggle_mode, log_stdout=None, background=False
):
"""Set the toggle mode of a KeypadLinc button.
Usage:
set_kpl_toggle_mode address button toggle_mode
address: Address of a KeypadLinc device
button: Button number to set (2-8 on an 8 button KPL, 3-6 on a 6 button KPL)
toggle_mode: Button toggle mode (0: toggle on/off, 1: On only, 2: Off only)
"""
try:
addresses = await self._ensure_address(
address=address,
name="Address",
ask_value=not background,
log_stdout=log_stdout,
allow_all=False,
match_device=True,
)
if not addresses:
log_stdout("Address is required")
return
address = addresses[0]
except ValueError:
log_stdout("Invalid device address or device not found")
return
device = devices[address]
if not hasattr(device, "async_set_toggle_mode"):
log_stdout("Device is not a KeypadLinc")
return
try:
button = await self._ensure_int(
button,
device.groups.keys(),
"Button",
not background,
log_stdout,
)
if button is None:
log_stdout("A button number is required")
return
except ValueError:
log_stdout("Invalid button number")
return
try:
toggle_mode = await self._ensure_int(
toggle_mode,
[0, 1, 2],
"Toggle mode",
not background,
log_stdout,
)
if toggle_mode is None:
log_stdout("A toggle mode is required")
return
except ValueError:
log_stdout("Invalid toggle mode")
return await device.async_set_toggle_mode(
button=button, toggle_mode=toggle_mode
)
async def do_set_kpl_radio_buttons(
self, address, button1, button2, *args, log_stdout=None, background=False
):
"""Set the toggle mode of a KeypadLinc button.
Usage:
set_kpl_radio_buttons address button1 button2 [button3 button4 button5 button6 button7]
address: Address of a KeypadLinc device
button: Button number to set (2-8 on an 8 button KPL, 3-6 on a 6 button KPL)
"""
try:
addresses = await self._ensure_address(
address=address,
name="Address",
ask_value=not background,
log_stdout=log_stdout,
allow_all=False,
match_device=True,
)
if not addresses:
log_stdout("Address is required")
return
address = addresses[0]
except ValueError:
log_stdout("Invalid device address or device not found")
return
device = devices[address]
if not hasattr(device, "async_set_radio_buttons"):
log_stdout("Device is not a KeypadLinc")
return
input_buttons = [button1, button2, *args]
ask_values = button2 is None and not background
buttons = []
for button in range(0, len(device.groups)):
try:
input_button = input_buttons[button]
except IndexError:
input_button = None
try:
button = await self._ensure_int(
value=input_button,
values=device.groups.keys(),
name="Button",
ask_value=ask_values,
log_stdout=log_stdout,
)
except ValueError:
if input_button is None and len(buttons) >= 2:
break
log_stdout("Invalid button number")
return
if button is None and len(buttons) < 2:
log_stdout("At least two buttons are required")
return
if button is None:
break
buttons.append(button)
await device.async_set_radio_buttons(buttons=buttons)
async def do_set_iolinc_mode(
self, address, latching_mode, log_stdout=None, background=False
):
"""Set the relay mode of an IO Linc device.
Usage:
set_iolinc_mode address latching_mode
address: IOLinc device address
latching_mode: Trigger or Momentary A, B or C
0 - Latching (Continuous)
1 - Momentary A
2 - Momentary B
3 - Momentary C
for more information see the IOLinc user manual.
"""
try:
addresses = await self._ensure_address(
address=address,
name="Address",
ask_value=not background,
log_stdout=log_stdout,
allow_all=False,
match_device=True,
)
if not addresses:
log_stdout("Address is required")
return
address = addresses[0]
except ValueError:
log_stdout("Invalid device address or device not found")
return
device = devices[address]
if not hasattr(device, "async_set_relay_mode"):
log_stdout("Device is not an IOLinc device")
return
try:
latching_mode = await self._ensure_int(
latching_mode, [0, 1, 2, 3], "Latching mode", not background, log_stdout
)
if latching_mode is None:
log_stdout("Value for latching mode is required")
return
except ValueError:
log_stdout("Invalid value for latching mode")
return
await device.async_set_relay_mode(latching_mode=RelayMode(latching_mode))
async def do_set_iolinc_delay(
self, address, seconds, log_stdout=None, background=False
):
"""Set the delay of an IO Linc device when in momentary mode.
Usage:
set_iolinc_delay address seconds
address: IOLinc device address
seconds: Integer number of seconds to delay before the relay is turned off
For more information see the IOLinc user manual.
"""
try:
addresses = await self._ensure_address(
address=address,
name="Address",
ask_value=not background,
log_stdout=log_stdout,
allow_all=False,
match_device=True,
)
if not addresses:
log_stdout("Address is required")
return
address = addresses[0]
except ValueError:
log_stdout("Invalid device address or device not found")
return
device = devices[address]
if not hasattr(device, "async_set_momentary_delay"):
log_stdout("Device is not an IOLinc device")
return
try:
seconds = await self._ensure_byte(
seconds, "Delay (seconds)", not background, log_stdout
)
if seconds is None:
log_stdout("Value for seconds is required")
except ValueError:
log_stdout("Invalid number for seconds")
return
await device.async_set_momentary_delay(seconds=seconds)
async def do_get_engine_version(self, address, log_stdout=None, background=False):
"""Get the engine version of the device.
Usage:
get_engine_version |all
"""
try:
addresses = await self._ensure_address(
address=address,
name="Address",
ask_value=not background,
log_stdout=log_stdout,
allow_all=True,
match_device=True,
)
if not addresses:
log_stdout("Address is required")
return
except ValueError:
log_stdout("Invalid device address or device not found")
return
for device_address in addresses:
device = devices[device_address]
if not device == devices.modem and device.cat != 0x03:
await device.async_get_engine_version()