org.eclipse.smarthome.binding.fsinternetradio.handler.FSInternetRadioHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.smarthome.binding.fsinternetradio.handler.FSInternetRadioHandler.java

Source

/**
 * Copyright (c) 2014-2017 by the respective copyright holders.
 * 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.eclipse.smarthome.binding.fsinternetradio.handler;

import static java.util.concurrent.TimeUnit.SECONDS;
import static org.eclipse.smarthome.binding.fsinternetradio.FSInternetRadioBindingConstants.*;

import java.math.BigDecimal;
import java.util.concurrent.ScheduledFuture;

import org.eclipse.smarthome.binding.fsinternetradio.internal.radio.FrontierSiliconRadio;
import org.eclipse.smarthome.core.library.types.DecimalType;
import org.eclipse.smarthome.core.library.types.IncreaseDecreaseType;
import org.eclipse.smarthome.core.library.types.OnOffType;
import org.eclipse.smarthome.core.library.types.PercentType;
import org.eclipse.smarthome.core.library.types.StringType;
import org.eclipse.smarthome.core.library.types.UpDownType;
import org.eclipse.smarthome.core.thing.Channel;
import org.eclipse.smarthome.core.thing.ChannelUID;
import org.eclipse.smarthome.core.thing.Thing;
import org.eclipse.smarthome.core.thing.ThingStatus;
import org.eclipse.smarthome.core.thing.ThingStatusDetail;
import org.eclipse.smarthome.core.thing.binding.BaseThingHandler;
import org.eclipse.smarthome.core.types.Command;
import org.eclipse.smarthome.core.types.UnDefType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.apache.commons.lang.StringUtils;

/**
 * The {@link FSInternetRadioHandler} is responsible for handling commands, which are
 * sent to one of the channels.
 *
 * @author Patrick Koenemann - Initial contribution
 * @author Mihaela Memova - removed the unused boolean parameter, changed the check for the PIN
 */
public class FSInternetRadioHandler extends BaseThingHandler {

    private Logger logger = LoggerFactory.getLogger(FSInternetRadioHandler.class);

    private FrontierSiliconRadio radio;

    /** Job that runs {@link #updateRunnable}. */
    private ScheduledFuture<?> updateJob;

    /** Runnable for job {@link #updateJob} for periodic refresh. */
    private Runnable updateRunnable = new Runnable() {
        @Override
        public void run() {
            if (radio == null) {
                // radio is not set, so set all channels to 'undefined'
                for (Channel channel : getThing().getChannels()) {
                    updateState(channel.getUID(), UnDefType.UNDEF);
                }
                // now let's silently check if it's back online
                radioLogin();
                return; // if login is successful, this method is called again :-)
            }
            try {
                final boolean radioOn = radio.getPower();
                for (Channel channel : getThing().getChannels()) {
                    if (!radioOn && !CHANNEL_POWER.equals(channel.getUID().getId())) {
                        // if radio is off, set all channels (except for 'POWER') to 'undefined'
                        updateState(channel.getUID(), UnDefType.UNDEF);
                    } else if (isLinked(channel.getUID().getId())) {
                        // update all channels that are linked
                        switch (channel.getUID().getId()) {
                        case CHANNEL_POWER:
                            updateState(channel.getUID(), radioOn ? OnOffType.ON : OnOffType.OFF);
                            break;
                        case CHANNEL_VOLUME_ABSOLUTE:
                            updateState(channel.getUID(),
                                    DecimalType.valueOf(String.valueOf(radio.getVolumeAbsolute())));
                            break;
                        case CHANNEL_VOLUME_PERCENT:
                            updateState(channel.getUID(),
                                    PercentType.valueOf(String.valueOf(radio.getVolumePercent())));
                            break;
                        case CHANNEL_MODE:
                            updateState(channel.getUID(), DecimalType.valueOf(String.valueOf(radio.getMode())));
                            break;
                        case CHANNEL_MUTE:
                            updateState(channel.getUID(), radio.getMuted() ? OnOffType.ON : OnOffType.OFF);
                            break;
                        case CHANNEL_PRESET:
                            // preset is write-only, ignore
                            break;
                        case CHANNEL_PLAY_INFO_NAME:
                            updateState(channel.getUID(), StringType.valueOf(radio.getPlayInfoName()));
                            break;
                        case CHANNEL_PLAY_INFO_TEXT:
                            updateState(channel.getUID(), StringType.valueOf(radio.getPlayInfoText()));
                            break;
                        default:
                            logger.warn("Ignoring unknown channel during update: " + channel.getLabel());
                        }
                    }
                }
                updateStatus(ThingStatus.ONLINE); // set it back online, maybe it was offline before
            } catch (Exception e) {
                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
            }
        }
    };

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

