org.openhab.binding.unifi.internal.handler.UniFiClientThingHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.openhab.binding.unifi.internal.handler.UniFiClientThingHandler.java

Source

/**
 * Copyright (c) 2010-2019 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.eclipse.smarthome.core.thing.ThingStatus.*;
import static org.eclipse.smarthome.core.thing.ThingStatusDetail.CONFIGURATION_ERROR;
import static org.openhab.binding.unifi.internal.UniFiBindingConstants.*;

import java.util.Calendar;

import org.apache.commons.lang.StringUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.core.library.types.DateTimeType;
import org.eclipse.smarthome.core.library.types.DecimalType;
import org.eclipse.smarthome.core.library.types.OnOffType;
import org.eclipse.smarthome.core.library.types.StringType;
import org.eclipse.smarthome.core.thing.ChannelUID;
import org.eclipse.smarthome.core.thing.Thing;
import org.eclipse.smarthome.core.thing.ThingTypeUID;
import org.eclipse.smarthome.core.types.Command;
import org.eclipse.smarthome.core.types.State;
import org.eclipse.smarthome.core.types.UnDefType;
import org.openhab.binding.unifi.internal.UniFiBindingConstants;
import org.openhab.binding.unifi.internal.UniFiClientThingConfig;
import org.openhab.binding.unifi.internal.api.UniFiException;
import org.openhab.binding.unifi.internal.api.model.UniFiClient;
import org.openhab.binding.unifi.internal.api.model.UniFiController;
import org.openhab.binding.unifi.internal.api.model.UniFiDevice;
import org.openhab.binding.unifi.internal.api.model.UniFiSite;
import org.openhab.binding.unifi.internal.api.model.UniFiWiredClient;
import org.openhab.binding.unifi.internal.api.model.UniFiWirelessClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The {@link UniFiClientThingHandler} is responsible for handling commands and status
 * updates for UniFi Wireless Devices.
 *
 * @author Matthew Bowman - Initial contribution
 * @author Patrik Wimnell - Blocking / Unblocking client support
 */
@NonNullByDefault
public class UniFiClientThingHandler extends UniFiBaseThingHandler<UniFiClient, UniFiClientThingConfig> {

    public static boolean supportsThingType(ThingTypeUID thingTypeUID) {
        return UniFiBindingConstants.THING_TYPE_WIRELESS_CLIENT.equals(thingTypeUID);
    }

    private final Logger logger = LoggerFactory.getLogger(UniFiClientThingHandler.class);

    private UniFiClientThingConfig config = new UniFiClientThingConfig();

    public UniFiClientThingHandler(Thing thing) {
        super(thing);
    }

    @Override
    protected synchronized void initialize(UniFiClientThingConfig config) {
        // mgb: called when the config changes
        if (thing.getStatus() == INITIALIZING) {
            logger.debug("Initializing the UniFi Client Handler with config = {}", config);
            if (!config.isValid()) {
                updateStatus(OFFLINE, CONFIGURATION_ERROR,
                        "You must define a MAC address, IP address, hostname or alias for this thing.");
                return;
            }
            this.config = config;
            updateStatus(ONLINE);
        }
    }

    private static boolean belongsToSite(UniFiClient client, String siteName) {
        boolean result = true; // mgb: assume true = proof by contradiction
        if (StringUtils.isNotEmpty(siteName)) {
            UniFiSite site = client.getSite();
            // mgb: if the 'site' can't be found or the name doesn't match...
            if (site == null || !site.matchesName(siteName)) {
                // mgb: ... then the client doesn't belong to this thing's configured 'site' and we 'filter' it
                result = false;
            }
        }
        return result;
    }

    @Override
    protected synchronized @Nullable UniFiClient getEntity(UniFiController controller) {
        UniFiClient client = controller.getClient(config.getClientID());
        // mgb: short circuit
        if (client == null || !belongsToSite(client, config.getSite())) {
            return null;
        }
        return client;
    }

    private State getDefaultState(String channelID, boolean clientHome) {
        State state = UnDefType.NULL;
        switch (channelID) {
        case CHANNEL_ONLINE:
        case CHANNEL_SITE:
        case CHANNEL_AP:
        case CHANNEL_ESSID:
        case CHANNEL_RSSI:
        case CHANNEL_MAC_ADDRESS:
        case CHANNEL_IP_ADDRESS:
        case CHANNEL_BLOCKED:
            state = (clientHome ? UnDefType.NULL : UnDefType.UNDEF); // skip the update if the client is home
            break;
        case CHANNEL_UPTIME:
            // mgb: uptime should default to 0 seconds
            state = (clientHome ? UnDefType.NULL : new DecimalType(0)); // skip the update if the client is home
            break;
        case CHANNEL_LAST_SEEN:
            // mgb: lastSeen should keep the last state no matter what
            state = UnDefType.NULL;
            break;
        case CHANNEL_RECONNECT:
            state = OnOffType.OFF;
            break;
        }
        return state;
    }

