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

Support for the default ramp rate setting for Dimmer and KeypadLinc #235

Merged
merged 8 commits into from
Dec 14, 2020
Merged
Show file tree
Hide file tree
Changes from 6 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
5 changes: 5 additions & 0 deletions docs/mqtt.md
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,11 @@ Switch, KeypadLinc, and Dimmer all support the flags:
signal_bits input. If a bit is 0, then that button is a toggle button
and will alternate on an doff signals

KeypadLinc and Dimmer support the flags:

- ramp_rate: integer in the range of 0x00-0x1f which sets the default ramp
rate that will be used when the button is pressed

IOLinc supports the flags:

- mode: "latching" / "momentary-a" / "momentary-b" / "momentary-c" to
Expand Down
56 changes: 54 additions & 2 deletions insteon_mqtt/device/Dimmer.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class Dimmer(Base):

def __init__(self, protocol, modem, address, name=None):
"""Constructor

Args:
protocol (Protocol): The Protocol object used to communicate
with the Insteon network. This is needed to allow the
Expand Down Expand Up @@ -521,6 +521,35 @@ def set_on_level(self, level, on_done=None):
msg_handler = handler.StandardCmd(msg, self.handle_on_level, on_done)
self.send(msg, msg_handler)

#-----------------------------------------------------------------------
def set_ramp_rate(self, rate, on_done=None):
"""Set the device default ramp rate.

This changes the dimmer default ramp rate of how quickly the it
will turn on or off. This rate can be between .1 seconds and up
to 9 minutes.

