]> git.basschouten.com Git - openhab-addons.git/blob
ae0c0d715683e1be7f24c44c76e8811b6a5828dd
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.eclipse.jetty.client.HttpClient;
22 import org.openhab.binding.fsinternetradio.internal.radio.FrontierSiliconRadio;
23 import org.openhab.core.library.types.DecimalType;
24 import org.openhab.core.library.types.IncreaseDecreaseType;
25 import org.openhab.core.library.types.OnOffType;
26 import org.openhab.core.library.types.PercentType;
27 import org.openhab.core.library.types.StringType;
28 import org.openhab.core.library.types.UpDownType;
29 import org.openhab.core.thing.Channel;
30 import org.openhab.core.thing.ChannelUID;
31 import org.openhab.core.thing.Thing;
32 import org.openhab.core.thing.ThingStatus;
33 import org.openhab.core.thing.ThingStatusDetail;
34 import org.openhab.core.thing.binding.BaseThingHandler;
35 import org.openhab.core.types.Command;
36 import org.openhab.core.types.UnDefType;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 /**
41  * The {@link FSInternetRadioHandler} is responsible for handling commands, which are
42  * sent to one of the channels.
43  *
44  * @author Patrick Koenemann - Initial contribution
45  * @author Mihaela Memova - removed the unused boolean parameter, changed the check for the PIN
46  * @author Svilen Valkanov - changed handler initialization
47  */
48 public class FSInternetRadioHandler extends BaseThingHandler {
49
50     private final Logger logger = LoggerFactory.getLogger(FSInternetRadioHandler.class);
51
52     FrontierSiliconRadio radio;
53     private final HttpClient httpClient;
54
55     /** Job that runs {@link #updateRunnable}. */
56     private ScheduledFuture<?> updateJob;
57
58     /** Runnable for job {@link #updateJob} for periodic refresh. */
59     private final Runnable updateRunnable = new Runnable() {
60         @Override
61         public void run() {
62             if (!radio.isLoggedIn()) {
63                 // radio is not set, so set all channels to 'undefined'
64                 for (Channel channel : getThing().getChannels()) {
65                     updateState(channel.getUID(), UnDefType.UNDEF);
66                 }
67                 // now let's silently check if it's back online
68                 radioLogin();
69                 return; // if login is successful, this method is called again :-)
70             }
71             try {
72                 final boolean radioOn = radio.getPower();
73                 for (Channel channel : getThing().getChannels()) {
74                     if (!radioOn && !CHANNEL_POWER.equals(channel.getUID().getId())) {
75                         // if radio is off, set all channels (except for 'POWER') to 'undefined'
76                         updateState(channel.getUID(), UnDefType.UNDEF);
77                     } else if (isLinked(channel.getUID().getId())) {
78                         // update all channels that are linked
79                         switch (channel.getUID().getId()) {
80                             case CHANNEL_POWER:
81                                 updateState(channel.getUID(), radioOn ? OnOffType.ON : OnOffType.OFF);
82                                 break;
83                             case CHANNEL_VOLUME_ABSOLUTE:
84                                 updateState(channel.getUID(),
85                                         DecimalType.valueOf(String.valueOf(radio.getVolumeAbsolute())));
86                                 break;
87                             case CHANNEL_VOLUME_PERCENT:
88                                 updateState(channel.getUID(),
89                                         PercentType.valueOf(String.valueOf(radio.getVolumePercent())));
90                                 break;
91                             case CHANNEL_MODE:
92                                 updateState(channel.getUID(), DecimalType.valueOf(String.valueOf(radio.getMode())));
93                                 break;
94                             case CHANNEL_MUTE:
95                                 updateState(channel.getUID(), radio.getMuted() ? OnOffType.ON : OnOffType.OFF);
96                                 break;
97                             case CHANNEL_PRESET:
98                                 // preset is write-only, ignore
99                                 break;
100                             case CHANNEL_PLAY_INFO_NAME:
101                                 updateState(channel.getUID(), StringType.valueOf(radio.getPlayInfoName()));
102                                 break;
103                             case CHANNEL_PLAY_INFO_TEXT:
104                                 updateState(channel.getUID(), StringType.valueOf(radio.getPlayInfoText()));
105                                 break;
106                             default:
107                                 logger.warn("Ignoring unknown channel during update: {}", channel.getLabel());
108                         }
109                     }
110                 }
111                 updateStatus(ThingStatus.ONLINE); // set it back online, maybe it was offline before
112             } catch (Exception e) {
113                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
114             }
115         }
116     };
117
118     public FSInternetRadioHandler(Thing thing, HttpClient httpClient) {
119         super(thing);
120         this.httpClient = httpClient;
121     }
122
123     @Override
124     public void initialize() {
125         // read configuration
126         final String ip = (String) getThing().getConfiguration().get(CONFIG_PROPERTY_IP);
127         final BigDecimal port = (BigDecimal) getThing().getConfiguration().get(CONFIG_PROPERTY_PORT);
128         final String pin = (String) getThing().getConfiguration().get(CONFIG_PROPERTY_PIN);
129
130         if (ip == null || pin == null || pin.isEmpty() || port.intValue() == 0) {
131             // configuration incomplete
132             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "Configuration incomplete");
133         } else {
134             radio = new FrontierSiliconRadio(ip, port.intValue(), pin, httpClient);
135             logger.debug("Initializing connection to {}:{}", ip, port);
136
137             // Long running initialization should be done asynchronously in background
138             radioLogin();
139
140             // also schedule a thread for polling with configured refresh rate
141             final BigDecimal period = (BigDecimal) getThing().getConfiguration().get(CONFIG_PROPERTY_REFRESH);
142             if (period != null && period.intValue() > 0) {
143                 updateJob = scheduler.scheduleWithFixedDelay(updateRunnable, period.intValue(), period.intValue(),
144                         SECONDS);
145             }
146         }
147     }
148
149     private void radioLogin() {
150         scheduler.execute(new Runnable() {
151             @Override
152             public void run() {
153                 try {
154                     if (radio.login()) {
155                         // Thing initialized. If done set status to ONLINE to indicate proper working.
156                         updateStatus(ThingStatus.ONLINE);
157
158                         // now update all channels!
159                         updateRunnable.run();
160                     }
161                 } catch (Exception e) {
162                     updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
163                 }
164             }
165         });
166     }
167
168     @Override
169     public void dispose() {
170         if (updateJob != null) {
171             updateJob.cancel(true);
172         }
173         updateJob = null;
174         radio = null;
175     }
176
177     @Override
178     public void handleCommand(final ChannelUID channelUID, final Command command) {
179         if (!radio.isLoggedIn()) {
180             // connection to radio is not initialized, log ignored command and set status, if it is not already offline
181             logger.debug("Ignoring command {} = {} because device is offline.", channelUID.getId(), command);
182             if (ThingStatus.ONLINE.equals(getThing().getStatus())) {
183                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR);
184             }
185             return;
186         }
187         try {
188             switch (channelUID.getId()) {
189                 case CHANNEL_POWER:
190                     if (OnOffType.ON.equals(command)) {
191                         radio.setPower(true);
192                     } else if (OnOffType.OFF.equals(command)) {
193                         radio.setPower(false);
194                     }
195                     // now all items should be updated! (wait some seconds so that text items are up-to-date)
196                     scheduler.schedule(updateRunnable, 4, SECONDS);
197                     break;
198                 case CHANNEL_VOLUME_PERCENT:
199                     if (IncreaseDecreaseType.INCREASE.equals(command) || UpDownType.UP.equals(command)) {
200                         radio.increaseVolumeAbsolute();
201                     } else if (IncreaseDecreaseType.DECREASE.equals(command) || UpDownType.DOWN.equals(command)) {
202                         radio.decreaseVolumeAbsolute();
203                     } else if (command instanceof PercentType percentCommand) {
204                         radio.setVolumePercent(percentCommand.intValue());
205                     }
206                     // absolute value should also be updated now, so let's update all items
207                     scheduler.schedule(updateRunnable, 1, SECONDS);
208                     break;
209                 case CHANNEL_VOLUME_ABSOLUTE:
210                     if (IncreaseDecreaseType.INCREASE.equals(command) || UpDownType.UP.equals(command)) {
211                         radio.increaseVolumeAbsolute();
212                     } else if (IncreaseDecreaseType.DECREASE.equals(command) || UpDownType.DOWN.equals(command)) {
213                         radio.decreaseVolumeAbsolute();
214                     } else if (command instanceof DecimalType decimalCommand) {
215                         radio.setVolumeAbsolute(decimalCommand.intValue());
216                     }
217                     // percent value should also be updated now, so let's update all items
218                     scheduler.schedule(updateRunnable, 1, SECONDS);
219                     break;
220                 case CHANNEL_MODE:
221                     if (command instanceof DecimalType decimalCommand) {
222                         radio.setMode(decimalCommand.intValue());
223                     }
224                     break;
225                 case CHANNEL_PRESET:
226                     if (command instanceof DecimalType decimalCommand) {
227                         radio.setPreset(decimalCommand.intValue());
228                     }
229                     break;
230                 case CHANNEL_MUTE:
231                     if (command instanceof OnOffType) {
232                         radio.setMuted(OnOffType.ON.equals(command));
233                     }
234                     break;
235                 default:
236                     logger.warn("Ignoring unknown command: {}", command);
237             }
238             // make sure that device state is online
239             updateStatus(ThingStatus.ONLINE);
240         } catch (Exception e) {
241             // set device state to offline
242             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
243         }
244     }
245 }