From fa688becc26666c34db523b42dfb5e0b235593c1 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 8 Dec 2021 12:13:57 -0800 Subject: [PATCH 1/6] Use Is_On for Buttons; Save LED Bits to Meta Fixes #456 Was using level instead of is_on, which was causing the led_bit variable to always be 0x00. New Feature: Led Bit value is saved across reboots, hopefully will cut down on the value getting out of sync. --- insteon_mqtt/device/KeypadLinc.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/insteon_mqtt/device/KeypadLinc.py b/insteon_mqtt/device/KeypadLinc.py index 81f1c71f..717daf8a 100644 --- a/insteon_mqtt/device/KeypadLinc.py +++ b/insteon_mqtt/device/KeypadLinc.py @@ -77,7 +77,9 @@ def __init__(self, protocol, modem, address, name=None, config_extra=None): # changing the led state - only toggling the load changes the state. # Since the non-load buttons have nothing to switch, the led state is # the state of the switch. - self._led_bits = 0x00 + self._led_bits = self.db.get_meta('led_bits') + if self._led_bits is None: + self._led_bits = 0x00 # 1 if the load is attached to the normal first button. If the load # is detached, this will be group 9. @@ -785,14 +787,8 @@ def handle_button_led(self, msg, on_done, group, is_on, led_bits, LOG.debug("KeypadLinc LED %s group %s ACK: %s", self.addr, group, msg) - # Update the LED bit for the updated group. - self._led_bits = led_bits - LOG.ui("KeypadLinc %s LED's changed to %s", self.addr, - "{:08b}".format(self._led_bits)) - # Change the level and emit the active signal. - self._set_state(group=group, level=0xff if is_on else 0x00, - reason=reason) + self._set_state(group=group, is_on=is_on, reason=reason) msg = "KeypadLinc %s LED group %s updated to %s" % \ (self.addr, group, is_on) @@ -854,6 +850,7 @@ def handle_refresh_led(self, msg): reason=reason) self._led_bits = led_bits + self.db.set_meta('led_bits', self._led_bits) #----------------------------------------------------------------------- def handle_refresh_state(self, msg, on_done): @@ -977,5 +974,8 @@ def _cache_state(self, group, is_on, level, reason): if group < 9: self._led_bits = util.bit_set(self._led_bits, group - 1, 1 if is_on else 0) + self.db.set_meta('led_bits', self._led_bits) + LOG.ui("KeypadLinc %s LEDs changed to %s", self.addr, + "{:08b}".format(self._led_bits)) #----------------------------------------------------------------------- From 758110d369cd6c590b301c9a0e821186daceab93 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 8 Dec 2021 15:08:41 -0800 Subject: [PATCH 2/6] Invert On_Only and Night_Only Bits on Motion Sensor On_Only and Night_Only are actually false on the device. Update tests to match. --- insteon_mqtt/device/Motion.py | 5 +++-- tests/device/test_MotionDev.py | 10 +++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/insteon_mqtt/device/Motion.py b/insteon_mqtt/device/Motion.py index 832948c0..4bed65c9 100644 --- a/insteon_mqtt/device/Motion.py +++ b/insteon_mqtt/device/Motion.py @@ -250,10 +250,11 @@ def _change_flags(self, flags, on_done=None): on_only = self.on_only # Generate the value of the combined flags. + # on_only and night_only are inverted value = 0 value = util.bit_set(value, 3, led_on) - value = util.bit_set(value, 2, night_only) - value = util.bit_set(value, 1, on_only) + value = util.bit_set(value, 2, False if night_only else True) + value = util.bit_set(value, 1, False if on_only else True) # Push the flags value to the device. data = bytes([ diff --git a/tests/device/test_MotionDev.py b/tests/device/test_MotionDev.py index a45d1a49..4646cb06 100644 --- a/tests/device/test_MotionDev.py +++ b/tests/device/test_MotionDev.py @@ -246,20 +246,20 @@ def on_done(*args): # already test extended flags above def test_change_flags_led_on(self, test_device): - test_device.led_on = 1 + test_device.led_on = 0 test_device.night_only = 1 test_device.on_only = 1 def on_done(*args): pass # Mark awake so messages get sent to protocol test_device.awake(on_done) - test_device._change_flags({'led_on':0}) + test_device._change_flags({'led_on':1}) # should see an ext flag request first assert len(test_device.protocol.sent) == 1 assert test_device.protocol.sent[0].msg.cmd1 == Msg.CmdType.EXTENDED_SET_GET assert test_device.protocol.sent[0].msg.cmd2 == 0x00 assert test_device.protocol.sent[0].msg.data[1] == 0x05 # Set flags - assert test_device.protocol.sent[0].msg.data[2] == 0x06 + assert test_device.protocol.sent[0].msg.data[2] == 0x08 def test_change_flags_night_only(self, test_device): test_device.led_on = 1 @@ -275,7 +275,7 @@ def on_done(*args): assert test_device.protocol.sent[0].msg.cmd1 == Msg.CmdType.EXTENDED_SET_GET assert test_device.protocol.sent[0].msg.cmd2 == 0x00 assert test_device.protocol.sent[0].msg.data[1] == 0x05 # Set flags - assert test_device.protocol.sent[0].msg.data[2] == 0x0A + assert test_device.protocol.sent[0].msg.data[2] == 0x0C def test_change_flags_on_only(self, test_device): test_device.led_on = 1 @@ -291,7 +291,7 @@ def on_done(*args): assert test_device.protocol.sent[0].msg.cmd1 == Msg.CmdType.EXTENDED_SET_GET assert test_device.protocol.sent[0].msg.cmd2 == 0x00 assert test_device.protocol.sent[0].msg.data[1] == 0x05 # Set flags - assert test_device.protocol.sent[0].msg.data[2] == 0x0C + assert test_device.protocol.sent[0].msg.data[2] == 0x0A def test_change_flags_bad(self, test_device, caplog): test_device.led_on = 1 From 3928971bb5e27dd78fe6a06f52b439de3c74c5f0 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 9 Dec 2021 08:30:45 -0800 Subject: [PATCH 3/6] Overhaul of Commandline Help Output * Removed excess matter where possible * Add description and pointer to command help * Extend Argparse module to Enable Grouping Sub-Parsers * Create groups for all commands to make them easier to find and read. * Reorder Commands in a logical fashion * Rewrite some help text * Add Description to all Commands so that command help contains details about the command. --- insteon_mqtt/cmd_line/argparse_ext.py | 57 +++++ insteon_mqtt/cmd_line/main.py | 331 +++++++++++++++++--------- 2 files changed, 282 insertions(+), 106 deletions(-) create mode 100644 insteon_mqtt/cmd_line/argparse_ext.py diff --git a/insteon_mqtt/cmd_line/argparse_ext.py b/insteon_mqtt/cmd_line/argparse_ext.py new file mode 100644 index 00000000..874bceeb --- /dev/null +++ b/insteon_mqtt/cmd_line/argparse_ext.py @@ -0,0 +1,57 @@ +#=========================================================================== +# +# Extend Argparse to Enable Sub-Parser Groups +# +# Based on this very old issue: https://bugs.python.org/issue9341 +# +# Adds the method `add_parser_group()` to the sub-parser class. +# This adds a group heading to the sub-parser list, just like the +# `add_argument_group()` method. +# +# NOTE: As noted on the issue page, this probably won't work with [parents]. +# see http://bugs.python.org/issue16807 +# +#=========================================================================== +# Pylint doesn't like us access protected items like this +#pylint:disable=protected-access,abstract-method +import argparse + + +class _SubParsersAction(argparse._SubParsersAction): + + class _PseudoGroup(argparse.Action): + + def __init__(self, container, title): + sup = super(_SubParsersAction._PseudoGroup, self) + sup.__init__(option_strings=[], dest=title) + self.container = container + self._choices_actions = [] + + def add_parser(self, name, **kwargs): + # add the parser to the main Action, but move the pseudo action + # in the group's own list + parser = self.container.add_parser(name, **kwargs) + choice_action = self.container._choices_actions.pop() + self._choices_actions.append(choice_action) + return parser + + def _get_subactions(self): + return self._choices_actions + + def add_parser_group(self, title): + # the formatter can handle recursive subgroups + grp = _SubParsersAction._PseudoGroup(self, title) + self._choices_actions.append(grp) + return grp + + def add_parser_group(self, title): + # + grp = _SubParsersAction._PseudoGroup(self, title) + self._choices_actions.append(grp) + return grp + + +class ArgumentParser(argparse.ArgumentParser): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.register('action', 'parsers', _SubParsersAction) diff --git a/insteon_mqtt/cmd_line/main.py b/insteon_mqtt/cmd_line/main.py index 80fe65da..dbc43d9f 100644 --- a/insteon_mqtt/cmd_line/main.py +++ b/insteon_mqtt/cmd_line/main.py @@ -3,8 +3,8 @@ # Command line parsing and main entry point. # #=========================================================================== -import argparse import sys +from . import argparse_ext from .. import config from . import device from . import modem @@ -16,17 +16,44 @@ def parse_args(args): """Input is command line arguments w/o arg[0] """ # pylint: disable=too-many-statements - p = argparse.ArgumentParser(prog="insteon-mqtt", - description="Insteon<->MQTT tool") + prog_description = """ This is a Python 3 package that communicates with + an Insteon PLM modem (USB and serial) or an Insteon Hub + and converts Insteon state changes to MQTT and MQTT + commands to Insteon commands. It allows an Insteon + network to be integrated into and controlled from + anything that can use MQTT. This package works well + with HomeAssistant and can be easily installed as an + addon using the HomeAssistant Supervisor.""" + p = argparse_ext.ArgumentParser(prog="insteon-mqtt", + description=prog_description) p.add_argument('-v', '--version', action='version', version='%(prog)s ' + __version__) p.add_argument("config", metavar="config.yaml", help="Configuration " "file to use.") - sub = p.add_subparsers(help="Command help") + sub = p.add_subparsers(title="commands", + description="See %(prog)s config.yaml -h" + " for specific command help.", + metavar="command") + + # Define sub-parser groups. The order they appear here will define the + # order they print on the screen + startgrp = sub.add_parser_group('\n Startup:') + initgrp = sub.add_parser_group('\n Initialize:') + commandgrp = sub.add_parser_group('\n Device Commands:') + infogrp = sub.add_parser_group('\n Get Information:') + configgrp = sub.add_parser_group('\n Configure:') + linkgrp = sub.add_parser_group('\n Link Management:') + systemgrp = sub.add_parser_group('\n System Wide:') + batterygrp = sub.add_parser_group('\n Battery Devices:') + advancedgrp = sub.add_parser_group('\n Advanced Commands:') + + # Define sub-parser commands. The order they appear here will define the + # order they appear within their respective groups. #--------------------------------------- # START command - sp = sub.add_parser("start", help="Start the Insteon<->MQTT server.") + sp = startgrp.add_parser("start", help="Start the Insteon<->MQTT server.", + description="Start the Insteon<->MQTT server.") sp.add_argument("-l", "--log", metavar="log_file", help="Logging file to use.") sp.add_argument("-ls", "--log-screen", action="store_true", @@ -36,20 +63,52 @@ def parse_args(args): "30=warn, 40=error, 50=critical") sp.set_defaults(func=start.start) + #--------------------------------------- + # modem.join_all command + sp = systemgrp.add_parser("join-all", help="Run 'join' command on all " + "devices.", + description="Run 'join' command on all devices.") + sp.add_argument("-q", "--quiet", action="store_true", + help="Don't print any command results to the screen.") + sp.set_defaults(func=modem.join_all) + + #--------------------------------------- + # modem.pair_all command + sp = systemgrp.add_parser("pair-all", help="Run 'pair' command on all " + "devices.", + description="Run 'pair' command on all devices.") + sp.add_argument("-q", "--quiet", action="store_true", + help="Don't print any command results to the screen.") + sp.set_defaults(func=modem.pair_all) + #--------------------------------------- # modem.refresh_all command - sp = sub.add_parser("refresh-all", help="Call refresh all on the devices " - "in the configuration.") + sp = systemgrp.add_parser("refresh-all", help="Run 'refresh' command on " + "all devices.", + description="Run 'refresh' command on all " + "devices.") sp.add_argument("-f", "--force", action="store_true", help="Force the modem/device database to be downloaded.") sp.add_argument("-q", "--quiet", action="store_true", help="Don't print any command results to the screen.") sp.set_defaults(func=modem.refresh_all) + # modem.get_engine_all command + sp = systemgrp.add_parser("get-engine-all", help="Run 'get-engine' " + "command on all devices.", + description="Run 'get-engine' command on all " + "devices.") + sp.add_argument("-q", "--quiet", action="store_true", + help="Don't print any command results to the screen.") + sp.set_defaults(func=modem.get_engine_all) + #--------------------------------------- # modem.sync_all command - sp = sub.add_parser("sync-all", help="Call sync on all the devices " - "in the configuration.") + sp = systemgrp.add_parser("sync-all", help="Run 'sync' command on all " + "devices.", + description="Run 'sync' command on all devices. " + "This will add or remove links on the device to " + "match those defined in the scenes.yaml file.") sp.add_argument("--run", action="store_true", default=False, help="Perform the actions altering the device db to bring " "it in sync. If not specified, will perform a dry-run " @@ -63,8 +122,13 @@ def parse_args(args): #--------------------------------------- # modem.import_scenes_all command - sp = sub.add_parser("import-scenes-all", help="Call import-scenes on all " - "the devices in the configuration.") + sp = systemgrp.add_parser("import-scenes-all", help="Run 'import-scenes' " + "command on all devices.", + description="Run 'import-scenes' command on all " + "devices. This will add all links defined on " + "the devices to the scenes.yaml file. Any link " + "defined in the scenes.yaml file that is not " + "present on the devices will be DELETED.") sp.add_argument("--run", action="store_true", default=False, help="Perform the actions altering the scenes config file " "to bring it in sync. If not specified, will perform a " @@ -73,50 +137,44 @@ def parse_args(args): help="Don't print any command results to the screen.") sp.set_defaults(func=modem.import_scenes_all) - # modem.get_engine_all command - sp = sub.add_parser("get-engine-all", help="Call get-engine on the " - "devices in the configuration.") - sp.add_argument("-q", "--quiet", action="store_true", - help="Don't print any command results to the screen.") - sp.set_defaults(func=modem.get_engine_all) - #--------------------------------------- - # modem.join_all command - sp = sub.add_parser("join-all", help="Call join all on the devices " - "in the configuration.") - sp.add_argument("-q", "--quiet", action="store_true", - help="Don't print any command results to the screen.") - sp.set_defaults(func=modem.join_all) - - #--------------------------------------- - # modem.pair_all command - sp = sub.add_parser("pair-all", help="Call pair all on the devices " - "in the configuration.") + # modem.get_devices command + sp = infogrp.add_parser("get-devices", help="Print a list of all the " + "devices that the program knows about.", + description="Print a list of all the devices " + "that the program knows about.") sp.add_argument("-q", "--quiet", action="store_true", help="Don't print any command results to the screen.") - sp.set_defaults(func=modem.pair_all) + sp.set_defaults(func=modem.get_devices) #--------------------------------------- - # modem.factory_reset command - sp = sub.add_parser("factory-reset", help="Perform a remote factory " - "reset. Currently only supported on the modem.") - sp.add_argument("-q", "--quiet", action="store_true", - help="Don't print any command results to the screen.") - sp.set_defaults(func=modem.factory_reset) + # device.print_db + sp = linkgrp.add_parser("print-db", help="Print the cache of the device " + "database.", + description="Print the cache of the device " + "database.") + sp.add_argument("address", help="Device address or name.") + sp.set_defaults(func=device.print_db) #--------------------------------------- - # modem.get_devices command - sp = sub.add_parser("get-devices", help="Return a list of all the devices " - "that the modem knows about.") + # device.refresh command + sp = linkgrp.add_parser("refresh", help="Refresh device state and link " + "database.", + description="Refresh device state and link " + "database.") + sp.add_argument("-f", "--force", action="store_true", + help="Force the device database to be downloaded.") sp.add_argument("-q", "--quiet", action="store_true", help="Don't print any command results to the screen.") - sp.set_defaults(func=modem.get_devices) + sp.add_argument("address", help="Device address or name.") + sp.set_defaults(func=device.refresh) #--------------------------------------- # device.linking command - sp = sub.add_parser("linking", help="Turn on device or modem linking. " - "This is the same as holding the modem set button " - "for 3 seconds.") + sp = linkgrp.add_parser("linking", help="Put device into linking mode.", + description="Put device into linking mode. This " + "is the same as holding the device " + "set button for 3 seconds.") sp.add_argument("-g", "--group", type=int, default=0x01, help="Group number to link with (1-255)") sp.add_argument("-q", "--quiet", action="store_true", @@ -126,33 +184,31 @@ def parse_args(args): #--------------------------------------- # device.join command - sp = sub.add_parser("join", help="Join the device to the modem. " - "Allows the modem to talk to the device.") + sp = initgrp.add_parser("join", help="Join a device to the modem.", + description="Join a device to the modem. " + "This adds a modem->device link, which " + "allows the modem to talk to the device. " + "This is generally the first thing you need " + "to do with a new device.") sp.add_argument("-q", "--quiet", action="store_true", help="Don't print any command results to the screen.") sp.add_argument("address", help="Device address or name.") sp.set_defaults(func=device.join) - #--------------------------------------- - # device.refresh command - sp = sub.add_parser("refresh", help="Refresh device/modem state and " - "all link database.") - sp.add_argument("-f", "--force", action="store_true", - help="Force the device database to be downloaded.") - sp.add_argument("-q", "--quiet", action="store_true", - help="Don't print any command results to the screen.") - sp.add_argument("address", help="Device address or name.") - sp.set_defaults(func=device.refresh) - #--------------------------------------- # device.get_flags command - sp = sub.add_parser("get-flags", help="Get device operating flags.") + sp = infogrp.add_parser("get-flags", help="Get device operating flags.", + description="Get the device operating flags.") sp.add_argument("address", help="Device address or name.") sp.set_defaults(func=device.get_flags) #--------------------------------------- # device.set_flags command - sp = sub.add_parser("set-flags", help="Set device operating flags.") + sp = configgrp.add_parser("set-flags", help="Set device operating flags.", + description="Set device operating flags. " + "Flags are set using FLAG=VALUE format. " + "Use an intentionally bad flag such as " + "BAD=FLAG to see a list of valid flags.") sp.add_argument("address", help="Device address or name.") sp.add_argument("flags", nargs="+", help="FLAG=VALUE to set. Valid " "flags names are device dependent. See the device " @@ -161,19 +217,25 @@ def parse_args(args): #--------------------------------------- # device.get_engine command - sp = sub.add_parser("get-engine", help="Get device engine version.") + sp = infogrp.add_parser("get-engine", help="Get device engine version.", + description="Get device engine version. " + "May solve communications problems with the " + "device.") sp.add_argument("address", help="Device address or name.") sp.set_defaults(func=device.get_engine) #--------------------------------------- # device.get_model command - sp = sub.add_parser("get-model", help="Get device model information.") + sp = infogrp.add_parser("get-model", help="Get device model information.", + description="Get device model information.") sp.add_argument("address", help="Device address or name.") sp.set_defaults(func=device.get_model) #--------------------------------------- # device.on command - sp = sub.add_parser("on", help="Turn a device on.") + sp = commandgrp.add_parser("on", help="Turn a device on.", + description="Turn a device on. Will not " + "trigger linked devices, see 'scene' for that.") sp.add_argument("-l", "--level", metavar="level", type=int, default=255, help="Level to use for dimmers (0-255)") sp.add_argument("-q", "--quiet", action="store_true", @@ -193,12 +255,14 @@ def parse_args(args): sp.set_defaults(func=device.on) #--------------------------------------- - # device.set command - sp = sub.add_parser("set", help="Turn a device to specific level.") + # device.off command + sp = commandgrp.add_parser("off", help="Turn a device off.", + description="Turn a device off. Will not " + "trigger linked devices, see 'scene' for that.") sp.add_argument("-q", "--quiet", action="store_true", help="Don't print any command results to the screen.") sp.add_argument("-g", "--group", type=int, default=0x01, - help="Group (button) number to set for multi-button " + help="Group (button) number to turn off for multi-button " "devices.") sp.add_argument("-r", "--reason", metavar="reason", type=str, default="", help="Reason message to send with the command. No " @@ -209,17 +273,17 @@ def parse_args(args): gp.add_argument("-f", "--fast", dest="mode", action="store_const", const="fast", help="Send an Insteon fast on command.") sp.add_argument("address", help="Device address or name.") - sp.add_argument("level", type=int, default=255, - help="Level to use for dimmers (0-255)") - sp.set_defaults(func=device.set) + sp.set_defaults(func=device.off) #--------------------------------------- - # device.off command - sp = sub.add_parser("off", help="Turn a device off.") + # device.set command + sp = commandgrp.add_parser("set", help="Turn a device to specific level.", + description="Turn a device to specific level. " + "Will not trigger linked devices.") sp.add_argument("-q", "--quiet", action="store_true", help="Don't print any command results to the screen.") sp.add_argument("-g", "--group", type=int, default=0x01, - help="Group (button) number to turn off for multi-button " + help="Group (button) number to set for multi-button " "devices.") sp.add_argument("-r", "--reason", metavar="reason", type=str, default="", help="Reason message to send with the command. No " @@ -230,11 +294,15 @@ def parse_args(args): gp.add_argument("-f", "--fast", dest="mode", action="store_const", const="fast", help="Send an Insteon fast on command.") sp.add_argument("address", help="Device address or name.") - sp.set_defaults(func=device.off) + sp.add_argument("level", type=int, default=255, + help="Level to use for dimmers (0-255)") + sp.set_defaults(func=device.set) #--------------------------------------- # device.increment_up command - sp = sub.add_parser("up", help="Increments a dimmer up.") + sp = commandgrp.add_parser("up", help="Increments a dimmer up.", + description="Increments a dimmer up in 1/32 " + "steps. Will not trigger linked devices.") sp.add_argument("-q", "--quiet", action="store_true", help="Don't print any command results to the screen.") sp.add_argument("-r", "--reason", metavar="reason", type=str, default="", @@ -244,8 +312,10 @@ def parse_args(args): sp.set_defaults(func=device.increment_up) #--------------------------------------- - # device.increment_up command - sp = sub.add_parser("down", help="Decrements a dimmer up.") + # device.increment_down command + sp = commandgrp.add_parser("down", help="Decrements a dimmer down.", + description="Increments a dimmer down in 1/32 " + "steps. Will not trigger linked devices.") sp.add_argument("-q", "--quiet", action="store_true", help="Don't print any command results to the screen.") sp.add_argument("-r", "--reason", metavar="reason", type=str, default="", @@ -256,7 +326,11 @@ def parse_args(args): #--------------------------------------- # device.scene command - sp = sub.add_parser("scene", help="Simulate a scene command.") + sp = commandgrp.add_parser("scene", help="Simulate a scene command.", + description="Simulate a scene command. " + "This triggers the device to react as though " + "its button was pressed, causing all linked " + "devices to react as well.") sp.add_argument("-q", "--quiet", action="store_true", help="Don't print any command results to the screen.") sp.add_argument("-r", "--reason", metavar="reason", type=str, default="", @@ -278,7 +352,13 @@ def parse_args(args): #--------------------------------------- # device.pair command - sp = sub.add_parser("pair", help="Pair a device with the modem.") + sp = initgrp.add_parser("pair", help="Pair a device with the modem.", + description="Pair a device to the modem. " + "This adds all the necessary device->modem links, " + "so that the modem will be informed of changes on " + "the device. " + "This is generally the second thing you need " + "to do with a new device.") sp.add_argument("-q", "--quiet", action="store_true", help="Don't print any command results to the screen.") sp.add_argument("address", help="Device address or name.") @@ -286,11 +366,12 @@ def parse_args(args): #--------------------------------------- # device.db_add add ctrl/rspdr command - sp = sub.add_parser("db-add", help="Add the device/modem as the " - "controller or responder of another device. The " - "addr1 input sets the device to modify. Also adds " - "the corresponding entry on the linked device unless " - "--one-way is set.") + sp = advancedgrp.add_parser("db-add", help="Add a link.", + description="Add the device/modem as the " + "controller or responder of another device. " + "The addr1 input sets the device to modify. " + "Also adds the corresponding entry on the " + "linked device unless --one-way is set.") sp.add_argument("-o", "--one-way", action="store_true", help="Only add the entry on address1. Otherwise the " "corresponding entry on address2 is also added.") @@ -322,10 +403,12 @@ def parse_args(args): #--------------------------------------- # device.db_del delete ctrl/rspdr command - sp = sub.add_parser("db-delete", help="Delete an entry in the device/" - "modem's all link database with the input address, " - "group, and mode. Also deletes the corresponding " - "entry on the linked device unless --one-way is set.") + sp = advancedgrp.add_parser("db-delete", help="Delete a link.", + description="Delete an entry in the device/" + "modem's all link database with the input " + "address, group, and mode. Also deletes the " + "corresponding entry on the linked device " + "unless --one-way is set.") sp.add_argument("-o", "--one-way", action="store_true", help="Only delete the modem entries. Otherwise the " "corresponding entry on the device is also removed.") @@ -346,8 +429,10 @@ def parse_args(args): #--------------------------------------- # device.set_button_led - sp = sub.add_parser("set-button-led", help="Set the button LED state for " - "a KeyPadLinc.") + sp = configgrp.add_parser("set-button-led", help="Set the button LED " + "state for a KeyPadLinc.", + description="Set the button LED state for a " + "KeyPadLinc.") sp.add_argument("-q", "--quiet", action="store_true", help="Don't print any command results to the screen.") sp.add_argument("address", help="Device address or name.") @@ -356,15 +441,13 @@ def parse_args(args): help="1 to turn the LED on, 0 to turn it off.") sp.set_defaults(func=device.set_button_led) - #--------------------------------------- - # device.print_db - sp = sub.add_parser("print-db", help="Print the current device database") - sp.add_argument("address", help="Device address or name.") - sp.set_defaults(func=device.print_db) - #--------------------------------------- # device.sync - sp = sub.add_parser("sync", help="Sync the defined scenes with device db") + sp = linkgrp.add_parser("sync", help="Sync the scenes defined in " + "scenes.yaml with device.", + description="This will add or remove links on the " + "device to match those defined in the " + "scenes.yaml file.") sp.add_argument("address", help="Device address or name.") sp.add_argument("--run", action="store_true", default=False, help="Perform the actions altering the device db to bring " @@ -379,8 +462,14 @@ def parse_args(args): #--------------------------------------- # device.import_scenes - sp = sub.add_parser("import-scenes", help="Import all of the scenes " - "defined on the device into the scenes config file.") + sp = linkgrp.add_parser("import-scenes", help="Import all of the scenes " + "defined on the device into " + "the scenes.yaml file.", + description="This will add all links defined on " + "the device to the scenes.yaml file. " + "Any link defined in the scenes.yaml " + "file that is not present on the " + "device will be DELETED.") sp.add_argument("address", help="Device address or name.") sp.add_argument("--run", action="store_true", default=False, help="Perform the actions altering the scenes config file " @@ -393,9 +482,18 @@ def parse_args(args): #--------------------------------------- # device.awake # Only works on battery devices - sp = sub.add_parser("awake", help="Mark a battery device as being awake." - "Hold the set button on the device until the light " - "blinks to force the device awake for 3 minutes.") + sp = batterygrp.add_parser("awake", help="Mark a battery device as being " + "awake.", + description="Mark a battery device as being " + "awake. Necessary in order to send " + "commands to the device in real " + "time. Otherwise commands will be " + "queued until the device is next " + "awake. To wake up a battery " + "device hold the set button on the " + "device until the light blinks. " + "The device will remain awake for " + "~3 minutes.") sp.add_argument("address", help="Device address or name.") sp.add_argument("-q", "--quiet", action="store_true", help="Don't print any command results to the screen.") @@ -404,9 +502,11 @@ def parse_args(args): #--------------------------------------- # device.get_battery_voltage # Only works on some battery devices - sp = sub.add_parser("get-battery-voltage", help="Check the battery " - "voltage. Will request the value the nex time the " - "device is awake.") + sp = batterygrp.add_parser("get-battery-voltage", help="Check the battery " + "voltage.", + description="Check the battery voltage. Will " + "request the value " + "the nex time the device is awake.") sp.add_argument("address", help="Device address or name.") sp.add_argument("-q", "--quiet", action="store_true", help="Don't print any command results to the screen.") @@ -415,8 +515,12 @@ def parse_args(args): #--------------------------------------- # device.set_low_battery_voltage # Only works on some battery devices, notably those with removable batts - sp = sub.add_parser("set-low-battery-voltage", help="Sets the threshold " - "voltage at which a battery will be signaled as low.") + sp = batterygrp.add_parser("set-low-battery-voltage", help="Sets the " + "threshold voltage at which a battery will be " + "signaled as low.", + description="Sets the threshold " + "voltage at which a battery will be signaled " + "as low.") sp.add_argument("address", help="Device address or name.") sp.add_argument("voltage", type=float, help="Low voltage as float.") sp.add_argument("-q", "--quiet", action="store_true", @@ -427,8 +531,11 @@ def parse_args(args): #--------------------------------------- # device.raw_message - sp = sub.add_parser("raw-command", help="Sends a raw message to a " - "configured device") + sp = advancedgrp.add_parser("raw-command", help="Sends a raw message to " + "the device.", + description="Sends a raw message to the " + "device. Useful for testing " + "features not yet supported") sp.add_argument("address", help="Device address or name.") sp.add_argument("cmd1", type=auto_int, help="cmd1 byte (supports " "both hex and decimal)") @@ -446,6 +553,18 @@ def parse_args(args): help="Don't print any command results to the screen.") sp.set_defaults(func=device.send_raw_command) + #--------------------------------------- + # modem.factory_reset command + sp = advancedgrp.add_parser("factory-reset", help="Perform a remote " + "factory reset. Currently only supported on " + "the modem.", + description="Perform a remote factory " + "reset. Currently only supported on the " + "modem.") + sp.add_argument("-q", "--quiet", action="store_true", + help="Don't print any command results to the screen.") + sp.set_defaults(func=modem.factory_reset) + return p.parse_args(args) From e345df98c4f6991cf12a0f66eefdc40ab5f3362c Mon Sep 17 00:00:00 2001 From: Kevin Robert Keegan Date: Fri, 10 Dec 2021 10:06:02 -0800 Subject: [PATCH 4/6] Update CHANGELOG.md --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70d9a59c..bab4d12f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Revision Change History +## [1.1.1] + +### Fixes + +- Fix error which caused changing the state of one keypadlinc button, to turn off all other keypadlinc buttons (#463) +- Fix error where the `on_only` and `night_only` flags for the motion sensor were reversed. (#464) + +### Additions + +- Significantly improvement in readabilty and content of the helptext for the commandline interface. (#465) + ## [1.1.0] ### Fixes From c8924e8b30e8dbe02df038071ddde7a661dff3cd Mon Sep 17 00:00:00 2001 From: Kevin Robert Keegan Date: Fri, 10 Dec 2021 10:51:02 -0800 Subject: [PATCH 5/6] Update CHANGELOG.md Fix Links. It is super annoying that the github shortcuts for issue and pr links don't work on pages. --- CHANGELOG.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bab4d12f..6e161b8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,26 +4,26 @@ ### Fixes -- Fix error which caused changing the state of one keypadlinc button, to turn off all other keypadlinc buttons (#463) -- Fix error where the `on_only` and `night_only` flags for the motion sensor were reversed. (#464) +- Fix error which caused changing the state of one keypadlinc button, to turn off all other keypadlinc buttons ([#463](https://github.com/TD22057/insteon-mqtt/pull/463)) +- Fix error where the `on_only` and `night_only` flags for the motion sensor were reversed. ([#464](https://github.com/TD22057/insteon-mqtt/pull/443)) ### Additions -- Significantly improvement in readabilty and content of the helptext for the commandline interface. (#465) +- Significantly improvement in readabilty and content of the helptext for the commandline interface. ([#465](https://github.com/TD22057/insteon-mqtt/pull/465)) ## [1.1.0] ### Fixes - Fixes a problem wherein the modem might not receive commands from secondary groups on multigroup controllers (keypadlincs, remotes, motion sensors). -If you are having an issue like this. After updating, run `pair` on the affected multigroup controller. (#453) +If you are having an issue like this. After updating, run `pair` on the affected multigroup controller. ([#453](https://github.com/TD22057/insteon-mqtt/pull/453)) ### Additions - Adds an availability topic to the topics published on MQTT. It will publish `online` when InsteonMQTT comes online and `offline` whenever InsteonMQTT goes offline even if as a result of a crash. This has been added into the default discovery templates for HomeAssistant. So you should now see your devices as `unavailable` if -InsteonMQTT is offline. (#448) -- If a level is now sent as part of the `scene` command, the device state will now properly report that level. (#449) +InsteonMQTT is offline. ([#448](https://github.com/TD22057/insteon-mqtt/pull/448)) +- If a level is now sent as part of the `scene` command, the device state will now properly report that level. ([#449](https://github.com/TD22057/insteon-mqtt/pull/449)) ## [1.0.2] From 0be10be7c6a6e8733e8c4d9b12816a068b064d2c Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Fri, 10 Dec 2021 11:16:05 -0800 Subject: [PATCH 6/6] =?UTF-8?q?Bump=20version:=201.1.0=20=E2=86=92=201.1.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- README.md | 2 +- config.json | 2 +- insteon_mqtt/const.py | 2 +- setup.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 0865cbe6..8847bc8e 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.1.0 +current_version = 1.1.1 commit = True tag = False diff --git a/README.md b/README.md index 5ddc87d9..a76e1d5a 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ integrated into and controlled from anything that can use MQTT. This package works well with HomeAssistant and can be easily [installed as an addon](docs/HA_Addon_Instructions.md) using the HomeAssistant Supervisor. -Version: 1.1.0 ([History](CHANGELOG.md)) +Version: 1.1.1 ([History](CHANGELOG.md)) ### Recent Breaking Changes diff --git a/config.json b/config.json index 93a0f7f8..053b687b 100644 --- a/config.json +++ b/config.json @@ -2,7 +2,7 @@ "name": "Insteon MQTT", "description": "Creates an MQTT interface to the Insteon protocol.", "slug": "insteon-mqtt", - "version": "1.1.0", + "version": "1.1.1", "startup": "services", "arch": ["amd64","armhf","aarch64","i386"], "boot": "auto", diff --git a/insteon_mqtt/const.py b/insteon_mqtt/const.py index d94e116d..6d9aaf20 100644 --- a/insteon_mqtt/const.py +++ b/insteon_mqtt/const.py @@ -11,6 +11,6 @@ variable throughout the code without causing a cyclic import """ -__version__ = "1.1.0" +__version__ = "1.1.1" #=========================================================================== diff --git a/setup.py b/setup.py index 72f239cd..8c4c30b8 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setuptools.setup( name = 'insteon-mqtt', - version = '1.1.0', + version = '1.1.1', description = "Insteon <-> MQTT bridge server", long_description = readme, author = "Ted Drain",