]> git.basschouten.com Git - openhab-addons.git/blob
33f2768e287f45c0c228b424c430809a095681e3
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.irobot.internal.dto;
14
15 import java.io.IOException;
16 import java.io.StringReader;
17 import java.net.DatagramPacket;
18 import java.net.DatagramSocket;
19 import java.net.InetAddress;
20 import java.nio.charset.StandardCharsets;
21
22 import com.google.gson.Gson;
23 import com.google.gson.JsonParseException;
24 import com.google.gson.stream.JsonReader;
25
26 /**
27  * iRobot discovery and identification protocol
28  *
29  * @author Pavel Fedin - Initial contribution
30  *
31  */
32 public class IdentProtocol {
33     private static final String UDP_PACKET_CONTENTS = "irobotmcs";
34     private static final int REMOTE_UDP_PORT = 5678;
35     private static final Gson gson = new Gson();
36
37     public static DatagramSocket sendRequest(InetAddress host) throws IOException {
38         DatagramSocket socket = new DatagramSocket();
39
40         socket.setBroadcast(true);
41         socket.setReuseAddress(true);
42
43         byte[] packetContents = UDP_PACKET_CONTENTS.getBytes(StandardCharsets.UTF_8);
44         DatagramPacket packet = new DatagramPacket(packetContents, packetContents.length, host, REMOTE_UDP_PORT);
45
46         socket.send(packet);
47         return socket;
48     }
49
50     public static DatagramPacket receiveResponse(DatagramSocket socket) throws IOException {
51         byte[] buffer = new byte[1024];
52         DatagramPacket incomingPacket = new DatagramPacket(buffer, buffer.length);
53
54         socket.setSoTimeout(1000 /* one second */);
55         socket.receive(incomingPacket);
56
57         return incomingPacket;
58     }
59
60     public static IdentData decodeResponse(DatagramPacket packet) throws JsonParseException {
61         /*
62          * packet is a JSON of the following contents (addresses are undisclosed):
63          * @formatter:off
64          * {
65          *   "ver":"3",
66          *   "hostname":"Roomba-3168820480607740",
67          *   "robotname":"Roomba",
68          *   "ip":"XXX.XXX.XXX.XXX",
69          *   "mac":"XX:XX:XX:XX:XX:XX",
70          *   "sw":"v2.4.6-3",
71          *   "sku":"R981040",
72          *   "nc":0,
73          *   "proto":"mqtt",
74          *   "cap":{
75          *     "pose":1,
76          *     "ota":2,
77          *     "multiPass":2,
78          *     "carpetBoost":1,
79          *     "pp":1,
80          *     "binFullDetect":1,
81          *     "langOta":1,
82          *     "maps":1,
83          *     "edge":1,
84          *     "eco":1,
85          *     "svcConf":1
86          *   }
87          * }
88          * @formatter:on
89          */
90         String reply = new String(packet.getData(), StandardCharsets.UTF_8);
91         // We are not consuming all the fields, so we have to create the reader explicitly
92         // If we use fromJson(String) or fromJson(java.util.reader), it will throw
93         // "JSON not fully consumed" exception, because not all the reader's content has been
94         // used up. We want to avoid that for compatibility reasons because newer iRobot versions
95         // may add fields.
96         JsonReader jsonReader = new JsonReader(new StringReader(reply));
97         IdentData data = gson.fromJson(jsonReader, IdentData.class);
98
99         data.postParse();
100         return data;
101     }
102
103     public static class IdentData {
104         public static int MIN_SUPPORTED_VERSION = 2;
105         public static String PRODUCT_ROOMBA = "Roomba";
106
107         public int ver;
108         private String hostname;
109         public String robotname;
110
111         // These two fields are synthetic, they are not contained in JSON
112         public String product;
113         public String blid;
114
115         public void postParse() {
116             // Synthesize missing properties.
117             String[] hostparts = hostname.split("-");
118
119             // This also comes from Roomba980-Python. Comments there say that "iRobot"
120             // prefix is used by i7. We assume for other robots it would be product
121             // name, e. g. "Scooba"
122             if (hostparts[0].equals("iRobot")) {
123                 product = "Roomba";
124             } else {
125                 product = hostparts[0];
126             }
127
128             blid = hostparts[1];
129         }
130     }
131 }