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