2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.unifi.internal.handler;
15 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.CHANNEL_ENABLE;
16 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.CHANNEL_ESSID;
17 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.CHANNEL_GUEST_CLIENTS;
18 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.CHANNEL_PASSPHRASE;
19 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.CHANNEL_QRCODE_ENCODING;
20 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.CHANNEL_SECURITY;
21 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.CHANNEL_SITE;
22 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.CHANNEL_WIRELESS_CLIENTS;
23 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.CHANNEL_WLANBAND;
24 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.CHANNEL_WPAENC;
25 import static org.openhab.binding.unifi.internal.UniFiBindingConstants.CHANNEL_WPAMODE;
27 import java.util.function.Function;
29 import org.eclipse.jdt.annotation.NonNullByDefault;
30 import org.eclipse.jdt.annotation.Nullable;
31 import org.openhab.binding.unifi.internal.UniFiWlanThingConfig;
32 import org.openhab.binding.unifi.internal.api.UniFiController;
33 import org.openhab.binding.unifi.internal.api.UniFiException;
34 import org.openhab.binding.unifi.internal.api.cache.UniFiControllerCache;
35 import org.openhab.binding.unifi.internal.api.dto.UniFiClient;
36 import org.openhab.binding.unifi.internal.api.dto.UniFiSite;
37 import org.openhab.binding.unifi.internal.api.dto.UniFiWirelessClient;
38 import org.openhab.binding.unifi.internal.api.dto.UniFiWlan;
39 import org.openhab.core.library.types.DecimalType;
40 import org.openhab.core.library.types.OnOffType;
41 import org.openhab.core.library.types.StringType;
42 import org.openhab.core.thing.ChannelUID;
43 import org.openhab.core.thing.Thing;
44 import org.openhab.core.thing.ThingStatus;
45 import org.openhab.core.thing.ThingStatusDetail;
46 import org.openhab.core.types.Command;
47 import org.openhab.core.types.State;
48 import org.openhab.core.types.UnDefType;
52 * @author Hilbrand Bouwkamp - Initial contribution
55 public class UniFiWlanThingHandler extends UniFiBaseThingHandler<UniFiWlan, UniFiWlanThingConfig> {
57 private UniFiWlanThingConfig config = new UniFiWlanThingConfig();
59 public UniFiWlanThingHandler(final Thing thing) {
64 protected boolean initialize(final UniFiWlanThingConfig config) {
67 if (!config.isValid()) {
68 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
69 "@text/error.thing.wlan.offline.configuration_error");
76 protected @Nullable UniFiWlan getEntity(final UniFiControllerCache cache) {
77 return cache.getWlan(config.getWlanId());
81 protected State getChannelState(final UniFiWlan wlan, final String channelId) {
86 state = OnOffType.from(wlan.isEnabled());
89 state = StringType.valueOf(wlan.getName());
92 final UniFiSite site = wlan.getSite();
93 if (site != null && site.getDescription() != null && !site.getDescription().isBlank()) {
94 state = StringType.valueOf(site.getDescription());
96 state = UnDefType.UNDEF;
99 case CHANNEL_WIRELESS_CLIENTS:
100 state = countClients(wlan, c -> true);
102 case CHANNEL_GUEST_CLIENTS:
103 state = countClients(wlan, c -> c.isGuest());
105 case CHANNEL_SECURITY:
106 state = StringType.valueOf(wlan.getSecurity());
108 case CHANNEL_WLANBAND:
109 state = StringType.valueOf(wlan.getWlanBand());
112 state = StringType.valueOf(wlan.getWpaEnc());
114 case CHANNEL_WPAMODE:
115 state = StringType.valueOf(wlan.getWpaMode());
117 case CHANNEL_PASSPHRASE:
118 state = StringType.valueOf(wlan.getXPassphrase());
120 case CHANNEL_QRCODE_ENCODING:
121 state = qrcodeEncoding(wlan);
124 // Unsupported channel; nothing to update
125 state = UnDefType.NULL;
130 private static State countClients(final UniFiWlan wlan, final Function<UniFiClient, Boolean> filter) {
131 final UniFiSite site = wlan.getSite();
132 return new DecimalType(site.getCache().countClients(site, c -> c instanceof UniFiWirelessClient
133 && wlan.getName().equals(((UniFiWirelessClient) c).getEssid()) && filter.apply(c)));
137 * Returns a MERCARD like notation of the Wi-Fi access code. Format:
138 * <code>WIFI:S:<SSID>;T:WPA|blank;P:<password>;;</code>
140 * @param wlan wlan UniFi entity object containing the data
141 * @return MERCARD like Wi-Fi access format
142 * @see https://github.com/zxing/zxing/wiki/Barcode-Contents#wi-fi-network-config-android-ios-11
144 private static State qrcodeEncoding(final UniFiWlan wlan) {
145 final String name = encode(wlan.getName());
146 final String xPassphrase = wlan.getXPassphrase();
147 final boolean nopass = xPassphrase == null || xPassphrase.isBlank();
148 final String mode = nopass ? "nopass" : "WPA";
149 final String hidden = wlan.isHideSsid() ? "H:true" : "";
150 final String passcode = nopass ? "" : "P:" + encode(xPassphrase);
152 return StringType.valueOf(String.format("WIFI:S:%s;T:%s;%s;%s;", name, mode, passcode, hidden));
155 private static String encode(final @Nullable String value) {
156 return value == null ? "" : value.replaceAll("([\\;,\":])", "\\\\$1");
160 protected boolean handleCommand(final UniFiController controller, final UniFiWlan entity,
161 final ChannelUID channelUID, final Command command) throws UniFiException {
162 final String channelID = channelUID.getId();
164 if (CHANNEL_ENABLE.equals(channelID) && command instanceof OnOffType) {
165 controller.enableWifi(entity, OnOffType.ON == command);