]> git.basschouten.com Git - openhab-addons.git/blob
d5735fb0544373376188e7e70d5464a6be61ff0f
[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.enigma2.internal.handler;
14
15 import static org.openhab.binding.enigma2.internal.Enigma2BindingConstants.*;
16
17 import java.time.LocalDateTime;
18 import java.util.Collection;
19 import java.util.Optional;
20 import java.util.Set;
21 import java.util.concurrent.ScheduledFuture;
22 import java.util.concurrent.TimeUnit;
23 import java.util.stream.Collectors;
24 import java.util.stream.Stream;
25
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.openhab.binding.enigma2.internal.Enigma2Client;
29 import org.openhab.binding.enigma2.internal.Enigma2Configuration;
30 import org.openhab.binding.enigma2.internal.Enigma2RemoteKey;
31 import org.openhab.binding.enigma2.internal.actions.Enigma2Actions;
32 import org.openhab.core.library.types.DecimalType;
33 import org.openhab.core.library.types.NextPreviousType;
34 import org.openhab.core.library.types.OnOffType;
35 import org.openhab.core.library.types.PercentType;
36 import org.openhab.core.library.types.PlayPauseType;
37 import org.openhab.core.library.types.StringType;
38 import org.openhab.core.thing.ChannelUID;
39 import org.openhab.core.thing.Thing;
40 import org.openhab.core.thing.ThingStatus;
41 import org.openhab.core.thing.ThingStatusDetail;
42 import org.openhab.core.thing.binding.BaseThingHandler;
43 import org.openhab.core.thing.binding.ThingHandlerService;
44 import org.openhab.core.types.Command;
45 import org.openhab.core.types.RefreshType;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 /**
50  * The {@link Enigma2Handler} is responsible for handling commands, which are
51  * sent to one of the channels.
52  *
53  * @author Guido Dolfen - Initial contribution
54  */
55 @NonNullByDefault
56 public class Enigma2Handler extends BaseThingHandler {
57     private final Logger logger = LoggerFactory.getLogger(Enigma2Handler.class);
58     private Enigma2Configuration configuration = new Enigma2Configuration();
59     private Optional<Enigma2Client> enigma2Client = Optional.empty();
60     private @Nullable ScheduledFuture<?> refreshJob;
61     private LocalDateTime lastAnswerTime = LocalDateTime.now();
62
63     public Enigma2Handler(Thing thing) {
64         super(thing);
65     }
66
67     @Override
68     public void initialize() {
69         configuration = getConfigAs(Enigma2Configuration.class);
70         if (configuration.host.isEmpty()) {
71             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "host must not be empty");
72         } else if (configuration.timeout <= 0 || configuration.timeout > 300) {
73             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
74                     "timeout must be between 0 and 300 seconds");
75         } else if (configuration.refreshInterval <= 0 || configuration.refreshInterval > 3600) {
76             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
77                     "refreshInterval must be between 0 and 3600 seconds");
78         }
79         enigma2Client = Optional.of(new Enigma2Client(configuration.host, configuration.user, configuration.password,
80                 configuration.timeout));
81         refreshJob = scheduler.scheduleWithFixedDelay(this::refresh, 2, configuration.refreshInterval,
82                 TimeUnit.SECONDS);
83     }
84
85     private void refresh() {
86         getEnigma2Client().ifPresent(client -> {
87             boolean online = client.refresh();
88             if (online) {
89                 updateStatus(ThingStatus.ONLINE);
90                 updateState(CHANNEL_POWER, client.isPower() ? OnOffType.ON : OnOffType.OFF);
91                 updateState(CHANNEL_MUTE, client.isMute() ? OnOffType.ON : OnOffType.OFF);
92                 updateState(CHANNEL_VOLUME, new PercentType(client.getVolume()));
93                 updateState(CHANNEL_CHANNEL, new StringType(client.getChannel()));
94                 updateState(CHANNEL_TITLE, new StringType(client.getTitle()));
95                 updateState(CHANNEL_DESCRIPTION, new StringType(client.getDescription()));
96                 if (lastAnswerTime.isBefore(client.getLastAnswerTime())) {
97                     lastAnswerTime = client.getLastAnswerTime();
98                     updateState(CHANNEL_ANSWER, new StringType(client.getAnswer()));
99                 }
100             } else {
101                 updateStatus(ThingStatus.OFFLINE);
102             }
103         });
104     }
105
106     @Override
107     public void dispose() {
108         ScheduledFuture<?> job = this.refreshJob;
109         if (job != null) {
110             job.cancel(true);
111         }
112         this.refreshJob = null;
113     }
114
115     @Override
116     public void handleCommand(ChannelUID channelUID, Command command) {
117         logger.debug("handleCommand({},{})", channelUID, command);
118         getEnigma2Client().ifPresent(client -> {
119             switch (channelUID.getId()) {
120                 case CHANNEL_POWER:
121                     handlePower(channelUID, command, client);
122                     break;
123                 case CHANNEL_CHANNEL:
124                     handleChannel(channelUID, command, client);
125                     break;
126                 case CHANNEL_MEDIA_PLAYER:
127                     handleMediaPlayer(channelUID, command);
128                     break;
129                 case CHANNEL_MEDIA_STOP:
130                     handleMediaStop(channelUID, command);
131                     break;
132                 case CHANNEL_MUTE:
133                     handleMute(channelUID, command, client);
134                     break;
135                 case CHANNEL_VOLUME:
136                     handleVolume(channelUID, command, client);
137                     break;
138                 case CHANNEL_TITLE:
139                     handleTitle(channelUID, command, client);
140                     break;
141                 case CHANNEL_DESCRIPTION:
142                     handleDescription(channelUID, command, client);
143                     break;
144                 case CHANNEL_ANSWER:
145                     handleAnswer(channelUID, command, client);
146                     break;
147                 default:
148                     logger.debug("Channel {} is not supported", channelUID);
149                     break;
150             }
151         });
152     }
153
154     private void handleVolume(ChannelUID channelUID, Command command, Enigma2Client client) {
155         if (command instanceof RefreshType) {
156             client.refreshVolume();
157             updateState(channelUID, new PercentType(client.getVolume()));
158         } else if (command instanceof PercentType percentCommand) {
159             client.setVolume(percentCommand.intValue());
160         } else if (command instanceof DecimalType decimalCommand) {
161             client.setVolume(decimalCommand.intValue());
162         } else {
163             logger.info("Channel {} only accepts PercentType, DecimalType, RefreshType. Type was {}.", channelUID,
164                     command.getClass());
165         }
166     }
167
168     private void handleMute(ChannelUID channelUID, Command command, Enigma2Client client) {
169         if (command instanceof RefreshType) {
170             client.refreshVolume();
171             updateState(channelUID, client.isMute() ? OnOffType.ON : OnOffType.OFF);
172         } else if (OnOffType.ON.equals(command)) {
173             client.setMute(true);
174         } else if (OnOffType.OFF.equals(command)) {
175             client.setMute(false);
176         } else {
177             logger.info("Channel {} only accepts OnOffType, RefreshType. Type was {}.", channelUID, command.getClass());
178         }
179     }
180
181     private void handleAnswer(ChannelUID channelUID, Command command, Enigma2Client client) {
182         if (command instanceof RefreshType) {
183             client.refreshAnswer();
184             if (lastAnswerTime.isBefore(client.getLastAnswerTime())) {
185                 lastAnswerTime = client.getLastAnswerTime();
186                 updateState(channelUID, new StringType(client.getAnswer()));
187             }
188         } else {
189             logger.info("Channel {} only accepts RefreshType. Type was {}.", channelUID, command.getClass());
190         }
191     }
192
193     private void handleMediaStop(ChannelUID channelUID, Command command) {
194         if (command instanceof RefreshType) {
195             return;
196         } else if (command instanceof OnOffType) {
197             sendRcCommand(Enigma2RemoteKey.STOP);
198         } else {
199             logger.info("Channel {} only accepts OnOffType, RefreshType. Type was {}.", channelUID, command.getClass());
200         }
201     }
202
203     private void handleMediaPlayer(ChannelUID channelUID, Command command) {
204         if (RefreshType.REFRESH == command) {
205             return;
206         } else if (PlayPauseType.PLAY == command) {
207             sendRcCommand(Enigma2RemoteKey.PLAY);
208         } else if (PlayPauseType.PAUSE == command) {
209             sendRcCommand(Enigma2RemoteKey.PAUSE);
210         } else if (NextPreviousType.NEXT == command) {
211             sendRcCommand(Enigma2RemoteKey.FAST_FORWARD);
212         } else if (NextPreviousType.PREVIOUS == command) {
213             sendRcCommand(Enigma2RemoteKey.FAST_BACKWARD);
214         } else {
215             logger.info("Channel {} only accepts PlayPauseType, NextPreviousType, RefreshType. Type was {}.",
216                     channelUID, command.getClass());
217         }
218     }
219
220     private void handleChannel(ChannelUID channelUID, Command command, Enigma2Client client) {
221         if (command instanceof RefreshType) {
222             client.refreshChannel();
223             updateState(channelUID, new StringType(client.getChannel()));
224         } else if (command instanceof StringType) {
225             client.setChannel(command.toString());
226         } else {
227             logger.info("Channel {} only accepts StringType, RefreshType. Type was {}.", channelUID,
228                     command.getClass());
229         }
230     }
231
232     private void handleTitle(ChannelUID channelUID, Command command, Enigma2Client client) {
233         if (command instanceof RefreshType) {
234             client.refreshEpg();
235             updateState(channelUID, new StringType(client.getTitle()));
236         } else {
237             logger.info("Channel {} only accepts RefreshType. Type was {}.", channelUID, command.getClass());
238         }
239     }
240
241     private void handleDescription(ChannelUID channelUID, Command command, Enigma2Client client) {
242         if (command instanceof RefreshType) {
243             client.refreshEpg();
244             updateState(channelUID, new StringType(client.getDescription()));
245         } else {
246             logger.info("Channel {} only accepts RefreshType. Type was {}.", channelUID, command.getClass());
247         }
248     }
249
250     private void handlePower(ChannelUID channelUID, Command command, Enigma2Client client) {
251         if (RefreshType.REFRESH == command) {
252             client.refreshPower();
253             updateState(channelUID, client.isPower() ? OnOffType.ON : OnOffType.OFF);
254         } else if (OnOffType.ON == command) {
255             client.setPower(true);
256         } else if (OnOffType.OFF == command) {
257             client.setPower(false);
258         } else {
259             logger.info("Channel {} only accepts OnOffType, RefreshType. Type was {}.", channelUID, command.getClass());
260         }
261     }
262
263     public void sendRcCommand(String rcButton) {
264         logger.debug("sendRcCommand({})", rcButton);
265         try {
266             Enigma2RemoteKey remoteKey = Enigma2RemoteKey.valueOf(rcButton);
267             sendRcCommand(remoteKey);
268         } catch (IllegalArgumentException ex) {
269             logger.warn("{} is not a valid value for button - available are: {}", rcButton,
270                     Stream.of(Enigma2RemoteKey.values()).map(b -> b.name()).collect(Collectors.joining(", ")));
271         }
272     }
273
274     private void sendRcCommand(Enigma2RemoteKey remoteKey) {
275         getEnigma2Client().ifPresent(client -> client.sendRcCommand(remoteKey.getValue()));
276     }
277
278     public void sendInfo(int timeout, String text) {
279         getEnigma2Client().ifPresent(client -> client.sendInfo(timeout, text));
280     }
281
282     public void sendWarning(int timeout, String text) {
283         getEnigma2Client().ifPresent(client -> client.sendWarning(timeout, text));
284     }
285
286     public void sendError(int timeout, String text) {
287         getEnigma2Client().ifPresent(client -> client.sendError(timeout, text));
288     }
289
290     public void sendQuestion(int timeout, String text) {
291         getEnigma2Client().ifPresent(client -> client.sendQuestion(timeout, text));
292     }
293
294     @Override
295     public Collection<Class<? extends ThingHandlerService>> getServices() {
296         return Set.of(Enigma2Actions.class);
297     }
298
299     /**
300      * Getter for Test-Injection
301      *
302      * @return Enigma2Client.
303      */
304     Optional<Enigma2Client> getEnigma2Client() {
305         return enigma2Client;
306     }
307 }