    private synchronized boolean isClientHome(UniFiClient client) {
        boolean online = false;
        if (client != null) {
            Calendar lastSeen = client.getLastSeen();
            if (lastSeen == null) {
                logger.warn("Could not determine if client is online: cid = {}, lastSeen = null",
                        config.getClientID());
            } else {
                Calendar considerHome = (Calendar) lastSeen.clone();
                considerHome.add(Calendar.SECOND, config.getConsiderHome());
                Calendar now = Calendar.getInstance();
                online = (now.compareTo(considerHome) < 0);
            }
        }
        return online;
    }

    @Override
    protected void refreshChannel(UniFiClient client, ChannelUID channelUID) {
        boolean clientHome = isClientHome(client);
        UniFiDevice device = client.getDevice();
        UniFiSite site = (device == null ? null : device.getSite());
        String channelID = channelUID.getIdWithoutGroup();
        State state = getDefaultState(channelID, clientHome);
        switch (channelID) {
        // mgb: common wired + wireless client channels

        // :online
        case CHANNEL_ONLINE:
            state = OnOffType.from(clientHome);
            break;

        // :site
        case CHANNEL_SITE:
            if (clientHome && site != null && StringUtils.isNotBlank(site.getDescription())) {
                state = StringType.valueOf(site.getDescription());
            }
            break;

        // :macAddress
        case CHANNEL_MAC_ADDRESS:
            if (clientHome && StringUtils.isNotBlank(client.getMac())) {
                state = StringType.valueOf(client.getMac());
            }
            break;

        // :ipAddress
        case CHANNEL_IP_ADDRESS:
            if (clientHome && StringUtils.isNotBlank(client.getIp())) {
                state = StringType.valueOf(client.getIp());
            }
            break;

        // :uptime
        case CHANNEL_UPTIME:
            if (clientHome && client.getUptime() != null) {
                state = new DecimalType(client.getUptime());
            }
            break;

        // :lastSeen
        case CHANNEL_LAST_SEEN:
            // mgb: we don't check clientOnline as lastSeen is also included in the Insights data
            if (client.getLastSeen() != null) {
                state = new DateTimeType(client.getLastSeen());
            }
            break;

        // :blocked
        case CHANNEL_BLOCKED:
            state = OnOffType.from(client.isBlocked());
            break;

        default:
            // mgb: additional wired client channels
            if (client.isWired() && (client instanceof UniFiWiredClient)) {
                state = getWiredChannelState((UniFiWiredClient) client, clientHome, channelID);
            }

            // mgb: additional wireless client channels
            else if (client.isWireless() && (client instanceof UniFiWirelessClient)) {
                state = getWirelessChannelState((UniFiWirelessClient) client, clientHome, channelID);
            }
            break;
        }
        // mgb: only non null states get updates
        if (state != UnDefType.NULL) {
            updateState(channelID, state);
        }
    }

    private State getWiredChannelState(UniFiWiredClient client, boolean clientHome, String channelID) {
        State state = UnDefType.NULL;
        return state;
    }

    private State getWirelessChannelState(UniFiWirelessClient client, boolean clientHome, String channelID) {
        State state = UnDefType.NULL;
        switch (channelID) {
        // :ap
        case CHANNEL_AP:
            UniFiDevice device = client.getDevice();
            if (clientHome && device != null && StringUtils.isNotBlank(device.getName())) {
                state = StringType.valueOf(device.getName());
            }
            break;

        // :essid
        case CHANNEL_ESSID:
            if (clientHome && StringUtils.isNotBlank(client.getEssid())) {
                state = StringType.valueOf(client.getEssid());
            }
            break;

        // :rssi
        case CHANNEL_RSSI:
            if (clientHome && client.getRssi() != null) {
                state = new DecimalType(client.getRssi());
            }
            break;

        // :reconnect
        case CHANNEL_RECONNECT:
            // nop - read-only channel
            break;
        }
        return state;
    }

    @Override
    protected void handleCommand(UniFiClient client, ChannelUID channelUID, Command command) throws UniFiException {
        String channelID = channelUID.getIdWithoutGroup();
        switch (channelID) {
        case CHANNEL_BLOCKED:
            handleBlockedCommand(client, channelUID, command);
            break;
        case CHANNEL_RECONNECT:
            handleReconnectCommand(client, channelUID, command);
            break;
        default:
            logger.warn("Ignoring unsupported command = {} for channel = {}", command, channelUID);
        }
    }

    private void handleBlockedCommand(UniFiClient client, ChannelUID channelUID, Command command)
            throws UniFiException {
        if (command instanceof OnOffType) {
            client.block(command == OnOffType.ON);
        } else {
            logger.warn("Ignoring unsupported command = {} for channel = {} - valid commands types are: OnOffType",
                    command, channelUID);
        }
    }

    private void handleReconnectCommand(UniFiClient client, ChannelUID channelUID, Command command)
            throws UniFiException {
        if (command instanceof OnOffType) {
            if (command == OnOffType.ON) {
                client.reconnect();
                updateState(channelUID, OnOffType.OFF);
            }
        } else {
            logger.warn("Ignoring unsupported command = {} for channel = {} - valid commands types are: OnOffType",
                    command, channelUID);
        }
    }

}