org.openhab.binding.samsungac.internal.SamsungAcBinding.java Source code

Java tutorial

Introduction

Here is the source code for org.openhab.binding.samsungac.internal.SamsungAcBinding.java

Source

/**
 * Copyright (c) 2010-2015, openHAB.org and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package org.openhab.binding.samsungac.internal;

import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.binding.openhab.samsungac.communicator.AirConditioner;
import org.binding.openhab.samsungac.communicator.SsdpDiscovery;
import org.openhab.binding.samsungac.SamsungAcBindingProvider;
import org.openhab.core.binding.AbstractActiveBinding;
import org.openhab.core.binding.BindingProvider;
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.types.Command;
import org.openhab.core.types.State;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * Binding listening OpenHAB bus and send commands to Samsung Air Conditioner
 * devices when command is received.
 * 
 * @author Stein Tore Tsse
 * @since 1.6.0
 */
public class SamsungAcBinding extends AbstractActiveBinding<SamsungAcBindingProvider> implements ManagedService {

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

    /**
     * the refresh interval which is used to check for lost connections
     * (optional, defaults to 60000ms)
     */
    private long refreshInterval = 60000;

    private Map<String, AirConditioner> nameHostMapper = null;

    protected Map<String, Map<String, String>> deviceConfigCache = new HashMap<String, Map<String, String>>();

    public SamsungAcBinding() {
    }

    public void activate() {
        logger.info("active");
    }

    public void deactivate() {
        logger.info("deactive");
        // close any open connections
        for (AirConditioner connector : nameHostMapper.values()) {
            connector.disconnect();
        }
    }

    /**
     * @{inheritDoc
     */
    @Override
    protected void internalReceiveCommand(String itemName, Command command) {

        if (itemName != null && command != null) {
            logger.debug("InternalReceiveCommand [" + itemName + ":" + command + "]");
            String hostName = getAirConditionerInstance(itemName);
            AirConditioner host = nameHostMapper.get(hostName);
            if (host == null) {
                logger.debug("Host with hostname:" + hostName + " not found...");
                return;
            }
            CommandEnum property = getProperty(itemName);

            String cmd = getCmdStringFromEnumValue(command, property);

            if (cmd != null) {
                sendCommand(host, property, cmd);
            } else
                logger.warn("Not sending for itemName: '" + itemName + "' because property not implemented: '"
                        + property + "'");
        }
    }

    private String getCmdStringFromEnumValue(Command command, CommandEnum property) {
        String cmd = null;
        switch (property) {
        case AC_FUN_POWER:
        case AC_ADD_SPI:
        case AC_ADD_AUTOCLEAN:
            cmd = "ON".equals(command.toString()) ? "On" : "Off";
            break;
        case AC_FUN_WINDLEVEL:
            cmd = WindLevelEnum.getFromValue(command).toString();
            break;
        case AC_FUN_OPMODE:
            cmd = OperationModeEnum.getFromValue(command).toString();
            break;
        case AC_FUN_COMODE:
            cmd = ConvenientModeEnum.getFromValue(command).toString();
            break;
        case AC_FUN_DIRECTION:
            cmd = DirectionEnum.getFromValue(command).toString();
            break;
        case AC_FUN_TEMPSET:
        case AC_FUN_ERROR:
        default:
            cmd = command.toString();
            break;
        }
        return cmd;
    }

    private void sendCommand(AirConditioner aircon, CommandEnum property, String value) {
        int i = 1;
        boolean commandSent = false;
        while (i < 5 && !commandSent) {
            try {
                logger.debug("[" + i + "/5] Sending command: " + value + " to property:" + property + " with ip:"
                        + aircon.getIpAddress());
                if (aircon.sendCommand(property, value) != null) {
                    commandSent = true;
                    logger.debug("Command[" + value + "] sent on try number " + i);
                }
            } catch (Exception e) {
                logger.warn(
                        "Could not send value: '" + value + "' to property:'" + property + "', try " + i + "/5");
                e.printStackTrace();
            } finally {
                i++;
            }
        }
    }

    private String getAirConditionerInstance(String itemName) {
        for (BindingProvider provider : providers) {
            if (provider instanceof SamsungAcBindingProvider) {
                SamsungAcBindingProvider acProvider = (SamsungAcBindingProvider) provider;
                if (acProvider.getItemNames().contains(itemName)) {
                    return acProvider.getAirConditionerInstance(itemName);
                }
            }
        }
        return null;
    }

    private CommandEnum getProperty(String itemName) {
        for (BindingProvider provider : providers) {
            if (provider instanceof SamsungAcBindingProvider) {
                SamsungAcBindingProvider acProvider = (SamsungAcBindingProvider) provider;
                if (acProvider.getItemNames().contains(itemName)) {
                    return acProvider.getProperty(itemName);
                }
            }
        }
        return null;
    }

    private String getItemName(String acName, CommandEnum property) {
        for (BindingProvider provider : providers) {
            if (provider instanceof SamsungAcBindingProvider) {
                SamsungAcBindingProvider acProvider = (SamsungAcBindingProvider) provider;
                return acProvider.getItemName(acName, property);
            }
        }
        return null;
    }

