org.openhab.binding.amazonechocontrol.handler.EchoHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.openhab.binding.amazonechocontrol.handler.EchoHandler.java

Source

/**
 * Copyright (c) 2010-2018 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.openhab.binding.amazonechocontrol.handler;

import static org.openhab.binding.amazonechocontrol.AmazonEchoControlBindingConstants.*;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang.StringUtils;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.smarthome.core.library.types.IncreaseDecreaseType;
import org.eclipse.smarthome.core.library.types.NextPreviousType;
import org.eclipse.smarthome.core.library.types.OnOffType;
import org.eclipse.smarthome.core.library.types.PercentType;
import org.eclipse.smarthome.core.library.types.PlayPauseType;
import org.eclipse.smarthome.core.library.types.RewindFastforwardType;
import org.eclipse.smarthome.core.library.types.StringType;
import org.eclipse.smarthome.core.thing.Bridge;
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.ThingUID;
import org.eclipse.smarthome.core.thing.binding.BaseThingHandler;
import org.eclipse.smarthome.core.types.Command;
import org.eclipse.smarthome.core.types.RefreshType;
import org.eclipse.smarthome.core.types.UnDefType;
import org.openhab.binding.amazonechocontrol.internal.Connection;
import org.openhab.binding.amazonechocontrol.internal.HttpException;
import org.openhab.binding.amazonechocontrol.internal.jsons.JsonBluetoothStates;
import org.openhab.binding.amazonechocontrol.internal.jsons.JsonBluetoothStates.BluetoothState;
import org.openhab.binding.amazonechocontrol.internal.jsons.JsonBluetoothStates.PairedDevice;
import org.openhab.binding.amazonechocontrol.internal.jsons.JsonDevices.Device;
import org.openhab.binding.amazonechocontrol.internal.jsons.JsonMediaState;
import org.openhab.binding.amazonechocontrol.internal.jsons.JsonMediaState.QueueEntry;
import org.openhab.binding.amazonechocontrol.internal.jsons.JsonNotificationResponse;
import org.openhab.binding.amazonechocontrol.internal.jsons.JsonNotificationSound;
import org.openhab.binding.amazonechocontrol.internal.jsons.JsonPlayerState;
import org.openhab.binding.amazonechocontrol.internal.jsons.JsonPlayerState.PlayerInfo;
import org.openhab.binding.amazonechocontrol.internal.jsons.JsonPlayerState.PlayerInfo.InfoText;
import org.openhab.binding.amazonechocontrol.internal.jsons.JsonPlayerState.PlayerInfo.MainArt;
import org.openhab.binding.amazonechocontrol.internal.jsons.JsonPlayerState.PlayerInfo.Provider;
import org.openhab.binding.amazonechocontrol.internal.jsons.JsonPlayerState.PlayerInfo.Volume;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The {@link EchoHandler} is responsible for the handling of the echo device
 *
 * @author Michael Geramb - Initial contribution
 */
@NonNullByDefault
public class EchoHandler extends BaseThingHandler {

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

    private @Nullable Device device;
    private @Nullable Connection connection;
    private @Nullable AccountHandler account;
    private @Nullable ScheduledFuture<?> updateStateJob;
    private @Nullable String lastKnownRadioStationId;
    private @Nullable String lastKnownBluetoothMAC;
    private @Nullable String lastKnownAmazonMusicId;
    private String musicProviderId = "TUNEIN";
    private boolean isPlaying = false;
    private boolean isPaused = false;
    private int lastKnownVolume = 25;
    private @Nullable BluetoothState bluetoothState;
    private boolean disableUpdate = false;
    private boolean updateRemind = true;
    private boolean updateTextToSpeech = true;
    private boolean updateAlarm = true;
    private boolean updateRoutine = true;
    private boolean updatePlayMusicVoiceCommand = true;
    private boolean updateStartCommand = true;
    private @Nullable JsonNotificationResponse currentNotification;
    private @Nullable ScheduledFuture<?> currentNotifcationUpdateTimer;

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

    @Override
    public void initialize() {
        logger.debug("Amazon Echo Control Binding initialized");

        if (this.connection != null) {
            setDeviceAndUpdateThingState(this.device);
        } else {
            updateStatus(ThingStatus.UNKNOWN);
            Bridge bridge = this.getBridge();
            if (bridge != null) {
                AccountHandler account = (AccountHandler) bridge.getHandler();
                if (account != null) {
                    this.account = account;
                    account.addEchoHandler(this);
                }
            }
        }
    }

