2 * Copyright (c) 2010-2023 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.benqprojector.internal.handler;
15 import static org.openhab.binding.benqprojector.internal.BenqProjectorBindingConstants.*;
17 import java.util.List;
18 import java.util.Optional;
19 import java.util.concurrent.ScheduledFuture;
20 import java.util.concurrent.TimeUnit;
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24 import org.openhab.binding.benqprojector.internal.BenqProjectorCommandException;
25 import org.openhab.binding.benqprojector.internal.BenqProjectorCommandType;
26 import org.openhab.binding.benqprojector.internal.BenqProjectorDevice;
27 import org.openhab.binding.benqprojector.internal.BenqProjectorException;
28 import org.openhab.binding.benqprojector.internal.configuration.BenqProjectorConfiguration;
29 import org.openhab.binding.benqprojector.internal.enums.Switch;
30 import org.openhab.core.io.transport.serial.SerialPortManager;
31 import org.openhab.core.library.types.DecimalType;
32 import org.openhab.core.library.types.OnOffType;
33 import org.openhab.core.library.types.StringType;
34 import org.openhab.core.thing.Channel;
35 import org.openhab.core.thing.ChannelUID;
36 import org.openhab.core.thing.Thing;
37 import org.openhab.core.thing.ThingStatus;
38 import org.openhab.core.thing.ThingStatusDetail;
39 import org.openhab.core.thing.ThingTypeUID;
40 import org.openhab.core.thing.binding.BaseThingHandler;
41 import org.openhab.core.types.Command;
42 import org.openhab.core.types.RefreshType;
43 import org.openhab.core.types.State;
44 import org.openhab.core.types.UnDefType;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
49 * The {@link BenqProjectorHandler} is responsible for handling commands, which are
50 * sent to one of the channels.
52 * Based on 'epsonprojector' originally by Pauli Anttila & Yannick Schaus
54 * @author Michael Lobstein - Initial contribution
57 public class BenqProjectorHandler extends BaseThingHandler {
58 private static final int DEFAULT_POLLING_INTERVAL_SEC = 10;
60 private final Logger logger = LoggerFactory.getLogger(BenqProjectorHandler.class);
61 private final SerialPortManager serialPortManager;
63 private @Nullable ScheduledFuture<?> pollingJob;
64 private Optional<BenqProjectorDevice> device = Optional.empty();
66 private boolean isPowerOn = false;
67 private int pollingInterval = DEFAULT_POLLING_INTERVAL_SEC;
69 public BenqProjectorHandler(Thing thing, SerialPortManager serialPortManager) {
71 this.serialPortManager = serialPortManager;
75 public void handleCommand(ChannelUID channelUID, Command command) {
76 String channelId = channelUID.getId();
77 if (command instanceof RefreshType) {
78 Channel channel = this.thing.getChannel(channelUID);
79 if (channel != null && getThing().getStatus() == ThingStatus.ONLINE) {
80 updateChannelState(channel);
83 BenqProjectorCommandType benqCommand = BenqProjectorCommandType.getCommandType(channelId);
84 sendDataToDevice(benqCommand, command);
89 public void initialize() {
90 BenqProjectorConfiguration config = getConfigAs(BenqProjectorConfiguration.class);
91 ThingTypeUID thingTypeUID = thing.getThingTypeUID();
93 if (THING_TYPE_PROJECTOR_SERIAL.equals(thingTypeUID)) {
94 device = Optional.of(new BenqProjectorDevice(serialPortManager, config));
95 } else if (THING_TYPE_PROJECTOR_TCP.equals(thingTypeUID)) {
96 device = Optional.of(new BenqProjectorDevice(config));
98 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
102 pollingInterval = config.pollingInterval;
103 updateStatus(ThingStatus.UNKNOWN);
104 schedulePollingJob();
108 * Schedule the polling job
110 private void schedulePollingJob() {
113 pollingJob = scheduler.scheduleWithFixedDelay(() -> {
114 List<Channel> channels = this.thing.getChannels();
115 for (Channel channel : channels) {
116 // only query power when projector is off
117 if (isPowerOn || channel.getUID().getId().equals(CHANNEL_TYPE_POWER)) {
118 updateChannelState(channel);
121 }, 0, (pollingInterval > 0) ? pollingInterval : DEFAULT_POLLING_INTERVAL_SEC, TimeUnit.SECONDS);
125 * Cancel the polling job
127 private void cancelPollingJob() {
128 ScheduledFuture<?> pollingJob = this.pollingJob;
129 if (pollingJob != null) {
130 pollingJob.cancel(true);
131 this.pollingJob = null;
136 public void dispose() {
142 private void updateChannelState(Channel channel) {
144 if (!isLinked(channel.getUID()) && !channel.getUID().getId().equals(CHANNEL_TYPE_POWER)) {
148 BenqProjectorCommandType benqCommand = BenqProjectorCommandType.getCommandType(channel.getUID().getId());
150 State state = queryDataFromDevice(benqCommand);
153 if (isLinked(channel.getUID())) {
154 updateState(channel.getUID(), state);
156 // the first valid response will cause the thing to go ONLINE
157 if (state != UnDefType.UNDEF) {
158 updateStatus(ThingStatus.ONLINE);
161 } catch (IllegalArgumentException e) {
162 logger.warn("Unknown channel {}, exception: {}", channel.getUID().getId(), e.getMessage());
167 private State queryDataFromDevice(BenqProjectorCommandType commandType) {
168 BenqProjectorDevice remoteController = device.get();
171 if (!remoteController.isConnected()) {
172 remoteController.connect();
175 switch (commandType) {
177 Switch powerStatus = remoteController.getPowerStatus();
178 if (powerStatus == Switch.ON) {
183 return OnOffType.OFF;
186 String source = remoteController.getSource();
187 if (source != null) {
188 return new StringType(source);
190 return UnDefType.UNDEF;
193 String picturemode = remoteController.getPictureMode();
194 if (picturemode != null) {
195 return new StringType(picturemode);
197 return UnDefType.UNDEF;
200 String aspectratio = remoteController.getAspectRatio();
201 if (aspectratio != null) {
202 return new StringType(aspectratio);
204 return UnDefType.UNDEF;
207 Switch freeze = remoteController.getFreeze();
208 return freeze == Switch.ON ? OnOffType.ON : OnOffType.OFF;
210 Switch blank = remoteController.getBlank();
211 return blank == Switch.ON ? OnOffType.ON : OnOffType.OFF;
215 int lampTime = remoteController.getLampTime();
216 return new DecimalType(lampTime);
218 logger.warn("Unknown '{}' command!", commandType);
219 return UnDefType.UNDEF;
221 } catch (BenqProjectorCommandException e) {
222 logger.debug("Error executing command '{}', {}", commandType, e.getMessage());
223 return UnDefType.UNDEF;
224 } catch (BenqProjectorException e) {
225 logger.debug("Couldn't execute command '{}', {}", commandType, e.getMessage());
230 return UnDefType.UNDEF;
233 private void sendDataToDevice(BenqProjectorCommandType commandType, Command command) {
234 BenqProjectorDevice remoteController = device.get();
237 if (!remoteController.isConnected()) {
238 remoteController.connect();
241 switch (commandType) {
243 if (command == OnOffType.ON) {
244 remoteController.setPower(Switch.ON);
247 remoteController.setPower(Switch.OFF);
252 remoteController.setSource(command.toString());
255 remoteController.setPictureMode(command.toString());
258 remoteController.setAspectRatio(command.toString());
261 remoteController.setFreeze(command == OnOffType.ON ? Switch.ON : Switch.OFF);
264 remoteController.setBlank(command == OnOffType.ON ? Switch.ON : Switch.OFF);
267 remoteController.sendDirectCommand(command.toString());
270 logger.warn("Unknown '{}' command!", commandType);
273 } catch (BenqProjectorCommandException e) {
274 logger.debug("Error executing command '{}', {}", commandType, e.getMessage());
275 } catch (BenqProjectorException e) {
276 logger.warn("Couldn't execute command '{}', {}", commandType, e.getMessage());
281 private void closeConnection() {
282 if (device.isPresent()) {
284 logger.debug("Closing connection to device '{}'", this.thing.getUID());
285 device.get().disconnect();
286 updateStatus(ThingStatus.OFFLINE);
287 } catch (BenqProjectorException e) {
288 logger.debug("Error occurred when closing connection to device '{}'", this.thing.getUID(), e);