2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.fsinternetradio.internal.handler;
15 import static java.util.concurrent.TimeUnit.SECONDS;
16 import static org.openhab.binding.fsinternetradio.internal.FSInternetRadioBindingConstants.*;
18 import java.math.BigDecimal;
19 import java.util.concurrent.ScheduledFuture;
21 import org.apache.commons.lang.StringUtils;
22 import org.eclipse.jetty.client.HttpClient;
23 import org.openhab.binding.fsinternetradio.internal.radio.FrontierSiliconRadio;
24 import org.openhab.core.library.types.DecimalType;
25 import org.openhab.core.library.types.IncreaseDecreaseType;
26 import org.openhab.core.library.types.OnOffType;
27 import org.openhab.core.library.types.PercentType;
28 import org.openhab.core.library.types.StringType;
29 import org.openhab.core.library.types.UpDownType;
30 import org.openhab.core.thing.Channel;
31 import org.openhab.core.thing.ChannelUID;
32 import org.openhab.core.thing.Thing;
33 import org.openhab.core.thing.ThingStatus;
34 import org.openhab.core.thing.ThingStatusDetail;
35 import org.openhab.core.thing.binding.BaseThingHandler;
36 import org.openhab.core.types.Command;
37 import org.openhab.core.types.UnDefType;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
42 * The {@link FSInternetRadioHandler} is responsible for handling commands, which are
43 * sent to one of the channels.
45 * @author Patrick Koenemann - Initial contribution
46 * @author Mihaela Memova - removed the unused boolean parameter, changed the check for the PIN
47 * @author Svilen Valkanov - changed handler initialization
49 public class FSInternetRadioHandler extends BaseThingHandler {
51 private final Logger logger = LoggerFactory.getLogger(FSInternetRadioHandler.class);
53 FrontierSiliconRadio radio;
54 private final HttpClient httpClient;
56 /** Job that runs {@link #updateRunnable}. */
57 private ScheduledFuture<?> updateJob;
59 /** Runnable for job {@link #updateJob} for periodic refresh. */
60 private final Runnable updateRunnable = new Runnable() {
63 if (!radio.isLoggedIn()) {
64 // radio is not set, so set all channels to 'undefined'
65 for (Channel channel : getThing().getChannels()) {
66 updateState(channel.getUID(), UnDefType.UNDEF);
68 // now let's silently check if it's back online
70 return; // if login is successful, this method is called again :-)
73 final boolean radioOn = radio.getPower();
74 for (Channel channel : getThing().getChannels()) {
75 if (!radioOn && !CHANNEL_POWER.equals(channel.getUID().getId())) {
76 // if radio is off, set all channels (except for 'POWER') to 'undefined'
77 updateState(channel.getUID(), UnDefType.UNDEF);
78 } else if (isLinked(channel.getUID().getId())) {
79 // update all channels that are linked
80 switch (channel.getUID().getId()) {
82 updateState(channel.getUID(), radioOn ? OnOffType.ON : OnOffType.OFF);
84 case CHANNEL_VOLUME_ABSOLUTE:
85 updateState(channel.getUID(),
86 DecimalType.valueOf(String.valueOf(radio.getVolumeAbsolute())));
88 case CHANNEL_VOLUME_PERCENT:
89 updateState(channel.getUID(),
90 PercentType.valueOf(String.valueOf(radio.getVolumePercent())));
93 updateState(channel.getUID(), DecimalType.valueOf(String.valueOf(radio.getMode())));
96 updateState(channel.getUID(), radio.getMuted() ? OnOffType.ON : OnOffType.OFF);
99 // preset is write-only, ignore
101 case CHANNEL_PLAY_INFO_NAME:
102 updateState(channel.getUID(), StringType.valueOf(radio.getPlayInfoName()));
104 case CHANNEL_PLAY_INFO_TEXT:
105 updateState(channel.getUID(), StringType.valueOf(radio.getPlayInfoText()));
108 logger.warn("Ignoring unknown channel during update: {}", channel.getLabel());
112 updateStatus(ThingStatus.ONLINE); // set it back online, maybe it was offline before
113 } catch (Exception e) {
114 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
119 public FSInternetRadioHandler(Thing thing, HttpClient httpClient) {
121 this.httpClient = httpClient;
125 public void initialize() {
126 // read configuration
127 final String ip = (String) getThing().getConfiguration().get(CONFIG_PROPERTY_IP);
128 final BigDecimal port = (BigDecimal) getThing().getConfiguration().get(CONFIG_PROPERTY_PORT);
129 final String pin = (String) getThing().getConfiguration().get(CONFIG_PROPERTY_PIN);
131 if (ip == null || StringUtils.isEmpty(pin) || port.intValue() == 0) {
132 // configuration incomplete
133 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Configuration incomplete");
135 radio = new FrontierSiliconRadio(ip, port.intValue(), pin, httpClient);
136 logger.debug("Initializing connection to {}:{}", ip, port);
138 // Long running initialization should be done asynchronously in background
141 // also schedule a thread for polling with configured refresh rate
142 final BigDecimal period = (BigDecimal) getThing().getConfiguration().get(CONFIG_PROPERTY_REFRESH);
143 if (period != null && period.intValue() > 0) {
144 updateJob = scheduler.scheduleWithFixedDelay(updateRunnable, period.intValue(), period.intValue(),
150 private void radioLogin() {
151 scheduler.execute(new Runnable() {
156 // Thing initialized. If done set status to ONLINE to indicate proper working.
157 updateStatus(ThingStatus.ONLINE);
159 // now update all channels!
160 updateRunnable.run();
162 } catch (Exception e) {
163 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
170 public void dispose() {
171 if (updateJob != null) {
172 updateJob.cancel(true);
179 public void handleCommand(final ChannelUID channelUID, final Command command) {
180 if (!radio.isLoggedIn()) {
181 // connection to radio is not initialized, log ignored command and set status, if it is not already offline
182 logger.debug("Ignoring command {} = {} because device is offline.", channelUID.getId(), command);
183 if (ThingStatus.ONLINE.equals(getThing().getStatus())) {
184 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
189 switch (channelUID.getId()) {
191 if (OnOffType.ON.equals(command)) {
192 radio.setPower(true);
193 } else if (OnOffType.OFF.equals(command)) {
194 radio.setPower(false);
196 // now all items should be updated! (wait some seconds so that text items are up-to-date)
197 scheduler.schedule(updateRunnable, 4, SECONDS);
199 case CHANNEL_VOLUME_PERCENT:
200 if (IncreaseDecreaseType.INCREASE.equals(command) || UpDownType.UP.equals(command)) {
201 radio.increaseVolumeAbsolute();
202 } else if (IncreaseDecreaseType.DECREASE.equals(command) || UpDownType.DOWN.equals(command)) {
203 radio.decreaseVolumeAbsolute();
204 } else if (command instanceof PercentType) {
205 radio.setVolumePercent(((PercentType) command).intValue());
207 // absolute value should also be updated now, so let's update all items
208 scheduler.schedule(updateRunnable, 1, SECONDS);
210 case CHANNEL_VOLUME_ABSOLUTE:
211 if (IncreaseDecreaseType.INCREASE.equals(command) || UpDownType.UP.equals(command)) {
212 radio.increaseVolumeAbsolute();
213 } else if (IncreaseDecreaseType.DECREASE.equals(command) || UpDownType.DOWN.equals(command)) {
214 radio.decreaseVolumeAbsolute();
215 } else if (command instanceof DecimalType) {
216 radio.setVolumeAbsolute(((DecimalType) command).intValue());
218 // percent value should also be updated now, so let's update all items
219 scheduler.schedule(updateRunnable, 1, SECONDS);
222 if (command instanceof DecimalType) {
223 radio.setMode(((DecimalType) command).intValue());
227 if (command instanceof DecimalType) {
228 radio.setPreset(((DecimalType) command).intValue());
232 if (command instanceof OnOffType) {
233 radio.setMuted(OnOffType.ON.equals(command));
237 logger.warn("Ignoring unknown command: {}", command);
239 // make sure that device state is online
240 updateStatus(ThingStatus.ONLINE);
241 } catch (Exception e) {
242 // set device state to offline
243 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());