]> git.basschouten.com Git - openhab-addons.git/blob
542164d15098fe5430e4881eb625c425c684b87f
[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.communication;
14
15 import java.io.IOException;
16 import java.io.InputStream;
17 import java.io.OutputStream;
18 import java.util.Arrays;
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.core.i18n.CommunicationException;
25 import org.openhab.core.i18n.ConnectionException;
26 import org.openhab.core.library.types.OnOffType;
27 import org.openhab.core.util.HexUtils;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 /**
32  * Class for communicating with Sony Projectors
33  *
34  * @author Markus Wehrle - Initial contribution
35  * @author Laurent Garnier - Refactoring to include new channels, consider serial connection and protocol depending on
36  *         model
37  * @author Laurent Garnier - Allow sending any IR command
38  */
39 @NonNullByDefault
40 public abstract class SonyProjectorConnector {
41
42     private final Logger logger = LoggerFactory.getLogger(SonyProjectorConnector.class);
43
44     private static final byte[] DUMMY_DATA = new byte[] { 0x00, 0x00 };
45     private static final byte[] POWER_ON = new byte[] { 0x00, 0x01 };
46     private static final byte[] POWER_OFF = new byte[] { 0x00, 0x00 };
47     private static final byte[] OVERSCAN_ON = new byte[] { 0x00, 0x01 };
48     private static final byte[] OVERSCAN_OFF = new byte[] { 0x00, 0x00 };
49     private static final byte[] PICTURE_ON = new byte[] { 0x00, 0x01 };
50     private static final byte[] PICTURE_OFF = new byte[] { 0x00, 0x00 };
51     private static final byte[] XVCOLOR_ON = new byte[] { 0x00, 0x01 };
52     private static final byte[] XVCOLOR_OFF = new byte[] { 0x00, 0x00 };
53
54     private SonyProjectorModel model;
55
56     /** The output stream */
57     protected @Nullable OutputStream dataOut;
58
59     /** The input stream */
60     protected @Nullable InputStream dataIn;
61
62     /** true if the connection is established, false if not */
63     protected boolean connected;
64
65     private boolean simu;
66
67     /**
68      * Constructor
69      *
70      * @param model the projector model in use
71      * @param simu whether the communication is simulated or real
72      */
73     public SonyProjectorConnector(SonyProjectorModel model, boolean simu) {
74         this.model = model;
75         this.simu = simu;
76     }
77
78     /**
79      * Set the projector model in use
80      *
81      * @param model the projector model in use
82      */
83     public void setModel(SonyProjectorModel model) {
84         this.model = model;
85     }
86
87     /**
88      * Request the projector to get the current power status
89      *
90      * @return the current power status
91      *
92      * @throws SonyProjectorException in case of any problem
93      */
94     public SonyProjectorStatusPower getStatusPower() throws SonyProjectorException {
95         return SonyProjectorStatusPower.getFromDataCode(getSetting(SonyProjectorItem.STATUS_POWER));
96     }
97
98     /**
99      * Power ON the projector
100      *
101      * @throws SonyProjectorException in case the projector is not ready for a power ON command or any other problem
102      */
103     public void powerOn() throws SonyProjectorException {
104         SonyProjectorStatusPower status = null;
105         try {
106             status = getStatusPower();
107         } catch (SonyProjectorException e) {
108         }
109         logger.debug("Current Power Status: {}", status == null ? "undefined" : status.toString());
110         if (status != null && status != SonyProjectorStatusPower.STANDBY) {
111             throw new SonyProjectorException("Projector not ready for command ON");
112         } else if (model.isPowerCmdAvailable()) {
113             logger.debug("Set Power ON using Power command");
114             setSetting(SonyProjectorItem.POWER, POWER_ON);
115         } else {
116             logger.debug("Set Power ON using IR Power command");
117             sendIR(SonyProjectorItem.POWER_ON);
118             if (status == null) {
119                 sendIR(SonyProjectorItem.POWER_ON);
120             }
121         }
122     }
123
124     /**
125      * Power OFF the projector
126      *
127      * @throws SonyProjectorException in case the projector is not ready for a power OFF command or any other problem
128      */
129     public void powerOff() throws SonyProjectorException {
130         SonyProjectorStatusPower status = null;
131         try {
132             status = getStatusPower();
133         } catch (SonyProjectorException e) {
134         }
135         logger.debug("Current Power Status: {}", status == null ? "undefined" : status.toString());
136         if (status == null || status != SonyProjectorStatusPower.POWER_ON) {
137             throw new SonyProjectorException("Projector not ready for command OFF");
138         } else if (model.isPowerCmdAvailable()) {
139             logger.debug("Set Power OFF using Power command");
140             setSetting(SonyProjectorItem.POWER, POWER_OFF);
141         } else {
142             logger.debug("Set Power OFF using IR Power command");
143             sendIR(SonyProjectorItem.POWER_OFF);
144         }
145     }
146
147     /**
148      * Request the projector to get the current calibration preset
149      *
150      * @return the current calibration preset
151      *
152      * @throws SonyProjectorException in case of any problem
153      */
154     public String getCalibrationPreset() throws SonyProjectorException {
155         return model.getCalibrPresetNameFromDataCode(getSetting(SonyProjectorItem.CALIBRATION_PRESET));
156     }
157
158     /**
159      * Request the projector to change the calibration preset
160      *
161      * @param value the calibration preset to set
162      *
163      * @throws SonyProjectorException in case of any problem
164      */
165     public void setCalibrationPreset(String value) throws SonyProjectorException {
166         setSetting(SonyProjectorItem.CALIBRATION_PRESET, model.getCalibrPresetFromName(value).getDataCode());
167     }
168
169     /**
170      * Request the projector to get the current video input
171      *
172      * @return the current video input
173      *
174      * @throws SonyProjectorException in case of any problem
175      */
176     public String getInput() throws SonyProjectorException {
177         return model.getInputNameFromDataCode(getSetting(SonyProjectorItem.INPUT));
178     }
179
180     /**
181      * Request the projector to change the video input
182      *
183      * @param value the video input to set
184      *
185      * @throws SonyProjectorException in case of any problem
186      */
187     public void setInput(String value) throws SonyProjectorException {
188         setSetting(SonyProjectorItem.INPUT, model.getInputFromName(value).getDataCode());
189     }
190
191     /**
192      * Request the projector to get the current contrast setting
193      *
194      * @return the current contrast value
195      *
196      * @throws SonyProjectorException in case of any problem
197      */
198     public int getContrast() throws SonyProjectorException {
199         return convertDataToInt(getSetting(SonyProjectorItem.CONTRAST));
200     }
201
202     /**
203      * Request the projector to change the contrast setting
204      *
205      * @param value the contrast value to set
206      *
207      * @throws SonyProjectorException in case of any problem
208      */
209     public void setContrast(int value) throws SonyProjectorException {
210         setSetting(SonyProjectorItem.CONTRAST, convertIntToData(value));
211     }
212
213     /**
214      * Request the projector to get the current brightness setting
215      *
216      * @return the current brightness value
217      *
218      * @throws SonyProjectorException in case of any problem
219      */
220     public int getBrightness() throws SonyProjectorException {
221         return convertDataToInt(getSetting(SonyProjectorItem.BRIGHTNESS));
222     }
223
224     /**
225      * Request the projector to change the brightness setting
226      *
227      * @param value the brightness value to set
228      *
229      * @throws SonyProjectorException in case of any problem
230      */
231     public void setBrightness(int value) throws SonyProjectorException {
232         setSetting(SonyProjectorItem.BRIGHTNESS, convertIntToData(value));
233     }
234
235     /**
236      * Request the projector to get the current color setting
237      *
238      * @return the current color value
239      *
240      * @throws SonyProjectorException in case of any problem
241      */
242     public int getColor() throws SonyProjectorException {
243         return convertDataToInt(getSetting(SonyProjectorItem.COLOR));
244     }
245
246     /**
247      * Request the projector to change the color setting
248      *
249      * @param value the color value to set
250      *
251      * @throws SonyProjectorException in case of any problem
252      */
253     public void setColor(int value) throws SonyProjectorException {
254         setSetting(SonyProjectorItem.COLOR, convertIntToData(value));
255     }
256
257     /**
258      * Request the projector to get the current hue setting
259      *
260      * @return the current hue value
261      *
262      * @throws SonyProjectorException in case of any problem
263      */
264     public int getHue() throws SonyProjectorException {
265         return convertDataToInt(getSetting(SonyProjectorItem.HUE));
266     }
267
268     /**
269      * Request the projector to change the hue setting
270      *
271      * @param value the hue value to set
272      *
273      * @throws SonyProjectorException in case of any problem
274      */
275     public void setHue(int value) throws SonyProjectorException {
276         setSetting(SonyProjectorItem.HUE, convertIntToData(value));
277     }
278
279     /**
280      * Request the projector to get the current sharpness setting
281      *
282      * @return the current sharpness value
283      *
284      * @throws SonyProjectorException in case of any problem
285      */
286     public int getSharpness() throws SonyProjectorException {
287         return convertDataToInt(getSetting(SonyProjectorItem.SHARPNESS));
288     }
289
290     /**
291      * Request the projector to change the sharpness setting
292      *
293      * @param value the sharpness value to set
294      *
295      * @throws SonyProjectorException in case of any problem
296      */
297     public void setSharpness(int value) throws SonyProjectorException {
298         setSetting(SonyProjectorItem.SHARPNESS, convertIntToData(value));
299     }
300
301     /**
302      * Request the projector to get the current contrast enhancer mode
303      *
304      * @return the current contrast enhancer mode
305      *
306      * @throws SonyProjectorException in case of any problem
307      */
308     public String getContrastEnhancer() throws SonyProjectorException {
309         return model.getContrastEnhancerNameFromDataCode(getSetting(SonyProjectorItem.CONTRAST_ENHANCER));
310     }
311
312     /**
313      * Request the projector to change the contrast enhancer mode
314      *
315      * @param value the contrast enhancer mode to set
316      *
317      * @throws SonyProjectorException in case of any problem
318      */
319     public void setContrastEnhancer(String value) throws SonyProjectorException {
320         setSetting(SonyProjectorItem.CONTRAST_ENHANCER, model.getContrastEnhancerDataCodeFromName(value));
321     }
322
323     /**
324      * Request the projector to get the current film mode
325      *
326      * @return the current film mode
327      *
328      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
329      */
330     public String getFilmMode() throws SonyProjectorException {
331         if (!model.isFilmModeAvailable()) {
332             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.FILM_MODE.getName()
333                     + " for projector model " + model.getName());
334         }
335         return model.getFilmModeNameFromDataCode(getSetting(SonyProjectorItem.FILM_MODE));
336     }
337
338     /**
339      * Request the projector to change the film mode
340      *
341      * @param value the film mode to set
342      *
343      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
344      */
345     public void setFilmMode(String value) throws SonyProjectorException {
346         if (!model.isFilmModeAvailable()) {
347             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.FILM_MODE.getName()
348                     + " for projector model " + model.getName());
349         }
350         setSetting(SonyProjectorItem.FILM_MODE, model.getFilmModeDataCodeFromName(value));
351     }
352
353     /**
354      * Request the projector to get the lamp use time
355      *
356      * @return the lamp use time
357      *
358      * @throws SonyProjectorException in case of any problem
359      */
360     public int getLampUseTime() throws SonyProjectorException {
361         return convertDataToInt(getSetting(SonyProjectorItem.LAMP_USE_TIME));
362     }
363
364     /**
365      * Request the projector to get the current mode for the lamp control setting
366      *
367      * @return the current mode for the lamp control setting
368      *
369      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
370      */
371     public String getLampControl() throws SonyProjectorException {
372         if (!model.isLampControlAvailable()) {
373             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.LAMP_CONTROL.getName()
374                     + " for projector model " + model.getName());
375         }
376         return SonyProjectorLampControl.getFromDataCode(getSetting(SonyProjectorItem.LAMP_CONTROL)).getName();
377     }
378
379     /**
380      * Request the projector to change the mode for the lamp control setting
381      *
382      * @param value the mode to set for the lamp control setting
383      *
384      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
385      */
386     public void setLampControl(String value) throws SonyProjectorException {
387         if (!model.isLampControlAvailable()) {
388             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.LAMP_CONTROL.getName()
389                     + " for projector model " + model.getName());
390         }
391         setSetting(SonyProjectorItem.LAMP_CONTROL, SonyProjectorLampControl.getFromName(value).getDataCode());
392     }
393
394     /**
395      * Request the projector if the picture is muted or not
396      *
397      * @return OnOffType.ON if the picture is muted, OnOffType.OFF if not
398      *
399      * @throws SonyProjectorException in case of any problem
400      */
401     public OnOffType getPictureMuting() throws SonyProjectorException {
402         return Arrays.equals(getSetting(SonyProjectorItem.PICTURE_MUTING), PICTURE_ON) ? OnOffType.ON : OnOffType.OFF;
403     }
404
405     /**
406      * Request the projector to mute the picture
407      *
408      * @throws SonyProjectorException in case of any problem
409      */
410     public void mutePicture() throws SonyProjectorException {
411         setSetting(SonyProjectorItem.PICTURE_MUTING, PICTURE_ON);
412     }
413
414     /**
415      * Request the projector to unmute the picture
416      *
417      * @throws SonyProjectorException in case of any problem
418      */
419     public void unmutePicture() throws SonyProjectorException {
420         setSetting(SonyProjectorItem.PICTURE_MUTING, PICTURE_OFF);
421     }
422
423     /**
424      * Request the projector to get the current mode for the picture position setting
425      *
426      * @return the current mode for the picture position setting
427      *
428      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
429      */
430     public String getPicturePosition() throws SonyProjectorException {
431         if (!model.isPicturePositionAvailable()) {
432             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.PICTURE_POSITION.getName()
433                     + " for projector model " + model.getName());
434         }
435         return model.getPicturePositionNameFromDataCode(getSetting(SonyProjectorItem.PICTURE_POSITION));
436     }
437
438     /**
439      * Request the projector to change the mode for the picture position setting
440      *
441      * @param value the mode to set for the picture position setting
442      *
443      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
444      */
445     public void setPicturePosition(String value) throws SonyProjectorException {
446         if (!model.isPicturePositionAvailable()) {
447             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.PICTURE_POSITION.getName()
448                     + " for projector model " + model.getName());
449         }
450         setSetting(SonyProjectorItem.PICTURE_POSITION, model.getPicturePositionCodeFromName(value));
451     }
452
453     /**
454      * Request the projector if the overscan is enabled or not
455      *
456      * @return OnOffType.ON if the overscan is enabled, OnOffType.OFF if not
457      *
458      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
459      */
460     public OnOffType getOverscan() throws SonyProjectorException {
461         if (!model.isOverscanAvailable()) {
462             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.OVERSCAN.getName()
463                     + " for projector model " + model.getName());
464         }
465         return Arrays.equals(getSetting(SonyProjectorItem.OVERSCAN), OVERSCAN_ON) ? OnOffType.ON : OnOffType.OFF;
466     }
467
468     /**
469      * Request the projector to enable the overscan
470      *
471      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
472      */
473     public void enableOverscan() throws SonyProjectorException {
474         if (!model.isOverscanAvailable()) {
475             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.OVERSCAN.getName()
476                     + " for projector model " + model.getName());
477         }
478         setSetting(SonyProjectorItem.OVERSCAN, OVERSCAN_ON);
479     }
480
481     /**
482      * Request the projector to disable the overscan
483      *
484      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
485      */
486     public void disableOverscan() throws SonyProjectorException {
487         if (!model.isOverscanAvailable()) {
488             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.OVERSCAN.getName()
489                     + " for projector model " + model.getName());
490         }
491         setSetting(SonyProjectorItem.OVERSCAN, OVERSCAN_OFF);
492     }
493
494     /**
495      * Request the projector to get the current aspect ratio mode
496      *
497      * @return the current current aspect ratio mode
498      *
499      * @throws SonyProjectorException in case of any problem
500      */
501     public String getAspect() throws SonyProjectorException {
502         return model.getAspectNameFromDataCode(getSetting(SonyProjectorItem.ASPECT));
503     }
504
505     /**
506      * Request the projector to change the aspect ratio mode
507      *
508      * @param value the aspect ratio mode to set
509      *
510      * @throws SonyProjectorException in case of any problem
511      */
512     public void setAspect(String value) throws SonyProjectorException {
513         setSetting(SonyProjectorItem.ASPECT, model.getAspectFromName(value).getDataCode());
514     }
515
516     /**
517      * Request the projector to get the current color temperature setting
518      *
519      * @return the current color temperature value
520      *
521      * @throws SonyProjectorException in case of any problem
522      */
523     public String getColorTemperature() throws SonyProjectorException {
524         return model.getColorTempNameFromDataCode(getSetting(SonyProjectorItem.COLOR_TEMP));
525     }
526
527     /**
528      * Request the projector to change the color temperature setting
529      *
530      * @param value the color temperature value to set
531      *
532      * @throws SonyProjectorException in case of any problem
533      */
534     public void setColorTemperature(String value) throws SonyProjectorException {
535         setSetting(SonyProjectorItem.COLOR_TEMP, model.getColorTempCodeFromName(value));
536     }
537
538     /**
539      * Request the projector to get the current iris mode
540      *
541      * @return the current iris mode
542      *
543      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
544      */
545     public String getIrisMode() throws SonyProjectorException {
546         if (!model.isIrisModeAvailable()) {
547             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.IRIS_MODE.getName()
548                     + " for projector model " + model.getName());
549         }
550         return model.getIrisModeNameFromDataCode(getSetting(SonyProjectorItem.IRIS_MODE));
551     }
552
553     /**
554      * Request the projector to change the iris mode
555      *
556      * @param value the iris mode to set
557      *
558      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
559      */
560     public void setIrisMode(String value) throws SonyProjectorException {
561         if (!model.isIrisModeAvailable()) {
562             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.IRIS_MODE.getName()
563                     + " for projector model " + model.getName());
564         }
565         setSetting(SonyProjectorItem.IRIS_MODE, model.getIrisModeCodeFromName(value));
566     }
567
568     /**
569      * Request the projector to get the current iris manual setting
570      *
571      * @return the current value for the iris manual setting
572      *
573      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
574      */
575     public int getIrisManual() throws SonyProjectorException {
576         if (!model.isIrisManualAvailable()) {
577             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.IRIS_MANUAL.getName()
578                     + " for projector model " + model.getName());
579         }
580         return convertDataToInt(getSetting(SonyProjectorItem.IRIS_MANUAL));
581     }
582
583     /**
584      * Request the projector to change the iris manual setting
585      *
586      * @param value the iris manual value to set
587      *
588      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
589      */
590     public void setIrisManual(int value) throws SonyProjectorException {
591         if (!model.isIrisManualAvailable()) {
592             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.IRIS_MANUAL.getName()
593                     + " for projector model " + model.getName());
594         }
595         setSetting(SonyProjectorItem.IRIS_MANUAL, convertIntToData(value));
596     }
597
598     /**
599      * Request the projector to get the current iris sensitivity
600      *
601      * @return the current iris sensitivity
602      *
603      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
604      */
605     public String getIrisSensitivity() throws SonyProjectorException {
606         if (!model.isIrisSensitivityAvailable()) {
607             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.IRIS_SENSITIVITY.getName()
608                     + " for projector model " + model.getName());
609         }
610         return SonyProjectorIrisSensitivity.getFromDataCode(getSetting(SonyProjectorItem.IRIS_SENSITIVITY)).getName();
611     }
612
613     /**
614      * Request the projector to change the iris sensitivity
615      *
616      * @param value the iris sensitivity to set
617      *
618      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
619      */
620     public void setIrisSensitivity(String value) throws SonyProjectorException {
621         if (!model.isIrisSensitivityAvailable()) {
622             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.IRIS_SENSITIVITY.getName()
623                     + " for projector model " + model.getName());
624         }
625         setSetting(SonyProjectorItem.IRIS_SENSITIVITY, SonyProjectorIrisSensitivity.getFromName(value).getDataCode());
626     }
627
628     /**
629      * Request the projector to get the current film projection mode
630      *
631      * @return the current film projection mode
632      *
633      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
634      */
635     public String getFilmProjection() throws SonyProjectorException {
636         if (!model.isFilmProjectionAvailable()) {
637             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.FILM_PROJECTION.getName()
638                     + " for projector model " + model.getName());
639         }
640         return model.getFilmProjectionNameFromDataCode(getSetting(SonyProjectorItem.FILM_PROJECTION));
641     }
642
643     /**
644      * Request the projector to change the film projection mode
645      *
646      * @param value the film projection mode to set
647      *
648      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
649      */
650     public void setFilmProjection(String value) throws SonyProjectorException {
651         if (!model.isFilmProjectionAvailable()) {
652             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.FILM_PROJECTION.getName()
653                     + " for projector model " + model.getName());
654         }
655         setSetting(SonyProjectorItem.FILM_PROJECTION, model.getFilmProjectionCodeFromName(value));
656     }
657
658     /**
659      * Request the projector to get the current motion enhancer mode
660      *
661      * @return the current motion enhancer mode
662      *
663      * @throws SonyProjectorException in case of any problem
664      */
665     public String getMotionEnhancer() throws SonyProjectorException {
666         if (!model.isMotionEnhancerAvailable()) {
667             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.MOTION_ENHANCER.getName()
668                     + " for projector model " + model.getName());
669         }
670         return model.getMotionEnhancerNameFromDataCode(getSetting(SonyProjectorItem.MOTION_ENHANCER));
671     }
672
673     /**
674      * Request the projector to change the motion enhancer mode
675      *
676      * @param value the motion enhancer mode to set
677      *
678      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
679      */
680     public void setMotionEnhancer(String value) throws SonyProjectorException {
681         if (!model.isMotionEnhancerAvailable()) {
682             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.MOTION_ENHANCER.getName()
683                     + " for projector model " + model.getName());
684         }
685         setSetting(SonyProjectorItem.MOTION_ENHANCER, model.getMotionEnhancerCodeFromName(value));
686     }
687
688     /**
689      * Request the projector to get the current gamma correction
690      *
691      * @return the current gamma correction
692      *
693      * @throws SonyProjectorException in case of any problem
694      */
695     public String getGammaCorrection() throws SonyProjectorException {
696         return model.getGammaCorrectionNameFromDataCode(getSetting(SonyProjectorItem.GAMMA_CORRECTION));
697     }
698
699     /**
700      * Request the projector to change the gamma correction
701      *
702      * @param value the gamma correction to set
703      *
704      * @throws SonyProjectorException in case of any problem
705      */
706     public void setGammaCorrection(String value) throws SonyProjectorException {
707         setSetting(SonyProjectorItem.GAMMA_CORRECTION, model.getGammaCorrectionCodeFromName(value));
708     }
709
710     /**
711      * Request the projector to get the current color space
712      *
713      * @return the current color space
714      *
715      * @throws SonyProjectorException in case of any problem
716      */
717     public String getColorSpace() throws SonyProjectorException {
718         return model.getColorSpaceNameFromDataCode(getSetting(SonyProjectorItem.COLOR_SPACE));
719     }
720
721     /**
722      * Request the projector to change the color space
723      *
724      * @param value the color space to set
725      *
726      * @throws SonyProjectorException in case of any problem
727      */
728     public void setColorSpace(String value) throws SonyProjectorException {
729         setSetting(SonyProjectorItem.COLOR_SPACE, model.getColorSpaceCodeFromName(value));
730     }
731
732     /**
733      * Request the projector to get the current noise reduction mode
734      *
735      * @return the current noise reduction mode
736      *
737      * @throws SonyProjectorException in case of any problem
738      */
739     public String getNr() throws SonyProjectorException {
740         return model.getNrNameFromDataCode(getSetting(SonyProjectorItem.NR));
741     }
742
743     /**
744      * Request the projector to change the noise reduction mode
745      *
746      * @param value the noise reduction mode to set
747      *
748      * @throws SonyProjectorException in case of any problem
749      */
750     public void setNr(String value) throws SonyProjectorException {
751         setSetting(SonyProjectorItem.NR, model.getNrCodeFromName(value));
752     }
753
754     /**
755      * Request the projector to get the current block noise reduction mode
756      *
757      * @return the current block noise reduction mode
758      *
759      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
760      */
761     public String getBlockNr() throws SonyProjectorException {
762         if (!model.isBlockNrAvailable()) {
763             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.BLOCK_NR.getName()
764                     + " for projector model " + model.getName());
765         }
766         return SonyProjectorBlockNr.getFromDataCode(getSetting(SonyProjectorItem.BLOCK_NR)).getName();
767     }
768
769     /**
770      * Request the projector to change the block noise reduction mode
771      *
772      * @param value the block noise reduction mode to set
773      *
774      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
775      */
776     public void setBlockNr(String value) throws SonyProjectorException {
777         if (!model.isBlockNrAvailable()) {
778             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.BLOCK_NR.getName()
779                     + " for projector model " + model.getName());
780         }
781         setSetting(SonyProjectorItem.BLOCK_NR, SonyProjectorBlockNr.getFromName(value).getDataCode());
782     }
783
784     /**
785      * Request the projector to get the current mosquito noise reduction mode
786      *
787      * @return the current mosquito noise reduction mode
788      *
789      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
790      */
791     public String getMosquitoNr() throws SonyProjectorException {
792         if (!model.isMosquitoNrAvailable()) {
793             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.MOSQUITO_NR.getName()
794                     + " for projector model " + model.getName());
795         }
796         return SonyProjectorMosquitoNr.getFromDataCode(getSetting(SonyProjectorItem.MOSQUITO_NR)).getName();
797     }
798
799     /**
800      * Request the projector to change the mosquito noise reduction mode
801      *
802      * @param value the mosquito noise reduction mode to set
803      *
804      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
805      */
806     public void setMosquitoNr(String value) throws SonyProjectorException {
807         if (!model.isMosquitoNrAvailable()) {
808             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.MOSQUITO_NR.getName()
809                     + " for projector model " + model.getName());
810         }
811         setSetting(SonyProjectorItem.MOSQUITO_NR, SonyProjectorMosquitoNr.getFromName(value).getDataCode());
812     }
813
814     /**
815      * Request the projector to get the current MPEG noise reduction mode
816      *
817      * @return the current MPEG noise reduction mode
818      *
819      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
820      */
821     public String getMpegNr() throws SonyProjectorException {
822         if (!model.isMpegNrAvailable()) {
823             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.MPEG_NR.getName()
824                     + " for projector model " + model.getName());
825         }
826         return model.getMpegNrNameFromDataCode(getSetting(SonyProjectorItem.MPEG_NR));
827     }
828
829     /**
830      * Request the projector to change the MPEG noise reduction mode
831      *
832      * @param value the MPEG noise reduction mode to set
833      *
834      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
835      */
836     public void setMpegNr(String value) throws SonyProjectorException {
837         if (!model.isMpegNrAvailable()) {
838             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.MPEG_NR.getName()
839                     + " for projector model " + model.getName());
840         }
841         setSetting(SonyProjectorItem.MPEG_NR, model.getMpegNrCodeFromName(value));
842     }
843
844     /**
845      * Request the projector to get the current value for xvColor
846      *
847      * @return the current value for xvColor
848      *
849      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
850      */
851     public OnOffType getXvColor() throws SonyProjectorException {
852         if (!model.isXvColorAvailable()) {
853             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.XVCOLOR.getName()
854                     + " for projector model " + model.getName());
855         }
856         return Arrays.equals(getSetting(SonyProjectorItem.XVCOLOR), XVCOLOR_ON) ? OnOffType.ON : OnOffType.OFF;
857     }
858
859     /**
860      * Request the projector to set xvColor to ON
861      *
862      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
863      */
864     public void enableXvColor() throws SonyProjectorException {
865         if (!model.isXvColorAvailable()) {
866             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.XVCOLOR.getName()
867                     + " for projector model " + model.getName());
868         }
869         setSetting(SonyProjectorItem.XVCOLOR, XVCOLOR_ON);
870     }
871
872     /**
873      * Request the projector to set xvColor to OFF
874      *
875      * @throws SonyProjectorException in case this setting is not available for the projector or any other problem
876      */
877     public void disableXvColor() throws SonyProjectorException {
878         if (!model.isXvColorAvailable()) {
879             throw new SonyProjectorException("Unavailable item " + SonyProjectorItem.XVCOLOR.getName()
880                     + " for projector model " + model.getName());
881         }
882         setSetting(SonyProjectorItem.XVCOLOR, XVCOLOR_OFF);
883     }
884
885     /**
886      * Send an IR command to the projector
887      *
888      * @param command the IR command
889      *
890      * @throws SonyProjectorException in case this IR command is not available or any other problem
891      */
892     public void sendIrCommand(String command) throws SonyProjectorException {
893         byte @Nullable [] irCode = null;
894         boolean found = true;
895         if (command.startsWith("INPUT_")) {
896             try {
897                 irCode = model.getInputFromName(command.substring(6)).getIrCode();
898             } catch (SonyProjectorException e) {
899                 found = false;
900             }
901         } else if (command.startsWith("PRESET_")) {
902             try {
903                 irCode = model.getCalibrPresetFromName(command.substring(7)).getIrCode();
904             } catch (SonyProjectorException e) {
905                 found = false;
906             }
907         } else if (command.startsWith("ASPECT_")) {
908             try {
909                 irCode = model.getAspectFromName(command.substring(7)).getIrCode();
910             } catch (SonyProjectorException e) {
911                 found = false;
912             }
913         } else {
914             try {
915                 irCode = SonyProjectorItem.getFromValue(command).getIrCode();
916             } catch (SonyProjectorException e) {
917                 found = false;
918             }
919         }
920         if (!found) {
921             // Check if the command is a direct IR code in hexadecimal
922             // Must be 4 characters starting by either 17 or 19 or 1B
923             String cmd = command.trim();
924             if (cmd.length() != 4) {
925                 throw new SonyProjectorException("Invalid IR code: " + command);
926             }
927             int hex;
928             try {
929                 hex = Integer.parseInt(cmd, 16);
930             } catch (NumberFormatException e) {
931                 throw new SonyProjectorException("Invalid IR code: " + command, e);
932             }
933             irCode = new byte[2];
934             irCode[0] = (byte) ((hex >> 8) & 0x000000FF);
935             irCode[1] = (byte) (hex & 0x000000FF);
936         }
937         if (irCode == null || !SonyProjectorItem.isValidIrCode(irCode)) {
938             throw new SonyProjectorException("Invalid IR code: " + command);
939         }
940         sendIR(irCode);
941     }
942
943     /**
944      * Request the projector to get the current value for a setting
945      *
946      * @param item the projector setting to get
947      *
948      * @return the current value for the setting
949      *
950      * @throws SonyProjectorException in case of any problem
951      */
952     protected byte[] getSetting(SonyProjectorItem item) throws SonyProjectorException {
953         logger.debug("Get setting {}", item.getName());
954
955         try {
956             byte[] result = getResponseData(executeCommand(item, true, DUMMY_DATA));
957
958             logger.debug("Get setting {} succeeded: result data: {}", item.getName(), HexUtils.bytesToHex(result));
959
960             return result;
961         } catch (CommunicationException | SonyProjectorException e) {
962             throw new SonyProjectorException("Get setting " + item.getName() + " failed", e);
963         }
964     }
965
966     /**
967      * Request the projector to set a new value for a setting
968      *
969      * @param item the projector setting to set
970      * @param data the value to set for the setting
971      *
972      * @throws SonyProjectorException in case of any problem
973      */
974     private void setSetting(SonyProjectorItem item, byte[] data) throws SonyProjectorException {
975         logger.debug("Set setting {} data {}", item.getName(), HexUtils.bytesToHex(data));
976
977         try {
978             executeCommand(item, false, data);
979         } catch (CommunicationException | SonyProjectorException e) {
980             throw new SonyProjectorException("Set setting " + item.getName() + " failed", e);
981         }
982
983         logger.debug("Set setting {} succeeded", item.getName());
984     }
985
986     /**
987      * Send an IR command to the projector
988      *
989      * @param item the IR information to send
990      *
991      * @throws SonyProjectorException in case of any problem
992      */
993     private void sendIR(SonyProjectorItem item) throws SonyProjectorException {
994         byte @Nullable [] irCode = item.getIrCode();
995         if (irCode == null || !SonyProjectorItem.isValidIrCode(irCode)) {
996             throw new SonyProjectorException("Send IR code failed, code is invalid");
997         }
998         sendIR(irCode);
999     }
1000
1001     /**
1002      * Send an IR command to the projector
1003      *
1004      * @param irCode the IR code (2 bytes) to send
1005      *
1006      * @throws SonyProjectorException in case of any problem
1007      */
1008     private synchronized void sendIR(byte[] irCode) throws SonyProjectorException {
1009         String codeStr = String.format("%02x%02x", irCode[0], irCode[1]);
1010         logger.debug("Send IR code {}", codeStr);
1011
1012         try {
1013             boolean runningSession = connected;
1014
1015             open();
1016
1017             // Build the message and send it
1018             writeCommand(buildMessage(irCode, false, DUMMY_DATA));
1019
1020             // Wait at least 45 ms
1021             Thread.sleep(45);
1022
1023             // No response expected for SIRCS commands
1024
1025             if (!runningSession) {
1026                 close();
1027             }
1028         } catch (CommunicationException e) {
1029             throw new SonyProjectorException("Send IR code " + codeStr + " failed", e);
1030         } catch (InterruptedException e) {
1031             Thread.currentThread().interrupt();
1032             throw new SonyProjectorException("Send IR code " + codeStr + " interrupted", e);
1033         }
1034
1035         logger.debug("Send IR code {} succeeded", codeStr);
1036     }
1037
1038     /**
1039      * Connect to the projector, write a command, read the response and disconnect
1040      *
1041      * @param item the projector setting to get or set
1042      * @param getCommand true for a GET command or false for a SET command
1043      * @param data the value to be considered in case of a SET command
1044      *
1045      * @return the buffer containing the returned message
1046      *
1047      * @throws ConnectionException in case of any connection problem
1048      * @throws CommunicationException in case of any communication problem
1049      * @throws SonyProjectorException in case of any other problem
1050      */
1051     private synchronized byte[] executeCommand(SonyProjectorItem item, boolean getCommand, byte[] data)
1052             throws ConnectionException, CommunicationException, SonyProjectorException {
1053         byte @Nullable [] code = item.getCode();
1054         if (code == null) {
1055             throw new SonyProjectorException("Undefined data code");
1056         }
1057
1058         boolean runningSession = connected;
1059
1060         open();
1061
1062         // Build the message and send it
1063         writeCommand(buildMessage(code, getCommand, data));
1064
1065         // Read the response
1066         byte[] responseMessage = readResponse();
1067
1068         if (!runningSession) {
1069             close();
1070         }
1071
1072         // Validate the content of the response
1073         validateResponse(responseMessage, item);
1074
1075         return responseMessage;
1076     }
1077
1078     /**
1079      * Open the connection with the projector if not yet opened
1080      *
1081      * @throws ConnectionException in case of any problem
1082      */
1083     public abstract void open() throws ConnectionException;
1084
1085     /**
1086      * Close the connection with the projector
1087      */
1088     public void close() {
1089         if (connected) {
1090             OutputStream dataOut = this.dataOut;
1091             if (dataOut != null) {
1092                 try {
1093                     dataOut.close();
1094                 } catch (IOException e) {
1095                 }
1096                 this.dataOut = null;
1097             }
1098             InputStream dataIn = this.dataIn;
1099             if (dataIn != null) {
1100                 try {
1101                     dataIn.close();
1102                 } catch (IOException e) {
1103                 }
1104                 this.dataIn = null;
1105             }
1106             connected = false;
1107         }
1108     }
1109
1110     /**
1111      * Build the message buffer corresponding to the request of a particular information
1112      *
1113      * @param itemCode the code (2 bytes) of the projector setting to get or set
1114      * @param getCommand true for a GET command or false for a SET command
1115      * @param data the value to be considered in case of a SET command
1116      *
1117      * @return the message buffer
1118      */
1119     protected abstract byte[] buildMessage(byte[] itemCode, boolean getCommand, byte[] data);
1120
1121     /**
1122      * Reads some number of bytes from the input stream and stores them into the buffer array b. The number of bytes
1123      * actually read is returned as an integer.
1124      *
1125      * @param dataBuffer the buffer into which the data is read.
1126      * @return the total number of bytes read into the buffer, or -1 if there is no more data because the end of the
1127      *         stream has been reached.
1128      * @throws CommunicationException if the input stream is null, if the first byte cannot be read for any reason
1129      *             other than the end of the file, if the input stream has been closed, or if some other I/O error
1130      *             occurs.
1131      */
1132     protected int readInput(byte[] dataBuffer) throws CommunicationException {
1133         if (simu) {
1134             throw new CommunicationException("readInput failed: should not be called in simu mode");
1135         }
1136         InputStream dataIn = this.dataIn;
1137         if (dataIn == null) {
1138             throw new CommunicationException("readInput failed: input stream is null");
1139         }
1140         try {
1141             return dataIn.read(dataBuffer);
1142         } catch (IOException e) {
1143             logger.debug("readInput failed: {}", e.getMessage());
1144             throw new CommunicationException("readInput failed", e);
1145         }
1146     }
1147
1148     /**
1149      * Write a command to the output stream
1150      *
1151      * @param message the buffer containing the message to be sent
1152      *
1153      * @throws CommunicationException in case of any communication problem
1154      */
1155     protected void writeCommand(byte[] message) throws CommunicationException {
1156         logger.debug("writeCommand: {}", HexUtils.bytesToHex(message));
1157         if (simu) {
1158             return;
1159         }
1160         OutputStream dataOut = this.dataOut;
1161         if (dataOut == null) {
1162             throw new CommunicationException("writeCommand failed: output stream is null");
1163         }
1164         try {
1165             dataOut.write(message);
1166             dataOut.flush();
1167         } catch (IOException e) {
1168             logger.debug("writeCommand failed: {}", e.getMessage());
1169             throw new CommunicationException("writeCommand failed", e);
1170         }
1171     }
1172
1173     /**
1174      * Read the response from the input stream
1175      *
1176      * @return the buffer containing the returned message
1177      *
1178      * @throws CommunicationException in case of any communication problem
1179      */
1180     protected abstract byte[] readResponse() throws CommunicationException;
1181
1182     /**
1183      * Validate the content of a returned message
1184      *
1185      * @param responseMessage the buffer containing the returned message
1186      * @param item the projector setting to get or set
1187      *
1188      * @throws CommunicationException if the message has unexpected content
1189      */
1190     protected abstract void validateResponse(byte[] responseMessage, SonyProjectorItem item)
1191             throws CommunicationException;
1192
1193     /**
1194      * Extract the value from the returned message
1195      *
1196      * @param responseMessage the buffer containing the returned message
1197      *
1198      * @return the value of the projector setting that was requested
1199      */
1200     protected abstract byte[] getResponseData(byte[] responseMessage);
1201
1202     private int convertDataToInt(byte[] data) {
1203         return ((data[0] & 0x000000FF) << 8) | (data[1] & 0x000000FF);
1204     }
1205
1206     private byte[] convertIntToData(int value) {
1207         byte[] data = new byte[2];
1208         data[0] = (byte) ((value & 0x0000FF00) >> 8);
1209         data[1] = (byte) (value & 0x000000FF);
1210         return data;
1211     }
1212 }