/**
* 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.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
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.ZWaveDeviceClass;
import org.openhab.binding.zwave.internal.protocol.ZWaveDeviceClass.Basic;
import org.openhab.binding.zwave.internal.protocol.ZWaveDeviceClass.Generic;
import org.openhab.binding.zwave.internal.protocol.ZWaveDeviceClass.Specific;
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 Multi Instance / Multi Channel command class. The
* Multi Instance command class is used to control multiple instances
* of the same device class on the node. Multi Channel support (version 2)
* of the command class can also handle multiple instances of different
* command classes. The instances are called endpoints in this version.
*
* Useful references -:
* https://groups.google.com/d/msg/openzwave/FeFNBI8GAKk/dyXAO54BiqgJ
*
* @author Jan-Willem Spuij
* @author Chris Jackson
* @author Michiel Leegwater
* @since 1.3.0
*/
@XStreamAlias("multiInstanceCommandClass")
public class ZWaveMultiInstanceCommandClass extends ZWaveCommandClass {
@XStreamOmitField
private static final Logger logger = LoggerFactory.getLogger(ZWaveMultiInstanceCommandClass.class);
private static final int MAX_SUPPORTED_VERSION = 2;
// Version 1
private static final int MULTI_INSTANCE_GET = 0x04;
private static final int MULTI_INSTANCE_REPORT = 0x05;
private static final int MULTI_INSTANCE_ENCAP = 0x06;
// Version 2
private static final int MULTI_CHANNEL_ENDPOINT_GET = 0x07;
private static final int MULTI_CHANNEL_ENDPOINT_REPORT = 0x08;
private static final int MULTI_CHANNEL_CAPABILITY_GET = 0x09;
private static final int MULTI_CHANNEL_CAPABILITY_REPORT = 0x0a;
private static final int MULTI_CHANNEL_ENDPOINT_FIND = 0x0b;
private static final int MULTI_CHANNEL_ENDPOINT_FIND_REPORT = 0x0c;
private static final int MULTI_CHANNEL_ENCAP = 0x0d;
private final Map<Integer, ZWaveEndpoint> endpoints = new HashMap<Integer, ZWaveEndpoint>();
private boolean endpointsAreTheSameDeviceClass;
// List of classes that DO NOT support multiple instances.
// This is used to reduce the number of requests during initialisation.
// Only add a class to this list if you are sure it doesn't support multiple instances!
@XStreamOmitField
private static final List<CommandClass> singleInstanceClasses = Arrays.asList(CommandClass.NO_OPERATION,
CommandClass.ASSOCIATION, CommandClass.MULTI_INSTANCE_ASSOCIATION, CommandClass.CONFIGURATION,
CommandClass.CLOCK, CommandClass.WAKE_UP, CommandClass.BATTERY);
/**
* Creates a new instance of the ZWaveMultiInstanceCommandClass 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 ZWaveMultiInstanceCommandClass(ZWaveNode node, ZWaveController controller, ZWaveEndpoint endpoint) {
super(node, controller, endpoint);
}
/**
* {@inheritDoc}
*/
@Override
public CommandClass getCommandClass() {
return CommandClass.MULTI_INSTANCE;
}
/**
* {@inheritDoc}
*/
@Override
public int getMaxVersion() {
return MAX_SUPPORTED_VERSION;
};
/**
* Gets the endpoint object using its endpoint ID as key.
* Returns null if the endpoint is not found.
*
* @param endpointId the endpoint ID of the endpoint to get.
* @return Endpoint object
* @throws IllegalArgumentException thrown when the endpoint is not found.
*/
public ZWaveEndpoint getEndpoint(int endpointId) {
return this.endpoints.get(endpointId);
}
/**
* Gets the collection of endpoints attached to this node.
*
* @return the collection of endpoints.
*/
public Collection<ZWaveEndpoint> getEndpoints() {
return this.endpoints.values();
}
/**
* {@inheritDoc}
*/
@Override
public void handleApplicationCommandRequest(SerialMessage serialMessage, int offset, int endpointId) {
logger.debug("NODE {}: Received Multi-instance/Multi-channel Request", this.getNode().getNodeId());
int command = serialMessage.getMessagePayloadByte(offset);
switch (command) {
case MULTI_INSTANCE_GET:
case MULTI_CHANNEL_ENDPOINT_GET:
case MULTI_CHANNEL_CAPABILITY_GET:
case MULTI_CHANNEL_ENDPOINT_FIND:
case MULTI_CHANNEL_ENDPOINT_FIND_REPORT:
logger.warn("NODE {}: Command {} not implemented.", this.getNode().getNodeId(), command);
return;
case MULTI_INSTANCE_REPORT:
handleMultiInstanceReportResponse(serialMessage, offset + 1);
break;
case MULTI_INSTANCE_ENCAP:
handleMultiInstanceEncapResponse(serialMessage, offset + 1);
break;
case MULTI_CHANNEL_ENDPOINT_REPORT:
handleMultiChannelEndpointReportResponse(serialMessage, offset + 1);
break;
case MULTI_CHANNEL_CAPABILITY_REPORT:
handleMultiChannelCapabilityReportResponse(serialMessage, offset + 1);
break;
case MULTI_CHANNEL_ENCAP:
handleMultiChannelEncapResponse(serialMessage, offset + 1);
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()));
}
}
/**
* Handles Multi Instance Report message. Handles Report on
* the number of instances for the command class.
* This is for Version 1 of the command class.
*
* @param serialMessage the serial message to process.
* @param offset the offset at which to start processing.
*/
private void handleMultiInstanceReportResponse(SerialMessage serialMessage, int offset) {
logger.trace("Process Multi-instance Report");
int commandClassCode = serialMessage.getMessagePayloadByte(offset);
int instances = serialMessage.getMessagePayloadByte(offset + 1);
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 = {}", this.getNode().getNodeId(), commandClass.getLabel());
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 (instances == 0) {
logger.debug("NODE {}: Instances = 0. Setting to 1.", this.getNode().getNodeId());
instances = 1;
}
zwaveCommandClass.setInstances(instances);
logger.debug("NODE {}: Command class {}, has {} instance(s).", this.getNode().getNodeId(),
commandClass.getLabel(), instances);
}
/**
* Handles Multi Instance Encapsulation message. Decapsulates
* an Application Command message and handles it using the right
* instance.
*
* @param serialMessage the serial message to process.
* @param offset the offset at which to start procesing.
*/
private void handleMultiInstanceEncapResponse(SerialMessage serialMessage, int offset) {
logger.trace("Process Multi-instance Encapsulation");
int instance = serialMessage.getMessagePayloadByte(offset);
int commandClassCode = serialMessage.getMessagePayloadByte(offset + 1);
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(String.format("NODE %d: Requested Command Class = %s (0x%02x)", this.getNode().getNodeId(),
commandClass.getLabel(), commandClassCode));
ZWaveCommandClass zwaveCommandClass = null;
// first get command class from endpoint, if supported
if (this.getVersion() >= 2) {
ZWaveEndpoint endpoint = this.endpoints.get(instance);
if (endpoint != null) {
zwaveCommandClass = endpoint.getCommandClass(commandClass);
if (zwaveCommandClass == null) {
logger.warn(String.format(
"NODE %d: CommandClass %s (0x%02x) not implemented by endpoint %d, fallback to main node.",
this.getNode().getNodeId(), commandClass.getLabel(), commandClassCode, instance));
}
}
}
if (zwaveCommandClass == null) {
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;
}
logger.debug("NODE {}: Instance = {}, calling handleApplicationCommandRequest.", this.getNode().getNodeId(),
instance);
zwaveCommandClass.handleApplicationCommandRequest(serialMessage, offset + 2, instance);
}
/**
* Handles Multi Channel Endpoint Report message. Handles Report on
* the number of endpoints and whether they are dynamic and/or have the
* same command classes.
* This is for Version 2 of the command class.
*
* @param serialMessage the serial message to process.
* @param offset the offset at which to start processing.
*/
private void handleMultiChannelEndpointReportResponse(SerialMessage serialMessage, int offset) {
logger.debug("Process Multi-channel endpoint Report");
boolean changingNumberOfEndpoints = (serialMessage.getMessagePayloadByte(offset) & 0x80) != 0;
endpointsAreTheSameDeviceClass = (serialMessage.getMessagePayloadByte(offset) & 0x40) != 0;
int endpoints = serialMessage.getMessagePayloadByte(offset + 1) & 0x7F;
logger.debug("NODE {}: Changing number of endpoints = {}", this.getNode().getNodeId(),
changingNumberOfEndpoints ? "true" : false);
logger.debug("NODE {}: Endpoints are the same device class = {}", this.getNode().getNodeId(),
endpointsAreTheSameDeviceClass ? "true" : false);
logger.debug("NODE {}: Number of endpoints = {}", this.getNode().getNodeId(), endpoints);
// TODO: handle dynamically added endpoints. Have never seen such a device.
if (changingNumberOfEndpoints) {
logger.warn(
"NODE {}: Changing number of endpoints, expect some weird behavior during multi channel handling.",
this.getNode().getNodeId());
}
// Add all the endpoints
for (int i = 1; i <= endpoints; i++) {
ZWaveEndpoint endpoint = new ZWaveEndpoint(i);
this.endpoints.put(i, endpoint);
}
}
/**
* Handles Multi Channel Capability Report message. Handles Report on
* an endpoint and adds command classes to the endpoint.
* This is for Version 2 of the command class.
*
* @param serialMessage the serial message to process.
* @param offset the offset at which to start processing.
*/
private void handleMultiChannelCapabilityReportResponse(SerialMessage serialMessage, int offset) {
logger.debug("NODE {}: Process Multi-channel capability Report", this.getNode().getNodeId());
int receivedEndpointId = serialMessage.getMessagePayloadByte(offset) & 0x7F;
boolean dynamic = ((serialMessage.getMessagePayloadByte(offset) & 0x80) != 0);
int genericDeviceClass = serialMessage.getMessagePayloadByte(offset + 1);
int specificDeviceClass = serialMessage.getMessagePayloadByte(offset + 2);
logger.debug("NODE {}: Endpoints are the same device class = {}", this.getNode().getNodeId(),
endpointsAreTheSameDeviceClass ? "true" : false);
// Loop either all endpoints, or just set command classes on one, depending on whether
// all endpoints have the same device class.
int startId = this.endpointsAreTheSameDeviceClass ? 1 : receivedEndpointId;
int endId = this.endpointsAreTheSameDeviceClass ? this.endpoints.size() : receivedEndpointId;
boolean supportsBasicCommandClass = this.getNode().supportsCommandClass(CommandClass.BASIC);
for (int endpointId = startId; endpointId <= endId; endpointId++) {
// Create a new endpoint
ZWaveEndpoint endpoint = this.endpoints.get(endpointId);
if (endpoint == null) {
logger.error("NODE {}: Endpoint {} not found. Cannot set command classes.", this.getNode().getNodeId(),
endpointId);
continue;
}
// Add the device classes
if (!updateDeviceClass(endpoint, genericDeviceClass, specificDeviceClass, dynamic)) {
// Updating device class failed, already logged, continue with next endpoint
continue;
}
// Add basic command class, if it's also supported by the parent node.
if (supportsBasicCommandClass) {
ZWaveCommandClass commandClass = new ZWaveBasicCommandClass(this.getNode(), this.getController(),
endpoint);
endpoint.addCommandClass(commandClass);
}
// Add all the command classes supported by this endpoint
addSupportedCommandClasses(serialMessage, offset, endpoint);
}
if (!this.endpointsAreTheSameDeviceClass) {
for (ZWaveEndpoint ep : this.endpoints.values()) {
// only advance node stage when all endpoints are known.
if (ep.getDeviceClass().getBasicDeviceClass() == Basic.NOT_KNOWN) {
return;
}
}
}
}
/**
* Determines the device class properties of the endpoint.
*
* @param endpoint The endpoint to update.
* @param genericDeviceClass The generic device class of the parent device of the endpoint
* @param specificDeviceClass The specific device class of the parent device of the endpoint
* @param dynamic True when the endpoint is dynamic.
* @return True when successful, false otherwise
*/
private boolean updateDeviceClass(ZWaveEndpoint endpoint, int genericDeviceClass, int specificDeviceClass,
boolean dynamic) {
Basic basic = this.getNode().getDeviceClass().getBasicDeviceClass();
Generic generic = Generic.getGeneric(genericDeviceClass);
if (generic == null) {
logger.error(
String.format("NODE %d: Endpoint %d has invalid device class. generic = 0x%02x, specific = 0x%02x.",
this.getNode().getNodeId(), endpoint, genericDeviceClass, specificDeviceClass));
return false;
}
Specific specific = Specific.getSpecific(generic, specificDeviceClass);
if (specific == null) {
logger.error(
String.format("NODE %d: Endpoint %d has invalid device class. generic = 0x%02x, specific = 0x%02x.",
this.getNode().getNodeId(), endpoint, genericDeviceClass, specificDeviceClass));
return false;
}
logger.debug("NODE {}: Endpoint Id = {}", this.getNode().getNodeId(), endpoint.getEndpointId());
logger.debug("NODE {}: Endpoints is dynamic = {}", this.getNode().getNodeId(), dynamic ? "true" : false);
logger.debug(String.format("NODE %d: Basic = %s 0x%02x", this.getNode().getNodeId(), basic.getLabel(),
basic.getKey()));
logger.debug(String.format("NODE %d: Generic = %s 0x%02x", this.getNode().getNodeId(), generic.getLabel(),
generic.getKey()));
logger.debug(String.format("NODE %d: Specific = %s 0x%02x", this.getNode().getNodeId(), specific.getLabel(),
specific.getKey()));
ZWaveDeviceClass deviceClass = endpoint.getDeviceClass();
deviceClass.setBasicDeviceClass(basic);
deviceClass.setGenericDeviceClass(generic);
deviceClass.setSpecificDeviceClass(specific);
return true;
}
/**
* Adds command classes to the endpoint based on the message from the device.
*
* @param serialMessage The message to get command classes from.
* @param offset The offset in the message.
* @param endpoint The endpoint
*/
private void addSupportedCommandClasses(SerialMessage serialMessage, int offset, ZWaveEndpoint endpoint) {
for (int i = 0; i < serialMessage.getMessagePayload().length - offset - 3; i++) {
// Get the command class ID
int data = serialMessage.getMessagePayloadByte(offset + 3 + i);
if (data == 0xef) {
// TODO: Implement control command classes
break;
}
// Create the command class
ZWaveCommandClass commandClass = ZWaveCommandClass.getInstance(data, this.getNode(), this.getController(),
endpoint);
if (commandClass == null) {
continue;
}
logger.debug("NODE {}: Endpoint {}: Adding command class {}.", this.getNode().getNodeId(),
endpoint.getEndpointId(), commandClass.getCommandClass().getLabel());
endpoint.addCommandClass(commandClass);
ZWaveCommandClass parentClass = this.getNode().getCommandClass(commandClass.getCommandClass());
// Copy version info to endpoint classes.
if (parentClass != null) {
commandClass.setVersion(parentClass.getVersion());
}
// With V2, we only have a single instance
commandClass.setInstances(1);
}
}
/**
* Handles Multi Channel Encapsulation message. Decapsulates
* an Application Command message and handles it using the right
* endpoint.
*
* @param serialMessage the serial message to process.
* @param offset the offset at which to start processing.
*/
private void handleMultiChannelEncapResponse(SerialMessage serialMessage, int offset) {
logger.trace("Process Multi-channel Encapsulation");
if (serialMessage.getMessagePayload().length < offset + 2) {
logger.error("NODE {}: Invalid data length ({}/{})", this.getNode().getNodeId(),
serialMessage.getMessagePayload().length, offset);
return;
}
CommandClass commandClass;
ZWaveCommandClass zwaveCommandClass;
int endpointId = serialMessage.getMessagePayloadByte(offset);
int commandClassCode = serialMessage.getMessagePayloadByte(offset + 2);
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(String.format("NODE %d: Requested Command Class = %s (0x%02x)", this.getNode().getNodeId(),
commandClass.getLabel(), commandClassCode));
ZWaveEndpoint endpoint = this.endpoints.get(endpointId);
if (endpoint == null) {
logger.error("NODE {}: Endpoint {} not found. Cannot set command classes.", this.getNode().getNodeId(),
endpointId);
return;
}
zwaveCommandClass = endpoint.getCommandClass(commandClass);
if (zwaveCommandClass == null) {
logger.warn(String.format(
"NODE %d: CommandClass %s (0x%02x) not implemented by endpoint %d, fallback to main node.",
this.getNode().getNodeId(), commandClass.getLabel(), commandClassCode, endpointId));
zwaveCommandClass = this.getNode().getCommandClass(commandClass);
}
if (zwaveCommandClass == null) {
logger.error(String.format("NODE %d: CommandClass %s (0x%02x) not implemented.", this.getNode().getNodeId(),
commandClass.getLabel(), commandClassCode));
return;
}
logger.debug("NODE {}: Endpoint = {}, calling handleApplicationCommandRequest.", this.getNode().getNodeId(),
endpointId);
zwaveCommandClass.handleApplicationCommandRequest(serialMessage, offset + 3, endpointId);
}
/**
* Gets a SerialMessage with the MULTI_INSTANCE_GET command.
* Returns the number of instances for this command class.
*
* @param the command class to return the number of instances for.
* @return the serial message.
*/
public SerialMessage getMultiInstanceGetMessage(CommandClass commandClass) {
logger.debug("NODE {}: Creating new message for command MULTI_INSTANCE_GET command class {}",
this.getNode().getNodeId(), commandClass.getLabel());
SerialMessage result = new SerialMessage(this.getNode().getNodeId(), SerialMessageClass.SendData,
SerialMessageType.Request, SerialMessageClass.ApplicationCommandHandler, SerialMessagePriority.Get);
byte[] newPayload = { (byte) this.getNode().getNodeId(), 3, (byte) getCommandClass().getKey(),
(byte) MULTI_INSTANCE_GET, (byte) commandClass.getKey() };
result.setMessagePayload(newPayload);
return result;
}
/**
* Gets a SerialMessage with the MULTI_INSTANCE_ENCAP command.
* Encapsulates a message for a specific instance.
*
* @param serialMessage the serial message to encapsulate
* @param instance the number of the instance to encapsulate the message for.
* @return the encapsulated serial message.
*/
public SerialMessage getMultiInstanceEncapMessage(SerialMessage serialMessage, int instance) {
logger.debug("NODE {}: Creating new message for command MULTI_INSTANCE_ENCAP instance {}",
this.getNode().getNodeId(), instance);
byte[] payload = serialMessage.getMessagePayload();
byte[] newPayload = new byte[payload.length + 3];
System.arraycopy(payload, 0, newPayload, 0, 2);
System.arraycopy(payload, 0, newPayload, 3, payload.length);
newPayload[1] += 3;
newPayload[2] = (byte) this.getCommandClass().getKey();
newPayload[3] = MULTI_INSTANCE_ENCAP;
newPayload[4] = (byte) (instance);
serialMessage.setMessagePayload(newPayload);
return serialMessage;
}
/**
* Gets a SerialMessage with the MULTI CHANNEL ENDPOINT GET command.
* Returns the endpoints for this node.
*
* @return the serial message.
*/
public SerialMessage getMultiChannelEndpointGetMessage() {
logger.debug("NODE {}: Creating new message for command MULTI_CHANNEL_ENDPOINT_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) MULTI_CHANNEL_ENDPOINT_GET };
result.setMessagePayload(newPayload);
return result;
}
/**
* Gets a SerialMessage with the MULTI_CHANNEL_CAPABILITY_GET command.
* Gets the capabilities for a specific endpoint.
*
* @param the number of the endpoint to get the
* @return the serial message.
*/
public SerialMessage getMultiChannelCapabilityGetMessage(ZWaveEndpoint endpoint) {
logger.debug("NODE {}: Creating new message for command MULTI_CHANNEL_CAPABILITY_GET endpoint {}",
this.getNode().getNodeId(), endpoint.getEndpointId());
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) MULTI_CHANNEL_CAPABILITY_GET, (byte) endpoint.getEndpointId() };
result.setMessagePayload(newPayload);
return result;
}
/**
* Gets a SerialMessage with the MULTI_INSTANCE_ENCAP command.
* Encapsulates a message for a specific instance.
*
* @param serialMessage the serial message to encapsulate
* @param endpoint the endpoint to encapsulate the message for.
* @return the encapsulated serial message.
*/
public SerialMessage getMultiChannelEncapMessage(SerialMessage serialMessage, ZWaveEndpoint endpoint) {
logger.debug("NODE {}: Creating new message for command MULTI_CHANNEL_ENCAP endpoint {}",
this.getNode().getNodeId(), endpoint.getEndpointId());
byte[] payload = serialMessage.getMessagePayload();
byte[] newPayload = new byte[payload.length + 4];
System.arraycopy(payload, 0, newPayload, 0, 2);
System.arraycopy(payload, 0, newPayload, 4, payload.length);
newPayload[1] += 4;
newPayload[2] = (byte) this.getCommandClass().getKey();
newPayload[3] = MULTI_CHANNEL_ENCAP;
newPayload[4] = 0x01;
newPayload[5] = (byte) endpoint.getEndpointId();
serialMessage.setMessagePayload(newPayload);
return serialMessage;
}
/**
* Initializes the Multi instance / endpoint command class by setting the number of instances
* or getting the endpoints.
*
* @return SerialMessage message to send
*/
public ArrayList<SerialMessage> initEndpoints(boolean refresh) {
ArrayList<SerialMessage> result = new ArrayList<SerialMessage>();
logger.debug("NODE {}: Initialising endpoints - version {}", this.getNode().getNodeId(), this.getVersion());
switch (this.getVersion()) {
case 1:
// Get number of instances for all command classes on this node.
for (ZWaveCommandClass commandClass : this.getNode().getCommandClasses()) {
logger.debug("NODE {}: ENDPOINTS - checking {}, Instances {}", this.getNode().getNodeId(),
commandClass.getCommandClass().toString(), commandClass.getInstances());
// Skip classes known NOT to support multiple instances.
// This allows us to reduce the number of frames we send during initialisation
// where we already know it doesn't support multi-instance.
if (singleInstanceClasses.contains(commandClass.getCommandClass())) {
commandClass.setInstances(1);
logger.debug("NODE {}: ENDPOINTS - skipping {}", this.getNode().getNodeId(),
commandClass.getCommandClass().toString());
continue;
}
// Instances is set to 1 after it's initialised
if (commandClass.getInstances() == 0) {
logger.debug("NODE {}: ENDPOINTS - found {}", this.getNode().getNodeId(),
commandClass.getCommandClass().toString());
result.add(getMultiInstanceGetMessage(commandClass.getCommandClass()));
}
}
break;
case 2:
// Set all classes to a single instance
for (ZWaveCommandClass commandClass : this.getNode().getCommandClasses()) {
commandClass.setInstances(1);
}
// Request the number of endpoints
if (refresh == true || this.endpoints.size() == 0) {
result.add(getMultiChannelEndpointGetMessage());
} else {
for (Map.Entry<Integer, ZWaveEndpoint> entry : this.endpoints.entrySet()) {
if (refresh == true || entry.getValue().getCommandClasses().size() == 0) {
result.add(this.getMultiChannelCapabilityGetMessage(entry.getValue()));
}
}
}
break;
default:
logger.warn(String.format("NODE %d: Unknown version %d for command class %s (0x%02x)",
this.getNode().getNodeId(), this.getVersion(), this.getCommandClass().toString(),
this.getCommandClass().getKey()));
break;
}
return result;
};
}