<commit_before>/**
 * Copyright (c) 2014-2015 openHAB UG (haftungsbeschraenkt) 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.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;

/**
 * The {@link FSInternetRadioHandler} is responsible for handling commands, which are
 * sent to one of the channels.
 *
 * @author <NAME> - Initial contribution
 */
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(false);
                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 (channel.isLinked()) {
                        // 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(true);

        // 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(boolean logFailure) {
        // 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 || pin == null || 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) {<commit_msg>Channel#isLinked is deprecated, use BaseThingHandler#isLinked<commit_after>/**
 * Copyright (c) 2014-2015 openHAB UG (haftungsbeschraenkt) 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.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;

/**
 * The {@link FSInternetRadioHandler} is responsible for handling commands, which are
 * sent to one of the channels.
 *
 * @author <NAME> - Initial contribution
 */
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(false);
                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(true);

        // 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(boolean logFailure) {
        // 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 || pin == null || 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) {
{'fraction_non_alphanumeric': 0.09113247863247863, 'fraction_numerical': 0.0018162393162393163, 'mean_word_length': 1.8101170819573702, 'pattern_counts': {'":': 2, '<': 7, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3530060', 'n_tokens_mistral': 4737, 'n_tokens_neox': 4470, 'n_words': 1060}