diff --git a/hal-core/src/se/hal/struct/devicedata/PressureSensorData.java b/hal-core/src/se/hal/struct/devicedata/PressureSensorData.java new file mode 100644 index 00000000..29fed6ce --- /dev/null +++ b/hal-core/src/se/hal/struct/devicedata/PressureSensorData.java @@ -0,0 +1,42 @@ +package se.hal.struct.devicedata; + +import se.hal.intf.HalSensorData; + + +public class PressureSensorData extends HalSensorData { + + private double pressure; + + + public PressureSensorData(){} + public PressureSensorData(double pressure, long timestamp){ + super.setTimestamp(timestamp); + this.pressure = pressure; + } + + + @Override + public String toString(){ + return pressure + " hPa"; + } + + // ---------------------------------------- + // Storage methods + // ---------------------------------------- + + /** + * @return pressure in degrees hPa + */ + @Override + public double getData() { + return pressure; + } + + /** + * @param pressure the temperature to set in degrees hPa + */ + @Override + public void setData(double pressure) { + this.pressure = pressure; + } +} diff --git a/logging.properties b/logging.properties index e9ea412c..f73fd7fd 100644 --- a/logging.properties +++ b/logging.properties @@ -26,5 +26,6 @@ se.hal.level = ALL # ------------------------------------- com.zsmartsystems.zigbee.level = INFO +profiling.com.zsmartsystems.zigbee.level = INFO #com.zsmartsystems.zigbee.dongle.cc2531.zigbee.util.ByteUtils.level = INFO #com.zsmartsystems.zigbee.dongle.cc2531.network.packet.ZToolPacketStream.level = INFO \ No newline at end of file diff --git a/plugins/hal-zigbee/src/se/hal/plugin/zigbee/HalZigbeeController.java b/plugins/hal-zigbee/src/se/hal/plugin/zigbee/HalZigbeeController.java index 6437f55e..7259af2d 100644 --- a/plugins/hal-zigbee/src/se/hal/plugin/zigbee/HalZigbeeController.java +++ b/plugins/hal-zigbee/src/se/hal/plugin/zigbee/HalZigbeeController.java @@ -15,15 +15,22 @@ import com.zsmartsystems.zigbee.transport.TransportConfig; import com.zsmartsystems.zigbee.transport.TransportConfigOption; import com.zsmartsystems.zigbee.transport.ZigBeePort; import com.zsmartsystems.zigbee.transport.ZigBeeTransportTransmit; +import com.zsmartsystems.zigbee.zcl.ZclAttribute; +import com.zsmartsystems.zigbee.zcl.ZclAttributeListener; +import com.zsmartsystems.zigbee.zcl.ZclCluster; import com.zsmartsystems.zigbee.zcl.clusters.*; import com.zsmartsystems.zigbee.zdo.field.NodeDescriptor; import se.hal.HalContext; import se.hal.intf.*; +import se.hal.plugin.zigbee.device.*; import zutil.Timer; import zutil.log.LogUtil; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.logging.Level; import java.util.logging.Logger; /** @@ -32,9 +39,11 @@ import java.util.logging.Logger; public class HalZigbeeController implements HalSensorController, HalEventController, HalAutostartController, + HalScannableController, ZigBeeAnnounceListener, ZigBeeNetworkNodeListener, - HalScannableController { + ZigBeeNetworkEndpointListener, + ZclAttributeListener { private static final Logger logger = LogUtil.getLogger(); @@ -51,14 +60,14 @@ public class HalZigbeeController implements HalSensorController, private Timer permitJoinTimer; private HalDeviceReportListener deviceListener; - private List registeredDevices; + private List registeredDevices = new ArrayList<>(); public HalZigbeeController() {} - // -------------------------- + // ------------------------------------------ // Lifecycle Methods - // -------------------------- + // ------------------------------------------ @Override public boolean isAvailable() { @@ -179,58 +188,128 @@ public class HalZigbeeController implements HalSensorController, serialPort.close(); } - // -------------------------- - // Zigbee Methods - // -------------------------- + // ------------------------------------------ + // Zigbee Node Methods + // ------------------------------------------ @Override public void deviceStatusUpdate(ZigBeeNodeStatus deviceStatus, Integer networkAddress, IeeeAddress ieeeAddress) { - System.out.println(deviceStatus.name() + " status updated."); + logger.fine("New device connected to network: " + ieeeAddress + "(" + deviceStatus + ")"); } @Override public void announceUnknownDevice(Integer networkAddress) { - System.out.println("Unknown device: " + networkAddress); + logger.fine("Unknown device connected to network: " + networkAddress); } @Override public void nodeAdded(final ZigBeeNode node) { - System.out.println("nodeAdded: " + node); + nodeUpdated(node); + } + @Override + public void nodeUpdated(final ZigBeeNode node) { // If this is the coordinator (NWK address 0), ignore this device if (node.getLogicalType() == NodeDescriptor.LogicalType.COORDINATOR || node.getNetworkAddress() == 0) { - System.out.println(node.getIeeeAddress() + ": is a coordinator, skipping."); + logger.fine("[Node: " + node.getIeeeAddress() + "]: Node is coordinator, ignoring registration."); return; } if (!node.isDiscovered()) { - System.out.println(node.getIeeeAddress() + ": Node discovery not complete"); + logger.fine("[Node: " + node.getIeeeAddress() + "]: Node discovery not complete, ignoring registration."); return; } // Perform the device properties discovery. - System.out.println(node.getIeeeAddress() + ": " + - "Manufacturer=" + node.getNodeDescriptor().getManufacturerCode()); - } - - @Override - public void nodeUpdated(final ZigBeeNode node) { - System.out.println("nodeUpdated: " + node); + node.removeNetworkEndpointListener(this); + node.addNetworkEndpointListener(this); + logger.fine("[Node: " + node.getIeeeAddress() + "]: Node has been registered: " + + "Manufacturer=" + node.getNodeDescriptor().getManufacturerCode() + + "Type=" + node.getNodeDescriptor().getLogicalType()); } @Override public void nodeRemoved(final ZigBeeNode node) { - System.out.println("nodeRemoved: " + node); + node.removeNetworkEndpointListener(this); + logger.fine("[Node: " + node.getIeeeAddress() + "]: Node registration has been removed."); } - // -------------------------- + // ------------------------------------------ + // Zigbee Endpoint Methods + // ------------------------------------------ + + @Override + public void deviceAdded(ZigBeeEndpoint endpoint) { + deviceUpdated(endpoint); + } + + @Override + public void deviceUpdated(ZigBeeEndpoint endpoint) { + logger.fine("[Node: " + endpoint.getIeeeAddress() + ", Endpoint: " + endpoint.getEndpointId() + "]: Received a Zigbee endpoint update: " + endpoint); + + for (int inputClusterId : endpoint.getInputClusterIds()) { + ZigbeeHalDeviceConfig config = createDeviceConfig(inputClusterId); + + if (config != null) + registerCluster(endpoint, config); + } + } + + private ZigbeeHalDeviceConfig createDeviceConfig(int clusterId) { + switch (clusterId) { + case ZclRelativeHumidityMeasurementCluster.CLUSTER_ID: return new ZigbeeHumidityConfig(); + case ZclOnOffCluster.CLUSTER_ID: return new ZigbeeOnOffConfig(); + case ZclPressureMeasurementCluster.CLUSTER_ID: return new ZigbeePressureConfig(); + case ZclTemperatureMeasurementCluster.CLUSTER_ID: return new ZigbeeTemperatureConfig(); + } + + return null; + } + + private void registerCluster(ZigBeeEndpoint endpoint, ZigbeeHalDeviceConfig config) { + ZclCluster cluster = endpoint.getInputCluster(config.getZigbeeClusterId()); + if (cluster != null) { + config.setZigbeeNodeAddress(endpoint.getIeeeAddress()); + cluster.addAttributeListener(this); + + // // TODO: Notify listener that a device is online + if (deviceListener != null) + deviceListener.reportReceived(config, null); + } + } + + @Override + public void deviceRemoved(ZigBeeEndpoint endpoint) { + logger.fine("[Node: " + endpoint.getIeeeAddress() + ", Endpoint: " + endpoint.getEndpointId() + "]: Endpoint removed: " + endpoint); + } + + // ------------------------------------------ + // Zigbee Cluster Attribute Methods + // ------------------------------------------ + + @Override + public void attributeUpdated(ZclAttribute attribute, Object value) { + if (deviceListener != null) { + ZigbeeHalDeviceConfig config = createDeviceConfig(attribute.getCluster().getId()); + + if (config != null) + deviceListener.reportReceived(config, config.getDeviceData(attribute)); + else + logger.severe("Cluster ID (" + attribute.getCluster().getId() + ") is not supported but a listener was added."); + } + } + + // ------------------------------------------ // Hal Overrides - // -------------------------- + // ------------------------------------------ @Override public void register(HalDeviceConfig deviceConfig) { - + if (deviceConfig instanceof ZigbeeHalDeviceConfig && !registeredDevices.contains(deviceConfig)) { + ZigbeeHalDeviceConfig zigbeeDevice = (ZigbeeHalDeviceConfig) deviceConfig; + registeredDevices.add(zigbeeDevice); + } } @Override @@ -245,7 +324,9 @@ public class HalZigbeeController implements HalSensorController, @Override public void send(HalEventConfig eventConfig, HalEventData eventData) { - + if (eventConfig instanceof ZigbeeHalEventDeviceConfig) { + ((ZigbeeHalEventDeviceConfig) eventConfig).sendZigbeeCommand(networkManager, eventData); + } } @Override @@ -255,6 +336,8 @@ public class HalZigbeeController implements HalSensorController, @Override public void startScan() { + logger.info("Starting Zigbee pairing process."); + networkManager.permitJoin(120); permitJoinTimer = new Timer(120_000); permitJoinTimer.start(); diff --git a/plugins/hal-zigbee/src/se/hal/plugin/zigbee/ZigBeeDataStore.java b/plugins/hal-zigbee/src/se/hal/plugin/zigbee/ZigBeeDataStore.java index e833175a..fd422c07 100644 --- a/plugins/hal-zigbee/src/se/hal/plugin/zigbee/ZigBeeDataStore.java +++ b/plugins/hal-zigbee/src/se/hal/plugin/zigbee/ZigBeeDataStore.java @@ -45,36 +45,6 @@ public class ZigBeeDataStore implements ZigBeeNetworkDataStore { private HashMap devices = new HashMap<>(); - public ZigBeeDataStore() { - /*ZigBeeNodeDao controller = new ZigBeeNodeDao(); - controller.setIeeeAddress(new IeeeAddress("00124B001CCE1B5F")); - controller.setNetworkAddress(0); - controller.setBindingTable(new HashSet<>()); - controller.setEndpoints(Collections.EMPTY_LIST); - controller.setNodeDescriptor(null); - controller.setPowerDescriptor(null); - writeNode(controller); - - ZigBeeNodeDao ikeaOutlet = new ZigBeeNodeDao(); - ikeaOutlet.setIeeeAddress(new IeeeAddress("00158D000488A47F")); - ikeaOutlet.setNetworkAddress(10697); - ikeaOutlet.setBindingTable(new HashSet<>()); - ikeaOutlet.setEndpoints(Collections.EMPTY_LIST); - ikeaOutlet.setNodeDescriptor(null); - ikeaOutlet.setPowerDescriptor(null); - writeNode(ikeaOutlet); - - ZigBeeNodeDao aquaraTemp = new ZigBeeNodeDao(); - aquaraTemp.setIeeeAddress(new IeeeAddress("842E14FFFE63AE4B")); - aquaraTemp.setNetworkAddress(52953); - aquaraTemp.setBindingTable(new HashSet<>()); - aquaraTemp.setEndpoints(Collections.EMPTY_LIST); - aquaraTemp.setNodeDescriptor(null); - aquaraTemp.setPowerDescriptor(null); - writeNode(aquaraTemp);*/ - } - - @Override public Set readNetworkNodes() { return devices.keySet(); diff --git a/plugins/hal-zigbee/src/se/hal/plugin/zigbee/device/ZigbeeHalDeviceConfig.java b/plugins/hal-zigbee/src/se/hal/plugin/zigbee/device/ZigbeeHalDeviceConfig.java new file mode 100644 index 00000000..ab12aa34 --- /dev/null +++ b/plugins/hal-zigbee/src/se/hal/plugin/zigbee/device/ZigbeeHalDeviceConfig.java @@ -0,0 +1,62 @@ +package se.hal.plugin.zigbee.device; + +import com.zsmartsystems.zigbee.IeeeAddress; +import com.zsmartsystems.zigbee.zcl.ZclAttribute; +import se.hal.intf.HalAbstractController; +import se.hal.intf.HalDeviceConfig; +import se.hal.intf.HalDeviceData; +import se.hal.plugin.zigbee.HalZigbeeController; + +/** + * A generic class that is extended by all Endpoint config classes. + */ +public abstract class ZigbeeHalDeviceConfig implements HalDeviceConfig { + private String zigbeeNodeAddressStr; + private transient IeeeAddress zigbeeNodeAddress; + + + public void setZigbeeNodeAddress(IeeeAddress zigbeeNodeAddress) { + this.zigbeeNodeAddress = zigbeeNodeAddress; + this.zigbeeNodeAddressStr = zigbeeNodeAddress.toString(); + } + + public IeeeAddress getZigbeeNodeAddress() { + if (zigbeeNodeAddress == null && zigbeeNodeAddressStr != null) + zigbeeNodeAddress = new IeeeAddress(zigbeeNodeAddressStr); + return zigbeeNodeAddress; + } + + // -------------------------- + // Abstract Methods + // -------------------------- + + /** + * @param zclAttribute + * @return a HalDeviceData object containing the same value representation as the endpoint. + */ + public abstract HalDeviceData getDeviceData(ZclAttribute zclAttribute); + + /** + * @return the cluster ID that is supported by this device config class + */ + public abstract int getZigbeeClusterId(); + + // -------------------------- + // Hal Methods + // -------------------------- + + @Override + public Class getDeviceControllerClass() { + return HalZigbeeController.class; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ZigbeeHalDeviceConfig that = (ZigbeeHalDeviceConfig) o; + return zigbeeNodeAddress.equals(that.zigbeeNodeAddress) && + getZigbeeClusterId() == that.getZigbeeClusterId(); + } +} diff --git a/plugins/hal-zigbee/src/se/hal/plugin/zigbee/device/ZigbeeHalEventDeviceConfig.java b/plugins/hal-zigbee/src/se/hal/plugin/zigbee/device/ZigbeeHalEventDeviceConfig.java new file mode 100644 index 00000000..1ed47db7 --- /dev/null +++ b/plugins/hal-zigbee/src/se/hal/plugin/zigbee/device/ZigbeeHalEventDeviceConfig.java @@ -0,0 +1,65 @@ +package se.hal.plugin.zigbee.device; + +import com.zsmartsystems.zigbee.CommandResult; +import com.zsmartsystems.zigbee.ZigBeeEndpoint; +import com.zsmartsystems.zigbee.ZigBeeNetworkManager; +import com.zsmartsystems.zigbee.ZigBeeNode; +import com.zsmartsystems.zigbee.zcl.ZclCluster; +import com.zsmartsystems.zigbee.zcl.ZclCommand; +import se.hal.intf.HalEventData; +import zutil.log.LogUtil; + +import java.util.logging.Logger; + +/** + * A generic class that is extended by all Endpoint config classes. + */ +public abstract class ZigbeeHalEventDeviceConfig extends ZigbeeHalDeviceConfig { + private static final Logger logger = LogUtil.getLogger(); + + + public ZclCluster getZigbeeCluster(ZigBeeNetworkManager networkManager) { + ZigBeeNode node = networkManager.getNode(getZigbeeNodeAddress()); + + for (ZigBeeEndpoint endpoint : node.getEndpoints()) { + ZclCluster cluster = endpoint.getInputCluster(getZigbeeClusterId()); + if (cluster != null) { + return cluster; + } + } + + return null; + } + + public void sendZigbeeCommand(ZigBeeNetworkManager networkManager, HalEventData data) { + ZclCluster cluster = getZigbeeCluster(networkManager); + + if (cluster != null) { + try { + ZclCommand command = getZigbeeCommandObject(data); + // Need to do reflection as the generic method has visibility protected. + CommandResult result = + (CommandResult) ZclCluster.class.getMethod("sendCommand", ZclCommand.class).invoke(command); + + if (result.isError() || result.isTimeout()) { + logger.warning("[Endpoint: " + cluster.getZigBeeAddress() + "] Command failed with error: " + result.isError() + " (timeout=" + result.isTimeout() + ")"); + } else { + logger.info("[Endpoint: " + cluster.getZigBeeAddress() + "] Command has been successfully sent"); + } + } catch (Exception e) { + logger.warning("[Endpoint: " + cluster.getZigBeeAddress() + "] Failed to send command [" + e.getMessage() + "]"); + } + } else { + logger.warning("[Node: " + getZigbeeNodeAddress() + "] Unable to find cluster."); + } + } + + /** + * Method will create a Zigbee command object based on the value of the Hal event data. + * + * @param data is the Hal event data value that should be converted. + * @return a new Zigbee command object or null if no equal representation can be created based on the data. + */ + protected abstract ZclCommand getZigbeeCommandObject(HalEventData data); + +} diff --git a/plugins/hal-zigbee/src/se/hal/plugin/zigbee/device/ZigbeeHumidityConfig.java b/plugins/hal-zigbee/src/se/hal/plugin/zigbee/device/ZigbeeHumidityConfig.java new file mode 100644 index 00000000..cbf80716 --- /dev/null +++ b/plugins/hal-zigbee/src/se/hal/plugin/zigbee/device/ZigbeeHumidityConfig.java @@ -0,0 +1,46 @@ +package se.hal.plugin.zigbee.device; + +import com.zsmartsystems.zigbee.zcl.ZclAttribute; +import com.zsmartsystems.zigbee.zcl.clusters.ZclRelativeHumidityMeasurementCluster; +import se.hal.intf.HalDeviceData; +import se.hal.intf.HalSensorConfig; +import se.hal.struct.devicedata.HumiditySensorData; + +/** + * A device configuration for a specific endpoint on a Zigbee device. + */ +public class ZigbeeHumidityConfig extends ZigbeeHalDeviceConfig implements HalSensorConfig { + + // -------------------------- + // Zigbee Methods + // -------------------------- + + @Override + public HalDeviceData getDeviceData(ZclAttribute zclAttribute) { + return null; + } + + @Override + public int getZigbeeClusterId() { + return ZclRelativeHumidityMeasurementCluster.CLUSTER_ID; + } + + // -------------------------- + // Hal Methods + // -------------------------- + + @Override + public long getDataInterval() { + return 0; + } + + @Override + public AggregationMethod getAggregationMethod() { + return AggregationMethod.AVERAGE; + } + + @Override + public Class getDeviceDataClass() { + return HumiditySensorData.class; + } +} diff --git a/plugins/hal-zigbee/src/se/hal/plugin/zigbee/device/ZigbeeOnOffConfig.java b/plugins/hal-zigbee/src/se/hal/plugin/zigbee/device/ZigbeeOnOffConfig.java new file mode 100644 index 00000000..faff06c1 --- /dev/null +++ b/plugins/hal-zigbee/src/se/hal/plugin/zigbee/device/ZigbeeOnOffConfig.java @@ -0,0 +1,50 @@ +package se.hal.plugin.zigbee.device; + +import com.zsmartsystems.zigbee.ZigBeeEndpoint; +import com.zsmartsystems.zigbee.zcl.ZclAttribute; +import com.zsmartsystems.zigbee.zcl.ZclCluster; +import com.zsmartsystems.zigbee.zcl.ZclCommand; +import com.zsmartsystems.zigbee.zcl.clusters.ZclOnOffCluster; +import com.zsmartsystems.zigbee.zcl.clusters.onoff.OffCommand; +import com.zsmartsystems.zigbee.zcl.clusters.onoff.OnCommand; +import se.hal.intf.HalDeviceData; +import se.hal.intf.HalEventConfig; +import se.hal.intf.HalEventData; +import se.hal.struct.devicedata.OnOffEventData; + +/** + * A device configuration for a specific endpoint on a Zigbee device. + */ +public class ZigbeeOnOffConfig extends ZigbeeHalEventDeviceConfig implements HalEventConfig { + + // -------------------------- + // Zigbee Methods + // -------------------------- + + @Override + public HalDeviceData getDeviceData(ZclAttribute zclAttribute) { + return null; + } + + @Override + public int getZigbeeClusterId() { + return ZclOnOffCluster.CLUSTER_ID; + } + + @Override + protected ZclCommand getZigbeeCommandObject(HalEventData data) { + if (! (data instanceof OnOffEventData)) + return null; + + return (((OnOffEventData) data).isOn() ? new OnCommand() : new OffCommand()); + } + + // -------------------------- + // Hal Methods + // -------------------------- + + @Override + public Class getDeviceDataClass() { + return OnOffEventData.class; + } +} diff --git a/plugins/hal-zigbee/src/se/hal/plugin/zigbee/device/ZigbeePressureConfig.java b/plugins/hal-zigbee/src/se/hal/plugin/zigbee/device/ZigbeePressureConfig.java new file mode 100644 index 00000000..19c214cc --- /dev/null +++ b/plugins/hal-zigbee/src/se/hal/plugin/zigbee/device/ZigbeePressureConfig.java @@ -0,0 +1,48 @@ +package se.hal.plugin.zigbee.device; + +import com.zsmartsystems.zigbee.zcl.ZclAttribute; +import com.zsmartsystems.zigbee.zcl.clusters.ZclTemperatureMeasurementCluster; +import se.hal.intf.HalDeviceData; +import se.hal.intf.HalSensorConfig; +import se.hal.struct.devicedata.TemperatureSensorData; + +/** + * A device configuration for a specific endpoint on a Zigbee device. + */ +public class ZigbeePressureConfig extends ZigbeeHalDeviceConfig implements HalSensorConfig { + + // -------------------------- + // Zigbee Methods + // -------------------------- + + @Override + public HalDeviceData getDeviceData(ZclAttribute zclAttribute) { + return null; + } + + @Override + public int getZigbeeClusterId() { + return ZclTemperatureMeasurementCluster.CLUSTER_ID; + } + + // -------------------------- + // Hal Methods + // -------------------------- + + @Override + public long getDataInterval() { + return 0; + } + + @Override + public AggregationMethod getAggregationMethod() { + return AggregationMethod.AVERAGE; + } + + @Override + public Class getDeviceDataClass() { + return TemperatureSensorData.class; + } + + +} diff --git a/plugins/hal-zigbee/src/se/hal/plugin/zigbee/device/ZigbeeTemperatureConfig.java b/plugins/hal-zigbee/src/se/hal/plugin/zigbee/device/ZigbeeTemperatureConfig.java new file mode 100644 index 00000000..433c5faf --- /dev/null +++ b/plugins/hal-zigbee/src/se/hal/plugin/zigbee/device/ZigbeeTemperatureConfig.java @@ -0,0 +1,47 @@ +package se.hal.plugin.zigbee.device; + +import com.zsmartsystems.zigbee.zcl.ZclAttribute; +import com.zsmartsystems.zigbee.zcl.clusters.ZclTemperatureMeasurementCluster; +import se.hal.intf.HalDeviceData; +import se.hal.intf.HalSensorConfig; +import se.hal.struct.devicedata.TemperatureSensorData; + +/** + * A device configuration for a specific endpoint on a Zigbee device. + */ +public class ZigbeeTemperatureConfig extends ZigbeeHalDeviceConfig implements HalSensorConfig { + + + // -------------------------- + // Zigbee Methods + // -------------------------- + + @Override + public HalDeviceData getDeviceData(ZclAttribute zclAttribute) { + return null; + } + + @Override + public int getZigbeeClusterId() { + return ZclTemperatureMeasurementCluster.CLUSTER_ID; + } + + // -------------------------- + // Hal Methods + // -------------------------- + + @Override + public long getDataInterval() { + return 0; + } + + @Override + public AggregationMethod getAggregationMethod() { + return AggregationMethod.AVERAGE; + } + + @Override + public Class getDeviceDataClass() { + return TemperatureSensorData.class; + } +} diff --git a/plugins/hal-zigbee/test/se/hal/plugin/zigbee/HalZigbeeControllerTest.java b/plugins/hal-zigbee/test/se/hal/plugin/zigbee/HalZigbeeControllerTest.java index a4d6693d..af940652 100644 --- a/plugins/hal-zigbee/test/se/hal/plugin/zigbee/HalZigbeeControllerTest.java +++ b/plugins/hal-zigbee/test/se/hal/plugin/zigbee/HalZigbeeControllerTest.java @@ -27,87 +27,122 @@ package se.hal.plugin.zigbee; import com.zsmartsystems.zigbee.ZigBeeEndpoint; import com.zsmartsystems.zigbee.ZigBeeNetworkManager; import com.zsmartsystems.zigbee.ZigBeeNode; +import com.zsmartsystems.zigbee.zcl.ZclAttribute; +import com.zsmartsystems.zigbee.zcl.ZclCluster; import zutil.log.CompactLogFormatter; import zutil.log.LogUtil; import java.io.IOException; +import java.util.Scanner; import java.util.logging.Level; public class HalZigbeeControllerTest { - public static void main(String[] args) throws IOException { + public static void main(String[] args) throws IOException, InterruptedException { LogUtil.readConfiguration("logging.properties"); LogUtil.setGlobalFormatter(new CompactLogFormatter()); LogUtil.setGlobalLevel(Level.ALL); HalZigbeeController controller = new HalZigbeeController(); - controller.initialize("COM3", HalZigbeeController.ZIGBEE_DONGLE_CC2531); + controller.initialize("COM5", HalZigbeeController.ZIGBEE_DONGLE_CC2531); - handleConsoleInput('h', controller.networkManager); + Scanner in = new Scanner(System.in); + handleConsoleInput("h", in, controller.networkManager); while (true) { - char input = waitForInout(); - handleConsoleInput(input, controller.networkManager); + System.out.print(""); + System.out.print("Input command and finish with ENTER: "); - if (input == 'q') break; + while (!in.hasNext()) { Thread.sleep(200); } + + String command = in.next().trim(); + handleConsoleInput(command, in, controller.networkManager); + in.nextLine(); // read in the rest of the input line + + if (command.equals("q")) break; } controller.close(); + System.exit(0); } - private static void handleConsoleInput(char input, ZigBeeNetworkManager networkManager) { - switch (input) { - case 'i': + private static void handleConsoleInput(String command, Scanner in, ZigBeeNetworkManager networkManager) { + switch (command) { + case "i": System.out.println("PAN ID = " + networkManager.getZigBeePanId()); System.out.println("Extended PAN ID = " + networkManager.getZigBeeExtendedPanId()); System.out.println("Channel = " + networkManager.getZigBeeChannel()); break; - case 'l': + case "l": + System.out.println("-----------------------------------------------------------------------"); for (ZigBeeNode node : networkManager.getNodes()) { - System.out.println(node + " (" + node.getNodeState() + ")"); + System.out.println("[node id: " + node.getNetworkAddress() + "] " + node + " (" + node.getNodeState() + ")"); for (ZigBeeEndpoint endpoint : node.getEndpoints()) { - System.out.println(" - " + endpoint); + System.out.println(" - [endpoint id: " + endpoint.getDeviceId() + "] " + endpoint); + System.out.println(" - Input Clusters:"); + + for (int inputClusterId : endpoint.getInputClusterIds()) { + ZclCluster cluster = endpoint.getInputCluster(inputClusterId); + System.out.println(" - [cluster id: " + inputClusterId + "] " + cluster); + + if (cluster != null) { + for (ZclAttribute attr : cluster.getAttributes()) { + System.out.println(" - [attr id: " + attr.getId() + "] " + attr); + } + } + } + + System.out.println(" - Output Clusters:"); + + for (int outputClusterId : endpoint.getOutputClusterIds()) { + ZclCluster cluster = endpoint.getInputCluster(outputClusterId); + System.out.println(" - [cluster id: " + outputClusterId + "] " + endpoint); + + if (cluster != null) { + for (ZclAttribute attr : cluster.getAttributes()) { + System.out.println(" - [attr id: " + attr.getId() + "] " + attr); + } + } + } } System.out.println(" Number of Endpoints: " + node.getEndpoints().size()); + System.out.println(); } + System.out.println("--------------------------"); System.out.println("Number of ZigBee Nodes: " + networkManager.getNodes().size()); + System.out.println("-----------------------------------------------------------------------"); break; - case 'p': + case "a": + ZigBeeNode node = networkManager.getNode(in.nextInt()); + ZigBeeEndpoint endpoint = node.getEndpoint(in.nextInt()); + System.out.println(" - [id: " + endpoint.getDeviceId() + "]" + endpoint); + break; + + case "p": System.out.println("Enabling pairing."); networkManager.permitJoin(200); break; - case 'q': + case "q": System.out.println("Shutting down."); break; - case 'h': + case "h": default: System.out.println("Available commands:"); System.out.println(" i: List network info"); System.out.println(" l: List available ZigBee Nodes"); + System.out.println(" a : read attribute"); System.out.println(" p: Enable pairing of ZigBee devices"); System.out.println(" q: Quit"); System.out.println(" h: Help text"); break; } } - - private static char waitForInout() throws IOException { - System.out.print(""); - System.out.print("Input command and finish with ENTER: "); - - while (true) { - char input=(char)System.in.read(); - if (input != '\n') - return input; - } - - } } \ No newline at end of file