From 394f18964dcf4720fd42b99f41df9dec5cc1a4b2 Mon Sep 17 00:00:00 2001 From: Lloyd Ramey Date: Mon, 17 May 2021 11:41:10 -0400 Subject: [PATCH 01/44] fix issue with hassio initial config setup --- hassio/entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hassio/entrypoint.sh b/hassio/entrypoint.sh index 5e077b69..3670d211 100755 --- a/hassio/entrypoint.sh +++ b/hassio/entrypoint.sh @@ -6,7 +6,7 @@ if [ ! -f /config/insteon-mqtt/config.yaml ]; then echo "Copying default config.yaml" - /bin/cp /opt/insteon-mqtt/config.yaml /config/insteon-mqtt/config.yaml + /bin/cp /config/insteon-mqtt/config.yaml.default /config/insteon-mqtt/config.yaml sed -i "s/storage: 'data'/storage: '\/config\/insteon-mqtt\/data'/" /config/insteon-mqtt/config.yaml sed -i "s/file: \/var\/log\/insteon_mqtt.log/file: \/config\/insteon-mqtt\/insteon_mqtt.log/" /config/insteon-mqtt/config.yaml fi From f71bb98b75c9a24bffdaec2392882ad6cd1b313b Mon Sep 17 00:00:00 2001 From: Lloyd Ramey Date: Mon, 17 May 2021 12:10:47 -0400 Subject: [PATCH 02/44] add files created by tests to gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index a452c1ec..152393bb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ __pycache__ .cache .coverage htmlcov + +# files created by tests +data/ +01.02.03.json \ No newline at end of file From 3eb1aee0e42a3900e54e2ccdf2de2671b5980634 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 17 May 2021 10:04:58 -0700 Subject: [PATCH 03/44] Add Missing File for Config Unit Test --- tests/configs/use_dns.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/configs/use_dns.yaml diff --git a/tests/configs/use_dns.yaml b/tests/configs/use_dns.yaml new file mode 100644 index 00000000..743e2d82 --- /dev/null +++ b/tests/configs/use_dns.yaml @@ -0,0 +1,9 @@ +insteon: + use_hub: True + hub_ip: insteon.example.com + hub_user: username + hub_password: password + +mqtt: + broker: broker.example.com + port: 1883 From 61d5d79f9665cf4666bf761f066171d30a98e632 Mon Sep 17 00:00:00 2001 From: Lloyd Ramey Date: Mon, 17 May 2021 13:17:47 -0400 Subject: [PATCH 04/44] allow configuring the resume dim operating flag --- insteon_mqtt/device/base/DimmerBase.py | 42 ++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/insteon_mqtt/device/base/DimmerBase.py b/insteon_mqtt/device/base/DimmerBase.py index e76e2713..cb815c9a 100644 --- a/insteon_mqtt/device/base/DimmerBase.py +++ b/insteon_mqtt/device/base/DimmerBase.py @@ -1,6 +1,6 @@ #=========================================================================== # -# DimmerBase Class. Specifically Ramp_Rate and On_Level Flags, +# DimmerBase Class. Specifically Resume_Dim, Ramp_Rate and On_Level Flags, # increment_up and increment_down functions. Extensions to ManualCtrl # Plus other dimmer helper functions # @@ -59,7 +59,8 @@ def __init__(self, protocol, modem, address, name=None, config_extra=None): # Define the flags handled by set_flags() self.set_flags_map.update({'on_level': self.set_on_level, - 'ramp_rate': self.set_ramp_rate}) + 'ramp_rate': self.set_ramp_rate, + 'resume_dim': self.set_resume_dim}) #========= Flags Functions #----------------------------------------------------------------------- @@ -142,6 +143,43 @@ def set_ramp_rate(self, on_done=None, **kwargs): msg_handler = handler.StandardCmd(msg, callback, on_done) self.send(msg, msg_handler) + def set_resume_dim(self, on_done=None, **kwargs): + """Set the device resume dim operating flag on/off + + This enables or disabled the resume dim level functionality + and is used in conjuction with the configured on level to determine + the default on level for 'normal' speeds. If this is enabled, the + device will resume it's previous level when turned on; otherwise + it will return to the configured on level. This can be very useful + because a double-tap (fast-on) will the turn the device to full + brightness if needed. + + Args: + enabled (bool): resume dim is enabled + on_done: Finished callback. This is called when the command has + completed. Signature is: on_done(success, msg, data) + """ + resume_dim = util.input_bool(kwargs, 'resume_dim') + + # These values were pulled from the insteon command tables pdf + # page 8 + # 0x04 - Enables resume dim + # 0x05 - Disables resume dim + if resume_dim: + LOG.info("Device %s enabling resume dim", self.label) + cmd2=0x04 + else: + LOG.info("Device %s disabling resume dim", self.label) + cmd2=0x05 + + msg = Msg.OutStandard.direct(self.addr, Msg.CmdType.SET_OPERATING_FLAGS, cmd2) + + # Use the standard command handler which will notify us when the + # command is ACK'ed. + callback = self.generic_ack_callback("Button resume dim updated") + msg_handler = handler.StandardCmd(msg, callback, on_done) + self.send(msg, msg_handler) + #----------------------------------------------------------------------- def handle_on_level(self, msg, on_done, level): """Callback for handling set_on_level() responses. From d23e1e4946311599801c75ec232b946d734fe194 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 17 May 2021 11:16:33 -0700 Subject: [PATCH 05/44] Update Changelog --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 827f325d..4c24f6dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Revision Change History +## [0.9.3] + +## Fixes + +- Fix failure to create initial `config.yaml` file in HomeAssistant + Supervisor installation. Thanks @lnr0626 ([PR 406][P406]) +- Update .gitignore to skip files created by testing. Thanks @lnr0626 + ([PR 407][P407]) +- Adds a file necessary for a unit test that was accidentally left out. + Thanks @lnr0626 ([PR 410][P410]) + + ## [0.9.2] ## Hotfix @@ -742,3 +754,6 @@ will add new features. [P400]: https://github.com/TD22057/insteon-mqtt/pull/400 [P404]: https://github.com/TD22057/insteon-mqtt/pull/404 [P402]: https://github.com/TD22057/insteon-mqtt/pull/402 +[P406]: https://github.com/TD22057/insteon-mqtt/pull/406 +[P407]: https://github.com/TD22057/insteon-mqtt/pull/407 +[P410]: https://github.com/TD22057/insteon-mqtt/pull/410 From 08f5693cbc7c774ebf77f74c52a9887dde1ad3c2 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 17 May 2021 11:28:57 -0700 Subject: [PATCH 06/44] Fix Various Flake Warnings --- insteon_mqtt/Modem.py | 1 - insteon_mqtt/cmd_line/modem.py | 3 +++ insteon_mqtt/config.py | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/insteon_mqtt/Modem.py b/insteon_mqtt/Modem.py index 8b2b2bcb..5ca54a50 100644 --- a/insteon_mqtt/Modem.py +++ b/insteon_mqtt/Modem.py @@ -17,7 +17,6 @@ from . import message as Msg from . import util from . import Scenes -from . import device as DevClass from .Signal import Signal LOG = log.get_logger() diff --git a/insteon_mqtt/cmd_line/modem.py b/insteon_mqtt/cmd_line/modem.py index 53c32097..1484e941 100644 --- a/insteon_mqtt/cmd_line/modem.py +++ b/insteon_mqtt/cmd_line/modem.py @@ -53,6 +53,7 @@ def get_engine_all(args, config): reply = util.send(config, topic, payload, args.quiet) return reply["status"] + #=========================================================================== def join_all(args, config): topic = "%s/modem" % (args.topic) @@ -63,6 +64,7 @@ def join_all(args, config): reply = util.send(config, topic, payload, args.quiet) return reply["status"] + #=========================================================================== def pair_all(args, config): topic = "%s/modem" % (args.topic) @@ -73,6 +75,7 @@ def pair_all(args, config): reply = util.send(config, topic, payload, args.quiet) return reply["status"] + #=========================================================================== def factory_reset(args, config): topic = "%s/modem" % (args.topic) diff --git a/insteon_mqtt/config.py b/insteon_mqtt/config.py index e246bac2..748e7f8d 100644 --- a/insteon_mqtt/config.py +++ b/insteon_mqtt/config.py @@ -10,7 +10,6 @@ #=========================================================================== import os.path import re -import ipaddress import yaml from cerberus import Validator from cerberus.errors import BasicErrorHandler From f3cafebc84b9d5817883bfc9e1a32280111475e2 Mon Sep 17 00:00:00 2001 From: Lloyd Ramey Date: Mon, 17 May 2021 16:44:20 -0400 Subject: [PATCH 07/44] use extended command for set flag --- insteon_mqtt/device/base/DimmerBase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/insteon_mqtt/device/base/DimmerBase.py b/insteon_mqtt/device/base/DimmerBase.py index cb815c9a..22d82c56 100644 --- a/insteon_mqtt/device/base/DimmerBase.py +++ b/insteon_mqtt/device/base/DimmerBase.py @@ -172,7 +172,7 @@ def set_resume_dim(self, on_done=None, **kwargs): LOG.info("Device %s disabling resume dim", self.label) cmd2=0x05 - msg = Msg.OutStandard.direct(self.addr, Msg.CmdType.SET_OPERATING_FLAGS, cmd2) + msg = Msg.OutExtended.direct(self.addr, Msg.CmdType.SET_OPERATING_FLAGS, cmd2, bytes([0x00] * 14)) # Use the standard command handler which will notify us when the # command is ACK'ed. From 4f417699b8739636a88c2d97c66dee03ec442937 Mon Sep 17 00:00:00 2001 From: Lloyd Ramey Date: Mon, 17 May 2021 16:47:12 -0400 Subject: [PATCH 08/44] fix flake8 issues --- insteon_mqtt/device/base/DimmerBase.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/insteon_mqtt/device/base/DimmerBase.py b/insteon_mqtt/device/base/DimmerBase.py index 22d82c56..46d10f5b 100644 --- a/insteon_mqtt/device/base/DimmerBase.py +++ b/insteon_mqtt/device/base/DimmerBase.py @@ -167,12 +167,14 @@ def set_resume_dim(self, on_done=None, **kwargs): # 0x05 - Disables resume dim if resume_dim: LOG.info("Device %s enabling resume dim", self.label) - cmd2=0x04 + cmd2 = 0x04 else: LOG.info("Device %s disabling resume dim", self.label) - cmd2=0x05 + cmd2 = 0x05 - msg = Msg.OutExtended.direct(self.addr, Msg.CmdType.SET_OPERATING_FLAGS, cmd2, bytes([0x00] * 14)) + cmd1 = Msg.CmdType.SET_OPERATING_FLAGS + msg = Msg.OutExtended.direct(self.addr, cmd1, cmd2, + bytes([0x00] * 14)) # Use the standard command handler which will notify us when the # command is ACK'ed. From 950deabb1a98a85e10c0f6a18021e5fae459b922 Mon Sep 17 00:00:00 2001 From: Lloyd Ramey Date: Mon, 17 May 2021 16:53:41 -0400 Subject: [PATCH 09/44] add test for resume dim --- tests/device/test_DimmerDev.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/device/test_DimmerDev.py b/tests/device/test_DimmerDev.py index 21e351c3..c420c11c 100644 --- a/tests/device/test_DimmerDev.py +++ b/tests/device/test_DimmerDev.py @@ -73,6 +73,16 @@ def test_handle_on_off_manual(self, test_device, group, cmd1, cmd2, expected): else: mocked.assert_not_called() + def test_set_resume_dim(self, test_device): + # set_resume_dim(self, resume_dim, on_done=None) + for params in ([True, 0x04], [False, 0x05]): + test_device.set_resume_dim(resume_dim=params[0]) + assert len(test_device.protocol.sent) == 1 + assert test_device.protocol.sent[0].msg.cmd1 == 0x20 + assert test_device.protocol.sent[0].msg.cmd2 == params[1] + assert test_device.protocol.sent[0].msg.data == bytes([0x00] * 14) + test_device.protocol.clear() + def test_set_on_level(self, test_device): # set_on_level(self, level, on_done=None) def level_bytes(level): From e6190150390ace56117da1ac05845c85ea60a303 Mon Sep 17 00:00:00 2001 From: Lloyd Ramey Date: Mon, 17 May 2021 15:08:37 -0400 Subject: [PATCH 10/44] allow sending raw commands to devices --- insteon_mqtt/cmd_line/device.py | 23 +++++++++++++++++++ insteon_mqtt/cmd_line/main.py | 19 ++++++++++++++++ insteon_mqtt/device/base/Base.py | 38 +++++++++++++++++++++++++++++++- 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/insteon_mqtt/cmd_line/device.py b/insteon_mqtt/cmd_line/device.py index b48564cd..97b554f9 100644 --- a/insteon_mqtt/cmd_line/device.py +++ b/insteon_mqtt/cmd_line/device.py @@ -343,4 +343,27 @@ def set_low_battery_voltage(args, config): reply = util.send(config, topic, payload, args.quiet) return reply["status"] + + +#=========================================================================== +def send_raw_message(args, config): + topic = "%s/%s" % (args.topic, args.address) + payload = { + "cmd" : "raw_message", + "cmd1": args.cmd1, + "cmd2": args.cmd2, + "ext_resp": args.ext_resp + } + + if args.ext_req: + payload["data"] = [0] * 14 + + if args.data: + payload["data"] = (args.data + ([0] * 14))[:14] + + if args.crc: + payload["crc_type"] = args.crc + + reply = util.send(config, topic, payload, args.quiet) + return reply["status"] #=========================================================================== diff --git a/insteon_mqtt/cmd_line/main.py b/insteon_mqtt/cmd_line/main.py index fc1daa48..0e762fef 100644 --- a/insteon_mqtt/cmd_line/main.py +++ b/insteon_mqtt/cmd_line/main.py @@ -423,6 +423,25 @@ def parse_args(args): help="Don't print any command results to the screen.") sp.set_defaults(func=device.set_low_battery_voltage) + auto_int = lambda x: int(x, 0) + + #--------------------------------------- + # device.raw_message + sp = sub.add_parser("raw-message", help="Sends a raw message to a configured device") + sp.add_argument("address", help="Device address or name.") + sp.add_argument("cmd1", type=auto_int, help="cmd1 byte") + sp.add_argument("cmd2", type=auto_int, help="cmd2 byte") + sp.add_argument("--ext-req", action="store_true", help="Send message " + "as an extended message") + sp.add_argument("--ext-resp", action="store_true", help="Receive response " + "as an extended message") + sp.add_argument("-d", "--data", type=auto_int, default=None, nargs="*", + help="the extended message data") + sp.add_argument("--crc", type=str, choices=["D14", "CRC"]) + sp.add_argument("-q", "--quiet", action="store_true", + help="Don't print any command results to the screen.") + sp.set_defaults(func=device.send_raw_message) + return p.parse_args(args) diff --git a/insteon_mqtt/device/base/Base.py b/insteon_mqtt/device/base/Base.py index af649798..6c6d8c9b 100644 --- a/insteon_mqtt/device/base/Base.py +++ b/insteon_mqtt/device/base/Base.py @@ -184,7 +184,8 @@ def __init__(self, protocol, modem, address, name=None, config_extra=None): 'get_engine' : self.get_engine, 'get_model' : self.get_model, 'sync': self.sync, - 'import_scenes': self.import_scenes + 'import_scenes': self.import_scenes, + 'raw_message': self.raw_message } # Device database delta. The delta tells us if the database is @@ -493,6 +494,31 @@ def set_flags(self, on_done, **kwargs): seq.add(function, **kwargs) seq.run() + #----------------------------------------------------------------------- + # TODO: remove this? i'm adding it to make it easier for me to test commands + # or extend it to support extended commands? + def raw_message(self, cmd1, cmd2, data=None, crc_type=None, ext_resp=False, on_done=None): + """Send a raw message to this device + + This can be used to send commands that aren't supported directly + by insteon-mqtt yet. This is as low level as it gets, use at your + own risk + """ + if data is None: + msg = Msg.OutStandard.direct(self.addr, cmd1, cmd2) + else: + msg = Msg.OutExtended.direct(self.addr, cmd1, cmd2, bytes(data), crc_type) + + if ext_resp: + # Use the standard command handler which will notify us when the + # command is ACK'ed. + msg_handler = handler.StandardCmd(msg, self.handle_std_raw_message, on_done) + else: + # Use the extended response command handler which will notify us when the + # command is ACK'ed. + msg_handler = handler.ExtendedCmdResponse(msg, self.handle_ext_raw_message, on_done) + + self.send(msg, msg_handler) #----------------------------------------------------------------------- def refresh(self, force=False, group=None, on_done=None): @@ -1149,6 +1175,16 @@ def handle_flags(self, msg, on_done): "{:08b}".format(msg.cmd2)) on_done(True, "Operation complete", msg.cmd2) + #----------------------------------------------------------------------- + def handle_std_raw_message(self, msg, on_done): + LOG.ui("Device %s raw message response: %s", self.addr, msg) + on_done(True, "Raw Message complete", None) + + #----------------------------------------------------------------------- + def handle_ext_raw_message(self, msg, on_done): + LOG.ui("Device %s raw message response: %s", self.addr, msg) + on_done(True, "Raw Message complete", None) + #----------------------------------------------------------------------- def handle_engine(self, msg, on_done): """Handle replies to the get engine command. From 5a810a8a879f34a702b07f73caac9fe887778409 Mon Sep 17 00:00:00 2001 From: Lloyd Ramey Date: Mon, 17 May 2021 18:01:09 -0400 Subject: [PATCH 11/44] add resume dim docs to mqtt document --- docs/mqtt.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/mqtt.md b/docs/mqtt.md index 45a30bb6..b267d7f0 100644 --- a/docs/mqtt.md +++ b/docs/mqtt.md @@ -380,6 +380,9 @@ Switch, KeypadLinc, and Dimmer all support the flags: - ramp_rate: float in the range of 0.5 to 540 seconds which sets the default ramp rate that will be used when the button is pressed + - resume_dim: bool indicating if the device's on level should be determined from + the configured on_level flag, or based on the last on level (currently this will + only effect manually pressing the button and not commands send through insteon-mqtt) IOLinc supports the flags: From ccc25f11d4919f4304e2ef412de7516fde6fffdf Mon Sep 17 00:00:00 2001 From: Lloyd Ramey Date: Mon, 17 May 2021 18:10:16 -0400 Subject: [PATCH 12/44] fix method docs --- insteon_mqtt/device/base/DimmerBase.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/insteon_mqtt/device/base/DimmerBase.py b/insteon_mqtt/device/base/DimmerBase.py index 46d10f5b..bd31c137 100644 --- a/insteon_mqtt/device/base/DimmerBase.py +++ b/insteon_mqtt/device/base/DimmerBase.py @@ -109,7 +109,7 @@ def set_ramp_rate(self, on_done=None, **kwargs): minutes. Args: - rate (float): Ramp rate in in the range [0.1, 540] seconds + ramp_rate (float): Ramp rate in in the range [0.1, 540] seconds on_done: Finished callback. This is called when the command has completed. Signature is: on_done(success, msg, data) """ @@ -155,7 +155,7 @@ def set_resume_dim(self, on_done=None, **kwargs): brightness if needed. Args: - enabled (bool): resume dim is enabled + resume_dim (bool): resume dim is enabled on_done: Finished callback. This is called when the command has completed. Signature is: on_done(success, msg, data) """ From 17213d4571a8c3287f3cc39ca180ad99d65afeb5 Mon Sep 17 00:00:00 2001 From: Lloyd Ramey Date: Tue, 18 May 2021 14:30:00 -0400 Subject: [PATCH 13/44] change command name to `raw_command` --- insteon_mqtt/cmd_line/device.py | 2 +- insteon_mqtt/cmd_line/main.py | 2 +- insteon_mqtt/device/base/Base.py | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/insteon_mqtt/cmd_line/device.py b/insteon_mqtt/cmd_line/device.py index 97b554f9..e1d167ef 100644 --- a/insteon_mqtt/cmd_line/device.py +++ b/insteon_mqtt/cmd_line/device.py @@ -349,7 +349,7 @@ def set_low_battery_voltage(args, config): def send_raw_message(args, config): topic = "%s/%s" % (args.topic, args.address) payload = { - "cmd" : "raw_message", + "cmd" : "raw_command", "cmd1": args.cmd1, "cmd2": args.cmd2, "ext_resp": args.ext_resp diff --git a/insteon_mqtt/cmd_line/main.py b/insteon_mqtt/cmd_line/main.py index 0e762fef..8bb472af 100644 --- a/insteon_mqtt/cmd_line/main.py +++ b/insteon_mqtt/cmd_line/main.py @@ -427,7 +427,7 @@ def parse_args(args): #--------------------------------------- # device.raw_message - sp = sub.add_parser("raw-message", help="Sends a raw message to a configured device") + sp = sub.add_parser("raw-command", help="Sends a raw message to a configured device") sp.add_argument("address", help="Device address or name.") sp.add_argument("cmd1", type=auto_int, help="cmd1 byte") sp.add_argument("cmd2", type=auto_int, help="cmd2 byte") diff --git a/insteon_mqtt/device/base/Base.py b/insteon_mqtt/device/base/Base.py index 6c6d8c9b..7bcf7715 100644 --- a/insteon_mqtt/device/base/Base.py +++ b/insteon_mqtt/device/base/Base.py @@ -185,7 +185,7 @@ def __init__(self, protocol, modem, address, name=None, config_extra=None): 'get_model' : self.get_model, 'sync': self.sync, 'import_scenes': self.import_scenes, - 'raw_message': self.raw_message + 'raw_command': self.raw_command } # Device database delta. The delta tells us if the database is @@ -495,9 +495,7 @@ def set_flags(self, on_done, **kwargs): seq.run() #----------------------------------------------------------------------- - # TODO: remove this? i'm adding it to make it easier for me to test commands - # or extend it to support extended commands? - def raw_message(self, cmd1, cmd2, data=None, crc_type=None, ext_resp=False, on_done=None): + def raw_command(self, cmd1, cmd2, data=None, crc_type=None, ext_resp=False, on_done=None): """Send a raw message to this device This can be used to send commands that aren't supported directly From 66a7c00273e6fd4e7e385867b6538bc3376555d3 Mon Sep 17 00:00:00 2001 From: Lloyd Ramey Date: Tue, 18 May 2021 14:30:26 -0400 Subject: [PATCH 14/44] pad and trim data coming from mqtt --- insteon_mqtt/device/base/Base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/insteon_mqtt/device/base/Base.py b/insteon_mqtt/device/base/Base.py index 7bcf7715..ce6197f8 100644 --- a/insteon_mqtt/device/base/Base.py +++ b/insteon_mqtt/device/base/Base.py @@ -505,7 +505,8 @@ def raw_command(self, cmd1, cmd2, data=None, crc_type=None, ext_resp=False, on_d if data is None: msg = Msg.OutStandard.direct(self.addr, cmd1, cmd2) else: - msg = Msg.OutExtended.direct(self.addr, cmd1, cmd2, bytes(data), crc_type) + padded_and_trimmed_data = (data + ([0] * 14))[:14] + msg = Msg.OutExtended.direct(self.addr, cmd1, cmd2, bytes(padded_and_trimmed_data), crc_type) if ext_resp: # Use the standard command handler which will notify us when the From b5226d8c4cefc96595f9fc21b7a6432a44e2a1a1 Mon Sep 17 00:00:00 2001 From: Lloyd Ramey Date: Tue, 18 May 2021 14:31:00 -0400 Subject: [PATCH 15/44] fix response handler --- insteon_mqtt/device/base/Base.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/insteon_mqtt/device/base/Base.py b/insteon_mqtt/device/base/Base.py index ce6197f8..5169dab8 100644 --- a/insteon_mqtt/device/base/Base.py +++ b/insteon_mqtt/device/base/Base.py @@ -509,13 +509,13 @@ def raw_command(self, cmd1, cmd2, data=None, crc_type=None, ext_resp=False, on_d msg = Msg.OutExtended.direct(self.addr, cmd1, cmd2, bytes(padded_and_trimmed_data), crc_type) if ext_resp: - # Use the standard command handler which will notify us when the + # Use the extended response command handler which will notify us when the # command is ACK'ed. - msg_handler = handler.StandardCmd(msg, self.handle_std_raw_message, on_done) + msg_handler = handler.ExtendedCmdResponse(msg, self.handle_raw_command, on_done) else: - # Use the extended response command handler which will notify us when the + # Use the standard command handler which will notify us when the # command is ACK'ed. - msg_handler = handler.ExtendedCmdResponse(msg, self.handle_ext_raw_message, on_done) + msg_handler = handler.StandardCmd(msg, self.handle_raw_command, on_done) self.send(msg, msg_handler) @@ -1175,12 +1175,7 @@ def handle_flags(self, msg, on_done): on_done(True, "Operation complete", msg.cmd2) #----------------------------------------------------------------------- - def handle_std_raw_message(self, msg, on_done): - LOG.ui("Device %s raw message response: %s", self.addr, msg) - on_done(True, "Raw Message complete", None) - - #----------------------------------------------------------------------- - def handle_ext_raw_message(self, msg, on_done): + def handle_raw_command(self, msg, on_done): LOG.ui("Device %s raw message response: %s", self.addr, msg) on_done(True, "Raw Message complete", None) From fc4ca12b438fab7bea7cfb85776c332e4372d8ca Mon Sep 17 00:00:00 2001 From: Lloyd Ramey Date: Tue, 18 May 2021 14:32:29 -0400 Subject: [PATCH 16/44] fix line length issue --- insteon_mqtt/device/base/Base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/insteon_mqtt/device/base/Base.py b/insteon_mqtt/device/base/Base.py index 5169dab8..9a80fd3f 100644 --- a/insteon_mqtt/device/base/Base.py +++ b/insteon_mqtt/device/base/Base.py @@ -505,8 +505,8 @@ def raw_command(self, cmd1, cmd2, data=None, crc_type=None, ext_resp=False, on_d if data is None: msg = Msg.OutStandard.direct(self.addr, cmd1, cmd2) else: - padded_and_trimmed_data = (data + ([0] * 14))[:14] - msg = Msg.OutExtended.direct(self.addr, cmd1, cmd2, bytes(padded_and_trimmed_data), crc_type) + padded_and_trimmed_data = bytes(data + ([0] * 14))[:14] + msg = Msg.OutExtended.direct(self.addr, cmd1, cmd2, padded_and_trimmed_data, crc_type) if ext_resp: # Use the extended response command handler which will notify us when the From 8c20690639df055155a4bcd71fcdbc999b5f2304 Mon Sep 17 00:00:00 2001 From: Lloyd Ramey Date: Tue, 18 May 2021 14:33:15 -0400 Subject: [PATCH 17/44] simplify cli handling --- insteon_mqtt/cmd_line/device.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/insteon_mqtt/cmd_line/device.py b/insteon_mqtt/cmd_line/device.py index e1d167ef..d4137b1e 100644 --- a/insteon_mqtt/cmd_line/device.py +++ b/insteon_mqtt/cmd_line/device.py @@ -356,10 +356,10 @@ def send_raw_message(args, config): } if args.ext_req: - payload["data"] = [0] * 14 + payload["data"] = [] if args.data: - payload["data"] = (args.data + ([0] * 14))[:14] + payload["data"] = args.data[:14] if args.crc: payload["crc_type"] = args.crc From da5b74d38c453a16bc888c7d50d062d5c80cc0f8 Mon Sep 17 00:00:00 2001 From: Lloyd Ramey Date: Tue, 18 May 2021 14:43:59 -0400 Subject: [PATCH 18/44] update cli command handler function name --- insteon_mqtt/cmd_line/device.py | 2 +- insteon_mqtt/cmd_line/main.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/insteon_mqtt/cmd_line/device.py b/insteon_mqtt/cmd_line/device.py index d4137b1e..a1899271 100644 --- a/insteon_mqtt/cmd_line/device.py +++ b/insteon_mqtt/cmd_line/device.py @@ -346,7 +346,7 @@ def set_low_battery_voltage(args, config): #=========================================================================== -def send_raw_message(args, config): +def send_raw_command(args, config): topic = "%s/%s" % (args.topic, args.address) payload = { "cmd" : "raw_command", diff --git a/insteon_mqtt/cmd_line/main.py b/insteon_mqtt/cmd_line/main.py index 8bb472af..5479a8a1 100644 --- a/insteon_mqtt/cmd_line/main.py +++ b/insteon_mqtt/cmd_line/main.py @@ -440,7 +440,7 @@ def parse_args(args): sp.add_argument("--crc", type=str, choices=["D14", "CRC"]) sp.add_argument("-q", "--quiet", action="store_true", help="Don't print any command results to the screen.") - sp.set_defaults(func=device.send_raw_message) + sp.set_defaults(func=device.send_raw_command) return p.parse_args(args) From 8cc5813d914aafed089e3698351a6bae1744cf71 Mon Sep 17 00:00:00 2001 From: Lloyd Ramey Date: Tue, 18 May 2021 15:01:21 -0400 Subject: [PATCH 19/44] add docs for raw_command --- docs/mqtt.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/docs/mqtt.md b/docs/mqtt.md index 45a30bb6..c0150f0a 100644 --- a/docs/mqtt.md +++ b/docs/mqtt.md @@ -498,6 +498,47 @@ below the expected value { "cmd": "set_low_battery_voltage", "voltage": 7.0 } ``` +### Send a raw insteon command. + +Supported: devices + +Send a raw insteon command to devices. Any changes as a result of this +command won't be kept in sync automatically, so use this at your risk. +This command is intended to help developing and debugging insteon-mqtt, +and is as low level as it gets. It supports standard and extended direct +messages. + +#### Standard Direct message payload + +A standard message requires the cmd1 and cmd2 attributes - allowed values are +`0x00` (0) -> `0xFF` (255). + + ``` + { "cmd": "raw_command", "cmd1": 31, "cmd2": 0 } + ``` + +#### Extended Direct message payload + +To send an extended message, simply provide the data property (a list of numbers). +It's value will be right padded with 0s and trimmed to 14 digits, then converted +to bytes. The allowed values cmd1, cmd2, and elements within the data array are +`0x00` (0) -> `0xFF` (255). The payload also allows specifying the crc type, with +the allowed values of `D14`, `CRC`. If the type is not supplied the data array +will be sent as is without calculating a CRC. + + ``` + { "cmd": "raw_command", "cmd1": 32, "cmd2": 4, data: [], crc_type: "D14" } + ``` + +#### Insteon command tables + +You can find insteon command tables online; however, they are usually out +dated. Most modern devices (since ~2012) run the i2cs engine, which is an +extension on the i2 engine and updated certain commands to require the +D14 crc. If you try commands from an old command table and recieve a +response that indicates an invalid checksum for a standard command, you +can try it as an extended command with the D14 checksum. + --- # State change commands From 48305e967cad431d5f0eb376d050828d6632037f Mon Sep 17 00:00:00 2001 From: Lloyd Ramey Date: Tue, 18 May 2021 15:06:10 -0400 Subject: [PATCH 20/44] add tests for raw_command --- tests/device/base/test_BaseDev.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/device/base/test_BaseDev.py b/tests/device/base/test_BaseDev.py index 43422241..e3b83b76 100644 --- a/tests/device/base/test_BaseDev.py +++ b/tests/device/base/test_BaseDev.py @@ -200,6 +200,22 @@ def test_get_flags(self, test_device): assert len(sent) == 1 assert sent[0].msg.to_bytes() == msg.to_bytes() + def test_send_std_raw_command(self, test_device): + msg = Msg.OutStandard.direct(test_device.addr, 0x32, 0x43) + test_device.raw_command(0x32, 0x43) + sent = test_device.protocol.sent + assert len(sent) == 1 + assert sent[0].msg.to_bytes() == msg.to_bytes() + + def test_send_ext_raw_command(self, test_device): + crc="D14" + data = [i for i in range(0, 20)] + msg = Msg.OutExtended.direct(test_device.addr, 0x32, 0x43, bytes(data[:14]), crc_type=crc) + test_device.raw_command(0x32, 0x43, data, crc_type=crc) + sent = test_device.protocol.sent + assert len(sent) == 1 + assert sent[0].msg.to_bytes() == msg.to_bytes() + def test_get_engine(self, test_device): msg = Msg.OutStandard.direct(test_device.addr, Msg.CmdType.GET_ENGINE_VERSION, 0x00) From e0aee4116f69db0770d5de7528b909a31a225ec5 Mon Sep 17 00:00:00 2001 From: Lloyd Ramey Date: Tue, 18 May 2021 15:12:38 -0400 Subject: [PATCH 21/44] fix flake issues --- insteon_mqtt/cmd_line/main.py | 13 +++++++------ insteon_mqtt/device/base/Base.py | 17 +++++++++++------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/insteon_mqtt/cmd_line/main.py b/insteon_mqtt/cmd_line/main.py index 5479a8a1..87de27a8 100644 --- a/insteon_mqtt/cmd_line/main.py +++ b/insteon_mqtt/cmd_line/main.py @@ -427,19 +427,20 @@ def parse_args(args): #--------------------------------------- # device.raw_message - sp = sub.add_parser("raw-command", help="Sends a raw message to a configured device") + sp = sub.add_parser("raw-command", help="Sends a raw message to a " + "configured device") sp.add_argument("address", help="Device address or name.") sp.add_argument("cmd1", type=auto_int, help="cmd1 byte") sp.add_argument("cmd2", type=auto_int, help="cmd2 byte") sp.add_argument("--ext-req", action="store_true", help="Send message " - "as an extended message") + "as an extended message") sp.add_argument("--ext-resp", action="store_true", help="Receive response " - "as an extended message") - sp.add_argument("-d", "--data", type=auto_int, default=None, nargs="*", - help="the extended message data") + "as an extended message") + sp.add_argument("-d", "--data", type=auto_int, default=None, nargs="*", + help="the extended message data") sp.add_argument("--crc", type=str, choices=["D14", "CRC"]) sp.add_argument("-q", "--quiet", action="store_true", - help="Don't print any command results to the screen.") + help="Don't print any command results to the screen.") sp.set_defaults(func=device.send_raw_command) return p.parse_args(args) diff --git a/insteon_mqtt/device/base/Base.py b/insteon_mqtt/device/base/Base.py index 9a80fd3f..aa11013c 100644 --- a/insteon_mqtt/device/base/Base.py +++ b/insteon_mqtt/device/base/Base.py @@ -494,8 +494,10 @@ def set_flags(self, on_done, **kwargs): seq.add(function, **kwargs) seq.run() + #----------------------------------------------------------------------- - def raw_command(self, cmd1, cmd2, data=None, crc_type=None, ext_resp=False, on_done=None): + def raw_command(self, cmd1, cmd2, data=None, crc_type=None, ext_resp=False, + on_done=None): """Send a raw message to this device This can be used to send commands that aren't supported directly @@ -506,16 +508,19 @@ def raw_command(self, cmd1, cmd2, data=None, crc_type=None, ext_resp=False, on_d msg = Msg.OutStandard.direct(self.addr, cmd1, cmd2) else: padded_and_trimmed_data = bytes(data + ([0] * 14))[:14] - msg = Msg.OutExtended.direct(self.addr, cmd1, cmd2, padded_and_trimmed_data, crc_type) + msg = Msg.OutExtended.direct(self.addr, cmd1, cmd2, + padded_and_trimmed_data, crc_type) if ext_resp: - # Use the extended response command handler which will notify us when the - # command is ACK'ed. - msg_handler = handler.ExtendedCmdResponse(msg, self.handle_raw_command, on_done) + # Use the extended response command handler which will notify us + # when the command is ACK'ed. + msg_handler = handler.ExtendedCmdResponse( + msg, self.handle_raw_command, on_done) else: # Use the standard command handler which will notify us when the # command is ACK'ed. - msg_handler = handler.StandardCmd(msg, self.handle_raw_command, on_done) + msg_handler = handler.StandardCmd( + msg, self.handle_raw_command, on_done) self.send(msg, msg_handler) From b557f0e8af716db2ceb7a1cc72bce9a21494721a Mon Sep 17 00:00:00 2001 From: Lloyd Ramey Date: Tue, 18 May 2021 15:16:46 -0400 Subject: [PATCH 22/44] add indication that either hex or decimal are supported on cli --- insteon_mqtt/cmd_line/main.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/insteon_mqtt/cmd_line/main.py b/insteon_mqtt/cmd_line/main.py index 87de27a8..80fe65da 100644 --- a/insteon_mqtt/cmd_line/main.py +++ b/insteon_mqtt/cmd_line/main.py @@ -430,14 +430,17 @@ def parse_args(args): sp = sub.add_parser("raw-command", help="Sends a raw message to a " "configured device") sp.add_argument("address", help="Device address or name.") - sp.add_argument("cmd1", type=auto_int, help="cmd1 byte") - sp.add_argument("cmd2", type=auto_int, help="cmd2 byte") + sp.add_argument("cmd1", type=auto_int, help="cmd1 byte (supports " + "both hex and decimal)") + sp.add_argument("cmd2", type=auto_int, help="cmd2 byte (supports " + "both hex and decimal)") sp.add_argument("--ext-req", action="store_true", help="Send message " "as an extended message") sp.add_argument("--ext-resp", action="store_true", help="Receive response " "as an extended message") sp.add_argument("-d", "--data", type=auto_int, default=None, nargs="*", - help="the extended message data") + help="the extended message data (supports both hex " + "and decimal)") sp.add_argument("--crc", type=str, choices=["D14", "CRC"]) sp.add_argument("-q", "--quiet", action="store_true", help="Don't print any command results to the screen.") From 16046aafc44c7a0ac76b73be03541114f371e86e Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 18 May 2021 13:24:50 -0700 Subject: [PATCH 23/44] Move Schemas to Data Folder; Fix gitignore; Update Setup.py Renamed folder to prepare for config-base.yaml to be added to the same folder. --- .gitignore | 4 ++-- insteon_mqtt/config.py | 2 +- insteon_mqtt/{schemas => data}/config-schema.yaml | 0 insteon_mqtt/{schemas => data}/scenes-schema.yaml | 0 setup.py | 4 ++-- 5 files changed, 5 insertions(+), 5 deletions(-) rename insteon_mqtt/{schemas => data}/config-schema.yaml (100%) rename insteon_mqtt/{schemas => data}/scenes-schema.yaml (100%) diff --git a/.gitignore b/.gitignore index 152393bb..fea00d74 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,5 @@ __pycache__ htmlcov # files created by tests -data/ -01.02.03.json \ No newline at end of file +/data/ +01.02.03.json diff --git a/insteon_mqtt/config.py b/insteon_mqtt/config.py index 748e7f8d..bacd5bbc 100644 --- a/insteon_mqtt/config.py +++ b/insteon_mqtt/config.py @@ -82,7 +82,7 @@ def validate_file(document, schema_file, name): """ basepath = os.path.dirname(__file__) schema = None - schema_file_path = os.path.join(basepath, 'schemas', schema_file) + schema_file_path = os.path.join(basepath, 'data', schema_file) with open(schema_file_path, "r") as f: schema = yaml.load(f, Loader=yaml.Loader) diff --git a/insteon_mqtt/schemas/config-schema.yaml b/insteon_mqtt/data/config-schema.yaml similarity index 100% rename from insteon_mqtt/schemas/config-schema.yaml rename to insteon_mqtt/data/config-schema.yaml diff --git a/insteon_mqtt/schemas/scenes-schema.yaml b/insteon_mqtt/data/scenes-schema.yaml similarity index 100% rename from insteon_mqtt/schemas/scenes-schema.yaml rename to insteon_mqtt/data/scenes-schema.yaml diff --git a/setup.py b/setup.py index 695cb36b..d566ce88 100644 --- a/setup.py +++ b/setup.py @@ -17,8 +17,8 @@ packages = setuptools.find_packages(exclude=["tests*"]), scripts = ['scripts/insteon-mqtt'], package_data = { - # include the schema files - "": ["schemas/*.yaml"], + # include the schema files and config-base.yaml + "": ["data/*.yaml"], }, install_requires = requirements, license = "GNU General Public License v3", From 1e43f213286160eefe5ce7b90e8d4aa3717c88e1 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 18 May 2021 13:30:19 -0700 Subject: [PATCH 24/44] Add Config Overlay Method --- insteon_mqtt/config.py | 51 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/insteon_mqtt/config.py b/insteon_mqtt/config.py index bacd5bbc..1346943f 100644 --- a/insteon_mqtt/config.py +++ b/insteon_mqtt/config.py @@ -125,14 +125,63 @@ def parse_validation_errors(errors, indent=0): def load(path): """Load the configuration file. + This will first load the base config and then overlay the user config + on top of this + Args: path: The file to load Returns: dict: Returns the configuration dictionary. """ + basepath = os.path.dirname(__file__) + base_config_path = os.path.join(basepath, 'data', 'config-base.yaml') + base_config = {} + user_config = {} + + with open(base_config_path, "r") as f: + base_config = yaml.load(f, Loader) + with open(path, "r") as f: - return yaml.load(f, Loader) + user_config = yaml.load(f, Loader) + + return overlay(base_config, user_config) + + +#=========================================================================== +def overlay(base_config, user_config): + """This overlays a user config file on top of the base config file + + This essentially merges the two yaml files into a single config using the + following rules + + 1. Any unique key found in user_config will be added to the base_config. + 2. Any value in user_config that __is not__ an instance of dict, will be + copied to the same key in base_config. + 3. Any value in user_config that __is__ a dict, will be recursively + examined applying these same rules. + + Returns: + (dict): the merged config + + """ + if isinstance(base_config, dict): + base_copy = base_config.copy() + for key in user_config: + if key not in base_copy: + # This is a new unique key, just push into base + base_copy[key] = user_config[key] + elif isinstance(user_config[key], dict): + # The value of the key in user is a dict, recursively process this + base_copy[key] = overlay(base_copy[key], user_config[key]) + else: + # The value of the key in user is not a dict, overwrite value in + # base + base_copy[key] = user_config[key] + else: + # base is not a dict, so push user value into base + base_copy = user_config + return base_copy #=========================================================================== From 5493942ce00a84eaa48e190e15b57aacf77ca2a4 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 18 May 2021 13:31:58 -0700 Subject: [PATCH 25/44] Move Config-Example to Config-Base --- config-example.yaml => insteon_mqtt/data/config-base.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename config-example.yaml => insteon_mqtt/data/config-base.yaml (100%) diff --git a/config-example.yaml b/insteon_mqtt/data/config-base.yaml similarity index 100% rename from config-example.yaml rename to insteon_mqtt/data/config-base.yaml From fc9cee51c97619244c51383cf6d44be1f4e72e2b Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 18 May 2021 14:12:10 -0700 Subject: [PATCH 26/44] Create Config-Example User File; Update Base Config File --- config-example.yaml | 181 +++++++++++++++++++++++++++++ insteon_mqtt/data/config-base.yaml | 87 +++++--------- 2 files changed, 211 insertions(+), 57 deletions(-) create mode 100644 config-example.yaml diff --git a/config-example.yaml b/config-example.yaml new file mode 100644 index 00000000..2da14c4e --- /dev/null +++ b/config-example.yaml @@ -0,0 +1,181 @@ +#========================================================================== +# +# Insteon <-> MQTT bridge configuration file. +# +# THIS IS JUST AN EXAMPLE FILE! +# +# At minimum, you need to make the following edits to use this as your +# config file: +# 1. Set the Modem port (or if using a hub hub_ip, hub_user, hub_password) +# 2. Remove the example devices +# 3. Add your insteon devices +# 4. Confirm your MQTT broker settings +# +# NOTE: the loader supports using a !include tag to load other as a +# item entry so you can do things like this: +# +# insteon: +# devices: !include devices.yaml +# +# Your config settings will be applied over the base settings. Any setting +# you do not define, will use the base values. You can view the base config +# in insteon_mqtt/data/config-base.yaml +# +#========================================================================== + +#========================================================================== +# +# Optional logging configuration (can also be set via the command line) +# +#========================================================================== +logging: + # 5=VERBOSE, 10=DEBUG, 20=INFO, 30=WARNING, 40=ERROR + # VERBOSE only adds logging of MQTT ping requests to DEBUG + #level: 40 + +#========================================================================== +# +# Insteon configuration +# +#========================================================================== +insteon: + ###### Modem + # You can use either a PLM Modem or an Insteon Hub, but not both. + + # PLM Modem option (Serial or USB) + port: '/dev/insteon' + + # Insteon Hub option + # See https://github.com/TD22057/insteon-mqtt/blob/master/docs/hub.md + # for a discussion of the details of using a Hub as your modem. + # If set to true, will use the Hub settings below and ignore the Serial + # settings above + use_hub: False + hub_ip: 192.168.1.1 + hub_user: username # Can be found on the underside of your hub + hub_password: password # Can be found on the underside of your hub + + ###### + + # Device database file storage location. + #storage: 'data' + + # Path to Scenes Definition file (Optional) + # The path can be specified either as an absolute path or as a relative path + # using the !rel_path directive. Where the path is relative to the + # config.yaml location + # + #scenes: /home/user/insteon_mqtt/scenes.yaml + #scenes: !rel_path scenes.yaml + + #------------------------------------------------------------------------ + # Devices require the Insteon hex address and an optional name. Note + # that MQTT address topics are always the lower case hex address or + # the input name depending on how they are configured below. + devices: + # On/off switch devices (outlets, wall switches, appliance modules, etc). + switch: + - 3a.29.84: 'xmas tree' + - 26.48.ff: 'xmas stairs' + - 37.2d.20: 'deck' + + # Dimming devices (outlets, wall switches, lamp modules, etc). + dimmer: + - 12.29.84: 'lamp2' + - 48.3d.46 + - 48.b0.ad: 'dim1' + + # Battery powered sensors (door, window). + battery_sensor: + - 94.a9.12 + + # Battery powered hidden door sensors + hidden_door: + - 32.a0.4b: 'back' + + # Battery powered motion sensors. + motion: + - 21.d6.d9: 'door' + + # Battery powered mini remotes. + mini_remote1: # Single Button Remotes + - 12.34.56: 'remote switch' + + mini_remote4: # Remotes with 4 Buttons + - 3f.12.d4: 'remote4' + + mini_remote8: # Remotes with 8 Buttons + - 3f.07.d4: 'remote1' + + # Smoke bridge module. + smoke_bridge: + - 44.a3.79: 'smoke alarm' + + # FanLinc fan controller (dimmer+fan). + fan_linc: + - 9a.a1.b3 + + # KeypadLinc dimmers (dimmer+scene controller). + keypad_linc: + - 46.7b.bc: 'porch' + + # KeypadLinc switches (on/off+scene controller). + keypad_linc_sw: + - 3c.42.9b: 'kp2' + + # Leak sensors. + leak: + - 21.d8.d9: 'bathroom' + + # IOLinc relay controllers + io_linc: + - 45.33.d4: 'garage' + + # On/off outlets + outlet: + - aa.11.cc: 'outlet' + + # Thermostatus + thermostat: + - aa.bb.cc: 'downstairs' + + # EZIO4O 4 output relay modules + ezio4o: + - 22.bb.cc: 'relays' + +#========================================================================== +# +# MQTT configuration +# +#========================================================================== +mqtt: + broker: 127.0.0.1 + #port: 1883 + # Optional broker login data. + #username: + #password: + + # Outbound messages configuration. Retain should generally be 1 + # so that the current state is available when someone subscribes. + #qos: 1 + #retain: 1 + + ### Discovery Settings + # + # Home Assistant implements mqtt device discovery as outlined at: + # https://www.home-assistant.io/docs/mqtt/discovery + # if discover_topic_base is defined, devices (as defined in config.yaml) + # announce themselves to Home Assistant. Announcing occurs once + # upon startup of insteon-mqtt and whenever HomeAssistant restart. + # + # The details of discovery_entities and how to define your own discovery + # templates can be found here: + # https://github.com/TD22057/insteon-mqtt/blob/master/docs/discovery.md + # + # Any additional variables that a specific device may offer are documented + # in the comments below under that device class. + # + # TO ENABLE THE DISCOVERY PLATFORM: Set the following to true + enable_discovery: false + +#---------------------------------------------------------------- diff --git a/insteon_mqtt/data/config-base.yaml b/insteon_mqtt/data/config-base.yaml index 13f5a227..018af7c7 100644 --- a/insteon_mqtt/data/config-base.yaml +++ b/insteon_mqtt/data/config-base.yaml @@ -1,21 +1,14 @@ #========================================================================== # -# Insteon <-> MQTT bridge configuration file. +# Insteon <-> MQTT bridge base configuration file. # -# THIS IS JUST AN EXAMPLE FILE! +# DO NOT EDIT THIS FILE!! # -# At minimum, you need to make the following edits to use this as your -# config file: -# 1. Set the Modem attributes, including port -# 2. Remove the example devices -# 3. Add your insteon devices -# 4. Confirm your MQTT broker settings +# This is the base configuration file. This file may be replaced or altered +# during an upgrade. # -# NOTE: the loader supports using a !include tag to load other as a -# item entry so you can do things like this: -# -# insteon: -# devices: !include devices.yaml +# To change the settings in the file, use a user config file. Any settings +# in that file will overwrite the settings here. # #========================================================================== @@ -27,7 +20,7 @@ logging: # 5=VERBOSE, 10=DEBUG, 20=INFO, 30=WARNING, 40=ERROR # VERBOSE only adds logging of MQTT ping requests to DEBUG - level: 10 + level: 40 # Print messages to the screen. #screen: False @@ -45,7 +38,7 @@ insteon: # You can use either a PLM Modem or an Insteon Hub, but not both. # PLM Modem option (Serial or USB) - port: '/dev/insteon' + #port: '/dev/insteon' #baudrate: 19200 # Insteon Hub option @@ -54,10 +47,10 @@ insteon: # If set to true, will use the Hub settings below and ignore the Serial # settings above use_hub: False - hub_ip: 192.168.1.1 + #hub_ip: 192.168.1.1 #hub_port: 25105 - hub_user: username # Can be found on the underside of your hub - hub_password: password # Can be found on the underside of your hub + #hub_user: username # Can be found on the underside of your hub + #hub_password: password # Can be found on the underside of your hub ###### @@ -88,74 +81,54 @@ insteon: # the input name depending on how they are configured below. devices: # On/off switch devices (outlets, wall switches, appliance modules, etc). - switch: - - 3a.29.84: 'xmas tree' - - 26.48.ff: 'xmas stairs' - - 37.2d.20: 'deck' + switch: [] # Dimming devices (outlets, wall switches, lamp modules, etc). - dimmer: - - 12.29.84: 'lamp2' - - 48.3d.46 - - 48.b0.ad: 'dim1' + dimmer: [] # Battery powered sensors (door, window). - battery_sensor: - - 94.a9.12 + battery_sensor: [] # Battery powered hidden door sensors - hidden_door: - - 32.a0.4b: 'back' + hidden_door: [] # Battery powered motion sensors. - motion: - - 21.d6.d9: 'door' + motion: [] # Battery powered mini remotes. - mini_remote1: # Single Button Remotes - - 12.34.56: 'remote switch' + mini_remote1: [] # Single Button Remotes + - mini_remote4: # Remotes with 4 Buttons - - 3f.12.d4: 'remote4' + mini_remote4: [] # Remotes with 4 Buttons - mini_remote8: # Remotes with 8 Buttons - - 3f.07.d4: 'remote1' + mini_remote8: [] # Remotes with 8 Buttons # Smoke bridge module. - smoke_bridge: - - 44.a3.79: 'smoke alarm' + smoke_bridge: [] # FanLinc fan controller (dimmer+fan). - fan_linc: - - 9a.a1.b3 + fan_linc: [] # KeypadLinc dimmers (dimmer+scene controller). - keypad_linc: - - 46.7b.bc: 'porch' + keypad_linc: [] # KeypadLinc switches (on/off+scene controller). - keypad_linc_sw: - - 3c.42.9b: 'kp2' + keypad_linc_sw: [] # Leak sensors. - leak: - - 21.d8.d9: 'bathroom' + leak: [] # IOLinc relay controllers - io_linc: - - 45.33.d4: 'garage' + io_linc: [] # On/off outlets - outlet: - - aa.11.cc: 'outlet' + outlet: [] # Thermostatus - thermostat: - - aa.bb.cc: 'downstairs' + thermostat: [] # EZIO4O 4 output relay modules - ezio4o: - - 22.bb.cc: 'relays' + ezio4o: [] #========================================================================== # @@ -163,7 +136,7 @@ insteon: # #========================================================================== mqtt: - broker: 127.0.0.1 + #broker: 127.0.0.1 port: 1883 # Optional broker login data. #username: From e45a8dd7d15441be64e7a3dff500775505ea41cf Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 18 May 2021 14:24:21 -0700 Subject: [PATCH 27/44] Update Config Example and Test Files to Comply with Schema Also load config file using load method so that overlay is called. --- config-example.yaml | 4 ++-- insteon_mqtt/config.py | 4 +--- tests/configs/good_hub.yaml | 4 ++++ 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/config-example.yaml b/config-example.yaml index 2da14c4e..35829c5c 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -31,7 +31,7 @@ logging: # 5=VERBOSE, 10=DEBUG, 20=INFO, 30=WARNING, 40=ERROR # VERBOSE only adds logging of MQTT ping requests to DEBUG - #level: 40 + level: 40 #========================================================================== # @@ -150,7 +150,7 @@ insteon: #========================================================================== mqtt: broker: 127.0.0.1 - #port: 1883 + port: 1883 # Optional broker login data. #username: #password: diff --git a/insteon_mqtt/config.py b/insteon_mqtt/config.py index 1346943f..4c5fa244 100644 --- a/insteon_mqtt/config.py +++ b/insteon_mqtt/config.py @@ -52,9 +52,7 @@ def validate(path): error = "" # Check the main config file first - document = None - with open(path, "r") as f: - document = yaml.load(f, Loader) + document = load(path) error += validate_file(document, 'config-schema.yaml', 'configuration') # Check the Scenes file diff --git a/tests/configs/good_hub.yaml b/tests/configs/good_hub.yaml index e18f3d5f..585d1ff3 100644 --- a/tests/configs/good_hub.yaml +++ b/tests/configs/good_hub.yaml @@ -3,3 +3,7 @@ insteon: hub_ip: 192.168.1.1 hub_user: username hub_password: password + +mqtt: + broker: 127.0.0.1 + port: 1883 From 4a8f9618737e5e219db9aa6a173d3eb983dd4109 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 18 May 2021 14:28:48 -0700 Subject: [PATCH 28/44] Fix Long Lines --- insteon_mqtt/config.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/insteon_mqtt/config.py b/insteon_mqtt/config.py index 4c5fa244..1355c14f 100644 --- a/insteon_mqtt/config.py +++ b/insteon_mqtt/config.py @@ -170,11 +170,10 @@ def overlay(base_config, user_config): # This is a new unique key, just push into base base_copy[key] = user_config[key] elif isinstance(user_config[key], dict): - # The value of the key in user is a dict, recursively process this + # The value of the key in user is a dict, recursively process base_copy[key] = overlay(base_copy[key], user_config[key]) else: - # The value of the key in user is not a dict, overwrite value in - # base + # The value of the key in user is not a dict, overwrite base base_copy[key] = user_config[key] else: # base is not a dict, so push user value into base From 1c32b6def2d694b32352db933fdb5fc5eee890d9 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 18 May 2021 14:45:55 -0700 Subject: [PATCH 29/44] Add Unit Tests for Overlay --- tests/test_config.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/test_config.py b/tests/test_config.py index c7bb1558..0f1da298 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -5,6 +5,7 @@ # pylint: disable=attribute-defined-outside-init #=========================================================================== import os +import yaml import pytest from unittest import mock from unittest.mock import call @@ -139,6 +140,31 @@ def test_validate_addr(self): validator._error.assert_called_once() validator._error.reset_mock() + #----------------------------------------------------------------------- + def test_overlay(self): + base = """ + root: + string: string + dict: + value1: value1 + value2: value2 + absent: here""" + user = """ + root: + string: new-string + dict: + value1: value1 + value2: + new-sub: value2 + new: new-key""" + config = IM.config.overlay(yaml.load(base, yaml.SafeLoader), + yaml.load(user, yaml.SafeLoader)) + assert config['root']['string'] == 'new-string' + assert config['root']['dict']['value1'] == 'value1' + assert config['root']['dict']['value2']['new-sub'] == 'value2' + assert config['root']['new'] == 'new-key' + assert config['absent'] == 'here' + #=========================================================================== class MockManager: def load_config(self, config): From 3950ee463d53bd15eeff4cd0d2121ca8ce04627b Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Tue, 18 May 2021 15:28:50 -0700 Subject: [PATCH 30/44] Update Documentation Given Config Overlay --- docs/configuration.md | 69 ++++++++++++++++++++++++++++++++++++++++++- docs/discovery.md | 7 +++-- docs/migrating.md | 61 ++++++++++---------------------------- 3 files changed, 88 insertions(+), 49 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 712461c3..5dd7cc29 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -12,13 +12,80 @@ The following is the bare minimum of changes to the `config.yaml` file that are ## Advanced Configuration - Generally not needed by most users +#### User Config Overlay of Base Config File + +Starting in version 1.0.0 InsteonMQTT now includes a base configuration file which the user configuration file is overlayed over the top. You should not directly edit the base configuration file, as it will be overwritten during updates. If you want to change a default setting, simply define the same key in your user configuration file and set your desired value. + +You can view the contents of the base configuration file here: +[config-base.yaml](https://github.com/TD22057/insteon-mqtt/blob/master/insteon_mqtt/data/config-base.yaml) + +Settings in your user config file will replace those in the base config file pursuant to the following rules: + +1. Any unique key found in user_config will be added to the base_config. +2. Any value in user_config that __is not__ an associative array, will be copied to the same key in base_config. +3. Any value in user_config that __is__ an associative array, will be recursively examined applying these same rules. + +So for example, assuming the following `base_config` and `user_config` files: + +**base_config:** +```YAML +insteon: + devices: + switch: [] + +mqtt: + broker: example.com + switch: + topic: some-topic + discovery_entities: + - component: switch + config: some-config + dimmer: + topic: some-topic2 + discovery_entities: + - component: dimmer + config: some-config +``` + +**user_config:** +```YAML +insteon: + devices: + switch: + - aa.bb.cc: my device + +mqtt: + broker: my.broker.com + switch: + topic: new_topic + dimmer: +``` + +The resulting config file will be: + +```YAML +insteon: + devices: + switch: + - aa.bb.cc: my device + +mqtt: + broker: my.broker.com + switch: + topic: new_topic + discovery_entities: + - component: switch + config: some-config + dimmer: +``` + #### `insteon` -> `storage` - device storage folder The device storage folder contains a json file for each device that is used to cache the various values on the device, most importantly it contains a cache of the device link database. Everything in this folder can be recreated by querying the device. There is no need to backup these files. Generally, most users should __not__ edit these files. #### `mqtt` - device templates -Insteon-MQTT uses Jinja2 templates for the greatest interoperability. Each device category `modem`, `modem`, `switch`, `dimmer`, ... uses templates for defining the mqtt topic and payload for each message type. See [Templating](templating.md) for help with Jinja2 templates. +Insteon-MQTT uses Jinja2 templates for the greatest interoperability. Each device category `modem`, `switch`, `dimmer`, ... uses templates for defining the mqtt topic and payload for each message type. See [Templating](templating.md) for help with Jinja2 templates. #### Device Specific Configuration Settings See [Device Specific Configuration Settings](config_extra.md) diff --git a/docs/discovery.md b/docs/discovery.md index b1f67b7b..f988e84f 100644 --- a/docs/discovery.md +++ b/docs/discovery.md @@ -41,8 +41,7 @@ mqtt: enable_discovery: true ``` -The config-example.yaml file that ships with InsteonMQTT contains the initial -templates for all Insteon devices. +The base configuration file that ships inside InsteonMQTT contains the initial templates for all Insteon devices. > __If you installed InsteonMQTT starting with version 0.8.3 or earlier__, you will need to read the [Migrating to Discovery](migrating.md) page for instructions on how to @@ -127,6 +126,8 @@ mqtt: # default ``` +If you review the contents of the base configuration file, under the `mqtt` section, you will see many examples of the `discovery_entities` setting. [config-base.yaml](https://github.com/TD22057/insteon-mqtt/blob/master/insteon_mqtt/data/config-base.yaml) + ### Using a Custom Device Template Each device can also define a distinct template for its discovery entities. @@ -279,6 +280,8 @@ relevant to the majority of your devices. This template can then be inserted into any of the `discovery_entities` by using the `device_info_template` variable. +You can view the default template in [config-base.yaml](https://github.com/TD22057/insteon-mqtt/blob/master/insteon_mqtt/data/config-base.yaml) + For example, the following a complex template that produces a nice device info: ```YAML diff --git a/docs/migrating.md b/docs/migrating.md index 702ea283..a20d88ad 100644 --- a/docs/migrating.md +++ b/docs/migrating.md @@ -1,18 +1,12 @@ # Migrating to Discovery for Installations 0.8.3 and Earlier -The current design of InsteonMQTT uses a single configuration file. Sadly -this means that when new additions are added to the configuration file, you -need to copy them from the sample configuration file into your own file. +Prior to version 1.0.0 InsteonMQTT used a single configuration file. Starting in version 1.0.0, the base configuration settings are contained in a base configuration file that ships with InsteonMQTT. You can view the contents of this file here: [config-base.yaml](https://github.com/TD22057/insteon-mqtt/blob/master/insteon_mqtt/data/config-base.yaml) -## Arguments Against Migrating +As described in [configuration](configuration.md), the settings in this base configuration file can be overwritten using your user configuration file. -Upgrading your config file to use the discovery platform and switching from -yaml defined entities in HomeAssistant o use the discovery platform will -require a little bit -of work. Depending on your installation, this could take __hours of work__. -So please consider whether this is worth it for you. +As a result, the easiest way to upgrade is to start a new `config.yaml` file as described below. However, before changing, consider if the Discovery Platform is worth it to you. -### Does Not Offer New InsteonMQTT Functionality +### Does Not In Itself Offer New InsteonMQTT Functionality The discovery platform is a __great feature for new users__. It allows them to define insteon devices once and get HomeAssistant entities with zero effort. @@ -29,57 +23,32 @@ through the `Configuration -> Integrations` page in HomeAssistant. This allows the user to change items using a graphical user interface, but all of the same items can be modified using yaml defintions as well. -### Upgrading Is Currently Time Consuming (Future releases should improve this) +### May Offer Access to Future InsteonMQTT Features or Fixes via Upgrades -The next minor release of InsteonMQTT intends to solve issues #383 and #391 -This should decrease the amount of copy and pasting that you have to do. -It is up to you, __but it may be easier to wait for the next minor release__ -before switching to the discovery platform. - -## Arguments for Migrating - -The discovery platform is a clear win for new users. - -For existing users, the only real benefit, is likely to be minor tweaks and -improvements to the HomeAssistant interaction. It is clear, that HomeAssistant -is heading away from the yaml configuration style and towards a more gui based -configuration. +Starting in version 1.0.0, now that the base config file is pushed as part of each upgrade, tweaks or fixes to the templates can be sent directly to you. Depending on your personality, this may be a good or a bad thing. # How to Migrate to the Discovery Platform As noted, this could take some time, it isn't really something that you can do in steps, so be sure you have enough time set aside. -1. Make a backup copy of your InsteonMQTT config.yaml file. +1. Move your InsteonMQTT config.yaml file to config-backup.yaml. 2. Make a backup copy of all HomeAssistant configurations that define insteon entities. -3. Copy the discovery settings in the `mqtt` key from the config-example.yaml -file. Specifically, the `enable_discovery`, `discovery_topic_base`, `discovery_ha_status` and `device_info_template` keys. -4. Under each of the device subkeys (e.g. `modem`, `switch`, `dimmer` ...) copy -the `discovery_entities` from the config-example.yaml into your config file. - ->The above steps can be completed without affecting your installation. The -following steps make changes that must either be completed or reverted to -enable things to work. - -5. Check to see if any of your `*_payload` entries differs from the suggested -entry defined in the config-example.yaml. The best way to do this is using a -diff tool. If they are different, either update your `*_payload` defintion, or -amend the `discovery_entities` as necessary. For example, if your -`state_payload` generates a json payload, the `discovery_entities` needs to be -defined to expect a json payload. -6.Remove or comment out the inston entities in your HomeAssistant +3. Rename `config-yaml.default` to `config.yaml`. +4. Follow the insstructions [Configuration Instructions](https://github.com/TD22057/insteon-mqtt/blob/master/docs/configuration.md) copying the details of your modem, devices, and mqtt broker from your backup file. +5.Remove or comment out the insteon entities in your HomeAssistant configuration. -7. Restart HomeAssistant (your front end will likely be filled with yellow triangles). -8. Make sure `enable_discovery` is set to `true` in your InsteonMQTT config. -9. Restart InsteonMQTT. -10. Using `Configuration -> Integrations` in HomeAssistant rename and adjust +6. Restart HomeAssistant (your front end will likely be filled with yellow triangles). +7. Make sure `enable_discovery` is set to `true` in your InsteonMQTT config. +8. Restart InsteonMQTT. +9. Using `Configuration -> Integrations` in HomeAssistant rename and adjust the entity ID of the discovered insteon entities to match your prior installation. You can hover over the yellow triangles in your fron end to see the missing Entity IDs. Once the Entity ID has been fixed, the yellow triangle will go away. You can also review your old insteon entity defintions one by one to verify that your entities have been created and are correctly identified. -11. Check the HomeAssistant log and the InsteonMQTT log for any errors. +10. Check the HomeAssistant log and the InsteonMQTT log for any errors. > If you make changes to your InsteonMQTT config, you will need to restart InsteonMQTT for them to take effect. It seems like in some cases, you may From cefb771eab684a662c9b57eeb0e0154e77d42736 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 19 May 2021 13:00:52 -0700 Subject: [PATCH 31/44] Add Back in Logging Settings; Update Hassio Entrypoint --- config-example.yaml | 5 +++++ hassio/entrypoint.sh | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/config-example.yaml b/config-example.yaml index 35829c5c..1a958ac1 100644 --- a/config-example.yaml +++ b/config-example.yaml @@ -33,6 +33,11 @@ logging: # VERBOSE only adds logging of MQTT ping requests to DEBUG level: 40 + # Print messages to the screen. + #screen: False + + # Print messages to a file. + #file: /var/log/insteon_mqtt.log #========================================================================== # # Insteon configuration diff --git a/hassio/entrypoint.sh b/hassio/entrypoint.sh index 3670d211..6d242293 100755 --- a/hassio/entrypoint.sh +++ b/hassio/entrypoint.sh @@ -7,8 +7,8 @@ if [ ! -f /config/insteon-mqtt/config.yaml ]; then echo "Copying default config.yaml" /bin/cp /config/insteon-mqtt/config.yaml.default /config/insteon-mqtt/config.yaml - sed -i "s/storage: 'data'/storage: '\/config\/insteon-mqtt\/data'/" /config/insteon-mqtt/config.yaml - sed -i "s/file: \/var\/log\/insteon_mqtt.log/file: \/config\/insteon-mqtt\/insteon_mqtt.log/" /config/insteon-mqtt/config.yaml + sed -i "s/#storage: 'data'/storage: '\/config\/insteon-mqtt\/data'/" /config/insteon-mqtt/config.yaml + sed -i "s/#file: \/var\/log\/insteon_mqtt.log/file: \/config\/insteon-mqtt\/insteon_mqtt.log/" /config/insteon-mqtt/config.yaml fi python3 /opt/insteon-mqtt/hassio/start.py /config/insteon-mqtt/config.yaml start From a68896eb61261ad0d73644d0e87dd57460c853d2 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 19 May 2021 13:13:11 -0700 Subject: [PATCH 32/44] Add Message to Hassio Log on First Startup; Don't Try to Run Don't try to actually start the program if we just created the initial config.yaml file, it is only going to error out. --- hassio/entrypoint.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hassio/entrypoint.sh b/hassio/entrypoint.sh index 6d242293..8c047ba3 100755 --- a/hassio/entrypoint.sh +++ b/hassio/entrypoint.sh @@ -5,10 +5,13 @@ /bin/cp /opt/insteon-mqtt/config-example.yaml /config/insteon-mqtt/config.yaml.default if [ ! -f /config/insteon-mqtt/config.yaml ]; then - echo "Copying default config.yaml" + echo "Welcome to InsteonMQTT!" + echo "Creating your initial config.yaml file." /bin/cp /config/insteon-mqtt/config.yaml.default /config/insteon-mqtt/config.yaml sed -i "s/#storage: 'data'/storage: '\/config\/insteon-mqtt\/data'/" /config/insteon-mqtt/config.yaml sed -i "s/#file: \/var\/log\/insteon_mqtt.log/file: \/config\/insteon-mqtt\/insteon_mqtt.log/" /config/insteon-mqtt/config.yaml + echo "Please edit the file /config/insteon_mqtt/config.yaml" + echo "Then you can start InsteonMQTT." +else + python3 /opt/insteon-mqtt/hassio/start.py /config/insteon-mqtt/config.yaml start fi - -python3 /opt/insteon-mqtt/hassio/start.py /config/insteon-mqtt/config.yaml start From 48251fb31fa78b9c06100eb13037ca71142aab24 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 19 May 2021 13:35:55 -0700 Subject: [PATCH 33/44] Update Changelog --- CHANGELOG.md | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c24f6dc..a6b0e81b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,16 @@ # Revision Change History -## [0.9.3] +## [1.0.0] -## Fixes +With the addition of the Discovery Platform in the last minor release, the improved config file design added by this release, and the numerous other additions and fixes added by everyone, it seemed about time to call this a version 1 product. 🎉 🎉 🎉 + +### Additions + +- There is now a base config file that will be updated as part of the distribution. Now, the only settings you need to define in your config.yaml, are those that are unique to your installation, or those settings you wish to override. For most users, you config.yaml file can now be 10x smaller! ([PR 414][P414]) +- Adds Resume_Dim feature. For dimmable devices when this flag is enabled, when the device is turned on from its button, it will resume the same brightness level it previously had before being turned off. Currently only works on i2CS devices, will be extended to i2 devices in the future. Thanks @lnr0626 ([PR 411][P411]) +- Adds the ability to send arbitrary insteon messages to a device for development purposes. This is a huge win for development. Thanks @lnr0626 ([PR 413][P413]) + +### Fixes - Fix failure to create initial `config.yaml` file in HomeAssistant Supervisor installation. Thanks @lnr0626 ([PR 406][P406]) @@ -14,12 +22,12 @@ ## [0.9.2] -## Hotfix +### Hotfix - Allows the use of domain names for the broker and hub addresses. I created a - bug in 0.9.1, my fault for thinking of this. ([PR 404][P404]) + bug in 0.9.1, my fault for not thinking of this. ([PR 404][P404]) -## Fixes +### Fixes - Adjusts the docker build script to one that works. ([PR 402][P402]) @@ -757,3 +765,6 @@ will add new features. [P406]: https://github.com/TD22057/insteon-mqtt/pull/406 [P407]: https://github.com/TD22057/insteon-mqtt/pull/407 [P410]: https://github.com/TD22057/insteon-mqtt/pull/410 +[P411]: https://github.com/TD22057/insteon-mqtt/pull/411 +[P413]: https://github.com/TD22057/insteon-mqtt/pull/413 +[P414]: https://github.com/TD22057/insteon-mqtt/pull/414 From 71d4de446b15fb3f9d19a57029c62027419ed17d Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 19 May 2021 14:24:21 -0700 Subject: [PATCH 34/44] Change State Payloads to JSON; Update Discovery Entities This changes the default payload for most of the state topics from plain string payloads to json payloads. This allows the additional attributes such as mode, reason, timestap... to be passed with the state as well. --- insteon_mqtt/data/config-base.yaml | 350 ++++++++++++++++++++++++----- 1 file changed, 294 insertions(+), 56 deletions(-) diff --git a/insteon_mqtt/data/config-base.yaml b/insteon_mqtt/data/config-base.yaml index 018af7c7..2d619c45 100644 --- a/insteon_mqtt/data/config-base.yaml +++ b/insteon_mqtt/data/config-base.yaml @@ -315,7 +315,11 @@ mqtt: # instant = 0/1 # reason = 'device'/'scene'/'command'/'refresh'/'...' state_topic: 'insteon/{{address}}/state' - state_payload: '{{on_str.upper()}}' + state_payload: >- + { "state" : "{{on_str.upper()}}", + "mode" : "{{mode.upper()}}", + "timestamp" : {{timestamp}}, + "reason" : "{{reason}}" } # Manual mode (holding down a button) is triggered once when the button # is held and once when it's released. Available variables for @@ -337,7 +341,12 @@ mqtt: # json = the input payload converted to json. Use json.VAR to extract # a variable from a json payload. on_off_topic: 'insteon/{{address}}/set' - on_off_payload: '{ "cmd" : "{{value.lower()}}" }' + on_off_payload: > + { "cmd" : {%- if json is defined and json -%} + "{{json.state.lower()}}" + {%- else -%} + "{{value.lower()}}" + {%- endif -%}} # Scene on/off command. This triggers the scene broadcast on the switch # in the same way clicking the button would. The inputs are the same as @@ -358,7 +367,12 @@ mqtt: "name": "{{name_user_case}}", "cmd_t": "{{on_off_topic}}", "stat_t": "{{state_topic}}", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "json_attr_t": "{{state_topic}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } #------------------------------------------------------------------------ @@ -393,8 +407,12 @@ mqtt: # instant = 0/1 # reason = 'device'/'scene'/'command'/'refresh'/'...' state_topic: 'insteon/{{address}}/state' - state_payload: > - { "state" : "{{on_str.upper()}}", "brightness" : {{level_255}} } + state_payload: >- + { "state" : "{{on_str.upper()}}", + "brightness" : {{level_255}}, + "mode" : "{{mode.upper()}}", + "timestamp" : {{timestamp}}, + "reason" : "{{reason}}" } # Manual mode (holding down a button) is triggered once when the button # is held and once when it's released. Available variables for @@ -477,7 +495,11 @@ mqtt: "stat_t": "{{state_topic}}", "brightness": true, "schema": "json", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "json_attr_t": "{{state_topic}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}" } #------------------------------------------------------------------------ @@ -507,7 +529,11 @@ mqtt: # on = 0/1 # on_str = 'off'/'on' state_topic: 'insteon/{{address}}/state' - state_payload: '{{on_str.upper()}}' + state_payload: >- + { "state" : "{{on_str.upper()}}", + "mode" : "{{mode.upper()}}", + "timestamp" : {{timestamp}}, + "reason" : "{{reason}}" } # Output low battery topic and payload. This message is sent # whenever the device detects a low battery. Available variables @@ -518,7 +544,9 @@ mqtt: # is_low = 0/1 # is_low_str = 'off'/'on' low_battery_topic: 'insteon/{{address}}/battery' - low_battery_payload: '{{is_low_str.upper()}}' + low_battery_payload: >- + { "state" : "{{is_low_str.upper()}}", + "timestamp" : {{timestamp}} } # Output heartbeat topic and payload. This message is sent # every 24 hours. Available variables for templating are: @@ -544,7 +572,13 @@ mqtt: "name": "{{name_user_case}} door", "stat_t": "{{state_topic}}", "device_class": "door", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "force_update": true, + "json_attr_t": "{{state_topic}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'binary_sensor' config: |- @@ -553,7 +587,13 @@ mqtt: "name": "{{name_user_case}} battery", "stat_t": "{{low_battery_topic}}", "device_class": "battery", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "force_update": true, + "json_attr_t": "{{low_battery_topic}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}} } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'sensor' config: |- @@ -562,7 +602,8 @@ mqtt: "name": "{{name_user_case}} heartbeat", "stat_t": "{{heartbeat_topic}}", "device_class": "timestamp", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "force_update": true } #------------------------------------------------------------------------ @@ -592,7 +633,9 @@ mqtt: # is_dusk_str = 'off'/'on' # state = 'dawn'/'dusk' dawn_dusk_topic: 'insteon/{{address}}/dawn' - dawn_dusk_payload: '{{is_dawn_str.upper()}}' + dawn_dusk_payload: >- + { "state" : "{{is_dawn_str.upper()}}", + "timestamp" : {{timestamp}} } # Discovery Entities - Used as part of HomeAssistant MQTT Discovery discovery_entities: @@ -603,7 +646,13 @@ mqtt: "name": "{{name_user_case}} motion", "stat_t": "{{state_topic}}", "device_class": "motion", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "force_update": true, + "json_attr_t": "{{state_topic}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'binary_sensor' config: |- @@ -612,7 +661,13 @@ mqtt: "name": "{{name_user_case}} battery", "stat_t": "{{low_battery_topic}}", "device_class": "battery", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "force_update": true, + "json_attr_t": "{{low_battery_topic}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}} } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'binary_sensor' config: |- @@ -621,7 +676,13 @@ mqtt: "name": "{{name_user_case}} dusk", "stat_t": "{{dawn_dusk_topic}}", "device_class": "light", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "force_update": true, + "json_attr_t": "{{dawn_dusk_topic}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}} } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } #------------------------------------------------------------------------ @@ -649,7 +710,9 @@ mqtt: # batt_volt = raw insteon voltage level battery_voltage_topic: 'insteon/{{address}}/battery_voltage' - battery_voltage_payload: '{"voltage" : {{batt_volt}}}' + battery_voltage_payload: >- + { "voltage" : {{batt_volt}}, + "timestamp" : {{timestamp}} } # Discovery Entities - Used as part of HomeAssistant MQTT Discovery discovery_entities: @@ -660,7 +723,13 @@ mqtt: "name": "{{name_user_case}} door", "stat_t": "{{state_topic}}", "device_class": "door", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "force_update": true, + "json_attr_t": "{{state_topic}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'binary_sensor' config: |- @@ -669,7 +738,13 @@ mqtt: "name": "{{name_user_case}} battery", "stat_t": "{{low_battery_topic}}", "device_class": "battery", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "force_update": true, + "json_attr_t": "{{low_battery_topic}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}} } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'sensor' config: |- @@ -678,7 +753,8 @@ mqtt: "name": "{{name_user_case}} heartbeat", "stat_t": "{{heartbeat_topic}}", "device_class": "timestamp", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "force_update": true } - component: 'sensor' config: |- @@ -687,7 +763,13 @@ mqtt: "name": "{{name_user_case}} voltage", "stat_t": "{{battery_voltage_topic}}", "device_class": "voltage", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "force_update": true, + "json_attr_t": "{{battery_voltage_topic}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}} } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.voltage}}{% endraw %}" } #------------------------------------------------------------------------ @@ -724,7 +806,9 @@ mqtt: # is_dry_str = 'off'/'on' # state = 'wet'/'dry' wet_dry_topic: 'insteon/{{address}}/wet' - wet_dry_payload: '{{is_wet_str.upper()}}' + wet_dry_payload: >- + { "state" : "{{is_wet_str.upper()}}", + "timestamp" : {{timestamp}} } # Discovery Entities - Used as part of HomeAssistant MQTT Discovery discovery_entities: @@ -735,7 +819,13 @@ mqtt: "name": "{{name_user_case}} leak", "stat_t": "{{wet_dry_topic}}", "device_class": "moisture", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "force_update": true, + "json_attr_t": "{{wet_dry_topic}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}} } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'sensor' config: |- @@ -769,7 +859,11 @@ mqtt: # fast = 0/1 # instant = 0/1 state_topic: 'insteon/{{address}}/state/{{button}}' - state_payload: '{{on_str.upper()}}' + state_payload: >- + { "state" : "{{on_str.upper()}}", + "mode" : "{{mode.upper()}}", + "timestamp" : {{timestamp}}, + "reason" : "{{reason}}" } # Manual mode (holding down a button) is triggered once when the button # is held and once when it's released. Available variables for @@ -801,7 +895,13 @@ mqtt: "uniq_id": "{{address}}_1", "name": "{{name_user_case}} btn 1", "stat_t": "{{state_topic_1}}", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "force_update": true, + "json_attr_t": "{{state_topic_1}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'binary_sensor' config: |- @@ -809,7 +909,13 @@ mqtt: "uniq_id": "{{address}}_2", "name": "{{name_user_case}} btn 2", "stat_t": "{{state_topic_2}}", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "force_update": true, + "json_attr_t": "{{state_topic_2}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'binary_sensor' config: |- @@ -817,7 +923,13 @@ mqtt: "uniq_id": "{{address}}_3", "name": "{{name_user_case}} btn 3", "stat_t": "{{state_topic_3}}", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "force_update": true, + "json_attr_t": "{{state_topic_3}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'binary_sensor' config: |- @@ -825,7 +937,13 @@ mqtt: "uniq_id": "{{address}}_4", "name": "{{name_user_case}} btn 4", "stat_t": "{{state_topic_4}}", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "force_update": true, + "json_attr_t": "{{state_topic_4}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'binary_sensor' config: |- @@ -833,7 +951,13 @@ mqtt: "uniq_id": "{{address}}_5", "name": "{{name_user_case}} btn 5", "stat_t": "{{state_topic_5}}", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "force_update": true, + "json_attr_t": "{{state_topic_5}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'binary_sensor' config: |- @@ -841,7 +965,11 @@ mqtt: "uniq_id": "{{address}}_6", "name": "{{name_user_case}} btn 6", "stat_t": "{{state_topic_6}}", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'binary_sensor' config: |- @@ -849,7 +977,11 @@ mqtt: "uniq_id": "{{address}}_7", "name": "{{name_user_case}} btn 7", "stat_t": "{{state_topic_7}}", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'binary_sensor' config: |- @@ -857,7 +989,11 @@ mqtt: "uniq_id": "{{address}}_8", "name": "{{name_user_case}} btn 8", "stat_t": "{{state_topic_8}}", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'binary_sensor' config: |- @@ -866,7 +1002,13 @@ mqtt: "name": "{{name_user_case}} battery", "stat_t": "{{low_battery_topic}}", "device_class": "battery", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "force_update": true, + "json_attr_t": "{{low_battery_topic}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}} } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } #------------------------------------------------------------------------ @@ -1137,7 +1279,11 @@ mqtt: # level_str = 'off'/'low'/'medium'/'high' # reason = 'device'/'scene'/'command'/'refresh'/'...' fan_state_topic: 'insteon/{{address}}/fan/state' - fan_state_payload: '{{on_str.upper()}}' + fan_state_payload: >- + { "state" : "{{on_str.upper()}}", + "level" : {{level_str}}, + "timestamp" : {{timestamp}}, + "reason" : "{{reason}}" } # Fan on/off command. Similar functionality to the cmd_topic but only # for turning the device on and off. If reason is input, is will be @@ -1189,10 +1335,11 @@ mqtt: "cmd_t": "{{fan_on_off_topic}}", "pct_cmd_t": "{{fan_speed_set_topic}}", "pct_cmd_tpl": "{% raw %}{% if value < 10 %}off{% elif value < 40 %}low{% elif value < 75 %}medium{% else %}high{% endif %}{% endraw %}", - "pct_stat_t": "{{fan_speed_topic}}", - "pct_val_tpl": "{% raw %}{% if value == 'low' %}33{% elif value == 'medium' %}67{% elif value == 'high' %}100{% else %}0{% endif %}{% endraw %}", - "pr_mode_stat_t": "{{fan_speed_topic}}", + "pct_stat_t": "{{fan_state_topic}}", + "pct_val_tpl": "{% raw %}{% if value_json.level == 'low' %}33{% elif value_json.level == 'medium' %}67{% elif value_json.level == 'high' %}100{% else %}0{% endif %}{% endraw %}", + "pr_mode_stat_t": "{{fan_state_topic}}", "pr_mode_cmd_t": "{{fan_speed_set_topic}}", + "pr_mode_val_tpl": "{% raw %}{{value_json.level}}{% endraw %}", "pr_modes": ["off", "low", "medium", "high"] } - component: 'light' @@ -1204,7 +1351,11 @@ mqtt: "stat_t": "{{state_topic}}", "brightness": true, "schema": "json", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "json_attr_t": "{{state_topic}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}" } #------------------------------------------------------------------------ @@ -1260,7 +1411,11 @@ mqtt: # instant = 0/1 # reason = 'device'/'scene'/'command'/'refresh'/'...' btn_state_topic: 'insteon/{{address}}/state/{{button}}' - btn_state_payload: '{{on_str.upper()}}' + btn_state_payload: >- + { "state" : "{{on_str.upper()}}", + "mode" : "{{mode.upper()}}", + "timestamp" : {{timestamp}}, + "reason" : "{{reason}}" } # Dimmer output state change topic and payload for button 1. This # message is sent whenever the device dimmer state changes for any @@ -1277,8 +1432,12 @@ mqtt: # instant = 0/1 # reason = 'device'/'scene'/'command'/'refresh'/'...' dimmer_state_topic: 'insteon/{{address}}/state/1' - dimmer_state_payload: > - { "state" : "{{on_str.upper()}}", "brightness" : {{level_255}} } + dimmer_state_payload: >- + { "state" : "{{on_str.upper()}}", + "brightness" : {{level_255}}, + "mode" : "{{mode.upper()}}", + "timestamp" : {{timestamp}}, + "reason" : "{{reason}}" } # Manual mode (holding down a button) is triggered once when the button # is held and once when it's released. Available variables for @@ -1302,7 +1461,12 @@ mqtt: # json = the input payload converted to json. Use json.VAR to extract # a variable from a json payload. btn_on_off_topic: 'insteon/{{address}}/set/{{button}}' - btn_on_off_payload: '{ "cmd" : "{{value.lower()}}" }' + btn_on_off_payload: >- + { "cmd" : {%- if json is defined and json -%} + "{{json.state.lower()}}" + {%- else -%} + "{{value.lower()}}" + {%- endif -%}} # Input dimming on/off command for button 1. Similar functionality to # the cmd_topic but only for turning the group 1 on and off and setting @@ -1387,7 +1551,15 @@ mqtt: {{dimmer_state_topic}} {%- else -%} {{btn_state_topic_1}} - {%- endif -%}" + {%- endif -%}", + "json_attr_t": "{%- if is_dimmable -%} + {{dimmer_state_topic}} + {%- else -%} + {{btn_state_topic_1}} + {%- endif -%}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}" } - component: 'switch' config: |- @@ -1396,7 +1568,12 @@ mqtt: "name": "{{name_user_case}} btn 2", "device": {{device_info_template}}, "cmd_t": "{{btn_on_off_topic_2}}", - "stat_t": "{{btn_state_topic_2}}" + "stat_t": "{{btn_state_topic_2}}", + "json_attr_t": "{{btn_state_topic_2}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'switch' config: |- @@ -1405,7 +1582,12 @@ mqtt: "name": "{{name_user_case}} btn 3", "device": {{device_info_template}}, "cmd_t": "{{btn_on_off_topic_3}}", - "stat_t": "{{btn_state_topic_3}}" + "stat_t": "{{btn_state_topic_3}}", + "json_attr_t": "{{btn_state_topic_3}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'switch' config: |- @@ -1414,7 +1596,12 @@ mqtt: "name": "{{name_user_case}} btn 4", "device": {{device_info_template}}, "cmd_t": "{{btn_on_off_topic_4}}", - "stat_t": "{{btn_state_topic_4}}" + "stat_t": "{{btn_state_topic_4}}", + "json_attr_t": "{{btn_state_topic_4}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'switch' config: |- @@ -1423,7 +1610,12 @@ mqtt: "name": "{{name_user_case}} btn 5", "device": {{device_info_template}}, "cmd_t": "{{btn_on_off_topic_5}}", - "stat_t": "{{btn_state_topic_5}}" + "stat_t": "{{btn_state_topic_5}}", + "json_attr_t": "{{btn_state_topic_5}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'switch' config: |- @@ -1432,7 +1624,12 @@ mqtt: "name": "{{name_user_case}} btn 6", "device": {{device_info_template}}, "cmd_t": "{{btn_on_off_topic_6}}", - "stat_t": "{{btn_state_topic_6}}" + "stat_t": "{{btn_state_topic_6}}", + "json_attr_t": "{{btn_state_topic_6}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'switch' config: |- @@ -1441,7 +1638,12 @@ mqtt: "name": "{{name_user_case}} btn 7", "device": {{device_info_template}}, "cmd_t": "{{btn_on_off_topic_7}}", - "stat_t": "{{btn_state_topic_7}}" + "stat_t": "{{btn_state_topic_7}}", + "json_attr_t": "{{btn_state_topic_7}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'switch' config: |- @@ -1450,7 +1652,12 @@ mqtt: "name": "{{name_user_case}} btn 8", "device": {{device_info_template}}, "cmd_t": "{{btn_on_off_topic_8}}", - "stat_t": "{{btn_state_topic_8}}" + "stat_t": "{{btn_state_topic_8}}", + "json_attr_t": "{{btn_state_topic_8}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'switch' config: |- @@ -1459,7 +1666,12 @@ mqtt: "name": "{{name_user_case}} btn 9", "device": {{device_info_template}}, "cmd_t": "{{btn_on_off_topic_9}}", - "stat_t": "{{btn_state_topic_9}}" + "stat_t": "{btn_state_topic_9}}", + "json_attr_t": "{{btn_state_topic_9}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } #------------------------------------------------------------------------ @@ -1503,7 +1715,12 @@ mqtt: # sensor_on_str = 'off'/'on' # relay_on_str = 'off'/'on' state_topic: 'insteon/{{address}}/state' - state_payload: '{ "sensor" : "{{sensor_on_str.lower()}}", "relay" : {{relay_on_str.lower()}} }' + state_payload: >- + { "sensor" : "{{sensor_on_str.lower()}}", + "relay" : "{{relay_on_str.lower()}}", + "mode" : "{{mode.upper()}}", + "timestamp" : {{timestamp}}, + "reason" : "{{reason}}" } # Output relay state change topic and template. This message is sent # whenever the device relay state changes. Available variables for @@ -1549,7 +1766,11 @@ mqtt: "name": "{{name_user_case}} relay", "cmd_t": "{{on_off_topic}}", "stat_t": "{{relay_state_topic}}", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "json_attr_t": "{{state_topic}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}" } - component: 'binary_sensor' config: |- @@ -1557,7 +1778,11 @@ mqtt: "uniq_id": "{{address}}_sensor", "name": "{{name_user_case}} sensor", "stat_t": "{{sensor_state_topic}}", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "json_attr_t": "{{state_topic}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}" } #------------------------------------------------------------------------ @@ -1591,7 +1816,11 @@ mqtt: # instant = 0/1 # reason = 'device'/'scene'/'command'/'refresh'/'...' state_topic: 'insteon/{{address}}/state/{{button}}' - state_payload: '{{on_str.upper()}}' + state_payload: >- + { "state" : "{{on_str.upper()}}", + "mode" : "{{mode.upper()}}", + "timestamp" : {{timestamp}}, + "reason" : "{{reason}}" } # Input on/off command. Similar functionality to the cmd_topic but only # for turning the device on and off. If reason is input, is will be @@ -1628,7 +1857,12 @@ mqtt: "name": "{{name_user_case}} top", "cmd_t": "{{on_off_topic_1}}", "stat_t": "{{state_topic_1}}", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "json_attr_t": "{{state_topic_1}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } - component: 'switch' config: |- @@ -1637,7 +1871,11 @@ mqtt: "name": "{{name_user_case}} bottom", "cmd_t": "{{on_off_topic_2}}", "stat_t": "{{state_topic_2}}", - "device": {{device_info_template}} + "device": {{device_info_template}}, + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"mode\" : \"{{value_json.mode}}\", \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}", + "val_tpl": "{% raw %}{{value_json.state}}{% endraw %}" } #------------------------------------------------------------------------ From c73568d4663e7dc7beb11a3662371e51bc678fd4 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 19 May 2021 14:37:59 -0700 Subject: [PATCH 35/44] Adjust Fan discovery Template It doesn't support json payloads for speed or presets. --- insteon_mqtt/data/config-base.yaml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/insteon_mqtt/data/config-base.yaml b/insteon_mqtt/data/config-base.yaml index 2d619c45..e263fa54 100644 --- a/insteon_mqtt/data/config-base.yaml +++ b/insteon_mqtt/data/config-base.yaml @@ -1335,12 +1335,15 @@ mqtt: "cmd_t": "{{fan_on_off_topic}}", "pct_cmd_t": "{{fan_speed_set_topic}}", "pct_cmd_tpl": "{% raw %}{% if value < 10 %}off{% elif value < 40 %}low{% elif value < 75 %}medium{% else %}high{% endif %}{% endraw %}", - "pct_stat_t": "{{fan_state_topic}}", - "pct_val_tpl": "{% raw %}{% if value_json.level == 'low' %}33{% elif value_json.level == 'medium' %}67{% elif value_json.level == 'high' %}100{% else %}0{% endif %}{% endraw %}", - "pr_mode_stat_t": "{{fan_state_topic}}", + "pct_stat_t": "{{fan_speed_topic}}", + "pct_val_tpl": "{% raw %}{% if value == 'low' %}33{% elif value == 'medium' %}67{% elif value == 'high' %}100{% else %}0{% endif %}{% endraw %}", + "pr_mode_stat_t": "{{fan_speed_topic}}", "pr_mode_cmd_t": "{{fan_speed_set_topic}}", - "pr_mode_val_tpl": "{% raw %}{{value_json.level}}{% endraw %}", - "pr_modes": ["off", "low", "medium", "high"] + "pr_modes": ["off", "low", "medium", "high"], + "json_attr_t": "{{fan_state_topic}}", + "json_attr_tpl": "{%- raw -%} + { \"timestamp\" : {{value_json.timestamp}}, \"reason\" : \"{{value_json.reason}}\" } + {%- endraw -%}" } - component: 'light' config: |- From 545024b22a68a5d1e012f25a3d8ab5418649feba Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 19 May 2021 14:42:42 -0700 Subject: [PATCH 36/44] JSON Double Quotes Around String --- insteon_mqtt/data/config-base.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/insteon_mqtt/data/config-base.yaml b/insteon_mqtt/data/config-base.yaml index e263fa54..9ba2c9c9 100644 --- a/insteon_mqtt/data/config-base.yaml +++ b/insteon_mqtt/data/config-base.yaml @@ -1281,7 +1281,7 @@ mqtt: fan_state_topic: 'insteon/{{address}}/fan/state' fan_state_payload: >- { "state" : "{{on_str.upper()}}", - "level" : {{level_str}}, + "level" : "{{level_str}}", "timestamp" : {{timestamp}}, "reason" : "{{reason}}" } From 5bcfcb0214d25a4bac2b7649f6a3e0a47bf30cbd Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Wed, 19 May 2021 14:59:06 -0700 Subject: [PATCH 37/44] Update Unit Tests --- tests/test_Integration.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/test_Integration.py b/tests/test_Integration.py index a43426b0..b3c0ef0d 100644 --- a/tests/test_Integration.py +++ b/tests/test_Integration.py @@ -40,7 +40,10 @@ def test_set_on_functions(stack): stack.write_to_modem('02623a29840f11ff06') # Return the device ACK stack.write_to_modem('02503a298441eee62b11ff') - assert stack.published_topics['insteon/3a.29.84/state'] == 'ON' + response = json.loads(stack.published_topics['insteon/3a.29.84/state']) + assert response['state'] == 'ON' + assert response['mode'] == 'NORMAL' + assert response['reason'] == 'command' #----------------------- # Test the level command @@ -52,8 +55,9 @@ def test_set_on_functions(stack): stack.write_to_modem('02621229840f117f06') # Return the device ACK 1229840f117f06 stack.write_to_modem('025012298441eee62b117f') - assert (stack.published_topics['insteon/12.29.84/state'] == - '{ "state" : "ON", "brightness" : 127 }') + response = json.loads(stack.published_topics['insteon/12.29.84/state']) + assert response['state'] == 'ON' + assert response['brightness'] == 127 # =============================================================== From 53940bcefc1212600fdb9d96b60c128fa9eb317b Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 20 May 2021 11:18:17 -0700 Subject: [PATCH 38/44] Fix a Few Typos in Base Config Should be State not Cmd --- insteon_mqtt/data/config-base.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/insteon_mqtt/data/config-base.yaml b/insteon_mqtt/data/config-base.yaml index 9ba2c9c9..2c61bb1d 100644 --- a/insteon_mqtt/data/config-base.yaml +++ b/insteon_mqtt/data/config-base.yaml @@ -282,8 +282,8 @@ mqtt: {%- endif -%}", "cmd_t": "{{scene_topic}}", "device": {{device_info_template}}, - "payload_on": "{\"cmd\": \"on\", \"group\": \"{{scene}}\"}", - "payload_off": "{\"cmd\": \"off\", \"group\": \"{{scene}}\"}" + "payload_on": "{\"state\": \"on\", \"group\": \"{{scene}}\"}", + "payload_off": "{\"state\": \"off\", \"group\": \"{{scene}}\"}" } # IMPORTANT: all devices must have the pair() command run one time to make @@ -352,7 +352,7 @@ mqtt: # in the same way clicking the button would. The inputs are the same as # those for the on_off topic and payload. scene_topic: 'insteon/{{address}}/scene' - scene_payload: '{ "cmd" : "{{json.cmd.lower()}}" + scene_payload: '{ "cmd" : "{{json.state.lower()}}" {% if json.brightness is defined %} , "level" : {{json.brightness}} {% endif %} @@ -1512,7 +1512,7 @@ mqtt: # in the same way clicking the button would. The inputs are the same as # those for the btn_on_off topic and payload. btn_scene_topic: 'insteon/{{address}}/scene/{{button}}' - btn_scene_payload: '{ "cmd" : "{{json.cmd.lower()}}" + btn_scene_payload: '{ "cmd" : "{{json.state.lower()}}" {% if json.brightness is defined %} , "level" : {{json.brightness}} {% endif %} From ea6c2513eb335349c88de3eb35611dbc18b87494 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 20 May 2021 11:20:54 -0700 Subject: [PATCH 39/44] Adjust Hub Unit Test to Avoid Race Condition This test is intermittently failing on github now. I suspect that the issue is that the time equal and not less than in some instances. --- tests/network/test_Hub.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/network/test_Hub.py b/tests/network/test_Hub.py index 09649a2a..c36d4518 100644 --- a/tests/network/test_Hub.py +++ b/tests/network/test_Hub.py @@ -88,7 +88,7 @@ def test_read(self, test_hub, read, expected, calls): #----------------------------------------------------------------------- @pytest.mark.parametrize("write,t,expected,buffer,calls", [ (None, None, None, 0, 0), - (bytes([0x00]), time.time(), bytes([0x00]), 0, 1), + (bytes([0x00]), time.time() - 1, bytes([0x00]), 0, 1), (bytes([0x00]), time.time() + 10, None, 1, 0), ]) def test_write(self, test_hub, write, t, expected, buffer, calls): From e9d1e56759f2a1a3938c1aeca5ff0ab3b8976ba7 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Thu, 20 May 2021 11:36:31 -0700 Subject: [PATCH 40/44] Adjust Hub Unit Tet to Avoid Race Condition Attempt 2 This time actually fix the correct test. --- tests/network/test_Hub.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/network/test_Hub.py b/tests/network/test_Hub.py index c36d4518..8aa17adf 100644 --- a/tests/network/test_Hub.py +++ b/tests/network/test_Hub.py @@ -88,8 +88,8 @@ def test_read(self, test_hub, read, expected, calls): #----------------------------------------------------------------------- @pytest.mark.parametrize("write,t,expected,buffer,calls", [ (None, None, None, 0, 0), - (bytes([0x00]), time.time() - 1, bytes([0x00]), 0, 1), - (bytes([0x00]), time.time() + 10, None, 1, 0), + (bytes([0x00]), time.time(), bytes([0x00]), 0, 1), + (bytes([0x00]), time.time() + 1000000, None, 1, 0), ]) def test_write(self, test_hub, write, t, expected, buffer, calls): # necessary to stop client from running From d275a270b863c6abc140663b821ad435fa9995fe Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Fri, 21 May 2021 10:25:47 -0700 Subject: [PATCH 41/44] Change IOLinc State Payloads to Uppercase All other state payloads are sent in uppercase. --- insteon_mqtt/data/config-base.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/insteon_mqtt/data/config-base.yaml b/insteon_mqtt/data/config-base.yaml index 2c61bb1d..b8884ad6 100644 --- a/insteon_mqtt/data/config-base.yaml +++ b/insteon_mqtt/data/config-base.yaml @@ -1719,8 +1719,8 @@ mqtt: # relay_on_str = 'off'/'on' state_topic: 'insteon/{{address}}/state' state_payload: >- - { "sensor" : "{{sensor_on_str.lower()}}", - "relay" : "{{relay_on_str.lower()}}", + { "sensor" : "{{sensor_on_str.upper()}}", + "relay" : "{{relay_on_str.upper()}}", "mode" : "{{mode.upper()}}", "timestamp" : {{timestamp}}, "reason" : "{{reason}}" } @@ -1734,7 +1734,7 @@ mqtt: # relay_on = 0/1 # relay_on_str = 'off'/'on' relay_state_topic: 'insteon/{{address}}/relay' - relay_state_payload: '{{relay_on_str.lower()}}' + relay_state_payload: '{{relay_on_str.upper()}}' # Output sensor state change topic and template. This message is sent # whenever the device sensor state changes. Available variables for @@ -1745,7 +1745,7 @@ mqtt: # sensor_on = 0/1 # sensor_on = 'off'/'on' sensor_state_topic: 'insteon/{{address}}/sensor' - sensor_state_payload: '{{sensor_on_str.lower()}}' + sensor_state_payload: '{{sensor_on_str.upper()}}' # Input on/off command. This forces the relay on/off and ignores the # momentary-A,B,C setting. Use this to force the relay to respond. From bd298e64158b9a2cf001cd096a84463e085b9155 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Fri, 21 May 2021 11:19:23 -0700 Subject: [PATCH 42/44] Better Error Report When Unknown Fields are in Discovery Class If a user mistakenly puts extra keys in a discovery class the current error looks like: ```yaml mqtt: kpl_six: btn_on_off_payload: - must be of dict type ``` This updates the schema so that the error now looks like: ```yaml mqtt: kpl_six: btn_on_off_payload: - unknown field ``` A slight improvement in wording. --- insteon_mqtt/data/config-schema.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/insteon_mqtt/data/config-schema.yaml b/insteon_mqtt/data/config-schema.yaml index 9139ec1c..3d20bf1f 100644 --- a/insteon_mqtt/data/config-schema.yaml +++ b/insteon_mqtt/data/config-schema.yaml @@ -140,6 +140,7 @@ insteon: mqtt: type: dict allow_unknown: + allow_unknown: False ## The only unknown keys are user defined discovery_class settings type: dict schema: From 2ad810ca9eed281641af52ddfb9bed9c025bcce9 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 24 May 2021 13:44:52 -0700 Subject: [PATCH 43/44] Update Changelog --- CHANGELOG.md | 2 ++ insteon_mqtt/mqtt/KeypadLincDimmer.py | 26 ++++++++++---------------- insteon_mqtt/mqtt/topic/SetTopic.py | 16 ++++++++++++++++ 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6b0e81b..8cad4198 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ With the addition of the Discovery Platform in the last minor release, the impro ### Additions - There is now a base config file that will be updated as part of the distribution. Now, the only settings you need to define in your config.yaml, are those that are unique to your installation, or those settings you wish to override. For most users, you config.yaml file can now be 10x smaller! ([PR 414][P414]) +- The base config file has been updated to provide device attributes in the state topics for devices. Similarly, the default discovery entity configurations have also been updated so that device attributes are now ingested into Home Assistant. Attributes such as `reason`, `mode`, `timestamp`, and others added in the future, are now available in HomeAssistant using the default InsteonMQTT setup. ([PR 417][P417]) - Adds Resume_Dim feature. For dimmable devices when this flag is enabled, when the device is turned on from its button, it will resume the same brightness level it previously had before being turned off. Currently only works on i2CS devices, will be extended to i2 devices in the future. Thanks @lnr0626 ([PR 411][P411]) - Adds the ability to send arbitrary insteon messages to a device for development purposes. This is a huge win for development. Thanks @lnr0626 ([PR 413][P413]) @@ -768,3 +769,4 @@ will add new features. [P411]: https://github.com/TD22057/insteon-mqtt/pull/411 [P413]: https://github.com/TD22057/insteon-mqtt/pull/413 [P414]: https://github.com/TD22057/insteon-mqtt/pull/414 +[P417]: https://github.com/TD22057/insteon-mqtt/pull/417 diff --git a/insteon_mqtt/mqtt/KeypadLincDimmer.py b/insteon_mqtt/mqtt/KeypadLincDimmer.py index 2e68644c..955a6c44 100644 --- a/insteon_mqtt/mqtt/KeypadLincDimmer.py +++ b/insteon_mqtt/mqtt/KeypadLincDimmer.py @@ -136,19 +136,13 @@ def _input_set_level(self, client, data, message, raise_errors=False): return LOG.info("KeypadLinc input command: %s", data) - level_str = data.get('level', None) - if level_str is None or level_str == "": - # Dimmer and command topic can be the same - # If this lacks a level command it is meant for on/off - self._input_set(client, data, message) - else: - try: - is_on, mode, transition = util.parse_on_off(data) - level = '0' if not is_on else data.get('level', None) - if level is not None: - level = int(level) - reason = data.get("reason", "") - self.device.set(is_on=is_on, level=level, mode=mode, - reason=reason, transition=transition) - except: - LOG.error("Invalid KeypadLinc level command: %s", data) + try: + is_on, mode, transition = util.parse_on_off(data) + level = '0' if not is_on else data.get('level', None) + if level is not None: + level = int(level) + reason = data.get("reason", "") + self.device.set(is_on=is_on, level=level, mode=mode, + reason=reason, transition=transition) + except: + LOG.error("Invalid KeypadLinc level command: %s", data) diff --git a/insteon_mqtt/mqtt/topic/SetTopic.py b/insteon_mqtt/mqtt/topic/SetTopic.py index dbb6caeb..c96cdfce 100644 --- a/insteon_mqtt/mqtt/topic/SetTopic.py +++ b/insteon_mqtt/mqtt/topic/SetTopic.py @@ -126,6 +126,22 @@ def _input_set(self, client, data, message, group=0x01): # Parse the input MQTT message. data = self.msg_set.to_json(message.payload) LOG.info("SetTopic input command: %s", data) + self._input_set_device(data, group) + + #----------------------------------------------------------------------- + def _input_set_device(self, data, group): + """Update the Device State Based on Set Command. + + This is called by _input_set() and was pulled out of that method + so that KeypadLinc dimmers, which use a distinct dimmer topic and + payload, can parse the mqtt data differently from the normal + command topic. + + Args: + client (paho.Client): The paho mqtt client (self.link). + data: Optional user data (unused). + message: MQTT message - has attrs: topic, payload, qos, retain. + """ try: # Tell the device to update its state. is_on, mode, transition = util.parse_on_off(data) From cb67514837de751ac111617c44929bd1d8439e00 Mon Sep 17 00:00:00 2001 From: KRKeegan Date: Mon, 24 May 2021 13:45:52 -0700 Subject: [PATCH 44/44] =?UTF-8?q?Bump=20version:=200.9.2=20=E2=86=92=201.0?= =?UTF-8?q?.0?= 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 4a5f7e8c..39feee34 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.9.2 +current_version = 1.0.0 commit = True tag = False diff --git a/README.md b/README.md index 20c83855..7e923736 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: 0.9.2 ([History](CHANGELOG.md)) +Version: 1.0.0 ([History](CHANGELOG.md)) ### Recent Breaking Changes diff --git a/config.json b/config.json index e7232c71..5523a15c 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": "0.9.2", + "version": "1.0.0", "startup": "services", "arch": ["amd64","armhf","aarch64","i386"], "boot": "auto", diff --git a/insteon_mqtt/const.py b/insteon_mqtt/const.py index 9dd69fb4..98cc90a8 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__ = "0.9.2" +__version__ = "1.0.0" #=========================================================================== diff --git a/setup.py b/setup.py index d566ce88..627ecfb4 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ setuptools.setup( name = 'insteon-mqtt', - version = '0.9.2', + version = '1.0.0', description = "Insteon <-> MQTT bridge server", long_description = readme, author = "Ted Drain",