From 8dc6c333b0abdbf78b5bb14427233375b82d53bc Mon Sep 17 00:00:00 2001 From: Kevin Robert Keegan Date: Wed, 16 Aug 2023 14:27:58 -0700 Subject: [PATCH 1/2] Allow String Mode When Using On, Off, or Set Command Fixes #512 --- insteon_mqtt/device/base/ResponderBase.py | 16 +++- tests/device/base/test_ResponderBase.py | 100 ++++++++++++++++++++++ 2 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 tests/device/base/test_ResponderBase.py diff --git a/insteon_mqtt/device/base/ResponderBase.py b/insteon_mqtt/device/base/ResponderBase.py index e15db11b..e87765f6 100644 --- a/insteon_mqtt/device/base/ResponderBase.py +++ b/insteon_mqtt/device/base/ResponderBase.py @@ -113,6 +113,12 @@ def on(self, group=0x01, level=None, mode=on_off.Mode.NORMAL, reason="", """ LOG.info("Device %s grp: %s cmd: on %s", self.addr, group, mode) assert group in self.responder_groups + if isinstance(mode, str): + try: + mode = on_off.Mode(mode.lower()) + except: + LOG.error("Invalid mode string '%s'", mode) + mode = on_off.Mode.NORMAL assert isinstance(mode, on_off.Mode) # Send the requested on code value. @@ -150,6 +156,12 @@ def off(self, group=0x01, mode=on_off.Mode.NORMAL, reason="", """ LOG.info("Device %s grp: %s cmd: off %s", self.addr, group, mode) assert group in self.responder_groups + if isinstance(mode, str): + try: + mode = on_off.Mode(mode.lower()) + except: + LOG.error("Invalid mode string '%s'", mode) + mode = on_off.Mode.NORMAL assert isinstance(mode, on_off.Mode) # Send an off or instant off command. @@ -372,7 +384,7 @@ def group_cmd_handle_increment(self, cmd, group, reason): """ # I am not sure I am aware of increment group commands. Is there # some way I can cause one to occur? - pass + pass # pragma: no cover #----------------------------------------------------------------------- def group_cmd_handle_manual(self, manual, group, reason): @@ -388,4 +400,4 @@ def group_cmd_handle_manual(self, manual, group, reason): group (int): The local db entry for this group command. reason (str): Whether the command was ON or OFF """ - pass + pass # pragma: no cover diff --git a/tests/device/base/test_ResponderBase.py b/tests/device/base/test_ResponderBase.py new file mode 100644 index 00000000..20e28843 --- /dev/null +++ b/tests/device/base/test_ResponderBase.py @@ -0,0 +1,100 @@ +#=========================================================================== +# +# Tests for: insteont_mqtt/device/base/ResponderBase.py +# +# pylint: disable=W0621,W0212, +# +#=========================================================================== +import logging +from pathlib import Path +# from pprint import pprint +from unittest import mock +from unittest.mock import call +import pytest +import insteon_mqtt as IM +from insteon_mqtt.device.base.ResponderBase import ResponderBase +from insteon_mqtt.device.base import Base +import insteon_mqtt.message as Msg +import insteon_mqtt.handler as Handler +import insteon_mqtt.on_off as on_off +import helpers as H + +@pytest.fixture +def test_device(tmpdir): + ''' + Returns a generically configured device for testing + ''' + protocol = H.main.MockProtocol() + modem = H.main.MockModem(tmpdir) + modem.db = IM.db.Modem(None, modem) + modem.scenes = IM.Scenes.SceneManager(modem, None) + addr = IM.Address(0x01, 0x02, 0x03) + device = ResponderBase(protocol, modem, addr) + return device + +class Test_ResponderBase_Cmds(): + ## On Command + @pytest.mark.parametrize("mode_arg,cmd1", [ + (None, 0x11), # mode not set + (on_off.Mode.INSTANT, 0x21), # mode as enum + ("instant", 0x21), # mode as str + ("bad_mode", 0x11), # bad mode + ]) + def test_on(self, test_device, mode_arg, cmd1): + with mock.patch.object(Base, 'send') as mocked: + if mode_arg is not None: + test_device.on(mode=mode_arg) + else: + test_device.on() + assert mocked.call_count == 1 + call_args = mocked.call_args_list + assert isinstance(call_args[0].args[0], Msg.OutStandard) + assert call_args[0].args[0].cmd1 == cmd1 + assert call_args[0].args[0].cmd2 == 0xFF + assert isinstance(call_args[0].args[1], Handler.StandardCmd) + + ## Off Command + @pytest.mark.parametrize("mode_arg,cmd1", [ + (None, 0x13), # mode not set + (on_off.Mode.INSTANT, 0x21), # mode as enum + ("instant", 0x21), # mode as str + ("bad_mode", 0x13), # bad mode + ]) + def test_off(self, test_device, mode_arg, cmd1): + with mock.patch.object(Base, 'send') as mocked: + if mode_arg is not None: + test_device.off(mode=mode_arg) + else: + test_device.off() + assert mocked.call_count == 1 + call_args = mocked.call_args_list + assert isinstance(call_args[0].args[0], Msg.OutStandard) + assert call_args[0].args[0].cmd1 == cmd1 + assert call_args[0].args[0].cmd2 == 0x00 + assert isinstance(call_args[0].args[1], Handler.StandardCmd) + + ## Set Command + @pytest.mark.parametrize("mode_arg,is_on,level,cmd1,cmd2", [ + (None, None, None, 0x13, 0x00), + (None, True, None, 0x11, 0xFF), + (None, None, 0x50, 0x11, 0xFF), + (on_off.Mode.INSTANT, None, None, 0x21, 0x00), + ('instant', None, None, 0x21, 0x00), + ], + ids=['mode not set', 'testB id', 'level is set', 'mode is enum', 'mode is str']) + def test_set(self, test_device, mode_arg, is_on, level, cmd1, cmd2): + kwargs = {} + with mock.patch.object(Base, 'send') as mocked: + if mode_arg is not None: + kwargs['mode'] = mode_arg + if is_on is not None: + kwargs['is_on'] = is_on + if level is not None: + kwargs['level'] = level + test_device.set(**kwargs) + assert mocked.call_count == 1 + call_args = mocked.call_args_list + assert isinstance(call_args[0].args[0], Msg.OutStandard) + assert call_args[0].args[0].cmd1 == cmd1 + assert call_args[0].args[0].cmd2 == cmd2 + assert isinstance(call_args[0].args[1], Handler.StandardCmd) \ No newline at end of file From 2a4aac4f759b1797fcaf84f1c1478700a4fb3b7b Mon Sep 17 00:00:00 2001 From: Kevin Robert Keegan Date: Wed, 16 Aug 2023 14:41:15 -0700 Subject: [PATCH 2/2] Fix Flake8 Errors --- insteon_mqtt/device/base/ResponderBase.py | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/insteon_mqtt/device/base/ResponderBase.py b/insteon_mqtt/device/base/ResponderBase.py index e87765f6..13115d1f 100644 --- a/insteon_mqtt/device/base/ResponderBase.py +++ b/insteon_mqtt/device/base/ResponderBase.py @@ -114,11 +114,11 @@ def on(self, group=0x01, level=None, mode=on_off.Mode.NORMAL, reason="", LOG.info("Device %s grp: %s cmd: on %s", self.addr, group, mode) assert group in self.responder_groups if isinstance(mode, str): - try: - mode = on_off.Mode(mode.lower()) - except: - LOG.error("Invalid mode string '%s'", mode) - mode = on_off.Mode.NORMAL + try: + mode = on_off.Mode(mode.lower()) + except: + LOG.error("Invalid mode string '%s'", mode) + mode = on_off.Mode.NORMAL assert isinstance(mode, on_off.Mode) # Send the requested on code value. @@ -157,11 +157,11 @@ def off(self, group=0x01, mode=on_off.Mode.NORMAL, reason="", LOG.info("Device %s grp: %s cmd: off %s", self.addr, group, mode) assert group in self.responder_groups if isinstance(mode, str): - try: - mode = on_off.Mode(mode.lower()) - except: - LOG.error("Invalid mode string '%s'", mode) - mode = on_off.Mode.NORMAL + try: + mode = on_off.Mode(mode.lower()) + except: + LOG.error("Invalid mode string '%s'", mode) + mode = on_off.Mode.NORMAL assert isinstance(mode, on_off.Mode) # Send an off or instant off command. @@ -384,7 +384,7 @@ def group_cmd_handle_increment(self, cmd, group, reason): """ # I am not sure I am aware of increment group commands. Is there # some way I can cause one to occur? - pass # pragma: no cover + pass # pragma: no cover #----------------------------------------------------------------------- def group_cmd_handle_manual(self, manual, group, reason): @@ -400,4 +400,4 @@ def group_cmd_handle_manual(self, manual, group, reason): group (int): The local db entry for this group command. reason (str): Whether the command was ON or OFF """ - pass # pragma: no cover + pass # pragma: no cover