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.enigma2.internal.handler;
15 import static org.openhab.binding.enigma2.internal.Enigma2BindingConstants.*;
17 import java.time.LocalDateTime;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.Optional;
21 import java.util.concurrent.ScheduledFuture;
22 import java.util.concurrent.TimeUnit;
23 import java.util.stream.Collectors;
24 import java.util.stream.Stream;
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.*;
33 import org.openhab.core.thing.ChannelUID;
34 import org.openhab.core.thing.Thing;
35 import org.openhab.core.thing.ThingStatus;
36 import org.openhab.core.thing.ThingStatusDetail;
37 import org.openhab.core.thing.binding.BaseThingHandler;
38 import org.openhab.core.thing.binding.ThingHandlerService;
39 import org.openhab.core.types.Command;
40 import org.openhab.core.types.RefreshType;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
45 * The {@link Enigma2Handler} is responsible for handling commands, which are
46 * sent to one of the channels.
48 * @author Guido Dolfen - Initial contribution
51 public class Enigma2Handler extends BaseThingHandler {
52 private final Logger logger = LoggerFactory.getLogger(Enigma2Handler.class);
53 private Enigma2Configuration configuration = new Enigma2Configuration();
54 private Optional<Enigma2Client> enigma2Client = Optional.empty();
55 private @Nullable ScheduledFuture<?> refreshJob;
56 private LocalDateTime lastAnswerTime = LocalDateTime.now();
58 public Enigma2Handler(Thing thing) {
63 public void initialize() {
64 configuration = getConfigAs(Enigma2Configuration.class);
65 if (configuration.host.isEmpty()) {
66 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR, "host must not be empty");
67 } else if (configuration.timeout <= 0 || configuration.timeout > 300) {
68 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
69 "timeout must be between 0 and 300 seconds");
70 } else if (configuration.refreshInterval <= 0 || configuration.refreshInterval > 3600) {
71 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
72 "refreshInterval must be between 0 and 3600 seconds");
74 enigma2Client = Optional.of(new Enigma2Client(configuration.host, configuration.user, configuration.password,
75 configuration.timeout));
76 refreshJob = scheduler.scheduleWithFixedDelay(this::refresh, 2, configuration.refreshInterval,
80 private void refresh() {
81 getEnigma2Client().ifPresent(client -> {
82 boolean online = client.refresh();
84 updateStatus(ThingStatus.ONLINE);
85 updateState(CHANNEL_POWER, client.isPower() ? OnOffType.ON : OnOffType.OFF);
86 updateState(CHANNEL_MUTE, client.isMute() ? OnOffType.ON : OnOffType.OFF);
87 updateState(CHANNEL_VOLUME, new PercentType(client.getVolume()));
88 updateState(CHANNEL_CHANNEL, new StringType(client.getChannel()));
89 updateState(CHANNEL_TITLE, new StringType(client.getTitle()));
90 updateState(CHANNEL_DESCRIPTION, new StringType(client.getDescription()));
91 if (lastAnswerTime.isBefore(client.getLastAnswerTime())) {
92 lastAnswerTime = client.getLastAnswerTime();
93 updateState(CHANNEL_ANSWER, new StringType(client.getAnswer()));
96 updateStatus(ThingStatus.OFFLINE);
102 public void dispose() {
103 ScheduledFuture<?> job = this.refreshJob;
107 this.refreshJob = null;
111 public void handleCommand(ChannelUID channelUID, Command command) {
112 logger.debug("handleCommand({},{})", channelUID, command);
113 getEnigma2Client().ifPresent(client -> {
114 switch (channelUID.getId()) {
116 handlePower(channelUID, command, client);
118 case CHANNEL_CHANNEL:
119 handleChannel(channelUID, command, client);
121 case CHANNEL_MEDIA_PLAYER:
122 handleMediaPlayer(channelUID, command);
124 case CHANNEL_MEDIA_STOP:
125 handleMediaStop(channelUID, command);
128 handleMute(channelUID, command, client);
131 handleVolume(channelUID, command, client);
134 handleTitle(channelUID, command, client);
136 case CHANNEL_DESCRIPTION:
137 handleDescription(channelUID, command, client);
140 handleAnswer(channelUID, command, client);
143 logger.debug("Channel {} is not supported", channelUID);
149 private void handleVolume(ChannelUID channelUID, Command command, Enigma2Client client) {
150 if (command instanceof RefreshType) {
151 client.refreshVolume();
152 updateState(channelUID, new PercentType(client.getVolume()));
153 } else if (command instanceof PercentType) {
154 client.setVolume(((PercentType) command).intValue());
155 } else if (command instanceof DecimalType) {
156 client.setVolume(((DecimalType) command).intValue());
158 logger.info("Channel {} only accepts PercentType, DecimalType, RefreshType. Type was {}.", channelUID,
163 private void handleMute(ChannelUID channelUID, Command command, Enigma2Client client) {
164 if (command instanceof RefreshType) {
165 client.refreshVolume();
166 updateState(channelUID, client.isMute() ? OnOffType.ON : OnOffType.OFF);
167 } else if (OnOffType.ON.equals(command)) {
168 client.setMute(true);
169 } else if (OnOffType.OFF.equals(command)) {
170 client.setMute(false);
172 logger.info("Channel {} only accepts OnOffType, RefreshType. Type was {}.", channelUID, command.getClass());
176 private void handleAnswer(ChannelUID channelUID, Command command, Enigma2Client client) {
177 if (command instanceof RefreshType) {
178 client.refreshAnswer();
179 if (lastAnswerTime.isBefore(client.getLastAnswerTime())) {
180 lastAnswerTime = client.getLastAnswerTime();
181 updateState(channelUID, new StringType(client.getAnswer()));
184 logger.info("Channel {} only accepts RefreshType. Type was {}.", channelUID, command.getClass());
188 private void handleMediaStop(ChannelUID channelUID, Command command) {
189 if (command instanceof RefreshType) {
191 } else if (command instanceof OnOffType) {
192 sendRcCommand(Enigma2RemoteKey.STOP);
194 logger.info("Channel {} only accepts OnOffType, RefreshType. Type was {}.", channelUID, command.getClass());
198 private void handleMediaPlayer(ChannelUID channelUID, Command command) {
199 if (RefreshType.REFRESH == command) {
201 } else if (PlayPauseType.PLAY == command) {
202 sendRcCommand(Enigma2RemoteKey.PLAY);
203 } else if (PlayPauseType.PAUSE == command) {
204 sendRcCommand(Enigma2RemoteKey.PAUSE);
205 } else if (NextPreviousType.NEXT == command) {
206 sendRcCommand(Enigma2RemoteKey.FAST_FORWARD);
207 } else if (NextPreviousType.PREVIOUS == command) {
208 sendRcCommand(Enigma2RemoteKey.FAST_BACKWARD);
210 logger.info("Channel {} only accepts PlayPauseType, NextPreviousType, RefreshType. Type was {}.",
211 channelUID, command.getClass());
215 private void handleChannel(ChannelUID channelUID, Command command, Enigma2Client client) {
216 if (command instanceof RefreshType) {
217 client.refreshChannel();
218 updateState(channelUID, new StringType(client.getChannel()));
219 } else if (command instanceof StringType) {
220 client.setChannel(command.toString());
222 logger.info("Channel {} only accepts StringType, RefreshType. Type was {}.", channelUID,
227 private void handleTitle(ChannelUID channelUID, Command command, Enigma2Client client) {
228 if (command instanceof RefreshType) {
230 updateState(channelUID, new StringType(client.getTitle()));
232 logger.info("Channel {} only accepts RefreshType. Type was {}.", channelUID, command.getClass());
236 private void handleDescription(ChannelUID channelUID, Command command, Enigma2Client client) {
237 if (command instanceof RefreshType) {
239 updateState(channelUID, new StringType(client.getDescription()));
241 logger.info("Channel {} only accepts RefreshType. Type was {}.", channelUID, command.getClass());
245 private void handlePower(ChannelUID channelUID, Command command, Enigma2Client client) {
246 if (RefreshType.REFRESH == command) {
247 client.refreshPower();
248 updateState(channelUID, client.isPower() ? OnOffType.ON : OnOffType.OFF);
249 } else if (OnOffType.ON == command) {
250 client.setPower(true);
251 } else if (OnOffType.OFF == command) {
252 client.setPower(false);
254 logger.info("Channel {} only accepts OnOffType, RefreshType. Type was {}.", channelUID, command.getClass());
258 public void sendRcCommand(String rcButton) {
259 logger.debug("sendRcCommand({})", rcButton);
261 Enigma2RemoteKey remoteKey = Enigma2RemoteKey.valueOf(rcButton);
262 sendRcCommand(remoteKey);
263 } catch (IllegalArgumentException ex) {
264 logger.warn("{} is not a valid value for button - available are: {}", rcButton,
265 Stream.of(Enigma2RemoteKey.values()).map(b -> b.name()).collect(Collectors.joining(", ")));
269 private void sendRcCommand(Enigma2RemoteKey remoteKey) {
270 getEnigma2Client().ifPresent(client -> client.sendRcCommand(remoteKey.getValue()));
273 public void sendInfo(int timeout, String text) {
274 getEnigma2Client().ifPresent(client -> client.sendInfo(timeout, text));
277 public void sendWarning(int timeout, String text) {
278 getEnigma2Client().ifPresent(client -> client.sendWarning(timeout, text));
281 public void sendError(int timeout, String text) {
282 getEnigma2Client().ifPresent(client -> client.sendError(timeout, text));
285 public void sendQuestion(int timeout, String text) {
286 getEnigma2Client().ifPresent(client -> client.sendQuestion(timeout, text));
290 public Collection<Class<? extends ThingHandlerService>> getServices() {
291 return Collections.singleton(Enigma2Actions.class);
295 * Getter for Test-Injection
297 * @return Enigma2Client.
299 Optional<Enigma2Client> getEnigma2Client() {
300 return enigma2Client;