    @Override
    public void initialize() {
        // Long running initialization should be done asynchronously in background
        radioLogin();

        // also schedule a thread for polling with configured refresh rate
        final BigDecimal period = (BigDecimal) getThing().getConfiguration().get(CONFIG_PROPERTY_REFRESH);
        if (period != null && period.intValue() > 0) {
            updateJob = scheduler.scheduleWithFixedDelay(updateRunnable, period.intValue(), period.intValue(),
                    SECONDS);
        }
    }

    private void radioLogin() {
        // read configuration and spawn thread to establish connection to device
        final String ip = (String) getThing().getConfiguration().get(CONFIG_PROPERTY_IP);
        final BigDecimal port = (BigDecimal) getThing().getConfiguration().get(CONFIG_PROPERTY_PORT);
        final String pin = (String) getThing().getConfiguration().get(CONFIG_PROPERTY_PIN);

        if (ip == null || StringUtils.isEmpty(pin) || port.intValue() == 0) {
            // configuration incomplete
            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Configuration incomplete");
        } else {
            scheduler.execute(new Runnable() {
                @Override
                public void run() {
                    logger.debug("creating new connection to " + ip + ":" + port);
                    try {
                        final FrontierSiliconRadio tmpRadio = new FrontierSiliconRadio(ip, port.intValue(), pin);
                        tmpRadio.login();
                        radio = tmpRadio; // login successful, now store radio in field

                        // Thing initialized. If done set status to ONLINE to indicate proper working.
                        updateStatus(ThingStatus.ONLINE);

                        // now update all channels!
                        updateRunnable.run();
                    } catch (Exception e) {
                        updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
                    }
                }
            });
        }
    }

    @Override
    public void dispose() {
        if (updateJob != null) {
            updateJob.cancel(true);
        }
        updateJob = null;
        radio = null;
    }

    @Override
    public void handleCommand(final ChannelUID channelUID, final Command command) {
        if (radio == null) {
            // connection to radio is not initialized, log ignored command and set status, if it is not already offline
            logger.debug(
                    "Ignoring command " + channelUID.getId() + " = " + command + " because device is offline.");
            if (ThingStatus.ONLINE.equals(getThing().getStatus())) {
                updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
            }
            return;
        }
        try {
            switch (channelUID.getId()) {
            case CHANNEL_POWER:
                if (OnOffType.ON.equals(command)) {
                    radio.setPower(true);
                } else if (OnOffType.OFF.equals(command)) {
                    radio.setPower(false);
                }
                // now all items should be updated! (wait some seconds so that text items are up-to-date)
                scheduler.schedule(updateRunnable, 4, SECONDS);
                break;
            case CHANNEL_VOLUME_PERCENT:
                if (IncreaseDecreaseType.INCREASE.equals(command) || UpDownType.UP.equals(command)) {
                    radio.increaseVolumeAbsolute();
                } else if (IncreaseDecreaseType.DECREASE.equals(command) || UpDownType.DOWN.equals(command)) {
                    radio.decreaseVolumeAbsolute();
                } else if (command instanceof PercentType) {
                    radio.setVolumePercent(((PercentType) command).intValue());
                }
                // absolute value should also be updated now, so let's update all items
                scheduler.schedule(updateRunnable, 1, SECONDS);
                break;
            case CHANNEL_VOLUME_ABSOLUTE:
                if (IncreaseDecreaseType.INCREASE.equals(command) || UpDownType.UP.equals(command)) {
                    radio.increaseVolumeAbsolute();
                } else if (IncreaseDecreaseType.DECREASE.equals(command) || UpDownType.DOWN.equals(command)) {
                    radio.decreaseVolumeAbsolute();
                } else if (command instanceof DecimalType) {
                    radio.setVolumeAbsolute(((DecimalType) command).intValue());
                }
                // percent value should also be updated now, so let's update all items
                scheduler.schedule(updateRunnable, 1, SECONDS);
                break;
            case CHANNEL_MODE:
                if (command instanceof DecimalType) {
                    radio.setMode(((DecimalType) command).intValue());
                }
                break;
            case CHANNEL_PRESET:
                if (command instanceof DecimalType) {
                    radio.setPreset(((DecimalType) command).intValue());
                }
                break;
            case CHANNEL_MUTE:
                if (command instanceof OnOffType) {
                    radio.setMuted(OnOffType.ON.equals(command));
                }
                break;
            default:
                logger.warn("Ignoring unknown command: " + command);
            }
            // make sure that device state is online
            updateStatus(ThingStatus.ONLINE);
        } catch (Exception e) {
            // set device state to offline
            updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
        }
    }
}