]> git.basschouten.com Git - openhab-addons.git/commitdiff
[innogysmarthome] NullPointerException fixed which could occur when no device state...
authorSven Strohschein <novanic@gmx.de>
Fri, 8 Jan 2021 22:07:38 +0000 (23:07 +0100)
committerGitHub <noreply@github.com>
Fri, 8 Jan 2021 22:07:38 +0000 (23:07 +0100)
* [innogysmarthome] Bug-fix - NPE solved which could occur when devices without a corresponding device state were returned by the API. That happened with the virtual device "NotificationSender" which is unimportant for the binding.

Signed-off-by: Sven Strohschein <sven.strohschein@gmail.com>
* [innogysmarthome] JavaDoc fixed

Signed-off-by: Sven Strohschein <sven.strohschein@gmail.com>
* [innogysmarthome] Code optimizations (warnings fixed)

Signed-off-by: Sven Strohschein <sven.strohschein@gmail.com>
* [innogysmarthome] Code refactoring (InnogyClient class split to make it smaller)

Signed-off-by: Sven Strohschein <sven.strohschein@gmail.com>
* [innogysmarthome] Code optimization

Signed-off-by: Sven Strohschein <sven.strohschein@gmail.com>
* [innogysmarthome] Code optimization

Signed-off-by: Sven Strohschein <sven.strohschein@gmail.com>
* [innogysmarthome] Code optimization

Signed-off-by: Sven Strohschein <sven.strohschein@gmail.com>
* [innogysmarthome] Code optimization

Signed-off-by: Sven Strohschein <sven.strohschein@gmail.com>
* [innogysmarthome] Code optimization ; Potential bug with the reachable attribute solved (it was set multiple times within a loop)

Signed-off-by: Sven Strohschein <sven.strohschein@gmail.com>
* [innogysmarthome] Code optimization

Signed-off-by: Sven Strohschein <sven.strohschein@gmail.com>
* [innogysmarthome] Code optimization

Signed-off-by: Sven Strohschein <sven.strohschein@gmail.com>
* [innogysmarthome] Code optimization

Signed-off-by: Sven Strohschein <sven.strohschein@gmail.com>
* [innogysmarthome] Code optimization

Signed-off-by: Sven Strohschein <sven.strohschein@gmail.com>
* [innogysmarthome] Code optimization

Signed-off-by: Sven Strohschein <sven.strohschein@gmail.com>
* [innogysmarthome] Code optimization

Signed-off-by: Sven Strohschein <sven.strohschein@gmail.com>
* [innogysmarthome] Tests added

Signed-off-by: Sven Strohschein <sven.strohschein@gmail.com>
* [innogysmarthome] Tests added

Signed-off-by: Sven Strohschein <sven.strohschein@gmail.com>
* [innogysmarthome] Code changed to not overwrite the reachable state provided by the API (when no corresponding message is available)

Signed-off-by: Sven Strohschein <sven.strohschein@gmail.com>
* [innogysmarthome] Copyright notice updated

Signed-off-by: Sven Strohschein <sven.strohschein@gmail.com>
* [innogysmarthome] Code and performance optimization

Signed-off-by: Sven Strohschein <sven.strohschein@gmail.com>
Co-authored-by: Sven Strohschein <sven.strohschein@gmail.com>
bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/InnogyClient.java
bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/client/entity/device/Device.java
bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyBridgeHandler.java
bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyDeviceHandler.java
bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/manager/DeviceStructureManager.java
bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/manager/FullDeviceManager.java [new file with mode: 0644]
bundles/org.openhab.binding.innogysmarthome/src/test/java/org/openhab/binding/innogysmarthome/internal/client/entity/device/DeviceTest.java [new file with mode: 0644]
bundles/org.openhab.binding.innogysmarthome/src/test/java/org/openhab/binding/innogysmarthome/internal/handler/InnogyBridgeHandlerTest.java

index d319e2bfad9a04b13e8dfcdd4e25ec795b802a30..3a841962a3270c7cac08760f1d771d5b0e176d1b 100644 (file)
  */
 package org.openhab.binding.innogysmarthome.internal.client;
 
-import static org.openhab.binding.innogysmarthome.internal.InnogyBindingConstants.*;
 import static org.openhab.binding.innogysmarthome.internal.client.Constants.*;
 
 import java.io.IOException;
 import java.net.URI;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+import java.util.stream.Collectors;
 
 import org.apache.commons.lang.StringUtils;
 import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -36,7 +32,6 @@ import org.eclipse.jetty.client.util.StringContentProvider;
 import org.eclipse.jetty.http.HttpHeader;
 import org.eclipse.jetty.http.HttpMethod;
 import org.eclipse.jetty.http.HttpStatus;
-import org.openhab.binding.innogysmarthome.internal.InnogyBindingConstants;
 import org.openhab.binding.innogysmarthome.internal.client.entity.StatusResponse;
 import org.openhab.binding.innogysmarthome.internal.client.entity.action.Action;
 import org.openhab.binding.innogysmarthome.internal.client.entity.action.ShutterAction;
@@ -48,7 +43,6 @@ import org.openhab.binding.innogysmarthome.internal.client.entity.device.DeviceS
 import org.openhab.binding.innogysmarthome.internal.client.entity.device.Gateway;
 import org.openhab.binding.innogysmarthome.internal.client.entity.device.State;
 import org.openhab.binding.innogysmarthome.internal.client.entity.error.ErrorResponse;
-import org.openhab.binding.innogysmarthome.internal.client.entity.link.Link;
 import org.openhab.binding.innogysmarthome.internal.client.entity.location.Location;
 import org.openhab.binding.innogysmarthome.internal.client.entity.message.Message;
 import org.openhab.binding.innogysmarthome.internal.client.exception.ApiException;
@@ -103,13 +97,6 @@ public class InnogyClient {
         this.httpClient = httpClient;
     }
 
