Skip to content

Commit

Permalink
[unifi] Fix portoverride to not remove any other data
Browse files Browse the repository at this point in the history
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 th new state this information would have been send too.
In this change the object to store the override has been replaced by a plain json object. Therefor we don't have to know what is in it and all information is kept.

Signed-off-by: Hilbrand Bouwkamp <[email protected]>
  • Loading branch information
Hilbrand committed Sep 7, 2022
1 parent 831e7ec commit 11d6783
Show file tree
Hide file tree
Showing 13 changed files with 273 additions and 175 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,16 @@
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;
Expand Down Expand Up @@ -92,8 +93,9 @@ public UniFiController(final HttpClient httpClient, final String host, final int
.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
Expand Down Expand Up @@ -151,7 +153,7 @@ public UniFiControllerCache getCache() {
return cache;
}

public @Nullable Map<Integer, UniFiPortTable> getSwitchPorts(@Nullable final String deviceId) {
public @Nullable Map<Integer, UniFiPortTuple> getSwitchPorts(@Nullable final String deviceId) {
return cache.getSwitchPorts(deviceId);
}

Expand All @@ -173,12 +175,20 @@ public void reconnect(final UniFiClient client) throws UniFiException {
refresh();
}

public void poeMode(final UniFiDevice device, final Map<Integer, UnfiPortOverride> data) throws UniFiException {
final UniFiControllerRequest<Void> 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<UnfiPortOverrideJsonElement> 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<Void> 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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ private Request newRequest() {
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));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<String, Map<Integer, UniFiPortTable>> devicesToPortTables = new ConcurrentHashMap<>();
private final Map<String, Map<Integer, UniFiPortTuple>> devicesToPortTables = new ConcurrentHashMap<>();

public void clear() {
sitesCache.clear();
Expand Down Expand Up @@ -93,9 +94,25 @@ public void putDevices(final UniFiDevice @Nullable [] devices) {
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<Integer, UniFiPortTuple> 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<Integer, UniFiPortTuple> 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));
}
});
});
}
Expand All @@ -105,11 +122,11 @@ public void putDevices(final UniFiDevice @Nullable [] devices) {
return devicesCache.get(id);
}

public Map<Integer, UniFiPortTable> getSwitchPorts(@Nullable final String deviceId) {
public Map<Integer, UniFiPortTuple> getSwitchPorts(@Nullable final String deviceId) {
return deviceId == null ? Map.of() : devicesToPortTables.getOrDefault(deviceId, Map.of());
}

public Collection<Map<Integer, UniFiPortTable>> getSwitchPorts() {
public Collection<Map<Integer, UniFiPortTuple>> getSwitchPorts() {
return devicesToPortTables.values();
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
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;

Expand Down Expand Up @@ -43,6 +44,8 @@ public class UniFiDevice implements HasId {

private UniFiPortTable[] portTable;

private JsonElement[] portOverrides;

public UniFiDevice(final UniFiControllerCache cache) {
this.cache = cache;
}
Expand Down Expand Up @@ -72,6 +75,10 @@ public UniFiPortTable[] getPortTable() {
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());
Expand Down

This file was deleted.

Loading

0 comments on commit 11d6783

Please sign in to comment.