org.openhab.binding.epsonprojector.internal.EpsonProjectorBinding.java Source code

Java tutorial

Introduction

Here is the source code for org.openhab.binding.epsonprojector.internal.EpsonProjectorBinding.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.epsonprojector.internal;

import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.openhab.binding.epsonprojector.EpsonProjectorBindingProvider;
import org.openhab.binding.epsonprojector.internal.EpsonProjectorDevice.AspectRatio;
import org.openhab.binding.epsonprojector.internal.EpsonProjectorDevice.Background;
import org.openhab.binding.epsonprojector.internal.EpsonProjectorDevice.Color;
import org.openhab.binding.epsonprojector.internal.EpsonProjectorDevice.ColorMode;
import org.openhab.binding.epsonprojector.internal.EpsonProjectorDevice.Gamma;
import org.openhab.binding.epsonprojector.internal.EpsonProjectorDevice.Luminance;
import org.openhab.binding.epsonprojector.internal.EpsonProjectorDevice.PowerStatus;
import org.openhab.binding.epsonprojector.internal.EpsonProjectorDevice.Source;
import org.openhab.binding.epsonprojector.internal.EpsonProjectorDevice.Switch;

import org.apache.commons.lang.StringUtils;
import org.openhab.core.binding.AbstractActiveBinding;
import org.openhab.core.items.Item;
import org.openhab.core.library.items.SwitchItem;
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;

/**
 * Binding which communicates with (one or many) Epson projectors.
 * 
 * 
 * @author Pauli Anttila
 * @since 1.3.0
 */
