[unifi] Fix bug with combination of other data/ports (#14060)

- 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>
This commit is contained in:
Hilbrand Bouwkamp 2022-12-31 11:03:56 +01:00 committed by GitHub
parent 2d5402656e
commit 202a8647e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 241 additions and 105 deletions

View 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;
}

View 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;

View 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);
}
}
}
}

View 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());
Stream.ofNullable(d.getPortTable()).forEach(pt -> {
final UniFiSwitchPorts switchPorts = devicesToPortTables.computeIfAbsent(d.getMac(),
p -> new UniFiSwitchPorts());
tuple.setDevice(d);
tuple.setTable(p);
});
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();
}

View File

@ -38,7 +38,8 @@ class UniFiDeviceCache extends UniFiCache<UniFiDevice> {
switch (prefix) {
case MAC:
return device.getMac();
default:
return null;
}
return null;
}
}

View File

@ -12,7 +12,6 @@
*/
package org.openhab.binding.unifi.internal.api.dto;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/**
@ -22,7 +21,7 @@ import com.google.gson.JsonObject;
*
* @author Hilbrand Bouwkamp - Initial contribution
*/
public class UnfiPortOverrideJsonElement {
public class UnfiPortOverrideJsonObject {
private static final String PORT_IDX = "port_idx";
private static final String PORT_CONF_ID = "port_conf_id";
@ -30,14 +29,18 @@ public class UnfiPortOverrideJsonElement {
private final JsonObject jsonObject;
public UnfiPortOverrideJsonElement(final JsonElement element) {
this.jsonObject = element.getAsJsonObject();
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();
}

View 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;
}

View 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;
}
}

View File

@ -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);
}
}
}

View 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();
}

View 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));

View 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();
}
});
}

View 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());
private boolean handleModeCommand(final UniFiController controller, final UniFiSwitchPorts ports,
final String poeMode) throws UniFiException {
final @Nullable 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());
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;
}

View 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);

View 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