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