]> git.basschouten.com Git - openhab-addons.git/commitdiff
[unifi] Fix bug with combination of other data/ports (#14060)
authorHilbrand Bouwkamp <hilbrand@h72.nl>
Sat, 31 Dec 2022 10:03:56 +0000 (11:03 +0100)
committerGitHub <noreply@github.com>
Sat, 31 Dec 2022 10:03:56 +0000 (11:03 +0100)
- It seems to throw an exception when updating internal cache. It can happen if you have a switch that has both PoE ports and other PoE ports or data in the port override.
- Fixed logout, should be POST instead of GET.
- Fixed typo in channel-type.config.unifi.poeEnable.mode.option.pasv24 should be without appending v.
- Removed compiler warnings.

Signed-off-by: Hilbrand Bouwkamp <hilbrand@h72.nl>
16 files changed:
bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/UniFiController.java
bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/UniFiControllerRequest.java
bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/cache/UniFiCache.java
bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/cache/UniFiControllerCache.java
bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/cache/UniFiDeviceCache.java
bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UnfiPortOverrideJsonElement.java [deleted file]
bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UnfiPortOverrideJsonObject.java [new file with mode: 0644]
bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiDevice.java
bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiPortTuple.java
bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiSwitchPorts.java [new file with mode: 0644]
bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/util/UnfiPortOverrideJsonElementDeserializer.java
bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/handler/UniFiBaseThingHandler.java
bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/handler/UniFiControllerThingHandler.java
bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/handler/UniFiPoePortThingHandler.java
bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/handler/UniFiThingDiscoveryService.java
bundles/org.openhab.binding.unifi/src/main/resources/OH-INF/i18n/unifi.properties

index 41f18fd3f3f652ba7faf773703ce7c50bf69b3c7..905e3a900ecf867bd75b5aae57b85a9fe247352f 100644 (file)
@@ -14,18 +14,17 @@ package org.openhab.binding.unifi.internal.api;
 
 import java.util.Collection;
 import java.util.List;
-import java.util.Map;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.jetty.client.HttpClient;
 import org.eclipse.jetty.http.HttpMethod;
 import org.openhab.binding.unifi.internal.api.cache.UniFiControllerCache;
-import org.openhab.binding.unifi.internal.api.dto.UnfiPortOverrideJsonElement;
+import org.openhab.binding.unifi.internal.api.dto.UnfiPortOverrideJsonObject;
 import org.openhab.binding.unifi.internal.api.dto.UniFiClient;
 import org.openhab.binding.unifi.internal.api.dto.UniFiDevice;
-import org.openhab.binding.unifi.internal.api.dto.UniFiPortTuple;
 import org.openhab.binding.unifi.internal.api.dto.UniFiSite;
+import org.openhab.binding.unifi.internal.api.dto.UniFiSwitchPorts;
 import org.openhab.binding.unifi.internal.api.dto.UniFiUnknownClient;
 import org.openhab.binding.unifi.internal.api.dto.UniFiWiredClient;
 import org.openhab.binding.unifi.internal.api.dto.UniFiWirelessClient;
@@ -42,6 +41,7 @@ import org.slf4j.LoggerFactory;
 import com.google.gson.FieldNamingPolicy;
 import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
+import com.google.gson.JsonObject;
 
 /**
  * The {@link UniFiController} is the main communication point with an external instance of the Ubiquiti Networks
@@ -94,7 +94,7 @@ public class UniFiController {
                 .registerTypeAdapter(UniFiWiredClient.class, clientInstanceCreator)
                 .registerTypeAdapter(UniFiWirelessClient.class, clientInstanceCreator).create();
         this.poeGson = new GsonBuilder()
-                .registerTypeAdapter(UnfiPortOverrideJsonElement.class, new UnfiPortOverrideJsonElementDeserializer())
+                .registerTypeAdapter(UnfiPortOverrideJsonObject.class, new UnfiPortOverrideJsonElementDeserializer())
                 .create();
     }
 
@@ -133,7 +133,7 @@ public class UniFiController {
 
     public void logout() throws UniFiException {
         csrfToken = "";
-        final UniFiControllerRequest<Void> req = newRequest(Void.class, HttpMethod.GET, gson);
+        final UniFiControllerRequest<Void> req = newRequest(Void.class, HttpMethod.POST, gson);
         req.setPath(unifios ? "/api/auth/logout" : "/logout");
         executeRequest(req);
     }
@@ -153,7 +153,7 @@ public class UniFiController {
         return cache;
     }
 
-    public @Nullable Map<Integer, UniFiPortTuple> getSwitchPorts(@Nullable final String deviceId) {
+    public @Nullable UniFiSwitchPorts getSwitchPorts(@Nullable final String deviceId) {
         return cache.getSwitchPorts(deviceId);
     }
 
@@ -175,10 +175,9 @@ public class UniFiController {
         refresh();
     }
 
-    public boolean poeMode(final UniFiDevice device, final List<UnfiPortOverrideJsonElement> data)
-            throws UniFiException {
+    public boolean poeMode(final UniFiDevice device, final List<JsonObject> data) throws UniFiException {
         // Safety check to make sure no empty data is send to avoid corrupting override data on the device.
-        if (data.isEmpty() || data.stream().anyMatch(p -> p.getJsonObject().entrySet().isEmpty())) {
+        if (data.isEmpty() || data.stream().anyMatch(p -> p.entrySet().isEmpty())) {
             logger.info("Not overriding port for '{}', because port data contains empty json: {}", device.getName(),
                     poeGson.toJson(data));
             return false;
@@ -225,7 +224,7 @@ public class UniFiController {
             throws UniFiException {
         T result;
         try {
-            result = request.execute();
+            result = (T) request.execute();
             csrfToken = request.getCsrfToken();
         } catch (final UniFiExpiredSessionException e) {
             if (fromLogin) {
@@ -234,11 +233,11 @@ public class UniFiController {
                 throw new UniFiCommunicationException(e);
             } else {
                 login();
-                result = executeRequest(request);
+                result = (T) executeRequest(request);
             }
         } catch (final UniFiNotAuthorizedException e) {
             logger.warn("Not Authorized! Please make sure your controller credentials have administrator rights");
-            result = null;
+            result = (T) null;
         }
         return result;
     }
index 2d021588987785a0e6fe59fccab138aad7e0dabb..1a9946a46d6c6b1cda421f7e638da0853f64d876 100644 (file)
@@ -124,14 +124,14 @@ class UniFiControllerRequest<T> {
     }
 
     public @Nullable T execute() throws UniFiException {
-        T result = null;
+        T result = (T) null;
         final String json = getContent();
         // mgb: only try and unmarshall non-void result types
         if (!Void.class.equals(resultType)) {
             final JsonObject jsonObject = JsonParser.parseString(json).getAsJsonObject();
 
             if (jsonObject.has(PROPERTY_DATA) && jsonObject.get(PROPERTY_DATA).isJsonArray()) {
-                result = gson.fromJson(jsonObject.getAsJsonArray(PROPERTY_DATA), resultType);
+                result = (T) gson.fromJson(jsonObject.getAsJsonArray(PROPERTY_DATA), resultType);
             }
         }
         return result;
index 44107ff2c208b8eeba8bf99031faddc9c1ebec95..50a468ee232aab9bdf0075d4430b9de0c8d5bbd1 100644 (file)
@@ -106,7 +106,9 @@ abstract class UniFiCache<T extends @Nullable HasId> {
             logger.debug("Put #{} entries in {}: {}", values.length, getClass().getSimpleName(),
                     lazyFormatAsList(values));
             for (final T value : values) {
-                put(value.getId(), value);
+                if (value != null) {
+                    put(value.getId(), value);
+                }
             }
         }
     }
index 698cb42b8fb2b676f10c2704bc5d3fb6ae15fca7..fbc3d40e9570e8d055eaffe1b62776197d24738d 100644 (file)
@@ -13,7 +13,6 @@
 package org.openhab.binding.unifi.internal.api.cache;
 
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -23,12 +22,11 @@ import java.util.stream.Stream;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
-import org.openhab.binding.unifi.internal.api.dto.UnfiPortOverrideJsonElement;
 import org.openhab.binding.unifi.internal.api.dto.UniFiClient;
 import org.openhab.binding.unifi.internal.api.dto.UniFiDevice;
-import org.openhab.binding.unifi.internal.api.dto.UniFiPortTable;
 import org.openhab.binding.unifi.internal.api.dto.UniFiPortTuple;
 import org.openhab.binding.unifi.internal.api.dto.UniFiSite;
+import org.openhab.binding.unifi.internal.api.dto.UniFiSwitchPorts;
 import org.openhab.binding.unifi.internal.api.dto.UniFiWlan;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -49,7 +47,7 @@ public class UniFiControllerCache {
     private final UniFiDeviceCache devicesCache = new UniFiDeviceCache();
     private final UniFiClientCache clientsCache = new UniFiClientCache();
     private final UniFiClientCache insightsCache = new UniFiClientCache();
-    private final Map<String, Map<Integer, UniFiPortTuple>> devicesToPortTables = new ConcurrentHashMap<>();
+    private final Map<String, UniFiSwitchPorts> devicesToPortTables = new ConcurrentHashMap<>();
 
     public void clear() {
         sitesCache.clear();
@@ -94,23 +92,23 @@ public class UniFiControllerCache {
         devicesCache.putAll(devices);
         if (devices != null) {
             Stream.of(devices).filter(Objects::nonNull).forEach(d -> {
-                Stream.ofNullable(d.getPortTable()).flatMap(pt -> Stream.of(pt)).filter(UniFiPortTable::isPortPoe)
-                        .forEach(p -> {
-                            final Map<Integer, UniFiPortTuple> tupleTable = devicesToPortTables
-                                    .computeIfAbsent(d.getMac(), tt -> new HashMap<>());
-                            final UniFiPortTuple tuple = tupleTable.computeIfAbsent(p.getPortIdx(),
-                                    t -> new UniFiPortTuple());
-
-                            tuple.setDevice(d);
-                            tuple.setTable(p);
-                        });
+                Stream.ofNullable(d.getPortTable()).forEach(pt -> {
+                    final UniFiSwitchPorts switchPorts = devicesToPortTables.computeIfAbsent(d.getMac(),
+                            p -> new UniFiSwitchPorts());
+
+                    Stream.of(pt).forEach(p -> {
+                        @SuppressWarnings("null")
+                        final UniFiPortTuple tuple = switchPorts.computeIfAbsent(p.getPortIdx());
+
+                        tuple.setDevice(d);
+                        tuple.setTable(p);
+                    });
+                });
                 Stream.ofNullable(d.getPortOverrides()).forEach(po -> {
-                    final Map<Integer, UniFiPortTuple> tupleTable = devicesToPortTables.get(d.getMac());
+                    final UniFiSwitchPorts tupleTable = devicesToPortTables.get(d.getMac());
 
                     if (tupleTable != null) {
-                        Stream.of(po).filter(pof -> !pof.getAsJsonObject().entrySet().isEmpty())
-                                .map(UnfiPortOverrideJsonElement::new).forEach(p -> tupleTable
-                                        .computeIfAbsent(p.getPortIdx(), t -> new UniFiPortTuple()).setJsonElement(p));
+                        Stream.of(po).forEach(p -> tupleTable.setOverride(p));
                     }
                 });
             });
@@ -121,11 +119,12 @@ public class UniFiControllerCache {
         return devicesCache.get(id);
     }
 
-    public Map<Integer, UniFiPortTuple> getSwitchPorts(@Nullable final String deviceId) {
-        return deviceId == null ? Map.of() : devicesToPortTables.getOrDefault(deviceId, Map.of());
+    public UniFiSwitchPorts getSwitchPorts(@Nullable final String deviceId) {
+        return deviceId == null ? new UniFiSwitchPorts()
+                : devicesToPortTables.getOrDefault(deviceId, new UniFiSwitchPorts());
     }
 
-    public Collection<Map<Integer, UniFiPortTuple>> getSwitchPorts() {
+    public Collection<UniFiSwitchPorts> getSwitchPorts() {
         return devicesToPortTables.values();
     }
 
index 8cfd91c977fd470dbb8d7e057a5bdc68ec2eb9cd..f9fd2d6362b79a5b7aed6d27d1344e311c7804e0 100644 (file)
@@ -38,7 +38,8 @@ class UniFiDeviceCache extends UniFiCache<UniFiDevice> {
         switch (prefix) {
             case MAC:
                 return device.getMac();
+            default:
+                return null;
         }
-        return null;
     }
 }
diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UnfiPortOverrideJsonElement.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UnfiPortOverrideJsonElement.java
deleted file mode 100644 (file)
index 2562503..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * Copyright (c) 2010-2022 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.unifi.internal.api.dto;
-
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-
-/**
- * The {@link UnfiPortOverride} represents the data model of UniFi port override.
- * Using plain JsonObject to make sure any data in the object is not lost when writing the data back to the UniFi
- * device.
- *
- * @author Hilbrand Bouwkamp - Initial contribution
- */
-public class UnfiPortOverrideJsonElement {
-
-    private static final String PORT_IDX = "port_idx";
-    private static final String PORT_CONF_ID = "port_conf_id";
-    private static final String POE_MODE = "poe_mode";
-
-    private final JsonObject jsonObject;
-
-    public UnfiPortOverrideJsonElement(final JsonElement element) {
-        this.jsonObject = element.getAsJsonObject();
-    }
-
-    public JsonObject getJsonObject() {
-        return jsonObject;
-    }
-
-    public int getPortIdx() {
-        return jsonObject.get(PORT_IDX).getAsInt();
-    }
-
-    public String getPortConfId() {
-        return jsonObject.get(PORT_CONF_ID).getAsString();
-    }
-
-    public String getPoeMode() {
-        return jsonObject.get(POE_MODE).getAsString();
-    }
-
-    public void setPoeMode(final String poeMode) {
-        jsonObject.addProperty(POE_MODE, poeMode);
-    }
-
-    @Override
-    public String toString() {
-        return jsonObject.toString();
-    }
-}
diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UnfiPortOverrideJsonObject.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UnfiPortOverrideJsonObject.java
new file mode 100644 (file)
index 0000000..91cdcd0
--- /dev/null
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2010-2022 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.unifi.internal.api.dto;
+
+import com.google.gson.JsonObject;
+
+/**
+ * The {@link UnfiPortOverride} represents the data model of UniFi port override.
+ * Using plain JsonObject to make sure any data in the object is not lost when writing the data back to the UniFi
+ * device.
+ *
+ * @author Hilbrand Bouwkamp - Initial contribution
+ */
+public class UnfiPortOverrideJsonObject {
+
+    private static final String PORT_IDX = "port_idx";
+    private static final String PORT_CONF_ID = "port_conf_id";
+    private static final String POE_MODE = "poe_mode";
+
+    private final JsonObject jsonObject;
+
+    public UnfiPortOverrideJsonObject(final JsonObject Object) {
+        this.jsonObject = Object.getAsJsonObject();
+    }
+
+    public JsonObject getJsonObject() {
+        return jsonObject;
+    }
+
+    public static boolean hasPortIdx(final JsonObject jsonObject) {
+        return jsonObject.has(PORT_IDX);
+    }
+
+    public int getPortIdx() {
+        return jsonObject.get(PORT_IDX).getAsInt();
+    }
+
+    public String getPortConfId() {
+        return jsonObject.get(PORT_CONF_ID).getAsString();
+    }
+
+    public String getPoeMode() {
+        return jsonObject.get(POE_MODE).getAsString();
+    }
+
+    public void setPoeMode(final String poeMode) {
+        jsonObject.addProperty(POE_MODE, poeMode);
+    }
+
+    @Override
+    public String toString() {
+        return jsonObject.toString();
+    }
+}
index 644ba3e6314c79a3faa9b0105be61207b9563b30..551065a1b64d3d2efd0243ace4ee2998b06d8b07 100644 (file)
@@ -15,7 +15,7 @@ package org.openhab.binding.unifi.internal.api.dto;
 import org.openhab.binding.unifi.internal.api.cache.UniFiControllerCache;
 import org.openhab.binding.unifi.internal.api.util.UniFiTidyLowerCaseStringDeserializer;
 
-import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
 import com.google.gson.annotations.JsonAdapter;
 import com.google.gson.annotations.SerializedName;
 
@@ -44,7 +44,7 @@ public class UniFiDevice implements HasId {
 
     private UniFiPortTable[] portTable;
 
-    private JsonElement[] portOverrides;
+    private JsonObject[] portOverrides;
 
     public UniFiDevice(final UniFiControllerCache cache) {
         this.cache = cache;
@@ -75,7 +75,7 @@ public class UniFiDevice implements HasId {
         return portTable;
     }
 
-    public JsonElement[] getPortOverrides() {
+    public JsonObject[] getPortOverrides() {
         return portOverrides;
     }
 
index fa9865d87e044ad8026a9064b78de4a63a3a28fe..fa83eff178c342f4b009547f53847401707baf7a 100644 (file)
@@ -14,7 +14,7 @@ package org.openhab.binding.unifi.internal.api.dto;
 
 /**
  * Tuple to store both the {@link UniFiPortTable}, which contains the all information related to the port,
- * and the {@link UnfiPortOverrideJsonElement}, which contains the raw json data of the port override.
+ * and the {@link UnfiPortOverrideJsonObject}, which contains the raw json data of the port override.
  *
  * @author Hilbrand Bouwkamp - Initial contribution
  */
@@ -24,7 +24,7 @@ public class UniFiPortTuple {
 
     private UniFiPortTable table;
 
-    private UnfiPortOverrideJsonElement jsonElement;
+    private UnfiPortOverrideJsonObject jsonElement;
 
     public UniFiDevice getDevice() {
         return device;
@@ -46,11 +46,11 @@ public class UniFiPortTuple {
         this.table = table;
     }
 
-    public UnfiPortOverrideJsonElement getJsonElement() {
+    public UnfiPortOverrideJsonObject getJsonElement() {
         return jsonElement;
     }
 
-    public void setJsonElement(final UnfiPortOverrideJsonElement jsonElement) {
+    public void setJsonElement(final UnfiPortOverrideJsonObject jsonElement) {
         this.jsonElement = jsonElement;
     }
 }
diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiSwitchPorts.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiSwitchPorts.java
new file mode 100644 (file)
index 0000000..91850f6
--- /dev/null
@@ -0,0 +1,122 @@
+/**
+ * Copyright (c) 2010-2022 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.unifi.internal.api.dto;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.eclipse.jdt.annotation.Nullable;
+
+import com.google.gson.JsonObject;
+
+/**
+ * Data object to keep track of all port data, including all port_override data (both for ports and additional data) on
+ * a switch device.
+ *
+ * @author Hilbrand Bouwkamp - Initial contribution
+ */
+@NonNullByDefault
+public class UniFiSwitchPorts {
+
+    /**
+     * Port data grouped by port id.
+     */
+    private final Map<Integer, UniFiPortTuple> ports = new HashMap<>();
+    /**
+     * Additional none port specific override data. Keep track to send to device when updating override data.
+     */
+    private final Set<JsonObject> otherOverrides = new HashSet<>();
+
+    /**
+     * Return port data for the given port
+     *
+     * @param portIdx port to get the data for
+     * @return Return port data for the given port
+     */
+    public @Nullable UniFiPortTuple getPort(final int portIdx) {
+        return ports.get(portIdx);
+    }
+
+    /**
+     * Return port data for the given port or if none exists set a new data object and return it.
+     *
+     * @param portIdx port to get the data for
+     * @return Return port data for the given port or if none exists set a new data object and return it.
+     */
+    public UniFiPortTuple computeIfAbsent(final int portIdx) {
+        final UniFiPortTuple tuple = ports.computeIfAbsent(portIdx, t -> new UniFiPortTuple());
+        if (tuple == null) {
+            // This should never happen because ports can never contain a null value, and computeIfAbsent should never
+            // return null. However to satisfy the compiler a check for null was added.
+            throw new IllegalStateException("UniFiPortTuple is null for portIdx " + portIdx);
+        }
+        return tuple;
+    }
+
+    /**
+     * @return Returns the list of PoE Ports.
+     */
+    public List<UniFiPortTuple> getPoePorts() {
+        return ports.values().stream().filter(e -> e.getTable().isPortPoe()).collect(Collectors.toList());
+    }
+
+    /**
+     * Returns the override data as list with json objects after calling the updateMethod on the data for the given
+     * portIdx.
+     * The update method changes the data in the internal structure.
+     *
+     * @param portIdx port to call updateMethod for
+     * @param updateMethod method to call to update data for a specific port
+     * @return Returns a list of json objects of all override data
+     */
+    public List<JsonObject> updatedList(final int portIdx, final Consumer<UnfiPortOverrideJsonObject> updateMethod) {
+        @SuppressWarnings("null")
+        final List<UnfiPortOverrideJsonObject> updatedList = ports.entrySet().stream()
+                .map(e -> e.getValue().getJsonElement()).filter(Objects::nonNull).collect(Collectors.toList());
+
+        updatedList.stream().filter(p -> p.getPortIdx() == portIdx).findAny().ifPresent(updateMethod::accept);
+
+        return Stream
+                .concat(otherOverrides.stream(), updatedList.stream().map(UnfiPortOverrideJsonObject::getJsonObject))
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Set the port override object. If it's for a specific port set bind it to the port data, otherwise store it as
+     * generic data.
+     *
+     * @param jsonObject json object to set
+     */
+    public void setOverride(final JsonObject jsonObject) {
+        if (UnfiPortOverrideJsonObject.hasPortIdx(jsonObject)) {
+            final UnfiPortOverrideJsonObject po = new UnfiPortOverrideJsonObject(jsonObject);
+            final UniFiPortTuple tuple = ports.get(po.getPortIdx());
+
+            if (tuple == null) {
+                otherOverrides.add(jsonObject);
+            } else {
+                tuple.setJsonElement(po);
+            }
+        } else {
+            otherOverrides.add(jsonObject);
+        }
+    }
+}
index 498a54d8b68506ff57d6b47f33d7d3e40ad1d3c7..dc5167bbce90f2bb9bf719915f26667bb0ea91fd 100644 (file)
@@ -15,22 +15,22 @@ package org.openhab.binding.unifi.internal.api.util;
 import java.lang.reflect.Type;
 
 import org.eclipse.jdt.annotation.NonNullByDefault;
-import org.openhab.binding.unifi.internal.api.dto.UnfiPortOverrideJsonElement;
+import org.openhab.binding.unifi.internal.api.dto.UnfiPortOverrideJsonObject;
 
 import com.google.gson.JsonElement;
 import com.google.gson.JsonSerializationContext;
 import com.google.gson.JsonSerializer;
 
 /**
- * Serializer for {@link UnfiPortOverrideJsonElement}. Returns the content of the jsonObject in the class.
+ * Serializer for {@link UnfiPortOverrideJsonObject}. Returns the content of the jsonObject in the class.
  *
  * @author Hilbrand Bouwkamp - Initial contribution
  */
 @NonNullByDefault
-public class UnfiPortOverrideJsonElementDeserializer implements JsonSerializer<UnfiPortOverrideJsonElement> {
+public class UnfiPortOverrideJsonElementDeserializer implements JsonSerializer<UnfiPortOverrideJsonObject> {
 
     @Override
-    public JsonElement serialize(final UnfiPortOverrideJsonElement src, final Type typeOfSrc,
+    public JsonElement serialize(final UnfiPortOverrideJsonObject src, final Type typeOfSrc,
             final JsonSerializationContext context) {
         return src.getJsonObject();
     }
index 5b8e41bf28b3e78c50d3f9f465ca69733b2fe6e9..299d23ab8608f0a64596f9396ff53ebaa69844c3 100644 (file)
@@ -61,6 +61,7 @@ public abstract class UniFiBaseThingHandler<E, C> extends BaseThingHandler {
             return;
         }
         // mgb: derive the config class from the generic type
+        @SuppressWarnings("null")
         final Class<?> clazz = (Class<?>) (((ParameterizedType) getClass().getGenericSuperclass())
                 .getActualTypeArguments()[1]);
         final C config = (C) getConfigAs(clazz);
@@ -99,7 +100,7 @@ public abstract class UniFiBaseThingHandler<E, C> extends BaseThingHandler {
         logger.debug("Handling command = {} for channel = {}", command, channelUID);
         // mgb: only handle commands if we're ONLINE
         if (getThing().getStatus() == ONLINE) {
-            final E entity = getEntity();
+            final @Nullable E entity = getEntity();
             final UniFiController controller = getController();
 
             if (command == REFRESH) {
@@ -128,13 +129,13 @@ public abstract class UniFiBaseThingHandler<E, C> extends BaseThingHandler {
     protected final void refresh() {
         // mgb: only refresh if we're ONLINE
         if (getThing().getStatus() == ONLINE) {
-            final E entity = getEntity();
+            final @Nullable E entity = getEntity();
 
             getThing().getChannels().forEach(channel -> updateState(entity, channel.getUID()));
         }
     }
 
-    private void updateState(final E entity, final ChannelUID channelUID) {
+    private void updateState(final @Nullable E entity, final ChannelUID channelUID) {
         final String channelId = channelUID.getId();
         final State state = Optional.ofNullable(entity).map(e -> getChannelState(e, channelId))
                 .orElseGet(() -> getDefaultState(channelId));
index 6c799375f5cf84d96e4e0b370823e3e553c6e9ed..151fd0c498a74d9d7f1053ddbc2e5633fb803c0b 100644 (file)
@@ -39,6 +39,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.BaseBridgeHandler;
+import org.openhab.core.thing.binding.ThingHandler;
 import org.openhab.core.thing.binding.ThingHandlerService;
 import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder;
 import org.openhab.core.types.Command;
@@ -107,13 +108,15 @@ public class UniFiControllerThingHandler extends BaseBridgeHandler {
     @Override
     public void dispose() {
         cancelRefreshJob();
+        final UniFiController controller = this.controller;
+
         if (controller != null) {
             try {
                 controller.stop();
             } catch (final UniFiException e) {
                 // mgb: nop as we're in dispose
             }
-            controller = null;
+            this.controller = null;
         }
     }
 
@@ -188,8 +191,10 @@ public class UniFiControllerThingHandler extends BaseBridgeHandler {
             uc.refresh();
             // mgb: then refresh all the client things
             getThing().getThings().forEach((thing) -> {
-                if (thing.getHandler() instanceof UniFiBaseThingHandler) {
-                    ((UniFiBaseThingHandler) thing.getHandler()).refresh();
+                final ThingHandler handler = thing.getHandler();
+
+                if (handler instanceof UniFiBaseThingHandler) {
+                    ((UniFiBaseThingHandler<?, ?>) handler).refresh();
                 }
             });
         }
index 90501cb7927b73210fda3114b5128ba1f7265522..b847c8001b2548902b274c744e1c1204f8047b18 100644 (file)
@@ -25,11 +25,6 @@ import static org.openhab.binding.unifi.internal.UniFiBindingConstants.CHANNEL_P
 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.CHANNEL_PORT_POE_VOLTAGE;
 import static org.openhab.core.library.unit.MetricPrefix.MILLI;
 
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.stream.Collectors;
-
 import javax.measure.quantity.ElectricCurrent;
 import javax.measure.quantity.ElectricPotential;
 import javax.measure.quantity.Power;
@@ -40,14 +35,15 @@ import org.openhab.binding.unifi.internal.UniFiPoePortThingConfig;
 import org.openhab.binding.unifi.internal.api.UniFiController;
 import org.openhab.binding.unifi.internal.api.UniFiException;
 import org.openhab.binding.unifi.internal.api.cache.UniFiControllerCache;
-import org.openhab.binding.unifi.internal.api.dto.UnfiPortOverrideJsonElement;
 import org.openhab.binding.unifi.internal.api.dto.UniFiDevice;
 import org.openhab.binding.unifi.internal.api.dto.UniFiPortTable;
 import org.openhab.binding.unifi.internal.api.dto.UniFiPortTuple;
+import org.openhab.binding.unifi.internal.api.dto.UniFiSwitchPorts;
 import org.openhab.core.library.types.OnOffType;
 import org.openhab.core.library.types.QuantityType;
 import org.openhab.core.library.types.StringType;
 import org.openhab.core.library.unit.Units;
+import org.openhab.core.thing.Channel;
 import org.openhab.core.thing.ChannelUID;
 import org.openhab.core.thing.Thing;
 import org.openhab.core.thing.ThingStatus;
@@ -64,8 +60,7 @@ import org.slf4j.LoggerFactory;
  * @author Hilbrand Bouwkamp - Initial contribution
  */
 @NonNullByDefault
-public class UniFiPoePortThingHandler
-        extends UniFiBaseThingHandler<Map<Integer, UniFiPortTuple>, UniFiPoePortThingConfig> {
+public class UniFiPoePortThingHandler extends UniFiBaseThingHandler<UniFiSwitchPorts, UniFiPoePortThingConfig> {
 
     private final Logger logger = LoggerFactory.getLogger(UniFiPoePortThingHandler.class);
 
@@ -84,20 +79,30 @@ public class UniFiPoePortThingHandler
                     "@text/error.thing.poe.offline.configuration_error");
             return false;
         }
-        final String channelConfigPoeEnableMode = (String) getThing().getChannel(CHANNEL_PORT_POE_ENABLE)
-                .getConfiguration().get(CHANNEL_ENABLE_PARAMETER_MODE);
-        poeEnableMode = channelConfigPoeEnableMode.isBlank() ? CHANNEL_ENABLE_PARAMETER_MODE_AUTO
-                : channelConfigPoeEnableMode;
-        return true;
+        return initPoeEnableMode();
+    }
+
+    private boolean initPoeEnableMode() {
+        final Channel channel = getThing().getChannel(CHANNEL_PORT_POE_ENABLE);
+
+        if (channel == null) {
+            return false;
+        } else {
+            final String channelConfigPoeEnableMode = (String) channel.getConfiguration()
+                    .get(CHANNEL_ENABLE_PARAMETER_MODE);
+            poeEnableMode = channelConfigPoeEnableMode.isBlank() ? CHANNEL_ENABLE_PARAMETER_MODE_AUTO
+                    : channelConfigPoeEnableMode;
+            return true;
+        }
     }
 
     @Override
-    protected @Nullable Map<Integer, UniFiPortTuple> getEntity(final UniFiControllerCache cache) {
+    protected @Nullable UniFiSwitchPorts getEntity(final UniFiControllerCache cache) {
         return cache.getSwitchPorts(config.getMacAddress());
     }
 
     @Override
-    protected State getChannelState(final Map<Integer, UniFiPortTuple> ports, final String channelId) {
+    protected State getChannelState(final UniFiSwitchPorts ports, final String channelId) {
         final UniFiPortTuple portTuple = getPort(ports);
 
         if (portTuple == null) {
@@ -143,30 +148,30 @@ public class UniFiPoePortThingHandler
         return UnDefType.NULL;
     }
 
-    private @Nullable UniFiPortTuple getPort(final Map<Integer, UniFiPortTuple> ports) {
-        return ports.get(config.getPortNumber());
+    private @Nullable UniFiPortTuple getPort(final UniFiSwitchPorts ports) {
+        return ports.getPort(config.getPortNumber());
     }
 
     @Override
-    protected boolean handleCommand(final UniFiController controller, final Map<Integer, UniFiPortTuple> ports,
+    protected boolean handleCommand(final UniFiController controller, final UniFiSwitchPorts ports,
             final ChannelUID channelUID, final Command command) throws UniFiException {
         final String channelID = channelUID.getIdWithoutGroup();
 
         switch (channelID) {
             case CHANNEL_PORT_POE_ENABLE:
                 if (command instanceof OnOffType) {
-                    return handleModeCommand(controller, ports, getPort(ports),
+                    return handleModeCommand(controller, ports,
                             OnOffType.ON == command ? poeEnableMode : CHANNEL_ENABLE_PARAMETER_MODE_OFF);
                 }
                 break;
             case CHANNEL_PORT_POE_MODE:
                 if (command instanceof StringType) {
-                    return handleModeCommand(controller, ports, getPort(ports), command.toFullString());
+                    return handleModeCommand(controller, ports, command.toFullString());
                 }
                 break;
             case CHANNEL_PORT_POE_CMD:
                 if (command instanceof StringType) {
-                    return handleCmd(controller, getPort(ports), command.toFullString());
+                    return handleCmd(controller, ports, command.toFullString());
                 }
             default:
                 return false;
@@ -174,39 +179,39 @@ public class UniFiPoePortThingHandler
         return false;
     }
 
-    private boolean handleModeCommand(final UniFiController controller, final Map<Integer, UniFiPortTuple> ports,
-            final @Nullable UniFiPortTuple uniFiPortTuple, final String poeMode) throws UniFiException {
-        final UniFiDevice device = controller.getCache().getDevice(config.getMacAddress());
-
-        if (device == null || uniFiPortTuple == null) {
-            logger.info("Could not change the PoE port state for thing '{}': device {} or portToUpdate {} null",
-                    getThing().getUID(), device, uniFiPortTuple);
-        } else {
-            final List<UnfiPortOverrideJsonElement> updatedList = ports.entrySet().stream()
-                    .map(e -> e.getValue().getJsonElement()).filter(Objects::nonNull).collect(Collectors.toList());
+    private boolean handleModeCommand(final UniFiController controller, final UniFiSwitchPorts ports,
+            final String poeMode) throws UniFiException {
+        final @Nullable UniFiDevice device = controller.getCache().getDevice(config.getMacAddress());
 
-            updatedList.stream().filter(p -> p.getPortIdx() == uniFiPortTuple.getPortIdx()).findAny()
-                    .ifPresent(p -> p.setPoeMode(poeMode));
-            controller.poeMode(device, updatedList);
+        if (canUpdate(device, ports) && device != null) {
+            controller.poeMode(device, ports.updatedList(config.getPortNumber(), p -> p.setPoeMode(poeMode)));
             // No refresh because UniFi device takes some time to update. Therefore a refresh would only show the
             // old state.
         }
         return true;
     }
 
-    private boolean handleCmd(final UniFiController controller, @Nullable final UniFiPortTuple portToUpdate,
-            final String command) throws UniFiException {
-        final UniFiDevice device = controller.getCache().getDevice(config.getMacAddress());
-        if (device == null || portToUpdate == null) {
-            logger.info("Could not change the PoE port state for thing '{}': device {} or portToUpdate {} null",
-                    getThing().getUID(), device, portToUpdate);
-        } else {
+    private boolean handleCmd(final UniFiController controller, final UniFiSwitchPorts ports, final String command)
+            throws UniFiException {
+        final @Nullable UniFiDevice device = controller.getCache().getDevice(config.getMacAddress());
+
+        if (canUpdate(device, ports) && device != null) {
             if (CHANNEL_PORT_POE_CMD_POWER_CYCLE.equalsIgnoreCase(command.replaceAll("[- ]", ""))) {
-                controller.poePowerCycle(device, portToUpdate.getPortIdx());
+                controller.poePowerCycle(device, config.getPortNumber());
             } else {
                 logger.info("Unknown command '{}' given to PoE port for thing '{}': device {} or portToUpdate {} null",
-                        command, getThing().getUID(), device, portToUpdate);
+                        command, getThing().getUID(), device, ports);
             }
+
+        }
+        return true;
+    }
+
+    private boolean canUpdate(final @Nullable UniFiDevice device, final UniFiSwitchPorts ports) {
+        if (device == null || getPort(ports) == null) {
+            logger.info("Could not change the PoE port state for thing '{}': device {} or portToUpdate {} null",
+                    getThing().getUID(), device, config.getPortNumber());
+            return false;
         }
         return true;
     }
index 0580b6b948df3f656442202180139045449423a3..477eb19958e374833835f87b18fc1cb3bc10403a 100644 (file)
@@ -21,7 +21,6 @@ import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER
 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER_WIFI_NAME;
 
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.concurrent.TimeUnit;
 import java.util.regex.Pattern;
 
@@ -34,6 +33,7 @@ import org.openhab.binding.unifi.internal.api.cache.UniFiControllerCache;
 import org.openhab.binding.unifi.internal.api.dto.UniFiClient;
 import org.openhab.binding.unifi.internal.api.dto.UniFiPortTuple;
 import org.openhab.binding.unifi.internal.api.dto.UniFiSite;
+import org.openhab.binding.unifi.internal.api.dto.UniFiSwitchPorts;
 import org.openhab.binding.unifi.internal.api.dto.UniFiWlan;
 import org.openhab.core.config.discovery.AbstractDiscoveryService;
 import org.openhab.core.config.discovery.DiscoveryResultBuilder;
@@ -161,9 +161,8 @@ public class UniFiThingDiscoveryService extends AbstractDiscoveryService
     }
 
     private void discoverPoePorts(final UniFiControllerCache cache, final ThingUID bridgeUID) {
-        for (final Map<Integer, UniFiPortTuple> uc : cache.getSwitchPorts()) {
-            for (final Entry<Integer, UniFiPortTuple> sp : uc.entrySet()) {
-                final UniFiPortTuple pt = sp.getValue();
+        for (final UniFiSwitchPorts uc : cache.getSwitchPorts()) {
+            for (final UniFiPortTuple pt : uc.getPoePorts()) {
                 final String deviceMac = pt.getDevice().getMac();
                 final String id = deviceMac.replace(":", "") + "_" + pt.getPortIdx();
                 final ThingUID thingUID = new ThingUID(UniFiBindingConstants.THING_TYPE_POE_PORT, bridgeUID, id);
index dd5f1bf1e19cfdcc10fff87752d970e3301de727..1de0ea01f4b4463c8a13305ecf8e4c3c5602248a 100644 (file)
@@ -127,7 +127,7 @@ channel-type.unifi.wpaMode.description = WPA Mode of the Wi-Fi network
 channel-type.config.unifi.poeEnable.mode.label = On Mode
 channel-type.config.unifi.poeEnable.mode.description = The value to set when setting PoE on.
 channel-type.config.unifi.poeEnable.mode.option.auto = Auto
-channel-type.config.unifi.poeEnable.mode.option.pasv24v = 24V
+channel-type.config.unifi.poeEnable.mode.option.pasv24 = 24V
 channel-type.config.unifi.poeEnable.mode.option.passthrough = Passthrough
 
 # status messages