    public void intialize(Connection connection, @Nullable Device deviceJson) {
        this.connection = connection;
        setDeviceAndUpdateThingState(deviceJson);
    }

    boolean setDeviceAndUpdateThingState(@Nullable Device device) {
        if (device == null) {
            updateStatus(ThingStatus.UNKNOWN);
            return false;
        }
        this.device = device;
        if (!device.online) {
            updateStatus(ThingStatus.OFFLINE);
            return false;
        }
        updateStatus(ThingStatus.ONLINE);
        return true;
    }

    @Override
    public void dispose() {
        stopCurrentNotification();
        ScheduledFuture<?> updateStateJob = this.updateStateJob;
        this.updateStateJob = null;
        if (updateStateJob != null) {
            updateStateJob.cancel(false);
        }
        super.dispose();
    }

    public @Nullable BluetoothState findBluetoothState() {
        return this.bluetoothState;
    }

    public @Nullable Connection findConnection() {
        return this.connection;
    }

    public @Nullable AccountHandler findAccount() {
        return this.account;
    }

    public @Nullable Device findDevice() {
        return this.device;
    }

    public String findSerialNumber() {
        String id = (String) getConfig().get(DEVICE_PROPERTY_SERIAL_NUMBER);
        if (id == null) {
            return "";
        }
        return id;
    }

