Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Get_Flags Function to Modem #359

Merged
merged 2 commits into from
Mar 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ to add new devices and features. This is the reason for the jump to 0.8.0.
to the command line `insteon-mqtt config.yaml -v`. Both will return the
version of Insteon-MQTT. ([PR 355][P355])

- Adds get_flags suppport to the modem. Only needed for debugging.
([PR 359][P359])

### Fixes

- Fixed an error where setting the MQTT id would prevent subscribing to
Expand Down Expand Up @@ -621,3 +624,4 @@ will add new features.
[P324]: https://github.com/TD22057/insteon-mqtt/pull/324
[P354]: https://github.com/TD22057/insteon-mqtt/pull/354
[P355]: https://github.com/TD22057/insteon-mqtt/pull/355
[P359]: https://github.com/TD22057/insteon-mqtt/pull/359
14 changes: 13 additions & 1 deletion docs/mqtt.md
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,19 @@ commands.

### Get and set operating flags.

Supported: devices
Supported: devices, modem (get_flags only)

To request the current flag settings on a device:

```
insteon-mqtt config.yaml get-flags aa.bb.cc
```

The MQTT format of the command is:

```
{ "cmd" : "get_flags"}
```

This command gets and sets various Insteon device flags. The set of
supported flags depends on the device type. The command line tool accepts an
Expand Down
13 changes: 13 additions & 0 deletions insteon_mqtt/Modem.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ def __init__(self, protocol, stack, timed_call):
'linking' : self.linking,
'scene' : self.scene,
'factory_reset' : self.factory_reset,
'get_flags' : self.get_flags,
'sync_all' : self.sync_all,
'sync' : self.sync,
'import_scenes': self.import_scenes,
Expand Down Expand Up @@ -712,6 +713,18 @@ def factory_reset(self, on_done=None):
msg_handler = handler.ModemReset(self, on_done)
self.send(msg, msg_handler)

#-----------------------------------------------------------------------
def get_flags(self, on_done=None):
"""Queries and Prints the Modem Flags to the Log

Args:
on_done: Finished callback. This is called when the command has
completed. Signature is: on_done(success, msg, data)
"""
msg = Msg.OutGetModemFlags()
msg_handler = handler.ModemGetFlags(self, on_done)
self.send(msg, msg_handler)

#-----------------------------------------------------------------------
def send(self, msg, msg_handler, high_priority=False, after=None):
"""Send a message to the modem.
Expand Down
63 changes: 63 additions & 0 deletions insteon_mqtt/handler/ModemGetFlags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#===========================================================================
#
# Modem get_flags handler.
#
#===========================================================================
from .. import log
from .. import message as Msg
from .Base import Base

LOG = log.get_logger()


class ModemGetFlags(Base):
"""Modem get_flags handler.

This handles a `get flags` command being sent to the modem. The response
to this command is a single message containing the flags and 2 spare
bytes.
"""
def __init__(self, modem, on_done=None):
"""Constructor

Args
modem (Modem): The Insteon modem object.
on_done: The finished callback. Calling signature:
on_done( bool success, str message, data )
"""
super().__init__(on_done)

self.modem = modem

#-----------------------------------------------------------------------
def msg_received(self, protocol, msg):
"""See if we can handle the message.

If we get an ACK of the user reset, we'll clear the modem database.

Args:
protocol (Protocol): The Insteon Protocol object
msg: Insteon message object that was read.

Returns:
Msg.UNKNOWN if we can't handle this message.
Msg.CONTINUE if we handled the message and expect more.
Msg.FINISHED if we handled the message and are done.
"""
if not self._PLM_sent:
# If PLM hasn't sent our message yet, this can't be for us
return Msg.UNKNOWN
if isinstance(msg, Msg.OutGetModemFlags):
if msg.is_ack:
LOG.ui("Modem flag byte is: %s, spare bytes are: %s, %s",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be more useful to print flags/spares in hex (assuming the conversion to string does so in decimal by default).

msg.modem_flags, msg.spare1, msg.spare2)
self.on_done(True, "Modem get_flags complete", None)
else:
LOG.error("Modem get_flags failed")
self.on_done(False, "Modem get_flags failed", None)

return Msg.FINISHED

return Msg.UNKNOWN

#-----------------------------------------------------------------------
1 change: 1 addition & 0 deletions insteon_mqtt/handler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
from .ModemLinkComplete import ModemLinkComplete
from .ModemLinkStart import ModemLinkStart
from .ModemReset import ModemReset
from .ModemGetFlags import ModemGetFlags
from .ModemScene import ModemScene
from .StandardCmd import StandardCmd
from .StandardCmdNAK import StandardCmdNAK
Expand Down
87 changes: 87 additions & 0 deletions insteon_mqtt/message/OutGetModemFlags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#===========================================================================
#
# Output insteon reset the PLM modem message.
#
#===========================================================================
from .Base import Base


class OutGetModemFlags(Base):
"""Command requesting the modem configuration

This command will retun 6 bytes:
- 0x02
- 0x73
- Modem Configuration Flags
- Spare 1
- Spare 2
- Ack/Nak
"""
msg_code = 0x73
fixed_msg_size = 6

#-----------------------------------------------------------------------
@classmethod
def from_bytes(cls, raw):
"""Read the message from a byte stream.

This should only be called if raw[1] == msg_code and len(raw) >=
msg_size().

You cannot pass the output of to_bytes() to this. to_bytes() is used
to output to the PLM but the modem sends back the same message with
an extra ack byte which this function can read.

Args:
raw (bytes): The current byte stream to read from.

