Skip to content

Commit f890200

Browse files
author
Ravi Nadahar
committed
Make ShellyThingConfiguration thread-safe
Signed-off-by: Ravi Nadahar <nadahar@rediffmail.com>
1 parent 240fc17 commit f890200

17 files changed

Lines changed: 408 additions & 209 deletions

bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api/ShellyHttpClient.java

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,7 @@ public class ShellyHttpClient {
7272
protected AtomicInteger timeoutsRecovered = new AtomicInteger(0);
7373
protected volatile boolean basicAuth = false;
7474

75-
// All access must be guarded by "this"
76-
protected ShellyThingConfiguration config;
75+
protected volatile ShellyThingConfiguration config;
7776

7877
private final ShellyDeviceProfile profile;
7978

@@ -128,11 +127,7 @@ protected String httpRequest(String uri) throws ShellyApiException {
128127
}
129128
return apiResult.response; // successful
130129
} catch (ShellyApiException e) {
131-
String password;
132-
synchronized (this) {
133-
password = config.password;
134-
}
135-
if (e.isHttpAccessUnauthorized() && !profile.isGen2 && !basicAuth && !password.isBlank()) {
130+
if (e.isHttpAccessUnauthorized() && !profile.isGen2 && !basicAuth && !config.getPassword().isBlank()) {
136131
logger.debug("{}: Access is unauthorized, auto-activate basic auth", thingName);
137132
basicAuth = true;
138133
apiResult = innerRequest(HttpMethod.GET, uri, null, "");
@@ -168,34 +163,27 @@ public String httpPost(@Nullable Shelly2AuthChallenge auth, String data) throws
168163

169164
private ShellyApiResult innerRequest(HttpMethod method, String uri, @Nullable Shelly2AuthChallenge auth,
170165
String data) throws ShellyApiException {
171-
String deviceIp;
172-
String userId;
173-
String password;
174-
synchronized (this) {
175-
deviceIp = config.deviceIp;
176-
userId = config.userId;
177-
password = config.password;
178-
}
179166

180167
Request request = null;
181-
String url = "http://" + deviceIp + uri;
168+
String url = "http://" + config.getDeviceIp() + uri;
182169
ShellyApiResultBuilder builder = ShellyApiResult.builder(method.toString(), url);
183170

184171
try {
185172
request = httpClient.newRequest(url).method(method.toString()).timeout(SHELLY_API_TIMEOUT_MS,
186173
TimeUnit.MILLISECONDS);
187174

188-
if (!uri.equals(SHELLY_URL_DEVINFO) && !password.isBlank()) { // not for /shelly or no password
189-
// configured
175+
if (!uri.equals(SHELLY_URL_DEVINFO) && !config.getPassword().isBlank()) { // not for /shelly or no password
176+
// configured
190177
// Add Auth info
191178
// Gen 1: Basic Auth
192179
// Gen 2: Digest Auth
193180
String authHeader = "";
194181
if (auth != null) { // only if we received an Auth challenge
195-
authHeader = formatAuthResponse(uri, buildAuthResponse(uri, auth, SHELLY2_AUTHDEF_USER, password));
182+
authHeader = formatAuthResponse(uri,
183+
buildAuthResponse(uri, auth, SHELLY2_AUTHDEF_USER, config.getPassword()));
196184
} else {
197185
if (basicAuth) {
198-
String bearer = userId + ":" + password;
186+
String bearer = config.getUserId() + ":" + config.getPassword();
199187
authHeader = HTTP_AUTH_TYPE_BASIC + " "
200188
+ Base64.getEncoder().encodeToString(bearer.getBytes(StandardCharsets.UTF_8));
201189
}

bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api1/Shelly1CoapHandler.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,8 @@ public synchronized void start(String thingName, ShellyThingConfiguration config
127127
String ps = substringAfter(profile.coiotEndpoint, ":");
128128
coiotPort = Integer.parseInt(ps);
129129
}
130-
coapServer.start(config.localIp, coiotPort, this);
131-
statusClient = new CoapClient(completeUrl(config.deviceIp, coiotPort, COLOIT_URI_DEVSTATUS))
130+
coapServer.start(config.getLocalIp(), coiotPort, this);
131+
statusClient = new CoapClient(completeUrl(config.getDeviceIp(), coiotPort, COLOIT_URI_DEVSTATUS))
132132
.setTimeout((long) SHELLY_API_TIMEOUT_MS).useNONs().setEndpoint(coapServer.getEndpoint());
133133
@Nullable
134134
Endpoint endpoint = null;
@@ -147,7 +147,7 @@ public synchronized void start(String thingName, ShellyThingConfiguration config
147147
throw new ShellyApiException("Network error", e);
148148
} catch (UnknownHostException e) {
149149
logger.info("{}: CoAP Exception (Unknown Host)", thingName, e);
150-
throw new ShellyApiException("Unknown Host: " + config.deviceIp, e);
150+
throw new ShellyApiException("Unknown Host: " + config.getDeviceIp(), e);
151151
}
152152
}
153153

@@ -177,7 +177,7 @@ public void processResponse(@Nullable Response response) {
177177

178178
List<Option> options = response.getOptions().asSortedList();
179179
String ip = response.getSourceContext().getPeerAddress().toString();
180-
boolean match = ip.contains("/" + config.deviceIp + ":");
180+
boolean match = ip.contains("/" + config.getDeviceIp() + ":");
181181
if (!match) {
182182
// We can't identify device by IP, so we need to check the CoAP header's Global Device ID
183183
for (Option opt : options) {
@@ -310,7 +310,7 @@ public void processResponse(@Nullable Response response) {
310310

311311
if (!updatesRequested) {
312312
// Observe Status Updates
313-
reqStatus = sendRequest(reqStatus, config.deviceIp, COLOIT_URI_DEVSTATUS, Type.NON);
313+
reqStatus = sendRequest(reqStatus, config.getDeviceIp(), COLOIT_URI_DEVSTATUS, Type.NON);
314314
updatesRequested = true;
315315
}
316316
}
@@ -537,7 +537,7 @@ private void discover() {
537537
}
538538
}
539539
}
540-
reqDescription = sendRequest(reqDescription, config.deviceIp, COLOIT_URI_DEVDESC, Type.CON);
540+
reqDescription = sendRequest(reqDescription, config.getDeviceIp(), COLOIT_URI_DEVDESC, Type.CON);
541541
}
542542

543543
/**

bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api1/Shelly1HttpApi.java

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -574,10 +574,10 @@ private void setSensorEventUrls() throws ShellyApiException, ShellyApiException
574574
synchronized (this) {
575575
config = this.config;
576576
}
577-
setEventUrl(config.eventsSensorReport, SHELLY_EVENT_SENSORREPORT, SHELLY_EVENT_DARK, SHELLY_EVENT_TWILIGHT,
578-
SHELLY_EVENT_FLOOD_DETECTED, SHELLY_EVENT_FLOOD_GONE, SHELLY_EVENT_OPEN, SHELLY_EVENT_CLOSE,
579-
SHELLY_EVENT_VIBRATION, SHELLY_EVENT_ALARM_MILD, SHELLY_EVENT_ALARM_HEAVY, SHELLY_EVENT_ALARM_OFF,
580-
SHELLY_EVENT_TEMP_OVER, SHELLY_EVENT_TEMP_UNDER);
577+
setEventUrl(config.isEventsSensorReport(), SHELLY_EVENT_SENSORREPORT, SHELLY_EVENT_DARK,
578+
SHELLY_EVENT_TWILIGHT, SHELLY_EVENT_FLOOD_DETECTED, SHELLY_EVENT_FLOOD_GONE, SHELLY_EVENT_OPEN,
579+
SHELLY_EVENT_CLOSE, SHELLY_EVENT_VIBRATION, SHELLY_EVENT_ALARM_MILD, SHELLY_EVENT_ALARM_HEAVY,
580+
SHELLY_EVENT_ALARM_OFF, SHELLY_EVENT_TEMP_OVER, SHELLY_EVENT_TEMP_UNDER);
581581
}
582582
}
583583

@@ -593,25 +593,25 @@ private void setEventUrls(Integer index) throws ShellyApiException {
593593
config = this.config;
594594
}
595595
if (profile.isRoller) {
596-
setEventUrl(EVENT_TYPE_ROLLER, 0, config.eventsRoller, SHELLY_EVENT_ROLLER_OPEN, SHELLY_EVENT_ROLLER_CLOSE,
597-
SHELLY_EVENT_ROLLER_STOP);
596+
setEventUrl(EVENT_TYPE_ROLLER, 0, config.isEventsRoller(), SHELLY_EVENT_ROLLER_OPEN,
597+
SHELLY_EVENT_ROLLER_CLOSE, SHELLY_EVENT_ROLLER_STOP);
598598
} else if (profile.isDimmer) {
599599
// 2 set of URLs
600-
setEventUrl(EVENT_TYPE_LIGHT, index, config.eventsButton, SHELLY_EVENT_BTN1_ON, SHELLY_EVENT_BTN1_OFF,
600+
setEventUrl(EVENT_TYPE_LIGHT, index, config.isEventsButton(), SHELLY_EVENT_BTN1_ON, SHELLY_EVENT_BTN1_OFF,
601601
SHELLY_EVENT_BTN2_ON, SHELLY_EVENT_BTN2_OFF);
602-
setEventUrl(EVENT_TYPE_LIGHT, index, config.eventsPush, SHELLY_EVENT_SHORTPUSH1, SHELLY_EVENT_LONGPUSH1,
602+
setEventUrl(EVENT_TYPE_LIGHT, index, config.isEventsPush(), SHELLY_EVENT_SHORTPUSH1, SHELLY_EVENT_LONGPUSH1,
603603
SHELLY_EVENT_SHORTPUSH2, SHELLY_EVENT_LONGPUSH2);
604604

605605
// Relay output
606-
setEventUrl(EVENT_TYPE_LIGHT, index, config.eventsSwitch, SHELLY_EVENT_OUT_ON, SHELLY_EVENT_OUT_OFF);
606+
setEventUrl(EVENT_TYPE_LIGHT, index, config.isEventsSwitch(), SHELLY_EVENT_OUT_ON, SHELLY_EVENT_OUT_OFF);
607607
} else if (profile.hasRelays) {
608608
// Standard relays: btn_xxx, out_xxx, short/longpush URLs
609-
setEventUrl(EVENT_TYPE_RELAY, index, config.eventsButton, SHELLY_EVENT_BTN_ON, SHELLY_EVENT_BTN_OFF);
610-
setEventUrl(EVENT_TYPE_RELAY, index, config.eventsPush, SHELLY_EVENT_SHORTPUSH, SHELLY_EVENT_LONGPUSH);
611-
setEventUrl(EVENT_TYPE_RELAY, index, config.eventsSwitch, SHELLY_EVENT_OUT_ON, SHELLY_EVENT_OUT_OFF);
609+
setEventUrl(EVENT_TYPE_RELAY, index, config.isEventsButton(), SHELLY_EVENT_BTN_ON, SHELLY_EVENT_BTN_OFF);
610+
setEventUrl(EVENT_TYPE_RELAY, index, config.isEventsPush(), SHELLY_EVENT_SHORTPUSH, SHELLY_EVENT_LONGPUSH);
611+
setEventUrl(EVENT_TYPE_RELAY, index, config.isEventsSwitch(), SHELLY_EVENT_OUT_ON, SHELLY_EVENT_OUT_OFF);
612612
} else if (profile.isLight) {
613613
// Duo, Bulb
614-
setEventUrl(EVENT_TYPE_LIGHT, index, config.eventsSwitch, SHELLY_EVENT_OUT_ON, SHELLY_EVENT_OUT_OFF);
614+
setEventUrl(EVENT_TYPE_LIGHT, index, config.isEventsSwitch(), SHELLY_EVENT_OUT_ON, SHELLY_EVENT_OUT_OFF);
615615
}
616616
}
617617

@@ -621,16 +621,16 @@ private void setEventUrl(boolean enabled, String... eventTypes) throws ShellyApi
621621
config = this.config;
622622
}
623623

624-
if (config.localIp.isEmpty()) {
624+
if (config.getLocalIp().isEmpty()) {
625625
throw new ShellyApiException(thingName + ": Local IP address was not detected, can't build Callback URL");
626626
}
627627
for (String eventType : eventTypes) {
628628
if (profile.containsEventUrl(eventType)) {
629629
// H&T adds the type=xx to report_url itself, so we need to ommit here
630630
String eclass = profile.isSensor ? EVENT_TYPE_SENSORDATA : eventType;
631631
String urlParm = eventType.contains("temp") || profile.isHT ? "" : "?type=" + eventType;
632-
String callBackUrl = "http://" + config.localIp + ":" + config.localPort + SHELLY1_CALLBACK_URI + "/"
633-
+ profile.thingName + "/" + eclass + urlParm;
632+
String callBackUrl = "http://" + config.getLocalIp() + ":" + config.getLocalPort()
633+
+ SHELLY1_CALLBACK_URI + "/" + profile.thingName + "/" + eclass + urlParm;
634634
String newUrl = enabled ? callBackUrl : SHELLY_NULL_URL;
635635
String testUrl = "\"" + mkEventUrl(eventType) + "\":\"" + newUrl + "\"";
636636
if (!enabled && !profile.settingsJson.contains(testUrl)) {
@@ -656,8 +656,9 @@ private void setEventUrl(String deviceClass, Integer index, boolean enabled, Str
656656

657657
for (String eventType : eventTypes) {
658658
if (profile.containsEventUrl(eventType)) {
659-
String callBackUrl = "http://" + config.localIp + ":" + config.localPort + SHELLY1_CALLBACK_URI + "/"
660-
+ profile.thingName + "/" + deviceClass + "/" + index + "?type=" + eventType;
659+
String callBackUrl = "http://" + config.getLocalIp() + ":" + config.getLocalPort()
660+
+ SHELLY1_CALLBACK_URI + "/" + profile.thingName + "/" + deviceClass + "/" + index + "?type="
661+
+ eventType;
661662
String newUrl = enabled ? callBackUrl : SHELLY_NULL_URL;
662663
String test = "\"" + mkEventUrl(eventType) + "\":\"" + callBackUrl + "\"";
663664
if (!enabled && !profile.settingsJson.contains(test)) {

bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api2/Shelly2ApiClient.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -260,9 +260,11 @@ public ShellyDeviceProfile getDeviceProfile(ThingTypeUID thingTypeUID, @Nullable
260260
profile.hasRelays = profile.numRelays > 0 || profile.numRollers > 0;
261261

262262
ShellySettingsDevice device = profile.device;
263-
if (config.realm.isBlank()) {
264-
config.realm = getString(profile.device.hostname);
265-
logger.trace("{}: {} is used as realm", thingName, config.realm);
263+
synchronized (config) {
264+
if (config.getRealm().isBlank()) {
265+
config.setRealm(getString(profile.device.hostname));
266+
logger.trace("{}: {} is used as realm", thingName, config.getRealm());
267+
}
266268
}
267269
profile.settings.fw = getString(device.fw);
268270
profile.fwDate = substringBefore(substringBefore(device.fw, "/"), "-");
@@ -1305,7 +1307,7 @@ protected Shelly2RpcBaseMessage buildRequest(String method, @Nullable Object par
13051307
String uid = thing.getThing().getUID().getAsString();
13061308
suffix = substringAfterLast(uid, ":");
13071309
} else {
1308-
suffix = config.localIp; // use a unique identifier;
1310+
suffix = config.getLocalIp(); // use a unique identifier;
13091311
}
13101312

13111313
Shelly2RpcBaseMessage request = new Shelly2RpcBaseMessage();

bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api2/Shelly2ApiRpc.java

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ public void initialize(String thingName, ShellyThingConfiguration config) throws
148148
rpcSocket.disconnect();
149149
}
150150

151-
rpcSocket = new Shelly2RpcSocket(thingName, thingTable, config.deviceIp, client, scheduler);
151+
rpcSocket = new Shelly2RpcSocket(thingName, thingTable, config.getDeviceIp(), client, scheduler);
152152
rpcSocket.addMessageHandler(this);
153153
this.rpcSocket = rpcSocket;
154154
initialized = true;
@@ -163,7 +163,7 @@ public boolean isInitialized() {
163163
public void startScan() {
164164
try {
165165
if (getProfile().isBlu) {
166-
installScript(SHELLY2_BLU_GWSCRIPT, config.enableBluGateway);
166+
installScript(SHELLY2_BLU_GWSCRIPT, config.getEnableBluGateway());
167167
}
168168
} catch (ShellyApiException e) {
169169
}
@@ -219,9 +219,11 @@ public ShellyDeviceProfile getDeviceProfile(ThingTypeUID thingTypeUID, @Nullable
219219
profile.hasRelays = profile.numRelays > 0 || profile.numRollers > 0;
220220

221221
ShellySettingsDevice device = profile.device;
222-
if (config.realm.isBlank()) {
223-
config.realm = getString(profile.device.hostname);
224-
logger.trace("{}: {} is used as realm", thingName, config.realm);
222+
synchronized (config) {
223+
if (config.getRealm().isBlank()) {
224+
config.setRealm(getString(profile.device.hostname));
225+
logger.trace("{}: {} is used as realm", thingName, config.getRealm());
226+
}
225227
}
226228
profile.settings.fw = getString(device.fw);
227229
profile.fwDate = substringBefore(substringBefore(device.fw, "/"), "-");
@@ -348,8 +350,8 @@ public ShellyDeviceProfile getDeviceProfile(ThingTypeUID thingTypeUID, @Nullable
348350
try {
349351
if (profile.alwaysOn && dc.ble != null) {
350352
logger.debug("{}: BLU Gateway support is {} for this device", thingName,
351-
config.enableBluGateway ? "enabled" : "disabled");
352-
if (config.enableBluGateway) {
353+
config.getEnableBluGateway() ? "enabled" : "disabled");
354+
if (config.getEnableBluGateway()) {
353355
boolean bluetooth = getBool(dc.ble.enable);
354356
boolean observer = dc.ble.observer != null && getBool(dc.ble.observer.enable);
355357
if (!bluetooth) {
@@ -365,7 +367,7 @@ public ShellyDeviceProfile getDeviceProfile(ThingTypeUID thingTypeUID, @Nullable
365367
restart = setBluetooth(true);
366368
}
367369

368-
installScript(SHELLY2_BLU_GWSCRIPT, config.enableBluGateway && bluetooth);
370+
installScript(SHELLY2_BLU_GWSCRIPT, config.getEnableBluGateway() && bluetooth);
369371

370372
if (restart) {
371373
logger.info("{}: Restart device to activate BLU Gateway", thingName);
@@ -383,8 +385,8 @@ public ShellyDeviceProfile getDeviceProfile(ThingTypeUID thingTypeUID, @Nullable
383385

384386
private void checkSetWsCallback() throws ShellyApiException {
385387
Shelly2ConfigParms wsConfig = apiRequest(SHELLYRPC_METHOD_WSGETCONFIG, null, Shelly2ConfigParms.class);
386-
String url = "ws://" + config.localIp + ":" + config.localPort + "/shelly/wsevent";
387-
if (!config.localIp.isEmpty() && !getBool(wsConfig.enable)
388+
String url = "ws://" + config.getLocalIp() + ":" + config.getLocalPort() + "/shelly/wsevent";
389+
if (!config.getLocalIp().isEmpty() && !getBool(wsConfig.enable)
388390
|| !url.equalsIgnoreCase(getString(wsConfig.server))) {
389391
logger.debug("{}: A battery device was detected without correct callback, fix it", thingName);
390392
wsConfig.enable = true;
@@ -1056,7 +1058,7 @@ public ShellySettingsLogin getLoginSettings() throws ShellyApiException {
10561058
public ShellySettingsLogin setLoginCredentials(String user, String password) throws ShellyApiException {
10571059
Shelly2RpcRequestParams params = new Shelly2RpcRequestParams();
10581060
params.user = "admin";
1059-
params.realm = config.realm;
1061+
params.realm = config.getRealm();
10601062
params.ha1 = sha256(params.user + ":" + params.realm + ":" + password);
10611063
apiRequest(SHELLYRPC_METHOD_AUTHSET, params, String.class);
10621064

bundles/org.openhab.binding.shelly/src/main/java/org/openhab/binding/shelly/internal/api2/ShellyBluApi.java

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -89,19 +89,13 @@ public void initialize(String thingName, ShellyThingConfiguration config) throws
8989
}
9090
}
9191

92-
@Override
93-
public void setConfig(String thingName, ShellyThingConfiguration config) {
94-
this.thingName = thingName;
95-
this.config = config;
96-
}
97-
9892
@Override
9993
public ShellySettingsDevice getDeviceInfo() throws ShellyApiException {
10094
ShellySettingsDevice info = new ShellySettingsDevice();
101-
info.hostname = !config.realm.isEmpty() ? config.realm : "";
95+
info.hostname = !config.getRealm().isEmpty() ? config.getRealm() : "";
10296
info.fw = "";
10397
info.type = "BLU";
104-
info.mac = config.deviceAddress;
98+
info.mac = config.getDeviceAddress();
10599
info.auth = false;
106100
info.gen = 2;
107101
return info;
@@ -125,8 +119,10 @@ public ShellyDeviceProfile getDeviceProfile(ThingTypeUID thingTypeUID, @Nullable
125119
}
126120

127121
profile.device = getDeviceInfo();
128-
if (config.realm.isEmpty()) {
129-
config.realm = getString(profile.device.hostname);
122+
synchronized (config) {
123+
if (config.getRealm().isEmpty()) {
124+
config.setRealm(getString(profile.device.hostname));
125+
}
130126
}
131127

132128
// for now we have no API to get this information

0 commit comments

Comments
 (0)