]> git.basschouten.com Git - openhab-addons.git/blob
66dc1e69bdd8179620b75325a735658591a0e277
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.gree.internal.handler;
14
15 import static org.openhab.binding.gree.internal.GreeBindingConstants.*;
16
17 import java.io.IOException;
18 import java.net.DatagramPacket;
19 import java.net.DatagramSocket;
20 import java.net.InetAddress;
21 import java.nio.charset.StandardCharsets;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Objects;
29 import java.util.Optional;
30
31 import org.eclipse.jdt.annotation.NonNullByDefault;
32 import org.openhab.binding.gree.internal.GreeCryptoUtil;
33 import org.openhab.binding.gree.internal.GreeException;
34 import org.openhab.binding.gree.internal.gson.GreeBindRequestPackDTO;
35 import org.openhab.binding.gree.internal.gson.GreeBindResponseDTO;
36 import org.openhab.binding.gree.internal.gson.GreeBindResponsePackDTO;
37 import org.openhab.binding.gree.internal.gson.GreeExecResponseDTO;
38 import org.openhab.binding.gree.internal.gson.GreeExecResponsePackDTO;
39 import org.openhab.binding.gree.internal.gson.GreeExecuteCommandPackDTO;
40 import org.openhab.binding.gree.internal.gson.GreeReqStatusPackDTO;
41 import org.openhab.binding.gree.internal.gson.GreeRequestDTO;
42 import org.openhab.binding.gree.internal.gson.GreeScanResponseDTO;
43 import org.openhab.binding.gree.internal.gson.GreeStatusResponseDTO;
44 import org.openhab.binding.gree.internal.gson.GreeStatusResponsePackDTO;
45 import org.openhab.core.library.types.QuantityType;
46 import org.openhab.core.library.unit.SIUnits;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 import com.google.gson.Gson;
51 import com.google.gson.JsonSyntaxException;
52
53 /**
54  * The GreeDevice object repesents a Gree Airconditioner and provides
55  * device specific attributes as well a the functionality for the Air Conditioner
56  *
57  * @author John Cunha - Initial contribution
58  * @author Markus Michels - Refactoring, adapted to OH 2.5x
59  */
60 @NonNullByDefault
61 public class GreeAirDevice {
62     private final Logger logger = LoggerFactory.getLogger(GreeAirDevice.class);
63     private static final Gson GSON = new Gson();
64     private boolean isBound = false;
65     private final InetAddress ipAddress;
66     private int port = 0;
67     private String encKey = "";
68     private Optional<GreeScanResponseDTO> scanResponseGson = Optional.empty();
69     private Optional<GreeStatusResponseDTO> statusResponseGson = Optional.empty();
70     private Optional<GreeStatusResponsePackDTO> prevStatusResponsePackGson = Optional.empty();
71
72     public GreeAirDevice() {
73         ipAddress = InetAddress.getLoopbackAddress();
74     }
75
76     public GreeAirDevice(InetAddress ipAddress, int port, GreeScanResponseDTO scanResponse) {
77         this.ipAddress = ipAddress;
78         this.port = port;
79         this.scanResponseGson = Optional.of(scanResponse);
80     }
81
82     public void getDeviceStatus(DatagramSocket clientSocket) throws GreeException {
83         if (!isBound) {
84             throw new GreeException("Device not bound");
85         }
86         try {
87             // Set the values in the HashMap
88             ArrayList<String> columns = new ArrayList<>();
89             columns.add(GREE_PROP_POWER);
90             columns.add(GREE_PROP_MODE);
91             columns.add(GREE_PROP_SETTEMP);
92             columns.add(GREE_PROP_WINDSPEED);
93             columns.add(GREE_PROP_AIR);
94             columns.add(GREE_PROP_DRY);
95             columns.add(GREE_PROP_HEALTH);
96             columns.add(GREE_PROP_SLEEP);
97             columns.add(GREE_PROP_LIGHT);
98             columns.add(GREE_PROP_SWINGLEFTRIGHT);
99             columns.add(GREE_PROP_SWINGUPDOWN);
100             columns.add(GREE_PROP_QUIET);
101             columns.add(GREE_PROP_TURBO);
102             columns.add(GREE_PROP_TEMPUNIT);
103             columns.add(GREE_PROP_HEAT);
104             columns.add(GREE_PROP_HEATCOOL);
105             columns.add(GREE_PROP_TEMPREC);
106             columns.add(GREE_PROP_PWR_SAVING);
107             columns.add(GREE_PROP_NOISESET);
108             columns.add(GREE_PROP_CURRENT_TEMP_SENSOR);
109
110             // Convert the parameter map values to arrays
111             String[] colArray = columns.toArray(new String[0]);
112
113             // Prep the Command Request pack
114             GreeReqStatusPackDTO reqStatusPackGson = new GreeReqStatusPackDTO();
115             reqStatusPackGson.t = GREE_CMDT_STATUS;
116             reqStatusPackGson.cols = colArray;
117             reqStatusPackGson.mac = getId();
118             String reqStatusPackStr = GSON.toJson(reqStatusPackGson);
119
120             // Encrypt and send the Status Request pack
121             String encryptedStatusReqPacket = GreeCryptoUtil.encryptPack(getKey(), reqStatusPackStr);
122             DatagramPacket sendPacket = createPackRequest(0,
123                     new String(encryptedStatusReqPacket.getBytes(), StandardCharsets.UTF_8));
124             clientSocket.send(sendPacket);
125
126             // Keep a copy of the old response to be used to check if values have changed
127             // If first time running, there will not be a previous GreeStatusResponsePack4Gson
128             if (statusResponseGson.isPresent() && statusResponseGson.get().packJson != null) {
129                 prevStatusResponsePackGson = Optional
130                         .of(new GreeStatusResponsePackDTO(statusResponseGson.get().packJson));
131             }
132
133             // Read the response, create the JSON to hold the response values
134             GreeStatusResponseDTO resp = receiveResponse(clientSocket, GreeStatusResponseDTO.class);
135             resp.decryptedPack = GreeCryptoUtil.decryptPack(getKey(), resp.pack);
136             logger.debug("Response from device: {}", resp.decryptedPack);
137             resp.packJson = GSON.fromJson(resp.decryptedPack, GreeStatusResponsePackDTO.class);
138
139             // save the results
140             statusResponseGson = Optional.of(resp);
141             updateTempFtoC();
142         } catch (IOException | JsonSyntaxException e) {
143             throw new GreeException("I/O exception while updating status", e);
144         } catch (RuntimeException e) {
145             logger.debug("Exception", e);
146             String json = statusResponseGson.map(r -> r.packJson.toString()).orElse("n/a");
147             throw new GreeException("Exception while updating status, JSON=" + json, e);
148         }
149     }
150
151     public void bindWithDevice(DatagramSocket clientSocket) throws GreeException {
152         try {
153             // Prep the Binding Request pack
154             GreeBindRequestPackDTO bindReqPackGson = new GreeBindRequestPackDTO();
155             bindReqPackGson.mac = getId();
156             bindReqPackGson.t = GREE_CMDT_BIND;
157             bindReqPackGson.uid = 0;
158             String bindReqPackStr = GSON.toJson(bindReqPackGson);
159
160             // Encrypt and send the Binding Request pack
161             String encryptedBindReqPacket = GreeCryptoUtil.encryptPack(GreeCryptoUtil.getAESGeneralKeyByteArray(),
162                     bindReqPackStr);
163             DatagramPacket sendPacket = createPackRequest(1, encryptedBindReqPacket);
164             clientSocket.send(sendPacket);
165
166             // Recieve a response, create the JSON to hold the response values
167             GreeBindResponseDTO resp = receiveResponse(clientSocket, GreeBindResponseDTO.class);
168             resp.decryptedPack = GreeCryptoUtil.decryptPack(GreeCryptoUtil.getAESGeneralKeyByteArray(), resp.pack);
169             resp.packJson = GSON.fromJson(resp.decryptedPack, GreeBindResponsePackDTO.class);
170
171             // Now set the key and flag to indicate the bind was successful
172             encKey = resp.packJson.key;
173
174             // save the outcome
175             isBound = true;
176         } catch (IOException | JsonSyntaxException e) {
177             throw new GreeException("Unable to bind to device", e);
178         }
179     }
180
181     public void setDevicePower(DatagramSocket clientSocket, int value) throws GreeException {
182         setCommandValue(clientSocket, GREE_PROP_POWER, value);
183     }
184
185     public void setDeviceMode(DatagramSocket clientSocket, int value) throws GreeException {
186         if ((value < 0 || value > 4)) {
187             throw new GreeException("Device mode out of range!");
188         }
189         setCommandValue(clientSocket, GREE_PROP_MODE, value);
190     }
191
192     /**
193      * SwUpDn: controls the swing mode of the vertical air blades
194      *
195      * 0: default
196      * 1: swing in full range
197      * 2: fixed in the upmost position (1/5)
198      * 3: fixed in the middle-up position (2/5)
199      * 4: fixed in the middle position (3/5)
200      * 5: fixed in the middle-low position (4/5)
201      * 6: fixed in the lowest position (5/5)
202      * 7: swing in the downmost region (5/5)
203      * 8: swing in the middle-low region (4/5)
204      * 9: swing in the middle region (3/5)
205      * 10: swing in the middle-up region (2/5)
206      * 11: swing in the upmost region (1/5)
207      */
208     public void setDeviceSwingUpDown(DatagramSocket clientSocket, int value) throws GreeException {
209         if (value < 0 || value > 11) {
210             throw new GreeException("SwingUpDown value is out of range!");
211         }
212         setCommandValue(clientSocket, GREE_PROP_SWINGUPDOWN, value);
213     }
214
215     /**
216      * SwingLfRig: controls the swing mode of the horizontal air blades (available on limited number of devices, e.g.
217      * some Cooper & Hunter units - thanks to mvmn)
218      *
219      * 0: default
220      * 1: full swing
221      * 2-6: fixed position from leftmost to rightmost
222      * Full swing, like for SwUpDn is not supported
223      */
224     public void setDeviceSwingLeftRight(DatagramSocket clientSocket, int value) throws GreeException {
225         if (value < 0 || value > 6) {
226             throw new GreeException("SwingLeftRight value is out of range!");
227         }
228         setCommandValue(clientSocket, GREE_PROP_SWINGLEFTRIGHT, value, 0, 6);
229     }
230
231     /**
232      * Only allow this to happen if this device has been bound and values are valid
233      * Possible values are :
234      * 0 : Auto
235      * 1 : Low
236      * 2 : Medium Low
237      * 3 : Medium
238      * 4 : Medium High
239      * 5 : High
240      */
241     public void setDeviceWindspeed(DatagramSocket clientSocket, int value) throws GreeException {
242         if (value < 0 || value > 5) {
243             throw new GreeException("Value out of range!");
244         }
245
246         HashMap<String, Integer> parameters = new HashMap<>();
247         parameters.put(GREE_PROP_WINDSPEED, value);
248         parameters.put(GREE_PROP_QUIET, 0);
249         parameters.put(GREE_PROP_TURBO, 0);
250         parameters.put(GREE_PROP_NOISE, 0);
251         executeCommand(clientSocket, parameters);
252     }
253
254     /**
255      * Tur: sets fan speed to the maximum. Fan speed cannot be changed while active and only available in Dry and Cool
256      * mode.
257      *
258      * 0: off
259      * 1: on
260      */
261     public void setDeviceTurbo(DatagramSocket clientSocket, int value) throws GreeException {
262         setCommandValue(clientSocket, GREE_PROP_TURBO, value, 0, 1);
263     }
264
265     public void setQuietMode(DatagramSocket clientSocket, int value) throws GreeException {
266         setCommandValue(clientSocket, GREE_PROP_QUIET, value, 0, 2);
267     }
268
269     public void setDeviceLight(DatagramSocket clientSocket, int value) throws GreeException {
270         setCommandValue(clientSocket, GREE_PROP_LIGHT, value);
271     }
272
273     /**
274      * @param value set temperature in degrees Celsius or Fahrenheit
275      */
276     public void setDeviceTempSet(DatagramSocket clientSocket, QuantityType<?> temp) throws GreeException {
277         // If commanding Fahrenheit set halfStep to 1 or 0 to tell the A/C which F integer
278         // temperature to use as celsius alone is ambigious
279         double newVal = temp.doubleValue();
280         int celsiusOrFahrenheit = SIUnits.CELSIUS.equals(temp.getUnit()) ? TEMP_UNIT_CELSIUS : TEMP_UNIT_FAHRENHEIT; // 0=Celsius,
281         // 1=Fahrenheit
282         if (((celsiusOrFahrenheit == TEMP_UNIT_CELSIUS) && (newVal < TEMP_MIN_C || newVal > TEMP_MAX_C))
283                 || ((celsiusOrFahrenheit == TEMP_UNIT_FAHRENHEIT) && (newVal < TEMP_MIN_F || newVal > TEMP_MAX_F))) {
284             throw new IllegalArgumentException("Temp Value out of Range");
285         }
286
287         // Default for Celsius
288         int outVal = (int) newVal;
289         int halfStep = TEMP_HALFSTEP_NO; // for whatever reason halfStep is not supported for Celsius
290
291         // If value argument is degrees F, convert Fahrenheit to Celsius,
292         // SetTem input to A/C always in Celsius despite passing in 1 to TemUn
293         // ******************TempRec TemSet Mapping for setting Fahrenheit****************************
294         // F = [68...86]
295         // C = [20.0, 20.5, 21.1, 21.6, 22.2, 22.7, 23.3, 23.8, 24.4, 25.0, 25.5, 26.1, 26.6, 27.2, 27.7, 28.3,
296         // 28.8, 29.4, 30.0]
297         //
298         // TemSet = [20..30] or [68..86]
299         // TemRec = value - (value) > 0 ? 1 : 1 -> when xx.5 is request xx will become TemSet and halfStep the indicator
300         // for "half on top of TemSet"
301         // ******************TempRec TemSet Mapping for setting Fahrenheit****************************
302         // subtract the float version - the int version to get the fractional difference
303         // if the difference is positive set halfStep to 1, negative to 0
304         if (celsiusOrFahrenheit == TEMP_UNIT_FAHRENHEIT) { // If Fahrenheit,
305             halfStep = newVal - outVal > 0 ? TEMP_HALFSTEP_YES : TEMP_HALFSTEP_NO;
306         }
307         logger.debug("Converted temp from {}{} to temp={}, halfStep={}, unit={})", newVal, temp.getUnit(), outVal,
308                 halfStep, celsiusOrFahrenheit == TEMP_UNIT_CELSIUS ? "C" : "F");
309
310         // Set the values in the HashMap
311         HashMap<String, Integer> parameters = new HashMap<>();
312         parameters.put(GREE_PROP_TEMPUNIT, celsiusOrFahrenheit);
313         parameters.put(GREE_PROP_SETTEMP, outVal);
314         parameters.put(GREE_PROP_TEMPREC, halfStep);
315         executeCommand(clientSocket, parameters);
316     }
317
318     public void setDeviceAir(DatagramSocket clientSocket, int value) throws GreeException {
319         setCommandValue(clientSocket, GREE_PROP_AIR, value);
320     }
321
322     public void setDeviceDry(DatagramSocket clientSocket, int value) throws GreeException {
323         setCommandValue(clientSocket, GREE_PROP_DRY, value);
324     }
325
326     public void setDeviceHealth(DatagramSocket clientSocket, int value) throws GreeException {
327         setCommandValue(clientSocket, GREE_PROP_HEALTH, value);
328     }
329
330     public void setDevicePwrSaving(DatagramSocket clientSocket, int value) throws GreeException {
331         // Set the values in the HashMap
332         HashMap<String, Integer> parameters = new HashMap<>();
333         parameters.put(GREE_PROP_PWR_SAVING, value);
334         parameters.put(GREE_PROP_WINDSPEED, 0);
335         parameters.put(GREE_PROP_QUIET, 0);
336         parameters.put(GREE_PROP_TURBO, 0);
337         parameters.put(GREE_PROP_SLEEP, 0);
338         parameters.put(GREE_PROP_SLEEPMODE, 0);
339         executeCommand(clientSocket, parameters);
340     }
341
342     public int getIntStatusVal(String valueName) {
343         /*
344          * Note : Values can be:
345          * "Pow": Power (0 or 1)
346          * "Mod": Mode: Auto: 0, Cool: 1, Dry: 2, Fan: 3, Heat: 4
347          * "SetTem": Requested Temperature
348          * "WdSpd": Fan Speed : Low:1, Medium Low:2, Medium :3, Medium High :4, High :5
349          * "Air": Air Mode Enabled
350          * "Blo": Dry
351          * "Health": Health
352          * "SwhSlp": Sleep
353          * "SlpMod": ???
354          * "Lig": Light On
355          * "SwingLfRig": Swing Left Right
356          * "SwUpDn": Swing Up Down: // Ceiling:0, Upwards : 10, Downwards : 11, Full range : 1
357          * "Quiet": Quiet mode
358          * "Tur": Turbo
359          * "StHt": 0,
360          * "TemUn": Temperature unit, 0 for Celsius, 1 for Fahrenheit
361          * "HeatCoolType"
362          * "TemRec": (0 or 1), Send with SetTem, when TemUn==1, distinguishes between upper and lower integer Fahrenheit
363          * temp
364          * "SvSt": Power Saving
365          */
366         // Find the valueName in the Returned Status object
367         if (isStatusAvailable()) {
368             List<String> colList = Arrays.asList(statusResponseGson.get().packJson.cols);
369             List<Integer> valList = Arrays.asList(statusResponseGson.get().packJson.dat);
370             int valueArrayposition = colList.indexOf(valueName);
371             if (valueArrayposition != -1) {
372                 // get the Corresponding value
373                 Integer value = valList.get(valueArrayposition);
374                 return value;
375             }
376         }
377
378         return -1;
379     }
380
381     public boolean isStatusAvailable() {
382         return statusResponseGson.isPresent() && (statusResponseGson.get().packJson.cols != null)
383                 && (statusResponseGson.get().packJson.dat != null);
384     }
385
386     public boolean hasStatusValChanged(String valueName) throws GreeException {
387         if (!prevStatusResponsePackGson.isPresent()) {
388             return true; // update value if there is no previous one
389         }
390         // Find the valueName in the Current Status object
391         List<String> currcolList = Arrays.asList(statusResponseGson.get().packJson.cols);
392         List<Integer> currvalList = Arrays.asList(statusResponseGson.get().packJson.dat);
393         int currvalueArrayposition = currcolList.indexOf(valueName);
394         if (currvalueArrayposition == -1) {
395             throw new GreeException("Unable to decode device status");
396         }
397
398         // Find the valueName in the Previous Status object
399         List<String> prevcolList = Arrays.asList(prevStatusResponsePackGson.get().cols);
400         List<Integer> prevvalList = Arrays.asList(prevStatusResponsePackGson.get().dat);
401         int prevvalueArrayposition = prevcolList.indexOf(valueName);
402         if (prevvalueArrayposition == -1) {
403             throw new GreeException("Unable to get status value");
404         }
405
406         // Finally Compare the values
407         return !Objects.equals(currvalList.get(currvalueArrayposition), prevvalList.get(prevvalueArrayposition));
408     }
409
410     protected void executeCommand(DatagramSocket clientSocket, Map<String, Integer> parameters) throws GreeException {
411         // Only allow this to happen if this device has been bound
412         if (!getIsBound()) {
413             throw new GreeException("Device is not bound!");
414         }
415
416         try {
417             // Convert the parameter map values to arrays
418             String[] keyArray = parameters.keySet().toArray(new String[0]);
419             Integer[] valueArray = parameters.values().toArray(new Integer[0]);
420
421             // Prep the Command Request pack
422             GreeExecuteCommandPackDTO execCmdPackGson = new GreeExecuteCommandPackDTO();
423             execCmdPackGson.opt = keyArray;
424             execCmdPackGson.p = valueArray;
425             execCmdPackGson.t = GREE_CMDT_CMD;
426             String execCmdPackStr = GSON.toJson(execCmdPackGson);
427
428             // Now encrypt and send the Command Request pack
429             String encryptedCommandReqPacket = GreeCryptoUtil.encryptPack(getKey(), execCmdPackStr);
430             DatagramPacket sendPacket = createPackRequest(0, encryptedCommandReqPacket);
431             clientSocket.send(sendPacket);
432
433             // Receive and decode result
434             GreeExecResponseDTO execResponseGson = receiveResponse(clientSocket, GreeExecResponseDTO.class);
435             execResponseGson.decryptedPack = GreeCryptoUtil.decryptPack(getKey(), execResponseGson.pack);
436
437             // Create the JSON to hold the response values
438             execResponseGson.packJson = GSON.fromJson(execResponseGson.decryptedPack, GreeExecResponsePackDTO.class);
439         } catch (IOException | JsonSyntaxException e) {
440             throw new GreeException("Exception on command execution", e);
441         }
442     }
443
444     private void setCommandValue(DatagramSocket clientSocket, String command, int value) throws GreeException {
445         executeCommand(clientSocket, Collections.singletonMap(command, value));
446     }
447
448     private void setCommandValue(DatagramSocket clientSocket, String command, int value, int min, int max)
449             throws GreeException {
450         if ((value < min) || (value > max)) {
451             throw new GreeException("Command value out of range!");
452         }
453         executeCommand(clientSocket, Collections.singletonMap(command, value));
454     }
455
456     private DatagramPacket createPackRequest(int i, String pack) {
457         GreeRequestDTO request = new GreeRequestDTO();
458         request.cid = GREE_CID;
459         request.i = i;
460         request.t = GREE_CMDT_PACK;
461         request.uid = 0;
462         request.tcid = getId();
463         request.pack = pack;
464         byte[] sendData = GSON.toJson(request).getBytes(StandardCharsets.UTF_8);
465         DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, ipAddress, port);
466         return sendPacket;
467     }
468
469     private <T> T receiveResponse(DatagramSocket clientSocket, Class<T> classOfT)
470             throws IOException, JsonSyntaxException {
471         byte[] receiveData = new byte[1024];
472         DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
473         clientSocket.receive(receivePacket);
474         String json = new String(receivePacket.getData(), StandardCharsets.UTF_8).replace("\\u0000", "").trim();
475         return GSON.fromJson(json, classOfT);
476     }
477
478     private void updateTempFtoC() {
479         // Status message back from A/C always reports degrees C
480         // If using Fahrenheit, us SetTem, TemUn and TemRec to reconstruct the Fahrenheit temperature
481         // Get Celsius or Fahrenheit from status message
482         int celsiusOrFahrenheit = getIntStatusVal(GREE_PROP_TEMPUNIT);
483         int newVal = getIntStatusVal(GREE_PROP_SETTEMP);
484         int halfStep = getIntStatusVal(GREE_PROP_TEMPREC);
485
486         if ((celsiusOrFahrenheit == -1) || (newVal == -1) || (halfStep == -1)) {
487             throw new IllegalArgumentException("SetTem,TemUn or TemRec is invalid, not performing conversion");
488         } else if (celsiusOrFahrenheit == 1) { // convert SetTem to Fahrenheit
489             // Find the valueName in the Returned Status object
490             String[] columns = statusResponseGson.get().packJson.cols;
491             Integer[] values = statusResponseGson.get().packJson.dat;
492             List<String> colList = Arrays.asList(columns);
493             int valueArrayposition = colList.indexOf(GREE_PROP_SETTEMP);
494             if (valueArrayposition != -1) {
495                 // convert Celsius to Fahrenheit,
496                 // SetTem status returns degrees C regardless of TempUn setting
497
498                 // Perform the float Celsius to Fahrenheit conversion add or subtract 0.5 based on the value of TemRec
499                 // (0 = -0.5, 1 = +0.5). Pass into a rounding function, this yeild the correct Fahrenheit Temperature to
500                 // match A/C display
501                 newVal = (int) (Math.round(((newVal * 9.0 / 5.0) + 32.0) + halfStep - 0.5));
502
503                 // Update the status array with F temp, assume this is updating the array in situ
504                 values[valueArrayposition] = newVal;
505             }
506         }
507     }
508
509     public InetAddress getAddress() {
510         return ipAddress;
511     }
512
513     public boolean getIsBound() {
514         return isBound;
515     }
516
517     public byte[] getKey() {
518         return encKey.getBytes(StandardCharsets.UTF_8);
519     }
520
521     public String getId() {
522         return scanResponseGson.isPresent() ? scanResponseGson.get().packJson.mac : "";
523     }
524
525     public String getName() {
526         return scanResponseGson.isPresent() ? scanResponseGson.get().packJson.name : "";
527     }
528
529     public String getVendor() {
530         return scanResponseGson.isPresent()
531                 ? scanResponseGson.get().packJson.brand + " " + scanResponseGson.get().packJson.vender
532                 : "";
533     }
534
535     public String getModel() {
536         return scanResponseGson.isPresent()
537                 ? scanResponseGson.get().packJson.series + " " + scanResponseGson.get().packJson.model
538                 : "";
539     }
540
541     public void setScanResponseGson(GreeScanResponseDTO gson) {
542         scanResponseGson = Optional.of(gson);
543     }
544 }