diff --git a/bundles/org.openhab.binding.unifi/README.md b/bundles/org.openhab.binding.unifi/README.md index 07ae96ffe0..a59f5a7921 100644 --- a/bundles/org.openhab.binding.unifi/README.md +++ b/bundles/org.openhab.binding.unifi/README.md @@ -11,6 +11,7 @@ This binding integrates with [Ubiquiti UniFi Networks](https://www.ubnt.com/prod - `wiredClient` - A wired client connected to the UniFi network - `poePort` - A PoE (Power over Ethernet) port on a UniFi switch - `accessPoint` - An access point managed by the UniFi controller software +- `network` - A network managed by the UniFi controller software. ## Discovery @@ -114,6 +115,14 @@ The following table describes the `accessPoint` configuration parameters: | mac | The MAC address of the access point | Required | - | | site | The site where the access point should be found | Optional | - | +### `network` + +The following table describes the `network` configuration parameters: + +| Parameter | Description | Config | Default | +| ------------ | -------------------------------------------------------------|--------- | ------- | +| nid | The id of the network | Required | - | + ## Channels ### `site` @@ -252,6 +261,17 @@ The `accessPoint` information that is retrieved is available as these channels: | experience | Number:Dimensionless | The average health indication of the connected clients | Read | | led | Switch | Switch the LED on or off | Read, Write | +### `network` + +The `network` information that is retrieved is available as these channels: + +| Channel ID | Item Type | Description | Permissions | +|-----------------|-----------|---------------------------------------------------------------------------------|-------------| +| enable | Switch | Enable status of the network | Read, Write | +| totalClients | Number | Total number of clients connected | Read | +| site | String | UniFi Site the client is associated with | Read | +| purpose | String | Purpose of the network (e.g. Corporate, Guest, WAN, VPN, VLAN Only) | Read | + ## Rule Actions As an alternative to using the `guestVoucher` and `guestVouchersGenerate` channels on the `site` thing, it is possible to use rule actions on the thing to generate, revoke and list guest vouchers. diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/UniFiBindingConstants.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/UniFiBindingConstants.java index c076d1072d..de0b67d937 100644 --- a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/UniFiBindingConstants.java +++ b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/UniFiBindingConstants.java @@ -34,16 +34,18 @@ public final class UniFiBindingConstants { // List of all Thing Types public static final ThingTypeUID THING_TYPE_CONTROLLER = new ThingTypeUID(BINDING_ID, "controller"); public static final ThingTypeUID THING_TYPE_SITE = new ThingTypeUID(BINDING_ID, "site"); + public static final ThingTypeUID THING_TYPE_NETWORK = new ThingTypeUID(BINDING_ID, "network"); public static final ThingTypeUID THING_TYPE_WLAN = new ThingTypeUID(BINDING_ID, "wlan"); public static final ThingTypeUID THING_TYPE_WIRED_CLIENT = new ThingTypeUID(BINDING_ID, "wiredClient"); public static final ThingTypeUID THING_TYPE_WIRELESS_CLIENT = new ThingTypeUID(BINDING_ID, "wirelessClient"); public static final ThingTypeUID THING_TYPE_POE_PORT = new ThingTypeUID(BINDING_ID, "poePort"); public static final ThingTypeUID THING_TYPE_ACCESS_POINT = new ThingTypeUID(BINDING_ID, "accessPoint"); public static final Set ALL_THING_TYPE_SUPPORTED = Set.of(THING_TYPE_CONTROLLER, THING_TYPE_SITE, + THING_TYPE_NETWORK, THING_TYPE_WLAN, THING_TYPE_WIRED_CLIENT, THING_TYPE_WIRELESS_CLIENT, + THING_TYPE_POE_PORT, THING_TYPE_ACCESS_POINT); + public static final Set THING_TYPE_SUPPORTED = Set.of(THING_TYPE_SITE, THING_TYPE_NETWORK, THING_TYPE_WLAN, THING_TYPE_WIRED_CLIENT, THING_TYPE_WIRELESS_CLIENT, THING_TYPE_POE_PORT, THING_TYPE_ACCESS_POINT); - public static final Set THING_TYPE_SUPPORTED = Set.of(THING_TYPE_SITE, THING_TYPE_WLAN, - THING_TYPE_WIRED_CLIENT, THING_TYPE_WIRELESS_CLIENT, THING_TYPE_POE_PORT, THING_TYPE_ACCESS_POINT); // List of site channels public static final String CHANNEL_TOTAL_CLIENTS = "totalClients"; @@ -53,6 +55,9 @@ public final class UniFiBindingConstants { public static final String CHANNEL_GUEST_VOUCHER = "guestVoucher"; public static final String CHANNEL_GUEST_VOUCHERS_GENERATE = "guestVouchersGenerate"; + // List of network channels + public static final String CHANNEL_PURPOSE = "purpose"; + // List of wlan channels public static final String CHANNEL_SECURITY = "security"; public static final String CHANNEL_WLANBAND = "wlanBand"; @@ -112,6 +117,7 @@ public final class UniFiBindingConstants { public static final String PARAMETER_CID = "cid"; public static final String PARAMETER_SID = "sid"; public static final String PARAMETER_WID = "wid"; + public static final String PARAMETER_NID = "nid"; public static final String PARAMETER_VOUCHER_COUNT = "voucherCount"; public static final String PARAMETER_VOUCHER_EXPIRATION = "voucherExpiration"; public static final String PARAMETER_VOUCHER_USERS = "voucherUsers"; diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/UniFiNetworkThingConfig.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/UniFiNetworkThingConfig.java new file mode 100644 index 0000000000..dffea55cb9 --- /dev/null +++ b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/UniFiNetworkThingConfig.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010-2025 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; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.openhab.binding.unifi.internal.handler.UniFiNetworkThingHandler; + +/** + * The {@link UniFiNetworkThingConfig} encapsulates all the configuration options for an instance of the + * {@link UniFiNetworkThingHandler}. + * + * @author Thomas Lauterbach - Initial contribution + */ +@NonNullByDefault +@SuppressWarnings("unused") +public class UniFiNetworkThingConfig { + + private String nid = ""; + + public String getNetworkId() { + return nid; + } + + private void setNetworkId(final String nid) { + // method to avoid auto format mark the field as final + this.nid = nid; + } + + public boolean isValid() { + return !nid.isBlank(); + } + + @Override + public String toString() { + return String.format("UniFiNetworkThingConfig{nid: '%s'}", nid); + } +} diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/UniFiThingHandlerFactory.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/UniFiThingHandlerFactory.java index ed957b8ec0..5b35de8c7f 100644 --- a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/UniFiThingHandlerFactory.java +++ b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/UniFiThingHandlerFactory.java @@ -21,6 +21,7 @@ import org.eclipse.jetty.util.ssl.SslContextFactory; import org.openhab.binding.unifi.internal.handler.UniFiAccessPointThingHandler; import org.openhab.binding.unifi.internal.handler.UniFiClientThingHandler; import org.openhab.binding.unifi.internal.handler.UniFiControllerThingHandler; +import org.openhab.binding.unifi.internal.handler.UniFiNetworkThingHandler; import org.openhab.binding.unifi.internal.handler.UniFiPoePortThingHandler; import org.openhab.binding.unifi.internal.handler.UniFiSiteThingHandler; import org.openhab.binding.unifi.internal.handler.UniFiWlanThingHandler; @@ -82,6 +83,8 @@ public class UniFiThingHandlerFactory extends BaseThingHandlerFactory { return new UniFiControllerThingHandler((Bridge) thing, httpClient); } else if (THING_TYPE_SITE.equals(thingTypeUID)) { return new UniFiSiteThingHandler(thing); + } else if (THING_TYPE_NETWORK.equals(thingTypeUID)) { + return new UniFiNetworkThingHandler(thing); } else if (THING_TYPE_WLAN.equals(thingTypeUID)) { return new UniFiWlanThingHandler(thing); } else if (THING_TYPE_WIRELESS_CLIENT.equals(thingTypeUID) || THING_TYPE_WIRED_CLIENT.equals(thingTypeUID)) { 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 0864f64c7e..d25216803d 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 @@ -14,6 +14,7 @@ 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; @@ -23,6 +24,7 @@ import org.openhab.binding.unifi.internal.api.cache.UniFiControllerCache; 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.UniFiNetwork; 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; @@ -34,6 +36,7 @@ import org.openhab.binding.unifi.internal.api.util.UnfiPortOverrideJsonElementDe 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; +import org.openhab.binding.unifi.internal.api.util.UniFiNetworkInstanceCreator; import org.openhab.binding.unifi.internal.api.util.UniFiSiteInstanceCreator; import org.openhab.binding.unifi.internal.api.util.UniFiVoucherInstanceCreator; import org.openhab.binding.unifi.internal.api.util.UniFiWlanInstanceCreator; @@ -87,12 +90,14 @@ public class UniFiController { this.timeoutSeconds = timeoutSeconds; this.csrfToken = ""; final UniFiSiteInstanceCreator siteInstanceCreator = new UniFiSiteInstanceCreator(cache); + final UniFiNetworkInstanceCreator networkInstanceCreator = new UniFiNetworkInstanceCreator(cache); final UniFiWlanInstanceCreator wlanInstanceCreator = new UniFiWlanInstanceCreator(cache); final UniFiDeviceInstanceCreator deviceInstanceCreator = new UniFiDeviceInstanceCreator(cache); final UniFiClientInstanceCreator clientInstanceCreator = new UniFiClientInstanceCreator(cache); final UniFiVoucherInstanceCreator voucherInstanceCreator = new UniFiVoucherInstanceCreator(cache); this.gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) .registerTypeAdapter(UniFiSite.class, siteInstanceCreator) + .registerTypeAdapter(UniFiNetwork.class, networkInstanceCreator) .registerTypeAdapter(UniFiWlan.class, wlanInstanceCreator) .registerTypeAdapter(UniFiDevice.class, deviceInstanceCreator) .registerTypeAdapter(UniFiClient.class, new UniFiClientDeserializer()) @@ -149,6 +154,7 @@ public class UniFiController { synchronized (this) { cache.clear(); final Collection sites = refreshSites(); + refreshNetworks(sites); refreshWlans(sites); refreshDevices(sites); refreshClients(sites); @@ -208,8 +214,17 @@ public class UniFiController { refresh(); } + public void enableNetwork(final UniFiNetwork network, final boolean enable) throws UniFiException { + final UniFiControllerRequest req = newRequest(Void.class, HttpMethod.PUT, gson); + req.setAPIPath(String.format("/api/s/%s/group/networkconf", network.getSite().getName())); + req.setBodyParameter("data", Map.of("enabled", enable)); + req.setBodyParameter("id", new String[] { network.getId() }); + executeRequest(req); + refresh(); + } + public void enableWifi(final UniFiWlan wlan, final boolean enable) throws UniFiException { - final UniFiControllerRequest req = newRequest(Void.class, HttpMethod.PUT, poeGson); + final UniFiControllerRequest req = newRequest(Void.class, HttpMethod.PUT, gson); req.setAPIPath(String.format("/api/s/%s/rest/wlanconf/%s", wlan.getSite().getName(), wlan.getId())); req.setBodyParameter("_id", wlan.getId()); req.setBodyParameter("enabled", enable ? "true" : "false"); @@ -309,6 +324,18 @@ public class UniFiController { return cache.setSites(executeRequest(req)); } + private void refreshNetworks(final Collection sites) throws UniFiException { + for (final UniFiSite site : sites) { + cache.putNetworks(getNetworks(site)); + } + } + + private UniFiNetwork @Nullable [] getNetworks(final UniFiSite site) throws UniFiException { + final UniFiControllerRequest req = newRequest(UniFiNetwork[].class, HttpMethod.GET, gson); + req.setAPIPath(String.format("/api/s/%s/rest/networkconf", site.getName())); + return executeRequest(req); + } + private void refreshWlans(final Collection sites) throws UniFiException { for (final UniFiSite site : sites) { cache.putWlans(getWlans(site)); 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 c0fb0c87ca..5c55869e60 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 @@ -25,6 +25,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; 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.UniFiNetwork; 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; @@ -46,6 +47,7 @@ public class UniFiControllerCache { private final Logger logger = LoggerFactory.getLogger(UniFiControllerCache.class); private final UniFiSiteCache sitesCache = new UniFiSiteCache(); + private final UniFiNetworkCache networksCache = new UniFiNetworkCache(); private final UniFiWlanCache wlansCache = new UniFiWlanCache(); private final UniFiDeviceCache devicesCache = new UniFiDeviceCache(); private final UniFiClientCache clientsCache = new UniFiClientCache(); @@ -55,6 +57,7 @@ public class UniFiControllerCache { public void clear() { sitesCache.clear(); + networksCache.clear(); wlansCache.clear(); devicesCache.clear(); clientsCache.clear(); @@ -77,6 +80,20 @@ public class UniFiControllerCache { return sitesCache.values(); } + // Networks Cache + + public void putNetworks(final UniFiNetwork @Nullable [] networks) { + networksCache.putAll(networks); + } + + public @Nullable UniFiNetwork getNetwork(@Nullable final String id) { + return networksCache.get(id); + } + + public Collection getNetworks() { + return networksCache.values(); + } + // Wlans Cache public void putWlans(final UniFiWlan @Nullable [] wlans) { diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/cache/UniFiNetworkCache.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/cache/UniFiNetworkCache.java new file mode 100644 index 0000000000..caaa78f5ac --- /dev/null +++ b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/cache/UniFiNetworkCache.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010-2025 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.cache; + +import static org.openhab.binding.unifi.internal.api.cache.UniFiCache.Prefix.ID; +import static org.openhab.binding.unifi.internal.api.cache.UniFiCache.Prefix.NAME; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.unifi.internal.api.dto.UniFiNetwork; + +/** + * The {@link UniFiNetworkCache} is a specific implementation of {@link UniFiCache} for the purpose of caching + * {@link UniFiNetwork} instances. + * + * The cache uses the following prefixes: id, name + * + * @author Thomas Lauterbach - Initial contribution + */ +@NonNullByDefault +class UniFiNetworkCache extends UniFiCache { + + public UniFiNetworkCache() { + super(ID, NAME); + } + + @Override + protected @Nullable String getSuffix(final UniFiNetwork network, final Prefix prefix) { + switch (prefix) { + case ID: + return network.getId(); + case NAME: + return network.getName(); + default: + return null; + } + } +} diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiClient.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiClient.java index f76059779b..ad00beb152 100644 --- a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiClient.java +++ b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiClient.java @@ -61,6 +61,8 @@ public abstract class UniFiClient implements HasId { @SerializedName("satisfaction") private Integer experience; + private String networkId; + protected UniFiClient(final UniFiControllerCache cache) { this.cache = cache; } @@ -122,6 +124,10 @@ public abstract class UniFiClient implements HasId { return experience; } + public UniFiNetwork getNetwork() { + return cache.getNetwork(networkId); + } + @Override public String toString() { return String.format( diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiNetwork.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiNetwork.java new file mode 100644 index 0000000000..310dff581b --- /dev/null +++ b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/dto/UniFiNetwork.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2010-2025 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 org.openhab.binding.unifi.internal.api.cache.UniFiControllerCache; + +import com.google.gson.annotations.SerializedName; + +/** + * @author Thomas Lauterbach - Initial contribution + */ +public class UniFiNetwork implements HasId { + + protected final transient UniFiControllerCache cache; + + @SerializedName("_id") + private String id; + + private String name; + + private String siteId; + + private boolean enabled; + + private String purpose; + + public UniFiNetwork(final UniFiControllerCache cache) { + this.cache = cache; + } + + @Override + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public UniFiSite getSite() { + return cache.getSite(siteId); + } + + public boolean isEnabled() { + return enabled; + } + + public String getPurpose() { + return purpose; + } + + @Override + public String toString() { + return String.format("UniFiNetwork{id: '%s', name: '%s'}", id, name); + } +} diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/util/UniFiNetworkInstanceCreator.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/util/UniFiNetworkInstanceCreator.java new file mode 100644 index 0000000000..018dfdf8f8 --- /dev/null +++ b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/api/util/UniFiNetworkInstanceCreator.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010-2025 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.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.unifi.internal.api.cache.UniFiControllerCache; +import org.openhab.binding.unifi.internal.api.dto.UniFiNetwork; + +import com.google.gson.InstanceCreator; +import com.google.gson.JsonSyntaxException; + +/** + * The {@link UniFiNetworkInstanceCreator} creates instances of {@link UniFiNetwork}s during the JSON unmarshalling of + * controller responses. + * + * @author Thomas Lauterbach - Initial contribution + */ +@NonNullByDefault +public class UniFiNetworkInstanceCreator implements InstanceCreator { + + private final UniFiControllerCache cache; + + public UniFiNetworkInstanceCreator(final UniFiControllerCache cache) { + this.cache = cache; + } + + @Override + public UniFiNetwork createInstance(final @Nullable Type type) { + if (UniFiNetwork.class.equals(type)) { + return new UniFiNetwork(cache); + } else { + throw new JsonSyntaxException("Expected a UniFiNetwork type, but got " + type); + } + } +} diff --git a/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/handler/UniFiNetworkThingHandler.java b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/handler/UniFiNetworkThingHandler.java new file mode 100644 index 0000000000..dd03fe1bbd --- /dev/null +++ b/bundles/org.openhab.binding.unifi/src/main/java/org/openhab/binding/unifi/internal/handler/UniFiNetworkThingHandler.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2010-2025 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.handler; + +import static org.openhab.binding.unifi.internal.UniFiBindingConstants.*; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.openhab.binding.unifi.internal.UniFiNetworkThingConfig; +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.UniFiNetwork; +import org.openhab.binding.unifi.internal.api.dto.UniFiSite; +import org.openhab.core.library.types.DecimalType; +import org.openhab.core.library.types.OnOffType; +import org.openhab.core.library.types.StringType; +import org.openhab.core.thing.ChannelUID; +import org.openhab.core.thing.Thing; +import org.openhab.core.thing.ThingStatus; +import org.openhab.core.thing.ThingStatusDetail; +import org.openhab.core.types.Command; +import org.openhab.core.types.State; +import org.openhab.core.types.UnDefType; + +/** + * The {@link UniFiNetworkThingHandler} is responsible for handling commands and status updates for a wireless network. + * + * @author Thomas Lauterbach - Initial contribution + */ +@NonNullByDefault +public class UniFiNetworkThingHandler extends UniFiBaseThingHandler { + + private UniFiNetworkThingConfig config = new UniFiNetworkThingConfig(); + + public UniFiNetworkThingHandler(final Thing thing) { + super(thing); + } + + @Override + protected boolean initialize(final UniFiNetworkThingConfig config) { + this.config = config; + + if (!config.isValid()) { + updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, + "@text/error.thing.network.offline.configuration_error"); + return false; + } + return true; + } + + @Override + protected @Nullable UniFiNetwork getEntity(final UniFiControllerCache cache) { + return cache.getNetwork(config.getNetworkId()); + } + + @Override + protected State getChannelState(final UniFiNetwork network, final String channelId) { + final State state; + + switch (channelId) { + case CHANNEL_ENABLE: + state = OnOffType.from(network.isEnabled()); + break; + case CHANNEL_SITE: + final UniFiSite site = network.getSite(); + if (site != null && site.getDescription() != null && !site.getDescription().isBlank()) { + state = StringType.valueOf(site.getDescription()); + } else { + state = UnDefType.UNDEF; + } + break; + case CHANNEL_PURPOSE: + state = StringType.valueOf(network.getPurpose()); + break; + case CHANNEL_TOTAL_CLIENTS: + state = countClients(network); + break; + default: + // Unsupported channel; nothing to update + state = UnDefType.NULL; + } + return state; + } + + private static State countClients(final UniFiNetwork network) { + final UniFiSite site = network.getSite(); + + if (site == null) { + return UnDefType.UNDEF; + } else { + return new DecimalType(site.getCache().countClients(site, c -> c instanceof UniFiClient client + && (network.getName() != null && network.equals(client.getNetwork())))); + } + } + + @Override + protected boolean handleCommand(final UniFiController controller, final UniFiNetwork network, + final ChannelUID channelUID, Command command) throws UniFiException { + final String channelID = channelUID.getId(); + + if (CHANNEL_ENABLE.equals(channelID) && command instanceof OnOffType && network.getSite() != null) { + controller.enableNetwork(network, OnOffType.ON == command); + return true; + } + return false; + } +} 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 5185586e74..ef9203693a 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 @@ -15,6 +15,7 @@ package org.openhab.binding.unifi.internal.handler; import static org.openhab.binding.unifi.internal.UniFiBindingConstants.DEVICE_TYPE_UAP; import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER_CID; import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER_MAC_ADDRESS; +import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER_NID; import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER_PORT_NUMBER; import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER_SID; import static org.openhab.binding.unifi.internal.UniFiBindingConstants.PARAMETER_SITE; @@ -32,6 +33,7 @@ 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.UniFiDevice; +import org.openhab.binding.unifi.internal.api.dto.UniFiNetwork; 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; @@ -81,6 +83,7 @@ public class UniFiThingDiscoveryService extends AbstractThingHandlerDiscoverySer final ThingUID bridgeUID = thingHandler.getThing().getUID(); discoverSites(cache, bridgeUID); + discoverNetworks(cache, bridgeUID); discoverWlans(cache, bridgeUID); discoverClients(cache, bridgeUID); discoverPoePorts(cache, bridgeUID); @@ -102,6 +105,19 @@ public class UniFiThingDiscoveryService extends AbstractThingHandlerDiscoverySer } } + private void discoverNetworks(final UniFiControllerCache cache, final ThingUID bridgeUID) { + for (final UniFiNetwork network : cache.getNetworks()) { + final ThingUID thingUID = new ThingUID(UniFiBindingConstants.THING_TYPE_NETWORK, bridgeUID, + stripIdShort(network.getId())); + final Map properties = Map.of(PARAMETER_NID, network.getId()); + + thingDiscovered( + DiscoveryResultBuilder.create(thingUID).withThingType(UniFiBindingConstants.THING_TYPE_NETWORK) + .withBridge(bridgeUID).withRepresentationProperty(PARAMETER_NID).withTTL(TTL_SECONDS) + .withProperties(properties).withLabel(network.getName()).build()); + } + } + private void discoverWlans(final UniFiControllerCache cache, final ThingUID bridgeUID) { for (final UniFiWlan wlan : cache.getWlans()) { final ThingUID thingUID = new ThingUID(UniFiBindingConstants.THING_TYPE_WLAN, bridgeUID, diff --git a/bundles/org.openhab.binding.unifi/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.binding.unifi/src/main/resources/OH-INF/config/config.xml index bfb1e68ace..b80f3495ec 100644 --- a/bundles/org.openhab.binding.unifi/src/main/resources/OH-INF/config/config.xml +++ b/bundles/org.openhab.binding.unifi/src/main/resources/OH-INF/config/config.xml @@ -50,6 +50,13 @@ + + + + The id or name of the network + + + 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 23d0882aaf..621a8d25c8 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 @@ -17,6 +17,9 @@ thing-type.unifi.accessPoint.channel.online.description = Online status of the d thing-type.unifi.accessPoint.channel.uptime.description = Uptime of the device (in seconds) thing-type.unifi.controller.label = UniFi Controller thing-type.unifi.controller.description = A UniFi controller +thing-type.unifi.network.label = UniFi Network +thing-type.unifi.network.description = A UniFi Network +thing-type.unifi.network.channel.site.description = UniFi Site the network is associated with thing-type.unifi.poePort.label = UniFi PoE Port thing-type.unifi.poePort.description = A Power Over Ethernet (PoE) port on a UniFi switch thing-type.unifi.site.label = UniFi Site @@ -54,6 +57,8 @@ thing-type.config.unifi.controller.unifios.label = UniFi OS thing-type.config.unifi.controller.unifios.description = If the UniFi Controller is running on UniFi OS. thing-type.config.unifi.controller.username.label = Username thing-type.config.unifi.controller.username.description = The username to access the UniFi Controller. +thing-type.config.unifi.network.nid.label = Network Id +thing-type.config.unifi.network.nid.description = The id or name of the network thing-type.config.unifi.poePort.macAddress.label = Switch MAC Address thing-type.config.unifi.poePort.macAddress.description = The MAC address of the switch this port is part of thing-type.config.unifi.poePort.portNumber.label = Port Number @@ -106,6 +111,8 @@ channel-type.unifi.macAddress.label = MAC Address channel-type.unifi.macAddress.description = MAC address of the client channel-type.unifi.name.label = Name channel-type.unifi.name.description = Name of the client +channel-type.unifi.networkEnable.label = Enable +channel-type.unifi.networkEnable.description = Enable status of the network channel-type.unifi.online.label = Online channel-type.unifi.online.description = Online status of the client channel-type.unifi.passphrase.label = Passphrase @@ -129,6 +136,13 @@ channel-type.unifi.poeVoltage.label = Port PoE Voltage channel-type.unifi.poeVoltage.description = Voltage usage of the PoE port channel-type.unifi.portOnline.label = Port Active channel-type.unifi.portOnline.description = PoE port is active +channel-type.unifi.purpose.label = Purpose +channel-type.unifi.purpose.description = Purpose of the network +channel-type.unifi.purpose.state.option.corporate = Corporate +channel-type.unifi.purpose.state.option.guest = Guest +channel-type.unifi.purpose.state.option.remote-user-vpn = VPN +channel-type.unifi.purpose.state.option.wan = WAN +channel-type.unifi.purpose.state.option.vlan-only = VLAN Only channel-type.unifi.qrcodeEncoding.label = QR Code Encoding channel-type.unifi.qrcodeEncoding.description = MECARD like encoding to generate a QRCode for easy access to the Wi-Fi network channel-type.unifi.reconnect.label = Reconnect @@ -195,6 +209,7 @@ error.thing.poe.offline.configuration_error = The configuration parameter macAdd error.thing.poe.offline.nodata_error = No data for the PoE port could be found in the UniFi API data. See TRACE log for actual API data. error.thing.site.offline.configuration_error = The configuration parameter sid must be set and not be empty. error.thing.ap.offline.configuration_error = The configuration parameter mac must be set and not be empty +error.thing.network.offline.configuration_error = The configuration parameter networkId must be set and not be empty # actions diff --git a/bundles/org.openhab.binding.unifi/src/main/resources/OH-INF/thing/thing-types.xml b/bundles/org.openhab.binding.unifi/src/main/resources/OH-INF/thing/thing-types.xml index a62853f31a..0d4b816634 100644 --- a/bundles/org.openhab.binding.unifi/src/main/resources/OH-INF/thing/thing-types.xml +++ b/bundles/org.openhab.binding.unifi/src/main/resources/OH-INF/thing/thing-types.xml @@ -34,6 +34,27 @@ + + + + + + + A UniFi Network + + + + + + UniFi Site the network is associated with + + + + nid + + + + @@ -491,4 +512,25 @@ + + String + + Purpose of the network + + + + + + + + + + + + + Switch + + Enable status of the network + +