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