public class EpsonProjectorBinding extends AbstractActiveBinding<EpsonProjectorBindingProvider>
        implements ManagedService {

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

    private final static int DEFAULT_PORT = 60128;

    /**
     * the interval to find new refresh candidates (defaults to 1000
     * milliseconds)
     */
    private int granularity = 1000;

    private Map<String, Long> lastUpdateMap = new HashMap<String, Long>();

    protected Map<String, DeviceConfig> deviceConfigCache = null;

    /**
     * RegEx to validate a config
     * <code>'^(.*?)\\.(host|port|serialPort)$'</code>
     */
    private static final Pattern EXTRACT_CONFIG_PATTERN = Pattern.compile("^(.*?)\\.(host|port|serialPort)$");

    public void activate() {
        logger.debug("Activate");
    }

    public void deactivate() {
        logger.debug("Deactivate");
        closeConnection();
    }

    private void closeConnection() {
        if (deviceConfigCache != null) {
            // close all connections
            for (Entry<String, DeviceConfig> entry : deviceConfigCache.entrySet()) {
                DeviceConfig deviceCfg = entry.getValue();
                if (deviceCfg != null) {
                    EpsonProjectorDevice device = deviceCfg.getConnection();

                    if (device != null) {
                        try {
                            logger.debug("Closing connection to device '{}' ", deviceCfg.deviceId);
                            device.disconnect();
                        } catch (EpsonProjectorException e) {
                            logger.error("Error occured when closing connection to device '{}'",
                                    deviceCfg.deviceId);
                        }
                    }
                }
            }

            deviceConfigCache = null;
        }
    }

    /**
     * @{inheritDoc
     */
    @Override
    protected long getRefreshInterval() {
        return granularity;
    }

    /**
     * @{inheritDoc
     */
    @Override
    protected String getName() {
        return "Epson projector Refresh Service";
    }

    /**
     * @{inheritDoc
     */
    @Override
    public void execute() {
        for (EpsonProjectorBindingProvider provider : providers) {
            for (String itemName : provider.getInBindingItemNames()) {

                int refreshInterval = provider.getRefreshInterval(itemName);

                Long lastUpdateTimeStamp = lastUpdateMap.get(itemName);
                if (lastUpdateTimeStamp == null) {
                    lastUpdateTimeStamp = 0L;
                }

                long age = System.currentTimeMillis() - lastUpdateTimeStamp;
                boolean needsUpdate = age >= refreshInterval;

                if (needsUpdate) {
                    boolean refreshOnlyWhenPowerOn = provider.refreshOnlyWhenPowerOn(itemName);
                    String deviceId = provider.getDeviceId(itemName);

                    if (refreshOnlyWhenPowerOn) {
                        OnOffType state = (OnOffType) queryDataFromDevice(deviceId, EpsonProjectorCommandType.POWER,
                                SwitchItem.class);

                        if (state != OnOffType.ON) {
                            logger.debug("projector power is OFF, skip refresh for item '{}'", itemName);
                            lastUpdateMap.put(itemName, System.currentTimeMillis());
                            return;
                        }
                    }

                    logger.debug("item '{}' is about to be refreshed now", itemName);

                    EpsonProjectorCommandType commmandType = provider.getCommandType(itemName);
                    Class<? extends Item> itemType = provider.getItemType(itemName);

                    State state = queryDataFromDevice(deviceId, commmandType, itemType);

                    if (state != null) {
                        eventPublisher.postUpdate(itemName, state);
                    } else {
                        logger.error("No response received from command '{}'", commmandType);
                    }

                    lastUpdateMap.put(itemName, System.currentTimeMillis());
                }
            }
        }
    }

    private State queryDataFromDevice(String deviceId, EpsonProjectorCommandType commmandType,
            Class<? extends Item> itemType) {

        DeviceConfig device = deviceConfigCache.get(deviceId);

        if (device == null) {
            logger.error("Could not find device '{}'", deviceId);
            return null;
        }

        EpsonProjectorDevice remoteController = device.getConnection();

        if (remoteController == null) {
            logger.error("Could not find device '{}'", deviceId);
            return null;
        }

        try {
            if (remoteController.isConnected() == false)
                remoteController.connect();

            switch (commmandType) {
            case AKEYSTONE:
                int autoKeystone = remoteController.getAutoKeystone();
                return new DecimalType(autoKeystone);
            case ASPECT_RATIO:
                AspectRatio aspectRatio = remoteController.getAspectRatio();
                return new StringType(aspectRatio.toString());
            case BACKGROUND:
                Background background = remoteController.getBackground();
                return new StringType(background.toString());
            case BRIGHTNESS:
                int brightness = remoteController.getBrightness();
                return new DecimalType(brightness);
            case COLOR:
                Color color = remoteController.getColor();
                return new StringType(color.toString());
            case COLOR_MODE:
                ColorMode colorMode = remoteController.getColorMode();
                return new StringType(colorMode.toString());
            case COLOR_TEMP:
                int ctemp = remoteController.getColorTemperature();
                return new DecimalType(ctemp);
            case CONTRAST:
                int contrast = remoteController.getContrast();
                return new DecimalType(contrast);
            case DENSITY:
                int density = remoteController.getDensity();
                return new DecimalType(density);
            case DIRECT_SOURCE:
                int directSource = remoteController.getDirectSource();
                return new DecimalType(directSource);
            case ERR_CODE:
                int err = remoteController.getError();
                return new DecimalType(err);
            case ERR_MESSAGE:
                String errString = remoteController.getErrorString();
                return new StringType(errString);
            case FLESH_TEMP:
                int fleshColor = remoteController.getFleshColor();
                return new DecimalType(fleshColor);
            case GAIN_BLUE:
                int gainBlue = remoteController.getGainBlue();
                return new DecimalType(gainBlue);
            case GAIN_GREEN:
                int gainGreen = remoteController.getGainGreen();
                return new DecimalType(gainGreen);
            case GAIN_RED:
                int gainRed = remoteController.getGainRed();
                return new DecimalType(gainRed);
            case GAMMA:
                Gamma gamma = remoteController.getGamma();
                return new StringType(gamma.toString());
            case GAMMA_STEP:
                logger.warn("Get '{}' not implemented!", commmandType.toString());
                return null;
            case HKEYSTONE:
                int hKeystone = remoteController.getHorizontalKeystone();
                return new DecimalType(hKeystone);
            case HPOSITION:
                int hPosition = remoteController.getHorizontalPosition();
                return new DecimalType(hPosition);
            case HREVERSE:
                Switch hReverse = remoteController.getHorizontalReverse();
                return hReverse == Switch.ON ? OnOffType.ON : OnOffType.OFF;
            case KEY_CODE:
                break;
            case LAMP_TIME:
                int lampTime = remoteController.getLampTime();
                return new DecimalType(lampTime);
            case LUMINANCE:
                Luminance luminance = remoteController.getLuminance();
                return new StringType(luminance.toString());
            case MUTE:
                Switch mute = remoteController.getMute();
                return mute == Switch.ON ? OnOffType.ON : OnOffType.OFF;
            case OFFSET_BLUE:
                int offsetBlue = remoteController.getOffsetBlue();
                return new DecimalType(offsetBlue);
            case OFFSET_GREEN:
                int offsetGreen = remoteController.getOffsetGreen();
                return new DecimalType(offsetGreen);
            case OFFSET_RED:
                int offsetRed = remoteController.getOffsetRed();
                return new DecimalType(offsetRed);
            case POWER:
                PowerStatus powerStatus = remoteController.getPowerStatus();

                if (powerStatus == PowerStatus.ON)
                    return OnOffType.ON;
                else
                    return OnOffType.OFF;
            case POWER_STATE:
                PowerStatus powerStatus1 = remoteController.getPowerStatus();
                return new StringType(powerStatus1.toString());
            case SHARP:
                logger.warn("Get '{}' not implemented!", commmandType.toString());
                return null;
            case SOURCE:
                Source source = remoteController.getSource();
                return new StringType(source.toString());
            case SYNC:
                int sync = remoteController.getSync();
                return new DecimalType(sync);
            case TINT:
                int tint = remoteController.getTint();
                return new DecimalType(tint);
            case TRACKING:
                int tracking = remoteController.getTracking();
                return new DecimalType(tracking);
            case VKEYSTONE:
                int vKeystone = remoteController.getVerticalKeystone();
                return new DecimalType(vKeystone);
            case VPOSITION:
                int vPosition = remoteController.getVerticalPosition();
                return new DecimalType(vPosition);
            case VREVERSE:
                Switch vReverse = remoteController.getVerticalReverse();
                return vReverse == Switch.ON ? OnOffType.ON : OnOffType.OFF;
            default:
                logger.warn("Unknown '{}' command!", commmandType);
                return null;
            }

        } catch (EpsonProjectorException e) {
            logger.warn("Couldn't execute command '{}', {}", commmandType.toString(), e);

        } catch (Exception e) {
            logger.warn("Couldn't create state of type '{}'", itemType);
            return null;
        }

        return null;
    }

    /**
     * @{inheritDoc
     */
    @Override
    public void internalReceiveCommand(String itemName, Command command) {
        EpsonProjectorBindingProvider provider = findFirstMatchingBindingProvider(itemName, command);

        if (provider == null) {
            logger.warn("doesn't find matching binding provider [itemName={}, command={}]", itemName, command);
            return;
        }

        if (provider.isOutBinding(itemName)) {
            EpsonProjectorCommandType commmandType = provider.getCommandType(itemName);
            String deviceId = provider.getDeviceId(itemName);
            if (commmandType != null) {
                sendDataToDevice(deviceId, commmandType, command);
            }
        } else {
            logger.warn("itemName={} is not out binding", itemName);
        }
    }

    private void sendDataToDevice(String deviceId, EpsonProjectorCommandType commmandType, Command command) {
        DeviceConfig device = deviceConfigCache.get(deviceId);

        if (device == null) {
            logger.error("Could not find device '{}'", deviceId);
            return;
        }

        EpsonProjectorDevice remoteController = device.getConnection();

        if (remoteController == null) {
            logger.error("Could not find device '{}'", deviceId);
            return;
        }

        try {

            if (remoteController.isConnected() == false)
                remoteController.connect();

            switch (commmandType) {
            case AKEYSTONE:
                remoteController.setAutoKeystone(((DecimalType) command).intValue());
                break;
            case ASPECT_RATIO:
                remoteController.setAspectRatio(AspectRatio.valueOf(command.toString()));
                break;
            case BACKGROUND:
                remoteController.setBackground(Background.valueOf(command.toString()));
                break;
            case BRIGHTNESS:
                remoteController.setBrightness(((DecimalType) command).intValue());
                break;
            case COLOR:
                remoteController.setColor(Color.valueOf(command.toString()));
                break;
            case COLOR_MODE:
                remoteController.setColorMode(ColorMode.valueOf(command.toString()));
                break;
            case COLOR_TEMP:
                remoteController.setColorTemperature(((DecimalType) command).intValue());
                break;
            case CONTRAST:
                remoteController.setContrast(((DecimalType) command).intValue());
                break;
            case DENSITY:
                remoteController.setDensity(((DecimalType) command).intValue());
                break;
            case DIRECT_SOURCE:
                remoteController.setDirectSource(((DecimalType) command).intValue());
                break;
            case ERR_CODE:
                logger.error("'{}' is read only parameter", commmandType);
                break;
            case ERR_MESSAGE:
                logger.error("'{}' is read only parameter", commmandType);
                break;
            case FLESH_TEMP:
                remoteController.setFleshColor(((DecimalType) command).intValue());
                break;
            case GAIN_BLUE:
                remoteController.setGainBlue(((DecimalType) command).intValue());
                break;
            case GAIN_GREEN:
                remoteController.setGainGreen(((DecimalType) command).intValue());
                break;
            case GAIN_RED:
                remoteController.setGainRed(((DecimalType) command).intValue());
                break;
            case GAMMA:
                remoteController.setGamma(Gamma.valueOf(command.toString()));
                break;
            case GAMMA_STEP:
                logger.warn("Set '{}' not implemented!", commmandType.toString());
                break;
            case HKEYSTONE:
                remoteController.setHorizontalKeystone(((DecimalType) command).intValue());
                break;
            case HPOSITION:
                remoteController.setHorizontalPosition(((DecimalType) command).intValue());
                break;
            case HREVERSE:
                remoteController.setHorizontalReverse((command == OnOffType.ON ? Switch.ON : Switch.OFF));
                break;
            case KEY_CODE:
                remoteController.sendKeyCode(((DecimalType) command).intValue());
                break;
            case LAMP_TIME:
                logger.error("'{}' is read only parameter", commmandType);
                break;
            case LUMINANCE:
                remoteController.setLuminance(Luminance.valueOf(command.toString()));
                break;
            case MUTE:
                remoteController.setMute((command == OnOffType.ON ? Switch.ON : Switch.OFF));
                break;
            case OFFSET_BLUE:
                remoteController.setOffsetBlue(((DecimalType) command).intValue());
                break;
            case OFFSET_GREEN:
                remoteController.setOffsetGreen(((DecimalType) command).intValue());
                break;
            case OFFSET_RED:
                remoteController.setOffsetRed(((DecimalType) command).intValue());
                break;
            case POWER:
                remoteController.setPower((command == OnOffType.ON ? Switch.ON : Switch.OFF));
                break;
            case POWER_STATE:
                logger.error("'{}' is read only parameter", commmandType);
                break;
            case SHARP:
                logger.warn("Set '{}' not implemented!", commmandType.toString());
                break;
            case SOURCE:
                remoteController.setSource(Source.valueOf(command.toString()));
                break;
            case SYNC:
                remoteController.setSync(((DecimalType) command).intValue());
                break;
            case TINT:
                remoteController.setTint(((DecimalType) command).intValue());
                break;
            case TRACKING:
                remoteController.setTracking(((DecimalType) command).intValue());
                break;
            case VKEYSTONE:
                remoteController.setVerticalKeystone(((DecimalType) command).intValue());
                break;
            case VPOSITION:
                remoteController.setVerticalPosition(((DecimalType) command).intValue());
                break;
            case VREVERSE:
                remoteController.setVerticalReverse((command == OnOffType.ON ? Switch.ON : Switch.OFF));
                break;
            default:
                logger.warn("Unknown '{}' command!", commmandType);
                break;
            }

        } catch (EpsonProjectorException e) {
            logger.error("Couldn't execute command '{}', {}", commmandType, e);

        }
    }

    /**
     * Find the first matching {@link ExecBindingProvider} according to
     * <code>itemName</code> and <code>command</code>. If no direct match is
     * found, a second match is issued with wilcard-command '*'.
     * 
     * @param itemName
     * @param command
     * 
     * @return the matching binding provider or <code>null</code> if no binding
     *         provider could be found
     */
    private EpsonProjectorBindingProvider findFirstMatchingBindingProvider(String itemName, Command command) {

        EpsonProjectorBindingProvider firstMatchingProvider = null;

        for (EpsonProjectorBindingProvider provider : this.providers) {
            EpsonProjectorCommandType commmandType = provider.getCommandType(itemName);

            if (commmandType != null) {
                firstMatchingProvider = provider;
                break;
            }
        }

        return firstMatchingProvider;
    }

    /**
     * @{inheritDoc
     */
    @Override
    public void updated(Dictionary<String, ?> config) throws ConfigurationException {

        logger.debug("Configuration updated, config {}", config != null ? true : false);

        if (config != null) {
            if (deviceConfigCache == null) {
                deviceConfigCache = new HashMap<String, DeviceConfig>();
            }

            String granularityString = (String) config.get("granularity");
            if (StringUtils.isNotBlank(granularityString)) {
                granularity = Integer.parseInt(granularityString);
            }

            Enumeration<String> keys = config.keys();
            while (keys.hasMoreElements()) {
                String key = (String) keys.nextElement();

                // the config-key enumeration contains additional keys that we
                // don't want to process here ...
                if ("service.pid".equals(key)) {
                    continue;
                }

                Matcher matcher = EXTRACT_CONFIG_PATTERN.matcher(key);

                if (!matcher.matches()) {
                    logger.warn("given config key '" + key
                            + "' does not follow the expected pattern '<id>.<host|port>'");
                    continue;
                }

                matcher.reset();
                matcher.find();

                String deviceId = matcher.group(1);

                DeviceConfig deviceConfig = deviceConfigCache.get(deviceId);

                if (deviceConfig == null) {
                    logger.debug("Added new device {}", deviceId);
                    deviceConfig = new DeviceConfig(deviceId);
                    deviceConfigCache.put(deviceId, deviceConfig);
                }

                String configKey = matcher.group(2);
                String value = (String) config.get(key);

                if ("serialPort".equals(configKey)) {
                    deviceConfig.serialPort = value;
                } else if ("host".equals(configKey)) {
                    deviceConfig.host = value;
                } else if ("port".equals(configKey)) {
                    deviceConfig.port = Integer.valueOf(value);
                } else {
                    throw new ConfigurationException(configKey,
                            "the given configKey '" + configKey + "' is unknown");
                }
            }

            setProperlyConfigured(true);
        }
    }

    /**
     * Internal data structure which carries the connection details of one
     * device (there could be several)
     */
    static class DeviceConfig {

        String deviceId;
        String serialPort = null;
        String host = null;
        int port = DEFAULT_PORT;

        EpsonProjectorDevice device = null;

        public DeviceConfig(String deviceId) {
            this.deviceId = deviceId;
        }

        @Override
        public String toString() {
            return "Device [id=" + deviceId + ", host=" + host + ", port=" + port + "]";
        }

        EpsonProjectorDevice getConnection() {
            if (device == null) {
                if (serialPort != null) {
                    device = new EpsonProjectorDevice(serialPort);
                } else if (host != null) {
                    device = new EpsonProjectorDevice(host, port);
                }
            }
            return device;
        }

    }

}