    @Override
    public void handleCommand(ChannelUID channelUID, Command command) {
        try {
            int waitForUpdate = 1000;
            boolean needBluetoothRefresh = false;
            String lastKnownBluetoothMAC = this.lastKnownBluetoothMAC;

            ScheduledFuture<?> updateStateJob = this.updateStateJob;
            this.updateStateJob = null;
            if (updateStateJob != null) {
                updateStateJob.cancel(false);
            }

            Connection connection = this.connection;
            if (connection == null) {
                return;
            }
            Device device = this.device;
            if (device == null) {
                return;
            }

            // Player commands
            String channelId = channelUID.getId();
            if (channelId.equals(CHANNEL_PLAYER)) {
                if (command == PlayPauseType.PAUSE || command == OnOffType.OFF) {
                    connection.command(device, "{\"type\":\"PauseCommand\"}");
                } else if (command == PlayPauseType.PLAY || command == OnOffType.ON) {
                    if (isPaused) {
                        connection.command(device, "{\"type\":\"PlayCommand\"}");
                    } else {
                        connection.playMusicVoiceCommand(device, this.musicProviderId, "!");
                        waitForUpdate = 3000;
                    }
                } else if (command == NextPreviousType.NEXT) {
                    connection.command(device, "{\"type\":\"NextCommand\"}");
                } else if (command == NextPreviousType.PREVIOUS) {
                    connection.command(device, "{\"type\":\"PreviousCommand\"}");
                } else if (command == RewindFastforwardType.FASTFORWARD) {
                    connection.command(device, "{\"type\":\"ForwardCommand\"}");
                } else if (command == RewindFastforwardType.REWIND) {
                    connection.command(device, "{\"type\":\"RewindCommand\"}");
                }
            }
            // Volume commands
            if (channelId.equals(CHANNEL_VOLUME)) {
                if (command instanceof PercentType) {
                    PercentType value = (PercentType) command;
                    int volume = value.intValue();
                    connection.command(device, "{\"type\":\"VolumeLevelCommand\",\"volumeLevel\":" + volume
                            + ",\"contentFocusClientId\":\"Default\"}");
                } else if (command == OnOffType.OFF) {
                    connection.command(device, "{\"type\":\"VolumeLevelCommand\",\"volumeLevel\":" + 0
                            + ",\"contentFocusClientId\":\"Default\"}");
                } else if (command == OnOffType.ON) {
                    connection.command(device, "{\"type\":\"VolumeLevelCommand\",\"volumeLevel\":" + lastKnownVolume
                            + ",\"contentFocusClientId\":\"Default\"}");
                } else if (command == IncreaseDecreaseType.INCREASE) {
                    if (lastKnownVolume < 100) {
                        lastKnownVolume++;
                        updateState(CHANNEL_VOLUME, new PercentType(lastKnownVolume));
                        connection.command(device, "{\"type\":\"VolumeLevelCommand\",\"volumeLevel\":"
                                + lastKnownVolume + ",\"contentFocusClientId\":\"Default\"}");
                    }
                } else if (command == IncreaseDecreaseType.DECREASE) {
                    if (lastKnownVolume > 0) {
                        lastKnownVolume--;
                        updateState(CHANNEL_VOLUME, new PercentType(lastKnownVolume));
                        connection.command(device, "{\"type\":\"VolumeLevelCommand\",\"volumeLevel\":"
                                + lastKnownVolume + ",\"contentFocusClientId\":\"Default\"}");
                    }
                }
            }
            // shuffle command
            if (channelId.equals(CHANNEL_SHUFFLE)) {
                if (command instanceof OnOffType) {
                    OnOffType value = (OnOffType) command;

                    connection.command(device, "{\"type\":\"ShuffleCommand\",\"shuffle\":\""
                            + (value == OnOffType.ON ? "true" : "false") + "\"}");
                }
            }

            // play music command
            if (channelId.equals(CHANNEL_MUSIC_PROVIDER_ID)) {
                if (command instanceof StringType) {
                    waitForUpdate = 0;
                    String musicProviderId = ((StringType) command).toFullString();
                    if (!StringUtils.equals(musicProviderId, this.musicProviderId)) {
                        this.musicProviderId = musicProviderId;
                        if (this.isPlaying) {
                            connection.playMusicVoiceCommand(device, this.musicProviderId, "!");
                            waitForUpdate = 3000;
                        }
                    }

                }
            }
            if (channelId.equals(CHANNEL_PLAY_MUSIC_VOICE_COMMAND)) {
                if (command instanceof StringType) {
                    String voiceCommand = ((StringType) command).toFullString();
                    if (!this.musicProviderId.isEmpty()) {
                        connection.playMusicVoiceCommand(device, this.musicProviderId, voiceCommand);
                        waitForUpdate = 3000;
                        updatePlayMusicVoiceCommand = true;
                    }
                }
            }

            // bluetooth commands
            if (channelId.equals(CHANNEL_BLUETOOTH_MAC)) {
                needBluetoothRefresh = true;
                if (command instanceof StringType) {
                    String address = ((StringType) command).toFullString();
                    if (!address.isEmpty()) {
                        waitForUpdate = 4000;
                    }
                    connection.bluetooth(device, address);
                }
            }
            if (channelId.equals(CHANNEL_BLUETOOTH)) {
                needBluetoothRefresh = true;
                if (command == OnOffType.ON) {
                    waitForUpdate = 4000;
                    String bluetoothId = lastKnownBluetoothMAC;
                    BluetoothState state = bluetoothState;
                    if (state != null && (StringUtils.isEmpty(bluetoothId))) {
                        PairedDevice[] pairedDeviceList = state.pairedDeviceList;
                        if (pairedDeviceList != null) {
                            for (PairedDevice paired : pairedDeviceList) {
                                if (paired == null) {
                                    continue;
                                }
                                if (StringUtils.isNotEmpty(paired.address)) {
                                    lastKnownBluetoothMAC = paired.address;
                                    break;
                                }
                            }
                        }
                    }
                    if (StringUtils.isNotEmpty(lastKnownBluetoothMAC)) {
                        connection.bluetooth(device, lastKnownBluetoothMAC);
                    }
                } else if (command == OnOffType.OFF) {
                    connection.bluetooth(device, null);
                }
            }
            if (channelId.equals(CHANNEL_BLUETOOTH_DEVICE_NAME)) {
                needBluetoothRefresh = true;
            }
            // amazon music commands
            if (channelId.equals(CHANNEL_AMAZON_MUSIC_TRACK_ID)) {
                if (command instanceof StringType) {

                    String trackId = ((StringType) command).toFullString();
                    if (StringUtils.isNotEmpty(trackId)) {
                        waitForUpdate = 3000;
                    }
                    connection.playAmazonMusicTrack(device, trackId);

                }
            }
            if (channelId.equals(CHANNEL_AMAZON_MUSIC_PLAY_LIST_ID)) {
                if (command instanceof StringType) {

                    String playListId = ((StringType) command).toFullString();
                    if (StringUtils.isNotEmpty(playListId)) {
                        waitForUpdate = 3000;
                    }
                    connection.playAmazonMusicPlayList(device, playListId);

                }
            }
            if (channelId.equals(CHANNEL_AMAZON_MUSIC)) {

                if (command == OnOffType.ON) {
                    String lastKnownAmazonMusicId = this.lastKnownAmazonMusicId;
                    if (StringUtils.isNotEmpty(lastKnownAmazonMusicId)) {
                        waitForUpdate = 3000;
                    }
                    connection.playAmazonMusicTrack(device, lastKnownAmazonMusicId);
                } else if (command == OnOffType.OFF) {
                    connection.playAmazonMusicTrack(device, "");
                }
            }

            // radio commands
            if (channelId.equals(CHANNEL_RADIO_STATION_ID)) {
                if (command instanceof StringType) {
                    String stationId = ((StringType) command).toFullString();
                    if (StringUtils.isNotEmpty(stationId)) {
                        waitForUpdate = 3000;
                    }
                    connection.playRadio(device, stationId);
                }
            }
            if (channelId.equals(CHANNEL_RADIO)) {
                if (command == OnOffType.ON) {
                    String lastKnownRadioStationId = this.lastKnownRadioStationId;
                    if (StringUtils.isNotEmpty(lastKnownRadioStationId)) {
                        waitForUpdate = 3000;
                    }
                    connection.playRadio(device, lastKnownRadioStationId);
                } else if (command == OnOffType.OFF) {
                    connection.playRadio(device, "");
                }
            }

            // notification
            if (channelId.equals(CHANNEL_REMIND)) {
                if (command instanceof StringType) {
                    stopCurrentNotification();
                    String reminder = ((StringType) command).toFullString();
                    if (StringUtils.isNotEmpty(reminder)) {
                        waitForUpdate = 3000;
                        updateRemind = true;
                        currentNotification = connection.notification(device, "Reminder", reminder, null);
                        currentNotifcationUpdateTimer = scheduler.scheduleWithFixedDelay(() -> {
                            updateNotificationTimerState();
                        }, 1, 1, TimeUnit.SECONDS);
                    }
                }
            }
            if (channelId.equals(CHANNEL_PLAY_ALARM_SOUND)) {
                if (command instanceof StringType) {
                    stopCurrentNotification();
                    String alarmSound = ((StringType) command).toFullString();
                    if (StringUtils.isNotEmpty(alarmSound)) {
                        waitForUpdate = 3000;
                        updateAlarm = true;
                        String[] parts = alarmSound.split(":", 2);
                        JsonNotificationSound sound = new JsonNotificationSound();
                        if (parts.length == 2) {
                            sound.providerId = parts[0];
                            sound.id = parts[1];
                        } else {
                            sound.providerId = "ECHO";
                            sound.id = alarmSound;
                        }
                        currentNotification = connection.notification(device, "Alarm", null, sound);
                        currentNotifcationUpdateTimer = scheduler.scheduleWithFixedDelay(() -> {
                            updateNotificationTimerState();
                        }, 1, 1, TimeUnit.SECONDS);

                    }
                }
            }

            // routine commands
            if (channelId.equals(CHANNEL_TEXT_TO_SPEECH)) {
                if (command instanceof StringType) {
                    String text = ((StringType) command).toFullString();
                    if (StringUtils.isNotEmpty(text)) {
                        waitForUpdate = 1000;
                        updateTextToSpeech = true;
                        connection.textToSpeech(device, text);
                    }
                }
            }
            if (channelId.equals(CHANNEL_START_COMMAND)) {
                if (command instanceof StringType) {
                    String commandText = ((StringType) command).toFullString();
                    if (StringUtils.isNotEmpty(commandText)) {
                        updateStartCommand = true;
                        if (commandText.startsWith(FLASH_BRIEFING_COMMAND_PREFIX)) {
                            // Handle custom flashbriefings commands
                            String flashbriefing = commandText.substring(FLASH_BRIEFING_COMMAND_PREFIX.length());

                            AccountHandler account = this.account;
                            if (account != null) {
                                for (FlashBriefingProfileHandler flashBriefing : account
                                        .getFlashBriefingProfileHandlers()) {
                                    ThingUID flashBriefingId = flashBriefing.getThing().getUID();
                                    if (StringUtils.equals(flashBriefing.getThing().getUID().getId(),
                                            flashbriefing)) {
                                        flashBriefing.handleCommand(
                                                new ChannelUID(flashBriefingId, CHANNEL_PLAY_ON_DEVICE),
                                                new StringType(device.serialNumber));
                                        break;
                                    }
                                }
                            }
                        } else {
                            // Handle standard commands
                            if (!commandText.startsWith("Alexa.")) {
                                commandText = "Alexa." + commandText + ".Play";
                            }
                            waitForUpdate = 1000;
                            connection.executeSequenceCommand(device, commandText, null);
                        }
                    }
                }
            }
            if (channelId.equals(CHANNEL_START_ROUTINE)) {
                if (command instanceof StringType) {
                    String utterance = ((StringType) command).toFullString();
                    if (StringUtils.isNotEmpty(utterance)) {
                        waitForUpdate = 1000;
                        updateRoutine = true;
                        connection.startRoutine(device, utterance);
                    }
                }
            }

            // force update of the state
            this.disableUpdate = true;
            final boolean bluetoothRefresh = needBluetoothRefresh;
            Runnable doRefresh = () -> {
                BluetoothState state = null;
                if (bluetoothRefresh) {
                    JsonBluetoothStates states;
                    states = connection.getBluetoothConnectionStates();
                    state = states.findStateByDevice(device);

                }
                this.disableUpdate = false;
                updateState(device, state);
            };
            if (command instanceof RefreshType) {
                waitForUpdate = 0;
            }
            if (waitForUpdate == 0) {
                doRefresh.run();
            } else {
                this.updateStateJob = scheduler.schedule(doRefresh, waitForUpdate, TimeUnit.MILLISECONDS);
            }
        } catch (IOException | URISyntaxException e) {
            logger.info("handleCommand fails: {}", e);
        }
    }

