]> git.basschouten.com Git - openhab-addons.git/blob
2d8cc2ca414972d4ae5057777d93c954ff7397f5
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 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.epsonprojector.internal.handler;
14
15 import static org.openhab.binding.epsonprojector.internal.EpsonProjectorBindingConstants.*;
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.epsonprojector.internal.EpsonProjectorCommandException;
25 import org.openhab.binding.epsonprojector.internal.EpsonProjectorCommandType;
26 import org.openhab.binding.epsonprojector.internal.EpsonProjectorDevice;
27 import org.openhab.binding.epsonprojector.internal.EpsonProjectorException;
28 import org.openhab.binding.epsonprojector.internal.configuration.EpsonProjectorConfiguration;
29 import org.openhab.binding.epsonprojector.internal.enums.AspectRatio;
30 import org.openhab.binding.epsonprojector.internal.enums.Background;
31 import org.openhab.binding.epsonprojector.internal.enums.ColorMode;
32 import org.openhab.binding.epsonprojector.internal.enums.Gamma;
33 import org.openhab.binding.epsonprojector.internal.enums.Luminance;
34 import org.openhab.binding.epsonprojector.internal.enums.PowerStatus;
35 import org.openhab.binding.epsonprojector.internal.enums.Switch;
36 import org.openhab.core.io.transport.serial.SerialPortManager;
37 import org.openhab.core.library.types.DecimalType;
38 import org.openhab.core.library.types.OnOffType;
39 import org.openhab.core.library.types.StringType;
40 import org.openhab.core.thing.Channel;
41 import org.openhab.core.thing.ChannelUID;
42 import org.openhab.core.thing.Thing;
43 import org.openhab.core.thing.ThingStatus;
44 import org.openhab.core.thing.ThingStatusDetail;
45 import org.openhab.core.thing.ThingTypeUID;
46 import org.openhab.core.thing.binding.BaseThingHandler;
47 import org.openhab.core.types.Command;
48 import org.openhab.core.types.RefreshType;
49 import org.openhab.core.types.State;
50 import org.openhab.core.types.UnDefType;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54 /**
55  * The {@link EpsonProjectorHandler} is responsible for handling commands, which are
56  * sent to one of the channels.
57  *
58  * @author Pauli Anttila, Yannick Schaus - Initial contribution
59  * @author Michael Lobstein - Improvements for OH3
60  */
61 @NonNullByDefault
62 public class EpsonProjectorHandler extends BaseThingHandler {
63     private static final int DEFAULT_POLLING_INTERVAL_SEC = 10;
64
65     private final Logger logger = LoggerFactory.getLogger(EpsonProjectorHandler.class);
66     private final SerialPortManager serialPortManager;
67
68     private @Nullable ScheduledFuture<?> pollingJob;
69     private Optional<EpsonProjectorDevice> device = Optional.empty();
70
71     private boolean isPowerOn = false;
72     private int pollingInterval = DEFAULT_POLLING_INTERVAL_SEC;
73
74     public EpsonProjectorHandler(Thing thing, SerialPortManager serialPortManager) {
75         super(thing);
76         this.serialPortManager = serialPortManager;
77     }
78
79     @Override
80     public void handleCommand(ChannelUID channelUID, Command command) {
81         String channelId = channelUID.getId();
82         if (command instanceof RefreshType) {
83             Channel channel = this.thing.getChannel(channelUID);
84             if (channel != null) {
85                 updateChannelState(channel);
86             }
87         } else {
88             EpsonProjectorCommandType epsonCommand = EpsonProjectorCommandType.getCommandType(channelId);
89             sendDataToDevice(epsonCommand, command);
90         }
91     }
92
93     @Override
94     public void initialize() {
95         EpsonProjectorConfiguration config = getConfigAs(EpsonProjectorConfiguration.class);
96         ThingTypeUID thingTypeUID = thing.getThingTypeUID();
97
98         if (THING_TYPE_PROJECTOR_SERIAL.equals(thingTypeUID)) {
99             device = Optional.of(new EpsonProjectorDevice(serialPortManager, config));
100         } else if (THING_TYPE_PROJECTOR_TCP.equals(thingTypeUID)) {
101             device = Optional.of(new EpsonProjectorDevice(config));
102         } else {
103             updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR);
104         }
105
106         pollingInterval = config.pollingInterval;
107         device.ifPresent(dev -> dev.setScheduler(scheduler));
108         updateStatus(ThingStatus.UNKNOWN);
109         schedulePollingJob();
110     }
111
112     /**
113      * Schedule the polling job
114      */
115     private void schedulePollingJob() {
116         cancelPollingJob();
117
118         pollingJob = scheduler.scheduleWithFixedDelay(() -> {
119             List<Channel> channels = this.thing.getChannels();
120             for (Channel channel : channels) {
121                 // only query power & lamp time when projector is off
122                 if (isPowerOn || (channel.getUID().getId().equals(CHANNEL_TYPE_POWER)
123                         || channel.getUID().getId().equals(CHANNEL_TYPE_LAMPTIME))) {
124                     updateChannelState(channel);
125                 }
126             }
127         }, 0, (pollingInterval > 0) ? pollingInterval : DEFAULT_POLLING_INTERVAL_SEC, TimeUnit.SECONDS);
128     }
129
130     /**
131      * Cancel the polling job
132      */
133     private void cancelPollingJob() {
134         ScheduledFuture<?> pollingJob = this.pollingJob;
135         if (pollingJob != null) {
136             pollingJob.cancel(true);
137             this.pollingJob = null;
138         }
139     }
140
141     @Override
142     public void dispose() {
143         cancelPollingJob();
144         closeConnection();
145         super.dispose();
146     }
147
148     private void updateChannelState(Channel channel) {
149         try {
150             if (!isLinked(channel.getUID()) && !channel.getUID().getId().equals(CHANNEL_TYPE_POWER)) {
151                 return;
152             }
153
154             EpsonProjectorCommandType epsonCommand = EpsonProjectorCommandType.getCommandType(channel.getUID().getId());
155
156             State state = queryDataFromDevice(epsonCommand);
157
158             if (state != null) {
159                 if (isLinked(channel.getUID())) {
160                     updateState(channel.getUID(), state);
161                 }
162                 // the first valid response will cause the thing to go ONLINE
163                 if (state != UnDefType.UNDEF) {
164                     updateStatus(ThingStatus.ONLINE);
165                 }
166             }
167         } catch (IllegalArgumentException e) {
168             logger.warn("Unknown channel {}", channel.getUID().getId());
169         }
170     }
171
172     @Nullable
173     private State queryDataFromDevice(EpsonProjectorCommandType commandType) {
174         EpsonProjectorDevice remoteController = device.get();
175
176         try {
177             if (!remoteController.isConnected()) {
178                 remoteController.connect();
179             }
180
181             if (!remoteController.isReady()) {
182                 logger.debug("Refusing command {} while not ready", commandType.toString());
183                 return null;
184             }
185
186             switch (commandType) {
187                 case AKEYSTONE:
188                     Switch autoKeystone = remoteController.getAutoKeystone();
189                     return autoKeystone == Switch.ON ? OnOffType.ON : OnOffType.OFF;
190                 case ASPECT_RATIO:
191                     AspectRatio aspectRatio = remoteController.getAspectRatio();
192                     return new StringType(aspectRatio.toString());
193                 case BACKGROUND:
194                     Background background = remoteController.getBackground();
195                     return new StringType(background.toString());
196                 case BRIGHTNESS:
197                     int brightness = remoteController.getBrightness();
198                     return new DecimalType(brightness);
199                 case COLOR_MODE:
200                     ColorMode colorMode = remoteController.getColorMode();
201                     return new StringType(colorMode.toString());
202                 case COLOR_TEMP:
203                     int ctemp = remoteController.getColorTemperature();
204                     return new DecimalType(ctemp);
205                 case CONTRAST:
206                     int contrast = remoteController.getContrast();
207                     return new DecimalType(contrast);
208                 case DENSITY:
209                     int density = remoteController.getDensity();
210                     return new DecimalType(density);
211                 case ERR_CODE:
212                     int err = remoteController.getError();
213                     return new DecimalType(err);
214                 case ERR_MESSAGE:
215                     String errString = remoteController.getErrorString();
216                     return new StringType(errString);
217                 case FLESH_TEMP:
218                     int fleshColor = remoteController.getFleshColor();
219                     return new DecimalType(fleshColor);
220                 case FREEZE:
221                     Switch freeze = remoteController.getFreeze();
222                     return freeze == Switch.ON ? OnOffType.ON : OnOffType.OFF;
223                 case GAMMA:
224                     Gamma gamma = remoteController.getGamma();
225                     return new StringType(gamma.toString());
226                 case HKEYSTONE:
227                     int hKeystone = remoteController.getHorizontalKeystone();
228                     return new DecimalType(hKeystone);
229                 case HPOSITION:
230                     int hPosition = remoteController.getHorizontalPosition();
231                     return new DecimalType(hPosition);
232                 case HREVERSE:
233                     Switch hReverse = remoteController.getHorizontalReverse();
234                     return hReverse == Switch.ON ? OnOffType.ON : OnOffType.OFF;
235                 case KEY_CODE:
236                     break;
237                 case LAMP_TIME:
238                     int lampTime = remoteController.getLampTime();
239                     return new DecimalType(lampTime);
240                 case LUMINANCE:
241                     Luminance luminance = remoteController.getLuminance();
242                     return new StringType(luminance.toString());
243                 case MUTE:
244                     Switch mute = remoteController.getMute();
245                     return mute == Switch.ON ? OnOffType.ON : OnOffType.OFF;
246                 case POWER:
247                     PowerStatus powerStatus = remoteController.getPowerStatus();
248                     if (isLinked(CHANNEL_TYPE_POWERSTATE)) {
249                         updateState(CHANNEL_TYPE_POWERSTATE, new StringType(powerStatus.toString()));
250                     }
251
252                     if (powerStatus == PowerStatus.ON || powerStatus == PowerStatus.WARMUP) {
253                         isPowerOn = true;
254                         return OnOffType.ON;
255                     } else {
256                         isPowerOn = false;
257                         return OnOffType.OFF;
258                     }
259                 case POWER_STATE:
260                     return null;
261                 case SOURCE:
262                     return new StringType(remoteController.getSource());
263                 case TINT:
264                     int tint = remoteController.getTint();
265                     return new DecimalType(tint);
266                 case VKEYSTONE:
267                     int vKeystone = remoteController.getVerticalKeystone();
268                     return new DecimalType(vKeystone);
269                 case VOLUME:
270                     int volume = remoteController.getVolume();
271                     return new DecimalType(volume);
272                 case VPOSITION:
273                     int vPosition = remoteController.getVerticalPosition();
274                     return new DecimalType(vPosition);
275                 case VREVERSE:
276                     Switch vReverse = remoteController.getVerticalReverse();
277                     return vReverse == Switch.ON ? OnOffType.ON : OnOffType.OFF;
278                 default:
279                     logger.warn("Unknown '{}' command!", commandType);
280                     return UnDefType.UNDEF;
281             }
282         } catch (EpsonProjectorCommandException e) {
283             logger.debug("Error executing command '{}', {}", commandType, e.getMessage());
284             return UnDefType.UNDEF;
285         } catch (EpsonProjectorException e) {
286             logger.debug("Couldn't execute command '{}', {}", commandType, e.getMessage());
287             closeConnection();
288             return null;
289         }
290
291         return UnDefType.UNDEF;
292     }
293
294     private void sendDataToDevice(EpsonProjectorCommandType commandType, Command command) {
295         EpsonProjectorDevice remoteController = device.get();
296
297         try {
298             if (!remoteController.isConnected()) {
299                 remoteController.connect();
300             }
301
302             if (!remoteController.isReady()) {
303                 logger.debug("Refusing command '{}' while not ready", commandType.toString());
304                 return;
305             }
306
307             switch (commandType) {
308                 case AKEYSTONE:
309                     remoteController.setAutoKeystone((command == OnOffType.ON ? Switch.ON : Switch.OFF));
310                     break;
311                 case ASPECT_RATIO:
312                     remoteController.setAspectRatio(AspectRatio.valueOf(command.toString()));
313                     break;
314                 case BACKGROUND:
315                     remoteController.setBackground(Background.valueOf(command.toString()));
316                     break;
317                 case BRIGHTNESS:
318                     remoteController.setBrightness(((DecimalType) command).intValue());
319                     break;
320                 case COLOR_MODE:
321                     remoteController.setColorMode(ColorMode.valueOf(command.toString()));
322                     break;
323                 case COLOR_TEMP:
324                     remoteController.setColorTemperature(((DecimalType) command).intValue());
325                     break;
326                 case CONTRAST:
327                     remoteController.setContrast(((DecimalType) command).intValue());
328                     break;
329                 case DENSITY:
330                     remoteController.setDensity(((DecimalType) command).intValue());
331                     break;
332                 case ERR_CODE:
333                     logger.warn("'{}' is read only parameter", commandType);
334                     break;
335                 case ERR_MESSAGE:
336                     logger.warn("'{}' is read only parameter", commandType);
337                     break;
338                 case FLESH_TEMP:
339                     remoteController.setFleshColor(((DecimalType) command).intValue());
340                     break;
341                 case FREEZE:
342                     remoteController.setFreeze(command == OnOffType.ON ? Switch.ON : Switch.OFF);
343                     break;
344                 case GAMMA:
345                     remoteController.setGamma(Gamma.valueOf(command.toString()));
346                     break;
347                 case HKEYSTONE:
348                     remoteController.setHorizontalKeystone(((DecimalType) command).intValue());
349                     break;
350                 case HPOSITION:
351                     remoteController.setHorizontalPosition(((DecimalType) command).intValue());
352                     break;
353                 case HREVERSE:
354                     remoteController.setHorizontalReverse((command == OnOffType.ON ? Switch.ON : Switch.OFF));
355                     break;
356                 case KEY_CODE:
357                     remoteController.sendKeyCode(command.toString());
358                     break;
359                 case LAMP_TIME:
360                     logger.warn("'{}' is read only parameter", commandType);
361                     break;
362                 case LUMINANCE:
363                     remoteController.setLuminance(Luminance.valueOf(command.toString()));
364                     break;
365                 case MUTE:
366                     remoteController.setMute((command == OnOffType.ON ? Switch.ON : Switch.OFF));
367                     break;
368                 case POWER:
369                     if (command == OnOffType.ON) {
370                         remoteController.setPower(Switch.ON);
371                         isPowerOn = true;
372                     } else {
373                         remoteController.setPower(Switch.OFF);
374                         isPowerOn = false;
375                     }
376                     break;
377                 case POWER_STATE:
378                     logger.warn("'{}' is read only parameter", commandType);
379                     break;
380                 case SOURCE:
381                     remoteController.setSource(command.toString());
382                     break;
383                 case TINT:
384                     remoteController.setTint(((DecimalType) command).intValue());
385                     break;
386                 case VKEYSTONE:
387                     remoteController.setVerticalKeystone(((DecimalType) command).intValue());
388                     break;
389                 case VOLUME:
390                     remoteController.setVolume(((DecimalType) command).intValue());
391                     break;
392                 case VPOSITION:
393                     remoteController.setVerticalPosition(((DecimalType) command).intValue());
394                     break;
395                 case VREVERSE:
396                     remoteController.setVerticalReverse((command == OnOffType.ON ? Switch.ON : Switch.OFF));
397                     break;
398                 default:
399                     logger.warn("Unknown '{}' command!", commandType);
400                     break;
401             }
402         } catch (EpsonProjectorCommandException e) {
403             logger.debug("Error executing command '{}', {}", commandType, e.getMessage());
404         } catch (EpsonProjectorException e) {
405             logger.warn("Couldn't execute command '{}', {}", commandType, e.getMessage());
406             closeConnection();
407         }
408     }
409
410     private void closeConnection() {
411         EpsonProjectorDevice remoteController = device.get();
412         try {
413             logger.debug("Closing connection to device '{}'", this.thing.getUID());
414             remoteController.disconnect();
415             updateStatus(ThingStatus.OFFLINE);
416         } catch (EpsonProjectorException e) {
417             logger.debug("Error occurred when closing connection to device '{}'", this.thing.getUID(), e);
418         }
419     }
420 }