/** * Copyright (c) 2010-2016 by the respective copyright holders. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.openhab.binding.zwave.internal.protocol.commandclass; import java.util.HashMap; import java.util.Map; import org.openhab.binding.zwave.internal.protocol.SerialMessage; import org.openhab.binding.zwave.internal.protocol.SerialMessage.SerialMessageClass; import org.openhab.binding.zwave.internal.protocol.SerialMessage.SerialMessagePriority; import org.openhab.binding.zwave.internal.protocol.SerialMessage.SerialMessageType; import org.openhab.binding.zwave.internal.protocol.ZWaveController; import org.openhab.binding.zwave.internal.protocol.ZWaveEndpoint; import org.openhab.binding.zwave.internal.protocol.ZWaveNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamOmitField; /** * Handles the Version command class. The Version Command Class is * used to obtain the library type, the protocol version * used by the node, the individual command class versions * used by the node and the vendor specific application * version from a device. * * @author Jan-Willem Spuij * @since 1.3.0 */ @XStreamAlias("versionCommandClass") public class ZWaveVersionCommandClass extends ZWaveCommandClass { @XStreamOmitField private static final Logger logger = LoggerFactory.getLogger(ZWaveVersionCommandClass.class); public static final int VERSION_GET = 0x11; public static final int VERSION_REPORT = 0x12; public static final int VERSION_COMMAND_CLASS_GET = 0x13; public static final int VERSION_COMMAND_CLASS_REPORT = 0x14; private LibraryType libraryType = LibraryType.LIB_UNKNOWN; private String protocolVersion; private String applicationVersion; /** * Creates a new instance of the ZWaveVersionCommandClass class. * * @param node the node this command class belongs to * @param controller the controller to use * @param endpoint the endpoint this Command class belongs to */ public ZWaveVersionCommandClass(ZWaveNode node, ZWaveController controller, ZWaveEndpoint endpoint) { super(node, controller, endpoint); } /** * {@inheritDoc} */ @Override public CommandClass getCommandClass() { return CommandClass.VERSION; } /** * {@inheritDoc} */ @Override public void handleApplicationCommandRequest(SerialMessage serialMessage, int offset, int endpoint) { logger.trace("Handle Message Version Request"); logger.debug("NODE {}: Received Version Request", this.getNode().getNodeId()); int command = serialMessage.getMessagePayloadByte(offset); switch (command) { case VERSION_GET: case VERSION_COMMAND_CLASS_GET: logger.warn("Command {} not implemented.", command); return; case VERSION_REPORT: logger.debug("NODE {}: Process Version Report", this.getNode().getNodeId()); libraryType = LibraryType.getLibraryType(serialMessage.getMessagePayloadByte(offset + 1)); protocolVersion = Integer.toString(serialMessage.getMessagePayloadByte(offset + 2)) + "." + Integer.toString(serialMessage.getMessagePayloadByte(offset + 3)); applicationVersion = Integer.toString(serialMessage.getMessagePayloadByte(offset + 4)) + "." + Integer.toString(serialMessage.getMessagePayloadByte(offset + 5)); logger.debug("NODE {}: Library Type = {} ({})", this.getNode().getNodeId(), libraryType.key, libraryType.label); logger.debug("NODE {}: Protocol Version = {}", this.getNode().getNodeId(), protocolVersion); logger.debug("NODE {}: Application Version = {}", this.getNode().getNodeId(), applicationVersion); break; case VERSION_COMMAND_CLASS_REPORT: logger.debug("NODE {}: Process Version Command Class Report", this.getNode().getNodeId()); int commandClassCode = serialMessage.getMessagePayloadByte(offset + 1); int commandClassVersion = serialMessage.getMessagePayloadByte(offset + 2); CommandClass commandClass = CommandClass.getCommandClass(commandClassCode); if (commandClass == null) { logger.error(String.format("NODE %d: Unsupported command class 0x%02x", this.getNode().getNodeId(), commandClassCode)); return; } logger.debug("NODE {}: Requested Command Class = {}, Version = {}", this.getNode().getNodeId(), commandClass.getLabel(), commandClassVersion); // The version is set on the command class for this node. By updating the version, extra functionality // is unlocked in the command class. // The messages are backwards compatible, so it's not a problem that there is a slight delay when the // command class version is queried on the // node. ZWaveCommandClass zwaveCommandClass = this.getNode().getCommandClass(commandClass); if (zwaveCommandClass == null) { logger.error(String.format("NODE %d: Unsupported command class %s (0x%02x)", this.getNode().getNodeId(), commandClass.getLabel(), commandClassCode)); return; } // If the device reports version 0, it means it doesn't support this command class! if (commandClassVersion == 0) { logger.info("NODE {}: Command Class {} has version 0!", this.getNode().getNodeId(), commandClass.getLabel()); // TODO: We should remove the class // For now, just set the version to 1 to avoid a loop on initialisation commandClassVersion = 1; // this.getNode().removeCommandClass(zwaveCommandClass); } if (commandClassVersion > zwaveCommandClass.getMaxVersion()) { zwaveCommandClass.setVersion(zwaveCommandClass.getMaxVersion()); logger.debug( "NODE {}: Version = {}, version set to maximum supported by the binding. Enabling extra functionality.", this.getNode().getNodeId(), zwaveCommandClass.getMaxVersion()); } else { zwaveCommandClass.setVersion(commandClassVersion); logger.debug("NODE {}: Version = {}, version set. Enabling extra functionality.", this.getNode().getNodeId(), commandClassVersion); } break; default: logger.warn(String.format("Unsupported Command 0x%02X for command class %s (0x%02X).", command, this.getCommandClass().getLabel(), this.getCommandClass().getKey())); } } /** * Gets a SerialMessage with the VERSION_GET command * * @return the serial message */ public SerialMessage getVersionMessage() { logger.debug("NODE {}: Creating new message for command VERSION_GET", this.getNode().getNodeId()); SerialMessage result = new SerialMessage(this.getNode().getNodeId(), SerialMessageClass.SendData, SerialMessageType.Request, SerialMessageClass.ApplicationCommandHandler, SerialMessagePriority.Config); byte[] newPayload = { (byte) this.getNode().getNodeId(), 2, (byte) getCommandClass().getKey(), (byte) VERSION_GET }; result.setMessagePayload(newPayload); return result; } /** * Gets a SerialMessage with the VERSION COMMAND CLASS GET command. * This version is used to differentiate between multiple versions of a command * and to enable extra functionality. * * @param commandClass The command class to get the version for. * @return the serial message */ public SerialMessage getCommandClassVersionMessage(CommandClass commandClass) { logger.debug("NODE {}: Creating new message for application command VERSION_COMMAND_CLASS_GET command class {}", this.getNode().getNodeId(), commandClass.getLabel()); SerialMessage result = new SerialMessage(this.getNode().getNodeId(), SerialMessageClass.SendData, SerialMessageType.Request, SerialMessageClass.ApplicationCommandHandler, SerialMessagePriority.Config); byte[] newPayload = { (byte) this.getNode().getNodeId(), 3, (byte) getCommandClass().getKey(), (byte) VERSION_COMMAND_CLASS_GET, (byte) commandClass.getKey() }; result.setMessagePayload(newPayload); return result; } /** * Check the version of a command class by sending a VERSION_COMMAND_CLASS_GET message to the node. * * @param commandClass the command class to check the version for. * @return serial message to be sent */ public SerialMessage checkVersion(ZWaveCommandClass commandClass) { ZWaveVersionCommandClass versionCommandClass = (ZWaveVersionCommandClass) this.getNode() .getCommandClass(CommandClass.VERSION); if (versionCommandClass == null) { logger.error(String.format( "NODE %d: Version command class not supported," + "reverting to version 1 for command class %s (0x%02x)", this.getNode().getNodeId(), commandClass.getCommandClass().getLabel(), commandClass.getCommandClass().getKey())); return null; } return versionCommandClass.getCommandClassVersionMessage(commandClass.getCommandClass()); }; /** * Returns the current ZWave library type */ public LibraryType getLibraryType() { return libraryType; } /** * Returns the version of the protocol used by the device * * @return Protocol version as double (version . subversion) */ public String getProtocolVersion() { return protocolVersion; } /** * Returns the version of the firmware used by the device * * @return Application version as double (version . subversion) */ public String getApplicationVersion() { return applicationVersion; } public enum LibraryType { LIB_UNKNOWN(0, "Unknown"), LIB_CONTROLLER_STATIC(1, "Static Controller"), LIB_CONTROLLER(2, "Controller"), LIB_SLAVE_ENHANCED(3, "Enhanced Slave"), LIB_SLAVE(4, "Static Controller"), LIB_INSTALLER(5, "Installer"), LIB_SLAVE_ROUTING(6, "Routing Slave"), LIB_CONTROLLER_BRIDGE(7, "Bridge Controller"), LIB_TEST(8, "Test"); /** * A mapping between the integer code and its corresponding Library type * to facilitate lookup by code. */ private static Map<Integer, LibraryType> libraryMapping; private int key; private String label; private LibraryType(int key, String label) { this.key = key; this.label = label; } private static void initMapping() { libraryMapping = new HashMap<Integer, LibraryType>(); for (LibraryType s : values()) { libraryMapping.put(s.key, s); } } /** * Lookup function based on the sensor type code. * Returns null if the code does not exist. * * @param i the code to lookup * @return enumeration value of the sensor type. */ public static LibraryType getLibraryType(int i) { if (libraryMapping == null) { initMapping(); } if (libraryMapping.get(i) == null) { return LIB_UNKNOWN; } return libraryMapping.get(i); } /** * @return the key */ public int getKey() { return key; } /** * @return the label */ public String getLabel() { return label; } } }