    /**
     * @{inheritDoc
     */
    public void updated(Dictionary<String, ?> config) throws ConfigurationException {
        Enumeration<String> keys = config.keys();

        String refreshIntervalString = (String) config.get("refresh");
        if (StringUtils.isNotBlank(refreshIntervalString)) {
            refreshInterval = Long.parseLong(refreshIntervalString);
            logger.info("Refresh interval set to " + refreshIntervalString + " ms");
        } else {
            logger.info("No refresh interval configured, using default: " + refreshInterval + " ms");
        }

        Map<String, AirConditioner> hosts = new HashMap<String, AirConditioner>();
        while (keys.hasMoreElements()) {
            String key = keys.nextElement();
            logger.debug("Configuration key is: " + key);
            if ("service.pid".equals(key)) {
                continue;
            }

            String[] parts = key.split("\\.");
            String hostname = parts[0];

            AirConditioner host = hosts.get(hostname);
            if (host == null) {
                host = new AirConditioner();
            }

            String value = ((String) config.get(key)).trim();

            if ("host".equals(parts[1])) {
                host.setIpAddress(value);
            }
            if ("mac".equals(parts[1])) {
                host.setMacAddress(value);
            }
            if ("token".equals(parts[1])) {
                host.setToken(value);
            }
            hosts.put(hostname, host);
        }
        nameHostMapper = hosts;

        if (nameHostMapper == null || nameHostMapper.size() == 0) {
            setProperlyConfigured(false);
            Map<String, Map<String, String>> discovered = SsdpDiscovery.discover();
            if (discovered != null && discovered.size() > 0) {
                for (Map<String, String> ac : discovered.values()) {
                    if (ac.get("IP") != null && ac.get("MAC_ADDR") != null)
                        logger.warn(
                                "We found air conditioner. Please put the following in your configuration file: "
                                        + "\r\n samsungac:<ACNAME>.host=" + ac.get("IP")
                                        + "\r\n samsungac:<ACNAME>.mac=" + ac.get("MAC_ADDR"));
                }
            } else {
                logger.warn("No Samsung Air Conditioner has been configured, and we could not find one either");
            }
        } else {
            setProperlyConfigured(true);
        }
    }

    @Override
    protected void execute() {
        if (!bindingsExist()) {
            logger.debug("There is no existing Samsung AC binding configuration => refresh cycle aborted!");
            return;
        }

        if (nameHostMapper == null) {
            logger.debug("Name host mapper not yet set. Aborted refresh");
            return;
        }

        for (Map.Entry<String, AirConditioner> entry : nameHostMapper.entrySet()) {
            AirConditioner host = entry.getValue();
            String acName = entry.getKey();
            if (host.isConnected()) {
                getAndUpdateStatusForAirConditioner(acName, host);
            } else {
                reconnectToAirConditioner(entry.getKey(), host);
            }
        }
    }

    private void reconnectToAirConditioner(String key, AirConditioner host) {
        logger.info("Broken connection found for '{}', attempting to reconnect...", key);
        try {
            host.login();
            logger.info("Connection to {} has succeeded", host.toString());
        } catch (Exception e) {
            if (e == null || e.toString() == null || e.getCause() == null) {
                logger.info("Returned null-exception...");
            } else
                logger.debug(e.toString() + " : " + e.getCause().toString());
            logger.info("Reconnect failed for '{}', will retry in {}s", key, refreshInterval / 1000);
        }
    }

    private void getAndUpdateStatusForAirConditioner(String acName, AirConditioner host) {
        Map<CommandEnum, String> status = new HashMap<CommandEnum, String>();
        try {
            logger.info("Getting status for ac: '" + acName + "'");
            status = host.getStatus();
        } catch (Exception e) {
            logger.info("Could not get status.. returning.., got exception: " + e.toString());
            return;
        }

        for (CommandEnum cmd : status.keySet()) {
            logger.debug("Trying to find item for: " + acName + " and cmd: " + cmd.toString());
            String item = getItemName(acName, cmd);
            String value = status.get(cmd);
            if (item != null && value != null) {
                updateItemWithValue(cmd, item, value);
            }
        }
    }

    private void updateItemWithValue(CommandEnum cmd, String item, String value) {
        switch (cmd) {
        case AC_FUN_TEMPNOW:
        case AC_FUN_TEMPSET:
            postUpdate(item, DecimalType.valueOf(value));
            break;
        case AC_FUN_POWER:
        case AC_ADD_SPI:
        case AC_ADD_AUTOCLEAN:
            postUpdate(item, value.toUpperCase().equals("ON") ? OnOffType.ON : OnOffType.OFF);
            break;
        case AC_FUN_COMODE:
            postUpdate(item, DecimalType.valueOf(Integer.toString(ConvenientModeEnum.valueOf(value).value)));
            break;
        case AC_FUN_OPMODE:
            postUpdate(item, DecimalType.valueOf(Integer.toString(OperationModeEnum.valueOf(value).value)));
            break;
        case AC_FUN_WINDLEVEL:
            postUpdate(item, DecimalType.valueOf(Integer.toString(WindLevelEnum.valueOf(value).value)));
            break;
        case AC_FUN_DIRECTION:
            postUpdate(item, DecimalType.valueOf(Integer.toString(DirectionEnum.valueOf(value).value)));
            break;
        case AC_FUN_ERROR:
        default:
            postUpdate(item, StringType.valueOf(value));
            break;
        }
    }

    private void postUpdate(String item, State state) {
        if (item != null && state != null) {
            logger.debug(item + " gets updated to: " + state);
            eventPublisher.postUpdate(item, state);
        } else {
            logger.debug("Could not update item: '" + item + "' with state: '" + state.toString() + "'");
        }
    }

    @Override
    protected long getRefreshInterval() {
        return refreshInterval;
    }

    @Override
    protected String getName() {
        return "Samsung Air Conditioner service";
    }
}