mirror of
https://github.com/zyphlar/LanternPowerMonitor.git
synced 2024-03-08 14:07:47 +00:00
Initial Commit
This commit is contained in:
65
zwave/lantern-zwave/pom.xml
Normal file
65
zwave/lantern-zwave/pom.xml
Normal file
@@ -0,0 +1,65 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>com.lanternsoftware.zwave</groupId>
|
||||
<artifactId>lantern-zwave</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.0.0</version>
|
||||
<name>lantern-zwave</name>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.neuronrobotics</groupId>
|
||||
<artifactId>nrjavaserial</artifactId>
|
||||
<version>3.15.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.29</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-core</artifactId>
|
||||
<version>1.2.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.lanternsoftware.util</groupId>
|
||||
<artifactId>lantern-util-dao</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>testCompile</goal>
|
||||
</goals>
|
||||
<phase>compile</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<optimize>true</optimize>
|
||||
<showDeprecation>true</showDeprecation>
|
||||
<encoding>UTF-8</encoding>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,237 @@
|
||||
package com.lanternsoftware.zwave.controller;
|
||||
|
||||
import com.lanternsoftware.util.NullUtils;
|
||||
import com.lanternsoftware.util.concurrency.ConcurrencyUtils;
|
||||
import com.lanternsoftware.zwave.message.IMessageSubscriber;
|
||||
import com.lanternsoftware.zwave.message.Message;
|
||||
import com.lanternsoftware.zwave.message.MessageEngine;
|
||||
import com.lanternsoftware.zwave.message.RequestMessage;
|
||||
import com.lanternsoftware.zwave.message.ResponseMessage;
|
||||
import com.lanternsoftware.zwave.message.impl.ByteMessage;
|
||||
import com.lanternsoftware.zwave.message.impl.ControllerCapabilitiesRequest;
|
||||
import com.lanternsoftware.zwave.message.impl.ControllerInitialDataRequest;
|
||||
import com.lanternsoftware.zwave.message.impl.GetControllerIdRequest;
|
||||
import com.lanternsoftware.zwave.message.impl.SendDataRequest;
|
||||
import com.lanternsoftware.zwave.node.NodeManager;
|
||||
import gnu.io.CommPortIdentifier;
|
||||
import gnu.io.SerialPort;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class Controller {
|
||||
private static final byte SOF = 0x01;
|
||||
private static final byte ACK = 0x06;
|
||||
private static final byte NAK = 0x15;
|
||||
private static final byte CAN = 0x18;
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(Controller.class);
|
||||
|
||||
private SerialPort serialPort;
|
||||
private OutputStream os;
|
||||
private boolean running = false;
|
||||
private AtomicInteger callbackId = new AtomicInteger(0);
|
||||
private final Object ackMutex = new Object();
|
||||
private final Object responseMutex = new Object();
|
||||
private final Object callbackMutex = new Object();
|
||||
private boolean responseReceived;
|
||||
private final Map<Byte, Byte> callbacks = new HashMap<>();
|
||||
private ExecutorService executor = Executors.newFixedThreadPool(2);
|
||||
private NodeManager nodeManager;
|
||||
|
||||
public boolean start(String _port) {
|
||||
try {
|
||||
CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(_port);
|
||||
serialPort = portIdentifier.open("zwaveport", 2000);
|
||||
serialPort.setSerialPortParams(115200, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
|
||||
serialPort.enableReceiveThreshold(1);
|
||||
serialPort.enableReceiveTimeout(1000);
|
||||
os = serialPort.getOutputStream();
|
||||
running = true;
|
||||
executor.submit(new MessageReceiver());
|
||||
MessageEngine.subscribe(new SendDataRequestHandler());
|
||||
nodeManager = new NodeManager(this);
|
||||
send(new ControllerCapabilitiesRequest());
|
||||
send(new ControllerInitialDataRequest());
|
||||
send(new GetControllerIdRequest());
|
||||
nodeManager.waitForStartup();
|
||||
logger.debug("Finishing Controller Start");
|
||||
return true;
|
||||
} catch (Exception _e) {
|
||||
if (serialPort != null) {
|
||||
serialPort.close();
|
||||
serialPort = null;
|
||||
}
|
||||
logger.error("Exception while starting controller", _e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
running = false;
|
||||
ConcurrencyUtils.sleep(2000);
|
||||
IOUtils.closeQuietly(os);
|
||||
if (serialPort != null) {
|
||||
serialPort.close();
|
||||
}
|
||||
executor.shutdown();
|
||||
}
|
||||
|
||||
public void send(Message _message) {
|
||||
executor.submit(new MessageSender(_message));
|
||||
}
|
||||
|
||||
private class MessageReceiver implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = serialPort.getInputStream();
|
||||
int nextByte = 0;
|
||||
int offset = 0;
|
||||
while (running) {
|
||||
nextByte = is.read();
|
||||
if (nextByte == -1)
|
||||
continue;
|
||||
switch (nextByte) {
|
||||
case SOF:
|
||||
int messageLength = is.read();
|
||||
byte[] buffer = new byte[messageLength + 2];
|
||||
buffer[0] = SOF;
|
||||
buffer[1] = (byte) messageLength;
|
||||
offset = 2;
|
||||
while (offset < messageLength + 2) {
|
||||
offset += is.read(buffer, offset, messageLength + 2 - offset);
|
||||
}
|
||||
processIncomingMessage(buffer);
|
||||
break;
|
||||
case ACK:
|
||||
synchronized (ackMutex) {
|
||||
logger.debug("Received ACK");
|
||||
ackMutex.notify();
|
||||
}
|
||||
MessageEngine.publish(new ByteMessage((byte) nextByte));
|
||||
break;
|
||||
case NAK:
|
||||
case CAN:
|
||||
synchronized (ackMutex) {
|
||||
logger.debug("Received: {}", NullUtils.toHex(new byte[]{(byte) nextByte}));
|
||||
ackMutex.notify();
|
||||
}
|
||||
MessageEngine.publish(new ByteMessage((byte) nextByte));
|
||||
break;
|
||||
default:
|
||||
sendRaw(new byte[]{NAK});
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException _e) {
|
||||
logger.error("Exception while receiving inbound, stopping controller", _e);
|
||||
stop();
|
||||
} finally {
|
||||
IOUtils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class MessageSender implements Runnable {
|
||||
private final Message message;
|
||||
|
||||
MessageSender(Message _message) {
|
||||
message = _message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
synchronized (Controller.this) {
|
||||
byte callback = 0;
|
||||
String log = "Sending message outbound: " + message.describe();
|
||||
if (message.isCallbackExpected()) {
|
||||
callback = (byte) (callbackId.getAndIncrement() % 126 + 1);
|
||||
callbacks.put(callback, message.getNodeId());
|
||||
log += " callback: " + callback;
|
||||
}
|
||||
logger.info(log);
|
||||
byte[] data = message.toByteArray((byte) 0, callback);
|
||||
logger.debug("Sending outbound: {}", NullUtils.toHexBytes(data));
|
||||
responseReceived = false;
|
||||
sendRaw(data);
|
||||
synchronized (ackMutex) {
|
||||
ackMutex.wait(1000);
|
||||
}
|
||||
logger.debug("Finished outbound of: {}", message.describe());
|
||||
}
|
||||
if (message instanceof RequestMessage) {
|
||||
logger.debug("Waiting for response from: {}", message.describe());
|
||||
synchronized (responseMutex) {
|
||||
responseMutex.wait(1000);
|
||||
logger.debug("Response received: {}", responseReceived);
|
||||
responseReceived = false;
|
||||
}
|
||||
}
|
||||
if (message.isCallbackExpected()) {
|
||||
logger.debug("Waiting for callback from: {}", message.describe());
|
||||
synchronized (callbackMutex) {
|
||||
callbackMutex.wait(1000);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException _e) {
|
||||
logger.error("Interrupted while sending outbound", _e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendRaw(byte[] _data) {
|
||||
try {
|
||||
os.write(_data);
|
||||
os.flush();
|
||||
} catch (IOException _e) {
|
||||
logger.error("IO exception while sending outbound", _e);
|
||||
}
|
||||
}
|
||||
|
||||
private void processIncomingMessage(byte[] _buffer) {
|
||||
logger.debug("Received inbound: {}", NullUtils.toHexBytes(_buffer));
|
||||
logger.debug("Sending ACK");
|
||||
sendRaw(new byte[]{ACK});
|
||||
Message message = MessageEngine.decode(_buffer);
|
||||
if (message != null) {
|
||||
logger.info("Received message inbound: {}", message.describe());
|
||||
MessageEngine.publish(message);
|
||||
if (message instanceof ResponseMessage) {
|
||||
synchronized (responseMutex) {
|
||||
responseReceived = true;
|
||||
responseMutex.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SendDataRequestHandler implements IMessageSubscriber<SendDataRequest> {
|
||||
@Override
|
||||
public Class<SendDataRequest> getHandledMessageClass() {
|
||||
return SendDataRequest.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(SendDataRequest _message) {
|
||||
Byte nodeId = callbacks.remove(_message.getCallbackId());
|
||||
if (nodeId != null) {
|
||||
logger.debug("Received callback for node: {} callback id: {}", nodeId, _message.getCallbackId());
|
||||
synchronized (callbackMutex) {
|
||||
callbackMutex.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package com.lanternsoftware.zwave.message;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public enum CommandClass {
|
||||
NO_OPERATION((byte)0x00, "NO_OPERATION"),
|
||||
BASIC((byte)0x20, "BASIC"),
|
||||
CONTROLLER_REPLICATION((byte)0x21, "CONTROLLER_REPLICATION"),
|
||||
APPLICATION_STATUS((byte)0x22, "APPLICATION_STATUS"),
|
||||
ZIP_SERVICES((byte)0x23, "ZIP_SERVICES"),
|
||||
ZIP_SERVER((byte)0x24, "ZIP_SERVER"),
|
||||
SWITCH_BINARY((byte)0x25, "SWITCH_BINARY", true, 0),
|
||||
SWITCH_MULTILEVEL((byte)0x26, "SWITCH_MULTILEVEL", true, 0),
|
||||
SWITCH_ALL((byte)0x27, "SWITCH_ALL"),
|
||||
SWITCH_TOGGLE_BINARY((byte)0x28, "SWITCH_TOGGLE_BINARY"),
|
||||
SWITCH_TOGGLE_MULTILEVEL((byte)0x29, "SWITCH_TOGGLE_MULTILEVEL"),
|
||||
CHIMNEY_FAN((byte)0x2A, "CHIMNEY_FAN"),
|
||||
SCENE_ACTIVATION((byte)0x2B, "SCENE_ACTIVATION"),
|
||||
SCENE_ACTUATOR_CONF((byte)0x2C, "SCENE_ACTUATOR_CONF"),
|
||||
SCENE_CONTROLLER_CONF((byte)0x2D, "SCENE_CONTROLLER_CONF"),
|
||||
ZIP_CLIENT((byte)0x2E, "ZIP_CLIENT"),
|
||||
ZIP_ADV_SERVICES((byte)0x2F, "ZIP_ADV_SERVICES"),
|
||||
SENSOR_BINARY((byte)0x30, "SENSOR_BINARY"),
|
||||
SENSOR_MULTILEVEL((byte)0x31, "SENSOR_MULTILEVEL"),
|
||||
METER((byte)0x32, "METER", true, 60),
|
||||
ZIP_ADV_SERVER((byte)0x33, "ZIP_ADV_SERVER"),
|
||||
ZIP_ADV_CLIENT((byte)0x34, "ZIP_ADV_CLIENT"),
|
||||
METER_PULSE((byte)0x35, "METER_PULSE"),
|
||||
METER_TBL_CONFIG((byte)0x3C, "METER_TBL_CONFIG"),
|
||||
METER_TBL_MONITOR((byte)0x3D, "METER_TBL_MONITOR"),
|
||||
METER_TBL_PUSH((byte)0x3E, "METER_TBL_PUSH"),
|
||||
THERMOSTAT_HEATING((byte)0x38, "THERMOSTAT_HEATING"),
|
||||
THERMOSTAT_MODE((byte)0x40, "THERMOSTAT_MODE"),
|
||||
THERMOSTAT_OPERATING_STATE((byte)0x42, "THERMOSTAT_OPERATING_STATE"),
|
||||
THERMOSTAT_SETPOINT((byte)0x43, "THERMOSTAT_SETPOINT"),
|
||||
THERMOSTAT_FAN_MODE((byte)0x44, "THERMOSTAT_FAN_MODE"),
|
||||
THERMOSTAT_FAN_STATE((byte)0x45, "THERMOSTAT_FAN_STATE"),
|
||||
CLIMATE_CONTROL_SCHEDULE((byte)0x46, "CLIMATE_CONTROL_SCHEDULE"),
|
||||
THERMOSTAT_SETBACK((byte)0x47, "THERMOSTAT_SETBACK"),
|
||||
DOOR_LOCK_LOGGING((byte)0x4C, "DOOR_LOCK_LOGGING"),
|
||||
SCHEDULE_ENTRY_LOCK((byte)0x4E, "SCHEDULE_ENTRY_LOCK"),
|
||||
BASIC_WINDOW_COVERING((byte)0x50, "BASIC_WINDOW_COVERING"),
|
||||
MTP_WINDOW_COVERING((byte)0x51, "MTP_WINDOW_COVERING"),
|
||||
MULTI_INSTANCE((byte)0x60, "MULTI_INSTANCE"),
|
||||
DOOR_LOCK((byte)0x62, "DOOR_LOCK"),
|
||||
USER_CODE((byte)0x63, "USER_CODE"),
|
||||
CONFIGURATION((byte)0x70, "CONFIGURATION"),
|
||||
ALARM((byte)0x71, "ALARM"),
|
||||
MANUFACTURER_SPECIFIC((byte)0x72, "MANUFACTURER_SPECIFIC"),
|
||||
POWERLEVEL((byte)0x73, "POWERLEVEL"),
|
||||
PROTECTION((byte)0x75, "PROTECTION"),
|
||||
LOCK((byte)0x76, "LOCK"),
|
||||
NODE_NAMING((byte)0x77, "NODE_NAMING"),
|
||||
FIRMWARE_UPDATE_MD((byte)0x7A, "FIRMWARE_UPDATE_MD"),
|
||||
GROUPING_NAME((byte)0x7B, "GROUPING_NAME"),
|
||||
REMOTE_ASSOCIATION_ACTIVATE((byte)0x7C, "REMOTE_ASSOCIATION_ACTIVATE"),
|
||||
REMOTE_ASSOCIATION((byte)0x7D, "REMOTE_ASSOCIATION"),
|
||||
BATTERY((byte)0x80, "BATTERY", true, 3600),
|
||||
CLOCK((byte)0x81, "CLOCK"),
|
||||
HAIL((byte)0x82, "HAIL"),
|
||||
WAKE_UP((byte)0x84, "WAKE_UP"),
|
||||
ASSOCIATION((byte)0x85, "ASSOCIATION"),
|
||||
VERSION((byte)0x86, "VERSION"),
|
||||
INDICATOR((byte)0x87, "INDICATOR"),
|
||||
PROPRIETARY((byte)0x88, "PROPRIETARY"),
|
||||
LANGUAGE((byte)0x89, "LANGUAGE"),
|
||||
TIME((byte)0x8A, "TIME"),
|
||||
TIME_PARAMETERS((byte)0x8B, "TIME_PARAMETERS"),
|
||||
GEOGRAPHIC_LOCATION((byte)0x8C, "GEOGRAPHIC_LOCATION"),
|
||||
COMPOSITE((byte)0x8D, "COMPOSITE"),
|
||||
MULTI_INSTANCE_ASSOCIATION((byte)0x8E, "MULTI_INSTANCE_ASSOCIATION"),
|
||||
MULTI_CMD((byte)0x8F, "MULTI_CMD"),
|
||||
ENERGY_PRODUCTION((byte)0x90, "ENERGY_PRODUCTION"),
|
||||
MANUFACTURER_PROPRIETARY((byte)0x91, "MANUFACTURER_PROPRIETARY"),
|
||||
SCREEN_MD((byte)0x92, "SCREEN_MD"),
|
||||
SCREEN_ATTRIBUTES((byte)0x93, "SCREEN_ATTRIBUTES"),
|
||||
SIMPLE_AV_CONTROL((byte)0x94, "SIMPLE_AV_CONTROL"),
|
||||
AV_CONTENT_DIRECTORY_MD((byte)0x95, "AV_CONTENT_DIRECTORY_MD"),
|
||||
AV_RENDERER_STATUS((byte)0x96, "AV_RENDERER_STATUS"),
|
||||
AV_CONTENT_SEARCH_MD((byte)0x97, "AV_CONTENT_SEARCH_MD"),
|
||||
SECURITY((byte)0x98, "SECURITY"),
|
||||
AV_TAGGING_MD((byte)0x99, "AV_TAGGING_MD"),
|
||||
IP_CONFIGURATION((byte)0x9A, "IP_CONFIGURATION"),
|
||||
ASSOCIATION_COMMAND_CONFIGURATION((byte)0x9B, "ASSOCIATION_COMMAND_CONFIGURATION"),
|
||||
SENSOR_ALARM((byte)0x9C, "SENSOR_ALARM"),
|
||||
SILENCE_ALARM((byte)0x9D, "SILENCE_ALARM"),
|
||||
SENSOR_CONFIGURATION((byte)0x9E, "SENSOR_CONFIGURATION"),
|
||||
MARK((byte)0xEF, "MARK"),
|
||||
NON_INTEROPERABLE((byte)0xF0, "NON_INTEROPERABLE"),
|
||||
ALL((byte)0xFF, null);
|
||||
|
||||
public final byte data;
|
||||
public final String label;
|
||||
public final boolean supportsPolling;
|
||||
public final int secondsRefreshInterval;
|
||||
private static final Map<Byte, CommandClass> classes = new HashMap<>();
|
||||
static {
|
||||
for (CommandClass cls : values()) {
|
||||
classes.put(cls.data, cls);
|
||||
}
|
||||
}
|
||||
|
||||
CommandClass(byte _data, String _label) {
|
||||
this(_data, _label, false, 0);
|
||||
}
|
||||
|
||||
CommandClass(byte _data, String _label, boolean _supportsPolling, int _secondsRefreshInterval) {
|
||||
data = _data;
|
||||
label = _label;
|
||||
supportsPolling = _supportsPolling;
|
||||
secondsRefreshInterval = _secondsRefreshInterval;
|
||||
}
|
||||
|
||||
public static CommandClass fromByte(byte _code) {
|
||||
CommandClass cls = classes.get(_code);
|
||||
return cls == null ? CommandClass.NO_OPERATION : cls;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.lanternsoftware.zwave.message;
|
||||
|
||||
import com.lanternsoftware.util.CollectionUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
public enum ControllerMessageType {
|
||||
None((byte)0x0,"None"),
|
||||
SerialApiGetInitData((byte)0x02,"SerialApiGetInitData"), // Request initial information about devices in network
|
||||
SerialApiApplicationNodeInfo((byte)0x03,"SerialApiApplicationNodeInfo"), // Set controller node information
|
||||
ApplicationCommandHandler((byte)0x04,"ApplicationCommandHandler"), // Handle application command
|
||||
GetControllerCapabilities((byte)0x05,"GetControllerCapabilities"), // Request controller capabilities (primary role, SUC/SIS availability)
|
||||
SerialApiSetTimeouts((byte)0x06,"SerialApiSetTimeouts"), // Set Serial API timeouts
|
||||
GetCapabilities((byte)0x07,"GetCapabilities"), // Request Serial API capabilities from the controller
|
||||
SerialApiSoftReset((byte)0x08,"SerialApiSoftReset"), // Soft reset. Restarts Z-Wave chip
|
||||
RfReceiveMode((byte)0x10,"RfReceiveMode"), // Power down the RF section of the stick
|
||||
SetSleepMode((byte)0x11,"SetSleepMode"), // Set the CPU into sleep mode
|
||||
SendNodeInfo((byte)0x12,"SendNodeInfo"), // Send Node Information Frame of the stick
|
||||
SendData((byte)0x13,"SendData"), // Send data.
|
||||
SendDataMulti((byte)0x14, "SendDataMulti"),
|
||||
GetVersion((byte)0x15,"GetVersion"), // Request controller hardware version
|
||||
SendDataAbort((byte)0x16,"SendDataAbort"), // Abort Send data.
|
||||
RfPowerLevelSet((byte)0x17,"RfPowerLevelSet"), // Set RF Power level
|
||||
SendDataMeta((byte)0x18, "SendDataMeta"),
|
||||
GetRandom((byte)0x1c,"GetRandom"), // ???
|
||||
MemoryGetId((byte)0x20,"MemoryGetId"), // ???
|
||||
MemoryGetByte((byte)0x21,"MemoryGetByte"), // Get a byte of memory.
|
||||
MemoryPutByte((byte)0x22, "MemoryPutByte"),
|
||||
ReadMemory((byte)0x23,"ReadMemory"), // Read memory.
|
||||
WriteMemory((byte)0x24, "WriteMemory"),
|
||||
SetLearnNodeState((byte)0x40,"SetLearnNodeState"), // ???
|
||||
IdentifyNode((byte)0x41,"IdentifyNode"), // Get protocol info (baud rate, listening, etc.) for a given node
|
||||
SetDefault((byte)0x42,"SetDefault"), // Reset controller and node info to default (original) values
|
||||
NewController((byte)0x43,"NewController"), // ???
|
||||
ReplicationCommandComplete((byte)0x44,"ReplicationCommandComplete"), // Replication send data complete
|
||||
ReplicationSendData((byte)0x45,"ReplicationSendData"), // Replication send data
|
||||
AssignReturnRoute((byte)0x46,"AssignReturnRoute"), // Assign a return route from the specified node to the controller
|
||||
DeleteReturnRoute((byte)0x47,"DeleteReturnRoute"), // Delete all return routes from the specified node
|
||||
RequestNodeNeighborUpdate((byte)0x48,"RequestNodeNeighborUpdate"), // Ask the specified node to update its neighbors (then read them from the controller)
|
||||
ApplicationUpdate((byte)0x49,"ApplicationUpdate"), // Get a list of supported (and controller) command classes
|
||||
AddNodeToNetwork((byte)0x4a,"AddNodeToNetwork"), // Control the addnode (or addcontroller) process...start, stop, etc.
|
||||
RemoveNodeFromNetwork((byte)0x4b,"RemoveNodeFromNetwork"), // Control the removenode (or removecontroller) process...start, stop, etc.
|
||||
CreateNewPrimary((byte)0x4c,"CreateNewPrimary"), // Control the createnewprimary process...start, stop, etc.
|
||||
ControllerChange((byte)0x4d,"ControllerChange"), // Control the transferprimary process...start, stop, etc.
|
||||
SetLearnMode((byte)0x50,"SetLearnMode"), // Put a controller into learn mode for replication/ receipt of configuration info
|
||||
AssignSucReturnRoute((byte)0x51,"AssignSucReturnRoute"), // Assign a return route to the SUC
|
||||
EnableSuc((byte)0x52,"EnableSuc"), // Make a controller a Static Update Controller
|
||||
RequestNetworkUpdate((byte)0x53,"RequestNetworkUpdate"), // Network update for a SUC(?)
|
||||
SetSucNodeID((byte)0x54,"SetSucNodeID"), // Identify a Static Update Controller node id
|
||||
DeleteSUCReturnRoute((byte)0x55,"DeleteSUCReturnRoute"), // Remove return routes to the SUC
|
||||
GetSucNodeId((byte)0x56,"GetSucNodeId"), // Try to retrieve a Static Update Controller node id (zero if no SUC present)
|
||||
SendSucId((byte)0x57, "SendSucId"),
|
||||
RequestNodeNeighborUpdateOptions((byte)0x5a,"RequestNodeNeighborUpdateOptions"), // Allow options for request node neighbor update
|
||||
RequestNodeInfo((byte)0x60,"RequestNodeInfo"), // Get info (supported command classes) for the specified node
|
||||
RemoveFailedNodeID((byte)0x61,"RemoveFailedNodeID"), // Mark a specified node id as failed
|
||||
IsFailedNodeID((byte)0x62,"IsFailedNodeID"), // Check to see if a specified node has failed
|
||||
ReplaceFailedNode((byte)0x63,"ReplaceFailedNode"), // Remove a failed node from the controller's list (?)
|
||||
GetRoutingInfo((byte)0x80,"GetRoutingInfo"), // Get a specified node's neighbor information from the controller
|
||||
LockRoute((byte)0x90, "LockRoute"),
|
||||
SerialApiSlaveNodeInfo((byte)0xA0,"SerialApiSlaveNodeInfo"), // Set application virtual slave node information
|
||||
ApplicationSlaveCommandHandler((byte)0xA1,"ApplicationSlaveCommandHandler"), // Slave command handler
|
||||
SendSlaveNodeInfo((byte)0xA2,"ApplicationSlaveCommandHandler"), // Send a slave node information frame
|
||||
SendSlaveData((byte)0xA3,"SendSlaveData"), // Send data from slave
|
||||
SetSlaveLearnMode((byte)0xA4,"SetSlaveLearnMode"), // Enter slave learn mode
|
||||
GetVirtualNodes((byte)0xA5,"GetVirtualNodes"), // Return all virtual nodes
|
||||
IsVirtualNode((byte)0xA6,"IsVirtualNode"), // Virtual node test
|
||||
WatchDogEnable((byte)0xB6, "WatchDogEnable"),
|
||||
WatchDogDisable((byte)0xB7, "WatchDogDisable"),
|
||||
WatchDogKick((byte)0xB6, "WatchDogKick"),
|
||||
RfPowerLevelGet((byte)0xBA,"RfPowerLevelSet"), // Get RF Power level
|
||||
GetLibraryType((byte)0xBD, "GetLibraryType"), // Gets the type of ZWave library on the stick
|
||||
SendTestFrame((byte)0xBE, "SendTestFrame"), // Send a test frame to a node
|
||||
GetProtocolStatus((byte)0xBF, "GetProtocolStatus"),
|
||||
SetPromiscuousMode((byte)0xD0,"SetPromiscuousMode"), // Set controller into promiscuous mode to listen to all frames
|
||||
PromiscuousApplicationCommandHandler((byte)0xD1,"PromiscuousApplicationCommandHandler"),
|
||||
ALL((byte)0xFF, null);
|
||||
|
||||
public final byte data;
|
||||
public final String label;
|
||||
|
||||
ControllerMessageType(byte _data, String _label) {
|
||||
data = _data;
|
||||
label = _label;
|
||||
}
|
||||
|
||||
private static Map<Byte, ControllerMessageType> types = CollectionUtils.transformToMap(Arrays.asList(values()), _type->_type.data);
|
||||
public static ControllerMessageType fromByte(byte _data) {
|
||||
ControllerMessageType type = types.get(_data);
|
||||
return type == null ? ControllerMessageType.None : type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.lanternsoftware.zwave.message;
|
||||
|
||||
public interface IMessageSubscriber<T extends Message> {
|
||||
Class<T> getHandledMessageClass();
|
||||
void onMessage(T _message);
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package com.lanternsoftware.zwave.message;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class Message {
|
||||
protected byte nodeId;
|
||||
protected final ControllerMessageType controllerMessageType;
|
||||
protected final MessageType messageType;
|
||||
protected final CommandClass commandClass;
|
||||
protected final byte command;
|
||||
|
||||
public Message(ControllerMessageType _controllerMessageType, MessageType _messageType, CommandClass _commandClass, byte _command) {
|
||||
this((byte) 0, _controllerMessageType, _messageType, _commandClass, _command);
|
||||
}
|
||||
|
||||
public Message(byte _nodeId, ControllerMessageType _controllerMessageType, MessageType _messageType, CommandClass _commandClass, byte _command) {
|
||||
nodeId = _nodeId;
|
||||
controllerMessageType = _controllerMessageType;
|
||||
messageType = _messageType;
|
||||
commandClass = _commandClass;
|
||||
command = _command;
|
||||
}
|
||||
|
||||
public byte getNodeId() {
|
||||
return nodeId;
|
||||
}
|
||||
|
||||
public void setNodeId(byte _nodeId) {
|
||||
nodeId = _nodeId;
|
||||
}
|
||||
|
||||
public byte[] toPayload() {
|
||||
try {
|
||||
byte[] payload = getPayload();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
if (nodeId > 0)
|
||||
os.write(nodeId);
|
||||
|
||||
if (commandClass != CommandClass.NO_OPERATION) {
|
||||
os.write(payload.length + 2);
|
||||
os.write(commandClass.data);
|
||||
os.write(command);
|
||||
}
|
||||
if (payload.length > 0)
|
||||
os.write(payload);
|
||||
os.close();
|
||||
return os.toByteArray();
|
||||
} catch (IOException _e) {
|
||||
_e.printStackTrace();
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
|
||||
public void fromPayload(byte[] _payload) {
|
||||
}
|
||||
|
||||
public byte[] getPayload() {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
public byte[] toByteArray(byte _transmitOptions, byte _callbackId) {
|
||||
try {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
byte[] payload = toPayload();
|
||||
os.write((byte) 0x01);
|
||||
int messageLength = payload.length + (isCallbackExpected() ? 5 : 3);
|
||||
os.write((byte) messageLength);
|
||||
os.write(messageType.data);
|
||||
os.write(controllerMessageType.data);
|
||||
if (payload.length > 0)
|
||||
os.write(payload);
|
||||
|
||||
if (isCallbackExpected()) {
|
||||
os.write(_transmitOptions);
|
||||
os.write(_callbackId);
|
||||
}
|
||||
|
||||
os.write((byte) 1);
|
||||
byte[] msg = os.toByteArray();
|
||||
msg[msg.length - 1] = calculateChecksum(msg);
|
||||
return msg;
|
||||
} catch (IOException _e) {
|
||||
_e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return toKey(controllerMessageType.data, messageType.data, commandClass.data, command);
|
||||
}
|
||||
|
||||
public static String toKey(byte _controllerMessageType, byte _messageType, byte _commandClass, byte _command) {
|
||||
return String.format("%02X%02X%02X%02X", _controllerMessageType, _messageType, _commandClass, _command);
|
||||
}
|
||||
|
||||
public static byte calculateChecksum(byte[] buffer) {
|
||||
byte checkSum = (byte) 0xFF;
|
||||
for (int i = 1; i < buffer.length - 1; i++) {
|
||||
checkSum = (byte) (checkSum ^ buffer[i]);
|
||||
}
|
||||
return checkSum;
|
||||
}
|
||||
|
||||
protected byte[] asByteArray(int _byte) {
|
||||
byte[] ret = new byte[1];
|
||||
ret[0] = (byte) _byte;
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected byte[] asByteArray(byte... _bytes) {
|
||||
return _bytes;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
|
||||
public boolean isCallbackExpected() {
|
||||
return (controllerMessageType == ControllerMessageType.SendData) && (messageType == MessageType.REQUEST);
|
||||
}
|
||||
|
||||
public String describe() {
|
||||
return name();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.lanternsoftware.zwave.message;
|
||||
|
||||
import com.lanternsoftware.util.CollectionUtils;
|
||||
import com.lanternsoftware.util.NullUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
public abstract class MessageEngine {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MessageEngine.class);
|
||||
|
||||
private static final Map<String, Message> messages = new HashMap<>();
|
||||
private static final Map<Class<?>, List<IMessageSubscriber<?>>> subscribers = new HashMap<>();
|
||||
static {
|
||||
for (Message m : ServiceLoader.load(Message.class)) {
|
||||
messages.put(m.getKey(), m);
|
||||
}
|
||||
for (IMessageSubscriber s : ServiceLoader.load(IMessageSubscriber.class)) {
|
||||
subscribe(s);
|
||||
}
|
||||
}
|
||||
|
||||
public static Message decode(byte[] _data) {
|
||||
byte messageCheckSum = Message.calculateChecksum(_data);
|
||||
byte messageCheckSumReceived = _data[_data.length - 1];
|
||||
if (messageCheckSum != messageCheckSumReceived) {
|
||||
logger.debug("Invalid checksum for message: {}", NullUtils.toHex(_data));
|
||||
return null;
|
||||
}
|
||||
MessageType messageType = _data[2] == 0x00 ? MessageType.REQUEST : MessageType.RESPONSE;
|
||||
ControllerMessageType controllerMessageType = ControllerMessageType.fromByte((byte)(_data[3] & 0xFF));
|
||||
int offset = ((messageType == MessageType.REQUEST) && NullUtils.isOneOf(controllerMessageType, ControllerMessageType.SendData, ControllerMessageType.ApplicationCommandHandler))?7:5;
|
||||
CommandClass commandClass = _data.length > offset + 1 ? CommandClass.fromByte((byte)(_data[offset] & 0xFF)):CommandClass.NO_OPERATION;
|
||||
byte command = ((commandClass == CommandClass.NO_OPERATION) || (_data.length <= offset+2))?0:(byte)(_data[offset+1] & 0xFF);
|
||||
Message message = messages.get(Message.toKey(controllerMessageType.data, messageType.data, commandClass.data, command));
|
||||
if (message == null) {
|
||||
logger.debug("Could not find message class for message: {} {} {} {}", controllerMessageType.label, messageType.name(), commandClass.label, command);
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Message ret = message.getClass().newInstance();
|
||||
ret.fromPayload(_data);
|
||||
return ret;
|
||||
} catch (Exception _e) {
|
||||
_e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void publish(Message _m) {
|
||||
for (IMessageSubscriber s : CollectionUtils.makeNotNull(subscribers.get(_m.getClass()))) {
|
||||
s.onMessage(_m);
|
||||
}
|
||||
}
|
||||
|
||||
public static void subscribe(IMessageSubscriber<?> _subscriber) {
|
||||
CollectionUtils.addToMultiMap(_subscriber.getHandledMessageClass(), _subscriber, subscribers);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.lanternsoftware.zwave.message;
|
||||
|
||||
public enum MessageType {
|
||||
REQUEST((byte)0),
|
||||
RESPONSE((byte)1);
|
||||
|
||||
public final byte data;
|
||||
|
||||
MessageType(byte _data) {
|
||||
data = _data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.lanternsoftware.zwave.message;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
public abstract class MessageUtil {
|
||||
private static final int SIZE_MASK = 0x07;
|
||||
private static final int SCALE_MASK = 0x18;
|
||||
private static final int SCALE_SHIFT = 0x03;
|
||||
private static final int PRECISION_MASK = 0xe0;
|
||||
private static final int PRECISION_SHIFT = 0x05;
|
||||
|
||||
public static double getTemperatureCelsius(byte[] _payload, int _offset) {
|
||||
int size = _payload[_offset] & SIZE_MASK;
|
||||
int scale = (_payload[_offset] & SCALE_MASK) >> SCALE_SHIFT;
|
||||
int precision = (_payload[_offset] & PRECISION_MASK) >> PRECISION_SHIFT;
|
||||
|
||||
if ((size+_offset) >= _payload.length)
|
||||
return 0.0;
|
||||
|
||||
int value = 0;
|
||||
for (int i = 0; i < size; ++i) {
|
||||
value <<= 8;
|
||||
value |= _payload[_offset + i + 1] & 0xFF;
|
||||
}
|
||||
|
||||
BigDecimal result;
|
||||
if ((_payload[_offset + 1] & 0x80) == 0x80) {
|
||||
if (size == 1)
|
||||
value |= 0xffffff00;
|
||||
else if (size == 2)
|
||||
value |= 0xffff0000;
|
||||
}
|
||||
result = BigDecimal.valueOf(value);
|
||||
BigDecimal divisor = BigDecimal.valueOf(Math.pow(10, precision));
|
||||
double temp = result.divide(divisor, RoundingMode.HALF_EVEN).doubleValue();
|
||||
return (scale == 1) ? (temp-32)/1.8 : temp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.lanternsoftware.zwave.message;
|
||||
|
||||
public abstract class NoCommandRequestMessage extends RequestMessage {
|
||||
public NoCommandRequestMessage(ControllerMessageType _controllerMessageType) {
|
||||
this((byte)0, _controllerMessageType);
|
||||
}
|
||||
|
||||
public NoCommandRequestMessage(byte _nodeId, ControllerMessageType _controllerMessageType) {
|
||||
super(_nodeId, _controllerMessageType, CommandClass.NO_OPERATION, (byte) 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.lanternsoftware.zwave.message;
|
||||
|
||||
public abstract class NoCommandResponseMessage extends ResponseMessage {
|
||||
public NoCommandResponseMessage(ControllerMessageType _controllerMessageType) {
|
||||
super(_controllerMessageType, CommandClass.NO_OPERATION, (byte) 0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.lanternsoftware.zwave.message;
|
||||
|
||||
public abstract class RequestMessage extends Message {
|
||||
public RequestMessage(ControllerMessageType _controllerMessageType, CommandClass _commandClass, byte _command) {
|
||||
this((byte)0, _controllerMessageType, _commandClass, _command);
|
||||
}
|
||||
|
||||
public RequestMessage(byte _nodeId, ControllerMessageType _controllerMessageType, CommandClass _commandClass, byte _command) {
|
||||
super(_nodeId, _controllerMessageType, MessageType.REQUEST, _commandClass, _command);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.lanternsoftware.zwave.message;
|
||||
|
||||
public abstract class ResponseMessage extends Message {
|
||||
public ResponseMessage(ControllerMessageType _controllerMessageType, CommandClass _commandClass, byte _command) {
|
||||
this((byte) 0, _controllerMessageType, _commandClass, _command);
|
||||
}
|
||||
|
||||
public ResponseMessage(byte _nodeId, ControllerMessageType _controllerMessageType, CommandClass _commandClass, byte _command) {
|
||||
super(_nodeId, _controllerMessageType, MessageType.RESPONSE, _commandClass, _command);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.lanternsoftware.zwave.message;
|
||||
|
||||
public abstract class SendDataRequestMessage extends RequestMessage {
|
||||
public SendDataRequestMessage(CommandClass _commandClass, byte _command) {
|
||||
this((byte)0, _commandClass, _command);
|
||||
}
|
||||
|
||||
public SendDataRequestMessage(byte _nodeId, CommandClass _commandClass, byte _command) {
|
||||
super(_nodeId, ControllerMessageType.SendData, _commandClass, _command);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.CommandClass;
|
||||
import com.lanternsoftware.zwave.message.ControllerMessageType;
|
||||
import com.lanternsoftware.zwave.message.RequestMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ApplicationUpdateRequest extends RequestMessage {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ApplicationUpdateRequest.class);
|
||||
|
||||
private List<CommandClass> commandClasses;
|
||||
|
||||
public ApplicationUpdateRequest() {
|
||||
super(ControllerMessageType.ApplicationUpdate, CommandClass.NO_OPERATION, (byte) 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromPayload(byte[] _payload) {
|
||||
nodeId = _payload[5];
|
||||
if (_payload[4] == (byte) 0x84)
|
||||
logger.debug("Received node information for node: {}", nodeId);
|
||||
int length = _payload[6];
|
||||
commandClasses = new ArrayList<>();
|
||||
for (int i = 7; i < length + 7; i++) {
|
||||
if (_payload[i] != (byte) 0xef) {
|
||||
CommandClass commandClass = CommandClass.fromByte(_payload[i]);
|
||||
if (commandClass != CommandClass.NO_OPERATION) {
|
||||
logger.debug("Received command class: {} for node: {}", commandClass.name(), nodeId);
|
||||
commandClasses.add(commandClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<CommandClass> getCommandClasses() {
|
||||
return commandClasses;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.CommandClass;
|
||||
import com.lanternsoftware.zwave.message.SendDataRequestMessage;
|
||||
|
||||
public class BinarySwitchSetRequest extends SendDataRequestMessage {
|
||||
private boolean on;
|
||||
|
||||
public BinarySwitchSetRequest() {
|
||||
this(true);
|
||||
}
|
||||
|
||||
public BinarySwitchSetRequest(boolean _on) {
|
||||
this((byte)0, _on);
|
||||
}
|
||||
|
||||
public BinarySwitchSetRequest(byte _nodeId, boolean _on) {
|
||||
super(_nodeId, CommandClass.SWITCH_BINARY, (byte)0x01);
|
||||
on = _on;
|
||||
}
|
||||
|
||||
public boolean isOn() {
|
||||
return on;
|
||||
}
|
||||
|
||||
public void setOn(boolean _on) {
|
||||
on = _on;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getPayload() {
|
||||
return asByteArray(on?0xFF:0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return name() + " node: " + nodeId + " on: " + on;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.ControllerMessageType;
|
||||
import com.lanternsoftware.zwave.message.NoCommandRequestMessage;
|
||||
|
||||
public class ByteMessage extends NoCommandRequestMessage {
|
||||
private byte b;
|
||||
|
||||
public ByteMessage() {
|
||||
super(ControllerMessageType.None);
|
||||
}
|
||||
|
||||
public ByteMessage(byte _b) {
|
||||
super(ControllerMessageType.None);
|
||||
b = _b;
|
||||
}
|
||||
|
||||
public byte getByte() {
|
||||
return b;
|
||||
}
|
||||
|
||||
public void setByte(byte _b) {
|
||||
b = _b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toByteArray(byte _transmitOptions, byte _callbackId) {
|
||||
return asByteArray(b);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.ControllerMessageType;
|
||||
import com.lanternsoftware.zwave.message.NoCommandRequestMessage;
|
||||
|
||||
public class ControllerCapabilitiesRequest extends NoCommandRequestMessage {
|
||||
public ControllerCapabilitiesRequest() {
|
||||
super(ControllerMessageType.GetCapabilities);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.CommandClass;
|
||||
import com.lanternsoftware.zwave.message.ControllerMessageType;
|
||||
import com.lanternsoftware.zwave.message.NoCommandResponseMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class ControllerCapabilitiesResponse extends NoCommandResponseMessage {
|
||||
private static final Logger logger = LoggerFactory.getLogger(ControllerCapabilitiesResponse.class);
|
||||
|
||||
private String serialAPIVersion;
|
||||
private int manufacturerId;
|
||||
private int deviceType;
|
||||
private int deviceId;
|
||||
private Set<CommandClass> supportedCommandClasses;
|
||||
|
||||
public ControllerCapabilitiesResponse() {
|
||||
super(ControllerMessageType.GetCapabilities);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromPayload(byte[] _payload) {
|
||||
serialAPIVersion = String.format("%d.%d", _payload[4], _payload[5]);
|
||||
manufacturerId = getShort(_payload, 6);
|
||||
deviceType = getShort(_payload, 8);
|
||||
deviceId = getShort(_payload, 10);
|
||||
supportedCommandClasses = new HashSet<>();
|
||||
for (int by = 12; by < _payload.length-1; by++) {
|
||||
for (int bi = 0; bi < 8; bi++) {
|
||||
if ((_payload[by] & (0x01 << bi)) != 0) {
|
||||
byte commandClassByte = (byte) (((by - 12) << 3) + bi + 1);
|
||||
CommandClass commandClass = CommandClass.fromByte(commandClassByte);
|
||||
if (commandClass != CommandClass.NO_OPERATION) {
|
||||
logger.debug("Supports command class: {}", commandClass.label);
|
||||
supportedCommandClasses.add(commandClass);
|
||||
} else {
|
||||
logger.debug("Supports unknown command class: {}", commandClassByte);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getShort(byte[] _data, int _offset) {
|
||||
return (toShort(_data[_offset]) << 8) | toShort(_data[_offset + 1]);
|
||||
}
|
||||
|
||||
private int toShort(byte _bt) {
|
||||
return _bt & 0xFF;
|
||||
}
|
||||
|
||||
public String getSerialAPIVersion() {
|
||||
return serialAPIVersion;
|
||||
}
|
||||
|
||||
public int getManufacturerId() {
|
||||
return manufacturerId;
|
||||
}
|
||||
|
||||
public int getDeviceType() {
|
||||
return deviceType;
|
||||
}
|
||||
|
||||
public int getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public Set<CommandClass> getSupportedCommandClasses() {
|
||||
return supportedCommandClasses;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.ControllerMessageType;
|
||||
import com.lanternsoftware.zwave.message.NoCommandRequestMessage;
|
||||
|
||||
public class ControllerInitialDataRequest extends NoCommandRequestMessage {
|
||||
public ControllerInitialDataRequest() {
|
||||
super(ControllerMessageType.SerialApiGetInitData);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.ControllerMessageType;
|
||||
import com.lanternsoftware.zwave.message.NoCommandResponseMessage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ControllerInitialDataResponse extends NoCommandResponseMessage {
|
||||
private boolean master;
|
||||
private boolean primary;
|
||||
private List<Byte> nodeIds;
|
||||
|
||||
public ControllerInitialDataResponse() {
|
||||
super(ControllerMessageType.SerialApiGetInitData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromPayload(byte[] _payload) {
|
||||
int length = _payload[6];
|
||||
if (length == 29) {
|
||||
byte nodeId = 1;
|
||||
nodeIds = new ArrayList<>();
|
||||
for (int i = 7; i < 7 + length; i++) {
|
||||
byte curByte = _payload[i];
|
||||
for (int j = 0; j < 8; j++) {
|
||||
int bit = 1 << j;
|
||||
if ((curByte & bit) == bit) {
|
||||
nodeIds.add(nodeId);
|
||||
}
|
||||
nodeId++;
|
||||
}
|
||||
}
|
||||
master = (_payload[5] & 0x1) == 0;
|
||||
primary = (_payload[5] & 0x4) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isMaster() {
|
||||
return master;
|
||||
}
|
||||
|
||||
public boolean isPrimary() {
|
||||
return primary;
|
||||
}
|
||||
|
||||
public List<Byte> getNodeIds() {
|
||||
return nodeIds;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.CommandClass;
|
||||
import com.lanternsoftware.zwave.message.ControllerMessageType;
|
||||
import com.lanternsoftware.zwave.message.RequestMessage;
|
||||
|
||||
public class DeviceManufacturerActionRequest extends RequestMessage {
|
||||
public DeviceManufacturerActionRequest() {
|
||||
this((byte) 0);
|
||||
}
|
||||
|
||||
public DeviceManufacturerActionRequest(byte _nodeId) {
|
||||
super(_nodeId, ControllerMessageType.SendData, CommandClass.MANUFACTURER_SPECIFIC, (byte) 0x04);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.ControllerMessageType;
|
||||
import com.lanternsoftware.zwave.message.NoCommandRequestMessage;
|
||||
|
||||
public class GetControllerIdRequest extends NoCommandRequestMessage {
|
||||
public GetControllerIdRequest() {
|
||||
super(ControllerMessageType.MemoryGetId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.ControllerMessageType;
|
||||
import com.lanternsoftware.zwave.message.NoCommandResponseMessage;
|
||||
|
||||
public class GetControllerIdResponse extends NoCommandResponseMessage {
|
||||
private long homeId;
|
||||
private int controllerId;
|
||||
|
||||
public GetControllerIdResponse() {
|
||||
super(ControllerMessageType.MemoryGetId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromPayload(byte[] _payload) {
|
||||
homeId = (getByte(_payload, 4) << 24) | (getByte(_payload, 5) << 16) | (getByte(_payload, 6) << 8) | getByte(_payload, 7);
|
||||
controllerId = _payload[8];
|
||||
}
|
||||
|
||||
public long getHomeId() {
|
||||
return homeId;
|
||||
}
|
||||
|
||||
public void setHomeId(int _homeId) {
|
||||
homeId = _homeId;
|
||||
}
|
||||
|
||||
public int getControllerId() {
|
||||
return controllerId;
|
||||
}
|
||||
|
||||
public void setControllerId(int _controllerId) {
|
||||
controllerId = _controllerId;
|
||||
}
|
||||
|
||||
private long getByte(byte[] _data, int _offset) {
|
||||
return _data[_offset] & 0xFF;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.CommandClass;
|
||||
import com.lanternsoftware.zwave.message.ControllerMessageType;
|
||||
import com.lanternsoftware.zwave.message.RequestMessage;
|
||||
|
||||
public class MultilevelSensorGetRequest extends RequestMessage {
|
||||
public MultilevelSensorGetRequest() {
|
||||
this((byte)0);
|
||||
}
|
||||
|
||||
public MultilevelSensorGetRequest(byte _nodeId) {
|
||||
super(_nodeId, ControllerMessageType.SendData, CommandClass.SENSOR_MULTILEVEL, (byte)0x04);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return name() + " node: " + nodeId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.CommandClass;
|
||||
import com.lanternsoftware.zwave.message.ControllerMessageType;
|
||||
import com.lanternsoftware.zwave.message.MessageUtil;
|
||||
import com.lanternsoftware.zwave.message.RequestMessage;
|
||||
|
||||
public class MultilevelSensorReportRequest extends RequestMessage {
|
||||
private double temperature;
|
||||
|
||||
public MultilevelSensorReportRequest() {
|
||||
this((byte) 0);
|
||||
}
|
||||
|
||||
public MultilevelSensorReportRequest(byte _nodeId) {
|
||||
super(_nodeId, ControllerMessageType.ApplicationCommandHandler, CommandClass.SENSOR_MULTILEVEL, (byte) 0x05);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromPayload(byte[] _payload) {
|
||||
nodeId = _payload[5];
|
||||
if (_payload[9] == (byte) 1)
|
||||
temperature = MessageUtil.getTemperatureCelsius(_payload, 10);
|
||||
}
|
||||
|
||||
public double getTemperatureCelsius() {
|
||||
return temperature;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return name() + " node: " + nodeId + " level: " + temperature;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.CommandClass;
|
||||
import com.lanternsoftware.zwave.message.ControllerMessageType;
|
||||
import com.lanternsoftware.zwave.message.RequestMessage;
|
||||
|
||||
public class MultilevelSwitchReportRequest extends RequestMessage {
|
||||
private int level;
|
||||
|
||||
public MultilevelSwitchReportRequest() {
|
||||
super(ControllerMessageType.ApplicationCommandHandler, CommandClass.SWITCH_MULTILEVEL, (byte) 0x03);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromPayload(byte[] _payload) {
|
||||
nodeId = _payload[5];
|
||||
level = _payload[9];
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return name() + " node: " + nodeId + " level: " + level;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.CommandClass;
|
||||
import com.lanternsoftware.zwave.message.SendDataRequestMessage;
|
||||
|
||||
public class MultilevelSwitchSetRequest extends SendDataRequestMessage {
|
||||
private int level;
|
||||
|
||||
public MultilevelSwitchSetRequest() {
|
||||
this(99);
|
||||
}
|
||||
|
||||
public MultilevelSwitchSetRequest(int _level) {
|
||||
this((byte)0, _level);
|
||||
}
|
||||
|
||||
public MultilevelSwitchSetRequest(byte _nodeId, int _level) {
|
||||
super(_nodeId, CommandClass.SWITCH_MULTILEVEL, (byte) 0x01);
|
||||
level = _level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getPayload() {
|
||||
return asByteArray(level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return name() + " node: " + nodeId + " level: " + level;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.ControllerMessageType;
|
||||
import com.lanternsoftware.zwave.message.NoCommandRequestMessage;
|
||||
|
||||
public class NodeInfoRequest extends NoCommandRequestMessage {
|
||||
public NodeInfoRequest() {
|
||||
super(ControllerMessageType.RequestNodeInfo);
|
||||
}
|
||||
|
||||
public NodeInfoRequest(byte _nodeId) {
|
||||
super(_nodeId, ControllerMessageType.RequestNodeInfo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.ControllerMessageType;
|
||||
import com.lanternsoftware.zwave.message.NoCommandResponseMessage;
|
||||
|
||||
public class NodeInfoResponse extends NoCommandResponseMessage {
|
||||
public NodeInfoResponse() {
|
||||
super(ControllerMessageType.RequestNodeInfo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.util.CollectionUtils;
|
||||
import com.lanternsoftware.zwave.message.CommandClass;
|
||||
import com.lanternsoftware.zwave.message.ControllerMessageType;
|
||||
import com.lanternsoftware.zwave.message.RequestMessage;
|
||||
|
||||
public class SendDataRequest extends RequestMessage {
|
||||
public enum TransmissionState {
|
||||
COMPLETE_OK((byte) 0x00, "Transmission complete and ACK received"),
|
||||
COMPLETE_NO_ACK((byte) 0x01, "Transmission complete, no ACK received"),
|
||||
COMPLETE_FAIL((byte) 0x02, "Transmission failed"),
|
||||
COMPLETE_NOT_IDLE((byte) 0x03, "Transmission failed, network busy"),
|
||||
COMPLETE_NOROUTE((byte) 0x04, "Tranmission complete, no return route");
|
||||
|
||||
public byte key;
|
||||
public String label;
|
||||
|
||||
TransmissionState(byte _key, String _label) {
|
||||
key = _key;
|
||||
label = _label;
|
||||
}
|
||||
|
||||
public static TransmissionState fromKey(byte _key) {
|
||||
for (TransmissionState state : values()) {
|
||||
if (state.key == _key)
|
||||
return state;
|
||||
}
|
||||
return COMPLETE_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
private TransmissionState state;
|
||||
private byte callbackId;
|
||||
|
||||
public SendDataRequest() {
|
||||
super(ControllerMessageType.SendData, CommandClass.NO_OPERATION, (byte) 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromPayload(byte[] _payload) {
|
||||
if (CollectionUtils.length(_payload) > 5) {
|
||||
callbackId = _payload[4];
|
||||
state = TransmissionState.fromKey(_payload[5]);
|
||||
}
|
||||
}
|
||||
|
||||
public TransmissionState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public byte getCallbackId() {
|
||||
return callbackId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return name() + " callbackId: " + callbackId + " state: " + state.name();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.util.CollectionUtils;
|
||||
import com.lanternsoftware.zwave.message.CommandClass;
|
||||
import com.lanternsoftware.zwave.message.ControllerMessageType;
|
||||
import com.lanternsoftware.zwave.message.ResponseMessage;
|
||||
|
||||
public class SendDataResponse extends ResponseMessage {
|
||||
private byte response;
|
||||
|
||||
public SendDataResponse() {
|
||||
this((byte) 0);
|
||||
}
|
||||
|
||||
public SendDataResponse(byte _response) {
|
||||
super(ControllerMessageType.SendData, CommandClass.NO_OPERATION, (byte) 0);
|
||||
response = _response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromPayload(byte[] _payload) {
|
||||
if (CollectionUtils.length(_payload) > 0)
|
||||
response = _payload[0];
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return response != 0;
|
||||
}
|
||||
|
||||
public String describe() {
|
||||
return name() + ": " + (isSuccess() ? "SUCCESS" : "FAILURE");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.CommandClass;
|
||||
import com.lanternsoftware.zwave.message.SendDataRequestMessage;
|
||||
|
||||
public class ThermostatModeGetRequest extends SendDataRequestMessage {
|
||||
public ThermostatModeGetRequest() {
|
||||
this((byte) 0);
|
||||
}
|
||||
|
||||
public ThermostatModeGetRequest(byte _nodeId) {
|
||||
super(_nodeId, CommandClass.THERMOSTAT_MODE, (byte) 0x02);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return name() + " node: " + nodeId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.CommandClass;
|
||||
import com.lanternsoftware.zwave.message.SendDataRequestMessage;
|
||||
import com.lanternsoftware.zwave.message.thermostat.ThermostatMode;
|
||||
|
||||
public class ThermostatModeSetRequest extends SendDataRequestMessage {
|
||||
private ThermostatMode mode;
|
||||
|
||||
public ThermostatModeSetRequest() {
|
||||
this((byte) 0, ThermostatMode.OFF);
|
||||
}
|
||||
|
||||
public ThermostatModeSetRequest(byte _nodeId, ThermostatMode _mode) {
|
||||
super(_nodeId, CommandClass.THERMOSTAT_MODE, (byte) 0x01);
|
||||
mode = _mode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getPayload() {
|
||||
return asByteArray(mode.data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return name() + " node: " + nodeId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.CommandClass;
|
||||
import com.lanternsoftware.zwave.message.SendDataRequestMessage;
|
||||
import com.lanternsoftware.zwave.message.thermostat.ThermostatSetPointIndex;
|
||||
|
||||
public class ThermostatSetPointGetRequest extends SendDataRequestMessage {
|
||||
private ThermostatSetPointIndex index;
|
||||
|
||||
public ThermostatSetPointGetRequest() {
|
||||
this((byte) 0, ThermostatSetPointIndex.HEATING);
|
||||
}
|
||||
|
||||
public ThermostatSetPointGetRequest(byte _nodeId, ThermostatSetPointIndex _index) {
|
||||
super(_nodeId, CommandClass.THERMOSTAT_SETPOINT, (byte) 0x02);
|
||||
index = _index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getPayload() {
|
||||
return asByteArray(index.index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return name() + " node: " + nodeId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.CommandClass;
|
||||
import com.lanternsoftware.zwave.message.ControllerMessageType;
|
||||
import com.lanternsoftware.zwave.message.MessageUtil;
|
||||
import com.lanternsoftware.zwave.message.RequestMessage;
|
||||
import com.lanternsoftware.zwave.message.thermostat.ThermostatSetPointIndex;
|
||||
|
||||
public class ThermostatSetPointReportRequest extends RequestMessage {
|
||||
private ThermostatSetPointIndex index;
|
||||
private double temperature;
|
||||
|
||||
public ThermostatSetPointReportRequest() {
|
||||
this((byte) 0);
|
||||
}
|
||||
|
||||
public ThermostatSetPointReportRequest(byte _nodeId) {
|
||||
super(_nodeId, ControllerMessageType.ApplicationCommandHandler, CommandClass.THERMOSTAT_SETPOINT, (byte) 0x03);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromPayload(byte[] _payload) {
|
||||
nodeId = _payload[5];
|
||||
index = ThermostatSetPointIndex.fromIndex(_payload[9]);
|
||||
if (index != null)
|
||||
temperature = MessageUtil.getTemperatureCelsius(_payload, 10);
|
||||
}
|
||||
|
||||
public ThermostatSetPointIndex getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public double getTemperatureCelsius() {
|
||||
return temperature;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return name() + " node: " + nodeId + " level: " + temperature;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.CommandClass;
|
||||
import com.lanternsoftware.zwave.message.SendDataRequestMessage;
|
||||
import com.lanternsoftware.zwave.message.thermostat.ThermostatSetPointIndex;
|
||||
|
||||
public class ThermostatSetPointSetRequest extends SendDataRequestMessage {
|
||||
private ThermostatSetPointIndex index;
|
||||
private int level;
|
||||
|
||||
public ThermostatSetPointSetRequest() {
|
||||
this(ThermostatSetPointIndex.HEATING, 72);
|
||||
}
|
||||
|
||||
public ThermostatSetPointSetRequest(ThermostatSetPointIndex _index, int _level) {
|
||||
this((byte)0, _index, _level);
|
||||
}
|
||||
|
||||
public ThermostatSetPointSetRequest(byte _nodeId, ThermostatSetPointIndex _index, int _level) {
|
||||
super(_nodeId, CommandClass.THERMOSTAT_SETPOINT, (byte) 0x01);
|
||||
index = _index;
|
||||
level = _level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getPayload() {
|
||||
return asByteArray(index.index, (byte)9, (byte)level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return name() + " node: " + nodeId + " level: " + level;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.zwave.message.CommandClass;
|
||||
import com.lanternsoftware.zwave.message.SendDataRequestMessage;
|
||||
|
||||
public class ThermostatSetPointSupportedGetRequest extends SendDataRequestMessage {
|
||||
public ThermostatSetPointSupportedGetRequest() {
|
||||
this((byte) 0);
|
||||
}
|
||||
|
||||
public ThermostatSetPointSupportedGetRequest(byte _nodeId) {
|
||||
super(_nodeId, CommandClass.THERMOSTAT_SETPOINT, (byte) 0x04);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return name() + " node: " + nodeId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.lanternsoftware.zwave.message.impl;
|
||||
|
||||
import com.lanternsoftware.util.CollectionUtils;
|
||||
import com.lanternsoftware.zwave.message.CommandClass;
|
||||
import com.lanternsoftware.zwave.message.ControllerMessageType;
|
||||
import com.lanternsoftware.zwave.message.RequestMessage;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class ThermostatSetPointSupportedReportRequest extends RequestMessage {
|
||||
private Set<Byte> supportedSetPointIndices;
|
||||
|
||||
public ThermostatSetPointSupportedReportRequest() {
|
||||
this((byte) 0);
|
||||
}
|
||||
|
||||
public ThermostatSetPointSupportedReportRequest(byte _nodeId) {
|
||||
super(_nodeId, ControllerMessageType.ApplicationCommandHandler, CommandClass.THERMOSTAT_SETPOINT, (byte) 0x05);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromPayload(byte[] _payload) {
|
||||
supportedSetPointIndices = new TreeSet<>();
|
||||
for (int i = 9; i < _payload.length - 1; ++i) {
|
||||
for (int bit = 0; bit < 8; ++bit) {
|
||||
if ((_payload[i] & (1 << bit)) != 0) {
|
||||
supportedSetPointIndices.add((byte)(((i - 9) << 3) + bit));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Set<Byte> getSupportedSetPointIndices() {
|
||||
return supportedSetPointIndices;
|
||||
}
|
||||
|
||||
public boolean isSupportedIndex(byte _index) {
|
||||
return CollectionUtils.contains(supportedSetPointIndices, _index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return name() + " node: " + nodeId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.lanternsoftware.zwave.message.thermostat;
|
||||
|
||||
public enum ThermostatMode {
|
||||
OFF((byte)0),
|
||||
HEAT((byte)1),
|
||||
COOL((byte)2),
|
||||
AUTO((byte)3),
|
||||
AUXILIARY((byte)4);
|
||||
|
||||
public final byte data;
|
||||
|
||||
ThermostatMode(byte _data) {
|
||||
data = _data;
|
||||
}
|
||||
|
||||
public static ThermostatMode fromByte(byte _bt) {
|
||||
for (ThermostatMode mode : values()) {
|
||||
if (mode.data == _bt)
|
||||
return mode;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.lanternsoftware.zwave.message.thermostat;
|
||||
|
||||
public enum ThermostatSetPointIndex {
|
||||
HEATING((byte)1),
|
||||
COOLING((byte)2),
|
||||
FURNACE((byte)7),
|
||||
DRY_AIR((byte)8),
|
||||
MOIST_AIR((byte)9),
|
||||
AUTO_CHANGEOVER((byte)10),
|
||||
HEATING_ECON((byte)11),
|
||||
COOLING_ECON((byte)12),
|
||||
AWAY_HEATING((byte)13),
|
||||
COOLING_HEATING((byte)14),
|
||||
HEATING_MINIMUM((byte)101),
|
||||
COOLING_MINIMUM((byte)102),
|
||||
FURNACE_MINIMUM((byte)107),
|
||||
DRY_AIR_MINIMUM((byte)108),
|
||||
MOIST_AIR_MINIMUM((byte)109),
|
||||
AUTO_CHANGEOVER_MINIMUM((byte)110),
|
||||
HEATING_ECON_MINIMUM((byte)111),
|
||||
COOLING_ECON_MINIMUM((byte)112),
|
||||
AWAY_HEATING_MINIMUM((byte)113),
|
||||
COOLING_HEATING_MINIMUM((byte)114),
|
||||
HEATING_MAXIMUM((byte)201),
|
||||
COOLING_MAXIMUM((byte)202),
|
||||
FURNACE_MAXIMUM((byte)207),
|
||||
DRY_AIR_MAXIMUM((byte)208),
|
||||
MOIST_AIR_MAXIMUM((byte)209),
|
||||
AUTO_CHANGEOVER_MAXIMUM((byte)210),
|
||||
HEATING_ECON_MAXIMUM((byte)211),
|
||||
COOLING_ECON_MAXIMUM((byte)212),
|
||||
AWAY_HEATING_MAXIMUM((byte)213),
|
||||
COOLING_HEATING_MAXIMUM((byte)214);
|
||||
|
||||
public final byte index;
|
||||
|
||||
ThermostatSetPointIndex(byte _index) {
|
||||
index = _index;
|
||||
}
|
||||
|
||||
public static ThermostatSetPointIndex fromIndex(byte _index) {
|
||||
for (ThermostatSetPointIndex idx : values()) {
|
||||
if (idx.index == _index)
|
||||
return idx;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.lanternsoftware.zwave.node;
|
||||
|
||||
import com.lanternsoftware.zwave.message.CommandClass;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class Node {
|
||||
private byte id;
|
||||
private Set<CommandClass> commandClasses;
|
||||
|
||||
public Node() {
|
||||
}
|
||||
|
||||
public Node(byte _id, Set<CommandClass> _commandClasses) {
|
||||
id = _id;
|
||||
commandClasses = _commandClasses;
|
||||
}
|
||||
|
||||
public byte getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(byte _id) {
|
||||
id = _id;
|
||||
}
|
||||
|
||||
public Set<CommandClass> getCommandClasses() {
|
||||
return commandClasses;
|
||||
}
|
||||
|
||||
public void setCommandClasses(Set<CommandClass> _commandClasses) {
|
||||
commandClasses = _commandClasses;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.lanternsoftware.zwave.node;
|
||||
|
||||
import com.lanternsoftware.util.CollectionUtils;
|
||||
import com.lanternsoftware.zwave.controller.Controller;
|
||||
import com.lanternsoftware.zwave.message.IMessageSubscriber;
|
||||
import com.lanternsoftware.zwave.message.MessageEngine;
|
||||
import com.lanternsoftware.zwave.message.impl.ApplicationUpdateRequest;
|
||||
import com.lanternsoftware.zwave.message.impl.ControllerInitialDataResponse;
|
||||
import com.lanternsoftware.zwave.message.impl.GetControllerIdResponse;
|
||||
import com.lanternsoftware.zwave.message.impl.NodeInfoRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class NodeManager {
|
||||
private static final Logger logger = LoggerFactory.getLogger(NodeManager.class);
|
||||
|
||||
private Controller controller;
|
||||
private ControllerInitialDataResponse initialDataResponse;
|
||||
private GetControllerIdResponse controllerIdResponse;
|
||||
private final Set<Byte> missingNodes = new HashSet<>();
|
||||
private Map<Byte, Node> nodes = new HashMap<>();
|
||||
|
||||
public NodeManager(final Controller _controller) {
|
||||
controller = _controller;
|
||||
MessageEngine.subscribe(new IMessageSubscriber<ControllerInitialDataResponse>() {
|
||||
@Override
|
||||
public Class<ControllerInitialDataResponse> getHandledMessageClass() {
|
||||
return ControllerInitialDataResponse.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(ControllerInitialDataResponse _response) {
|
||||
synchronized (NodeManager.this) {
|
||||
initialDataResponse = _response;
|
||||
init();
|
||||
}
|
||||
}
|
||||
});
|
||||
MessageEngine.subscribe(new IMessageSubscriber<GetControllerIdResponse>() {
|
||||
@Override
|
||||
public Class<GetControllerIdResponse> getHandledMessageClass() {
|
||||
return GetControllerIdResponse.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(GetControllerIdResponse _response) {
|
||||
synchronized (NodeManager.this) {
|
||||
controllerIdResponse = _response;
|
||||
init();
|
||||
}
|
||||
}
|
||||
});
|
||||
MessageEngine.subscribe(new IMessageSubscriber<ApplicationUpdateRequest>() {
|
||||
@Override
|
||||
public Class<ApplicationUpdateRequest> getHandledMessageClass() {
|
||||
return ApplicationUpdateRequest.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(ApplicationUpdateRequest _request) {
|
||||
synchronized (NodeManager.this) {
|
||||
if (missingNodes.remove(_request.getNodeId()))
|
||||
nodes.put(_request.getNodeId(), new Node(_request.getNodeId(), CollectionUtils.asHashSet(_request.getCommandClasses())));
|
||||
|
||||
logger.debug("Received command classes for node: {}", _request.getNodeId());
|
||||
requestNodeInfo();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void init() {
|
||||
if (!isStarted())
|
||||
return;
|
||||
missingNodes.clear();
|
||||
logger.info("Node Ids:{}", CollectionUtils.transformToCommaSeparated(initialDataResponse.getNodeIds(), String::valueOf));
|
||||
// missingNodes.addAll(CollectionUtils.filter(initialDataResponse.getNodeIds(), _b->_b != controllerIdResponse.getControllerId()));
|
||||
requestNodeInfo();
|
||||
}
|
||||
|
||||
private void requestNodeInfo() {
|
||||
if (!missingNodes.isEmpty())
|
||||
controller.send(new NodeInfoRequest(CollectionUtils.getFirst(missingNodes)));
|
||||
else
|
||||
notify();
|
||||
}
|
||||
|
||||
private boolean isStarted() {
|
||||
return ((initialDataResponse != null) && (controllerIdResponse != null));
|
||||
}
|
||||
|
||||
public void waitForStartup() {
|
||||
synchronized (this) {
|
||||
if (!isStarted()) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException _e) {
|
||||
_e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
com.lanternsoftware.zwave.message.impl.ApplicationUpdateRequest
|
||||
com.lanternsoftware.zwave.message.impl.BinarySwitchSetRequest
|
||||
com.lanternsoftware.zwave.message.impl.ByteMessage
|
||||
com.lanternsoftware.zwave.message.impl.ControllerCapabilitiesRequest
|
||||
com.lanternsoftware.zwave.message.impl.ControllerCapabilitiesResponse
|
||||
com.lanternsoftware.zwave.message.impl.ControllerInitialDataRequest
|
||||
com.lanternsoftware.zwave.message.impl.ControllerInitialDataResponse
|
||||
com.lanternsoftware.zwave.message.impl.DeviceManufacturerActionRequest
|
||||
com.lanternsoftware.zwave.message.impl.GetControllerIdRequest
|
||||
com.lanternsoftware.zwave.message.impl.GetControllerIdResponse
|
||||
com.lanternsoftware.zwave.message.impl.MultilevelSensorGetRequest
|
||||
com.lanternsoftware.zwave.message.impl.MultilevelSensorReportRequest
|
||||
com.lanternsoftware.zwave.message.impl.MultilevelSwitchReportRequest
|
||||
com.lanternsoftware.zwave.message.impl.MultilevelSwitchSetRequest
|
||||
com.lanternsoftware.zwave.message.impl.NodeInfoRequest
|
||||
com.lanternsoftware.zwave.message.impl.NodeInfoResponse
|
||||
com.lanternsoftware.zwave.message.impl.SendDataRequest
|
||||
com.lanternsoftware.zwave.message.impl.SendDataResponse
|
||||
com.lanternsoftware.zwave.message.impl.ThermostatModeGetRequest
|
||||
com.lanternsoftware.zwave.message.impl.ThermostatSetPointGetRequest
|
||||
com.lanternsoftware.zwave.message.impl.ThermostatSetPointReportRequest
|
||||
com.lanternsoftware.zwave.message.impl.ThermostatSetPointSetRequest
|
||||
com.lanternsoftware.zwave.message.impl.ThermostatSetPointSupportedGetRequest
|
||||
com.lanternsoftware.zwave.message.impl.ThermostatSetPointSupportedReportRequest
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.lanternsoftware.zwave;
|
||||
|
||||
public class TestBits {
|
||||
public static void main(String[] args) {
|
||||
System.out.println(Integer.toBinaryString(6));
|
||||
System.out.println(Integer.toBinaryString(0x18));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user