Args:
rate (int): The default ramp rate in the range [0,31]
on_done: Finished callback. This is called when the command has
completed. Signature is: on_done(success, msg, data)
"""
LOG.info("Dimmer %s setting ramp rate to %s", self.label, rate)

# Extended message data - see Insteon dev guide p156.
data = bytes([
0x01, # D1 must be group 0x01
0x05, # D2 set ramp rate when button is pressed
rate, # D3 rate
] + [0x00] * 11)

msg = Msg.OutExtended.direct(self.addr, 0x2e, 0x00, data)

# Use the standard command handler which will notify us when the
# command is ACK'ed.
msg_handler = handler.StandardCmd(msg, self.handle_ramp_rate, on_done)
self.send(msg, msg_handler)

#-----------------------------------------------------------------------
def set_flags(self, on_done, **kwargs):
"""Set internal device flags.
Expand All @@ -545,7 +574,8 @@ def set_flags(self, on_done, **kwargs):
# passed in.
FLAG_BACKLIGHT = "backlight"
FLAG_ON_LEVEL = "on_level"
flags = set([FLAG_BACKLIGHT, FLAG_ON_LEVEL])
FLAG_RAMP_RATE = "ramp_rate"
flags = set([FLAG_BACKLIGHT, FLAG_ON_LEVEL, FLAG_RAMP_RATE])
unknown = set(kwargs.keys()).difference(flags)
if unknown:
raise Exception("Unknown Dimmer flags input: %s.\n Valid flags "
Expand All @@ -562,6 +592,10 @@ def set_flags(self, on_done, **kwargs):
on_level = util.input_byte(kwargs, FLAG_ON_LEVEL)
seq.add(self.set_on_level, on_level)

if FLAG_RAMP_RATE in kwargs:
rate = util.input_byte(kwargs, FLAG_RAMP_RATE)
seq.add(self.set_ramp_rate, rate)

seq.run()

#-----------------------------------------------------------------------
Expand Down Expand Up @@ -600,6 +634,24 @@ def handle_on_level(self, msg, on_done):
else:
on_done(False, "Button on level failed", None)

#-----------------------------------------------------------------------
def handle_ramp_rate(self, msg, on_done):
"""Callback for handling set_ramp_rate() responses.

This is called when we get a response to the set_ramp_rate() command.
We don't need to do anything - just call the on_done callback with
the status.

Args:
msg (InpStandard): The response message from the command.
on_done: Finished callback. This is called when the command has
completed. Signature is: on_done(success, msg, data)
"""
if msg.flags.type == Msg.Flags.Type.DIRECT_ACK:
on_done(True, "Button ramp rate updated", None)
else:
on_done(False, "Button ramp rate failed", None)

#-----------------------------------------------------------------------
def handle_broadcast(self, msg):
"""Handle broadcast messages from this device.
Expand Down
42 changes: 41 additions & 1 deletion insteon_mqtt/device/KeypadLinc.py
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,41 @@ def set_on_level(self, level, on_done=None):

self.send(msg, msg_handler)

#-----------------------------------------------------------------------
def set_ramp_rate(self, rate, on_done=None):
"""Set the device default ramp rate.

This changes the dimmer default ramp rate of how quickly the it
will turn on or off. This rate can be between .1 seconds and up
to 9 minutes.

Args:
rate (int): The default ramp rate in the range [0,31]
Copy link
Contributor

@tstabrawa tstabrawa Dec 4, 2020

Choose a reason for hiding this comment

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

Sorry for the late comment. I noticed that this code is expecting a ramp rate value that would be handed directly to the device -- so the user would be expected to know what value to use based on how those values are interpreted by devices (such as by consulting this table.)

I just wanted to raise the question - would it be better if the ramp rate were specified in seconds, and translated using the Dimmer.ramp_pretty lookup table, similar to as done in link_data_from_pretty()? (Same question applies to the changes to the Dimmer class.)

This might be a question for @krkeegan or @TD22057 to answer, though.

on_done: Finished callback. This is called when the command has
completed. Signature is: on_done(success, msg, data)
"""
if not self.is_dimmer:
LOG.error("KeypadLinc %s switch doesn't support setting ramp_rate",
self.addr)
return

LOG.info("Dimmer %s setting ramp rate to %s", self.label, rate)

# Extended message data - see Insteon dev guide p156.
data = bytes([
0x01, # D1 must be group 0x01
0x05, # D2 set ramp rate when button is pressed
rate, # D3 rate
] + [0x00] * 11)

msg = Msg.OutExtended.direct(self.addr, 0x2e, 0x00, data)

# Use the standard command handler which will notify us when the
# command is ACK'ed.
callback = functools.partial(self.handle_ack, task="Button ramp rate")
msg_handler = handler.StandardCmd(msg, callback, on_done)
self.send(msg, msg_handler)

#-----------------------------------------------------------------------
def set_flags(self, on_done, **kwargs):
"""Set internal device flags.
Expand All @@ -895,14 +930,15 @@ def set_flags(self, on_done, **kwargs):
FLAG_BACKLIGHT = "backlight"
FLAG_GROUP = "group"
FLAG_ON_LEVEL = "on_level"
FLAG_RAMP_RATE = "ramp_rate"
FLAG_LOAD_ATTACH = "load_attached"
FLAG_FOLLOW_MASK = "follow_mask"
FLAG_OFF_MASK = "off_mask"
FLAG_SIGNAL_BITS = "signal_bits"
FLAG_NONTOGGLE_BITS = "nontoggle_bits"
flags = set([FLAG_BACKLIGHT, FLAG_LOAD_ATTACH, FLAG_FOLLOW_MASK,
FLAG_SIGNAL_BITS, FLAG_NONTOGGLE_BITS, FLAG_OFF_MASK,
FLAG_GROUP, FLAG_ON_LEVEL])
FLAG_GROUP, FLAG_ON_LEVEL, FLAG_RAMP_RATE])
unknown = set(kwargs.keys()).difference(flags)
if unknown:
raise Exception("Unknown KeypadLinc flags input: %s.\n Valid "
Expand All @@ -927,6 +963,10 @@ def set_flags(self, on_done, **kwargs):
on_level = util.input_byte(kwargs, FLAG_ON_LEVEL)
seq.add(self.set_on_level, on_level)

if FLAG_RAMP_RATE in kwargs:
rate = util.input_byte(kwargs, FLAG_RAMP_RATE)
seq.add(self.set_ramp_rate, rate)

if FLAG_FOLLOW_MASK in kwargs:
if group is None:
raise Exception("follow_mask requires group=<NUM> to be input")
Expand Down