]> git.basschouten.com Git - openhab-addons.git/blob
a8969ec4bec2a963ef69135527d0017196ca4a30
[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.benqprojector.internal.handler;
14
15 import static org.openhab.binding.benqprojector.internal.BenqProjectorBindingConstants.*;
16
17 import java.util.List;
18 import java.util.Optional;
19 import java.util.concurrent.ScheduledFuture;
20 import java.util.concurrent.TimeUnit;
21
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;
47
48 /**
49  * The {@link BenqProjectorHandler} is responsible for handling commands, which are
50  * sent to one of the channels.
51  *
52  * Based on 'epsonprojector' originally by Pauli Anttila & Yannick Schaus
53  *
54  * @author Michael Lobstein - Initial contribution
55  */
56 @NonNullByDefault
57 public class BenqProjectorHandler extends BaseThingHandler {
58     private static final int DEFAULT_POLLING_INTERVAL_SEC = 10;
59
60     private final Logger logger = LoggerFactory.getLogger(BenqProjectorHandler.class);
61     private final SerialPortManager serialPortManager;
62
63     private @Nullable ScheduledFuture<?> pollingJob;
64     private Optional<BenqProjectorDevice> device = Optional.empty();
65
66     private boolean isPowerOn = false;
67     private int pollingInterval = DEFAULT_POLLING_INTERVAL_SEC;
68
69     public BenqProjectorHandler(Thing thing, SerialPortManager serialPortManager) {
70         super(thing);
71         this.serialPortManager = serialPortManager;
72     }
73
74     @Override
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);
81             }
82         } else {
83             BenqProjectorCommandType benqCommand = BenqProjectorCommandType.getCommandType(channelId);
84             sendDataToDevice(benqCommand, command);
85         }
86     }
87
88     @Override
89     public void initialize() {
90         BenqProjectorConfiguration config = getConfigAs(BenqProjectorConfiguration.class);
91         ThingTypeUID thingTypeUID = thing.getThingTypeUID();
92
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));
97         } else {
98             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
99             return;
100         }
101
102         pollingInterval = config.pollingInterval;
103         updateStatus(ThingStatus.UNKNOWN);
104         schedulePollingJob();
105     }
106
107     /**
108      * Schedule the polling job
109      */
110     private void schedulePollingJob() {
111         cancelPollingJob();
112
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);
119                 }
120             }
121         }, 0, (pollingInterval > 0) ? pollingInterval : DEFAULT_POLLING_INTERVAL_SEC, TimeUnit.SECONDS);
122     }
123
124     /**
125      * Cancel the polling job
126      */
127     private void cancelPollingJob() {
128         ScheduledFuture<?> pollingJob = this.pollingJob;
129         if (pollingJob != null) {
130             pollingJob.cancel(true);
131             this.pollingJob = null;
132         }
133     }
134
135     @Override
136     public void dispose() {
137         cancelPollingJob();
138         closeConnection();
139         super.dispose();
140     }
141
142     private void updateChannelState(Channel channel) {
143         try {
144             if (!isLinked(channel.getUID()) && !channel.getUID().getId().equals(CHANNEL_TYPE_POWER)) {
145                 return;
146             }
147
148             BenqProjectorCommandType benqCommand = BenqProjectorCommandType.getCommandType(channel.getUID().getId());
149
150             State state = queryDataFromDevice(benqCommand);
151
152             if (state != null) {
153                 if (isLinked(channel.getUID())) {
154                     updateState(channel.getUID(), state);
155                 }
156                 // the first valid response will cause the thing to go ONLINE
157                 if (state != UnDefType.UNDEF) {
158                     updateStatus(ThingStatus.ONLINE);
159                 }
160             }
161         } catch (IllegalArgumentException e) {
162             logger.warn("Unknown channel {}, exception: {}", channel.getUID().getId(), e.getMessage());
163         }
164     }
165
166     @Nullable
167     private State queryDataFromDevice(BenqProjectorCommandType commandType) {
168         BenqProjectorDevice remoteController = device.get();
169
170         try {
171             if (!remoteController.isConnected()) {
172                 remoteController.connect();
173             }
174
175             switch (commandType) {
176                 case POWER:
177                     Switch powerStatus = remoteController.getPowerStatus();
178                     if (powerStatus == Switch.ON) {
179                         isPowerOn = true;
180                         return OnOffType.ON;
181                     } else {
182                         isPowerOn = false;
183                         return OnOffType.OFF;
184                     }
185                 case SOURCE:
186                     String source = remoteController.getSource();
187                     if (source != null) {
188                         return new StringType(source);
189                     } else {
190                         return UnDefType.UNDEF;
191                     }
192                 case PICTURE_MODE:
193                     String picturemode = remoteController.getPictureMode();
194                     if (picturemode != null) {
195                         return new StringType(picturemode);
196                     } else {
197                         return UnDefType.UNDEF;
198                     }
199                 case ASPECT_RATIO:
200                     String aspectratio = remoteController.getAspectRatio();
201                     if (aspectratio != null) {
202                         return new StringType(aspectratio);
203                     } else {
204                         return UnDefType.UNDEF;
205                     }
206                 case FREEZE:
207                     Switch freeze = remoteController.getFreeze();
208                     return freeze == Switch.ON ? OnOffType.ON : OnOffType.OFF;
209                 case BLANK:
210                     Switch blank = remoteController.getBlank();
211                     return blank == Switch.ON ? OnOffType.ON : OnOffType.OFF;
212                 case DIRECTCMD:
213                     break;
214                 case LAMP_TIME:
215                     int lampTime = remoteController.getLampTime();
216                     return new DecimalType(lampTime);
217                 default:
218                     logger.warn("Unknown '{}' command!", commandType);
219                     return UnDefType.UNDEF;
220             }
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());
226             closeConnection();
227             return null;
228         }
229
230         return UnDefType.UNDEF;
231     }
232
233     private void sendDataToDevice(BenqProjectorCommandType commandType, Command command) {
234         BenqProjectorDevice remoteController = device.get();
235
236         try {
237             if (!remoteController.isConnected()) {
238                 remoteController.connect();
239             }
240
241             switch (commandType) {
242                 case POWER:
243                     if (command == OnOffType.ON) {
244                         remoteController.setPower(Switch.ON);
245                         isPowerOn = true;
246                     } else {
247                         remoteController.setPower(Switch.OFF);
248                         isPowerOn = false;
249                     }
250                     break;
251                 case SOURCE:
252                     remoteController.setSource(command.toString());
253                     break;
254                 case PICTURE_MODE:
255                     remoteController.setPictureMode(command.toString());
256                     break;
257                 case ASPECT_RATIO:
258                     remoteController.setAspectRatio(command.toString());
259                     break;
260                 case FREEZE:
261                     remoteController.setFreeze(command == OnOffType.ON ? Switch.ON : Switch.OFF);
262                     break;
263                 case BLANK:
264                     remoteController.setBlank(command == OnOffType.ON ? Switch.ON : Switch.OFF);
265                     break;
266                 case DIRECTCMD:
267                     remoteController.sendDirectCommand(command.toString());
268                     break;
269                 default:
270                     logger.warn("Unknown '{}' command!", commandType);
271                     break;
272             }
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());
277             closeConnection();
278         }
279     }
280
281     private void closeConnection() {
282         if (device.isPresent()) {
283             try {
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);
289             }
290         }
291     }
292 }