]> git.basschouten.com Git - openhab-addons.git/blob
a24cb7f0431721ae1f8bb17bcb9e63c887e340f4
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.fsinternetradio.internal.handler;
14
15 import static java.util.concurrent.TimeUnit.SECONDS;
16 import static org.openhab.binding.fsinternetradio.internal.FSInternetRadioBindingConstants.*;
17
18 import java.math.BigDecimal;
19 import java.util.concurrent.ScheduledFuture;
20
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;
40
41 /**
42  * The {@link FSInternetRadioHandler} is responsible for handling commands, which are
43  * sent to one of the channels.
44  *
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
48  */
49 public class FSInternetRadioHandler extends BaseThingHandler {
50
51     private final Logger logger = LoggerFactory.getLogger(FSInternetRadioHandler.class);
52
53     FrontierSiliconRadio radio;
54     private final HttpClient httpClient;
55
56     /** Job that runs {@link #updateRunnable}. */
57     private ScheduledFuture<?> updateJob;
58
59     /** Runnable for job {@link #updateJob} for periodic refresh. */
60     private final Runnable updateRunnable = new Runnable() {
61         @Override
62         public void run() {
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);
67                 }
68                 // now let's silently check if it's back online
69                 radioLogin();
70                 return; // if login is successful, this method is called again :-)
71             }
72             try {
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()) {
81                             case CHANNEL_POWER:
82                                 updateState(channel.getUID(), radioOn ? OnOffType.ON : OnOffType.OFF);
83                                 break;
84                             case CHANNEL_VOLUME_ABSOLUTE:
85                                 updateState(channel.getUID(),
86                                         DecimalType.valueOf(String.valueOf(radio.getVolumeAbsolute())));
87                                 break;
88                             case CHANNEL_VOLUME_PERCENT:
89                                 updateState(channel.getUID(),
90                                         PercentType.valueOf(String.valueOf(radio.getVolumePercent())));
91                                 break;
92                             case CHANNEL_MODE:
93                                 updateState(channel.getUID(), DecimalType.valueOf(String.valueOf(radio.getMode())));
94                                 break;
95                             case CHANNEL_MUTE:
96                                 updateState(channel.getUID(), radio.getMuted() ? OnOffType.ON : OnOffType.OFF);
97                                 break;
98                             case CHANNEL_PRESET:
99                                 // preset is write-only, ignore
100                                 break;
101                             case CHANNEL_PLAY_INFO_NAME:
102                                 updateState(channel.getUID(), StringType.valueOf(radio.getPlayInfoName()));
103                                 break;
104                             case CHANNEL_PLAY_INFO_TEXT:
105                                 updateState(channel.getUID(), StringType.valueOf(radio.getPlayInfoText()));
106                                 break;
107                             default:
108                                 logger.warn("Ignoring unknown channel during update: {}", channel.getLabel());
109                         }
110                     }
111                 }
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());
115             }
116         }
117     };
118
119     public FSInternetRadioHandler(Thing thing, HttpClient httpClient) {
120         super(thing);
121         this.httpClient = httpClient;
122     }
123
124     @Override
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);
130
131         if (ip == null || StringUtils.isEmpty(pin) || port.intValue() == 0) {
132             // configuration incomplete
133             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Configuration incomplete");
134         } else {
135             radio = new FrontierSiliconRadio(ip, port.intValue(), pin, httpClient);
136             logger.debug("Initializing connection to {}:{}", ip, port);
137
138             // Long running initialization should be done asynchronously in background
139             radioLogin();
140
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(),
145                         SECONDS);
146             }
147         }
148     }
149
150     private void radioLogin() {
151         scheduler.execute(new Runnable() {
152             @Override
153             public void run() {
154                 try {
155                     if (radio.login()) {
156                         // Thing initialized. If done set status to ONLINE to indicate proper working.
157                         updateStatus(ThingStatus.ONLINE);
158
159                         // now update all channels!
160                         updateRunnable.run();
161                     }
162                 } catch (Exception e) {
163                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
164                 }
165             }
166         });
167     }
168
169     @Override
170     public void dispose() {
171         if (updateJob != null) {
172             updateJob.cancel(true);
173         }
174         updateJob = null;
175         radio = null;
176     }
177
178     @Override
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);
185             }
186             return;
187         }
188         try {
189             switch (channelUID.getId()) {
190                 case CHANNEL_POWER:
191                     if (OnOffType.ON.equals(command)) {
192                         radio.setPower(true);
193                     } else if (OnOffType.OFF.equals(command)) {
194                         radio.setPower(false);
195                     }
196                     // now all items should be updated! (wait some seconds so that text items are up-to-date)
197                     scheduler.schedule(updateRunnable, 4, SECONDS);
198                     break;
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());
206                     }
207                     // absolute value should also be updated now, so let's update all items
208                     scheduler.schedule(updateRunnable, 1, SECONDS);
209                     break;
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());
217                     }
218                     // percent value should also be updated now, so let's update all items
219                     scheduler.schedule(updateRunnable, 1, SECONDS);
220                     break;
221                 case CHANNEL_MODE:
222                     if (command instanceof DecimalType) {
223                         radio.setMode(((DecimalType) command).intValue());
224                     }
225                     break;
226                 case CHANNEL_PRESET:
227                     if (command instanceof DecimalType) {
228                         radio.setPreset(((DecimalType) command).intValue());
229                     }
230                     break;
231                 case CHANNEL_MUTE:
232                     if (command instanceof OnOffType) {
233                         radio.setMuted(OnOffType.ON.equals(command));
234                     }
235                     break;
236                 default:
237                     logger.warn("Ignoring unknown command: {}", command);
238             }
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());
244         }
245     }
246 }