    private void stopCurrentNotification() {
        ScheduledFuture<?> currentNotifcationUpdateTimer = this.currentNotifcationUpdateTimer;
        if (currentNotifcationUpdateTimer != null) {
            this.currentNotifcationUpdateTimer = null;
            currentNotifcationUpdateTimer.cancel(true);
        }
        JsonNotificationResponse currentNotification = this.currentNotification;
        if (currentNotification != null) {
            this.currentNotification = null;
            Connection currentConnection = this.connection;
            if (currentConnection != null) {
                try {
                    currentConnection.stopNotification(currentNotification);
                } catch (IOException | URISyntaxException e) {
                    logger.warn("Stop notification failed: {}", e);
                }
            }
        }
    }

    private void updateNotificationTimerState() {
        boolean stopCurrentNotifcation = true;
        JsonNotificationResponse currentNotification = this.currentNotification;
        try {
            if (currentNotification != null) {
                Connection currentConnection = connection;
                if (currentConnection != null) {
                    JsonNotificationResponse newState = currentConnection.getNotificationState(currentNotification);
                    if (StringUtils.equals(newState.status, "ON")) {
                        stopCurrentNotifcation = false;
                    }
                }
            }
        } catch (IOException | URISyntaxException e) {
            logger.warn("update notification state fails: {}", e);
        }
        if (stopCurrentNotifcation) {
            if (currentNotification != null) {
                String type = currentNotification.type;
                if (type != null) {
                    if (type.equals("Reminder")) {
                        updateState(CHANNEL_REMIND, new StringType(""));
                        updateRemind = false;
                    }
                    if (type.equals("Alarm")) {
                        updateState(CHANNEL_PLAY_ALARM_SOUND, new StringType(""));
                        updateAlarm = false;
                    }
                }
            }
            stopCurrentNotification();
        }
    }