-    /**
-     * @return the bridgeInfo
-     */
-    public @Nullable Gateway getBridgeDetails() {
-        return bridgeDetails;
-    }
-
     /**
      * Gets the status
      *
@@ -117,8 +104,6 @@ public class InnogyClient {
      * the {@link #configVersion} is set.
      *
      * @throws SessionExistsException thrown, if a session already exists
-     * @throws IOException
-     * @throws ApiException
      */
     public void refreshStatus() throws IOException, ApiException, AuthenticationException {
         logger.debug("Get innogy SmartHome status...");
@@ -133,12 +118,9 @@ public class InnogyClient {
     /**
      * Executes a HTTP GET request with default headers and returns data as object of type T.
      *
-     * @param url
+     * @param url request URL
      * @param clazz type of data to return
-     * @return
-     * @throws IOException
-     * @throws AuthenticationException
-     * @throws ApiException
+     * @return response content
      */
     private <T> T executeGet(final String url, final Class<T> clazz)
             throws IOException, AuthenticationException, ApiException {
@@ -150,11 +132,9 @@ public class InnogyClient {
     /**
      * Executes a HTTP GET request with default headers and returns data as List of type T.
      *
-     * @param url
+     * @param url request URL
      * @param clazz array type of data to return as list
-     * @throws IOException
-     * @throws AuthenticationException
-     * @throws ApiException
+     * @return response content (as a List)
      */
     private <T> List<T> executeGetList(final String url, final Class<T[]> clazz)
             throws IOException, AuthenticationException, ApiException {
@@ -164,19 +144,15 @@ public class InnogyClient {
     /**
      * Executes a HTTP POST request with the given {@link Action} as content.
      *
-     * @param url
-     * @param action
-     * @return
-     * @throws IOException
-     * @throws AuthenticationException
-     * @throws ApiException
+     * @param url request URL
+     * @param action action to execute
      */
-    private ContentResponse executePost(final String url, final Action action)
+    private void executePost(final String url, final Action action)
             throws IOException, AuthenticationException, ApiException {
         final String json = gson.toJson(action);
         logger.debug("Action {} JSON: {}", action.getType(), json);
 
-        return request(httpClient.newRequest(url).method(HttpMethod.POST)
+        request(httpClient.newRequest(url).method(HttpMethod.POST)
                 .content(new StringContentProvider(json), CONTENT_TYPE).accept(CONTENT_TYPE));
     }
 
@@ -197,6 +173,7 @@ public class InnogyClient {
     }
 
     public AccessTokenResponse getAccessTokenResponse() throws AuthenticationException, IOException {
+        @Nullable
         final AccessTokenResponse accessTokenResponse;
         try {
             accessTokenResponse = oAuthService.getAccessTokenResponse();
@@ -212,17 +189,11 @@ public class InnogyClient {
     /**
      * Handles errors from the {@link ContentResponse} and throws the following errors:
      *
-     * @param response
+     * @param response response
      * @param uri uri of api call made
-     * @throws SessionExistsException
-     * @throws SessionNotFoundException
      * @throws ControllerOfflineException thrown, if the innogy SmartHome controller (SHC) is offline.
-     * @throws IOException
-     * @throws ApiException
-     * @throws AuthenticationException
      */
-    private void handleResponseErrors(final ContentResponse response, final URI uri)
-            throws IOException, ApiException, AuthenticationException {
+    private void handleResponseErrors(final ContentResponse response, final URI uri) throws IOException, ApiException {
         String content = "";
 
         switch (response.getStatus()) {
@@ -275,11 +246,6 @@ public class InnogyClient {
 
     /**
      * Sets a new state of a SwitchActuator.
-     *
-     * @param capabilityId
-     * @param state
-     * @throws IOException
-     * @throws ApiException
      */
     public void setSwitchActuatorState(final String capabilityId, final boolean state)
             throws IOException, ApiException, AuthenticationException {
@@ -288,11 +254,6 @@ public class InnogyClient {
 
     /**
      * Sets the dimmer level of a DimmerActuator.
-     *
-     * @param capabilityId
-     * @param dimLevel
-     * @throws IOException
-     * @throws ApiException
      */
     public void setDimmerActuatorState(final String capabilityId, final int dimLevel)
             throws IOException, ApiException, AuthenticationException {
@@ -301,12 +262,6 @@ public class InnogyClient {
 
     /**
      * Sets the roller shutter level of a RollerShutterActuator.
-     *
-     * @param capabilityId
-     * @param rollerShutterLevel
-     * @throws IOException
-     * @throws ApiException
-     * @throws AuthenticationException
      */
     public void setRollerShutterActuatorState(final String capabilityId, final int rollerShutterLevel)
             throws IOException, ApiException, AuthenticationException {
@@ -316,12 +271,6 @@ public class InnogyClient {
 
     /**
      * Starts or stops moving a RollerShutterActuator
-     *
-     * @param capabilityId
-     * @param rollerShutterAction
-     * @throws IOException
-     * @throws ApiException
-     * @throws AuthenticationException
      */
     public void setRollerShutterAction(final String capabilityId,
             final ShutterAction.ShutterActions rollerShutterAction)
@@ -331,11 +280,6 @@ public class InnogyClient {
 
     /**
      * Sets a new state of a VariableActuator.
-     *
-     * @param capabilityId
-     * @param state
-     * @throws IOException
-     * @throws ApiException
      */
     public void setVariableActuatorState(final String capabilityId, final boolean state)
             throws IOException, ApiException, AuthenticationException {
@@ -344,11 +288,6 @@ public class InnogyClient {
 
     /**
      * Sets the point temperature.
-     *
-     * @param capabilityId
-     * @param pointTemperature
-     * @throws IOException
-     * @throws ApiException
      */
     public void setPointTemperatureState(final String capabilityId, final double pointTemperature)
             throws IOException, ApiException, AuthenticationException {
@@ -358,11 +297,6 @@ public class InnogyClient {
 
     /**
      * Sets the operation mode to "Auto" or "Manu".
-     *
-     * @param capabilityId
-     * @param autoMode
-     * @throws IOException
-     * @throws ApiException
      */
     public void setOperationMode(final String capabilityId, final boolean autoMode)
             throws IOException, ApiException, AuthenticationException {
@@ -374,11 +308,6 @@ public class InnogyClient {
 
     /**
      * Sets the alarm state.
-     *
-     * @param capabilityId
-     * @param alarmState
-     * @throws IOException
-     * @throws ApiException
      */
     public void setAlarmActuatorState(final String capabilityId, final boolean alarmState)
             throws IOException, ApiException, AuthenticationException {
@@ -388,222 +317,26 @@ public class InnogyClient {
     /**
      * Load the device and returns a {@link List} of {@link Device}s..
      *
+     * @param deviceIds Ids of the devices to return
      * @return List of Devices
-     * @throws IOException
-     * @throws ApiException
      */
-    public List<Device> getDevices() throws IOException, ApiException, AuthenticationException {
+    public List<Device> getDevices(Collection<String> deviceIds)
+            throws IOException, ApiException, AuthenticationException {
         logger.debug("Loading innogy devices...");
-        return executeGetList(API_URL_DEVICE, Device[].class);
+        List<Device> devices = executeGetList(API_URL_DEVICE, Device[].class);
+        return devices.stream().filter(d -> deviceIds.contains(d.getId())).collect(Collectors.toList());
     }
 
     /**
      * Loads the {@link Device} with the given deviceId.
-     *
-     * @param deviceId
-     * @return
-     * @throws IOException
-     * @throws ApiException
      */
     public Device getDeviceById(final String deviceId) throws IOException, ApiException, AuthenticationException {
         logger.debug("Loading device with id {}...", deviceId);
         return executeGet(API_URL_DEVICE_ID.replace("{id}", deviceId), Device.class);
     }
 
-    /**
-     * Returns a {@link List} of all {@link Device}s with the full configuration details, {@link Capability}s and
-     * states. Calling this may take a while...
-     *
-     * @return
-     * @throws IOException
-     * @throws ApiException
-     */
-    public List<Device> getFullDevices() throws IOException, ApiException, AuthenticationException {
-        // LOCATIONS
-        final List<Location> locationList = getLocations();
-        final Map<String, Location> locationMap = new HashMap<>();
-        for (final Location l : locationList) {
-            locationMap.put(l.getId(), l);
-        }
-
-        // CAPABILITIES
-        final List<Capability> capabilityList = getCapabilities();
-        final Map<String, Capability> capabilityMap = new HashMap<>();
-        for (final Capability c : capabilityList) {
-            capabilityMap.put(c.getId(), c);
-        }
-
-        // CAPABILITY STATES
-        final List<CapabilityState> capabilityStateList = getCapabilityStates();
-        final Map<String, CapabilityState> capabilityStateMap = new HashMap<>();
-        for (final CapabilityState cs : capabilityStateList) {
-            capabilityStateMap.put(cs.getId(), cs);
-        }
-
-        // DEVICE STATES
-        final List<DeviceState> deviceStateList = getDeviceStates();
-        final Map<String, DeviceState> deviceStateMap = new HashMap<>();
-        for (final DeviceState es : deviceStateList) {
-            deviceStateMap.put(es.getId(), es);
-        }
-
-        // MESSAGES
-        final List<Message> messageList = getMessages();
-        final Map<String, List<Message>> deviceMessageMap = new HashMap<>();
-        for (final Message m : messageList) {
-            if (m.getDevices() != null && !m.getDevices().isEmpty()) {
-                final String deviceId = m.getDevices().get(0).replace("/device/", "");
-                List<Message> ml;
-                if (deviceMessageMap.containsKey(deviceId)) {
-                    ml = deviceMessageMap.get(deviceId);
-                } else {
-                    ml = new ArrayList<>();
-                }
-                ml.add(m);
-                deviceMessageMap.put(deviceId, ml);
-            }
-        }
-
-        // DEVICES
-        final List<Device> deviceList = getDevices();
-        for (final Device d : deviceList) {
-            if (InnogyBindingConstants.BATTERY_POWERED_DEVICES.contains(d.getType())) {
-                d.setIsBatteryPowered(true);
-            }
-
-            // location
-            d.setLocation(locationMap.get(d.getLocationId()));
-            final HashMap<String, Capability> deviceCapabilityMap = new HashMap<>();
-
-            // capabilities and their states
-            for (final String cl : d.getCapabilityLinkList()) {
-                final Capability c = capabilityMap.get(Link.getId(cl));
-                final String capabilityId = c.getId();
-                final CapabilityState capabilityState = capabilityStateMap.get(capabilityId);
-                c.setCapabilityState(capabilityState);
-                deviceCapabilityMap.put(capabilityId, c);
-            }
-            d.setCapabilityMap(deviceCapabilityMap);
-
-            // device states
-            d.setDeviceState(deviceStateMap.get(d.getId()));
-
-            // messages
-            if (deviceMessageMap.containsKey(d.getId())) {
-                d.setMessageList(deviceMessageMap.get(d.getId()));
-                for (final Message m : d.getMessageList()) {
-                    switch (m.getType()) {
-                        case Message.TYPE_DEVICE_LOW_BATTERY:
-                            d.setLowBattery(true);
-                            d.setLowBatteryMessageId(m.getId());
-                            break;
-                    }
-                }
-            }
-        }
-
-        return deviceList;
-    }
-
-    /**
-     * Returns the {@link Device} with the given deviceId with full configuration details, {@link Capability}s and
-     * states. Calling this may take a little bit longer...
-     *
-     * @param deviceId
-     * @return
-     * @throws IOException
-     * @throws ApiException
-     */
-    public Device getFullDeviceById(final String deviceId) throws IOException, ApiException, AuthenticationException {
-        // LOCATIONS
-        final List<Location> locationList = getLocations();
-        final Map<String, Location> locationMap = new HashMap<>();
-        for (final Location l : locationList) {
-            locationMap.put(l.getId(), l);
-        }
-
-        // CAPABILITIES FOR DEVICE
-        final List<Capability> capabilityList = getCapabilitiesForDevice(deviceId);
-        final Map<String, Capability> capabilityMap = new HashMap<>();
-        for (final Capability c : capabilityList) {
-            capabilityMap.put(c.getId(), c);
-        }
-
-        // CAPABILITY STATES
-        final List<CapabilityState> capabilityStateList = getCapabilityStates();
-        final Map<String, CapabilityState> capabilityStateMap = new HashMap<>();
-        for (final CapabilityState cs : capabilityStateList) {
-            capabilityStateMap.put(cs.getId(), cs);
-        }
-
-        // DEVICE STATE
-        final State state = getDeviceStateByDeviceId(deviceId);
-        final DeviceState deviceState = new DeviceState();
-        deviceState.setId(deviceId);
-        deviceState.setState(state);
-
-        // MESSAGES
-        final List<Message> messageList = getMessages();
-        final List<Message> ml = new ArrayList<>();
-        final String deviceIdPath = "/device/" + deviceId;
-
-        for (final Message m : messageList) {
-            logger.trace("Message Type {} with ID {}", m.getType(), m.getId());
-            if (m.getDevices() != null && !m.getDevices().isEmpty()) {
-                for (final String li : m.getDevices()) {
-                    if (deviceIdPath.equals(li)) {
-                        ml.add(m);
-                    }
-                }
-            }
-        }
-
-        // DEVICE
-        final Device d = getDeviceById(deviceId);
-        if (BATTERY_POWERED_DEVICES.contains(d.getType())) {
-            d.setIsBatteryPowered(true);
-            d.setLowBattery(false);
-        }
-
-        // location
-        d.setLocation(locationMap.get(d.getLocationId()));
-
-        // capabilities and their states
-        final HashMap<String, Capability> deviceCapabilityMap = new HashMap<>();
-        for (final String cl : d.getCapabilityLinkList()) {
-
-            final Capability c = capabilityMap.get(Link.getId(cl));
-            c.setCapabilityState(capabilityStateMap.get(c.getId()));
-            deviceCapabilityMap.put(c.getId(), c);
-
-        }
-        d.setCapabilityMap(deviceCapabilityMap);
-
-        // device states
-        d.setDeviceState(deviceState);
-
-        // messages
-        if (!ml.isEmpty()) {
-            d.setMessageList(ml);
-            for (final Message m : d.getMessageList()) {
-                switch (m.getType()) {
-                    case Message.TYPE_DEVICE_LOW_BATTERY:
-                        d.setLowBattery(true);
-                        d.setLowBatteryMessageId(m.getId());
-                        break;
-                }
-            }
-        }
-
-        return d;
-    }
-
     /**
      * Loads the states for all {@link Device}s.
-     *
-     * @return
-     * @throws IOException
-     * @throws ApiException
      */
     public List<DeviceState> getDeviceStates() throws IOException, ApiException, AuthenticationException {
         logger.debug("Loading device states...");
@@ -612,11 +345,6 @@ public class InnogyClient {
 
     /**
      * Loads the device state for the given deviceId.
-     *
-     * @param deviceId
-     * @return
-     * @throws IOException
-     * @throws ApiException
      */
     public State getDeviceStateByDeviceId(final String deviceId)
             throws IOException, ApiException, AuthenticationException {
@@ -628,8 +356,6 @@ public class InnogyClient {
      * Loads the locations and returns a {@link List} of {@link Location}s.
      *
      * @return a List of Devices
-     * @throws IOException
-     * @throws ApiException
      */
     public List<Location> getLocations() throws IOException, ApiException, AuthenticationException {
         logger.debug("Loading locations...");
@@ -640,9 +366,7 @@ public class InnogyClient {
      * Loads and returns a {@link List} of {@link Capability}s for the given deviceId.
      *
      * @param deviceId the id of the {@link Device}
-     * @return
-     * @throws IOException
-     * @throws ApiException
+     * @return capabilities of the device
      */
     public List<Capability> getCapabilitiesForDevice(final String deviceId)
             throws IOException, ApiException, AuthenticationException {
@@ -652,10 +376,6 @@ public class InnogyClient {
 
     /**
      * Loads and returns a {@link List} of all {@link Capability}s.
-     *
-     * @return
-     * @throws IOException
-     * @throws ApiException
      */
     public List<Capability> getCapabilities() throws IOException, ApiException, AuthenticationException {
         logger.debug("Loading capabilities...");
@@ -664,10 +384,6 @@ public class InnogyClient {
 
     /**
      * Loads and returns a {@link List} of all {@link Capability}States.
-     *
-     * @return
-     * @throws IOException
-     * @throws ApiException
      */
     public List<CapabilityState> getCapabilityStates() throws IOException, ApiException, AuthenticationException {
         logger.debug("Loading capability states...");
@@ -676,10 +392,6 @@ public class InnogyClient {
 
     /**
      * Returns a {@link List} of all {@link Message}s.
-     *
-     * @return
-     * @throws IOException
-     * @throws ApiException
      */
     public List<Message> getMessages() throws IOException, ApiException, AuthenticationException {
         logger.debug("Loading messages...");
@@ -692,11 +404,4 @@ public class InnogyClient {
     public String getConfigVersion() {
         return configVersion;
     }
-
-    /**
-     * @param configVersion the configVersion to set
-     */
-    public void setConfigVersion(final String configVersion) {
-        this.configVersion = configVersion;
-    }
 }
index 46bee1d5eb8511e3e4199b583e99951998afbc0b..8a0f2014e111f055796cfa4f4465be160b2d0008 100644 (file)
@@ -14,9 +14,7 @@ package org.openhab.binding.innogysmarthome.internal.client.entity.device;
 
 import static org.openhab.binding.innogysmarthome.internal.InnogyBindingConstants.*;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
+import java.util.*;
 
 import org.openhab.binding.innogysmarthome.internal.client.entity.capability.Capability;
 import org.openhab.binding.innogysmarthome.internal.client.entity.location.Location;
@@ -38,8 +36,6 @@ public class Device {
     protected static final String PROTOCOL_ID_VIRTUAL = "Virtual";
     protected static final String PROTOCOL_ID_WMBUS = "wMBus";
 
-    public static final List<String> EMPTY_CAPABILITY_LINK_LIST = new ArrayList<>();
-
     /**
      * Unique id for the device, always available in model.
      */
@@ -82,15 +78,9 @@ public class Device {
 
     private DeviceConfig config;
 
-    /**
-     * Contains a list of the device capabilities.
-     *
-     * Optional.
-     */
-    @SerializedName("capabilities")
-    private List<String> capabilityLinkList;
+    private List<String> capabilities;
 
-    private HashMap<String, Capability> capabilityMap;
+    private Map<String, Capability> capabilityMap;
 
     private DeviceState deviceState;
 
@@ -115,12 +105,6 @@ public class Device {
     private List<Message> messageList;
 
     private boolean lowBattery;
-    /**
-     * Stores the message id, that contains the low battery state. This is needed to identify the device, when the
-     * message
-     * with that id is deleted (thus low battery state is false again).
-     */
-    private String lowBatteryMessageId;
 
     /**
      * Stores, if the {@link Device} is battery powered.
@@ -263,32 +247,28 @@ public class Device {
     /**
      * @return the capabilityList
      */
-    public List<String> getCapabilityLinkList() {
-        if (capabilityLinkList != null) {
-            return capabilityLinkList;
-        } else {
-            return EMPTY_CAPABILITY_LINK_LIST;
-        }
+    public List<String> getCapabilities() {
+        return Objects.requireNonNullElse(capabilities, Collections.emptyList());
     }
 
     /**
      * @param capabilityList the capabilityList to set
      */
-    public void setCapabilityList(List<String> capabilityList) {
-        this.capabilityLinkList = capabilityList;
+    public void setCapabilities(List<String> capabilityList) {
+        this.capabilities = capabilityList;
     }
 
     /**
      * @param capabilityMap the capabilityMap to set
      */
-    public void setCapabilityMap(HashMap<String, Capability> capabilityMap) {
+    public void setCapabilityMap(Map<String, Capability> capabilityMap) {
         this.capabilityMap = capabilityMap;
     }
 
     /**
      * @return the capabilityMap
      */
-    public HashMap<String, Capability> getCapabilityMap() {
+    public Map<String, Capability> getCapabilityMap() {
         return this.capabilityMap;
     }
 
@@ -310,7 +290,7 @@ public class Device {
     }
 
     /**
-     * @param locationList the locationList to set
+     * @param locationLink the locationList to set
      */
     public void setLocation(String locationLink) {
         this.locationLink = locationLink;
@@ -366,10 +346,31 @@ public class Device {
      */
     public void setMessageList(List<Message> messageList) {
         this.messageList = messageList;
-
-        for (final Message m : messageList) {
-            setLowBattery(Message.TYPE_DEVICE_LOW_BATTERY.equals(m.getType()));
-            setReachable(!Message.TYPE_DEVICE_UNREACHABLE.equals(m.getType()));
+        applyMessageList(messageList);
+    }
+
+    private void applyMessageList(List<Message> messageList) {
+        if (messageList != null && !messageList.isEmpty()) {
+            boolean isUnreachableMessageFound = false;
+            boolean isLowBatteryMessageFound = false;
+            for (final Message message : messageList) {
+                switch (message.getType()) {
+                    case Message.TYPE_DEVICE_UNREACHABLE:
+                        isUnreachableMessageFound = true;
+                        break;
+                    case Message.TYPE_DEVICE_LOW_BATTERY:
+                        isLowBatteryMessageFound = true;
+                        break;
+                }
+            }
+            if (isUnreachableMessageFound) {
+                setReachable(false); // overwrite only when there is a corresponding message (to keep the state of the
+                                     // API in other cases)
+            }
+            if (isLowBatteryMessageFound) {
+                setLowBattery(true); // overwrite only when there is a corresponding message (to keep the state of the
+                                     // API in other cases)
+            }
         }
     }
 
@@ -378,7 +379,7 @@ public class Device {
      *
      * @param isReachable
      */
-    public void setReachable(boolean isReachable) {
+    private void setReachable(boolean isReachable) {
         if (getDeviceState().hasIsReachableState()) {
             getDeviceState().setReachable(isReachable);
         }
@@ -398,7 +399,7 @@ public class Device {
      *
      * @param hasLowBattery
      */
-    public void setLowBattery(boolean hasLowBattery) {
+    private void setLowBattery(boolean hasLowBattery) {
         this.lowBattery = hasLowBattery;
     }
 
@@ -411,14 +412,6 @@ public class Device {
         return lowBattery;
     }
 
-    public String getLowBatteryMessageId() {
-        return this.lowBatteryMessageId;
-    }
-
-    public void setLowBatteryMessageId(String messageId) {
-        this.lowBatteryMessageId = messageId;
-    }
-
     /**
      * Returns true, if the {@link Device} is battery powered.
      *
index 308d40b49d568c939673a8c270fe87adc82ead06..a9e74b1b7faf81872ab9cc22741800166daf09ef 100644 (file)
@@ -49,6 +49,7 @@ import org.openhab.binding.innogysmarthome.internal.discovery.InnogyDeviceDiscov
 import org.openhab.binding.innogysmarthome.internal.listener.DeviceStatusListener;
 import org.openhab.binding.innogysmarthome.internal.listener.EventListener;
 import org.openhab.binding.innogysmarthome.internal.manager.DeviceStructureManager;
+import org.openhab.binding.innogysmarthome.internal.manager.FullDeviceManager;
 import org.openhab.core.auth.client.oauth2.AccessTokenRefreshListener;
 import org.openhab.core.auth.client.oauth2.AccessTokenResponse;
 import org.openhab.core.auth.client.oauth2.OAuthClientService;
@@ -167,7 +168,7 @@ public class InnogyBridgeHandler extends BaseBridgeHandler
         if (checkOnAuthCode()) {
             final InnogyClient localClient = createInnogyClient(oAuthService, httpClient);
             client = localClient;
-            deviceStructMan = new DeviceStructureManager(localClient);
+            deviceStructMan = new DeviceStructureManager(createFullDeviceManager(localClient));
             oAuthService.addAccessTokenRefreshListener(this);
             registerDeviceStatusListener(InnogyBridgeHandler.this);
             scheduleRestartClient(false);
@@ -892,6 +893,10 @@ public class InnogyBridgeHandler extends BaseBridgeHandler
         return scheduler;
     }
 
+    FullDeviceManager createFullDeviceManager(InnogyClient client) {
+        return new FullDeviceManager(client);
+    }
+
     InnogyClient createInnogyClient(final OAuthClientService oAuthService, final HttpClient httpClient) {
         return new InnogyClient(oAuthService, httpClient);
     }
index 7ccc456cee907f32054963d5452517823de455bc..e3fc306b09957d7e596dcb078148c9ab8ae50d5b 100644 (file)
@@ -16,7 +16,6 @@ import static org.openhab.binding.innogysmarthome.internal.InnogyBindingConstant
 
 import java.time.format.DateTimeFormatter;
 import java.time.format.FormatStyle;
-import java.util.HashMap;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
@@ -708,7 +707,7 @@ public class InnogyDeviceHandler extends BaseThingHandler implements DeviceStatu
                 boolean deviceChanged = false;
                 final String linkedCapabilityId = event.getSourceId();
 
-                HashMap<String, Capability> capabilityMap = device.getCapabilityMap();
+                Map<String, Capability> capabilityMap = device.getCapabilityMap();
                 Capability capability = capabilityMap.get(linkedCapabilityId);
                 logger.trace("Loaded Capability {}, {} with id {}, device {} from device id {}", capability.getType(),
                         capability.getName(), capability.getId(), capability.getDeviceLink(), device.getId());
index 967e51568cd121bd2adc45538994a2650b654a07..5be3b1dee877a2b70d17a038149a5fea70bf76cc 100644 (file)
@@ -46,7 +46,7 @@ public class DeviceStructureManager {
 
     private final Logger logger = LoggerFactory.getLogger(DeviceStructureManager.class);
 
-    private final InnogyClient client;
+    private final FullDeviceManager deviceManager;
     private final Map<String, Device> deviceMap;
     private final Map<String, Device> capabilityIdToDeviceMap;
     private String bridgeDeviceId = "";
@@ -54,10 +54,10 @@ public class DeviceStructureManager {
     /**
      * Constructs the {@link DeviceStructureManager}.
      *
-     * @param client the {@link InnogyClient}
+     * @param deviceManager the {@link FullDeviceManager}
      */
-    public DeviceStructureManager(InnogyClient client) {
-        this.client = client;
+    public DeviceStructureManager(FullDeviceManager deviceManager) {
+        this.deviceManager = deviceManager;
         deviceMap = Collections.synchronizedMap(new HashMap<>());
         capabilityIdToDeviceMap = new ConcurrentHashMap<>();
     }
@@ -82,7 +82,7 @@ public class DeviceStructureManager {
     public void refreshDevices() throws IOException, ApiException, AuthenticationException {
         deviceMap.clear();
         capabilityIdToDeviceMap.clear();
-        List<Device> devices = client.getFullDevices();
+        List<Device> devices = deviceManager.getFullDevices();
         for (Device d : devices) {
             handleRefreshedDevice(d);
         }
@@ -98,7 +98,7 @@ public class DeviceStructureManager {
      */
     public void refreshDevice(String deviceId) throws IOException, ApiException, AuthenticationException {
         logger.trace("Refreshing Device with id '{}'", deviceId);
-        Device d = client.getFullDeviceById(deviceId);
+        Device d = deviceManager.getFullDeviceById(deviceId);
         handleRefreshedDevice(d);
     }
 
@@ -155,7 +155,7 @@ public class DeviceStructureManager {
             getDeviceMap().put(device.getId(), device);
         }
 
-        for (String cl : device.getCapabilityLinkList()) {
+        for (String cl : device.getCapabilities()) {
             capabilityIdToDeviceMap.put(Link.getId(cl), device);
         }
     }
diff --git a/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/manager/FullDeviceManager.java b/bundles/org.openhab.binding.innogysmarthome/src/main/java/org/openhab/binding/innogysmarthome/internal/manager/FullDeviceManager.java
new file mode 100644 (file)
index 0000000..09acbd9
--- /dev/null
@@ -0,0 +1,223 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.innogysmarthome.internal.manager;
+
+import static org.openhab.binding.innogysmarthome.internal.InnogyBindingConstants.BATTERY_POWERED_DEVICES;
+
+import java.io.IOException;
+import java.util.*;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+import org.openhab.binding.innogysmarthome.internal.client.InnogyClient;
+import org.openhab.binding.innogysmarthome.internal.client.entity.capability.Capability;
+import org.openhab.binding.innogysmarthome.internal.client.entity.capability.CapabilityState;
+import org.openhab.binding.innogysmarthome.internal.client.entity.device.Device;
+import org.openhab.binding.innogysmarthome.internal.client.entity.device.DeviceState;
+import org.openhab.binding.innogysmarthome.internal.client.entity.link.Link;
+import org.openhab.binding.innogysmarthome.internal.client.entity.location.Location;
+import org.openhab.binding.innogysmarthome.internal.client.entity.message.Message;
+import org.openhab.binding.innogysmarthome.internal.client.exception.ApiException;
+import org.openhab.binding.innogysmarthome.internal.client.exception.AuthenticationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Sven Strohschein - Initial contribution (but only created by refactoring the InnogyClient class)
+ */
+@NonNullByDefault
+public class FullDeviceManager {
+
+    private final Logger logger = LoggerFactory.getLogger(FullDeviceManager.class);
+
+    private final InnogyClient client;
+
+    public FullDeviceManager(InnogyClient client) {
+        this.client = client;
+    }
+
+    /**
+     * Returns a {@link List} of all {@link Device}s with the full configuration details, {@link Capability}s and
+     * states. Calling this may take a while...
+     */
+    public List<Device> getFullDevices() throws IOException, ApiException, AuthenticationException {
+
+        final Map<String, Location> locationMap = createLocationMap(client);
+        final Map<String, Capability> capabilityMap = createCapabilityMap(client);
+        final Map<String, DeviceState> deviceStateMap = createDeviceStateMap(client);
+        final Map<String, List<Message>> messageMap = createMessageMap(client);
+
+        final List<Device> deviceList = client.getDevices(deviceStateMap.keySet());
+        for (final Device device : deviceList) {
+            final String deviceId = device.getId();
+            initializeDevice(device, deviceStateMap.get(deviceId), locationMap, capabilityMap,
+                    getMessageList(device, messageMap));
+        }
+        return deviceList;
+    }
+
+    /**
+     * Returns the {@link Device} with the given deviceId with full configuration details, {@link Capability}s and
+     * states. Calling this may take a little bit longer...
+     */
+    public Device getFullDeviceById(final String deviceId) throws IOException, ApiException, AuthenticationException {
+        final Map<String, Location> locationMap = createLocationMap(client);
+        final Map<String, Capability> capabilityMap = createCapabilityMap(deviceId, client);
+        final List<Message> messageMap = createMessageMap(deviceId, client);
+
+        final DeviceState deviceState = new DeviceState();
+        deviceState.setId(deviceId);
+        deviceState.setState(client.getDeviceStateByDeviceId(deviceId));
+
+        final Device device = client.getDeviceById(deviceId);
+        initializeDevice(device, deviceState, locationMap, capabilityMap, messageMap);
+        return device;
+    }
+
+    private void initializeDevice(Device device, @Nullable DeviceState deviceState, Map<String, Location> locationMap,
+            Map<String, Capability> capabilityMap, List<Message> messageList) {
+
+        device.setDeviceState(deviceState);
+
+        if (isBatteryPowered(device)) {
+            device.setIsBatteryPowered(true);
+        }
+
+        device.setLocation(locationMap.get(device.getLocationId()));
+
+        device.setCapabilityMap(createDeviceCapabilityMap(device, capabilityMap));
+
+        device.setMessageList(messageList);
+    }
+
+    private static boolean isBatteryPowered(Device device) {
+        return BATTERY_POWERED_DEVICES.contains(device.getType());
+    }
+
+    private List<Message> getMessageList(Device device, Map<String, List<Message>> messageMap) {
+        return Objects.requireNonNullElse(messageMap.get(device.getId()), Collections.emptyList());
+    }
+
+    private static Map<String, Location> createLocationMap(InnogyClient client)
+            throws IOException, ApiException, AuthenticationException {
+        final List<Location> locationList = client.getLocations();
+        final Map<String, Location> locationMap = new HashMap<>(locationList.size());
+        for (final Location location : locationList) {
+            locationMap.put(location.getId(), location);
+        }
+        return locationMap;
+    }
+
+    private static Map<String, CapabilityState> createCapabilityStateMap(InnogyClient client)
+            throws IOException, ApiException, AuthenticationException {
+        final List<CapabilityState> capabilityStateList = client.getCapabilityStates();
+        final Map<String, CapabilityState> capabilityStateMap = new HashMap<>(capabilityStateList.size());
+        for (final CapabilityState capabilityState : capabilityStateList) {
+            capabilityStateMap.put(capabilityState.getId(), capabilityState);
+        }
+        return capabilityStateMap;
+    }
+
+    private static Map<String, Capability> createCapabilityMap(InnogyClient client)
+            throws IOException, ApiException, AuthenticationException {
+
+        final Map<String, CapabilityState> capabilityStateMap = createCapabilityStateMap(client);
+        final List<Capability> capabilityList = client.getCapabilities();
+
+        return initializeCapabilities(capabilityStateMap, capabilityList);
+    }
+
+    private static Map<String, Capability> createCapabilityMap(String deviceId, InnogyClient client)
+            throws IOException, ApiException, AuthenticationException {
+
+        final Map<String, CapabilityState> capabilityStateMap = createCapabilityStateMap(client);
+        final List<Capability> capabilityList = client.getCapabilitiesForDevice(deviceId);
+
+        return initializeCapabilities(capabilityStateMap, capabilityList);
+    }
+
+    private static Map<String, Capability> initializeCapabilities(Map<String, CapabilityState> capabilityStateMap,
+            List<Capability> capabilityList) {
+        final Map<String, Capability> capabilityMap = new HashMap<>(capabilityList.size());
+        for (final Capability capability : capabilityList) {
+            String capabilityId = capability.getId();
+
+            CapabilityState capabilityState = capabilityStateMap.get(capabilityId);
+            capability.setCapabilityState(capabilityState);
+
+            capabilityMap.put(capabilityId, capability);
+        }
+        return capabilityMap;
+    }
+
+    private static Map<String, Capability> createDeviceCapabilityMap(Device device,
+            Map<String, Capability> capabilityMap) {
+
+        final HashMap<String, Capability> deviceCapabilityMap = new HashMap<>();
+        for (final String capabilityValue : device.getCapabilities()) {
+            final Capability capability = capabilityMap.get(Link.getId(capabilityValue));
+            final String capabilityId = capability.getId();
+            deviceCapabilityMap.put(capabilityId, capability);
+        }
+        return deviceCapabilityMap;
+    }
+
+    private static Map<String, DeviceState> createDeviceStateMap(InnogyClient client)
+            throws IOException, ApiException, AuthenticationException {
+        final List<DeviceState> deviceStateList = client.getDeviceStates();
+        final Map<String, DeviceState> deviceStateMap = new HashMap<>(deviceStateList.size());
+        for (final DeviceState deviceState : deviceStateList) {
+            deviceStateMap.put(deviceState.getId(), deviceState);
+        }
+        return deviceStateMap;
+    }
+
+    private List<Message> createMessageMap(String deviceId, InnogyClient client)
+            throws IOException, ApiException, AuthenticationException {
+        final List<Message> messages = client.getMessages();
+        final List<Message> messageList = new ArrayList<>();
+        final String deviceIdPath = "/device/" + deviceId;
+
+        for (final Message message : messages) {
+            logger.trace("Message Type {} with ID {}", message.getType(), message.getId());
+            if (message.getDevices() != null && !message.getDevices().isEmpty()) {
+                for (final String li : message.getDevices()) {
+                    if (deviceIdPath.equals(li)) {
+                        messageList.add(message);
+                    }
+                }
+            }
+        }
+        return messageList;
+    }
+
+    private static Map<String, List<Message>> createMessageMap(InnogyClient client)
+            throws IOException, ApiException, AuthenticationException {
+        final List<Message> messageList = client.getMessages();
+        final Map<String, List<Message>> deviceMessageMap = new HashMap<>();
+        for (final Message message : messageList) {
+            if (message.getDevices() != null && !message.getDevices().isEmpty()) {
+                final String deviceId = message.getDevices().get(0).replace("/device/", "");
+                List<Message> ml;
+                if (deviceMessageMap.containsKey(deviceId)) {
+                    ml = deviceMessageMap.get(deviceId);
+                } else {
+                    ml = new ArrayList<>();
+                }
+                ml.add(message);
+                deviceMessageMap.put(deviceId, ml);
+            }
+        }
+        return deviceMessageMap;
+    }
+}
diff --git a/bundles/org.openhab.binding.innogysmarthome/src/test/java/org/openhab/binding/innogysmarthome/internal/client/entity/device/DeviceTest.java b/bundles/org.openhab.binding.innogysmarthome/src/test/java/org/openhab/binding/innogysmarthome/internal/client/entity/device/DeviceTest.java
new file mode 100644 (file)
index 0000000..b8871cf
--- /dev/null
@@ -0,0 +1,194 @@
+/**
+ * Copyright (c) 2010-2021 Contributors to the openHAB project
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+package org.openhab.binding.innogysmarthome.internal.client.entity.device;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+import org.openhab.binding.innogysmarthome.internal.client.entity.message.Message;
+import org.openhab.binding.innogysmarthome.internal.client.entity.state.BooleanState;
+
+/**
+ * @author Sven Strohschein - Initial contribution
+ */
+public class DeviceTest {
+
+    @Test
+    public void testSetMessageListLowBatteryMessage() {
+        Device device = createDevice();
+
+        assertTrue(device.isReachable());
+        assertFalse(device.hasLowBattery());
+
+        device.setMessageList(Collections.singletonList(createMessage(Message.TYPE_DEVICE_LOW_BATTERY)));
+
+        assertTrue(device.isReachable());
+        assertTrue(device.hasLowBattery());
+    }
+
+    @Test
+    public void testSetMessageListUnreachableMessage() {
+        Device device = createDevice();
+
+        assertTrue(device.isReachable());
+        assertFalse(device.hasLowBattery());
+
+        device.setMessageList(Collections.singletonList(createMessage(Message.TYPE_DEVICE_UNREACHABLE)));
+
+        assertFalse(device.isReachable());
+        assertFalse(device.hasLowBattery());
+    }
+
+    @Test
+    public void testSetMessageListResetByEmpty() {
+        Device device = createDevice();
+
+        assertNull(device.getMessageList());
+        assertTrue(device.isReachable());
+        assertFalse(device.hasLowBattery());
+
+        List<Message> messages = Arrays.asList(createMessage(Message.TYPE_DEVICE_LOW_BATTERY),
+                createMessage(Message.TYPE_DEVICE_UNREACHABLE));
+        device.setMessageList(messages);
+
+        assertEquals(messages, device.getMessageList());
+        assertFalse(device.isReachable());
+        assertTrue(device.hasLowBattery());
+
+        device.setMessageList(Collections.emptyList());
+
+        // Nothing should get changed.
+        // New messages are only set in real-life when the device is refreshed with new data of the API.
+        // Therefore the data of the API should be kept / not overwritten when no corresponding messages are available.
+        assertEquals(Collections.emptyList(), device.getMessageList());
+        assertFalse(device.isReachable());
+        assertTrue(device.hasLowBattery());
+    }
+
+    @Test
+    public void testSetMessageListResetByNULL() {
+        Device device = createDevice();
+
+        assertNull(device.getMessageList());
+        assertTrue(device.isReachable());
+        assertFalse(device.hasLowBattery());
+
+        List<Message> messages = Arrays.asList(createMessage(Message.TYPE_DEVICE_LOW_BATTERY),
+                createMessage(Message.TYPE_DEVICE_UNREACHABLE));
+        device.setMessageList(messages);
+
+        assertEquals(messages, device.getMessageList());
+        assertFalse(device.isReachable());
+        assertTrue(device.hasLowBattery());
+
+        device.setMessageList(null);
+
+        // Nothing should get changed.
+        // New messages are only set in real-life when the device is refreshed with new data of the API.
+        // Therefore the data of the API should be kept / not overwritten when no corresponding messages are available.
+        assertNull(device.getMessageList());
+        assertFalse(device.isReachable());
+        assertTrue(device.hasLowBattery());
+    }
+
+    @Test
+    public void testSetMessageListResetByUnimportantMessage() {
+        Device device = createDevice();
+
+        assertNull(device.getMessageList());
+        assertTrue(device.isReachable());
+        assertFalse(device.hasLowBattery());
+
+        List<Message> messages = Arrays.asList(createMessage(Message.TYPE_DEVICE_LOW_BATTERY),
+                createMessage(Message.TYPE_DEVICE_UNREACHABLE));
+        device.setMessageList(messages);
+
+        assertEquals(messages, device.getMessageList());
+        assertFalse(device.isReachable());
+        assertTrue(device.hasLowBattery());
+
+        messages = Collections.singletonList(createMessage("UNKNOWN"));
+        device.setMessageList(messages);
+
+        // Nothing should get changed.
+        // New messages are only set in real-life when the device is refreshed with new data of the API.
+        // Therefore the data of the API should be kept / not overwritten when no corresponding messages are available.
+        assertEquals(messages, device.getMessageList());
+        assertFalse(device.isReachable());
+        assertTrue(device.hasLowBattery());
+    }
+
+    @Test
+    public void testSetMessageListUnimportantMessage() {
+        Device device = createDevice();
+
+        assertTrue(device.isReachable());
+        assertFalse(device.hasLowBattery());
+
+        device.setMessageList(Collections.singletonList(createMessage("UNKNOWN")));
+
+        assertTrue(device.isReachable());
+        assertFalse(device.hasLowBattery());
+    }
+
+    private Message createMessage(String messageType) {
+        Message message = new Message();
+        message.setType(messageType);
+        return message;
+    }
+
+    @Test
+    public void testSetMessageListNULL() {
+        Device device = createDevice();
+
+        assertTrue(device.isReachable());
+        assertFalse(device.hasLowBattery());
+
+        device.setMessageList(null);
+
+        assertTrue(device.isReachable());
+        assertFalse(device.hasLowBattery());
+    }
+
+    @Test
+    public void testSetMessageListEmpty() {
+        Device device = createDevice();
+
+        assertTrue(device.isReachable());
+        assertFalse(device.hasLowBattery());
+
+        device.setMessageList(Collections.emptyList());
+
+        assertTrue(device.isReachable());
+        assertFalse(device.hasLowBattery());
+    }
+
+    private static Device createDevice() {
+        BooleanState isReachableState = new BooleanState();
+        isReachableState.setValue(true);
+
+        State state = new State();
+        state.setIsReachable(isReachableState);
+
+        DeviceState deviceState = new DeviceState();
+        deviceState.setState(state);
+
+        Device device = new Device();
+        device.setDeviceState(deviceState);
+        return device;
+    }
+}
index 1a64bb0b5d01359fcffab121e51db068dca7ffdd..c7aba6d714f1f27534de7e05cd7635c8200e9602 100644 (file)
@@ -30,6 +30,7 @@ import org.openhab.binding.innogysmarthome.internal.InnogyWebSocket;
 import org.openhab.binding.innogysmarthome.internal.client.InnogyClient;
 import org.openhab.binding.innogysmarthome.internal.client.entity.device.Device;
 import org.openhab.binding.innogysmarthome.internal.client.entity.device.DeviceConfig;
+import org.openhab.binding.innogysmarthome.internal.manager.FullDeviceManager;
 import org.openhab.core.auth.client.oauth2.OAuthClientService;
 import org.openhab.core.auth.client.oauth2.OAuthFactory;
 import org.openhab.core.config.core.Configuration;
@@ -174,6 +175,7 @@ public class InnogyBridgeHandlerTest {
     private class InnogyBridgeHandlerAccessible extends InnogyBridgeHandler {
 
         private final InnogyClient innogyClientMock;
+        private final FullDeviceManager fullDeviceManagerMock;
         private final ScheduledExecutorService schedulerMock;
         private int executionCount;
         private int directExecutionCount;
@@ -188,7 +190,8 @@ public class InnogyBridgeHandlerTest {
             bridgeDevice.setConfig(new DeviceConfig());
 
             innogyClientMock = mock(InnogyClient.class);
-            when(innogyClientMock.getFullDevices()).thenReturn(Collections.singletonList(bridgeDevice));
+            fullDeviceManagerMock = mock(FullDeviceManager.class);
+            when(fullDeviceManagerMock.getFullDevices()).thenReturn(Collections.singletonList(bridgeDevice));
 
             schedulerMock = mock(ScheduledExecutorService.class);
 
@@ -218,6 +221,12 @@ public class InnogyBridgeHandlerTest {
             return directExecutionCount;
         }
 
+        @Override
+        @NonNull
+        FullDeviceManager createFullDeviceManager(@NonNull InnogyClient client) {
+            return fullDeviceManagerMock;
+        }
+
         @Override
         @NonNull
         InnogyClient createInnogyClient(@NonNull OAuthClientService oAuthService, @NonNull HttpClient httpClient) {