-
Notifications
You must be signed in to change notification settings - Fork 88
/
__init__.py
186 lines (160 loc) · 6.13 KB
/
__init__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
import eg
import socket
import select
from time import sleep
from threading import Event, Thread
from struct import pack, unpack
eg.RegisterPlugin(
name = "Onkyo ISCP",
author = (
"Alexander Hartmaier",
"Sem;colon",
),
version = "0.06",
kind = "external",
guid = "{5B3B8AEB-08D7-4FD0-8BEE-8FE50C231E09}",
description = "Controls any Onkyo Receiver which supports the ISCP protocol.",
url = "http://www.eventghost.net/forum/viewtopic.php?f=10&t=2964",
)
class Text:
tcpBox = "TCP/IP Settings"
ip = "IP:"
port = "Port:"
timeout = "Timeout:"
class SendCommand:
commandBox = "Command Settings"
command = "Code to send:"
class OnkyoISCP(eg.PluginBase):
text = Text
header = 'ISCP'
headersize = 16
version = 1
unittype = 1 # receiver
def __init__(self):
self.AddAction(SendCommand)
def __start__(self, ip, port, timeout):
self.ip = ip
self.port = int(port)
self.timeout = float(timeout)
self.Connect()
def __stop__(self):
if hasattr(self, 'stopThreadEvent'):
self.stopThreadEvent.set()
self.socket.close()
def Receive(self):
while not self.stopThreadEvent.is_set():
try:
ready = select.select([self.socket], [], [])
# the first element of the returned list is a list of readable sockets
if ready[0]:
# 1024 bytes should be enough for every received event
reply = self.socket.recv(1024)
# unpack ISCP header
header, headersize, datasize, version = unpack(
'!4sIIBxxx',
reply[0:self.headersize]
)
if header != self.header:
self.PrintError("OnkyoISCP: Received packet not ISCP")
return
if version != self.version:
self.PrintError("OnkyoISCP: ISCP version " + str(version) + " not supported")
return
#print "Header: " + header
#print "Header size: " + str(headersize)
#print "Data size: " + str(datasize)
#print "Version: " + str(version)
# message ends in EOL CR LF, is three chars shorter than data size
message = reply[headersize:headersize+datasize].rstrip('\x1a\r\n')
#print "Message: " + message
messagesize = len(message)
# parse message
#unittype = message[1]
#print "Unit type: " + unittype
command = message[2:5]
parameter = message[5:messagesize]
self.TriggerEvent(command, payload=parameter)
self.TriggerEvent(command + parameter)
except Exception as e:
self.PrintError("OnkyoISCP: " + str(e))
def Connect(self):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.settimeout(self.timeout)
self.socket = s
ip = self.ip
port = self.port
try:
s.connect((ip, port))
except Exception as e:
self.PrintError("OnkyoISCP: Failed to connect to " + ip + ":" + str(port) + ": " + str(e))
else:
print "Connected to " + ip + ":" + str(port)
self.stopThreadEvent = Event()
thread = Thread(
target = self.Receive,
)
thread.start()
def Configure(self, ip="", port="60128", timeout="1"):
text = self.text
panel = eg.ConfigPanel()
wx_ip = panel.TextCtrl(ip)
wx_port = panel.SpinIntCtrl(port, max=65535)
wx_timeout = panel.TextCtrl(timeout)
st_ip = panel.StaticText(text.ip)
st_port = panel.StaticText(text.port)
st_timeout = panel.StaticText(text.timeout)
eg.EqualizeWidths((st_ip, st_port, st_timeout))
tcpBox = panel.BoxedGroup(
text.tcpBox,
(st_ip, wx_ip),
(st_port, wx_port),
(st_timeout, wx_timeout),
)
panel.sizer.Add(tcpBox, 0, wx.EXPAND)
while panel.Affirmed():
panel.SetResult(
wx_ip.GetValue(),
wx_port.GetValue(),
wx_timeout.GetValue(),
)
class SendCommand(eg.ActionBase):
def __call__(self, Command):
message = '!' + str(self.plugin.unittype) + Command + '\x0d'
# unlike specified the datasize needs to include the headersize
# to make it work for some models (Integra DHC-9.9)
# while others (Onkyo PR-SC5507) work fine without it
# both work when the headersize is included
datasize = self.plugin.headersize + len(message)
line = pack('!4sIIBxxx',
self.plugin.header,
self.plugin.headersize,
datasize,
self.plugin.version
) + message
try:
self.plugin.socket.sendall(line)
sleep(0.1)
except socket.error, msg:
self.PrintError("OnkyoISCP: Error sending command, retrying: " + msg)
# try to reopen the socket on error
# happens if no commands are sent for a long time
# and the tcp connection got closed because
# e.g. the receiver was switched off and on again
self.plugin.Connect()
try:
self.plugin.socket.sendall(line)
except socket.error, msg:
self.PrintError("OnkyoISCP: Error sending command: " + msg)
def Configure(self, Command=""):
panel = eg.ConfigPanel()
text = self.text
st_command = panel.StaticText(text.command)
wx_command = panel.TextCtrl(Command)
commandBox = panel.BoxedGroup(
text.commandBox,
(st_command, wx_command)
)
panel.sizer.Add(commandBox, 0, wx.EXPAND)
while panel.Affirmed():
panel.SetResult(wx_command.GetValue())