Skip to content

Commit 81ea83b

Browse files
authored
[gree] Fixes - using refresh value from configuration and add encryption type - Combined (openhab#18360)
* [gree]:Use the refresh value from the config Signed-off-by: Zhivka Dimova <[email protected]>
1 parent 1ee12ba commit 81ea83b

File tree

10 files changed

+80
-53
lines changed

10 files changed

+80
-53
lines changed

bundles/org.openhab.binding.gree/README.md

+8-14
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,13 @@ No binding configuration is required.
1919

2020
## Thing Configuration
2121

22-
| Channel Name | Type | Description |
23-
|--------------------------|-----------------|-----------------------------------------------------------------------------------------------|
24-
| ipAddress | IP Address | IP address of the unit. |
25-
| broadcastAddress | IP Address | Broadcast address being used for discovery, usually derived from the IP interface address. |
26-
| refresh | Integer | Refresh interval in seconds for polling the device status. |
27-
| currentTemperatureOffset | Decimal | Offset in Celsius for the current temperature value received from the device. |
28-
| encryptionType | EncryptionTypes | Encryption type (ECB or GCM) used for communicating with the AC device |
29-
30-
The Air Conditioner's IP address is mandatory, all other parameters are optional.
31-
If the broadcast is not set (default) it will be derived from openHAB's network setting (Check Network Settings in the openHAB UI).
32-
The binding tries to automatically detect the encryption type when communicating with the AC.
33-
If this fails, you might need need to set the encryption type manually.
34-
Only change this if you have a good reason to.
22+
| Parameter | Parameter ID | Required | Default | Description |
23+
|--------------------|--------------------------|----------|---------|--------------|
24+
| IP Address | ipAddress | yes | | IP address of the unit. |
25+
| Broadcast Address | broadcastAddress | no | | Broadcast address being used for discovery, usually derived from the IP interface address. If the broadcast is not set (default) it will be derived from openHAB's network setting (Check Network Settings in the openHAB UI). |
26+
| Refresh Interval | refreshInterval | no | 60 | Refresh interval in seconds for polling the device status. |
27+
| Offset Temperature | currentTemperatureOffset | no | 0 | Offset in Celsius for the current temperature value received from the device. |
28+
| Encryption Type | encryptionType | no | ECB | Encryption type used for communicating with the AC device. Options: ECB (firmware version < 1.23), COMBINED (firmware version >= 1.23 - use ECB for decoding the scan responce and GCM for further encryption and decryption) and GCM (used by the latest devices). The binding tries to automatically detect the encryption type, in case it fails, you might need to set the encryption type manually. Only change this if you have a good reason to. |
3529

3630
## Channels
3731

@@ -67,7 +61,7 @@ When changing mode, the air conditioner will be turned on unless "off" is select
6761
### Things
6862

6963
```java
70-
Thing gree:airconditioner:a1234561 [ ipAddress="192.168.1.111", refresh=2, encryptionType="ECB" ]
64+
Thing gree:airconditioner:a1234561 [ ipAddress="192.168.1.111", refreshInterval=2, encryptionType="ECB" ]
7165
```
7266

7367
### Items

bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/GreeBindingConstants.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public class GreeBindingConstants {
4343
public static final String PROPERTY_IP = "ipAddress";
4444
public static final String PROPERTY_BROADCAST = "broadcastAddress";
4545

46+
public static final String PROPERTY_REFRESH_INTERVAL = "refreshInterval";
4647
public static final String PROPERTY_ENCRYPTION_TYPE = "encryptionType";
4748

4849
// List of all Channel ids
@@ -163,7 +164,6 @@ public class GreeBindingConstants {
163164
public static final int DATAGRAM_SOCKET_TIMEOUT = 5000; // regular read timeout
164165
public static final int DISCOVERY_TIMEOUT_MS = 7000; // do not change!!
165166
public static final int MAX_SCAN_CYCLES = 3;
166-
public static final int REFRESH_INTERVAL_SEC = 5;
167167
public static final int MAX_API_RETRIES = 3;
168168

169169
public static final int DIGITS_TEMP = 1;
@@ -184,6 +184,7 @@ public class GreeBindingConstants {
184184
public enum EncryptionTypes {
185185
UNKNOWN,
186186
ECB,
187+
COMBINED,
187188
GCM;
188189

189190
private static final Map<String, EncryptionTypes> MAP = Stream.of(EncryptionTypes.values())

bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/GreeConfiguration.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
public class GreeConfiguration {
2929
public String ipAddress = "";
3030
public String broadcastAddress = "";
31-
public int refresh = 60;
31+
public int refreshInterval = 60;
3232
/**
3333
* The currentTemperatureOffset is configureable in case the user wants to offset this temperature for calibration
3434
* of the temperature sensor.
@@ -38,7 +38,8 @@ public class GreeConfiguration {
3838

3939
@Override
4040
public String toString() {
41-
return "Config: ipAddress=" + ipAddress + ", broadcastAddress=" + broadcastAddress + ", refresh=" + refresh
42-
+ ", currentTemperatureOffset=" + currentTemperatureOffset + ", encryptionType=" + encryptionType;
41+
return "Config: ipAddress=" + ipAddress + ", broadcastAddress=" + broadcastAddress + ", refreshInterval="
42+
+ refreshInterval + ", currentTemperatureOffset=" + currentTemperatureOffset + ", encryptionType="
43+
+ encryptionType;
4344
}
4445
}

bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/GreeCryptoUtil.java

+20-12
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
import org.eclipse.jdt.annotation.NonNullByDefault;
3333
import org.openhab.binding.gree.internal.gson.GreeBaseDTO;
34+
import org.openhab.binding.gree.internal.gson.GreeScanResponseDTO;
3435

3536
/**
3637
* The CryptoUtil class provides functionality for encrypting and decrypting
@@ -56,10 +57,10 @@ public static byte[] getGCMGeneralKeyByteArray() {
5657
}
5758

5859
public static byte[] getGeneralKeyByteArray(EncryptionTypes encType) {
59-
if (encType == EncryptionTypes.GCM) {
60-
return getGCMGeneralKeyByteArray();
60+
if (encType == EncryptionTypes.ECB) {
61+
return getAESGeneralKeyByteArray();
6162
}
62-
return getAESGeneralKeyByteArray();
63+
return getGCMGeneralKeyByteArray();
6364
}
6465

6566
public static byte[] getGCMIVByteArray() {
@@ -87,10 +88,15 @@ public static <T extends GreeBaseDTO> String decrypt(T response, EncryptionTypes
8788
encType = getEncryptionType(response);
8889
}
8990

90-
if (encType == EncryptionTypes.GCM) {
91-
return decrypt(getGCMGeneralKeyByteArray(), response, encType);
91+
// Devices with firmware version above 1.23 (encType set to COMBINED) are encrypting the scan response using
92+
// AES general key and ECB encryption type (which means that it is needs to be decrypted the same way), but
93+
// they are expecting the bind request to be encrypted with GCM general key and GCM encryption, and for
94+
// everything else - the device's key and GCM encryption type
95+
if (encType == EncryptionTypes.ECB
96+
|| (response instanceof GreeScanResponseDTO && encType == EncryptionTypes.COMBINED)) {
97+
return decryptPack(getAESGeneralKeyByteArray(), response.pack);
9298
} else {
93-
return decrypt(getAESGeneralKeyByteArray(), response, encType);
99+
return decryptGCMPack(getGCMGeneralKeyByteArray(), response.pack, response.tag);
94100
}
95101
}
96102

@@ -100,10 +106,11 @@ public static <T extends GreeBaseDTO> String decrypt(byte[] keyarray, T response
100106
encType = getEncryptionType(response);
101107
}
102108

103-
if (encType == EncryptionTypes.GCM) {
104-
return decryptGCMPack(keyarray, response.pack, response.tag);
105-
} else {
109+
if (encType == EncryptionTypes.ECB
110+
|| (response instanceof GreeScanResponseDTO && encType == EncryptionTypes.COMBINED)) {
106111
return decryptPack(keyarray, response.pack);
112+
} else {
113+
return decryptGCMPack(keyarray, response.pack, response.tag);
107114
}
108115
}
109116

@@ -150,12 +157,13 @@ public static String decryptGCMPack(byte[] keyBytes, String pack, String tag) th
150157
}
151158

152159
public static String[] encrypt(byte[] keyarray, String message, EncryptionTypes encType) throws GreeException {
153-
if (encType == EncryptionTypes.GCM) {
154-
return encryptGCMPack(keyarray, message);
155-
} else {
160+
if (encType == EncryptionTypes.ECB) {
156161
String[] res = new String[1];
157162
res[0] = encryptPack(keyarray, message);
158163
return res;
164+
} else {
165+
// for GCM and COMBINED always use GCM encription encription
166+
return encryptGCMPack(keyarray, message);
159167
}
160168
}
161169

bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/discovery/GreeDeviceFinder.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ public void scan(DatagramSocket clientSocket, String broadcastAddress, boolean s
116116
if ("gree".equalsIgnoreCase(scanResponseGson.packJson.brand)) {
117117
// Create a new GreeDevice
118118
logger.debug("Discovered device at {}:{}", remoteAddress.getHostAddress(), remotePort);
119-
GreeAirDevice newDevice = new GreeAirDevice(remoteAddress, remotePort, scanResponseGson);
119+
GreeAirDevice newDevice = new GreeAirDevice(remoteAddress, remotePort, scanResponseGson,
120+
encryptionTypeConfig);
120121
addDevice(newDevice);
121122
} else {
122123
logger.debug("Unit discovered, but brand is not GREE");

bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/discovery/GreeDiscoveryService.java

+1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ public void createResult(Map<String, GreeAirDevice> deviceList) {
113113
properties.put(PROPERTY_IP, ipAddress);
114114
properties.put(PROPERTY_BROADCAST, broadcastAddress);
115115
properties.put(PROPERTY_ENCRYPTION_TYPE, device.getEncryptionType());
116+
properties.put(PROPERTY_REFRESH_INTERVAL, device.getRefreshInterval());
116117
ThingUID thingUID = new ThingUID(THING_TYPE_GREEAIRCON, device.getId());
117118
DiscoveryResult result = DiscoveryResultBuilder.create(thingUID).withProperties(properties)
118119
.withRepresentationProperty(Thing.PROPERTY_MAC_ADDRESS).withLabel(device.getName()).build();

bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/handler/GreeAirDevice.java

+30-14
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public class GreeAirDevice {
6565
private int port = 0;
6666
private String encKey = "";
6767
private EncryptionTypes encType = EncryptionTypes.UNKNOWN;
68+
private int refreshInterval = 5;
6869
private Optional<GreeScanResponseDTO> scanResponseGson = Optional.empty();
6970
private Optional<GreeStatusResponseDTO> statusResponseGson = Optional.empty();
7071
private Optional<GreeStatusResponsePackDTO> prevStatusResponsePackGson = Optional.empty();
@@ -74,10 +75,19 @@ public GreeAirDevice() {
7475
}
7576

7677
public GreeAirDevice(InetAddress ipAddress, int port, GreeScanResponseDTO scanResponse) {
78+
this(ipAddress, port, scanResponse, GreeCryptoUtil.getEncryptionType(scanResponse));
79+
}
80+
81+
public GreeAirDevice(InetAddress ipAddress, int port, GreeScanResponseDTO scanResponse,
82+
EncryptionTypes encryptionType) {
7783
this.ipAddress = ipAddress;
7884
this.port = port;
7985
this.scanResponseGson = Optional.of(scanResponse);
80-
this.encType = GreeCryptoUtil.getEncryptionType(scanResponse);
86+
if (encryptionType == EncryptionTypes.UNKNOWN) {
87+
this.encType = GreeCryptoUtil.getEncryptionType(scanResponse);
88+
} else {
89+
this.encType = encryptionType;
90+
}
8191
}
8292

8393
public void getDeviceStatus(DatagramSocket clientSocket) throws GreeException {
@@ -175,9 +185,9 @@ public void bindWithDevice(DatagramSocket clientSocket, EncryptionTypes encrypti
175185
// save the outcome
176186
isBound = true;
177187
} catch (IOException | JsonSyntaxException e) {
178-
if (encType != EncryptionTypes.GCM) {
179-
logger.debug("Unable to bind to device - changing the encryption mode to GCM and trying again", e);
180-
bindWithDevice(clientSocket, EncryptionTypes.GCM);
188+
if (encType == EncryptionTypes.ECB) {
189+
logger.debug("Unable to bind to device - changing the encryption mode to COMBINED and trying again", e);
190+
bindWithDevice(clientSocket, EncryptionTypes.COMBINED);
181191
} else {
182192
throw new GreeException("Unable to bind to device", e);
183193
}
@@ -466,11 +476,11 @@ private DatagramPacket createPackRequest(int i, String[] data) {
466476
request.uid = 0;
467477
request.tcid = getId();
468478
request.pack = data[0];
469-
if (encType == EncryptionTypes.GCM) {
479+
if (encType != EncryptionTypes.ECB) {
470480
if (data.length > 1) {
471481
request.tag = data[1];
472482
} else {
473-
logger.warn("Missing string for tag property for GCM encryption data");
483+
logger.warn("Missing string for tag property for {} encryption data", encType);
474484
}
475485
}
476486
byte[] sendData = GSON.toJson(request).getBytes(StandardCharsets.UTF_8);
@@ -526,15 +536,13 @@ public boolean getIsBound() {
526536
}
527537

528538
public void setEncryptionType(EncryptionTypes value) {
529-
if (value == EncryptionTypes.UNKNOWN) {
530-
logger.debug("Trying to set encryption type to 'UNKNOWN' for device: {}, current value: {}", getName(),
531-
encType);
532-
if (encType == EncryptionTypes.UNKNOWN) {
533-
logger.debug("Falling back to 'ECB' for device: {}", getName());
534-
encType = EncryptionTypes.ECB;
535-
}
539+
logger.debug("setEncriptionType called for device: {}, to change from: {}, to: {}", getName(), encType, value);
540+
if (value == EncryptionTypes.UNKNOWN && encType == EncryptionTypes.UNKNOWN) {
541+
logger.debug("Set default ECB type for device: {}", getName());
542+
encType = EncryptionTypes.ECB;
543+
} else if (value == EncryptionTypes.UNKNOWN) {
544+
logger.debug("Trying to set the encription type to UNKNOWN, no change made for device: {}", getName());
536545
} else {
537-
logger.debug("Change encryption type for device: {}, from : {}, to: {}", getName(), encType, value);
538546
encType = value;
539547
}
540548
}
@@ -543,6 +551,14 @@ public EncryptionTypes getEncryptionType() {
543551
return encType;
544552
}
545553

554+
public void setRefreshInterval(int value) {
555+
refreshInterval = value;
556+
}
557+
558+
public int getRefreshInterval() {
559+
return refreshInterval;
560+
}
561+
546562
public byte[] getKey() {
547563
return encKey.getBytes(StandardCharsets.UTF_8);
548564
}

bundles/org.openhab.binding.gree/src/main/java/org/openhab/binding/gree/internal/handler/GreeHandler.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public GreeHandler(Thing thing, GreeTranslationProvider messages, GreeDeviceFind
8484
@Override
8585
public void initialize() {
8686
config = getConfigAs(GreeConfiguration.class);
87-
if (config.ipAddress.isEmpty() || (config.refresh < 0)) {
87+
if (config.ipAddress.isBlank() || (config.refreshInterval < 0)) {
8888
String message = messages.get("thinginit.invconf");
8989
logger.warn("{}: {}", thingId, message);
9090
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, message);
@@ -405,16 +405,17 @@ private void startAutomaticRefresh() {
405405
};
406406

407407
if (refreshTask == null) {
408-
refreshTask = scheduler.scheduleWithFixedDelay(refresher, 0, REFRESH_INTERVAL_SEC, TimeUnit.SECONDS);
409-
logger.debug("{}: Automatic refresh started ({} second interval)", thingId, config.refresh);
408+
refreshTask = scheduler.scheduleWithFixedDelay(refresher, 0, config.refreshInterval, TimeUnit.SECONDS);
409+
device.setRefreshInterval(config.refreshInterval);
410+
logger.debug("{}: Automatic refresh started ({} second interval)", thingId, config.refreshInterval);
410411
forceRefresh = true;
411412
}
412413
}
413414

414415
private boolean isMinimumRefreshTimeExceeded() {
415416
long currentTime = Instant.now().toEpochMilli();
416417
long timeSinceLastRefresh = currentTime - lastRefreshTime;
417-
if (!forceRefresh && (timeSinceLastRefresh < config.refresh * 1000)) {
418+
if (!forceRefresh && (timeSinceLastRefresh < config.refreshInterval * 1000)) {
418419
return false;
419420
}
420421
lastRefreshTime = currentTime;

bundles/org.openhab.binding.gree/src/main/resources/OH-INF/i18n/gree.properties

+4-2
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ thing-type.config.gree.airconditioner.ipAddress.label = IP Address
1414
thing-type.config.gree.airconditioner.ipAddress.description = IP Address of the GREE unit.
1515
thing-type.config.gree.airconditioner.broadcastAddress.label = Subnet Broadcast Address
1616
thing-type.config.gree.airconditioner.broadcastAddress.description = Broadcast IP address of the local subnet.
17-
thing-type.config.gree.airconditioner.refresh.label = Refresh Interval
18-
thing-type.config.gree.airconditioner.refresh.description = Interval to query an update from the device.
17+
thing-type.config.gree.airconditioner.refreshInterval.label = Refresh Interval
18+
thing-type.config.gree.airconditioner.refreshInterval.description = Interval to query an update from the device.
1919
thing-type.config.gree.airconditioner.currentTemperatureOffset.label = Offset for Current Temperature
2020
thing-type.config.gree.airconditioner.currentTemperatureOffset.description = The offset in Celsius for the current temperature value received from the device.
2121
thing-type.config.gree.airconditioner.encryptionType.label = Encryption type
2222
thing-type.config.gree.airconditioner.encryptionType.description = The encryption type used for encrypting the data send to the AC device.
23+
thing-type.config.gree.airconditioner.encryptionType.state.option.UNKNOWN = Automatic
2324
thing-type.config.gree.airconditioner.encryptionType.state.option.ECB = ECB
25+
thing-type.config.gree.airconditioner.encryptionType.state.option.COMBINED = Combined
2426
thing-type.config.gree.airconditioner.encryptionType.state.option.GCM = GCM
2527

2628
# channel types

bundles/org.openhab.binding.gree/src/main/resources/OH-INF/thing/thing-types.xml

+4-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
<parameter name="broadcastAddress" type="text" required="false">
3232
<context>network-address</context>
3333
</parameter>
34-
<parameter name="refresh" type="integer" required="true" unit="s">
34+
<parameter name="refreshInterval" type="integer" required="true" unit="s">
3535
<default>60</default>
3636
<unitLabel>seconds</unitLabel>
3737
<advanced>true</advanced>
@@ -42,11 +42,13 @@
4242
<advanced>true</advanced>
4343
</parameter>
4444
<parameter name="encryptionType" type="text">
45+
<advanced>true</advanced>
4546
<options>
47+
<option value="UNKNOWN">@text/thing-type.config.gree.airconditioner.encryptionType.state.option.UNKNOWN</option>
4648
<option value="ECB">@text/thing-type.config.gree.airconditioner.encryptionType.state.option.ECB</option>
49+
<option value="COMBINED">@text/thing-type.config.gree.airconditioner.encryptionType.state.option.COMBINED</option>
4750
<option value="GCM">@text/thing-type.config.gree.airconditioner.encryptionType.state.option.GCM</option>
4851
</options>
49-
<advanced>true</advanced>
5052
</parameter>
5153
</config-description>
5254

0 commit comments

Comments
 (0)