org.openhab.binding.yamahareceiver.internal.YamahaReceiverBinding.java Source code

Java tutorial

Introduction

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

import java.io.IOException;
import java.util.Collection;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.openhab.binding.yamahareceiver.YamahaReceiverBindingProvider;
import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConfig.BindingType;
import org.openhab.binding.yamahareceiver.internal.YamahaReceiverBindingConfig.Zone;
import org.openhab.binding.yamahareceiver.internal.hardware.YamahaReceiverProxy;
import org.openhab.binding.yamahareceiver.internal.hardware.YamahaReceiverState;
import org.openhab.core.binding.AbstractActiveBinding;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.IncreaseDecreaseType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.types.UpDownType;
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;

/**
 * Yamaha Reciever binding. Handles all commands and polls configured devices to
 * process updates.
 * 
 * @author Eric Thill
 * @author Ben Jones
 * @since 1.6.0
 */
public class YamahaReceiverBinding extends AbstractActiveBinding<YamahaReceiverBindingProvider>
        implements ManagedService {

    public static final String CONFIG_KEY_HOST = "host";
    public static final long DEFAULT_REFRESH_INTERVAL = 60000;
    public static final String DEFAULT_DEVICE_UID = "default";

    private static final float VOLUME_DB_MIN = -80f;
    private static final float VOLUME_DB_MAX = 16f;
    private static final String BINDING_NAME = "YamahaReceiverBinding";

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

    private long refreshInterval = DEFAULT_REFRESH_INTERVAL;

    // Map of proxies. key=deviceUid, value=proxy
    // Used to keep track of proxies
    private final Map<String, YamahaReceiverProxy> proxies = new HashMap<String, YamahaReceiverProxy>();

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

    @Override
    protected String getName() {
        return "YamahaReceiver Refresh Service";
    }

    @Override
    protected void execute() {
        try {
            // Iterate through all proxies
            for (Map.Entry<String, YamahaReceiverProxy> entry : proxies.entrySet()) {
                String deviceUid = entry.getKey();
                YamahaReceiverProxy receiverProxy = entry.getValue();
                sendUpdates(receiverProxy, deviceUid);
            }
        } catch (Throwable t) {
            logger.error("Error polling devices for " + getName(), t);
        }
    }

    private void sendUpdates(YamahaReceiverProxy receiverProxy, String deviceUid) {
        // Get all item configurations belonging to this proxy
        Collection<YamahaReceiverBindingConfig> configs = getDeviceConfigs(deviceUid);
        try {
            for (Zone zone : Zone.values()) {
                // Poll the state from the device
                YamahaReceiverState state = receiverProxy.getState(zone);

                // Create state updates
                State powerUpdate = state.isPower() ? OnOffType.ON : OnOffType.OFF;
                State muteUpdate = state.isMute() ? OnOffType.ON : OnOffType.OFF;
                State inputUpdate = new StringType(state.getInput());
                State surroundUpdate = new StringType(state.getSurroundProgram());
                State updateVolumeDb = new DecimalType(state.getVolume());
                State updateVolumePercent = new PercentType((int) dbToPercent(state.getVolume()));

                // Send updates
                sendUpdate(configs, zone, BindingType.power, powerUpdate);
                sendUpdate(configs, zone, BindingType.mute, muteUpdate);
                sendUpdate(configs, zone, BindingType.input, inputUpdate);
                sendUpdate(configs, zone, BindingType.surroundProgram, surroundUpdate);
                sendUpdate(configs, zone, BindingType.volumePercent, updateVolumePercent);
                sendUpdate(configs, zone, BindingType.volumeDb, updateVolumeDb);
            }
        } catch (IOException e) {
            logger.warn("Cannot communicate with " + receiverProxy.getHost());
        }
    }

    private Collection<YamahaReceiverBindingConfig> getDeviceConfigs(String deviceUid) {
        Map<String, YamahaReceiverBindingConfig> items = new HashMap<String, YamahaReceiverBindingConfig>();
        for (YamahaReceiverBindingProvider provider : this.providers) {
            provider.getDeviceConfigs(deviceUid, items);
        }
        return items.values();
    }

    private void sendUpdate(Collection<YamahaReceiverBindingConfig> configs, Zone zone, BindingType type,
            State state) {
        for (YamahaReceiverBindingConfig config : configs) {
            if (config.getZone() == zone && config.getBindingType() == type) {
                eventPublisher.postUpdate(config.getItemName(), state);
            }
        }
    }

    private static final int percentToDB(byte percentByte) {
        float percent = percentByte * .01f;
        float range = VOLUME_DB_MAX - VOLUME_DB_MIN;
        return (int) ((percent * range) + VOLUME_DB_MIN);
    }

    private static final float dbToPercent(float db) {
        float range = VOLUME_DB_MAX - VOLUME_DB_MIN;
        return 100 * (((db - VOLUME_DB_MIN) / range));
    }

    @Override
    protected void internalReceiveCommand(String itemName, Command command) {
        YamahaReceiverBindingConfig config = getConfigForItemName(itemName);
        if (config == null) {
            logger.error("Received command for unknown item '" + itemName + "'");
            return;
        }
        YamahaReceiverProxy proxy = proxies.get(config.getDeviceUid());
        if (proxy == null) {
            logger.error("Received command for unknown device uid '" + config.getDeviceUid() + "'");
            return;
        }

        if (logger.isDebugEnabled()) {
            logger.debug(BINDING_NAME + " processing command '" + command + "' of type '"
                    + command.getClass().getSimpleName() + "' for item '" + itemName + "'");
        }

        try {
            Zone zone = config.getZone();
            BindingType type = config.getBindingType();
            if (type == BindingType.power) {
                if (command instanceof OnOffType) {
                    proxy.setPower(zone, command == OnOffType.ON);
                }
            } else if (type == BindingType.volumePercent || type == BindingType.volumeDb) {
                if (command instanceof IncreaseDecreaseType || command instanceof UpDownType) {
                    // increase/decrease dB by .5 dB
                    float db = proxy.getState(zone).getVolume();
                    float adjAmt;
                    if (command == IncreaseDecreaseType.INCREASE || command == UpDownType.UP) {
                        adjAmt = .5f;
                    } else {
                        adjAmt = -.5f;
                    }
                    float newDb = db + adjAmt;
                    proxy.setVolume(zone, newDb);
                    // send new value as update
                    State newState = new DecimalType(newDb);
                    eventPublisher.postUpdate(itemName, newState);
                } else if (command instanceof PercentType) {
                    // set dB from percent
                    byte percent = ((PercentType) command).byteValue();
                    int db = percentToDB(percent);
                    proxy.setVolume(zone, db);
                } else {
                    // set dB from value
                    float db = Float.parseFloat(command.toString());
                    proxy.setVolume(zone, db);
                }
                // Volume updates multiple values => send update now
                sendUpdates(proxy, config.getDeviceUid());
            } else if (type == BindingType.mute) {
                if (command instanceof OnOffType) {
                    proxy.setMute(zone, command == OnOffType.ON);
                }
            } else if (type == BindingType.input) {
                proxy.setInput(zone, parseString(command.toString()));
            } else if (type == BindingType.surroundProgram) {
                proxy.setSurroundProgram(zone, parseString(command.toString()));
            } else if (type == BindingType.netRadio) {
                if (command instanceof DecimalType) {
                    proxy.setNetRadio(((DecimalType) command).intValue());
                }
            }
        } catch (IOException e) {
            logger.warn("Cannot communicate with " + proxy.getHost() + " (uid: " + config.getDeviceUid() + ")");
        } catch (Throwable t) {
            logger.error("Error processing command '" + command + "' for item '" + itemName + "'", t);
        }
    }

    private String parseString(String str) {
        // This is annoying when we're dealing with spaces
        str = str.trim();
        if (str.startsWith("\"") && str.endsWith("\"")) {
            str = str.substring(1, str.length() - 1);
        }
        return str;
    }

    private YamahaReceiverBindingConfig getConfigForItemName(String itemName) {
        for (YamahaReceiverBindingProvider provider : this.providers) {
            if (provider.getItemConfig(itemName) != null) {
                return provider.getItemConfig(itemName);
            }
        }
        return null;
    }

    @Override
    protected void internalReceiveUpdate(String itemName, State newState) {
        // ignore
    }

    @Override
    public void updated(Dictionary<String, ?> config) throws ConfigurationException {
        logger.debug(BINDING_NAME + " updated");
        try {
            // Process device configuration
            if (config != null) {
                String refreshIntervalString = (String) config.get("refresh");
                if (StringUtils.isNotBlank(refreshIntervalString)) {
                    refreshInterval = Long.parseLong(refreshIntervalString);
                }
                // parse all configured receivers
                // ( yamahareceiver:<uid>.host=10.0.0.2 )
                Enumeration<String> keys = config.keys();
                while (keys.hasMoreElements()) {
                    String key = keys.nextElement();
                    if (key.endsWith(CONFIG_KEY_HOST)) {
                        // parse host
                        String host = (String) config.get(key);
                        int separatorIdx = key.indexOf('.');
                        // no uid => one device => use default UID
                        String uid = separatorIdx == -1 ? DEFAULT_DEVICE_UID : key.substring(0, separatorIdx);
                        // proxy is stateless. keep them in a map in the
                        // binding.
                        proxies.put(uid, new YamahaReceiverProxy(host));
                    }
                }
                setProperlyConfigured(true);
            }
        } catch (Throwable t) {
            logger.error("Error configuring " + getName(), t);
        }
    }

    @Override
    public void activate() {
        logger.debug(BINDING_NAME + " activated");
    }

    @Override
    public void deactivate() {
        logger.debug(BINDING_NAME + " deactivated");
    }

}