[melcloud] Improve null handling (#17295)

* Add null annotations

Signed-off-by: Leo Siepel <leosiepel@gmail.com>
This commit is contained in:
lsiepel 2024-09-09 21:21:37 +02:00 committed by GitHub
parent 27e08879f6
commit d2d2e63016
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 154 additions and 128 deletions

View File

@ -17,6 +17,7 @@ import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.core.thing.ThingTypeUID;
/**
@ -26,6 +27,7 @@ import org.openhab.core.thing.ThingTypeUID;
* @author Luca Calcaterra - Initial contribution
* @author Wietse van Buitenen - Added heatpump device
*/
@NonNullByDefault
public class MelCloudBindingConstants {
private static final String BINDING_ID = "melcloud";

View File

@ -14,6 +14,7 @@ package org.openhab.binding.melcloud.internal;
import static org.openhab.binding.melcloud.internal.MelCloudBindingConstants.*;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.melcloud.internal.handler.MelCloudAccountHandler;
import org.openhab.binding.melcloud.internal.handler.MelCloudDeviceHandler;
@ -33,6 +34,7 @@ import org.osgi.service.component.annotations.Component;
* @author Luca Calcaterra - Initial contribution
* @author Wietse van Buitenen - Added heatpump device
*/
@NonNullByDefault
@Component(configurationPid = "binding.melcloud", service = ThingHandlerFactory.class)
public class MelCloudHandlerFactory extends BaseThingHandlerFactory {

View File

@ -19,13 +19,15 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import org.openhab.binding.melcloud.internal.api.json.Device;
import org.openhab.binding.melcloud.internal.api.json.DeviceStatus;
import org.openhab.binding.melcloud.internal.api.json.HeatpumpDeviceStatus;
import org.openhab.binding.melcloud.internal.api.json.ListDevicesResponse;
import org.openhab.binding.melcloud.internal.api.json.LoginClientResponse;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.openhab.binding.melcloud.internal.api.dto.Device;
import org.openhab.binding.melcloud.internal.api.dto.DeviceStatus;
import org.openhab.binding.melcloud.internal.api.dto.HeatpumpDeviceStatus;
import org.openhab.binding.melcloud.internal.api.dto.ListDevicesResponse;
import org.openhab.binding.melcloud.internal.api.dto.LoginClientResponse;
import org.openhab.binding.melcloud.internal.exceptions.MelCloudCommException;
import org.openhab.binding.melcloud.internal.exceptions.MelCloudLoginException;
import org.openhab.core.io.net.http.HttpUtil;
@ -45,6 +47,7 @@ import com.google.gson.JsonSyntaxException;
* @author Pauli Anttila - Refactoring
* @author Wietse van Buitenen - Return all devices, added heatpump device
*/
@NonNullByDefault
public class MelCloudConnection {
private static final String LOGIN_URL = "https://app.melcloud.com/Mitsubishi.Wifi.Client/Login/ClientLogin";
@ -54,18 +57,18 @@ public class MelCloudConnection {
private static final int TIMEOUT_MILLISECONDS = 10000;
// Gson objects are safe to share across threads and are somewhat expensive to construct. Use a single instance.
private static final Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation()
private static final Gson GSON = new GsonBuilder().excludeFieldsWithoutExposeAnnotation()
.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();
private final Logger logger = LoggerFactory.getLogger(MelCloudConnection.class);
private boolean isConnected = false;
private String sessionKey;
private String sessionKey = "";
public void login(String username, String password, int languageId)
throws MelCloudCommException, MelCloudLoginException {
setConnected(false);
sessionKey = null;
sessionKey = "";
JsonObject jsonReq = new JsonObject();
jsonReq.addProperty("Email", username);
jsonReq.addProperty("Password", password);
@ -79,7 +82,7 @@ public class MelCloudConnection {
String loginResponse = HttpUtil.executeUrl("POST", LOGIN_URL, null, data, "application/json",
TIMEOUT_MILLISECONDS);
logger.debug("Login response: {}", loginResponse);
LoginClientResponse resp = gson.fromJson(loginResponse, LoginClientResponse.class);
LoginClientResponse resp = Objects.requireNonNull(GSON.fromJson(loginResponse, LoginClientResponse.class));
if (resp.getErrorId() != null) {
String errorMsg = String.format("Login failed, error code: %s", resp.getErrorId());
if (resp.getErrorMessage() != null) {
@ -101,7 +104,7 @@ public class MelCloudConnection {
TIMEOUT_MILLISECONDS);
logger.debug("Device list response: {}", response);
List<Device> devices = new ArrayList<>();
ListDevicesResponse[] buildings = gson.fromJson(response, ListDevicesResponse[].class);
ListDevicesResponse[] buildings = GSON.fromJson(response, ListDevicesResponse[].class);
Arrays.asList(buildings).forEach(building -> {
if (building.getStructure().getDevices() != null) {
devices.addAll(building.getStructure().getDevices());
@ -137,7 +140,7 @@ public class MelCloudConnection {
try {
String response = HttpUtil.executeUrl("GET", url, getHeaderProperties(), null, null, TIMEOUT_MILLISECONDS);
logger.debug("Device status response: {}", response);
return gson.fromJson(response, DeviceStatus.class);
return Objects.requireNonNull(GSON.fromJson(response, DeviceStatus.class));
} catch (IOException | JsonSyntaxException e) {
setConnected(false);
throw new MelCloudCommException("Error occurred during device status fetch", e);
@ -146,14 +149,14 @@ public class MelCloudConnection {
public DeviceStatus sendDeviceStatus(DeviceStatus deviceStatus) throws MelCloudCommException {
assertConnected();
String content = gson.toJson(deviceStatus, DeviceStatus.class);
String content = GSON.toJson(deviceStatus, DeviceStatus.class);
logger.debug("Sending device status: {}", content);
InputStream data = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8));
try {
String response = HttpUtil.executeUrl("POST", DEVICE_URL + "/SetAta", getHeaderProperties(), data,
"application/json", TIMEOUT_MILLISECONDS);
logger.debug("Device status sending response: {}", response);
return gson.fromJson(response, DeviceStatus.class);
return Objects.requireNonNull(GSON.fromJson(response, DeviceStatus.class));
} catch (IOException | JsonSyntaxException e) {
setConnected(false);
throw new MelCloudCommException("Error occurred during device command sending", e);
@ -166,7 +169,7 @@ public class MelCloudConnection {
try {
String response = HttpUtil.executeUrl("GET", url, getHeaderProperties(), null, null, TIMEOUT_MILLISECONDS);
logger.debug("Device heatpump status response: {}", response);
return gson.fromJson(response, HeatpumpDeviceStatus.class);
return Objects.requireNonNull(GSON.fromJson(response, HeatpumpDeviceStatus.class));
} catch (IOException | JsonSyntaxException e) {
setConnected(false);
throw new MelCloudCommException("Error occurred during heatpump device status fetch", e);
@ -176,14 +179,14 @@ public class MelCloudConnection {
public HeatpumpDeviceStatus sendHeatpumpDeviceStatus(HeatpumpDeviceStatus heatpumpDeviceStatus)
throws MelCloudCommException {
assertConnected();
String content = gson.toJson(heatpumpDeviceStatus, HeatpumpDeviceStatus.class);
String content = GSON.toJson(heatpumpDeviceStatus, HeatpumpDeviceStatus.class);
logger.debug("Sending heatpump device status: {}", content);
InputStream data = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8));
try {
String response = HttpUtil.executeUrl("POST", DEVICE_URL + "/SetAtw", getHeaderProperties(), data,
"application/json", TIMEOUT_MILLISECONDS);
logger.debug("Device heatpump status sending response: {}", response);
return gson.fromJson(response, HeatpumpDeviceStatus.class);
return Objects.requireNonNull(GSON.fromJson(response, HeatpumpDeviceStatus.class));
} catch (IOException | JsonSyntaxException e) {
setConnected(false);
throw new MelCloudCommException("Error occurred during heatpump device command sending", e);

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.melcloud.internal.api.json;
package org.openhab.binding.melcloud.internal.api.dto;
import java.util.List;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.melcloud.internal.api.json;
package org.openhab.binding.melcloud.internal.api.dto;
import java.security.Permissions;
import java.util.List;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.melcloud.internal.api.json;
package org.openhab.binding.melcloud.internal.api.dto;
import java.util.List;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.melcloud.internal.api.json;
package org.openhab.binding.melcloud.internal.api.dto;
import java.util.List;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.melcloud.internal.api.json;
package org.openhab.binding.melcloud.internal.api.dto;
import java.util.List;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.melcloud.internal.api.json;
package org.openhab.binding.melcloud.internal.api.dto;
import java.util.List;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.melcloud.internal.api.json;
package org.openhab.binding.melcloud.internal.api.dto;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.melcloud.internal.api.json;
package org.openhab.binding.melcloud.internal.api.dto;
import java.util.List;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.melcloud.internal.api.json;
package org.openhab.binding.melcloud.internal.api.dto;
import com.google.gson.annotations.Expose;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.melcloud.internal.api.json;
package org.openhab.binding.melcloud.internal.api.dto;
import com.google.gson.annotations.Expose;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.melcloud.internal.api.json;
package org.openhab.binding.melcloud.internal.api.dto;
import com.google.gson.annotations.Expose;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.melcloud.internal.api.json;
package org.openhab.binding.melcloud.internal.api.dto;
import java.util.List;

View File

@ -10,7 +10,7 @@
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.binding.melcloud.internal.api.json;
package org.openhab.binding.melcloud.internal.api.dto;
import com.google.gson.annotations.Expose;

View File

@ -12,17 +12,21 @@
*/
package org.openhab.binding.melcloud.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* Config class for an A.C. device.
*
* @author Pauli Anttila - Initial Contribution
*
*/
@NonNullByDefault
public class AcDeviceConfig {
public Integer deviceID;
public Integer buildingID;
public Integer pollingInterval;
public Integer deviceID = 0;
public @Nullable Integer buildingID;
public Integer pollingInterval = 360;
@Override
public String toString() {

View File

@ -12,17 +12,20 @@
*/
package org.openhab.binding.melcloud.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Config class for MELCloud account parameters.
*
* @author Pauli Anttila - Initial Contribution
*
*/
@NonNullByDefault
public class AccountConfig {
public String username;
public String password;
public int language;
public String username = "";
public String password = "";
public int language = 0;
@Override
public String toString() {
@ -30,9 +33,6 @@ public class AccountConfig {
}
private String getPasswordForPrinting() {
if (password != null) {
return password.isEmpty() ? "<empty>" : "*********";
}
return "<null>";
return password.isEmpty() ? "<empty>" : "*********";
}
}

View File

@ -12,16 +12,20 @@
*/
package org.openhab.binding.melcloud.internal.config;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
/**
* Config class for a Heatpump device.
*
* @author Wietse van Buitenen - Initial Contribution
*
*/
@NonNullByDefault
public class HeatpumpDeviceConfig {
public Integer deviceID;
public Integer buildingID;
public Integer pollingInterval;
public Integer deviceID = 0;
public @Nullable Integer buildingID;
public Integer pollingInterval = 360;
@Override
public String toString() {

View File

@ -20,9 +20,10 @@ import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.melcloud.internal.MelCloudBindingConstants;
import org.openhab.binding.melcloud.internal.api.json.Device;
import org.openhab.binding.melcloud.internal.api.dto.Device;
import org.openhab.binding.melcloud.internal.exceptions.MelCloudCommException;
import org.openhab.binding.melcloud.internal.exceptions.MelCloudLoginException;
import org.openhab.binding.melcloud.internal.handler.MelCloudAccountHandler;
@ -43,14 +44,15 @@ import org.slf4j.LoggerFactory;
* @author Pauli Anttila - Refactoring
* @author Wietse van Buitenen - Check device type, added heatpump device
*/
@NonNullByDefault
@Component(scope = ServiceScope.PROTOTYPE, service = MelCloudDiscoveryService.class)
public class MelCloudDiscoveryService extends AbstractThingHandlerDiscoveryService<@NonNull MelCloudAccountHandler> {
public class MelCloudDiscoveryService extends AbstractThingHandlerDiscoveryService<MelCloudAccountHandler> {
private final Logger logger = LoggerFactory.getLogger(MelCloudDiscoveryService.class);
private static final String PROPERTY_DEVICE_ID = "deviceID";
private static final int DISCOVER_TIMEOUT_SECONDS = 10;
private ScheduledFuture<?> scanTask;
private @Nullable ScheduledFuture<?> scanTask;
/**
* Creates a MelCloudDiscoveryService with enabled autostart.
@ -67,7 +69,8 @@ public class MelCloudDiscoveryService extends AbstractThingHandlerDiscoveryServi
@Override
protected void startScan() {
if (this.scanTask != null) {
ScheduledFuture<?> scanTask = this.scanTask;
if (scanTask != null) {
scanTask.cancel(true);
}
this.scanTask = scheduler.schedule(() -> discoverDevices(), 0, TimeUnit.SECONDS);
@ -77,8 +80,9 @@ public class MelCloudDiscoveryService extends AbstractThingHandlerDiscoveryServi
protected void stopScan() {
super.stopScan();
if (this.scanTask != null) {
this.scanTask.cancel(true);
ScheduledFuture<?> scanTask = this.scanTask;
if (scanTask != null) {
scanTask.cancel(true);
this.scanTask = null;
}
}
@ -88,7 +92,7 @@ public class MelCloudDiscoveryService extends AbstractThingHandlerDiscoveryServi
try {
List<Device> deviceList = thingHandler.getDeviceList();
if (deviceList == null) {
if (deviceList.isEmpty()) {
logger.debug("No devices found");
} else {
ThingUID bridgeUID = thingHandler.getThing().getUID();

View File

@ -12,11 +12,14 @@
*/
package org.openhab.binding.melcloud.internal.exceptions;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Exception to encapsulate any issues communicating with MELCloud.
*
* @author Pauli Anttila - Initial Contribution
*/
@NonNullByDefault
public class MelCloudCommException extends Exception {
private static final long serialVersionUID = 1L;

View File

@ -12,11 +12,14 @@
*/
package org.openhab.binding.melcloud.internal.exceptions;
import org.eclipse.jdt.annotation.NonNullByDefault;
/**
* Exception to encapsulate any login issues with MELCloud.
*
* @author Pauli Anttila - Initial Contribution
*/
@NonNullByDefault
public class MelCloudLoginException extends Exception {
private static final long serialVersionUID = 1L;

View File

@ -15,15 +15,16 @@ package org.openhab.binding.melcloud.internal.handler;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.melcloud.internal.api.MelCloudConnection;
import org.openhab.binding.melcloud.internal.api.json.Device;
import org.openhab.binding.melcloud.internal.api.json.DeviceStatus;
import org.openhab.binding.melcloud.internal.api.json.HeatpumpDeviceStatus;
import org.openhab.binding.melcloud.internal.api.dto.Device;
import org.openhab.binding.melcloud.internal.api.dto.DeviceStatus;
import org.openhab.binding.melcloud.internal.api.dto.HeatpumpDeviceStatus;
import org.openhab.binding.melcloud.internal.config.AccountConfig;
import org.openhab.binding.melcloud.internal.discovery.MelCloudDiscoveryService;
import org.openhab.binding.melcloud.internal.exceptions.MelCloudCommException;
@ -47,14 +48,15 @@ import org.slf4j.LoggerFactory;
* @author Pauli Anttila - Refactoring
* @author Wietse van Buitenen - Return all devices, added heatpump device
*/
@NonNullByDefault
public class MelCloudAccountHandler extends BaseBridgeHandler {
private final Logger logger = LoggerFactory.getLogger(MelCloudAccountHandler.class);
private MelCloudConnection connection;
private List<Device> devices;
private ScheduledFuture<?> connectionCheckTask;
private AccountConfig config;
private boolean loginCredentialError;
private MelCloudConnection connection = new MelCloudConnection();
private List<Device> devices = Collections.emptyList();
private @Nullable ScheduledFuture<?> connectionCheckTask;
private AccountConfig config = new AccountConfig();
private boolean loginCredentialError = false;
public MelCloudAccountHandler(Bridge bridge) {
super(bridge);
@ -69,8 +71,6 @@ public class MelCloudAccountHandler extends BaseBridgeHandler {
public void initialize() {
logger.debug("Initializing MELCloud account handler.");
config = getConfigAs(AccountConfig.class);
connection = new MelCloudConnection();
devices = Collections.emptyList();
loginCredentialError = false;
startConnectionCheck();
}
@ -79,9 +79,7 @@ public class MelCloudAccountHandler extends BaseBridgeHandler {
public void dispose() {
logger.debug("Running dispose()");
stopConnectionCheck();
connection = null;
devices = Collections.emptyList();
config = null;
}
@Override
@ -102,6 +100,7 @@ public class MelCloudAccountHandler extends BaseBridgeHandler {
throw new MelCloudLoginException("Connection to MELCloud can't be opened because of wrong credentials");
}
logger.debug("Initializing connection to MELCloud");
updateStatus(ThingStatus.OFFLINE);
try {
connection.login(config.username, config.password, config.language);
@ -139,10 +138,10 @@ public class MelCloudAccountHandler extends BaseBridgeHandler {
}
}
public DeviceStatus fetchDeviceStatus(int deviceId, Optional<Integer> buildingId)
public DeviceStatus fetchDeviceStatus(int deviceId, @Nullable Integer buildingId)
throws MelCloudCommException, MelCloudLoginException {
connectIfNotConnected();
int bid = buildingId.orElse(findBuildingId(deviceId));
int bid = buildingId != null ? buildingId : findBuildingId(deviceId);
try {
return connection.fetchDeviceStatus(deviceId, bid);
@ -165,10 +164,10 @@ public class MelCloudAccountHandler extends BaseBridgeHandler {
}
}
public HeatpumpDeviceStatus fetchHeatpumpDeviceStatus(int deviceId, Optional<Integer> buildingId)
public HeatpumpDeviceStatus fetchHeatpumpDeviceStatus(int deviceId, @Nullable Integer buildingId)
throws MelCloudCommException, MelCloudLoginException {
connectIfNotConnected();
int bid = buildingId.orElse(findBuildingId(deviceId));
int bid = buildingId != null ? buildingId : findBuildingId(deviceId);
try {
return connection.fetchHeatpumpDeviceStatus(deviceId, bid);
@ -180,15 +179,13 @@ public class MelCloudAccountHandler extends BaseBridgeHandler {
}
private int findBuildingId(int deviceId) throws MelCloudCommException {
if (devices != null) {
return devices.stream().filter(d -> d.getDeviceID() == deviceId).findFirst().orElseThrow(
() -> new MelCloudCommException(String.format("Can't find building id for device id %s", deviceId)))
.getBuildingID();
}
throw new MelCloudCommException(String.format("Can't find building id for device id %s", deviceId));
return devices.stream().filter(d -> d.getDeviceID() == deviceId).findFirst().orElseThrow(
() -> new MelCloudCommException(String.format("Can't find building id for device id %s", deviceId)))
.getBuildingID();
}
private void startConnectionCheck() {
ScheduledFuture<?> connectionCheckTask = this.connectionCheckTask;
if (connectionCheckTask == null || connectionCheckTask.isCancelled()) {
logger.debug("Start periodic connection check");
Runnable runnable = () -> {
@ -207,17 +204,18 @@ public class MelCloudAccountHandler extends BaseBridgeHandler {
}
}
};
connectionCheckTask = scheduler.scheduleWithFixedDelay(runnable, 0, 300, TimeUnit.SECONDS);
this.connectionCheckTask = scheduler.scheduleWithFixedDelay(runnable, 0, 300, TimeUnit.SECONDS);
} else {
logger.debug("Connection check task already running");
}
}
private void stopConnectionCheck() {
ScheduledFuture<?> connectionCheckTask = this.connectionCheckTask;
if (connectionCheckTask != null) {
logger.debug("Stop periodic connection check");
connectionCheckTask.cancel(true);
connectionCheckTask = null;
this.connectionCheckTask = null;
}
}
}

View File

@ -22,13 +22,14 @@ import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Optional;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.measure.quantity.Temperature;
import org.openhab.binding.melcloud.internal.api.json.DeviceStatus;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.melcloud.internal.api.dto.DeviceStatus;
import org.openhab.binding.melcloud.internal.config.AcDeviceConfig;
import org.openhab.binding.melcloud.internal.exceptions.MelCloudCommException;
import org.openhab.binding.melcloud.internal.exceptions.MelCloudLoginException;
@ -46,6 +47,7 @@ import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.BridgeHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
@ -59,7 +61,7 @@ import org.slf4j.LoggerFactory;
* @author Luca Calcaterra - Initial contribution
* @author Pauli Anttila - Refactoring
*/
@NonNullByDefault
public class MelCloudDeviceHandler extends BaseThingHandler {
private static final int EFFECTIVE_FLAG_POWER = 0x01;
@ -70,10 +72,10 @@ public class MelCloudDeviceHandler extends BaseThingHandler {
private static final int EFFECTIVE_FLAG_VANE_HORIZONTAL = 0x100;
private final Logger logger = LoggerFactory.getLogger(MelCloudDeviceHandler.class);
private AcDeviceConfig config;
private MelCloudAccountHandler melCloudHandler;
private DeviceStatus deviceStatus;
private ScheduledFuture<?> refreshTask;
private AcDeviceConfig config = new AcDeviceConfig();
private @Nullable MelCloudAccountHandler melCloudHandler;
private @Nullable DeviceStatus deviceStatus;
private @Nullable ScheduledFuture<?> refreshTask;
public MelCloudDeviceHandler(Thing thing) {
super(thing);
@ -84,7 +86,7 @@ public class MelCloudDeviceHandler extends BaseThingHandler {
logger.debug("Initializing {} handler.", getThing().getThingTypeUID());
Bridge bridge = getBridge();
if (bridge == null) {
if (bridge == null || !(bridge.getHandler() instanceof BridgeHandler bridgeHandler)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Bridge Not set");
return;
}
@ -92,15 +94,16 @@ public class MelCloudDeviceHandler extends BaseThingHandler {
config = getConfigAs(AcDeviceConfig.class);
logger.debug("A.C. device config: {}", config);
initializeBridge(bridge.getHandler(), bridge.getStatus());
initializeBridge(bridgeHandler, bridge.getStatus());
}
@Override
public void dispose() {
logger.debug("Running dispose()");
ScheduledFuture<?> refreshTask = this.refreshTask;
if (refreshTask != null) {
refreshTask.cancel(true);
refreshTask = null;
this.refreshTask = null;
}
melCloudHandler = null;
}
@ -109,25 +112,21 @@ public class MelCloudDeviceHandler extends BaseThingHandler {
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
logger.debug("bridgeStatusChanged {} for thing {}", bridgeStatusInfo, getThing().getUID());
Bridge bridge = getBridge();
if (bridge != null) {
initializeBridge(bridge.getHandler(), bridgeStatusInfo.getStatus());
if (bridge != null && bridge.getHandler() instanceof BridgeHandler bridgeHandler) {
initializeBridge(bridgeHandler, bridgeStatusInfo.getStatus());
}
}
private void initializeBridge(ThingHandler thingHandler, ThingStatus bridgeStatus) {
logger.debug("initializeBridge {} for thing {}", bridgeStatus, getThing().getUID());
if (thingHandler != null && bridgeStatus != null) {
melCloudHandler = (MelCloudAccountHandler) thingHandler;
melCloudHandler = (MelCloudAccountHandler) thingHandler;
if (bridgeStatus == ThingStatus.ONLINE) {
updateStatus(ThingStatus.ONLINE);
startAutomaticRefresh();
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
}
if (bridgeStatus == ThingStatus.ONLINE) {
updateStatus(ThingStatus.ONLINE);
startAutomaticRefresh();
} else {
updateStatus(ThingStatus.OFFLINE);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
}
}
@ -139,12 +138,12 @@ public class MelCloudDeviceHandler extends BaseThingHandler {
logger.debug("Refresh command not supported");
return;
}
MelCloudAccountHandler melCloudHandler = this.melCloudHandler;
if (melCloudHandler == null) {
logger.warn("No connection to MELCloud available, ignoring command");
return;
}
DeviceStatus deviceStatus = this.deviceStatus;
if (deviceStatus == null) {
logger.info("No initial data available, bridge is probably offline. Ignoring command");
return;
@ -229,18 +228,19 @@ public class MelCloudDeviceHandler extends BaseThingHandler {
}
private void startAutomaticRefresh() {
ScheduledFuture<?> refreshTask = this.refreshTask;
if (refreshTask == null || refreshTask.isCancelled()) {
refreshTask = scheduler.scheduleWithFixedDelay(this::getDeviceDataAndUpdateChannels, 1,
this.refreshTask = scheduler.scheduleWithFixedDelay(this::getDeviceDataAndUpdateChannels, 1,
config.pollingInterval, TimeUnit.SECONDS);
}
}
private void getDeviceDataAndUpdateChannels() {
if (melCloudHandler.isConnected()) {
MelCloudAccountHandler melCloudHandler = this.melCloudHandler;
if (melCloudHandler != null && melCloudHandler.isConnected()) {
logger.debug("Update device '{}' channels", getThing().getThingTypeUID());
try {
DeviceStatus newDeviceStatus = melCloudHandler.fetchDeviceStatus(config.deviceID,
Optional.ofNullable(config.buildingID));
DeviceStatus newDeviceStatus = melCloudHandler.fetchDeviceStatus(config.deviceID, config.buildingID);
updateChannels(newDeviceStatus);
} catch (MelCloudLoginException e) {
logger.debug("Login error occurred during device '{}' polling, reason {}. ",
@ -255,7 +255,7 @@ public class MelCloudDeviceHandler extends BaseThingHandler {
}
private synchronized void updateChannels(DeviceStatus newDeviceStatus) {
deviceStatus = newDeviceStatus;
DeviceStatus deviceStatus = this.deviceStatus = newDeviceStatus;
for (Channel channel : getThing().getChannels()) {
updateChannels(channel.getUID().getId(), deviceStatus);
}

View File

@ -22,13 +22,14 @@ import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Optional;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.measure.quantity.Temperature;
import org.openhab.binding.melcloud.internal.api.json.HeatpumpDeviceStatus;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.melcloud.internal.api.dto.HeatpumpDeviceStatus;
import org.openhab.binding.melcloud.internal.config.HeatpumpDeviceConfig;
import org.openhab.binding.melcloud.internal.exceptions.MelCloudCommException;
import org.openhab.binding.melcloud.internal.exceptions.MelCloudLoginException;
@ -45,6 +46,7 @@ import org.openhab.core.thing.ThingStatus;
import org.openhab.core.thing.ThingStatusDetail;
import org.openhab.core.thing.ThingStatusInfo;
import org.openhab.core.thing.binding.BaseThingHandler;
import org.openhab.core.thing.binding.BridgeHandler;
import org.openhab.core.thing.binding.ThingHandler;
import org.openhab.core.types.Command;
import org.openhab.core.types.RefreshType;
@ -57,16 +59,17 @@ import org.slf4j.LoggerFactory;
*
* @author Wietse van Buitenen - Initial contribution
*/
@NonNullByDefault
public class MelCloudHeatpumpDeviceHandler extends BaseThingHandler {
private static final long EFFECTIVE_FLAG_POWER = 1L;
private static final long EFFECTIVE_FLAG_TEMPERATURE_ZONE1 = 8589934720L;
private static final long EFFECTIVE_FLAG_HOTWATER = 65536L;
private final Logger logger = LoggerFactory.getLogger(MelCloudHeatpumpDeviceHandler.class);
private HeatpumpDeviceConfig config;
private MelCloudAccountHandler melCloudHandler;
private HeatpumpDeviceStatus heatpumpDeviceStatus;
private ScheduledFuture<?> refreshTask;
private HeatpumpDeviceConfig config = new HeatpumpDeviceConfig();
private @Nullable MelCloudAccountHandler melCloudHandler;
private @Nullable HeatpumpDeviceStatus heatpumpDeviceStatus;
private @Nullable ScheduledFuture<?> refreshTask;
public MelCloudHeatpumpDeviceHandler(Thing thing) {
super(thing);
@ -77,7 +80,7 @@ public class MelCloudHeatpumpDeviceHandler extends BaseThingHandler {
logger.debug("Initializing {} handler.", getThing().getThingTypeUID());
Bridge bridge = getBridge();
if (bridge == null) {
if (bridge == null || !(bridge.getHandler() instanceof BridgeHandler bridgeHandler)) {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Bridge Not set");
return;
}
@ -85,15 +88,16 @@ public class MelCloudHeatpumpDeviceHandler extends BaseThingHandler {
config = getConfigAs(HeatpumpDeviceConfig.class);
logger.debug("Heatpump device config: {}", config);
initializeBridge(bridge.getHandler(), bridge.getStatus());
initializeBridge(bridgeHandler, bridge.getStatus());
}
@Override
public void dispose() {
logger.debug("Running dispose()");
ScheduledFuture<?> refreshTask = this.refreshTask;
if (refreshTask != null) {
refreshTask.cancel(true);
refreshTask = null;
this.refreshTask = null;
}
melCloudHandler = null;
}
@ -102,25 +106,21 @@ public class MelCloudHeatpumpDeviceHandler extends BaseThingHandler {
public void bridgeStatusChanged(ThingStatusInfo bridgeStatusInfo) {
logger.debug("bridgeStatusChanged {} for thing {}", bridgeStatusInfo, getThing().getUID());
Bridge bridge = getBridge();
if (bridge != null) {
initializeBridge(bridge.getHandler(), bridgeStatusInfo.getStatus());
if (bridge != null && bridge.getHandler() instanceof BridgeHandler bridgeHandler) {
initializeBridge(bridgeHandler, bridgeStatusInfo.getStatus());
}
}
private void initializeBridge(ThingHandler thingHandler, ThingStatus bridgeStatus) {
logger.debug("initializeBridge {} for thing {}", bridgeStatus, getThing().getUID());
if (thingHandler != null && bridgeStatus != null) {
melCloudHandler = (MelCloudAccountHandler) thingHandler;
melCloudHandler = (MelCloudAccountHandler) thingHandler;
if (bridgeStatus == ThingStatus.ONLINE) {
updateStatus(ThingStatus.ONLINE);
startAutomaticRefresh();
} else {
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
}
if (bridgeStatus == ThingStatus.ONLINE) {
updateStatus(ThingStatus.ONLINE);
startAutomaticRefresh();
} else {
updateStatus(ThingStatus.OFFLINE);
updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.BRIDGE_OFFLINE);
}
}
@ -133,11 +133,12 @@ public class MelCloudHeatpumpDeviceHandler extends BaseThingHandler {
return;
}
MelCloudAccountHandler melCloudHandler = this.melCloudHandler;
if (melCloudHandler == null) {
logger.warn("No connection to MELCloud available, ignoring command");
return;
}
HeatpumpDeviceStatus heatpumpDeviceStatus = this.heatpumpDeviceStatus;
if (heatpumpDeviceStatus == null) {
logger.info("No initial data available, bridge is probably offline. Ignoring command");
return;
@ -207,18 +208,20 @@ public class MelCloudHeatpumpDeviceHandler extends BaseThingHandler {
}
private void startAutomaticRefresh() {
ScheduledFuture<?> refreshTask = this.refreshTask;
if (refreshTask == null || refreshTask.isCancelled()) {
refreshTask = scheduler.scheduleWithFixedDelay(this::getDeviceDataAndUpdateChannels, 1,
this.refreshTask = scheduler.scheduleWithFixedDelay(this::getDeviceDataAndUpdateChannels, 1,
config.pollingInterval, TimeUnit.SECONDS);
}
}
private void getDeviceDataAndUpdateChannels() {
if (melCloudHandler.isConnected()) {
MelCloudAccountHandler melCloudHandler = this.melCloudHandler;
if (melCloudHandler != null && melCloudHandler.isConnected()) {
logger.debug("Update device '{}' channels", getThing().getThingTypeUID());
try {
HeatpumpDeviceStatus newHeatpumpDeviceStatus = melCloudHandler
.fetchHeatpumpDeviceStatus(config.deviceID, Optional.ofNullable(config.buildingID));
.fetchHeatpumpDeviceStatus(config.deviceID, config.buildingID);
updateChannels(newHeatpumpDeviceStatus);
} catch (MelCloudLoginException e) {
logger.debug("Login error occurred during device '{}' polling, reason {}. ",
@ -233,7 +236,7 @@ public class MelCloudHeatpumpDeviceHandler extends BaseThingHandler {
}
private synchronized void updateChannels(HeatpumpDeviceStatus newHeatpumpDeviceStatus) {
heatpumpDeviceStatus = newHeatpumpDeviceStatus;
HeatpumpDeviceStatus heatpumpDeviceStatus = this.heatpumpDeviceStatus = newHeatpumpDeviceStatus;
for (Channel channel : getThing().getChannels()) {
updateChannels(channel.getUID().getId(), heatpumpDeviceStatus);
}