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.Predicate;
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;
51 * The {@link UniFiWlanThingHandler} is responsible for handling commands and status updates for a wireless network.
53 * @author Hilbrand Bouwkamp - Initial contribution
56 public class UniFiWlanThingHandler extends UniFiBaseThingHandler<UniFiWlan, UniFiWlanThingConfig> {
58 private UniFiWlanThingConfig config = new UniFiWlanThingConfig();
60 public UniFiWlanThingHandler(final Thing thing) {
65 protected boolean initialize(final UniFiWlanThingConfig config) {
68 if (!config.isValid()) {
69 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
70 "@text/error.thing.wlan.offline.configuration_error");
77 protected @Nullable UniFiWlan getEntity(final UniFiControllerCache cache) {
78 return cache.getWlan(config.getWlanId());
82 protected State getChannelState(final UniFiWlan wlan, final String channelId) {
87 state = OnOffType.from(wlan.isEnabled());
90 state = StringType.valueOf(wlan.getName());
93 final UniFiSite site = wlan.getSite();
94 if (site != null && site.getDescription() != null && !site.getDescription().isBlank()) {
95 state = StringType.valueOf(site.getDescription());
97 state = UnDefType.UNDEF;
100 case CHANNEL_WIRELESS_CLIENTS:
101 state = countClients(wlan, c -> true);
103 case CHANNEL_GUEST_CLIENTS:
104 state = countClients(wlan, c -> c.isGuest());
106 case CHANNEL_SECURITY:
107 state = StringType.valueOf(wlan.getSecurity());
109 case CHANNEL_WLANBAND:
110 state = StringType.valueOf(wlan.getWlanBand());
113 state = StringType.valueOf(wlan.getWpaEnc());
115 case CHANNEL_WPAMODE:
116 state = StringType.valueOf(wlan.getWpaMode());
118 case CHANNEL_PASSPHRASE:
119 state = StringType.valueOf(wlan.getXPassphrase());
121 case CHANNEL_QRCODE_ENCODING:
122 state = qrcodeEncoding(wlan);
125 // Unsupported channel; nothing to update
126 state = UnDefType.NULL;
131 private static State countClients(final UniFiWlan wlan, final Predicate<UniFiClient> filter) {
132 final UniFiSite site = wlan.getSite();
135 return UnDefType.UNDEF;
137 return new DecimalType(site.getCache().countClients(site,
138 c -> c instanceof UniFiWirelessClient wirelessClient
139 && (wlan.getName() != null && wlan.getName().equals(wirelessClient.getEssid()))
145 * Returns a MERCARD like notation of the Wi-Fi access code. Format:
146 * <code>WIFI:S:<SSID>;T:WPA|blank;P:<password>;;</code>
148 * @param wlan wlan UniFi entity object containing the data
149 * @return MERCARD like Wi-Fi access format
150 * @see https://github.com/zxing/zxing/wiki/Barcode-Contents#wi-fi-network-config-android-ios-11
152 private static State qrcodeEncoding(final UniFiWlan wlan) {
153 final String name = encode(wlan.getName());
154 final String xPassphrase = wlan.getXPassphrase();
155 final boolean nopass = xPassphrase == null || xPassphrase.isBlank();
156 final String mode = nopass ? "nopass" : "WPA";
157 final String hidden = wlan.isHideSsid() ? "H:true" : "";
158 final String passcode = nopass ? "" : "P:" + encode(xPassphrase);
160 return StringType.valueOf(String.format("WIFI:S:%s;T:%s;%s;%s;", name, mode, passcode, hidden));
163 private static String encode(final @Nullable String value) {
164 return value == null ? "" : value.replaceAll("([\\;,\":])", "\\\\$1");
168 protected boolean handleCommand(final UniFiController controller, final UniFiWlan entity,
169 final ChannelUID channelUID, final Command command) throws UniFiException {
170 final String channelID = channelUID.getId();
172 if (CHANNEL_ENABLE.equals(channelID) && command instanceof OnOffType && entity.getSite() != null) {
173 controller.enableWifi(entity, OnOffType.ON == command);