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