]> git.basschouten.com Git - openhab-addons.git/blob
d4d5f7996002e0ec64adca91e93d70c065bf08f8
[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.sonyprojector.internal.handler;
14
15 import static org.openhab.binding.sonyprojector.internal.SonyProjectorBindingConstants.*;
16
17 import java.util.concurrent.ScheduledFuture;
18 import java.util.concurrent.TimeUnit;
19
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.openhab.binding.sonyprojector.internal.SonyProjectorException;
23 import org.openhab.binding.sonyprojector.internal.SonyProjectorModel;
24 import org.openhab.binding.sonyprojector.internal.SonyProjectorStateDescriptionOptionProvider;
25 import org.openhab.binding.sonyprojector.internal.communication.SonyProjectorConnector;
26 import org.openhab.binding.sonyprojector.internal.communication.SonyProjectorStatusPower;
27 import org.openhab.binding.sonyprojector.internal.communication.sdcp.SonyProjectorSdcpConnector;
28 import org.openhab.binding.sonyprojector.internal.communication.sdcp.SonyProjectorSdcpSimuConnector;
29 import org.openhab.binding.sonyprojector.internal.communication.serial.SonyProjectorSerialConnector;
30 import org.openhab.binding.sonyprojector.internal.communication.serial.SonyProjectorSerialOverIpConnector;
31 import org.openhab.binding.sonyprojector.internal.communication.serial.SonyProjectorSerialSimuConnector;
32 import org.openhab.binding.sonyprojector.internal.configuration.SonyProjectorEthernetConfiguration;
33 import org.openhab.binding.sonyprojector.internal.configuration.SonyProjectorSerialConfiguration;
34 import org.openhab.binding.sonyprojector.internal.configuration.SonyProjectorSerialOverIpConfiguration;
35 import org.openhab.core.cache.ExpiringCacheMap;
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.PercentType;
40 import org.openhab.core.library.types.StringType;
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.binding.BaseThingHandler;
46 import org.openhab.core.types.Command;
47 import org.openhab.core.types.RefreshType;
48 import org.openhab.core.types.State;
49 import org.openhab.core.types.UnDefType;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53 /**
54  * The {@link SonyProjectorHandler} is responsible for handling commands, which are
55  * sent to one of the channels.
56  *
57  * @author Markus Wehrle - Initial contribution
58  * @author Laurent Garnier - Refactoring, poll thread for regular channels updates, new serial thing type, new channels
59  */
60 @NonNullByDefault
61 public class SonyProjectorHandler extends BaseThingHandler {
62
63     private final Logger logger = LoggerFactory.getLogger(SonyProjectorHandler.class);
64
65     private static final SonyProjectorModel DEFAULT_MODEL = SonyProjectorModel.VW520;
66     private static final long POLLING_INTERVAL = TimeUnit.SECONDS.toSeconds(15);
67
68     private @Nullable ScheduledFuture<?> refreshJob;
69
70     private boolean identifyProjector;
71     private SonyProjectorModel projectorModel = DEFAULT_MODEL;
72     private SonyProjectorConnector connector = new SonyProjectorSdcpSimuConnector(DEFAULT_MODEL);
73
74     private SonyProjectorStateDescriptionOptionProvider stateDescriptionProvider;
75     private SerialPortManager serialPortManager;
76
77     private boolean simu;
78
79     private final Object commandLock = new Object();
80
81     private final ExpiringCacheMap<String, State> cache;
82
83     public SonyProjectorHandler(Thing thing, SonyProjectorStateDescriptionOptionProvider stateDescriptionProvider,
84             SerialPortManager serialPortManager) {
85         super(thing);
86         this.stateDescriptionProvider = stateDescriptionProvider;
87         this.serialPortManager = serialPortManager;
88         this.cache = new ExpiringCacheMap<>(TimeUnit.SECONDS.toMillis(POLLING_INTERVAL));
89     }
90
91     @Override
92     public void handleCommand(ChannelUID channelUID, Command command) {
93         String channel = channelUID.getId();
94         if (command instanceof RefreshType) {
95             State state = cache.get(channel);
96             if (state != null) {
97                 updateState(channel, state);
98             }
99             return;
100         }
101
102         synchronized (commandLock) {
103             try {
104                 connector.open();
105             } catch (SonyProjectorException e) {
106                 logger.debug("Command {} from channel {} failed: {}", command, channel, e.getMessage());
107                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
108                 return;
109             }
110             try {
111                 switch (channel) {
112                     case CHANNEL_POWER:
113                         if (command == OnOffType.ON) {
114                             connector.powerOn();
115                         } else if (command == OnOffType.OFF) {
116                             connector.powerOff();
117                         } else {
118                             throw new SonyProjectorException("Invalid command value");
119                         }
120                         break;
121                     case CHANNEL_INPUT:
122                         connector.setInput(command.toString());
123                         break;
124                     case CHANNEL_CALIBRATION_PRESET:
125                         connector.setCalibrationPreset(command.toString());
126                         refreshChannel(CHANNEL_CONTRAST, true);
127                         refreshChannel(CHANNEL_BRIGHTNESS, true);
128                         refreshChannel(CHANNEL_COLOR, true);
129                         refreshChannel(CHANNEL_HUE, true);
130                         refreshChannel(CHANNEL_SHARPNESS, true);
131                         refreshChannel(CHANNEL_COLOR_TEMP, true);
132                         refreshChannel(CHANNEL_IRIS_MODE, true);
133                         refreshChannel(CHANNEL_IRIS_MANUAL, true);
134                         refreshChannel(CHANNEL_IRIS_SENSITIVITY, true);
135                         refreshChannel(CHANNEL_LAMP_CONTROL, true);
136                         refreshChannel(CHANNEL_FILM_PROJECTION, true);
137                         refreshChannel(CHANNEL_MOTION_ENHANCER, true);
138                         refreshChannel(CHANNEL_CONTRAST_ENHANCER, true);
139                         refreshChannel(CHANNEL_FILM_MODE, true);
140                         refreshChannel(CHANNEL_GAMMA_CORRECTION, true);
141                         refreshChannel(CHANNEL_COLOR_SPACE, true);
142                         refreshChannel(CHANNEL_NR, true);
143                         refreshChannel(CHANNEL_BLOCK_NR, true);
144                         refreshChannel(CHANNEL_MOSQUITO_NR, true);
145                         refreshChannel(CHANNEL_MPEG_NR, true);
146                         refreshChannel(CHANNEL_XVCOLOR, true);
147                         break;
148                     case CHANNEL_CONTRAST:
149                         if (command instanceof DecimalType) {
150                             connector.setContrast(((DecimalType) command).intValue());
151                         } else if (command instanceof PercentType) {
152                             connector.setContrast(((PercentType) command).intValue());
153                         } else {
154                             throw new SonyProjectorException("Invalid command value");
155                         }
156                         break;
157                     case CHANNEL_BRIGHTNESS:
158                         if (command instanceof DecimalType) {
159                             connector.setBrightness(((DecimalType) command).intValue());
160                         } else if (command instanceof PercentType) {
161                             connector.setBrightness(((PercentType) command).intValue());
162                         } else {
163                             throw new SonyProjectorException("Invalid command value");
164                         }
165                         break;
166                     case CHANNEL_COLOR:
167                         if (command instanceof DecimalType) {
168                             connector.setColor(((DecimalType) command).intValue());
169                         } else if (command instanceof PercentType) {
170                             connector.setColor(((PercentType) command).intValue());
171                         } else {
172                             throw new SonyProjectorException("Invalid command value");
173                         }
174                         break;
175                     case CHANNEL_HUE:
176                         if (command instanceof DecimalType) {
177                             connector.setHue(((DecimalType) command).intValue());
178                         } else if (command instanceof PercentType) {
179                             connector.setHue(((PercentType) command).intValue());
180                         } else {
181                             throw new SonyProjectorException("Invalid command value");
182                         }
183                         break;
184                     case CHANNEL_SHARPNESS:
185                         if (command instanceof DecimalType) {
186                             connector.setSharpness(((DecimalType) command).intValue());
187                         } else if (command instanceof PercentType) {
188                             connector.setSharpness(((PercentType) command).intValue());
189                         } else {
190                             throw new SonyProjectorException("Invalid command value");
191                         }
192                         break;
193                     case CHANNEL_COLOR_TEMP:
194                         connector.setColorTemperature(command.toString());
195                         break;
196                     case CHANNEL_IRIS_MODE:
197                         connector.setIrisMode(command.toString());
198                         refreshChannel(CHANNEL_IRIS_MANUAL, true);
199                         break;
200                     case CHANNEL_IRIS_MANUAL:
201                         if (command instanceof DecimalType) {
202                             connector.setIrisManual(((DecimalType) command).intValue());
203                         } else if (command instanceof PercentType) {
204                             connector.setIrisManual(((PercentType) command).intValue());
205                         } else {
206                             throw new SonyProjectorException("Invalid command value");
207                         }
208                         break;
209                     case CHANNEL_IRIS_SENSITIVITY:
210                         connector.setIrisSensitivity(command.toString());
211                         break;
212                     case CHANNEL_LAMP_CONTROL:
213                         connector.setLampControl(command.toString());
214                         break;
215                     case CHANNEL_FILM_PROJECTION:
216                         connector.setFilmProjection(command.toString());
217                         break;
218                     case CHANNEL_MOTION_ENHANCER:
219                         connector.setMotionEnhancer(command.toString());
220                         break;
221                     case CHANNEL_CONTRAST_ENHANCER:
222                         connector.setContrastEnhancer(command.toString());
223                         break;
224                     case CHANNEL_FILM_MODE:
225                         connector.setFilmMode(command.toString());
226                         break;
227                     case CHANNEL_GAMMA_CORRECTION:
228                         connector.setGammaCorrection(command.toString());
229                         break;
230                     case CHANNEL_COLOR_SPACE:
231                         connector.setColorSpace(command.toString());
232                         break;
233                     case CHANNEL_NR:
234                         connector.setNr(command.toString());
235                         break;
236                     case CHANNEL_BLOCK_NR:
237                         connector.setBlockNr(command.toString());
238                         break;
239                     case CHANNEL_MOSQUITO_NR:
240                         connector.setMosquitoNr(command.toString());
241                         break;
242                     case CHANNEL_MPEG_NR:
243                         connector.setMpegNr(command.toString());
244                         break;
245                     case CHANNEL_XVCOLOR:
246                         if (command == OnOffType.ON) {
247                             connector.enableXvColor();
248                             refreshChannel(CHANNEL_GAMMA_CORRECTION, true);
249                         } else if (command == OnOffType.OFF) {
250                             connector.disableXvColor();
251                             refreshChannel(CHANNEL_GAMMA_CORRECTION, true);
252                         } else {
253                             throw new SonyProjectorException("Invalid command value");
254                         }
255                         break;
256                     case CHANNEL_PICTURE_MUTING:
257                         if (command == OnOffType.ON) {
258                             connector.mutePicture();
259                         } else if (command == OnOffType.OFF) {
260                             connector.unmutePicture();
261                         } else {
262                             throw new SonyProjectorException("Invalid command value");
263                         }
264                         break;
265                     case CHANNEL_ASPECT:
266                         connector.setAspect(command.toString());
267                         break;
268                     case CHANNEL_OVERSCAN:
269                         if (command == OnOffType.ON) {
270                             connector.enableOverscan();
271                         } else if (command == OnOffType.OFF) {
272                             connector.disableOverscan();
273                         } else {
274                             throw new SonyProjectorException("Invalid command value");
275                         }
276                         break;
277                     case CHANNEL_PICTURE_POSITION:
278                         connector.setPicturePosition(command.toString());
279                         break;
280                     default:
281                         throw new SonyProjectorException("Unexpected command");
282                 }
283                 logger.debug("Command {} from channel {} succeeded", command, channel);
284             } catch (SonyProjectorException e) {
285                 logger.debug("Command {} from channel {} failed: {}", command, channel, e.getMessage());
286                 refreshChannel(channel, true);
287             }
288             connector.close();
289         }
290     }
291
292     @Override
293     public void initialize() {
294         logger.debug("Start initializing handler for thing {}", getThing().getUID());
295
296         boolean configOk = false;
297
298         if (getThing().getThingTypeUID().equals(THING_TYPE_ETHERNET)) {
299             SonyProjectorEthernetConfiguration config = getConfigAs(SonyProjectorEthernetConfiguration.class);
300             String configModel = config.model;
301             logger.debug("Ethernet config host {}", config.host);
302             logger.debug("Ethernet config port {}", config.port);
303             logger.debug("Ethernet config model {}", configModel);
304             logger.debug("Ethernet config community {}", config.community);
305             if (config.host == null || config.host.isEmpty()) {
306                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
307                         "@text/offline.config-error-unknown-host");
308             } else if (configModel == null || configModel.isEmpty()) {
309                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
310                         "@text/offline.config-error-unknown-model");
311             } else {
312                 configOk = true;
313
314                 connector = simu ? new SonyProjectorSdcpSimuConnector(DEFAULT_MODEL)
315                         : new SonyProjectorSdcpConnector(config.host, config.port, config.community, DEFAULT_MODEL);
316                 identifyProjector = "AUTO".equals(configModel);
317                 projectorModel = switchToModel("AUTO".equals(configModel) ? null : configModel, true);
318
319                 updateStatus(ThingStatus.UNKNOWN);
320             }
321         } else if (getThing().getThingTypeUID().equals(THING_TYPE_SERIAL)) {
322             SonyProjectorSerialConfiguration config = getConfigAs(SonyProjectorSerialConfiguration.class);
323             String configModel = config.model;
324             logger.debug("Serial config port {}", config.port);
325             logger.debug("Serial config model {}", configModel);
326             if (config.port == null || config.port.isEmpty()) {
327                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
328                         "@text/offline.config-error-unknown-port");
329             } else if (config.port.toLowerCase().startsWith("rfc2217")) {
330                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
331                         "@text/offline.config-error-invalid-thing-type");
332             } else if (configModel == null || configModel.isEmpty()) {
333                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
334                         "@text/offline.config-error-unknown-model");
335             } else {
336                 configOk = true;
337
338                 connector = simu ? new SonyProjectorSerialSimuConnector(serialPortManager, DEFAULT_MODEL)
339                         : new SonyProjectorSerialConnector(serialPortManager, config.port, DEFAULT_MODEL);
340                 identifyProjector = false;
341                 projectorModel = switchToModel(configModel, true);
342
343                 updateStatus(ThingStatus.UNKNOWN);
344             }
345         } else if (getThing().getThingTypeUID().equals(THING_TYPE_SERIAL_OVER_IP)) {
346             SonyProjectorSerialOverIpConfiguration config = getConfigAs(SonyProjectorSerialOverIpConfiguration.class);
347             String configModel = config.model;
348             logger.debug("Serial over IP config host {}", config.host);
349             logger.debug("Serial over IP config port {}", config.port);
350             logger.debug("Serial over IP config model {}", configModel);
351             if (config.host == null || config.host.isEmpty()) {
352                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
353                         "@text/offline.config-error-unknown-host");
354             } else if (config.port == null) {
355                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
356                         "@text/offline.config-error-unknown-port");
357             } else if (config.port <= 0) {
358                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
359                         "@text/offline.config-error-invalid-port");
360             } else if (configModel == null || configModel.isEmpty()) {
361                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.CONFIGURATION_ERROR,
362                         "@text/offline.config-error-unknown-model");
363             } else {
364                 configOk = true;
365
366                 connector = simu ? new SonyProjectorSerialSimuConnector(serialPortManager, DEFAULT_MODEL)
367                         : new SonyProjectorSerialOverIpConnector(serialPortManager, config.host, config.port,
368                                 DEFAULT_MODEL);
369                 identifyProjector = false;
370                 projectorModel = switchToModel(configModel, true);
371
372                 updateStatus(ThingStatus.UNKNOWN);
373             }
374         }
375
376         if (!configOk) {
377             connector = new SonyProjectorSdcpSimuConnector(DEFAULT_MODEL);
378         } else {
379             ScheduledFuture<?> refreshJob = this.refreshJob;
380             if (refreshJob == null || refreshJob.isCancelled()) {
381                 this.refreshJob = scheduler.scheduleWithFixedDelay(() -> {
382                     pollProjector();
383                 }, 1, POLLING_INTERVAL, TimeUnit.SECONDS);
384             }
385         }
386
387         logger.debug("Finished initializing!");
388     }
389
390     @Override
391     public void dispose() {
392         logger.debug("Disposing handler for thing {}", getThing().getUID());
393         ScheduledFuture<?> refreshJob = this.refreshJob;
394         if (refreshJob != null && !refreshJob.isCancelled()) {
395             refreshJob.cancel(true);
396             this.refreshJob = null;
397         }
398         connector.close();
399         super.dispose();
400     }
401
402     private void pollProjector() {
403         synchronized (commandLock) {
404             logger.debug("Polling the projector to refresh the channels...");
405
406             try {
407                 connector.open();
408             } catch (SonyProjectorException e) {
409                 logger.debug("Poll projector failed: {}", e.getMessage());
410                 updateStatus(ThingStatus.OFFLINE, ThingStatusDetail.COMMUNICATION_ERROR, e.getMessage());
411                 return;
412             }
413
414             boolean isOn = refreshPowerState();
415             refreshModel();
416             refreshChannel(CHANNEL_INPUT, isOn);
417             refreshChannel(CHANNEL_CALIBRATION_PRESET, isOn);
418             refreshChannel(CHANNEL_CONTRAST, isOn);
419             refreshChannel(CHANNEL_BRIGHTNESS, isOn);
420             refreshChannel(CHANNEL_COLOR, isOn);
421             refreshChannel(CHANNEL_HUE, isOn);
422             refreshChannel(CHANNEL_SHARPNESS, isOn);
423             refreshChannel(CHANNEL_COLOR_TEMP, isOn);
424             refreshChannel(CHANNEL_IRIS_MODE, isOn);
425             refreshChannel(CHANNEL_IRIS_MANUAL, isOn);
426             refreshChannel(CHANNEL_IRIS_SENSITIVITY, isOn);
427             refreshChannel(CHANNEL_LAMP_CONTROL, isOn);
428             refreshChannel(CHANNEL_FILM_PROJECTION, isOn);
429             refreshChannel(CHANNEL_MOTION_ENHANCER, isOn);
430             refreshChannel(CHANNEL_CONTRAST_ENHANCER, isOn);
431             refreshChannel(CHANNEL_FILM_MODE, isOn);
432             refreshChannel(CHANNEL_GAMMA_CORRECTION, isOn);
433             refreshChannel(CHANNEL_COLOR_SPACE, isOn);
434             refreshChannel(CHANNEL_NR, isOn);
435             refreshChannel(CHANNEL_BLOCK_NR, isOn);
436             refreshChannel(CHANNEL_MOSQUITO_NR, isOn);
437             refreshChannel(CHANNEL_MPEG_NR, isOn);
438             refreshChannel(CHANNEL_XVCOLOR, isOn);
439             refreshChannel(CHANNEL_PICTURE_MUTING, isOn);
440             refreshChannel(CHANNEL_ASPECT, isOn);
441             refreshChannel(CHANNEL_OVERSCAN, isOn);
442             refreshChannel(CHANNEL_PICTURE_POSITION, isOn);
443             refreshChannel(CHANNEL_LAMP_USE_TIME, isOn);
444
445             connector.close();
446
447             updateStatus(ThingStatus.ONLINE);
448
449             logger.debug("End of the polling thread");
450         }
451     }
452
453     private void refreshModel() {
454         if (identifyProjector && getThing().getThingTypeUID().equals(THING_TYPE_ETHERNET)) {
455             try {
456                 String modelName = ((SonyProjectorSdcpConnector) connector).getModelName();
457                 logger.debug("getModelName returned {}", modelName);
458                 identifyProjector = false;
459                 switchToModel(modelName, false);
460             } catch (SonyProjectorException e) {
461                 logger.debug("getModelName failed: {}", e.getMessage());
462             }
463         }
464     }
465
466     private SonyProjectorModel switchToModel(@Nullable String modelName, boolean force) {
467         SonyProjectorModel model = DEFAULT_MODEL;
468         if (modelName != null && !modelName.isEmpty()) {
469             try {
470                 model = SonyProjectorModel.getFromName(modelName, false);
471                 logger.debug("Model found: {}", model.getName());
472             } catch (SonyProjectorException e) {
473                 logger.info("Model {} is unknow; consider {} by default", modelName, DEFAULT_MODEL.getName());
474             }
475         }
476         if (force || !model.getName().equals(projectorModel.getName())) {
477             connector.setModel(model);
478             stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_INPUT),
479                     model.getInputStateOptions());
480             stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_CALIBRATION_PRESET),
481                     model.getCalibrPresetStateOptions());
482             stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_COLOR_TEMP),
483                     model.getColorTempStateOptions());
484             stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_IRIS_MODE),
485                     model.getIrisModeStateOptions());
486             stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_IRIS_SENSITIVITY),
487                     model.getIrisSensitivityStateOptions());
488             stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_LAMP_CONTROL),
489                     model.getLampControlStateOptions());
490             stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_FILM_PROJECTION),
491                     model.getFilmProjectionStateOptions());
492             stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_MOTION_ENHANCER),
493                     model.getMotionEnhancerStateOptions());
494             stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_CONTRAST_ENHANCER),
495                     model.getContrastEnhancerStateOptions());
496             stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_FILM_MODE),
497                     model.getFilmModeStateOptions());
498             stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_GAMMA_CORRECTION),
499                     model.getGammaCorrectionStateOptions());
500             stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_COLOR_SPACE),
501                     model.getColorSpaceStateOptions());
502             stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_NR),
503                     model.getNrStateOptions());
504             stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_BLOCK_NR),
505                     model.getBlockNrStateOptions());
506             stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_MOSQUITO_NR),
507                     model.getMosquitoNrStateOptions());
508             stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_MPEG_NR),
509                     model.getMpegNrStateOptions());
510             stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_ASPECT),
511                     model.getAspectStateOptions());
512             stateDescriptionProvider.setStateOptions(new ChannelUID(getThing().getUID(), CHANNEL_PICTURE_POSITION),
513                     model.getPicturePositionStateOptions());
514         }
515         return model;
516     }
517
518     private boolean refreshPowerState() {
519         boolean on = false;
520         State state = UnDefType.UNDEF;
521         try {
522             SonyProjectorStatusPower value = connector.getStatusPower();
523             logger.debug("Get Status Power returned {}", value);
524             on = value.isOn();
525             state = new StringType(value.name());
526         } catch (SonyProjectorException e) {
527             logger.debug("Get Status Power failed: {}", e.getMessage());
528         }
529         updateChannelStateAndCache(CHANNEL_POWER, on ? OnOffType.ON : OnOffType.OFF);
530         updateChannelStateAndCache(CHANNEL_POWERSTATE, state);
531         return on;
532     }
533
534     private @Nullable State requestProjectorValue(String channel, boolean requestValue) {
535         State state = null;
536         boolean precond;
537         switch (channel) {
538             case CHANNEL_POWER:
539             case CHANNEL_POWERSTATE:
540             case CHANNEL_INPUT:
541             case CHANNEL_CALIBRATION_PRESET:
542             case CHANNEL_CONTRAST:
543             case CHANNEL_BRIGHTNESS:
544             case CHANNEL_COLOR:
545             case CHANNEL_HUE:
546             case CHANNEL_SHARPNESS:
547             case CHANNEL_COLOR_TEMP:
548             case CHANNEL_CONTRAST_ENHANCER:
549             case CHANNEL_GAMMA_CORRECTION:
550             case CHANNEL_COLOR_SPACE:
551             case CHANNEL_NR:
552             case CHANNEL_PICTURE_MUTING:
553             case CHANNEL_ASPECT:
554                 precond = true;
555                 break;
556             case CHANNEL_IRIS_MODE:
557                 precond = projectorModel.isIrisModeAvailable();
558                 break;
559             case CHANNEL_IRIS_MANUAL:
560                 precond = projectorModel.isIrisManualAvailable();
561                 break;
562             case CHANNEL_IRIS_SENSITIVITY:
563                 precond = projectorModel.isIrisSensitivityAvailable();
564                 break;
565             case CHANNEL_LAMP_CONTROL:
566                 precond = projectorModel.isLampControlAvailable();
567                 break;
568             case CHANNEL_FILM_PROJECTION:
569                 precond = projectorModel.isFilmProjectionAvailable();
570                 break;
571             case CHANNEL_MOTION_ENHANCER:
572                 precond = projectorModel.isMotionEnhancerAvailable();
573                 break;
574             case CHANNEL_FILM_MODE:
575                 precond = projectorModel.isFilmModeAvailable();
576                 break;
577             case CHANNEL_BLOCK_NR:
578                 precond = projectorModel.isBlockNrAvailable();
579                 break;
580             case CHANNEL_MOSQUITO_NR:
581                 precond = projectorModel.isMosquitoNrAvailable();
582                 break;
583             case CHANNEL_MPEG_NR:
584                 precond = projectorModel.isMpegNrAvailable();
585                 break;
586             case CHANNEL_XVCOLOR:
587                 precond = projectorModel.isXvColorAvailable();
588                 break;
589             case CHANNEL_OVERSCAN:
590                 precond = projectorModel.isOverscanAvailable();
591                 break;
592             case CHANNEL_PICTURE_POSITION:
593                 precond = projectorModel.isPicturePositionAvailable();
594                 break;
595             case CHANNEL_LAMP_USE_TIME:
596                 precond = requestValue;
597                 break;
598             default:
599                 precond = false;
600                 break;
601         }
602         if (isLinked(channel) && precond) {
603             state = UnDefType.UNDEF;
604             if (requestValue) {
605                 try {
606                     switch (channel) {
607                         case CHANNEL_POWER:
608                             state = connector.getStatusPower().isOn() ? OnOffType.ON : OnOffType.OFF;
609                             break;
610                         case CHANNEL_POWERSTATE:
611                             state = new StringType(connector.getStatusPower().name());
612                             break;
613                         case CHANNEL_INPUT:
614                             state = new StringType(connector.getInput());
615                             break;
616                         case CHANNEL_CALIBRATION_PRESET:
617                             state = new StringType(connector.getCalibrationPreset());
618                             break;
619                         case CHANNEL_CONTRAST:
620                             state = new PercentType(connector.getContrast());
621                             break;
622                         case CHANNEL_BRIGHTNESS:
623                             state = new PercentType(connector.getBrightness());
624                             break;
625                         case CHANNEL_COLOR:
626                             state = new PercentType(connector.getColor());
627                             break;
628                         case CHANNEL_HUE:
629                             state = new PercentType(connector.getHue());
630                             break;
631                         case CHANNEL_SHARPNESS:
632                             state = new PercentType(connector.getSharpness());
633                             break;
634                         case CHANNEL_COLOR_TEMP:
635                             state = new StringType(connector.getColorTemperature());
636                             break;
637                         case CHANNEL_IRIS_MODE:
638                             state = new StringType(connector.getIrisMode());
639                             break;
640                         case CHANNEL_IRIS_MANUAL:
641                             state = new PercentType(connector.getIrisManual());
642                             break;
643                         case CHANNEL_IRIS_SENSITIVITY:
644                             state = new StringType(connector.getIrisSensitivity());
645                             break;
646                         case CHANNEL_LAMP_CONTROL:
647                             state = new StringType(connector.getLampControl());
648                             break;
649                         case CHANNEL_FILM_PROJECTION:
650                             state = new StringType(connector.getFilmProjection());
651                             break;
652                         case CHANNEL_MOTION_ENHANCER:
653                             state = new StringType(connector.getMotionEnhancer());
654                             break;
655                         case CHANNEL_CONTRAST_ENHANCER:
656                             state = new StringType(connector.getContrastEnhancer());
657                             break;
658                         case CHANNEL_FILM_MODE:
659                             state = new StringType(connector.getFilmMode());
660                             break;
661                         case CHANNEL_GAMMA_CORRECTION:
662                             state = new StringType(connector.getGammaCorrection());
663                             break;
664                         case CHANNEL_COLOR_SPACE:
665                             state = new StringType(connector.getColorSpace());
666                             break;
667                         case CHANNEL_NR:
668                             state = new StringType(connector.getNr());
669                             break;
670                         case CHANNEL_BLOCK_NR:
671                             state = new StringType(connector.getBlockNr());
672                             break;
673                         case CHANNEL_MOSQUITO_NR:
674                             state = new StringType(connector.getMosquitoNr());
675                             break;
676                         case CHANNEL_MPEG_NR:
677                             state = new StringType(connector.getMpegNr());
678                             break;
679                         case CHANNEL_XVCOLOR:
680                             state = connector.getXvColor();
681                             break;
682                         case CHANNEL_PICTURE_MUTING:
683                             state = connector.getPictureMuting();
684                             break;
685                         case CHANNEL_ASPECT:
686                             state = new StringType(connector.getAspect());
687                             break;
688                         case CHANNEL_OVERSCAN:
689                             state = connector.getOverscan();
690                             break;
691                         case CHANNEL_PICTURE_POSITION:
692                             state = new StringType(connector.getPicturePosition());
693                             break;
694                         case CHANNEL_LAMP_USE_TIME:
695                             state = new DecimalType(connector.getLampUseTime());
696                             break;
697                         default:
698                             break;
699                     }
700                     logger.debug("Refresh channel {} with value {}", channel, state);
701                 } catch (SonyProjectorException e) {
702                     logger.debug("Refresh channel {} failed: {}", channel, e.getMessage());
703                 }
704             }
705         }
706         return state;
707     }
708
709     private void refreshChannel(String channel, boolean requestValue) {
710         updateChannelStateAndCache(channel, requestProjectorValue(channel, requestValue));
711     }
712
713     private void updateChannelStateAndCache(String channel, @Nullable State state) {
714         if (state != null) {
715             updateState(channel, state);
716
717             if (!cache.containsKey(channel)) {
718                 cache.put(channel, () -> {
719                     synchronized (commandLock) {
720                         return requestProjectorValue(channel, true);
721                     }
722                 });
723             }
724             cache.putValue(channel, state);
725         }
726     }
727 }