/** * 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.ConfigurationParameter; 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.openhab.binding.zwave.internal.protocol.event.ZWaveCommandClassValueEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.annotations.XStreamOmitField; /** * Handles the Configuration command class. This allows reading and writing of * node configuration parameters * * @author Chris Jackson * @since 1.4.0 */ @XStreamAlias("configurationCommandClass") public class ZWaveConfigurationCommandClass extends ZWaveCommandClass { @XStreamOmitField private static final Logger logger = LoggerFactory.getLogger(ZWaveConfigurationCommandClass.class); private static final int CONFIGURATIONCMD_SET = 0x04; private static final int CONFIGURATIONCMD_GET = 0x05; private static final int CONFIGURATIONCMD_REPORT = 0x06; // Stores the list of configuration parameters. These are used for persistence of values and restore. private Map<Integer, ConfigurationParameter> configParameters = new HashMap<Integer, ConfigurationParameter>(); /** * Creates a new instance of the ZWaveConfigurationCommandClass 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 ZWaveConfigurationCommandClass(ZWaveNode node, ZWaveController controller, ZWaveEndpoint endpoint) { super(node, controller, endpoint); } /** * {@inheritDoc} */ @Override public CommandClass getCommandClass() { return CommandClass.CONFIGURATION; } /** * {@inheritDoc} */ @Override public void handleApplicationCommandRequest(SerialMessage serialMessage, int offset, int endpoint) { logger.debug("NODE {}: Received Configuration Request", this.getNode().getNodeId()); int command = serialMessage.getMessagePayloadByte(offset); switch (command) { case CONFIGURATIONCMD_SET: logger.trace("NODE {}: Process Configuration Set", this.getNode().getNodeId()); processConfigurationReport(serialMessage, offset); break; case CONFIGURATIONCMD_GET: logger.warn( String.format("NODE %d: Command 0x%02X not implemented.", this.getNode().getNodeId(), command)); return; case CONFIGURATIONCMD_REPORT: logger.trace("NODE {}: Process Configuration Report", this.getNode().getNodeId()); processConfigurationReport(serialMessage, offset); break; default: logger.warn(String.format("NODE %d: Unsupported Command 0x%02X for command class %s (0x%02X).", this.getNode().getNodeId(), command, this.getCommandClass().getLabel(), this.getCommandClass().getKey())); } } /** * Processes a CONFIGURATIONCMD_REPORT / CONFIGURATIONCMD_SET message. * * @param serialMessage * the incoming message to process. * @param offset * the offset position from which to start message processing. * @param endpoint * the endpoint or instance number this message is meant for. */ protected void processConfigurationReport(SerialMessage serialMessage, int offset) { // Extract the parameter index and value int parameter = serialMessage.getMessagePayloadByte(offset + 1); int size = serialMessage.getMessagePayloadByte(offset + 2); // ZWave plus devices seem to return 0 if we request a parameter that doesn't exist if (size == 0) { logger.warn("NODE {}: Parameter {} response has 0 length", this.getNode().getNodeId(), parameter); return; } // Recover the data try { int value = extractValue(serialMessage.getMessagePayload(), offset + 3, size); logger.debug("NODE {}: Node configuration report, parameter={}, value={}, size={}", this.getNode().getNodeId(), parameter, value, size); ConfigurationParameter configurationParameter; // Check if the parameter exists in our list configurationParameter = this.configParameters.get(parameter); if (configurationParameter == null) { configurationParameter = new ConfigurationParameter(parameter, value, size); } else { configurationParameter.setValue(value); } this.configParameters.put(parameter, configurationParameter); ZWaveConfigurationParameterEvent zEvent = new ZWaveConfigurationParameterEvent(this.getNode().getNodeId(), configurationParameter); this.getController().notifyEventListeners(zEvent); } catch (NumberFormatException e) { return; } } /** * Gets a SerialMessage with the CONFIGURATIONCMD_GET command * * @return the serial message */ public SerialMessage getConfigMessage(int parameter) { // Check if the parameter exists in our list ConfigurationParameter configurationParameter = this.configParameters.get(parameter); if (configurationParameter != null && configurationParameter.getWriteOnly() == true) { logger.debug("NODE {}: CONFIGURATIONCMD_GET ignored for parameter {} - parameter is write only", this.getNode().getNodeId(), parameter); return null; } logger.debug("NODE {}: Creating new message for application command CONFIGURATIONCMD_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(), 3, (byte) getCommandClass().getKey(), (byte) CONFIGURATIONCMD_GET, (byte) (parameter & 0xff) }; result.setMessagePayload(newPayload); return result; } /** * Gets a SerialMessage with the CONFIGURATIONCMD_SET command * * @param parameter the parameter to set. * @return the serial message */ public SerialMessage setConfigMessage(ConfigurationParameter parameter) { if (parameter != null && parameter.getReadOnly() == true) { logger.debug("NODE {}: CONFIGURATIONCMD_SET ignored for parameter {} - parameter is read only", this.getNode().getNodeId(), parameter); return null; } logger.debug("NODE {}: Creating new message for application command CONFIGURATIONCMD_SET", this.getNode().getNodeId()); SerialMessage result = new SerialMessage(this.getNode().getNodeId(), SerialMessageClass.SendData, SerialMessageType.Request, SerialMessageClass.SendData, SerialMessagePriority.Config); byte[] newPayload = new byte[parameter.getSize() + 6]; newPayload[0] = (byte) this.getNode().getNodeId(); newPayload[1] = (byte) (4 + parameter.getSize()); newPayload[2] = (byte) getCommandClass().getKey(); newPayload[3] = (byte) CONFIGURATIONCMD_SET; newPayload[4] = (byte) (parameter.getIndex() & 0xFF); newPayload[5] = (byte) (parameter.getSize() & 0xFF); for (int i = 0; i < parameter.getSize(); i++) { newPayload[6 + i] = (byte) (parameter.getValue() >> ((parameter.getSize() - i - 1) * 8) & 0xFF); } result.setMessagePayload(newPayload); return result; } /** * Gets the stored parameter. * * @param index the parameter to get. * @return the stored parameter value; */ public ConfigurationParameter getParameter(Integer index) { return this.configParameters.get(index); } /** * Sets a parameter as Read Only * Some parameters in some devices can not be written to. Trying to write them results * in a timeout and this should be avoided. * * @param index the parameter index * @param readOnly true if the parameter can not be read */ public void setParameterReadOnly(Integer index, boolean readOnly) { ConfigurationParameter configurationParameter; // Check if the parameter exists in our list configurationParameter = this.configParameters.get(index); if (configurationParameter == null) { configurationParameter = new ConfigurationParameter(index, 0, 1); configParameters.put(index, configurationParameter); } configurationParameter.setReadOnly(readOnly); } /** * Sets a parameter as Write Only * Some parameters in some devices can not be read. Trying to read them results * in a timeout and this should be avoided. * * @param index the parameter index * @param writeOnly true if the parameter can not be read */ public void setParameterWriteOnly(Integer index, Integer size, boolean writeOnly) { ConfigurationParameter configurationParameter; // Check if the parameter exists in our list configurationParameter = this.configParameters.get(index); if (configurationParameter == null) { configurationParameter = new ConfigurationParameter(index, 0, size); configParameters.put(index, configurationParameter); } configurationParameter.setWriteOnly(writeOnly); } /** * ZWave configuration parameter received event. * Sent from the Configuration Command Class to the binding * when a configuration value is received. * * @author Chris Jackson * @since 1.4.0 */ public class ZWaveConfigurationParameterEvent extends ZWaveCommandClassValueEvent { /** * Constructor. Creates a new instance of the ZWaveConfigurationParameterEvent * class. * * @param nodeId the nodeId of the event. Must be set to the controller node. */ public ZWaveConfigurationParameterEvent(int nodeId, ConfigurationParameter parameter) { super(nodeId, 0, CommandClass.CONFIGURATION, parameter); } /** * Returns the {@link ConfigurationParameter} that was received as event. * * @return the configuration parameter. */ public ConfigurationParameter getParameter() { return (ConfigurationParameter) this.getValue(); } } }