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.util.Set;
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.io.IOLine;
028import com.digi.xbee.api.listeners.IIOSampleReceiveListener;
029import com.digi.xbee.api.listeners.ISMSReceiveListener;
030import com.digi.xbee.api.models.AssociationIndicationStatus;
031import com.digi.xbee.api.models.CellularAssociationIndicationStatus;
032import com.digi.xbee.api.models.PowerLevel;
033import com.digi.xbee.api.models.XBee64BitAddress;
034import com.digi.xbee.api.models.XBeeIMEIAddress;
035import com.digi.xbee.api.models.XBeeProtocol;
036import com.digi.xbee.api.packet.XBeePacket;
037import com.digi.xbee.api.packet.cellular.TXSMSPacket;
038import com.digi.xbee.api.utils.ByteUtils;
039
040/**
041 * This class represents a local Cellular device.
042 * 
043 * @see DigiPointDevice
044 * @see DigiMeshDevice
045 * @see Raw802Device
046 * @see ThreadDevice
047 * @see WiFiDevice
048 * @see XBeeDevice
049 * @see ZigBeeDevice
050 * 
051 * @since 1.2.0
052 */
053public class CellularDevice extends IPDevice {
054
055        // Constants
056        private static final String OPERATION_EXCEPTION = "Operation not supported in Cellular protocol.";
057        
058        // Variables
059        private XBeeIMEIAddress imeiAddress;
060        
061        /**
062         * Class constructor. Instantiates a new {@code CellularDevice} object in 
063         * the given port name and baud rate.
064         * 
065         * @param port Serial port name where Cellular device is attached to.
066         * @param baudRate Serial port baud rate to communicate with the device. 
067         *                 Other connection parameters will be set as default (8 
068         *                 data bits, 1 stop bit, no parity, no flow control).
069         * 
070         * @throws IllegalArgumentException if {@code baudRate < 0}.
071         * @throws NullPointerException if {@code port == null}.
072         * 
073         * @see #CellularDevice(IConnectionInterface)
074         * @see #CellularDevice(String, SerialPortParameters)
075         * @see #CellularDevice(String, int, int, int, int, int)
076         */
077        public CellularDevice(String port, int baudRate) {
078                this(XBee.createConnectiontionInterface(port, baudRate));
079        }
080        
081        /**
082         * Class constructor. Instantiates a new {@code CellularDevice} object in 
083         * the given serial port name and settings.
084         * 
085         * @param port Serial port name where Cellular device is attached to.
086         * @param baudRate Serial port baud rate to communicate with the device.
087         * @param dataBits Serial port data bits.
088         * @param stopBits Serial port data bits.
089         * @param parity Serial port data bits.
090         * @param flowControl Serial port data bits.
091         * 
092         * @throws IllegalArgumentException if {@code baudRate < 0} or
093         *                                  if {@code dataBits < 0} or
094         *                                  if {@code stopBits < 0} or
095         *                                  if {@code parity < 0} or
096         *                                  if {@code flowControl < 0}.
097         * @throws NullPointerException if {@code port == null}.
098         * 
099         * @see #CellularDevice(IConnectionInterface)
100         * @see #CellularDevice(String, int)
101         * @see #CellularDevice(String, SerialPortParameters)
102         */
103        public CellularDevice(String port, int baudRate, int dataBits, int stopBits, int parity, int flowControl) {
104                this(port, new SerialPortParameters(baudRate, dataBits, stopBits, parity, flowControl));
105        }
106        
107        /**
108         * Class constructor. Instantiates a new {@code CellularDevice} object in 
109         * the given serial port name and parameters.
110         * 
111         * @param port Serial port name where Cellular device is attached to.
112         * @param serialPortParameters Object containing the serial port parameters.
113         * 
114         * @throws NullPointerException if {@code port == null} or
115         *                              if {@code serialPortParameters == null}.
116         * 
117         * @see #CellularDevice(IConnectionInterface)
118         * @see #CellularDevice(String, int)
119         * @see #CellularDevice(String, int, int, int, int, int)
120         * @see com.digi.xbee.api.connection.serial.SerialPortParameters
121         */
122        public CellularDevice(String port, SerialPortParameters serialPortParameters) {
123                this(XBee.createConnectiontionInterface(port, serialPortParameters));
124        }
125
126        /**
127         * Class constructor. Instantiates a new {@code CellularDevice} object with 
128         * the given connection interface.
129         * 
130         * @param connectionInterface The connection interface with the physical 
131         *                            Cellular device.
132         * 
133         * @throws NullPointerException if {@code connectionInterface == null}
134         * 
135         * @see #CellularDevice(String, int)
136         * @see #CellularDevice(String, SerialPortParameters)
137         * @see #CellularDevice(String, int, int, int, int, int)
138         * @see com.digi.xbee.api.connection.IConnectionInterface
139         */
140        public CellularDevice(IConnectionInterface connectionInterface) {
141                super(connectionInterface);
142        }
143        
144        /*
145         * (non-Javadoc)
146         * @see com.digi.xbee.api.XBeeDevice#open()
147         */
148        @Override
149        public void open() throws XBeeException {
150                super.open();
151                if (xbeeProtocol != XBeeProtocol.CELLULAR)
152                        throw new XBeeDeviceException("XBee device is not a " + getXBeeProtocol().getDescription() + " device, it is a " + xbeeProtocol.getDescription() + " device.");
153        }
154        
155        /*
156         * (non-Javadoc)
157         * @see com.digi.xbee.api.XBeeDevice#getXBeeProtocol()
158         */
159        @Override
160        public XBeeProtocol getXBeeProtocol() {
161                return XBeeProtocol.CELLULAR;
162        }
163        
164        /*
165         * (non-Javadoc)
166         * @see com.digi.xbee.api.AbstractXBeeDevice#readDeviceInfo()
167         */
168        @Override
169        public void readDeviceInfo() throws TimeoutException, XBeeException {
170                super.readDeviceInfo();
171                
172                // Generate the IMEI address.
173                imeiAddress = new XBeeIMEIAddress(xbee64BitAddress.getValue());
174        }
175        
176        /**
177         * Returns whether the device is connected to the Internet or not.
178         * 
179         * @return {@code true} if the device is connected to the Internet, 
180         *         {@code false} otherwise.
181         * 
182         * @throws InterfaceNotOpenException if this device connection is not open.
183         * @throws TimeoutException if there is a timeout getting the association 
184         *                          indication status.
185         * @throws XBeeException if there is any other XBee related exception.
186         * 
187         * @see #getCellularAssociationIndicationStatus()
188         * @see com.digi.xbee.api.models.CellularAssociationIndicationStatus
189         */
190        public boolean isConnected() throws TimeoutException, XBeeException {
191                CellularAssociationIndicationStatus status = getCellularAssociationIndicationStatus();
192                return status == CellularAssociationIndicationStatus.SUCCESSFULLY_CONNECTED;
193        }
194        
195        /**
196         * Returns the IMEI address of this Cellular device.
197         * 
198         * <p>To refresh this value use the {@link #readDeviceInfo()} method.</p>
199         * 
200         * @return The IMEI address of this Cellular device.
201         * 
202         * @see com.digi.xbee.api.models.XBeeIMEIAddress
203         */
204        public XBeeIMEIAddress getIMEIAddress() {
205                return imeiAddress;
206        }
207        
208        /**
209         * Returns the current association status of this Cellular device.
210         * 
211         * <p>It indicates occurrences of errors during the modem initialization 
212         * and connection.</p>
213         * 
214         * @return The association indication status of the Cellular device.
215         * 
216         * @throws InterfaceNotOpenException if this device connection is not open.
217         * @throws TimeoutException if there is a timeout getting the association 
218         *                          indication status.
219         * @throws XBeeException if there is any other XBee related exception.
220         * 
221         * @see com.digi.xbee.api.models.CellularAssociationIndicationStatus
222         */
223        public CellularAssociationIndicationStatus getCellularAssociationIndicationStatus() throws TimeoutException, 
224                        XBeeException {
225                byte[] associationIndicationValue = getParameter("AI");
226                return CellularAssociationIndicationStatus.get(ByteUtils.byteArrayToInt(associationIndicationValue));
227        }
228        
229        /**
230         * @deprecated Operation not supported in Cellular protocol. Use
231         *             {@link #getCellularAssociationIndicationStatus()} instead.
232         *             This method will raise an 
233         *             {@link UnsupportedOperationException}.
234         */
235        @Override
236        protected AssociationIndicationStatus getAssociationIndicationStatus()
237                        throws TimeoutException, XBeeException {
238                // Not supported in Cellular.
239                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
240        }
241        
242        /**
243         * @deprecated Cellular protocol does not have an associated 64-bit address.
244         */
245        @Override
246        public XBee64BitAddress get64BitAddress() {
247                // Cellular protocol does not have 64-bit address.
248                return null;
249        }
250        
251        /**
252         * @deprecated Operation not supported in this protocol. This method will
253         *             raise an {@link UnsupportedOperationException}.
254         */
255        @Override
256        public void addIOSampleListener(IIOSampleReceiveListener listener) {
257                // Not supported in IP modules.
258                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
259        }
260        
261        /**
262         * @deprecated Operation not supported in this protocol. This method will
263         *             raise an {@link UnsupportedOperationException}.
264         */
265        @Override
266        public void removeIOSampleListener(IIOSampleReceiveListener listener) {
267                // Not supported in IP modules.
268                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
269        }
270        
271        /**
272         * @deprecated Operation not supported in Cellular protocol. This method
273         *             will raise an {@link UnsupportedOperationException}.
274         */
275        @Override
276        public Set<IOLine> getDIOChangeDetection() throws TimeoutException,
277                        XBeeException {
278                // Not supported in Cellular.
279                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
280        }
281        
282        /**
283         * @deprecated Operation not supported in Cellular protocol. This method
284         *             will raise an {@link UnsupportedOperationException}.
285         */
286        @Override
287        public void setDIOChangeDetection(Set<IOLine> lines)
288                        throws TimeoutException, XBeeException {
289                // Not supported in Cellular.
290                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
291        }
292        
293        /**
294         * @deprecated Operation not supported in Cellular protocol. This method
295         *             will raise an {@link UnsupportedOperationException}.
296         */
297        @Override
298        public int getIOSamplingRate() throws TimeoutException, XBeeException {
299                // Not supported in Cellular.
300                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
301        }
302        
303        /**
304         * @deprecated Operation not supported in Cellular protocol. This method
305         *             will raise an {@link UnsupportedOperationException}.
306         */
307        @Override
308        public void setIOSamplingRate(int rate) throws TimeoutException,
309                        XBeeException {
310                // Not supported in Cellular.
311                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
312        }
313        
314        /**
315         * @deprecated Cellular protocol does not have node identifier.
316         */
317        @Override
318        public String getNodeID() {
319                // Cellular protocol does not have Node Identifier.
320                return null;
321        }
322        
323        /**
324         * @deprecated Operation not supported in Cellular protocol. This method
325         *             will raise an {@link UnsupportedOperationException}.
326         */
327        @Override
328        public void setNodeID(String nodeID) throws TimeoutException, XBeeException {
329                // Not supported in Cellular.
330                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
331        }
332        
333        /**
334         * @deprecated Operation not supported in Cellular protocol. This method
335         *             will raise an {@link UnsupportedOperationException}.
336         */
337        @Override
338        public PowerLevel getPowerLevel() throws TimeoutException, XBeeException {
339                // Not supported in Cellular.
340                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
341        }
342        
343        /**
344         * @deprecated Operation not supported in Cellular protocol. This method
345         *             will raise an {@link UnsupportedOperationException}.
346         */
347        @Override
348        public void setPowerLevel(PowerLevel powerLevel) throws TimeoutException,
349                        XBeeException {
350                // Not supported in Cellular.
351                throw new UnsupportedOperationException(OPERATION_EXCEPTION);
352        }
353        
354        /*
355         * (non-Javadoc)
356         * @see com.digi.xbee.api.AbstractXBeeDevice#addSMSListener(com.digi.xbee.api.listeners.ISMSReceiveListener)
357         */
358        @Override
359        public void addSMSListener(ISMSReceiveListener listener) {
360                super.addSMSListener(listener);
361        }
362        
363        /*
364         * (non-Javadoc)
365         * @see com.digi.xbee.api.AbstractXBeeDevice#removeSMSListener(com.digi.xbee.api.listeners.ISMSReceiveListener)
366         */
367        @Override
368        public void removeSMSListener(ISMSReceiveListener listener) {
369                super.removeSMSListener(listener);
370        }
371        
372        /**
373         * Sends the provided SMS message to the given phone number.
374         * 
375         * <p>This method blocks till a success or error response arrives or the 
376         * configured receive timeout expires.</p>
377         * 
378         * <p>The receive timeout is configured using the {@code setReceiveTimeout}
379         * method and can be consulted with {@code getReceiveTimeout} method.</p>
380         * 
381         * <p>For non-blocking operations use the method 
382         * {@link #sendSMSAsync(String, String)}.</p>
383         * 
384         * @param phoneNumber The phone number to send the SMS to.
385         * @param data String containing the text of the SMS.
386         * 
387         * @throws IllegalArgumentException if {@code phoneNumber.length() > 20} or
388         *                                  if {@code phoneNumber} is invalid.
389         * @throws InterfaceNotOpenException if this device connection is not open.
390         * @throws NullPointerException if {@code phoneNumber == null} or 
391         *                              if {@code data == null}.
392         * @throws TimeoutException if there is a timeout sending the SMS.
393         * @throws XBeeException if there is any other XBee related exception.
394         * 
395         * @see #getReceiveTimeout()
396         * @see #setReceiveTimeout(int)
397         * @see #sendSMSAsync(String, String)
398         */
399        public void sendSMS(String phoneNumber, String data) throws TimeoutException, XBeeException {
400                if (phoneNumber == null)
401                        throw new NullPointerException("Phone number cannot be null");
402                if (data == null)
403                        throw new NullPointerException("Data cannot be null");
404                
405                // Check if device is remote.
406                if (isRemote())
407                        throw new OperationNotSupportedException("Cannot send SMS from a remote device.");
408                
409                logger.debug(toString() + "Sending SMS to {} >> {}.", phoneNumber, data);
410                
411                XBeePacket xbeePacket = new TXSMSPacket(getNextFrameID(), phoneNumber, data);
412                
413                sendAndCheckXBeePacket(xbeePacket, false);
414        }
415        
416        /**
417         * Sends asynchronously the provided SMS to the given phone number.
418         * 
419         * <p>Asynchronous transmissions do not wait for answer or for transmit 
420         * status packet.</p>
421         * 
422         * @param phoneNumber The phone number to send the SMS to.
423         * @param data String containing the text of the SMS.
424         * 
425         * @throws IllegalArgumentException if {@code phoneNumber.length() > 20} or
426         *                                  if {@code phoneNumber} is invalid.
427         * @throws InterfaceNotOpenException if this device connection is not open.
428         * @throws NullPointerException if {@code phoneNumber == null} or 
429         *                              if {@code data == null}.
430         * @throws TimeoutException if there is a timeout sending the SMS.
431         * @throws XBeeException if there is any other XBee related exception.
432         * 
433         * @see #sendSMS(String, String)
434         */
435        public void sendSMSAsync(String phoneNumber, String data) throws TimeoutException, XBeeException {
436                if (phoneNumber == null)
437                        throw new NullPointerException("Phone number cannot be null");
438                if (data == null)
439                        throw new NullPointerException("Data cannot be null");
440                
441                // Check if device is remote.
442                if (isRemote())
443                        throw new OperationNotSupportedException("Cannot send SMS from a remote device.");
444                
445                logger.debug(toString() + "Sending SMS asynchronously to {} >> {}.", phoneNumber, data);
446                
447                XBeePacket xbeePacket = new TXSMSPacket(getNextFrameID(), phoneNumber, data);
448                
449                sendAndCheckXBeePacket(xbeePacket, true);
450        }
451}