    public void updateState(@Nullable Device device, @Nullable BluetoothState bluetoothState) {
        if (this.disableUpdate) {
            return;
        }
        if (!setDeviceAndUpdateThingState(device)) {
            return;
        }
        if (device == null) {
            return;
        }

        Connection connection = this.connection;
        if (connection == null) {
            return;
        }

        PlayerInfo playerInfo = null;
        Provider provider = null;
        InfoText infoText = null;
        MainArt mainArt = null;
        try {
            JsonPlayerState playerState = connection.getPlayer(device);
            playerInfo = playerState.playerInfo;
            if (playerInfo != null) {
                infoText = playerInfo.infoText;
                if (infoText == null) {
                    infoText = playerInfo.miniInfoText;
                }
                mainArt = playerInfo.mainArt;
                provider = playerInfo.provider;
            }
        } catch (HttpException e) {
            if (e.getCode() == 400) {
                // Ignore
            } else {
                logger.info("getPlayer fails: {}", e);
            }
        } catch (IOException | URISyntaxException e) {
            logger.info("getPlayer fails: {}", e);
        }
        JsonMediaState mediaState = null;
        try {
            mediaState = connection.getMediaState(device);

        } catch (HttpException e) {
            if (e.getCode() == 400) {

                updateState(CHANNEL_RADIO_STATION_ID, new StringType(""));

            } else {
                logger.info("getMediaState fails: {}", e);
            }
        } catch (IOException | URISyntaxException e) {
            logger.info("getMediaState fails: {}", e);
        }
        // check playing
        isPlaying = (playerInfo != null && StringUtils.equals(playerInfo.state, "PLAYING"))
                || (mediaState != null && StringUtils.equals(mediaState.currentState, "PLAYING"));

        isPaused = (playerInfo != null && StringUtils.equals(playerInfo.state, "PAUSED"))
                || (mediaState != null && StringUtils.equals(mediaState.currentState, "PAUSED"));
        // handle music provider id

        if (provider != null && isPlaying) {
            String musicProviderId;
            if (mediaState != null && StringUtils.equals(mediaState.currentState, "PLAYING")) {
                musicProviderId = mediaState.providerId;
            } else {
                musicProviderId = provider.providerName;
            }
            // Map the music provider id to the one used for starting music with voice command
            if (musicProviderId != null) {
                musicProviderId = musicProviderId.toUpperCase();
            }
            if (StringUtils.equals(musicProviderId, "CLOUD_PLAYER")) {
                musicProviderId = "AMAZON_MUSIC";
            }
            if (StringUtils.equals(musicProviderId, "TUNE_IN")) {
                musicProviderId = "TUNEIN";
            }
            if (musicProviderId != null) {
                this.musicProviderId = musicProviderId;
            }
        }

        // handle amazon music
        String amazonMusicTrackId = "";
        String amazonMusicPlayListId = "";
        boolean amazonMusic = false;
        if (mediaState != null && isPlaying && StringUtils.equals(mediaState.providerId, "CLOUD_PLAYER")
                && StringUtils.isNotEmpty(mediaState.contentId)) {
            amazonMusicTrackId = mediaState.contentId;
            lastKnownAmazonMusicId = amazonMusicTrackId;
            amazonMusic = true;
        }

        // handle bluetooth
        String bluetoothMAC = "";
        String bluetoothDeviceName = "";
        boolean bluetoothIsConnected = false;
        if (bluetoothState != null) {
            this.bluetoothState = bluetoothState;
            PairedDevice[] pairedDeviceList = bluetoothState.pairedDeviceList;
            if (pairedDeviceList != null) {
                for (PairedDevice paired : pairedDeviceList) {
                    if (paired == null) {
                        continue;
                    }
                    if (paired.connected && paired.address != null) {
                        bluetoothIsConnected = true;
                        bluetoothMAC = paired.address;
                        bluetoothDeviceName = paired.friendlyName;
                        if (StringUtils.isEmpty(bluetoothDeviceName)) {
                            bluetoothDeviceName = paired.address;
                        }
                        break;
                    }
                }
            }
        }
        if (StringUtils.isNotEmpty(bluetoothMAC)) {
            lastKnownBluetoothMAC = bluetoothMAC;
        }

        // handle radio
        boolean isRadio = false;
        if (mediaState != null && StringUtils.isNotEmpty(mediaState.radioStationId)) {
            lastKnownRadioStationId = mediaState.radioStationId;
            if (provider != null && StringUtils.equalsIgnoreCase(provider.providerName, "TuneIn Live-Radio")) {
                isRadio = true;
            }
        }
        String radioStationId = "";
        if (isRadio && mediaState != null && StringUtils.equals(mediaState.currentState, "PLAYING")
                && mediaState.radioStationId != null) {
            radioStationId = mediaState.radioStationId;
        }

        // handle title, subtitle, imageUrl
        String title = "";
        String subTitle1 = "";
        String subTitle2 = "";
        String imageUrl = "";
        if (infoText != null) {
            if (infoText.title != null) {
                title = infoText.title;
            }
            if (infoText.subText1 != null) {
                subTitle1 = infoText.subText1;
            }

            if (infoText.subText2 != null) {
                subTitle2 = infoText.subText2;
            }
        }
        if (mainArt != null) {
            if (mainArt.url != null) {
                imageUrl = mainArt.url;
            }
        }
        if (mediaState != null) {
            QueueEntry[] queueEntries = mediaState.queue;
            if (queueEntries != null && queueEntries.length > 0) {
                QueueEntry entry = queueEntries[0];
                if (entry != null) {

                    if (isRadio) {
                        if (StringUtils.isEmpty(imageUrl) && entry.imageURL != null) {
                            imageUrl = entry.imageURL;
                        }
                        if (StringUtils.isEmpty(subTitle1) && entry.radioStationSlogan != null) {
                            subTitle1 = entry.radioStationSlogan;
                        }
                        if (StringUtils.isEmpty(subTitle2) && entry.radioStationLocation != null) {
                            subTitle2 = entry.radioStationLocation;
                        }
                    }
                }
            }
        }

        // handle provider
        String providerDisplayName = "";
        if (provider != null) {
            if (provider.providerDisplayName != null) {
                providerDisplayName = provider.providerDisplayName;
            }
            if (StringUtils.isNotEmpty(provider.providerName) && StringUtils.isEmpty(providerDisplayName)) {
                providerDisplayName = provider.providerName;
            }
        }

        // handle volume
        Integer volume = null;
        if (mediaState != null) {
            volume = mediaState.volume;
        } else if (playerInfo != null) {

            Volume volumnInfo = playerInfo.volume;
            if (volumnInfo != null) {
                volume = volumnInfo.volume;
            }
        }
        if (volume != null && volume > 0) {
            lastKnownVolume = volume;
        }

        // Update states
        if (updateRemind && currentNotifcationUpdateTimer == null) {
            updateRemind = false;
            updateState(CHANNEL_REMIND, new StringType(""));
        }
        if (updateAlarm && currentNotifcationUpdateTimer == null) {
            updateAlarm = false;
            updateState(CHANNEL_PLAY_ALARM_SOUND, new StringType(""));
        }
        if (updateRoutine) {
            updateRoutine = false;
            updateState(CHANNEL_START_ROUTINE, new StringType(""));
        }
        if (updateTextToSpeech) {
            updateTextToSpeech = false;
            updateState(CHANNEL_TEXT_TO_SPEECH, new StringType(""));
        }
        if (updatePlayMusicVoiceCommand) {
            updatePlayMusicVoiceCommand = false;
            updateState(CHANNEL_PLAY_MUSIC_VOICE_COMMAND, new StringType(""));
        }
        if (updateStartCommand) {
            updateStartCommand = false;
            updateState(CHANNEL_START_COMMAND, new StringType(""));
        }
        updateState(CHANNEL_MUSIC_PROVIDER_ID, new StringType(musicProviderId));
        updateState(CHANNEL_AMAZON_MUSIC_TRACK_ID, new StringType(amazonMusicTrackId));
        updateState(CHANNEL_AMAZON_MUSIC, isPlaying && amazonMusic ? OnOffType.ON : OnOffType.OFF);
        updateState(CHANNEL_AMAZON_MUSIC_PLAY_LIST_ID, new StringType(amazonMusicPlayListId));
        updateState(CHANNEL_RADIO_STATION_ID, new StringType(radioStationId));
        updateState(CHANNEL_RADIO, isPlaying && isRadio ? OnOffType.ON : OnOffType.OFF);
        updateState(CHANNEL_VOLUME, volume != null ? new PercentType(volume) : UnDefType.UNDEF);
        updateState(CHANNEL_PROVIDER_DISPLAY_NAME, new StringType(providerDisplayName));
        updateState(CHANNEL_PLAYER, isPlaying ? PlayPauseType.PLAY : PlayPauseType.PAUSE);
        updateState(CHANNEL_IMAGE_URL, new StringType(imageUrl));
        updateState(CHANNEL_TITLE, new StringType(title));
        updateState(CHANNEL_SUBTITLE1, new StringType(subTitle1));
        updateState(CHANNEL_SUBTITLE2, new StringType(subTitle2));
        if (bluetoothState != null) {
            updateState(CHANNEL_BLUETOOTH, bluetoothIsConnected ? OnOffType.ON : OnOffType.OFF);
            updateState(CHANNEL_BLUETOOTH_MAC, new StringType(bluetoothMAC));
            updateState(CHANNEL_BLUETOOTH_DEVICE_NAME, new StringType(bluetoothDeviceName));
        }
    }
}