Skip to content

Commit

Permalink
Zwave multilevel switch v3 (openhab#1104)
Browse files Browse the repository at this point in the history
* ZWave enhancements to multilevel switch to better support V3 command class

Signed-off-by: Chris Jackson <[email protected]>
  • Loading branch information
cdjackson authored and doubled-ca committed Aug 14, 2016
1 parent 5c7bca3 commit d369944
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
*/
package org.openhab.binding.zwave.test.internal.protocol.commandclass;

import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;

import java.util.Arrays;
import java.util.Collection;

import org.junit.Test;
import org.openhab.binding.zwave.internal.protocol.SerialMessage;
Expand Down Expand Up @@ -70,4 +71,31 @@ public void stopLevelChangeMessage() {
msg = cls.stopLevelChangeMessage();
assertTrue(Arrays.equals(msg.getMessageBuffer(), expectedResponseV1));
}

@Test
public void getSupportedMessage() {
ZWaveMultiLevelSwitchCommandClass cls = (ZWaveMultiLevelSwitchCommandClass) getCommandClass(
CommandClass.SWITCH_MULTILEVEL);
SerialMessage msg;

byte[] expectedResponseV3 = { 1, 9, 0, 19, 99, 2, 38, 6, 0, 0, -92 };
cls.setVersion(3);
msg = cls.getSupportedMessage();
assertTrue(Arrays.equals(msg.getMessageBuffer(), expectedResponseV3));
}

@Test
public void initialize() {
ZWaveMultiLevelSwitchCommandClass cls = (ZWaveMultiLevelSwitchCommandClass) getCommandClass(
CommandClass.SWITCH_MULTILEVEL);
Collection<SerialMessage> msgs;

cls.setVersion(1);
msgs = cls.initialize(true);
assertEquals(0, msgs.size());

cls.setVersion(3);
msgs = cls.initialize(true);
assertEquals(1, msgs.size());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ public void getValueMessage() {
byte[] expectedResponseV1 = { 1, 9, 0, 19, 99, 2, 39, 2, 0, 0, -95 };
cls.setVersion(1);
msg = cls.getValueMessage();
byte[] x = msg.getMessageBuffer();
assertTrue(Arrays.equals(msg.getMessageBuffer(), expectedResponseV1));
}

Expand All @@ -45,7 +44,6 @@ public void setValueMessage() {
byte[] expectedResponseV1 = { 1, 10, 0, 19, 99, 3, 39, 1, 2, 0, 0, -94 };
cls.setVersion(1);
msg = cls.setValueMessage(SwitchAllMode.SWITCH_ALL_INCLUDE_OFF_ONLY.ordinal());
byte[] x = msg.getMessageBuffer();
assertTrue(Arrays.equals(msg.getMessageBuffer(), expectedResponseV1));
}

Expand All @@ -70,5 +68,4 @@ public void allOffMessage() {
msg = cls.allOffMessage();
assertTrue(Arrays.equals(msg.getMessageBuffer(), expectedResponseV1));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,20 @@ public State handleEvent(ZWaveThingChannel channel, ZWaveCommandClassValueEvent
boolean configInvertPercent = "true".equalsIgnoreCase(channel.getArguments().get("config_invert_percent"));

int value = (int) event.getValue();
State state;

// A value of 254 means the device doesn't know it's current position
if (value == 254) {
// TODO: Should this return UNDEFINED?
return null;
}

State state = null;
switch (channel.getDataType()) {
case PercentType:
if (value < 0 || value > 100) {
break;
}

if (configInvertPercent) {
state = new PercentType(100 - value);
} else {
Expand Down Expand Up @@ -107,11 +118,9 @@ public State handleEvent(ZWaveThingChannel channel, ZWaveCommandClassValueEvent
}
break;
case IncreaseDecreaseType:
state = null;
break;
default:
state = null;
logger.warn("No conversion in {} to {}", this.getClass().getSimpleName(), channel.getDataType());
logger.warn("No conversion in {} to {}", getClass().getSimpleName(), channel.getDataType());
break;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.openhab.binding.zwave.internal.protocol.SerialMessage;
Expand Down Expand Up @@ -39,7 +40,7 @@
*/
@XStreamAlias("multiLevelSwitchCommandClass")
public class ZWaveMultiLevelSwitchCommandClass extends ZWaveCommandClass
implements ZWaveBasicCommands, ZWaveCommandClassDynamicState {
implements ZWaveBasicCommands, ZWaveCommandClassInitialization, ZWaveCommandClassDynamicState {

@XStreamOmitField
private static final Logger logger = LoggerFactory.getLogger(ZWaveMultiLevelSwitchCommandClass.class);
Expand All @@ -53,6 +54,12 @@ public class ZWaveMultiLevelSwitchCommandClass extends ZWaveCommandClass
private static final int SWITCH_MULTILEVEL_SUPPORTED_GET = 0x06;
private static final int SWITCH_MULTILEVEL_SUPPORTED_REPORT = 0x07;

private SwitchType switchTypePrimary = null;
private SwitchType switchTypeSecondary = null;

@XStreamOmitField
private boolean initialiseDone = false;

@XStreamOmitField
private boolean dynamicDone = false;

Expand Down Expand Up @@ -86,7 +93,7 @@ public CommandClass getCommandClass() {
@Override
public void handleApplicationCommandRequest(SerialMessage serialMessage, int offset, int endpoint)
throws ZWaveSerialMessageException {
logger.debug("NODE {}: Received Switch Multi Level Request", getNode().getNodeId());
logger.debug("NODE {}: Received SWITCH_MULTILEVEL command V{}", getNode().getNodeId(), getVersion());
int command = serialMessage.getMessagePayloadByte(offset);
switch (command) {
case SWITCH_MULTILEVEL_SET:
Expand All @@ -96,10 +103,20 @@ public void handleApplicationCommandRequest(SerialMessage serialMessage, int off
logger.debug("NODE {}: Switch Multi Level report, value = {}", getNode().getNodeId(), value);
ZWaveCommandClassValueEvent zEvent = new ZWaveCommandClassValueEvent(getNode().getNodeId(), endpoint,
getCommandClass(), value);

getController().notifyEventListeners(zEvent);

dynamicDone = true;
break;
case SWITCH_MULTILEVEL_SUPPORTED_REPORT:
int primary = serialMessage.getMessagePayloadByte(offset + 1) & 0x1f;
int secondary = serialMessage.getMessagePayloadByte(offset + 1) & 0x1f;

switchTypePrimary = SwitchType.getSwitchType(primary);
switchTypeSecondary = SwitchType.getSwitchType(secondary);

initialiseDone = true;
break;
default:
logger.warn(String.format("Unsupported Command %d for command class %s (0x%02X).", command,
getCommandClass().getLabel(), getCommandClass().getKey()));
Expand Down Expand Up @@ -160,23 +177,24 @@ public SerialMessage setValueMessage(int level) {
*/
public SerialMessage stopLevelChangeMessage() {
logger.debug("NODE {}: Creating new message for command SWITCH_MULTILEVEL_STOP_LEVEL_CHANGE",
this.getNode().getNodeId());
SerialMessage result = new SerialMessage(this.getNode().getNodeId(), SerialMessageClass.SendData,
getNode().getNodeId());
SerialMessage result = new SerialMessage(getNode().getNodeId(), SerialMessageClass.SendData,
SerialMessageType.Request, SerialMessageClass.SendData, SerialMessagePriority.Set);
byte[] newPayload = { (byte) this.getNode().getNodeId(), 2, (byte) getCommandClass().getKey(),
byte[] newPayload = { (byte) getNode().getNodeId(), 2, (byte) getCommandClass().getKey(),
(byte) SWITCH_MULTILEVEL_STOP_LEVEL_CHANGE };
result.setMessagePayload(newPayload);
return result;
}

/**
* Gets a SerialMessage with the SWITCH_MULTILEVEL_START_LEVEL_CHANGE command
*
*
* @param increase true to increase the level, false to decrease
* @param duration sets the dimming duration
* @return the serial message
*/
public SerialMessage startLevelChangeMessage(boolean increase, int duration) {
// TODO: This is only V2 implementation! V3 has some extra options.
logger.debug("NODE {}: Creating new message for command SWITCH_MULTILEVEL_START_LEVEL_CHANGE",
getNode().getNodeId());
SerialMessage result = new SerialMessage(getNode().getNodeId(), SerialMessageClass.SendData,
Expand All @@ -194,6 +212,39 @@ public SerialMessage startLevelChangeMessage(boolean increase, int duration) {
return result;
}

/**
* Gets a SerialMessage with the SWITCH_MULTILEVEL_SET command
*
* @param the level to set. 0 is mapped to off, > 0 is mapped to on.
* @return the serial message
*/
public SerialMessage getSupportedMessage() {
logger.debug("NODE {}: Creating new message for command SWITCH_MULTILEVEL_SUPPORTED_GET",
getNode().getNodeId());
SerialMessage result = new SerialMessage(this.getNode().getNodeId(), SerialMessageClass.SendData,
SerialMessageType.Request, SerialMessageClass.SendData, SerialMessagePriority.Set);
byte[] newPayload = { (byte) getNode().getNodeId(), 2, (byte) getCommandClass().getKey(),
(byte) SWITCH_MULTILEVEL_SUPPORTED_GET };
result.setMessagePayload(newPayload);
return result;
}

/**
* {@inheritDoc}
*/
@Override
public Collection<SerialMessage> initialize(boolean refresh) {
ArrayList<SerialMessage> result = new ArrayList<SerialMessage>();

if ((refresh == true || initialiseDone == false) && getVersion() >= 3) {
result.add(getSupportedMessage());
} else {
initialiseDone = true;
}

return result;
}

/**
* {@inheritDoc}
*/
Expand All @@ -205,4 +256,64 @@ public Collection<SerialMessage> getDynamicValues(boolean refresh) {
}
return result;
}

/**
* Return the primary switch type
*
* @return
*/
public SwitchType getPrimarySwitchType() {
return switchTypePrimary;
}

/**
* Return the secondary switch type
*
* @return
*/
public SwitchType getSecondarySwitchType() {
return switchTypeSecondary;
}

/**
* Enum to define the different switch types defined by ZWave
*
*/
@XStreamAlias("multilevelSwitchType")
public enum SwitchType {
UNDEFINED(0),
OFF_ON(1),
DOWN_UP(2),
CLOSE_OPEN(3),
CCW_CW(4),
LEFT_RIGHT(5),
REVERSE_FORWARD(6),
PULL_PUSH(7);

private final int value;
private static Map<Integer, SwitchType> codeToTypeMapping;

private static void initMapping() {
codeToTypeMapping = new HashMap<Integer, SwitchType>();
for (SwitchType s : values()) {
codeToTypeMapping.put(s.value, s);
}
}

public static SwitchType getSwitchType(int i) {
if (codeToTypeMapping == null) {
initMapping();
}

return codeToTypeMapping.get(i);
}

private SwitchType(int value) {
this.value = value;
}

public int getValue() {
return value;
}
}
}

0 comments on commit d369944

Please sign in to comment.