/** * 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.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.openhab.binding.zwave.internal.config.ZWaveDbCommandClass; 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 Binary Sensor command class. Binary sensors indicate there * status or event as on (0xFF) or off (0x00). * The commands include the possibility to get a given * value and report a value. * * @author Jan-Willem Spuij * @since 1.3.0 */ @XStreamAlias("binarySensorCommandClass") public class ZWaveBinarySensorCommandClass extends ZWaveCommandClass implements ZWaveGetCommands, ZWaveCommandClassDynamicState { @XStreamOmitField private static final Logger logger = LoggerFactory.getLogger(ZWaveBinarySensorCommandClass.class); private static final int MAX_SUPPORTED_VERSION = 2; private static final int SENSOR_BINARY_GET = 0x02; private static final int SENSOR_BINARY_REPORT = 0x03; @XStreamOmitField private boolean dynamicDone = false; private boolean isGetSupported = true; /** * Creates a new instance of the ZWaveBinarySensorCommandClass 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 ZWaveBinarySensorCommandClass(ZWaveNode node, ZWaveController controller, ZWaveEndpoint endpoint) { super(node, controller, endpoint); } /** * {@inheritDoc} */ @Override public CommandClass getCommandClass() { return CommandClass.SENSOR_BINARY; } /** * {@inheritDoc} */ @Override public void handleApplicationCommandRequest(SerialMessage serialMessage, int offset, int endpoint) { logger.trace("Handle Message Sensor Binary Request"); logger.debug("NODE {}: Received Sensor Binary Request (v{})", this.getNode().getNodeId(), this.getVersion()); int command = serialMessage.getMessagePayloadByte(offset); switch (command) { case SENSOR_BINARY_GET: logger.warn("NODE {}: Command {} not implemented.", this.getNode().getNodeId(), command); return; case SENSOR_BINARY_REPORT: logger.trace("Process Sensor Binary Report"); int value = serialMessage.getMessagePayloadByte(offset + 1); SensorType sensorType = SensorType.UNKNOWN; if (this.getVersion() > 1 && serialMessage.getMessagePayload().length > offset + 2) { logger.debug("Processing Sensor Type {}", serialMessage.getMessagePayloadByte(offset + 2)); // For V2, we have the sensor type after the value sensorType = SensorType.getSensorType(serialMessage.getMessagePayloadByte(offset + 2)); logger.debug("Sensor Type is {}", sensorType); if (sensorType == null) { sensorType = SensorType.UNKNOWN; } } logger.debug("NODE {}: Sensor Binary report, type={}, value={}", this.getNode().getNodeId(), sensorType.getLabel(), value); ZWaveBinarySensorValueEvent zEvent = new ZWaveBinarySensorValueEvent(this.getNode().getNodeId(), endpoint, sensorType, value); this.getController().notifyEventListeners(zEvent); dynamicDone = true; 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())); } } /** * Gets a SerialMessage with the SENSOR_BINARY_GET command * * @return the serial message */ @Override public SerialMessage getValueMessage() { if (isGetSupported == false) { logger.debug("NODE {}: Node doesn't support get requests", this.getNode().getNodeId()); return null; } logger.debug("NODE {}: Creating new message for application command SENSOR_BINARY_GET", this.getNode().getNodeId()); SerialMessage result = new SerialMessage(this.getNode().getNodeId(), SerialMessageClass.SendData, SerialMessageType.Request, SerialMessageClass.ApplicationCommandHandler, SerialMessagePriority.Get); byte[] newPayload = { (byte) this.getNode().getNodeId(), 2, (byte) getCommandClass().getKey(), (byte) SENSOR_BINARY_GET }; // Should there be another byte here to specify the sensor type? // Looking at the RaZberry doc, it talks about requesting the sensor type // and using FF for the first sensor. // Maybe this is a V2 feature - need to find some docs on V2! result.setMessagePayload(newPayload); return result; } @Override public boolean setOptions(ZWaveDbCommandClass options) { if (options.isGetSupported != null) { isGetSupported = options.isGetSupported; } return true; } /** * {@inheritDoc} */ @Override public Collection<SerialMessage> getDynamicValues(boolean refresh) { ArrayList<SerialMessage> result = new ArrayList<SerialMessage>(); if (refresh == true || dynamicDone == false) { result.add(getValueMessage()); } return result; } /** * {@inheritDoc} */ @Override public int getMaxVersion() { return MAX_SUPPORTED_VERSION; } /** * Z-Wave SensorType enumeration. The sensor type indicates the type * of sensor that is reported. * * @author Chris Jackson * @since 1.5.0 */ @XStreamAlias("binarySensorType") public enum SensorType { UNKNOWN(0x00, "Unknown"), GENERAL(0x01, "General Purpose"), SMOKE(0x02, "Smoke"), CO(0x03, "Carbon Monoxide"), CO2(0x04, "Carbon Dioxide"), HEAT(0x05, "Heat"), WATER(0x06, "Water"), FREEZE(0x07, "Freeze"), TAMPER(0x08, "Tamper"), AUX(0x09, "Aux"), DOORWINDOW(0x0a, "Door/Window"), TILT(0x0b, "Tilt"), MOTION(0x0c, "Motion"), GLASSBREAK(0x0d, "Glass Break"); /** * A mapping between the integer code and its corresponding Sensor type * to facilitate lookup by code. */ private static Map<Integer, SensorType> codeToSensorTypeMapping; private int key; private String label; private SensorType(int key, String label) { this.key = key; this.label = label; } private static void initMapping() { codeToSensorTypeMapping = new HashMap<Integer, SensorType>(); for (SensorType s : values()) { codeToSensorTypeMapping.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 SensorType getSensorType(int i) { if (codeToSensorTypeMapping == null) { initMapping(); } return codeToSensorTypeMapping.get(i); } /** * @return the key */ public int getKey() { return key; } /** * @return the label */ public String getLabel() { return label; } } /** * Z-Wave Binary Sensor Event class. Indicates that an sensor value changed. * * @author Chris Jackson * @since 1.5.0 */ public class ZWaveBinarySensorValueEvent extends ZWaveCommandClassValueEvent { private SensorType sensorType; /** * Constructor. Creates a instance of the ZWaveBinarySensorValueEvent class. * * @param nodeId the nodeId of the event * @param endpoint the endpoint of the event. * @param sensorType the sensor type that triggered the event; * @param value the value for the event. */ private ZWaveBinarySensorValueEvent(int nodeId, int endpoint, SensorType sensorType, Object value) { super(nodeId, endpoint, CommandClass.SENSOR_BINARY, value); this.sensorType = sensorType; } /** * Gets the alarm type for this alarm sensor value event. */ public SensorType getSensorType() { return sensorType; } } }