Returns:
Returns the constructed OutResetModem object.
"""
assert len(raw) >= cls.fixed_msg_size
assert raw[0] == 0x02 and raw[1] == cls.msg_code
modem_flags = raw[2]
spare1 = raw[3]
spare2 = raw[4]
is_ack = raw[5] == 0x06
return OutGetModemFlags(is_ack, modem_flags, spare1, spare2)

#-----------------------------------------------------------------------
def __init__(self, is_ack=None, modem_flags=None, spare1=None,
spare2=None):
"""Constructor

Args:
is_ack (bool): True for ACK, False for NAK. None for output
commands to the modem.
"""
super().__init__()

self.is_ack = is_ack
self.modem_flags = modem_flags
self.spare1 = spare1
self.spare2 = spare2

#-----------------------------------------------------------------------
def to_bytes(self):
"""Convert the message to a byte array.

Returns:
bytes: Returns the message as bytes.
"""
return bytes([0x02, self.msg_code])

#-----------------------------------------------------------------------
def __str__(self):
ack = ""
flags = ""
spares = ""
if self.is_ack is not None:
ack = " ack: %s" % str(self.is_ack)
flags = " modem flags: %s" % str(self.modem_flags)
spares = " spares: %s %s" % (str(self.spare1), str(self.spare2))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be more useful to print flags/spares in hex (assuming the conversion to string does so in decimal by default).

return "OutGetModemFlags%s%s%s" % (flags, spares, ack)

#-----------------------------------------------------------------------

#===========================================================================
2 changes: 2 additions & 0 deletions insteon_mqtt/message/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
from .OutModemLinking import OutModemLinking
from .OutModemScene import OutModemScene
from .OutResetModem import OutResetModem
from .OutGetModemFlags import OutGetModemFlags
from .OutStandard import OutStandard, OutExtended

# Hub Messages
Expand Down Expand Up @@ -85,6 +86,7 @@
0x69 : OutAllLinkGetFirst,
0x6a : OutAllLinkGetNext,
0x6f : OutAllLinkUpdate,
0x73 : OutGetModemFlags,

# Hub Messages
0x7f : HubRFUnknown,
Expand Down
70 changes: 70 additions & 0 deletions tests/handler/test_ModemGetFlags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#===========================================================================
#
# Tests for: insteont_mqtt/handler/ModemGetFlags.py
#
# pylint: disable=attribute-defined-outside-init
#===========================================================================
import insteon_mqtt as IM
import insteon_mqtt.message as Msg
import helpers as H


class Test_ModemGetFlags:
def test_acks(self, tmpdir):
calls = []

def callback(success, msg, done):
calls.append((success, msg, done))

modem = H.main.MockModem(tmpdir)
proto = H.main.MockProtocol()
handler = IM.handler.ModemGetFlags(modem, callback)
handler._PLM_sent = True
handler._PLM_ACK = True

#Try a good message
msg = Msg.OutGetModemFlags(is_ack=True, modem_flags=0x01, spare1=0x02,
spare2=0x03)
r = handler.msg_received(proto, msg)
assert r == Msg.FINISHED
assert calls[0][0]

#Try a NAK message
msg = Msg.OutGetModemFlags(is_ack=False, modem_flags=0x01, spare1=0x02,
spare2=0x03)
r = handler.msg_received(proto, msg)
assert r == Msg.FINISHED
assert not calls[1][0]

#Wrong Message
msg = Msg.OutResetModem(is_ack=True)
r = handler.msg_received(proto, msg)
assert r == Msg.UNKNOWN

#-----------------------------------------------------------------------
def test_plm_sent(self, tmpdir):
calls = []

def callback(success, msg, done):
calls.append((success, msg, done))

modem = H.main.MockModem(tmpdir)
proto = H.main.MockProtocol()
handler = IM.handler.ModemGetFlags(modem, callback)
assert not handler._PLM_sent

#Try a message prior to sent
msg = Msg.OutGetModemFlags(is_ack=False, modem_flags=0x01, spare1=0x02,
spare2=0x03)
r = handler.msg_received(proto, msg)
assert r == Msg.UNKNOWN

# Signal Sent
handler.sending_message(msg)
assert handler._PLM_sent

#Try a message prior to sent
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is actually after sending ...

msg = Msg.OutGetModemFlags(is_ack=False, modem_flags=0x01, spare1=0x02,
spare2=0x03)
r = handler.msg_received(proto, msg)
assert r == Msg.FINISHED
47 changes: 47 additions & 0 deletions tests/message/test_OutGetModemFlags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#===========================================================================
#
# Tests for: insteont_mqtt/message/OutGetModemFlags.py
#
#===========================================================================
import insteon_mqtt.message as Msg


class Test_OutGetModemFlags:
#-----------------------------------------------------------------------
def test_out(self):
obj = Msg.OutGetModemFlags()
assert obj.fixed_msg_size == 6

b = obj.to_bytes()
rt = bytes([0x02, 0x73])
assert b == rt

str(obj)

#-----------------------------------------------------------------------
def test_in_ack(self):
b = bytes([0x02, 0x73, 0x01, 0x02, 0x03, 0x06])
obj = Msg.OutGetModemFlags.from_bytes(b)
assert obj.is_ack is True
assert obj.modem_flags == 0x01
assert obj.spare1 == 0x02
assert obj.spare2 == 0x03

str(obj)

#-----------------------------------------------------------------------
def test_in_nack(self):
b = bytes([0x02, 0x73, 0x01, 0x02, 0x03, 0x15])
obj = Msg.OutGetModemFlags.from_bytes(b)
assert obj.is_ack is False
assert obj.modem_flags == 0x01
assert obj.spare1 == 0x02
assert obj.spare2 == 0x03

str(obj)


#-----------------------------------------------------------------------


#===========================================================================