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.io.ByteArrayInputStream;
019import java.net.Inet4Address;
020import java.net.UnknownHostException;
021import java.util.ArrayList;
022import java.util.List;
023
024import com.digi.xbee.api.connection.IConnectionInterface;
025import com.digi.xbee.api.connection.serial.SerialPortParameters;
026import com.digi.xbee.api.exceptions.InterfaceNotOpenException;
027import com.digi.xbee.api.exceptions.InvalidOperatingModeException;
028import com.digi.xbee.api.exceptions.TimeoutException;
029import com.digi.xbee.api.exceptions.XBeeDeviceException;
030import com.digi.xbee.api.exceptions.XBeeException;
031import com.digi.xbee.api.listeners.IPacketReceiveListener;
032import com.digi.xbee.api.models.ATCommandStatus;
033import com.digi.xbee.api.models.AccessPoint;
034import com.digi.xbee.api.models.IPAddressingMode;
035import com.digi.xbee.api.models.WiFiAssociationIndicationStatus;
036import com.digi.xbee.api.models.WiFiEncryptionType;
037import com.digi.xbee.api.models.XBeeProtocol;
038import com.digi.xbee.api.packet.APIFrameType;
039import com.digi.xbee.api.packet.XBeeAPIPacket;
040import com.digi.xbee.api.packet.XBeePacket;
041import com.digi.xbee.api.packet.common.ATCommandPacket;
042import com.digi.xbee.api.packet.common.ATCommandResponsePacket;
043import com.digi.xbee.api.utils.ByteUtils;
044
045/**
046 * This class represents a local Wi-Fi device.
047 * 
048 * @see CellularDevice
049 * @see DigiPointDevice
050 * @see DigiMeshDevice
051 * @see Raw802Device
052 * @see ThreadDevice
053 * @see XBeeDevice
054 * @see ZigBeeDevice
055 * 
056 * @since 1.2.0
057 */
058public class WiFiDevice extends IPDevice {
059        
060        // Constants.
061        private final static int DEFAULT_ACCESS_POINT_TIMETOUT = 15000; // 15 seconds of timeout to connect, disconnect and scan access points.
062        
063        private static final String AS_COMMAND = "AS";
064        private static final String ERROR_ALREADY_CONNECTED = "Device is already connected to an access point.";
065        
066        private static final int DISCOVER_TIMEOUT = 30000;
067        
068        // Variables.
069        private boolean scanningAccessPoints = false;
070        private boolean scanningAccessPointsError = false;
071        
072        protected int accessPointTimeout = DEFAULT_ACCESS_POINT_TIMETOUT;
073        
074        /**
075         * Class constructor. Instantiates a new {@code WiFiDevice} object in 
076         * the given port name and baud rate.
077         * 
078         * @param port Serial port name where Wi-Fi device is attached to.
079         * @param baudRate Serial port baud rate to communicate with the device. 
080         *                 Other connection parameters will be set as default (8 
081         *                 data bits, 1 stop bit, no parity, no flow control).
082         * 
083         * @throws IllegalArgumentException if {@code baudRate < 0}.
084         * @throws NullPointerException if {@code port == null}.
085         * 
086         * @see #WiFiDevice(IConnectionInterface)
087         * @see #WiFiDevice(String, SerialPortParameters)
088         * @see #WiFiDevice(String, int, int, int, int, int)
089         */
090        public WiFiDevice(String port, int baudRate) {
091                this(XBee.createConnectiontionInterface(port, baudRate));
092        }
093        
094        /**
095         * Class constructor. Instantiates a new {@code WiFiDevice} object in 
096         * the given serial port name and settings.
097         * 
098         * @param port Serial port name where Wi-Fi device is attached to.
099         * @param baudRate Serial port baud rate to communicate with the device.
100         * @param dataBits Serial port data bits.
101         * @param stopBits Serial port data bits.
102         * @param parity Serial port data bits.
103         * @param flowControl Serial port data bits.
104         * 
105         * @throws IllegalArgumentException if {@code baudRate < 0} or
106         *                                  if {@code dataBits < 0} or
107         *                                  if {@code stopBits < 0} or
108         *                                  if {@code parity < 0} or
109         *                                  if {@code flowControl < 0}.
110         * @throws NullPointerException if {@code port == null}.
111         * 
112         * @see #WiFiDevice(IConnectionInterface)
113         * @see #WiFiDevice(String, int)
114         * @see #WiFiDevice(String, SerialPortParameters)
115         */
116        public WiFiDevice(String port, int baudRate, int dataBits, int stopBits, int parity, int flowControl) {
117                this(port, new SerialPortParameters(baudRate, dataBits, stopBits, parity, flowControl));
118        }
119        
120        /**
121         * Class constructor. Instantiates a new {@code WiFiDevice} object in 
122         * the given serial port name and parameters.
123         * 
124         * @param port Serial port name where Wi-Fi device is attached to.
125         * @param serialPortParameters Object containing the serial port parameters.
126         * 
127         * @throws NullPointerException if {@code port == null} or
128         *                              if {@code serialPortParameters == null}.
129         * 
130         * @see #WiFiDevice(IConnectionInterface)
131         * @see #WiFiDevice(String, int)
132         * @see #WiFiDevice(String, int, int, int, int, int)
133         * @see com.digi.xbee.api.connection.serial.SerialPortParameters
134         */
135        public WiFiDevice(String port, SerialPortParameters serialPortParameters) {
136                this(XBee.createConnectiontionInterface(port, serialPortParameters));
137        }
138        
139        /**
140         * Class constructor. Instantiates a new {@code WiFiDevice} object with 
141         * the given connection interface.
142         * 
143         * @param connectionInterface The connection interface with the physical 
144         *                            Wi-Fi device.
145         * 
146         * @throws NullPointerException if {@code connectionInterface == null}
147         * 
148         * @see #WiFiDevice(String, int)
149         * @see #WiFiDevice(String, SerialPortParameters)
150         * @see #WiFiDevice(String, int, int, int, int, int)
151         * @see com.digi.xbee.api.connection.IConnectionInterface
152         */
153        public WiFiDevice(IConnectionInterface connectionInterface) {
154                super(connectionInterface);
155        }
156        
157        /*
158         * (non-Javadoc)
159         * @see com.digi.xbee.api.XBeeDevice#open()
160         */
161        @Override
162        public void open() throws XBeeException {
163                super.open();
164                if (xbeeProtocol != XBeeProtocol.XBEE_WIFI)
165                        throw new XBeeDeviceException("XBee device is not a " + getXBeeProtocol().getDescription() + " device, it is a " + xbeeProtocol.getDescription() + " device.");
166        }
167        
168        /*
169         * (non-Javadoc)
170         * @see com.digi.xbee.api.XBeeDevice#getXBeeProtocol()
171         */
172        @Override
173        public XBeeProtocol getXBeeProtocol() {
174                return XBeeProtocol.XBEE_WIFI;
175        }
176        
177        /**
178         * Returns the current association status of this Wi-Fi device.
179         * 
180         * <p>It indicates occurrences of errors during the Wi-Fi transceiver 
181         * initialization and connection.</p>
182         * 
183         * @return The association indication status of the Wi-Fi device.
184         * 
185         * @throws InterfaceNotOpenException if this device connection is not open.
186         * @throws TimeoutException if there is a timeout getting the association 
187         *                          indication status.
188         * @throws XBeeException if there is any other XBee related exception.
189         * 
190         * @see com.digi.xbee.api.models.WiFiAssociationIndicationStatus
191         */
192        public WiFiAssociationIndicationStatus getWiFiAssociationIndicationStatus() throws TimeoutException, 
193                        XBeeException {
194                byte[] associationIndicationValue = getParameter("AI");
195                return WiFiAssociationIndicationStatus.get(ByteUtils.byteArrayToInt(associationIndicationValue));
196        }
197        
198        /**
199         * Finds and reports the access point that matches the supplied SSID.
200         * 
201         * <p>This method blocks until the access point is discovered or the 
202         * configured access point timeout expires.</p>
203         * 
204         * <p>The access point timeout is configured using the 
205         * {@code setAccessPointTimeout} method and can be consulted with 
206         * {@code getAccessPointTimeout} method.</p>
207         * 
208         * @param ssid The SSID of the access point to discover.
209         * 
210         * @return The discovered access point with the given SSID, {@code null} 
211         *         if the timeout expires and the access point was not found.
212         * 
213         * @throws InterfaceNotOpenException if this device connection is not open.
214         * @throws NullPointerException if {@code ssid == null}.
215         * @throws TimeoutException if there is a timeout getting the access point.
216         * @throws XBeeException if there is an error sending the discovery command.
217         * 
218         * @see #getAccessPointTimeout()
219         * @see #scanAccessPoints()
220         * @see #setAccessPointTimeout(int)
221         */
222        public AccessPoint getAccessPoint(String ssid) throws XBeeException {
223                if (ssid == null)
224                        throw new NullPointerException("SSID cannot be null.");
225                
226                logger.debug("{}AS for '{}' access point.", toString(), ssid);
227                
228                List<AccessPoint> accessPointsList = scanAccessPoints();
229                
230                for (AccessPoint accessPoint:accessPointsList) {
231                        if (accessPoint.getSSID().equals(ssid))
232                                return accessPoint;
233                }
234                
235                return null;
236        }
237        
238        /**
239         * Performs a scan to search for access points in the vicinity.
240         * 
241         * <p>This method blocks until all the access points are discovered or the 
242         * configured access point timeout expires.</p>
243         * 
244         * <p>The access point timeout is configured using the 
245         * {@code setAccessPointTimeout} method and can be consulted with 
246         * {@code getAccessPointTimeout} method.</p>
247         * 
248         * @return The list of access points discovered.
249         * 
250         * @throws InterfaceNotOpenException if this device connection is not open.
251         * @throws TimeoutException if there is a timeout scanning the access points.
252         * @throws XBeeException if there is any other XBee related exception.
253         * 
254         * @see #getAccessPoint(String)
255         * @see #getAccessPointTimeout()
256         * @see #setAccessPointTimeout(int)
257         */
258        public List<AccessPoint> scanAccessPoints() throws XBeeException {
259                // Check if the connection is open.
260                if (!isOpen())
261                        throw new InterfaceNotOpenException();
262                
263                final ArrayList<AccessPoint> accessPointsList = new ArrayList<AccessPoint>();
264                
265                switch (getOperatingMode()) {
266                case AT:
267                case UNKNOWN:
268                default:
269                        throw new InvalidOperatingModeException(operatingMode);
270                case API:
271                case API_ESCAPE:
272                        // Subscribe listener to wait for active scan responses.
273                        IPacketReceiveListener packetReceiveListener = new IPacketReceiveListener() {
274                                /*
275                                 * (non-Javadoc)
276                                 * @see com.digi.xbee.api.listeners.IPacketReceiveListener#packetReceived(com.digi.xbee.api.packet.XBeePacket)
277                                 */
278                                @Override
279                                public void packetReceived(XBeePacket receivedPacket) {
280                                        if (!scanningAccessPoints)
281                                                return;
282                                        
283                                        try {
284                                                if (((XBeeAPIPacket)receivedPacket).getFrameType() != APIFrameType.AT_COMMAND_RESPONSE)
285                                                        return;
286                                                ATCommandResponsePacket response = (ATCommandResponsePacket)receivedPacket;
287                                                if (!response.getCommand().equals(AS_COMMAND))
288                                                        return;
289                                                
290                                                // Check for error.
291                                                if (response.getStatus() == ATCommandStatus.ERROR) {
292                                                        scanningAccessPointsError = true;
293                                                        scanningAccessPoints = false;
294                                                // Check for end of discovery.
295                                                } else if (response.getCommandValue() == null 
296                                                                || response.getCommandValue().length == 0)
297                                                        scanningAccessPoints = false;
298                                                else {
299                                                        AccessPoint accessPoint = parseDiscoveredAccessPoint(response.getCommandValue());
300                                                        if (accessPoint != null)
301                                                                accessPointsList.add(accessPoint);
302                                                }
303                                        } catch (ClassCastException e) {
304                                                // Do nothing here.
305                                        }
306                                }
307                        };
308                        
309                        logger.debug("{}Start scanning access points.", toString());
310                        addPacketListener(packetReceiveListener);
311                        
312                        try {
313                                scanningAccessPoints = true;
314                                // Send the active scan command.
315                                sendPacketAsync(new ATCommandPacket(getNextFrameID(), AS_COMMAND, ""));
316                                
317                                // Wait until the discovery process finishes or timeouts.
318                                long deadLine = System.currentTimeMillis() + DISCOVER_TIMEOUT;
319                                while (scanningAccessPoints && System.currentTimeMillis() < deadLine)
320                                        sleep(100);
321                                
322                                // Check if we exited because of a timeout.
323                                if (scanningAccessPoints)
324                                        throw new TimeoutException();
325                                // Check if there was an error in the active scan command (device is already connected).
326                                if (scanningAccessPointsError)
327                                        throw new XBeeException(ERROR_ALREADY_CONNECTED);
328                        } finally {
329                                scanningAccessPoints = false;
330                                scanningAccessPointsError = false;
331                                removePacketListener(packetReceiveListener);
332                                logger.debug("{}Stop scanning access points.", toString());
333                        }
334                }
335                
336                return accessPointsList;
337        }
338        
339        /**
340         * Connects to the access point with provided SSID.
341         * 
342         * <p>This method blocks until the connection with the access point is 
343         * established or the configured access point timeout expires.</p>
344         * 
345         * <p>The access point timeout is configured using the 
346         * {@code setAccessPointTimeout} method and can be consulted with 
347         * {@code getAccessPointTimeout} method.</p>
348         * 
349         * <p>Once the module is connected to the access point, you can issue 
350         * the {@link #writeChanges()} method to save the connection settings. This 
351         * way the module will try to connect to the access point every time it 
352         * is powered on.</p>
353         * 
354         * @param ssid The SSID of the access point to connect to.
355         * @param password The password for the access point, {@code null} if it 
356         *                 does not have any encryption enabled.
357         * 
358         * @return {@code true} if the module connected to the access point 
359         *         successfully, {@code false} otherwise.
360         * 
361         * @throws InterfaceNotOpenException if this device connection is not open.
362         * @throws NullPointerException if {@code ssid == null}.
363         * @throws TimeoutException if there is a timeout sending the connect 
364         *                          commands.
365         * @throws XBeeException if the SSID provided is {@code null} or if there 
366         *                       is any other XBee related exception.
367         * 
368         * @see #connect(AccessPoint, String)
369         * @see #disconnect()
370         * @see #getAccessPoint(String)
371         * @see #getAccessPointTimeout()
372         * @see #scanAccessPoints()
373         * @see #setAccessPointTimeout(int)
374         */
375        public boolean connect(String ssid, String password) 
376                        throws TimeoutException, XBeeException {
377                if (ssid == null)
378                        throw new NullPointerException("SSID cannot be null.");
379                
380                AccessPoint accessPoint = getAccessPoint(ssid);
381                if (accessPoint == null)
382                        throw new XBeeException("Couldn't find any access point with the proviced SSID.");
383                
384                return connect(accessPoint, password);
385        }
386        
387        /**
388         * Connects to the provided access point.
389         * 
390         * <p>This method blocks until the connection with the access point is 
391         * established or the configured access point timeout expires.</p>
392         * 
393         * <p>The access point timeout is configured using the 
394         * {@code setAccessPointTimeout} method and can be consulted with 
395         * {@code getAccessPointTimeout} method.</p>
396         * 
397         * <p>Once the module is connected to the access point, you can issue 
398         * the {@code writeSettings} method to save the connection settings. This 
399         * way the module will try to connect to the access point every time it 
400         * is powered on.</p>
401         * 
402         * @param accessPoint The access point to connect to.
403         * @param password The password for the access point, {@code null} if it 
404         *                 does not have any encryption enabled.
405         * 
406         * @return {@code true} if the module connected to the access point 
407         *         successfully, {@code false} otherwise.
408         * 
409         * @throws InterfaceNotOpenException if this device connection is not open.
410         * @throws NullPointerException if {@code accessPoint == null}.
411         * @throws TimeoutException if there is a timeout sending the connect 
412         *                          commands.
413         * @throws XBeeException if there is any other XBee related exception.
414         * 
415         * @see #connect(String, String)
416         * @see #disconnect()
417         * @see #getAccessPoint(String)
418         * @see #getAccessPointTimeout()
419         * @see #scanAccessPoints()
420         * @see #setAccessPointTimeout(int)
421         * @see com.digi.xbee.api.models.AccessPoint
422         */
423        public boolean connect(AccessPoint accessPoint, String password) 
424                        throws TimeoutException, XBeeException {
425                if (accessPoint == null)
426                        throw new NullPointerException("Access point cannot be null.");
427                
428                // Set connection parameters.
429                setParameter("ID", accessPoint.getSSID().getBytes());
430                setParameter("EE", new byte[]{(byte)accessPoint.getEncryptionType().getID()});
431                if (password != null && accessPoint.getEncryptionType() != WiFiEncryptionType.NONE)
432                        setParameter("PK", password.getBytes());
433                
434                // Wait for the module to connect to the access point.
435                long deadLine = System.currentTimeMillis() + accessPointTimeout;
436                while (System.currentTimeMillis() < deadLine) {
437                        sleep(100);
438                        // Get the association indication value of the module.
439                        byte[] status = getParameter("AI");
440                        if (status == null || status.length < 1)
441                                continue;
442                        if (status[0] == 0)
443                                return true;
444                }
445                
446                return false;
447        }
448        
449        /**
450         * Disconnects from the access point the device is connected to.
451         * 
452         * <p>This method blocks until the device disconnects totally from the 
453         * access point or the configured access point timeout expires.</p>
454         * 
455         * <p>The access point timeout is configured using the 
456         * {@code setAccessPointTimeout} method and can be consulted with 
457         * {@code getAccessPointTimeout} method.</p>
458         * 
459         * @return {@code true} if the module disconnected from the access point 
460         *         successfully, {@code false} otherwise.
461         * 
462         * @throws InterfaceNotOpenException if this device connection is not open.
463         * @throws TimeoutException if there is a timeout sending the disconnect 
464         *                          command.
465         * @throws XBeeException if there is any other XBee related exception.
466         * 
467         * @see #connect(AccessPoint, String)
468         * @see #connect(String, String)
469         * @see #getAccessPointTimeout()
470         * @see #setAccessPointTimeout(int)
471         */
472        public boolean disconnect() throws TimeoutException, XBeeException {
473                executeParameter("NR");
474                // Wait for the module to connect to the access point.
475                long deadLine = System.currentTimeMillis() + accessPointTimeout;
476                while (System.currentTimeMillis() < deadLine) {
477                        sleep(100);
478                        // Get the association indication value of the module.
479                        byte[] status = getParameter("AI");
480                        if (status == null || status.length < 1)
481                                continue;
482                        // Status 0x23 (35) means the SSID is not configured.
483                        if (status[0] == 0x23)
484                                return true;
485                }
486                
487                return false;
488        }
489        
490        /**
491         * Returns whether the device is connected to an access point or not.
492         * 
493         * @return {@code true} if the device is connected to an access point, 
494         *         {@code false} otherwise.
495         * 
496         * @throws InterfaceNotOpenException if this device connection is not open.
497         * @throws TimeoutException if there is a timeout getting the association 
498         *                          indication status.
499         * @throws XBeeException if there is any other XBee related exception.
500         * 
501         * @see #getWiFiAssociationIndicationStatus()
502         * @see com.digi.xbee.api.models.WiFiAssociationIndicationStatus
503         */
504        public boolean isConnected() throws TimeoutException, XBeeException {
505                WiFiAssociationIndicationStatus status = getWiFiAssociationIndicationStatus();
506                
507                return status == WiFiAssociationIndicationStatus.SUCCESSFULLY_JOINED;
508        }
509        
510        /**
511         * Parses the given active scan API data and returns an {@code AccessPoint} 
512         * object.
513         * 
514         * @param data Data to parse.
515         * 
516         * @return Discovered access point. {@code null} if the parsed data does
517         *         not correspond to an access point.
518         */
519        private AccessPoint parseDiscoveredAccessPoint(byte[] data) {
520                AccessPoint accessPoint = null;
521                
522                int version;
523                int channel;
524                int security;
525                int signalStrength;
526                int signalQuality;
527                
528                String ssidName = "";
529                
530                ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
531                if (inputStream.available() == 0)
532                        return null;
533                // Read the version.
534                version = ByteUtils.byteArrayToInt((ByteUtils.readBytes(1, inputStream)));
535                if (inputStream.available() == 0)
536                        return null;
537                // Read the channel.
538                channel = ByteUtils.byteArrayToInt((ByteUtils.readBytes(1, inputStream)));
539                if (inputStream.available() == 0)
540                        return null;
541                // Read the encryption type.
542                security = ByteUtils.byteArrayToInt((ByteUtils.readBytes(1, inputStream)));
543                if (inputStream.available() == 0)
544                        return null;
545                // Read the signal strength and generate the signal quality.
546                signalStrength = ByteUtils.byteArrayToInt((ByteUtils.readBytes(1, inputStream)));
547                signalQuality = getSignalQuality(version, signalStrength);
548                
549                // Read the SSID name.
550                if (inputStream.available() == 0)
551                        return null;
552                int readByte = -1;
553                while ((readByte = inputStream.read()) != -1)
554                        ssidName += new String(new byte[]{(byte)(readByte & 0xFF)});
555                
556                // Build the access point object.
557                accessPoint = new AccessPoint(ssidName, WiFiEncryptionType.get(security), channel, signalQuality);
558                
559                logger.debug("{}Discovered: SSID[{}], encryption[{}], channel[{}], signal quality[{}].",
560                                toString(), ssidName, WiFiEncryptionType.get(security).name(), channel, signalQuality);
561                
562                return accessPoint;
563        }
564        
565        /**
566         * Converts the signal strength value in signal quality (%) based on the 
567         * provided Wi-Fi version.
568         * 
569         * @param wifiVersion Wi-Fi protocol version of the Wi-Fi XBee device.
570         * @param signalStrength Signal strength value to convert to %.
571         * 
572         * @return The signal quality in %.
573         */
574        private int getSignalQuality(int wifiVersion, int signalStrength) {
575                int quality;
576                
577                if (wifiVersion == 1) {
578                        if (signalStrength <= -100)
579                                quality = 0;
580                        else if(signalStrength >= -50)
581                                quality = 100;
582                        else
583                                quality = (2 * (signalStrength + 100)); 
584                } else
585                        quality = 2 * signalStrength;
586                
587                if (quality > 100)
588                        quality = 100;
589                if (quality < 0)
590                        quality = 0;
591                
592                return quality;
593        }
594        
595        /**
596         * Sleeps the thread for the given number of milliseconds.
597         * 
598         * @param milliseconds The number of milliseconds that the thread should 
599         *        be sleeping.
600         */
601        private void sleep(int milliseconds) {
602                try {
603                        Thread.sleep(milliseconds);
604                } catch (InterruptedException e) { }
605        }
606        
607        /**
608         * Returns the configured access point timeout for connecting, 
609         * disconnecting and scanning access points.
610         * 
611         * @return The current access point timeout in milliseconds.
612         * 
613         * @see #setAccessPointTimeout(int)
614         */
615        public int getAccessPointTimeout() {
616                return accessPointTimeout;
617        }
618        
619        /**
620         * Configures the access point timeout in milliseconds for connecting, 
621         * disconnecting and scanning access points.
622         *  
623         * @param accessPointTimeout The new access point timeout in milliseconds.
624         * 
625         * @throws IllegalArgumentException if {@code accessPointTimeout < 0}.
626         * 
627         * @see #getAccessPointTimeout()
628         */
629        public void setAccessPointTimeout(int accessPointTimeout) {
630                if (accessPointTimeout < 0)
631                        throw new IllegalArgumentException("Access point timeout cannot be less than 0.");
632                
633                this.accessPointTimeout = accessPointTimeout;
634        }
635        
636        /**
637         * Sets the IP addressing mode.
638         * 
639         * @param mode IP addressing mode.
640         * 
641         * @throws NullPointerException if {@code mode == null}.
642         * @throws TimeoutException if there is a timeout setting the IP addressing
643         *                          mode.
644         * @throws XBeeException if there is any other XBee related exception.
645         * 
646         * @see #getIPAddressingMode()
647         * @see IPAddressingMode
648         */
649        public void setIPAddressingMode(IPAddressingMode mode) throws TimeoutException, XBeeException {
650                if (mode == null)
651                        throw new NullPointerException("IP addressing mode cannot be null.");
652                
653                setParameter("MA", ByteUtils.intToByteArray(mode.getID()));
654        }
655        
656        /**
657         * Returns the IP addressing mode.
658         * 
659         * @return The configured IP addressing mode.
660         * 
661         * @throws TimeoutException if there is a timeout reading the IP addressing
662         *                          mode.
663         * @throws XBeeException if there is any other XBee related exception.
664         * 
665         * @see #setIPAddressingMode(IPAddressingMode)
666         * @see IPAddressingMode
667         */
668        public IPAddressingMode getIPAddressingMode() throws TimeoutException, XBeeException {
669                return IPAddressingMode.get(ByteUtils.byteArrayToInt(getParameter("MA")));
670        }
671        
672        /**
673         * Sets the IP address of the module.
674         * 
675         * <p>This method <b>can only be called</b> if the module is configured in
676         * {@link IPAddressingMode#STATIC} mode. Otherwise an {@code XBeeException}
677         * will be thrown.</p>
678         * 
679         * @param address IP address.
680         * 
681         * @throws NullPointerException if {@code address == null}.
682         * @throws TimeoutException if there is a timeout setting the IP address.
683         * @throws XBeeException if the module is in {@link IPAddressingMode#DHCP}
684         *                       mode or there is any other XBee related exception.
685         * 
686         * @see #getIPAddress()
687         * @see #getIPAddressingMode()
688         * @see Inet4Address
689         */
690        public void setIPAddress(Inet4Address address) throws TimeoutException, XBeeException {
691                if (address == null)
692                        throw new NullPointerException("IP address cannot be null.");
693                
694                setParameter("MY", address.getAddress());
695        }
696        
697        /**
698         * Sets the IP address subnet mask.
699         * 
700         * <p>This method <b>can only be called</b> if the module is configured in
701         * {@link IPAddressingMode#STATIC} mode. Otherwise an {@code XBeeException}
702         * will be thrown.</p>
703         * 
704         * @param address IP address subnet mask.
705         * 
706         * @throws NullPointerException if {@code address == null}.
707         * @throws TimeoutException if there is a timeout setting the IP address
708         *                          mask.
709         * @throws XBeeException if the module is in {@link IPAddressingMode#DHCP}
710         *                       mode or there is any other XBee related exception.
711         * 
712         * @see #getIPAddressingMode()
713         * @see #getIPAddressMask()
714         * @see Inet4Address
715         */
716        public void setIPAddressMask(Inet4Address address) throws TimeoutException, XBeeException {
717                if (address == null)
718                        throw new NullPointerException("Address mask cannot be null.");
719                
720                setParameter("MK", address.getAddress());
721        }
722        
723        /**
724         * Returns the IP address subnet mask.
725         * 
726         * @return The configured IP address subnet mask.
727         * 
728         * @throws TimeoutException if there is a timeout reading the IP address
729         *                          mask.
730         * @throws XBeeException if there is any other XBee related exception.
731         * 
732         * @see #setIPAddressMask(Inet4Address)
733         * @see Inet4Address
734         */
735        public Inet4Address getIPAddressMask() throws TimeoutException, XBeeException {
736                try {
737                        return (Inet4Address) Inet4Address.getByAddress(getParameter("MK"));
738                } catch (UnknownHostException e) {
739                        throw new XBeeException(e);
740                }
741        }
742        
743        /**
744         * Sets the IP address of the gateway.
745         * 
746         * <p>This method <b>can only be called</b> if the module is configured in
747         * {@link IPAddressingMode#STATIC} mode. Otherwise an {@code XBeeException}
748         * will be thrown.</p>
749         * 
750         * @param address IP address of the gateway.
751         * 
752         * @throws NullPointerException if {@code address == null}.
753         * @throws TimeoutException if there is a timeout setting the gateway IP
754         *                          address.
755         * @throws XBeeException if the module is in {@link IPAddressingMode#DHCP}
756         *                       mode or there is any other XBee related exception.
757         * 
758         * @see #getGatewayIPAddress()
759         * @see #getIPAddressingMode()
760         * @see Inet4Address
761         */
762        public void setGatewayIPAddress(Inet4Address address) throws TimeoutException, XBeeException {
763                if (address == null)
764                        throw new NullPointerException("Gateway address cannot be null.");
765                
766                setParameter("GW", address.getAddress());
767        }
768        
769        /**
770         * Returns the IP address of the gateway.
771         * 
772         * @return The configured IP address of the gateway.
773         * 
774         * @throws TimeoutException if there is a timeout reading the gateway IP
775         *                          address.
776         * @throws XBeeException if there is any other XBee related exception.
777         * 
778         * @see #setGatewayIPAddress(Inet4Address)
779         * @see Inet4Address
780         */
781        public Inet4Address getGatewayIPAddress() throws TimeoutException, XBeeException {
782                try {
783                        return (Inet4Address) Inet4Address.getByAddress(getParameter("GW"));
784                } catch (UnknownHostException e) {
785                        throw new XBeeException(e);
786                }
787        }
788        
789        /**
790         * Sets the IP address of domain name server.
791         * 
792         * @param address DNS address.
793         * 
794         * @throws NullPointerException if {@code address == null}.
795         * @throws TimeoutException if there is a timeout setting the DNS address.
796         * @throws XBeeException if there is any other XBee related exception.
797         * 
798         * @see #getDNSAddress()
799         * @see Inet4Address
800         */
801        public void setDNSAddress(Inet4Address address) throws TimeoutException, XBeeException {
802                if (address == null)
803                        throw new NullPointerException("DNS address cannot be null.");
804                
805                setParameter("NS", address.getAddress());
806        }
807        
808        /**
809         * Returns the IP address of domain name server.
810         * 
811         * @return The configured DNS address.
812         * 
813         * @throws TimeoutException if there is a timeout reading the DNS address.
814         * @throws XBeeException if there is any other XBee related exception.
815         * 
816         * @see #setDNSAddress(Inet4Address)
817         * @see Inet4Address
818         */
819        public Inet4Address getDNSAddress() throws TimeoutException, XBeeException {
820                try {
821                        return (Inet4Address) Inet4Address.getByAddress(getParameter("NS"));
822                } catch (UnknownHostException e) {
823                        throw new XBeeException(e);
824                }
825        }
826
827}