pysdcp-extended 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ pySDCP: Copyright (c) 2017 Guy Shapira
4
+ pySDCP-extended: Copyright (c) 2024 kennymc.c
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
@@ -0,0 +1,120 @@
1
+ Metadata-Version: 2.1
2
+ Name: pysdcp-extended
3
+ Version: 0.1.0
4
+ Summary: Extended SDCP / PJ Talk library to control Sony projectors
5
+ Home-page: https://github.com/kennymc-c/pySDCP-extended
6
+ License: MIT
7
+ Keywords: sdcp,pjtalk,sony,projector,ip-control,home-automation
8
+ Author: kennymc.c
9
+ Requires-Python: >=3.11,<4.0
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Home Automation
19
+ Classifier: Topic :: Software Development :: Libraries
20
+ Project-URL: Repository, https://github.com/kennymc-c/pySDCP-extended
21
+ Description-Content-Type: text/markdown
22
+
23
+ # pySDCP-extended
24
+
25
+ <!---[![PyPi](https://img.shields.io/pypi/v/pysdcp-extended.svg)](https://pypi.org/project/pysdcp-extended)--->
26
+
27
+ Extended Sony SDCP / PJ Talk projector control.
28
+
29
+ Python **3** library to query and control Sony Projectors using SDCP (PJ Talk) protocol over IP.
30
+
31
+ ## Features
32
+
33
+ * Auto discover projector using SDAP (Simple Display Advertisement Protocol)
34
+ * Query and change power & input (HDMI 1 + 2)
35
+ * Set aspect ratio/zoom and calibration presets
36
+
37
+ ### Extended Features
38
+
39
+ * Support for more commands (added to protocol.py)
40
+ * Query and set picture muting
41
+ * Query lamp hours
42
+ * Query model name and serial number
43
+ * Show response error message from the projector
44
+ * Set a custom PJ Talk community & UDP advertisement SDAP port and TCP SDCP port
45
+
46
+ ## Protocol Documentation
47
+
48
+ * [Link](https://www.digis.ru/upload/iblock/f5a/VPL-VW320,%20VW520_ProtocolManual.pdf)
49
+ * [Link](https://docs.sony.com/release/VW100_protocol.pdf)
50
+
51
+ ## Supported Projectors
52
+
53
+ Supported Sony projectors should include:
54
+
55
+ * VPL-HW65ES
56
+ * VPL-VW100
57
+ * VPL-VW260
58
+ * VPL-VW270
59
+ * VPL-VW285
60
+ * VPL-VW315
61
+ * VPL-VW320
62
+ * VPL-VW328
63
+ * VPL-VW365
64
+ * VPL-VW515
65
+ * VPL-VW520
66
+ * VPL-VW528
67
+ * VPL-VW665
68
+
69
+ ## Installation
70
+
71
+ ```pip install pysdcp-extended```
72
+
73
+ ## Examples
74
+
75
+ Sending any command will initiate auto discovery of the projector if none is known and will carry on the command. So just go for it and maybe you get lucky
76
+
77
+ ```python
78
+ import pysdcp_extended
79
+
80
+ my_projector = pysdcp_extended.Projector()
81
+
82
+ my_projector.get_power()
83
+ my_projector.set_power(True)
84
+ ```
85
+
86
+ Skip discovery to save time or if you know the IP of the projector
87
+
88
+ ```python
89
+ my_known_projector = pysdcp.Projector('10.1.2.3')
90
+ my_known_projector.set_HDMI_input(2)
91
+ ```
92
+
93
+ You can also set a custom PJ Talk community and tcp/udp port. By default "SONY" will be used as the community and 53862 as udp port for SDAP advertisement and 53484 as tcp port for SDCP
94
+
95
+ ```python
96
+ my_known_projector = pysdcp.Projector(ip='10.1.2.3', community="THEATER", udp_port=53860, tcp_port=53480)
97
+ ```
98
+
99
+ ### Commands from protocol.py
100
+
101
+ While you can use the build in functions like get_power() or set_HDMI_input() you can also directly send any command from protocol.py like this
102
+ If you need to use more commands, just add to _protocol.py_, and send it like this:
103
+
104
+ ```python
105
+ from pysdcp_extended.protocol.py import *
106
+
107
+ my_projector._send_command(action=ACTIONS["SET"], command=COMMANDS_IR["CURSOR_UP"])
108
+ ```
109
+
110
+ Please note that commands in `COMMANDS_IR` work as fire and forget and you only get a response if there is a timeout.
111
+
112
+ ## Credits
113
+
114
+ This plugin is an extended fork of [pySDCP](https://github.com/Galala7/pySDCP) by [Galala7](https://github.com/Galala7) which is based on [sony-sdcp-com](https://github.com/vokkim/sony-sdcp-com) NodeJS library by [vokkim](https://github.com/vokkim).
115
+
116
+ ## See also
117
+
118
+ * [homebridge-sony-sdcp](https://github.com/Galala7/homebridge-sony-sdcp) - Homebridge plugin to control Sony Projectors (based on Galala7/pySDCP)
119
+ * [ucr2-integration-sonySDCP](https://github.com/kennymc-c/ucr2-integration-sonySDCP) - SDCP integration for Unfolded Circle Remote devices
120
+
@@ -0,0 +1,97 @@
1
+ # pySDCP-extended
2
+
3
+ <!---[![PyPi](https://img.shields.io/pypi/v/pysdcp-extended.svg)](https://pypi.org/project/pysdcp-extended)--->
4
+
5
+ Extended Sony SDCP / PJ Talk projector control.
6
+
7
+ Python **3** library to query and control Sony Projectors using SDCP (PJ Talk) protocol over IP.
8
+
9
+ ## Features
10
+
11
+ * Auto discover projector using SDAP (Simple Display Advertisement Protocol)
12
+ * Query and change power & input (HDMI 1 + 2)
13
+ * Set aspect ratio/zoom and calibration presets
14
+
15
+ ### Extended Features
16
+
17
+ * Support for more commands (added to protocol.py)
18
+ * Query and set picture muting
19
+ * Query lamp hours
20
+ * Query model name and serial number
21
+ * Show response error message from the projector
22
+ * Set a custom PJ Talk community & UDP advertisement SDAP port and TCP SDCP port
23
+
24
+ ## Protocol Documentation
25
+
26
+ * [Link](https://www.digis.ru/upload/iblock/f5a/VPL-VW320,%20VW520_ProtocolManual.pdf)
27
+ * [Link](https://docs.sony.com/release/VW100_protocol.pdf)
28
+
29
+ ## Supported Projectors
30
+
31
+ Supported Sony projectors should include:
32
+
33
+ * VPL-HW65ES
34
+ * VPL-VW100
35
+ * VPL-VW260
36
+ * VPL-VW270
37
+ * VPL-VW285
38
+ * VPL-VW315
39
+ * VPL-VW320
40
+ * VPL-VW328
41
+ * VPL-VW365
42
+ * VPL-VW515
43
+ * VPL-VW520
44
+ * VPL-VW528
45
+ * VPL-VW665
46
+
47
+ ## Installation
48
+
49
+ ```pip install pysdcp-extended```
50
+
51
+ ## Examples
52
+
53
+ Sending any command will initiate auto discovery of the projector if none is known and will carry on the command. So just go for it and maybe you get lucky
54
+
55
+ ```python
56
+ import pysdcp_extended
57
+
58
+ my_projector = pysdcp_extended.Projector()
59
+
60
+ my_projector.get_power()
61
+ my_projector.set_power(True)
62
+ ```
63
+
64
+ Skip discovery to save time or if you know the IP of the projector
65
+
66
+ ```python
67
+ my_known_projector = pysdcp.Projector('10.1.2.3')
68
+ my_known_projector.set_HDMI_input(2)
69
+ ```
70
+
71
+ You can also set a custom PJ Talk community and tcp/udp port. By default "SONY" will be used as the community and 53862 as udp port for SDAP advertisement and 53484 as tcp port for SDCP
72
+
73
+ ```python
74
+ my_known_projector = pysdcp.Projector(ip='10.1.2.3', community="THEATER", udp_port=53860, tcp_port=53480)
75
+ ```
76
+
77
+ ### Commands from protocol.py
78
+
79
+ While you can use the build in functions like get_power() or set_HDMI_input() you can also directly send any command from protocol.py like this
80
+ If you need to use more commands, just add to _protocol.py_, and send it like this:
81
+
82
+ ```python
83
+ from pysdcp_extended.protocol.py import *
84
+
85
+ my_projector._send_command(action=ACTIONS["SET"], command=COMMANDS_IR["CURSOR_UP"])
86
+ ```
87
+
88
+ Please note that commands in `COMMANDS_IR` work as fire and forget and you only get a response if there is a timeout.
89
+
90
+ ## Credits
91
+
92
+ This plugin is an extended fork of [pySDCP](https://github.com/Galala7/pySDCP) by [Galala7](https://github.com/Galala7) which is based on [sony-sdcp-com](https://github.com/vokkim/sony-sdcp-com) NodeJS library by [vokkim](https://github.com/vokkim).
93
+
94
+ ## See also
95
+
96
+ * [homebridge-sony-sdcp](https://github.com/Galala7/homebridge-sony-sdcp) - Homebridge plugin to control Sony Projectors (based on Galala7/pySDCP)
97
+ * [ucr2-integration-sonySDCP](https://github.com/kennymc-c/ucr2-integration-sonySDCP) - SDCP integration for Unfolded Circle Remote devices
@@ -0,0 +1,29 @@
1
+ [tool.poetry]
2
+ name = "pysdcp-extended"
3
+ version = "0.1.0"
4
+ description = "Extended SDCP / PJ Talk library to control Sony projectors"
5
+ authors = ["kennymc.c"]
6
+ license = "MIT"
7
+ readme = "README.md"
8
+ repository = "https://github.com/kennymc-c/pySDCP-extended"
9
+ keywords = ["sdcp", "pjtalk", "sony", "projector", "ip-control", "home-automation"]
10
+ classifiers = [
11
+ "Development Status :: 4 - Beta",
12
+ "Intended Audience :: Developers",
13
+ "Operating System :: OS Independent",
14
+ "Topic :: Software Development :: Libraries",
15
+ "Topic :: Home Automation",
16
+ "Programming Language :: Python :: 3.11",
17
+ ]
18
+ # Need as the package name contains a hyphen
19
+ packages = [
20
+ { include = "pysdcp_extended" },
21
+ ]
22
+
23
+ [tool.poetry.dependencies]
24
+ python = "^3.11"
25
+
26
+
27
+ [build-system]
28
+ requires = ["poetry-core"]
29
+ build-backend = "poetry.core.masonry.api"
@@ -0,0 +1,285 @@
1
+ #! py3
2
+
3
+ import socket
4
+ from collections import namedtuple
5
+ from struct import *
6
+
7
+ from pysdcp_extended.protocol import *
8
+
9
+ Header = namedtuple("Header", ['version', 'category', 'community'])
10
+ ProjInfo = namedtuple("ProjInfo", ['id', 'product_name', 'serial_number', 'power_state', 'location'])
11
+
12
+
13
+ def create_command_buffer(header: Header, action, command, data=None):
14
+ # create bytearray in the right size
15
+ if data is not None:
16
+ my_buf = bytearray(12)
17
+ else:
18
+ my_buf = bytearray(10)
19
+ # header
20
+ my_buf[0] = 2 # only works with version 2, don't know why
21
+ my_buf[1] = header.category
22
+ # community
23
+ my_buf[2] = ord(header.community[0])
24
+ my_buf[3] = ord(header.community[1])
25
+ my_buf[4] = ord(header.community[2])
26
+ my_buf[5] = ord(header.community[3])
27
+ # command
28
+ my_buf[6] = action
29
+ pack_into(">H", my_buf, 7, command)
30
+ if data is not None:
31
+ # add data len
32
+ my_buf[9] = 2 # Data is always 2 bytes
33
+ # add data
34
+ pack_into(">H", my_buf, 10, data)
35
+ else:
36
+ my_buf[9] = 0
37
+ return my_buf
38
+
39
+
40
+ def process_command_response(msgBuf):
41
+ my_header = Header(
42
+ version=int(msgBuf[0]),
43
+ category=int(msgBuf[1]),
44
+ community=decode_text_field(msgBuf[2:6]))
45
+ is_success = bool(msgBuf[6])
46
+ command = unpack(">H", msgBuf[7:9])[0]
47
+ data_len = int(msgBuf[9])
48
+ if data_len != 0:
49
+ data = unpack(">H", msgBuf[10:10 + data_len])[0]
50
+ else:
51
+ data = None
52
+ return my_header, is_success, command, data
53
+
54
+
55
+ def process_SDAP(SDAP_buffer) -> (Header, ProjInfo):
56
+ try:
57
+ my_header = Header(
58
+ version=int(SDAP_buffer[2]),
59
+ category=int(SDAP_buffer[3]),
60
+ community=decode_text_field(SDAP_buffer[4:8]))
61
+ my_info = ProjInfo(
62
+ id=SDAP_buffer[0:2].decode(),
63
+ product_name=decode_text_field(SDAP_buffer[8:20]),
64
+ serial_number=unpack('>I', SDAP_buffer[20:24])[0],
65
+ power_state=unpack('>H', SDAP_buffer[24:26])[0],
66
+ location=decode_text_field(SDAP_buffer[26:]))
67
+ except Exception as e:
68
+ print("Error parsing SDAP packet: {}".format(e))
69
+ raise
70
+ return my_header, my_info
71
+
72
+
73
+ def decode_text_field(buf):
74
+ """
75
+ Convert char[] string in buffer to python str object
76
+ :param buf: bytearray with array of chars
77
+ :return: string
78
+ """
79
+ return buf.decode().strip(b'\x00'.decode())
80
+
81
+
82
+ class Projector:
83
+ def __init__(self, ip: str = None, community: str = "SONY", udp_port: int = 53862, tcp_port: int = 53484):
84
+ """
85
+ Base class for projector communication.
86
+ Enables communication with Projector, Sending commands and Querying Power State
87
+
88
+ :param ip: str, IP address for projector. If given, will create a projector with default values to communicate
89
+ with projector on the given ip. i.e. "10.0.0.5"
90
+ :param community: str, PJ Talk Community for the projector. If not given "SONY" will be used
91
+ :param udp_port: int, SDAP Advertisement UDP port. If not given 53862 will be used
92
+ :param tcp_port: int, PJ Talk/SDCP TCP port. If not given 53484 will be used
93
+ """
94
+ self.info = ProjInfo(
95
+ product_name=None,
96
+ serial_number=None,
97
+ power_state=None,
98
+ location=None,
99
+ id=None)
100
+ if ip is None:
101
+ # Create empty Projector object
102
+ self.ip = None
103
+ self.header = Header(version=None, category=None, community=None)
104
+ self.is_init = False
105
+ else:
106
+ # Create projector from known ip
107
+ # Set default values to enable immediately communication with known project (ip)
108
+ self.ip = ip
109
+ self.header = Header(category=10, version=2, community=community)
110
+ self.is_init = True
111
+
112
+ # Default ports
113
+ self.UDP_IP = ""
114
+ self.UDP_PORT = udp_port
115
+ self.TCP_PORT = tcp_port
116
+ self.TCP_TIMEOUT = 2
117
+ self.UDP_TIMEOUT = 31
118
+
119
+ # Valid settings
120
+ self.SCREEN_SETTINGS = {
121
+ "ASPECT_RATIO": ASPECT_RATIOS,
122
+ "PICTURE_POSITION": PICTURE_POSITIONS,
123
+ }
124
+
125
+ def __eq__(self, other):
126
+ return self.info.serial_number == other.info.serial_number
127
+
128
+ def _send_command(self, action, command, data=None, timeout=None):
129
+ timeout = timeout if timeout is not None else self.TCP_TIMEOUT
130
+ if not self.is_init:
131
+ self.find_projector()
132
+ if not self.is_init:
133
+ raise Exception("No projector found and / or specified")
134
+
135
+ my_buf = create_command_buffer(self.header, action, command, data)
136
+
137
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
138
+ sock.settimeout(timeout)
139
+ try:
140
+ sock.connect((self.ip, self.TCP_PORT))
141
+ sent = sock.send(my_buf)
142
+ except socket.timeout as e:
143
+ raise Exception("Timeout while trying to send command {}".format(command)) from e
144
+
145
+ if len(my_buf) != sent:
146
+ raise ConnectionError(
147
+ "Failed sending entire buffer to projector. Sent {} out of {} !".format(sent, len(my_buf)))
148
+
149
+ #Check if command is an simulated ir command without a response from the projector and always return true to avoid a timeout
150
+ if data is None and str(hex(command)).startswith(("0x17", "0x19", "0x1B")):
151
+ sock.close()
152
+
153
+ return True
154
+ else:
155
+ response_buf = sock.recv(1024)
156
+
157
+ sock.close()
158
+
159
+ _, is_success, _, data = process_command_response(response_buf)
160
+
161
+ if not is_success:
162
+ command = "{:x}".format(command)
163
+ try:
164
+ error_msg = RESPONSE_ERRORS[data]
165
+ except KeyError:
166
+ error_code = "{:x}".format(data)
167
+ error_msg = "Unknown error code: " + error_code
168
+ raise Exception("Received failed status from projector while sending command 0x" + command + ". " + error_msg)
169
+
170
+ return data
171
+
172
+ def find_projector(self, udp_ip: str = None, udp_port: int = None, timeout=None):
173
+
174
+ self.UDP_PORT = udp_port if udp_port is not None else self.UDP_PORT
175
+ self.UDP_IP = udp_ip if udp_ip is not None else self.UDP_IP
176
+ timeout = timeout if timeout is not None else self.UDP_TIMEOUT
177
+
178
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
179
+
180
+ sock.bind((self.UDP_IP, self.UDP_PORT))
181
+
182
+ sock.settimeout(timeout)
183
+ try:
184
+ SDAP_buffer, addr = sock.recvfrom(1028)
185
+ except socket.timeout:
186
+ return False
187
+
188
+ self.header, self.info = process_SDAP(SDAP_buffer)
189
+ self.ip = addr[0]
190
+ self.is_init = True
191
+
192
+ def get_pjinfo(self, udp_ip: str = None, udp_port: int = None, timeout=None):
193
+ '''
194
+ Returns ip, serial and model name from projector via SDAP advertisement service as a dictionary. Can take up to 30 seconds.
195
+ '''
196
+ self.UDP_PORT = udp_port if udp_port is not None else self.UDP_PORT
197
+ self.UDP_IP = udp_ip if udp_ip is not None else self.UDP_IP
198
+ timeout = timeout if timeout is not None else self.UDP_TIMEOUT
199
+
200
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
201
+
202
+ sock.bind((self.UDP_IP, self.UDP_PORT))
203
+
204
+ sock.settimeout(timeout)
205
+ try:
206
+ SDAP_buffer, addr = sock.recvfrom(1028)
207
+ except socket.timeout as e:
208
+ raise Exception("Timeout while waiting for data from projector") from e
209
+
210
+ serial = unpack('>I', SDAP_buffer[20:24])[0]
211
+ model = decode_text_field(SDAP_buffer[8:20])
212
+ ip = addr[0]
213
+
214
+ result = {"model":model, "serial":serial, "ip":ip}
215
+
216
+ return result
217
+
218
+ def set_power(self, on=True):
219
+ self._send_command(action=ACTIONS["SET"], command=COMMANDS["SET_POWER"],
220
+ data=POWER_STATUS["START_UP"] if on else POWER_STATUS["STANDBY"])
221
+ return True
222
+
223
+ def set_HDMI_input(self, hdmi_num: int):
224
+ self._send_command(action=ACTIONS["SET"], command=COMMANDS["INPUT"],
225
+ data=INPUTS["HDMI1"] if hdmi_num == 1 else INPUTS["HDMI2"])
226
+ return True
227
+
228
+ def get_input(self):
229
+ data = self._send_command(action=ACTIONS["GET"], command=COMMANDS["INPUT"])
230
+ if data == INPUTS["HDMI1"]:
231
+ return "HDMI 1"
232
+ elif data == INPUTS["HDMI2"]:
233
+ return "HDMI 2"
234
+
235
+ def set_screen(self, command: str, value: str):
236
+ valid_values = self.SCREEN_SETTINGS.get(command)
237
+ if valid_values is None:
238
+ raise Exception("Invalid screen setting {}".format(command))
239
+
240
+ if value not in valid_values:
241
+ raise Exception("Invalid parameter: {}. Expected one of: {}".format(value, valid_values.keys()))
242
+
243
+ self._send_command(action=ACTIONS["SET"], command=COMMANDS[command],
244
+ data=valid_values[value])
245
+ return True
246
+
247
+ def get_power(self):
248
+ data = self._send_command(action=ACTIONS["GET"], command=COMMANDS["GET_STATUS_POWER"])
249
+ if data == POWER_STATUS["STANDBY"] or data == POWER_STATUS["COOLING"] or data == POWER_STATUS["COOLING2"]:
250
+ return False
251
+ else:
252
+ return True
253
+
254
+ def get_muting(self):
255
+ data = self._send_command(action=ACTIONS["GET"], command=COMMANDS["PICTURE_MUTING"])
256
+ if data == PICTURE_MUTING["OFF"]:
257
+ return False
258
+ else:
259
+ return True
260
+
261
+ def set_muting(self, on=True):
262
+ self._send_command(action=ACTIONS["SET"], command=COMMANDS["PICTURE_MUTING"],
263
+ data=PICTURE_MUTING["ON"] if on else PICTURE_MUTING["OFF"])
264
+ return True
265
+
266
+ def get_lamp_hours(self):
267
+ data = self._send_command(action=ACTIONS["GET"], command=COMMANDS["GET_STATUS_LAMP_TIMER"])
268
+ hours = "{:d}".format(data)
269
+ return hours
270
+
271
+
272
+ if __name__ == '__main__':
273
+ # b = Projector()
274
+ # b.find_projector(timeout=1)
275
+ # # print(b.get_power())
276
+ # # b = Projector("10.0.0.139")
277
+ # # #
278
+ # print(b.get_power())
279
+ # print(b.set_power(False))
280
+ # # import time
281
+ # # time.sleep(7)
282
+ # print (b.set_HDMI_input(1))
283
+ # # time.sleep(7)
284
+ # # print (b.set_HDMI_input(2))
285
+ pass
@@ -0,0 +1,184 @@
1
+ # Defines and protocol details from here: https://www.digis.ru/upload/iblock/f5a/VPL-VW320,%20VW520_ProtocolManual.pdf
2
+
3
+ ACTIONS = {
4
+ "GET": 0x01,
5
+ "SET": 0x00
6
+ }
7
+
8
+ #Command
9
+
10
+ #Fire and forget, no response from the projector
11
+ COMMANDS_IR = {
12
+ #PROJECTOR=17, PROJECTOR-E=19, PROJECTOR-EE=1B
13
+ "MENU": 0x1729,
14
+ "CURSOR_RIGHT": 0x1733,
15
+ "CURSOR_LEFT": 0x1734,
16
+ "CURSOR_UP": 0x1735,
17
+ "CURSOR_DOWN": 0x1736,
18
+ "CURSOR_ENTER": 0x175A,
19
+ "LENS_SHIFT_UP": 0x1772,
20
+ "LENS_SHIFT_DOWN": 0x1773,
21
+ "LENS_SHIFT_LEFT": 0x1902,
22
+ "LENS_SHIFT_RIGHT": 0x1903,
23
+ "LENS_FOCUS_FAR": 0x1774,
24
+ "LENS_FOCUS_NEAR": 0x1775,
25
+ "LENS_ZOOM_LARGE": 0x1777,
26
+ "LENS_ZOOM_SMALL": 0x1778,
27
+ }
28
+
29
+ COMMANDS = {
30
+ "SET_POWER": 0x0130,
31
+ "CALIBRATION_PRESET": 0x0002,
32
+ "LAMP_CONTROL": 0x001A,
33
+ "MOTIONFLOW": 0x0059,
34
+ "HDR": 0x007C,
35
+ "INPUT_LAG_REDUCTION": 0x0099,
36
+ "PICTURE_POSITION": 0x0066,
37
+ "ASPECT_RATIO": 0x0020,
38
+ "HDMI1_DYNAMIC_RANGE": 0x006E,
39
+ "HDMI2_DYNAMIC_RANGE": 0x006F,
40
+ "2D_3D_DISPLAY_SELECT": 0x0060,
41
+ "3D_FORMAT": 0x0061,
42
+ "INPUT": 0x0001,
43
+ "PICTURE_MUTING": 0x0030,
44
+ "MENU_POSITION": 0x00A6,
45
+ "GET_STATUS_ERROR": 0x0101,
46
+ "GET_STATUS_POWER": 0x0102,
47
+ "GET_STATUS_LAMP_TIMER": 0x0113,
48
+ }
49
+
50
+ #Data
51
+
52
+ CALIBRATION_PRESETS = {
53
+ "CINEMA_FILM_1": 0x0000,
54
+ "CINEMA_FILM_2": 0x0001,
55
+ "REF": 0x0002,
56
+ "TV": 0x0003,
57
+ "PHOTO": 0x0004,
58
+ "GAME": 0x0005,
59
+ "BRIGHT_CINEMA": 0x0006,
60
+ "BRIGHT_TV": 0x0007,
61
+ "USER": 0x0008,
62
+ }
63
+
64
+ LAMP_CONTROL= {
65
+ "LOW": 0x0000,
66
+ "HIGH": 0x0001,
67
+ }
68
+
69
+ MOTIONFLOW = {
70
+ "OFF": 0x0000,
71
+ "SMOTH_HIGH": 0x0001,
72
+ "SMOTH_LOW": 0x0002,
73
+ "IMPULSE": 0x0003,
74
+ "COMBINATION": 0x0004,
75
+ "TRUE_CINEMA": 0x0005
76
+ }
77
+
78
+ HDR = {
79
+ "OFF": 0x0000,
80
+ "ON": 0x0001,
81
+ "AUTO": 0x0002,
82
+ }
83
+
84
+ INPUT_LAG_REDUCTION= {
85
+ "OFF": 0x0000,
86
+ "ON": 0x0001,
87
+ }
88
+
89
+ PICTURE_POSITIONS = {
90
+ "1_85": 0x0000,
91
+ "2_35": 0x0001,
92
+ "CUSTOM_1": 0x0002,
93
+ "CUSTOM_2": 0x0003,
94
+ "CUSTOM_3": 0x0004
95
+ }
96
+
97
+ ASPECT_RATIOS = {
98
+ "NORMAL": 0x0001,
99
+ "V_STRETCH": 0x000B,
100
+ "ZOOM_1_85": 0x000C,
101
+ "ZOOM_2_35": 0x000D,
102
+ "STRETCH": 0x000E,
103
+ "SQUEEZE": 0x000F
104
+ }
105
+
106
+ DYNAMIC_RANGES = {
107
+ "AUTO": 0x0000,
108
+ "LIMITED": 0x0001,
109
+ "FULL": 0x0002
110
+ }
111
+
112
+ TWO_D_THREE_D_SELECT = {
113
+ "AUTO": 0x0000,
114
+ "3D": 0x0001,
115
+ "2D": 0x0002,
116
+ }
117
+
118
+ THREE_D_FORMATS = {
119
+ "SIMULATED_3D": 0x0000,
120
+ "SIDE_BY_SIDE": 0x0001,
121
+ "OVER_UNDER": 0x0002,
122
+ }
123
+
124
+ INPUTS = {
125
+ "HDMI1": 0x002,
126
+ "HDMI2": 0x003,
127
+ }
128
+
129
+ PICTURE_MUTING = {
130
+ "OFF": 0x0000,
131
+ "ON": 0x0001,
132
+ }
133
+
134
+ MENU_POSITIONS= {
135
+ "BOTTOM_LEFT": 0x0000,
136
+ "CENTER": 0x0001,
137
+ }
138
+
139
+ #Response data
140
+
141
+ ERROR_STATUS = {
142
+ "NO_ERROR": 0,
143
+ "LAMP_ERROR": 1,
144
+ "FAN_ERROR": 2,
145
+ "COVER_ERROR": 4,
146
+ "TEMP_ERROR": 8,
147
+ "D5V_ERROR": 10,
148
+ "POWER_ERROR": 20,
149
+ "TEMP_WARNING": 40,
150
+ }
151
+
152
+ POWER_STATUS = {
153
+ "STANDBY": 0,
154
+ "START_UP": 1,
155
+ "START_UP_LAMP": 2,
156
+ "POWER_ON": 3,
157
+ "COOLING": 4,
158
+ "COOLING2": 5
159
+ }
160
+
161
+ RESPONSE_ERRORS = {
162
+ 0x101: "Item Error: Invalid Item",
163
+ 0x102: "Item Error: Invalid Item Request",
164
+ 0x103: "Item Error: Invalid Length",
165
+ 0x104: "Item Error: Invalid Data",
166
+ 0x111: "Item Error: Short Data",
167
+ 0x180: "Item Error: Not Applicable Item",
168
+ 0x201: "Community Error: Different Community",
169
+ 0x1001: "Request Error: Invalid Version",
170
+ 0x1002: "Request Error: Invalid Category",
171
+ 0x1003: "Request Error: Invalid Request",
172
+ 0x1011: "Request Error: Short Header",
173
+ 0x1012: "Request Error: Short Community",
174
+ 0x1013: "Request Error: Short Command",
175
+ 0xF001: "Comm Error: Timeout",
176
+ 0xF010: "Comm Error: Check Sum Error",
177
+ 0xF020: "Comm Error: Framing Error",
178
+ 0xF030: "Comm Error: Parity Error",
179
+ 0xF040: "Comm Error: Over Run Error",
180
+ 0xF050: "Comm Error: Other Comm Error",
181
+ 0xF0F0: "Comm Error: Unknown Response",
182
+ 0xF110: "NVRAM Error: Read Error",
183
+ 0xF120: "NVRAM Error: Write Error",
184
+ }