From 698703b0c6949347957403600e7cdca87b556f60 Mon Sep 17 00:00:00 2001 From: Hilbrand Bouwkamp Date: Sat, 10 Sep 2022 16:59:42 +0200 Subject: [PATCH] [unifi] Fix portoverride to not remove any other data (#13362) When a user has configured additional settings on a PoE port, like name. These settings where lost when changing the PoEPort status in openHAB. This was because in the binding only some information of the override was stored and when writing the new state this information would have been sent too. In this change the object to store the override has been replaced by a plain json object. Therefore we don't have to know what is in it and all information is kept. Signed-off-by: Hilbrand Bouwkamp --- .../unifi/internal/api/UniFiController.java | 32 ++++++--- .../internal/api/UniFiControllerRequest.java | 1 + .../api/cache/UniFiControllerCache.java | 33 ++++++--- .../internal/api/dto/UnfiPortOverride.java | 72 ------------------- .../api/dto/UnfiPortOverrideJsonElement.java | 61 ++++++++++++++++ .../unifi/internal/api/dto/UniFiDevice.java | 7 ++ .../internal/api/dto/UniFiPortOverrides.java | 42 ----------- .../internal/api/dto/UniFiPortTable.java | 29 +++++--- .../internal/api/dto/UniFiPortTuple.java | 56 +++++++++++++++ ...fiPortOverrideJsonElementDeserializer.java | 37 ++++++++++ .../handler/UniFiPoePortThingHandler.java | 51 +++++++------ .../handler/UniFiThingDiscoveryService.java | 17 ++--- .../resources/OH-INF/i18n/unifi.properties | 4 +- 13 files changed, 265 insertions(+), 177 deletions(-) delete mode 100644 bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UnfiPortOverride.java create mode 100644 bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UnfiPortOverrideJsonElement.java delete mode 100644 bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiPortOverrides.java create mode 100644 bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiPortTuple.java create mode 100644 bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/util/UnfiPortOverrideJsonElementDeserializer.java diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/UniFiController.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/UniFiController.java index d0c6678a0..41f18fd3f 100644 --- a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/UniFiController.java +++ b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/UniFiController.java @@ -21,15 +21,16 @@ 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.UnfiPortOverride; +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.UniFiUnknownClient; import org.openhab.binding.unifi.internal.api.dto.UniFiWiredClient; import org.openhab.binding.unifi.internal.api.dto.UniFiWirelessClient; import org.openhab.binding.unifi.internal.api.dto.UniFiWlan; +import org.openhab.binding.unifi.internal.api.util.UnfiPortOverrideJsonElementDeserializer; import org.openhab.binding.unifi.internal.api.util.UniFiClientDeserializer; import org.openhab.binding.unifi.internal.api.util.UniFiClientInstanceCreator; import org.openhab.binding.unifi.internal.api.util.UniFiDeviceInstanceCreator; @@ -92,8 +93,9 @@ public class UniFiController { .registerTypeAdapter(UniFiUnknownClient.class, clientInstanceCreator) .registerTypeAdapter(UniFiWiredClient.class, clientInstanceCreator) .registerTypeAdapter(UniFiWirelessClient.class, clientInstanceCreator).create(); - this.poeGson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) - .excludeFieldsWithoutExposeAnnotation().create(); + this.poeGson = new GsonBuilder() + .registerTypeAdapter(UnfiPortOverrideJsonElement.class, new UnfiPortOverrideJsonElementDeserializer()) + .create(); } // Public API @@ -151,7 +153,7 @@ public class UniFiController { return cache; } - public @Nullable Map getSwitchPorts(@Nullable final String deviceId) { + public @Nullable Map getSwitchPorts(@Nullable final String deviceId) { return cache.getSwitchPorts(deviceId); } @@ -173,12 +175,20 @@ public class UniFiController { refresh(); } - public void poeMode(final UniFiDevice device, final Map data) throws UniFiException { - final UniFiControllerRequest req = newRequest(Void.class, HttpMethod.PUT, poeGson); - req.setAPIPath(String.format("/api/s/%s/rest/device/%s", device.getSite().getName(), device.getId())); - req.setBodyParameter("port_overrides", data.values()); - executeRequest(req); - refresh(); + public boolean poeMode(final UniFiDevice device, final List 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())) { + logger.info("Not overriding port for '{}', because port data contains empty json: {}", device.getName(), + poeGson.toJson(data)); + return false; + } else { + final UniFiControllerRequest req = newRequest(Void.class, HttpMethod.PUT, poeGson); + req.setAPIPath(String.format("/api/s/%s/rest/device/%s", device.getSite().getName(), device.getId())); + req.setBodyParameter("port_overrides", data); + executeRequest(req); + return true; + } } public void poePowerCycle(final UniFiDevice device, final Integer portIdx) throws UniFiException { diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/UniFiControllerRequest.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/UniFiControllerRequest.java index 4acf1e0b4..2d0215889 100644 --- a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/UniFiControllerRequest.java +++ b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/UniFiControllerRequest.java @@ -230,6 +230,7 @@ class UniFiControllerRequest { if (!bodyParameters.isEmpty()) { final String jsonBody = gson.toJson(bodyParameters); + logger.debug("Body parameters for request '{}': {}", request.getPath(), jsonBody); request.content( new StringContentProvider(CONTENT_TYPE_APPLICATION_JSON_UTF_8, jsonBody, StandardCharsets.UTF_8)); } diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/cache/UniFiControllerCache.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/cache/UniFiControllerCache.java index 174062cca..e8189ddaa 100644 --- a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/cache/UniFiControllerCache.java +++ b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/cache/UniFiControllerCache.java @@ -13,19 +13,20 @@ 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; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; -import java.util.stream.Collectors; 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.UniFiWlan; import org.slf4j.Logger; @@ -47,7 +48,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> devicesToPortTables = new ConcurrentHashMap<>(); + private final Map> devicesToPortTables = new ConcurrentHashMap<>(); public void clear() { sitesCache.clear(); @@ -93,9 +94,25 @@ public class UniFiControllerCache { if (devices != null) { Stream.of(devices).filter(Objects::nonNull).forEach(d -> { Stream.ofNullable(d.getPortTable()).filter(ptl -> ptl.length > 0 && ptl[0].isPortPoe()).forEach(pt -> { - Stream.of(pt).forEach(p -> p.setDevice(d)); - devicesToPortTables.put(d.getMac(), - Stream.of(pt).collect(Collectors.toMap(UniFiPortTable::getPortIdx, Function.identity()))); + final Map tupleTable = devicesToPortTables.computeIfAbsent(d.getMac(), + p -> new HashMap<>()); + + Stream.of(pt).forEach(p -> { + final UniFiPortTuple tuple = tupleTable.computeIfAbsent(p.getPortIdx(), + t -> new UniFiPortTuple()); + + tuple.setDevice(d); + tuple.setTable(p); + }); + }); + Stream.ofNullable(d.getPortOverrides()).filter(ptl -> ptl.length > 0).forEach(po -> { + final Map tupleTable = devicesToPortTables.get(d.getMac()); + + if (tupleTable != null) { + Stream.of(po).filter(pof -> !pof.getAsJsonObject().entrySet().isEmpty()) + .map(UnfiPortOverrideJsonElement::new) + .forEach(p -> tupleTable.get(p.getPortIdx()).setJsonElement(p)); + } }); }); } @@ -105,11 +122,11 @@ public class UniFiControllerCache { return devicesCache.get(id); } - public Map getSwitchPorts(@Nullable final String deviceId) { + public Map getSwitchPorts(@Nullable final String deviceId) { return deviceId == null ? Map.of() : devicesToPortTables.getOrDefault(deviceId, Map.of()); } - public Collection> getSwitchPorts() { + public Collection> getSwitchPorts() { return devicesToPortTables.values(); } diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UnfiPortOverride.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UnfiPortOverride.java deleted file mode 100644 index bd4a41ff0..000000000 --- a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UnfiPortOverride.java +++ /dev/null @@ -1,72 +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.annotations.Expose; - -/** - * The {@link UnfiPortOverride} represents the data model of UniFi port override. - * - * @author Hilbrand Bouwkamp - Initial contribution - */ -public class UnfiPortOverride { - - @Expose - private int portIdx; - - @Expose - private String portconfId; - - @Expose - private String poeMode; - - public UnfiPortOverride() { - // Constructor for GSON. - } - - public UnfiPortOverride(final int portIdx, final String portconfId, final String poeMode) { - this.portIdx = portIdx; - this.portconfId = portconfId; - this.poeMode = poeMode; - } - - public int getPortIdx() { - return portIdx; - } - - public String getPortconfId() { - return portconfId; - } - - public String getPoeMode() { - return poeMode; - } - - public void setPortIdx(final int portIdx) { - this.portIdx = portIdx; - } - - public void setPortconfId(final String portconfId) { - this.portconfId = portconfId; - } - - public void setPoeMode(final String poeMode) { - this.poeMode = poeMode; - } - - @Override - public String toString() { - return String.format("UnfiPortOverride{portIx: '%d', portconfId: '%s', poeMode: '%s'}", portIdx, portconfId, - poeMode); - } -} 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 new file mode 100644 index 000000000..2562503b7 --- /dev/null +++ b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UnfiPortOverrideJsonElement.java @@ -0,0 +1,61 @@ +/** + * 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/UniFiDevice.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiDevice.java index 0cd1e1b38..644ba3e63 100644 --- a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiDevice.java +++ b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiDevice.java @@ -15,6 +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.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; @@ -43,6 +44,8 @@ public class UniFiDevice implements HasId { private UniFiPortTable[] portTable; + private JsonElement[] portOverrides; + public UniFiDevice(final UniFiControllerCache cache) { this.cache = cache; } @@ -72,6 +75,10 @@ public class UniFiDevice implements HasId { return portTable; } + public JsonElement[] getPortOverrides() { + return portOverrides; + } + @Override public String toString() { return String.format("UniFiDevice{mac: '%s', name: '%s', model: '%s', site: %s}", mac, name, model, getSite()); diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiPortOverrides.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiPortOverrides.java deleted file mode 100644 index f6645781d..000000000 --- a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiPortOverrides.java +++ /dev/null @@ -1,42 +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 java.util.ArrayList; -import java.util.List; - -import com.google.gson.annotations.Expose; - -/** - * The {@link UniFiPortOverrides} represents the data model of UniFi port overrides. - * - * @author Hilbrand Bouwkamp - Initial contribution - */ -public class UniFiPortOverrides { - - @Expose - private final List portOverrides = new ArrayList<>(); - - public void addPortOverride(final UnfiPortOverride unfiPortOverride) { - portOverrides.add(unfiPortOverride); - } - - public void addPortOverride(final int portIdx, final String portconfId, final String poeMode) { - portOverrides.add(new UnfiPortOverride(portIdx, portconfId, poeMode)); - } - - @Override - public String toString() { - return String.format("UniFiPortOverrides: {}", String.join(", ", portOverrides.toArray(new String[0]))); - } -} diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiPortTable.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiPortTable.java index 637ed2dc5..6a4b239df 100644 --- a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiPortTable.java +++ b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiPortTable.java @@ -12,14 +12,23 @@ */ package org.openhab.binding.unifi.internal.api.dto; +import com.google.gson.annotations.Expose; + /** * The {@link UniFiPortTable} represents the data model of UniFi port table, which is an extend of port override. * * @author Hilbrand Bouwkamp - Initial contribution */ -public class UniFiPortTable extends UnfiPortOverride { +public class UniFiPortTable { - private transient UniFiDevice device; + @Expose + private int portIdx; + + @Expose + private String portconfId; + + @Expose + private String poeMode; private String name; @@ -40,12 +49,16 @@ public class UniFiPortTable extends UnfiPortOverride { private String poeCurrent; - public UniFiDevice getDevice() { - return device; + public int getPortIdx() { + return portIdx; } - public void setDevice(final UniFiDevice device) { - this.device = device; + public String getPortconfId() { + return portconfId; + } + + public String getPoeMode() { + return poeMode; } public String getName() { @@ -83,7 +96,7 @@ public class UniFiPortTable extends UnfiPortOverride { @Override public String toString() { return String.format( - "UniFiPortTable{name: '%s', enable: '%b', up: '%b', portPoe: '%b', poeEnable: '%b, poePower: '%s', poeVoltage: '%s', poeCurrent: '%s'}", - name, enable, up, portPoe, poeEnable, poePower, poeVoltage, poeCurrent); + "UniFiPortTable{portIx: '%d', portconfId: '%s', poeMode: '%s', name: '%s', enable: '%b', up: '%b', portPoe: '%b', poeEnable: '%b, poePower: '%s', poeVoltage: '%s', poeCurrent: '%s'}", + portIdx, portconfId, poeMode, name, enable, up, portPoe, poeEnable, poePower, poeVoltage, poeCurrent); } } diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiPortTuple.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiPortTuple.java new file mode 100644 index 000000000..fa9865d87 --- /dev/null +++ b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiPortTuple.java @@ -0,0 +1,56 @@ +/** + * 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; + +/** + * 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. + * + * @author Hilbrand Bouwkamp - Initial contribution + */ +public class UniFiPortTuple { + + private UniFiDevice device; + + private UniFiPortTable table; + + private UnfiPortOverrideJsonElement jsonElement; + + public UniFiDevice getDevice() { + return device; + } + + public void setDevice(final UniFiDevice device) { + this.device = device; + } + + public int getPortIdx() { + return table == null ? 0 : table.getPortIdx(); + } + + public UniFiPortTable getTable() { + return table; + } + + public void setTable(final UniFiPortTable table) { + this.table = table; + } + + public UnfiPortOverrideJsonElement getJsonElement() { + return jsonElement; + } + + public void setJsonElement(final UnfiPortOverrideJsonElement jsonElement) { + this.jsonElement = jsonElement; + } +} diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/util/UnfiPortOverrideJsonElementDeserializer.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/util/UnfiPortOverrideJsonElementDeserializer.java new file mode 100644 index 000000000..498a54d8b --- /dev/null +++ b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/util/UnfiPortOverrideJsonElementDeserializer.java @@ -0,0 +1,37 @@ +/** + * 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.util; + +import java.lang.reflect.Type; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.unifi.internal.api.dto.UnfiPortOverrideJsonElement; + +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. + * + * @author Hilbrand Bouwkamp - Initial contribution + */ +@NonNullByDefault +public class UnfiPortOverrideJsonElementDeserializer implements JsonSerializer { + + @Override + public JsonElement serialize(final UnfiPortOverrideJsonElement src, final Type typeOfSrc, + final JsonSerializationContext context) { + return src.getJsonObject(); + } +} diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/handler/UniFiPoePortThingHandler.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/handler/UniFiPoePortThingHandler.java index 236f1c091..e0edb5d37 100644 --- a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/handler/UniFiPoePortThingHandler.java +++ b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/handler/UniFiPoePortThingHandler.java @@ -25,8 +25,10 @@ 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.HashMap; +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; @@ -38,9 +40,10 @@ 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.UnfiPortOverride; +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.core.library.types.OnOffType; import org.openhab.core.library.types.QuantityType; import org.openhab.core.library.types.StringType; @@ -62,7 +65,7 @@ import org.slf4j.LoggerFactory; */ @NonNullByDefault public class UniFiPoePortThingHandler - extends UniFiBaseThingHandler, UniFiPoePortThingConfig> { + extends UniFiBaseThingHandler, UniFiPoePortThingConfig> { private final Logger logger = LoggerFactory.getLogger(UniFiPoePortThingHandler.class); @@ -89,13 +92,13 @@ public class UniFiPoePortThingHandler } @Override - protected @Nullable Map getEntity(final UniFiControllerCache cache) { + protected @Nullable Map getEntity(final UniFiControllerCache cache) { return cache.getSwitchPorts(config.getMacAddress()); } @Override - protected State getChannelState(final Map ports, final String channelId) { - final UniFiPortTable port = getPort(ports); + protected State getChannelState(final Map ports, final String channelId) { + final UniFiPortTable port = getPort(ports).getTable(); if (port == null) { logger.debug("No PoE port for thing '{}' could be found in the data. Refresh ignored.", @@ -129,12 +132,12 @@ public class UniFiPoePortThingHandler return state; } - private @Nullable UniFiPortTable getPort(final Map ports) { + private @Nullable UniFiPortTuple getPort(final Map ports) { return ports.get(config.getPortNumber()); } @Override - protected boolean handleCommand(final UniFiController controller, final Map ports, + protected boolean handleCommand(final UniFiController controller, final Map ports, final ChannelUID channelUID, final Command command) throws UniFiException { final String channelID = channelUID.getIdWithoutGroup(); @@ -160,44 +163,40 @@ public class UniFiPoePortThingHandler return false; } - private boolean handleModeCommand(final UniFiController controller, final Map ports, - final @Nullable UniFiPortTable portToUpdate, final String poeMode) throws UniFiException { + private boolean handleModeCommand(final UniFiController controller, final Map ports, + final @Nullable UniFiPortTuple uniFiPortTuple, final String poeMode) throws UniFiException { final UniFiDevice device = controller.getCache().getDevice(config.getMacAddress()); - if (device == null || portToUpdate == null) { + if (device == null || uniFiPortTuple == null) { logger.info("Could not change the PoE port state for thing '{}': device {} or portToUpdate {} null", - getThing().getUID(), device, portToUpdate); - return false; + getThing().getUID(), device, uniFiPortTuple); } else { - final UnfiPortOverride override = new UnfiPortOverride(); - override.setPortIdx(portToUpdate.getPortIdx()); - override.setPortconfId(portToUpdate.getPortconfId()); - override.setPoeMode(poeMode); - final Map newMap = new HashMap<>(ports); + final List updatedList = ports.entrySet().stream() + .map(e -> e.getValue().getJsonElement()).filter(Objects::nonNull).collect(Collectors.toList()); - newMap.put(portToUpdate.getPortIdx(), override); - controller.poeMode(device, newMap); - refresh(); - return true; + updatedList.stream().filter(p -> p.getPortIdx() == uniFiPortTuple.getPortIdx()).findAny() + .ifPresent(p -> p.setPoeMode(poeMode)); + controller.poeMode(device, updatedList); + // 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 UniFiPortTable portToUpdate, + 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); - return false; } else { if (CHANNEL_PORT_POE_CMD_POWER_CYCLE.equalsIgnoreCase(command.replaceAll("[- ]", ""))) { controller.poePowerCycle(device, portToUpdate.getPortIdx()); - return true; } else { logger.info("Unknown command '{}' given to PoE port for thing '{}': device {} or portToUpdate {} null", command, getThing().getUID(), device, portToUpdate); - return false; } } + return true; } } diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/handler/UniFiThingDiscoveryService.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/handler/UniFiThingDiscoveryService.java index f204154ca..0580b6b94 100644 --- a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/handler/UniFiThingDiscoveryService.java +++ b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/handler/UniFiThingDiscoveryService.java @@ -23,6 +23,7 @@ import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.TimeUnit; +import java.util.regex.Pattern; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; @@ -31,7 +32,7 @@ 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.UniFiClient; -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.UniFiWlan; import org.openhab.core.config.discovery.AbstractDiscoveryService; @@ -58,7 +59,7 @@ public class UniFiThingDiscoveryService extends AbstractDiscoveryService private static final int UNIFI_DISCOVERY_TIMEOUT_SECONDS = 30; private static final long TTL_SECONDS = TimeUnit.MINUTES.toSeconds(5); private static final int THING_ID_LENGTH = 8; - private static final String DEFAULT_PORTNAME = "Port"; + private static final Pattern DEFAULT_PORTNAME = Pattern.compile("Port \\d+"); private final Logger logger = LoggerFactory.getLogger(UniFiThingDiscoveryService.class); @@ -160,9 +161,9 @@ public class UniFiThingDiscoveryService extends AbstractDiscoveryService } private void discoverPoePorts(final UniFiControllerCache cache, final ThingUID bridgeUID) { - for (final Map uc : cache.getSwitchPorts()) { - for (final Entry sp : uc.entrySet()) { - final UniFiPortTable pt = sp.getValue(); + for (final Map uc : cache.getSwitchPorts()) { + for (final Entry sp : uc.entrySet()) { + final UniFiPortTuple pt = sp.getValue(); 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); @@ -182,9 +183,9 @@ public class UniFiThingDiscoveryService extends AbstractDiscoveryService * @param pt port object * @return label for the discovered PoE port */ - private static @Nullable String portName(final UniFiPortTable pt) { - final String portName = pt.getName(); + private @Nullable String portName(final UniFiPortTuple pt) { + final String portName = pt.getTable().getName(); - return portName.startsWith(DEFAULT_PORTNAME) ? pt.getDevice().getName() + " " + portName : portName; + return DEFAULT_PORTNAME.matcher(portName).find() ? pt.getDevice().getName() + " " + portName : portName; } } diff --git a/bundles/org.openhab.binding.unifi/src/main/resources/OH-INF/i18n/unifi.properties b/bundles/org.openhab.binding.unifi/src/main/resources/OH-INF/i18n/unifi.properties index ccd435fd0..b8c2d0e0e 100644 --- a/bundles/org.openhab.binding.unifi/src/main/resources/OH-INF/i18n/unifi.properties +++ b/bundles/org.openhab.binding.unifi/src/main/resources/OH-INF/i18n/unifi.properties @@ -69,6 +69,8 @@ channel-type.unifi.macAddress.label = MAC Address channel-type.unifi.macAddress.description = MAC address of the client channel-type.unifi.online.label = Online channel-type.unifi.online.description = Online status of the client +channel-type.unifi.passphrase.label = Passphrase +channel-type.unifi.passphrase.description = Passphrase of the Wi-Fi network channel-type.unifi.poeCmd.label = PoE Command channel-type.unifi.poeCmd.description = Command that can be given to the PoE port channel-type.unifi.poeCmd.command.option.power-cycle = Power Cycle @@ -119,8 +121,6 @@ channel-type.unifi.wpaEnc.label = WPA Encoding channel-type.unifi.wpaEnc.description = WPA Encoding of the Wi-Fi network channel-type.unifi.wpaMode.label = WPA Mode channel-type.unifi.wpaMode.description = WPA Mode of the Wi-Fi network -channel-type.unifi.passphrase.label = Passphrase -channel-type.unifi.passphrase.description = Passphrase of the Wi-Fi network # channel types config