001/*
002 * Copyright 2017-2019, Digi International Inc.
003 *
004 * This Source Code Form is subject to the terms of the Mozilla Public
005 * License, v. 2.0. If a copy of the MPL was not distributed with this
006 * file, you can obtain one at http://mozilla.org/MPL/2.0/.
007 *
008 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 
009 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 
010 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 
011 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 
012 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
013 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
014 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
015 */
016package com.digi.xbee.api;
017
018import java.net.Inet6Address;
019
020import com.digi.xbee.api.connection.IConnectionInterface;
021import com.digi.xbee.api.connection.serial.SerialPortParameters;
022import com.digi.xbee.api.exceptions.InterfaceNotOpenException;
023import com.digi.xbee.api.exceptions.OperationNotSupportedException;
024import com.digi.xbee.api.exceptions.TimeoutException;
025import com.digi.xbee.api.exceptions.XBeeDeviceException;
026import com.digi.xbee.api.exceptions.XBeeException;
027import com.digi.xbee.api.listeners.IExplicitDataReceiveListener;
028import com.digi.xbee.api.models.APIOutputMode;
029import com.digi.xbee.api.models.AssociationIndicationStatus;
030import com.digi.xbee.api.models.ExplicitXBeeMessage;
031import com.digi.xbee.api.models.XBee16BitAddress;
032import com.digi.xbee.api.models.XBee64BitAddress;
033import com.digi.xbee.api.models.XBeeProtocol;
034import com.digi.xbee.api.models.XBeeTransmitOptions;
035import com.digi.xbee.api.packet.XBeePacket;
036import com.digi.xbee.api.packet.common.ExplicitAddressingPacket;
037import com.digi.xbee.api.utils.HexUtils;
038
039/**
040 * This class represents a local ZigBee device.
041 * 
042 * @see CellularDevice
043 * @see DigiPointDevice
044 * @see DigiMeshDevice
045 * @see Raw802Device
046 * @see ThreadDevice
047 * @see WiFiDevice
048 * @see XBeeDevice
049 */
050public class ZigBeeDevice extends XBeeDevice {
051
052        // Constants
053        private static final String OPERATION_EXCEPTION = "Operation not supported in ZigBee protocol.";
054        
055        /**
056         * Class constructor. Instantiates a new {@code ZigBeeDevice} object in the 
057         * given port name and baud rate.
058         * 
059         * @param port Serial port name where ZigBee device is attached to.
060         * @param baudRate Serial port baud rate to communicate with the device. 
061         *                 Other connection parameters will be set as default (8 
062         *                 data bits, 1 stop bit, no parity, no flow control).
063         * 
064         * @throws IllegalArgumentException if {@code baudRate < 0}.
065         * @throws NullPointerException if {@code port == null}.
066         * 
067         * @see #ZigBeeDevice(IConnectionInterface)
068         * @see #ZigBeeDevice(String, SerialPortParameters)
069         * @see #ZigBeeDevice(String, int, int, int, int, int)
070         */
071        public ZigBeeDevice(String port, int baudRate) {
072                this(XBee.createConnectiontionInterface(port, baudRate));
073        }
074        
075        /**
076         * Class constructor. Instantiates a new {@code ZigBeeDevice} object in the 
077         * given serial port name and settings.
078         * 
079         * @param port Serial port name where ZigBee device is attached to.
080         * @param baudRate Serial port baud rate to communicate with the device.
081         * @param dataBits Serial port data bits.
082         * @param stopBits Serial port data bits.
083         * @param parity Serial port data bits.
084         * @param flowControl Serial port data bits.
085         * 
086         * @throws IllegalArgumentException if {@code baudRate < 0} or
087         *                                  if {@code dataBits < 0} or
088         *                                  if {@code stopBits < 0} or
089         *                                  if {@code parity < 0} or
090         *                                  if {@code flowControl < 0}.
091         * @throws NullPointerException if {@code port == null}.
092         * 
093         * @see #ZigBeeDevice(IConnectionInterface)
094         * @see #ZigBeeDevice(String, int)
095         * @see #ZigBeeDevice(String, SerialPortParameters)
096         */
097        public ZigBeeDevice(String port, int baudRate, int dataBits, int stopBits, int parity, int flowControl) {
098                this(port, new SerialPortParameters(baudRate, dataBits, stopBits, parity, flowControl));
099        }
100        
101        /**
102         * Class constructor. Instantiates a new {@code ZigBeeDevice} object in the 
103         * given serial port name and parameters.
104         * 
105         * @param port Serial port name where ZigBee device is attached to.
106         * @param serialPortParameters Object containing the serial port parameters.
107         * 
108         * @throws NullPointerException if {@code port == null} or
109         *                              if {@code serialPortParameters == null}.
110         * 
111         * @see #ZigBeeDevice(IConnectionInterface)
112         * @see #ZigBeeDevice(String, int)
113         * @see #ZigBeeDevice(String, int, int, int, int, int)
114         * @see com.digi.xbee.api.connection.serial.SerialPortParameters
115         */
116        public ZigBeeDevice(String port, SerialPortParameters serialPortParameters) {
117                this(XBee.createConnectiontionInterface(port, serialPortParameters));
118        }
119        
120        /**
121         * Class constructor. Instantiates a new {@code ZigBeeDevice} object with the 
122         * given connection interface.
123         * 
124         * @param connectionInterface The connection interface with the physical 
125         *                            ZigBee device.
126         * 
127         * @throws NullPointerException if {@code connectionInterface == null}
128         * 
129         * @see #ZigBeeDevice(IConnectionInterface)
130         * @see #ZigBeeDevice(String, int)
131         * @see #ZigBeeDevice(String, SerialPortParameters)
132         * @see #ZigBeeDevice(String, int, int, int, int, int)
133         * @see com.digi.xbee.api.connection.IConnectionInterface
134         */
135        public ZigBeeDevice(IConnectionInterface connectionInterface) {
136                super(connectionInterface);
137        }
138        
139        /*
140         * (non-Javadoc)
141         * @see com.digi.xbee.api.XBeeDevice#open()
142         */
143        @Override
144        public void open() throws XBeeException {
145                super.open();
146                if (xbeeProtocol != XBeeProtocol.ZIGBEE)
147                        throw new XBeeDeviceException("XBee device is not a " + getXBeeProtocol().getDescription() + " device, it is a " + xbeeProtocol.getDescription() + " device.");
148        }
149        
150        /*
151         * (non-Javadoc)
152         * @see com.digi.xbee.api.XBeeDevice#getNetwork()
153         */
154        @Override
155        public XBeeNetwork getNetwork() {
156                if (!isOpen())
157                        throw new InterfaceNotOpenException();
158                if (network == null)
159                        network = new ZigBeeNetwork(this);
160                return network;
161        }
162        
163        /*
164         * (non-Javadoc)
165         * @see com.digi.xbee.api.XBeeDevice#getXBeeProtocol()
166         */
167        @Override
168        public XBeeProtocol getXBeeProtocol() {
169                return XBeeProtocol.ZIGBEE;
170        }
171        
172        /*
173         * (non-Javadoc)
174         * @see com.digi.xbee.api.XBeeDevice#sendDataAsync(com.digi.xbee.api.models.XBee64BitAddress, com.digi.xbee.api.models.XBee16BitAddress, byte[])
175         */
176        @Override
177        public void sendDataAsync(XBee64BitAddress address64Bit, XBee16BitAddress address16bit, byte[] data) throws XBeeException {
178                super.sendDataAsync(address64Bit, address16bit, data);
179        }
180        
181        /*
182         * (non-Javadoc)
183         * @see com.digi.xbee.api.XBeeDevice#sendData(com.digi.xbee.api.models.XBee64BitAddress, com.digi.xbee.api.models.XBee16BitAddress, byte[])
184         */
185        @Override
186        public void sendData(XBee64BitAddress address64Bit, XBee16BitAddress address16bit, byte[] data) throws TimeoutException, XBeeException {
187                super.sendData(address64Bit, address16bit, data);
188        }
189        
190        /*
191         * (non-Javadoc)
192         * @see com.digi.xbee.api.AbstractXBeeDevice#getAssociationIndicationStatus()
193         */
194        @Override
195        public AssociationIndicationStatus getAssociationIndicationStatus() throws TimeoutException, XBeeException {
196                return super.getAssociationIndicationStatus();
197        }
198        
199        /*
200         * (non-Javadoc)
201         * @see com.digi.xbee.api.AbstractXBeeDevice#forceDisassociate()
202         */
203        @Override
204        public void forceDisassociate() throws TimeoutException, XBeeException {
205                super.forceDisassociate();
206        }
207        
208        /*
209         * (non-Javadoc)
210         * @see com.digi.xbee.api.XBeeDevice#readExplicitData()
211         */
212        @Override
213        public ExplicitXBeeMessage readExplicitData() {
214                return super.readExplicitData();
215        }
216        
217        /*
218         * (non-Javadoc)
219         * @see com.digi.xbee.api.XBeeDevice#readExplicitData(int)
220         */
221        @Override
222        public ExplicitXBeeMessage readExplicitData(int timeout) {
223                return super.readExplicitData(timeout);
224        }
225        
226        /*
227         * (non-Javadoc)
228         * @see com.digi.xbee.api.XBeeDevice#readExplicitDataFrom(com.digi.xbee.api.RemoteXBeeDevice)
229         */
230        @Override
231        public ExplicitXBeeMessage readExplicitDataFrom(RemoteXBeeDevice remoteXBeeDevice) {
232                return super.readExplicitDataFrom(remoteXBeeDevice);
233        }
234        
235        /*
236         * (non-Javadoc)
237         * @see com.digi.xbee.api.XBeeDevice#readExplicitDataFrom(com.digi.xbee.api.RemoteXBeeDevice, int)
238         */
239        @Override
240        public ExplicitXBeeMessage readExplicitDataFrom(RemoteXBeeDevice remoteXBeeDevice, int timeout) {
241                return super.readExplicitDataFrom(remoteXBeeDevice, timeout);
242        }
243        
244        /*
245         * (non-Javadoc)
246         * @see com.digi.xbee.api.AbstractXBeeDevice#addExplicitDataListener(com.digi.xbee.api.listeners.IExplicitDataReceiveListener)
247         */
248        @Override
249        public void addExplicitDataListener(IExplicitDataReceiveListener listener) {
250                super.addExplicitDataListener(listener);
251        }
252        
253        /*
254         * (non-Javadoc)
255         * @see com.digi.xbee.api.AbstractXBeeDevice#removeExplicitDataListener(com.digi.xbee.api.listeners.IExplicitDataReceiveListener)
256         */
257        @Override
258        public void removeExplicitDataListener(IExplicitDataReceiveListener listener) {
259                super.removeExplicitDataListener(listener);
260        }
261        
262        /*
263         * (non-Javadoc)
264         * @see com.digi.xbee.api.XBeeDevice#getAPIOutputMode()
265         */
266        @Override
267        public APIOutputMode getAPIOutputMode() throws TimeoutException, XBeeException {
268                return super.getAPIOutputMode();
269        }
270        
271        /*
272         * (non-Javadoc)
273         * @see com.digi.xbee.api.XBeeDevice#setAPIOutputMode(com.digi.xbee.api.models.APIOutputMode)
274         */
275        @Override
276        public void setAPIOutputMode(APIOutputMode apiOutputMode) throws TimeoutException, XBeeException {
277                super.setAPIOutputMode(apiOutputMode);
278        }
279        
280        /*
281         * (non-Javadoc)
282         * @see com.digi.xbee.api.XBeeDevice#sendExplicitData(com.digi.xbee.api.RemoteXBeeDevice, int, int, int, int, byte[])
283         */
284        @Override
285        public void sendExplicitData(RemoteXBeeDevice remoteXBeeDevice, int sourceEndpoint, int destEndpoint, int clusterID,
286                        int profileID, byte[] data) throws TimeoutException, XBeeException {
287                super.sendExplicitData(remoteXBeeDevice, sourceEndpoint, destEndpoint, clusterID, profileID, data);
288        }
289        
290        /*
291         * (non-Javadoc)
292         * @see com.digi.xbee.api.XBeeDevice#sendExplicitData(com.digi.xbee.api.models.XBee64BitAddress, com.digi.xbee.api.models.XBee16BitAddress, int, int, int, int, byte[])
293         */
294        @Override
295        public void sendExplicitData(XBee64BitAddress address64Bit, XBee16BitAddress address16bit, int sourceEndpoint, int destEndpoint, 
296                        int clusterID, int profileID, byte[] data) throws TimeoutException, XBeeException {
297                super.sendExplicitData(address64Bit, address16bit, sourceEndpoint, destEndpoint, clusterID, profileID, data);
298        }
299        
300        /*
301         * (non-Javadoc)
302         * @see com.digi.xbee.api.XBeeDevice#sendBroadcastExplicitData(int, int, int, int, byte[])
303         */
304        @Override
305        public void sendBroadcastExplicitData(int sourceEndpoint, int destEndpoint, int clusterID, int profileID, 
306                        byte[] data) throws TimeoutException, XBeeException {
307                super.sendBroadcastExplicitData(sourceEndpoint, destEndpoint, clusterID, profileID, data);
308        }
309        
310        /*
311         * (non-Javadoc)
312         * @see com.digi.xbee.api.XBeeDevice#sendExplicitDataAsync(com.digi.xbee.api.RemoteXBeeDevice, int, int, int, int, byte[])
313         */
314        @Override
315        public void sendExplicitDataAsync(RemoteXBeeDevice xbeeDevice, int sourceEndpoint, int destEndpoint, int clusterID,
316                        int profileID, byte[] data) throws XBeeException {
317                super.sendExplicitDataAsync(xbeeDevice, sourceEndpoint, destEndpoint, clusterID, profileID, data);
318        }
319        
320        /*
321         * (non-Javadoc)
322         * @see com.digi.xbee.api.XBeeDevice#sendExplicitDataAsync(com.digi.xbee.api.models.XBee64BitAddress, com.digi.xbee.api.models.XBee16BitAddress, int, int, int, int, byte[])
323         */
324        @Override
325        public void sendExplicitDataAsync(XBee64BitAddress address64Bit, XBee16BitAddress address16Bit, int sourceEndpoint,
326                        int destEndpoint, int clusterID, int profileID, byte[] data) throws XBeeException {
327                super.sendExplicitDataAsync(address64Bit, address16Bit, sourceEndpoint, destEndpoint, clusterID, profileID, data);
328        }
329        
330        /**
331         * Sends a multicast transmission with the provided data to the given 
332         * group ID.
333         * 
334         * <p>This method blocks till a success or error response arrives or the 
335         * configured receive timeout expires.</p>
336         * 
337         * <p>The receive timeout is configured using the setReceiveTimeout method 
338         * and can be consulted with getReceiveTimeout method.</p>
339         * 
340         * @param groupID 16-bit address of the destination group ID.
341         * @param sourceEndpoint Source endpoint for the transmission.
342         * @param destEndpoint Destination endpoint for the transmission.
343         * @param clusterID Cluster ID used in the transmission.
344         * @param profileID Profile ID used in the transmission.
345         * @param data Byte array containing the data to be sent.
346         * 
347         * @throws IllegalArgumentException if {@code sourceEndpoint < 0} or 
348         *                                  if {@code sourceEndpoint > 0xFF} or 
349         *                                  if {@code destEndpoint < 0} or 
350         *                                  if {@code destEndpoint > 0xFF} or 
351         *                                  if {@code clusterID < 0} or 
352         *                                  if {@code clusterID > 0xFFFF} or 
353         *                                  if {@code profileID < 0} or 
354         *                                  if {@code profileID > 0xFFFF}.
355         * @throws InterfaceNotOpenException if this device connection is not open.
356         * @throws NullPointerException if {@code groupID == null} or 
357         *                              if {@code data == null}.
358         * @throws TimeoutException if there is a timeout sending the data.
359         * @throws XBeeException if there is any other XBee related exception.
360         * 
361         * @see #sendMulticastDataAsync(XBee16BitAddress, int, int, int, int, byte[])
362         * @see #getReceiveTimeout()
363         * @see #setReceiveTimeout(int)
364         * @see com.digi.xbee.api.models.XBee16BitAddress
365         */
366        public void sendMulticastData(XBee16BitAddress groupID, int sourceEndpoint, int destEndpoint, int clusterID,
367                        int profileID, byte[] data) throws XBeeException {
368                // Verify the parameters are not null, if they are null, throw an exception.
369                if (groupID == null)
370                        throw new NullPointerException("Destination group ID cannot be null");
371                if (data == null)
372                        throw new NullPointerException("Data cannot be null.");
373                if (sourceEndpoint < 0 || sourceEndpoint > 0xFF)
374                        throw new IllegalArgumentException("Source endpoint must be between 0 and 0xFF.");
375                if (destEndpoint < 0 || destEndpoint > 0xFF)
376                        throw new IllegalArgumentException("Destination endpoint must be between 0 and 0xFF.");
377                if (clusterID < 0 || clusterID > 0xFFFF)
378                        throw new IllegalArgumentException("Cluster ID must be between 0 and 0xFFFF.");
379                if (profileID < 0 || profileID > 0xFFFF)
380                        throw new IllegalArgumentException("Profile ID must be between 0 and 0xFFFF.");
381                
382                // Check if device is remote.
383                if (isRemote())
384                        throw new OperationNotSupportedException("Cannot send multicast data to a remote device from a remote device.");
385                
386                logger.debug(toString() + "Sending multicast data to {} [{} - {} - {} - {}] >> {}.", groupID, 
387                                HexUtils.integerToHexString(sourceEndpoint, 1), HexUtils.integerToHexString(destEndpoint, 1), 
388                                HexUtils.integerToHexString(clusterID, 2), HexUtils.integerToHexString(profileID, 2), 
389                                HexUtils.prettyHexString(data));
390                
391                XBeePacket xbeePacket = new ExplicitAddressingPacket(getNextFrameID(), XBee64BitAddress.UNKNOWN_ADDRESS, 
392                                groupID, sourceEndpoint, destEndpoint, clusterID, profileID, 0, XBeeTransmitOptions.ENABLE_MULTICAST, data);
393                sendAndCheckXBeePacket(xbeePacket, false);
394        }
395        
396        /**
397         * Sends a multicast asynchronous transmission with the provided data to 
398         * the given group ID.
399         * 
400         * <p>Asynchronous transmissions do not wait for answer from the remote 
401         * device or for transmit status packet.</p>
402         * 
403         * @param groupID 16-bit address of the destination group ID.
404         * @param sourceEndpoint Source endpoint for the transmission.
405         * @param destEndpoint Destination endpoint for the transmission.
406         * @param clusterID Cluster ID used in the transmission.
407         * @param profileID Profile ID used in the transmission.
408         * @param data Byte array containing the data to be sent.
409         * 
410         * @throws IllegalArgumentException if {@code sourceEndpoint < 0} or 
411         *                                  if {@code sourceEndpoint > 0xFF} or 
412         *                                  if {@code destEndpoint < 0} or 
413         *                                  if {@code destEndpoint > 0xFF} or 
414         *                                  if {@code clusterID < 0} or 
415         *                                  if {@code clusterID > 0xFFFF} or 
416         *                                  if {@code profileID < 0} or 
417         *                                  if {@code profileID > 0xFFFF}.
418         * @throws InterfaceNotOpenException if this device connection is not open.
419         * @throws NullPointerException if {@code groupID == null} or 
420         *                              if {@code data == null}.
421         * @throws XBeeException if there is any other XBee related exception.
422         * 
423         * @see #sendMulticastData(XBee16BitAddress, int, int, int, int, byte[])
424         * @see com.digi.xbee.api.models.XBee64BitAddress
425         */
426        public void sendMulticastDataAsync(XBee16BitAddress groupID, int sourceEndpoint, int destEndpoint, int clusterID,
427                        int profileID, byte[] data) throws XBeeException {
428                // Verify the parameters are not null, if they are null, throw an exception.
429                if (groupID == null)
430                        throw new NullPointerException("Destination group ID cannot be null");
431                if (data == null)
432                        throw new NullPointerException("Data cannot be null.");
433                if (sourceEndpoint < 0 || sourceEndpoint > 0xFF)
434                        throw new IllegalArgumentException("Source endpoint must be between 0 and 0xFF.");
435                if (destEndpoint < 0 || destEndpoint > 0xFF)
436                        throw new IllegalArgumentException("Destination endpoint must be between 0 and 0xFF.");
437                if (clusterID < 0 || clusterID > 0xFFFF)
438                        throw new IllegalArgumentException("Cluster ID must be between 0 and 0xFFFF.");
439                if (profileID < 0 || profileID > 0xFFFF)
440                        throw new IllegalArgumentException("Profile ID must be between 0 and 0xFFFF.");
441                
442                // Check if device is remote.
443                if (isRemote())
444                        throw new OperationNotSupportedException("Cannot send multicast data to a remote device from a remote device.");
445                
446                logger.debug(toString() + "Sending multicast data asynchronously to {} [{} - {} - {} - {}] >> {}.", groupID, 
447                                HexUtils.integerToHexString(sourceEndpoint, 1), HexUtils.integerToHexString(destEndpoint, 1), 
448                                HexUtils.integerToHexString(clusterID, 2), HexUtils.integerToHexString(profileID, 2), 
449                                HexUtils.prettyHexString(data));
450                
451                XBeePacket xbeePacket = new ExplicitAddressingPacket(getNextFrameID(), XBee64BitAddress.UNKNOWN_ADDRESS, 
452                                groupID, sourceEndpoint, destEndpoint, clusterID, profileID, 0, XBeeTransmitOptions.ENABLE_MULTICAST, data);
453                sendAndCheckXBeePacket(xbeePacket, true);
454        }
455        
456        /**
457         * @deprecated ZigBee protocol does not have an associated IPv6 address.
458         */
459        @Override
460        public Inet6Address getIPv6Address() {
461                // ZigBee protocol does not have IPv6 address.
462                return null;
463        }
464        
465        /**
466         * @deprecated Operation not supported in ZigBee protocol. This method
467         *             will raise an {@link UnsupportedOperationException}.
468         */
469        @Override
470        public Inet6Address getIPv6DestinationAddress()
471                        throws TimeoutException, XBeeException {
472                // Not supported in ZigBee.
473                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
474        }
475        
476        /**
477         * @deprecated Operation not supported in ZigBee protocol. This method
478         *             will raise an {@link UnsupportedOperationException}.
479         */
480        @Override
481        public void setIPv6DestinationAddress(Inet6Address ipv6Address)
482                        throws TimeoutException, XBeeException {
483                // Not supported in ZigBee.
484                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
485        }
486}