27 from __future__ import absolute_import |
27 from __future__ import absolute_import |
28 import socket |
28 import socket |
29 from six.moves import xrange |
29 from six.moves import xrange |
30 import wx |
30 import wx |
31 import wx.lib.mixins.listctrl as listmix |
31 import wx.lib.mixins.listctrl as listmix |
32 from zeroconf import ServiceBrowser, Zeroconf |
32 from zeroconf import ServiceBrowser, Zeroconf, get_all_addresses |
33 |
33 import netifaces |
34 |
34 |
35 service_type = '_PYRO._tcp.local.' |
35 service_type = '_Beremiz._tcp.local.' |
36 |
36 |
37 |
37 |
38 class AutoWidthListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin): |
38 class AutoWidthListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin): |
39 def __init__(self, parent, id, name, pos=wx.DefaultPosition, |
39 def __init__(self, parent, id, name, pos=wx.DefaultPosition, |
40 size=wx.DefaultSize, style=0): |
40 size=wx.DefaultSize, style=0): |
93 def _init_ctrls(self, prnt): |
93 def _init_ctrls(self, prnt): |
94 self.staticText1 = wx.StaticText( |
94 self.staticText1 = wx.StaticText( |
95 label=_('Services available:'), name='staticText1', parent=self, |
95 label=_('Services available:'), name='staticText1', parent=self, |
96 pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) |
96 pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) |
97 |
97 |
98 refreshID = wx.NewId() |
|
99 self.RefreshButton = wx.Button( |
98 self.RefreshButton = wx.Button( |
100 id=refreshID, |
|
101 label=_('Refresh'), name='RefreshButton', parent=self, |
99 label=_('Refresh'), name='RefreshButton', parent=self, |
102 pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) |
100 pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) |
103 self.Bind(wx.EVT_BUTTON, self.OnRefreshButton, id=refreshID) |
101 self.RefreshButton.Bind(wx.EVT_BUTTON, self.OnRefreshButton) |
104 |
102 |
105 self.ByIPCheck = wx.CheckBox(self, label=_("Use IP instead of Service Name")) |
103 self.ByIPCheck = wx.CheckBox(self, label=_("Use IP instead of Service Name")) |
|
104 self.ByIPCheck.SetValue(True) |
106 |
105 |
107 self._init_sizers() |
106 self._init_sizers() |
108 self.Fit() |
107 self.Fit() |
109 |
108 |
110 def __init__(self, parent): |
109 def __init__(self, parent): |
120 self.itemDataMap = {} |
119 self.itemDataMap = {} |
121 self.nextItemId = 0 |
120 self.nextItemId = 0 |
122 |
121 |
123 self.URI = None |
122 self.URI = None |
124 self.Browser = None |
123 self.Browser = None |
125 |
124 self.ZeroConfInstance = None |
126 self.ZeroConfInstance = Zeroconf() |
125 |
127 self.RefreshList() |
126 self.RefreshList() |
128 self.LatestSelection = None |
127 self.LatestSelection = None |
129 |
128 |
|
129 self.IfacesMonitorState = None |
|
130 self.IfacesMonitorTimer = wx.Timer(self) |
|
131 self.IfacesMonitorTimer.Start(2000) |
|
132 self.Bind(wx.EVT_TIMER, self.IfacesMonitor , self.IfacesMonitorTimer) |
|
133 |
130 def __del__(self): |
134 def __del__(self): |
|
135 self.IfacesMonitorTimer.Stop() |
|
136 self.Browser.cancel() |
|
137 self.ZeroConfInstance.close() |
|
138 |
|
139 def IfacesMonitor(self, event): |
|
140 NewState = get_all_addresses(socket.AF_INET) |
|
141 |
|
142 if self.IfacesMonitorState != NewState: |
|
143 if self.IfacesMonitorState is not None: |
|
144 # refresh only if a new address appeared |
|
145 for addr in NewState: |
|
146 if addr not in self.IfacesMonitorState: |
|
147 self.RefreshList() |
|
148 break |
|
149 self.IfacesMonitorState = NewState |
|
150 event.Skip() |
|
151 |
|
152 def RefreshList(self): |
|
153 self.ServicesList.DeleteAllItems() |
131 if self.Browser is not None: |
154 if self.Browser is not None: |
132 self.Browser.cancel() |
155 self.Browser.cancel() |
133 self.ZeroConfInstance.close() |
156 if self.ZeroConfInstance is not None: |
134 |
157 self.ZeroConfInstance.close() |
135 def RefreshList(self): |
158 self.ZeroConfInstance = Zeroconf() |
136 if self.Browser is not None: |
|
137 self.Browser.cancel() |
|
138 self.Browser = ServiceBrowser(self.ZeroConfInstance, service_type, self) |
159 self.Browser = ServiceBrowser(self.ZeroConfInstance, service_type, self) |
139 |
160 |
140 def OnRefreshButton(self, event): |
161 def OnRefreshButton(self, event): |
141 self.ServicesList.DeleteAllItems() |
|
142 self.RefreshList() |
162 self.RefreshList() |
143 |
163 |
144 # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py |
164 # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py |
145 def GetListCtrl(self): |
165 def GetListCtrl(self): |
146 return self.ServicesList |
166 return self.ServicesList |
165 # |
185 # |
166 # self.URI = "%s://%s:%s"%(connect_type, connect_address, connect_port) |
186 # self.URI = "%s://%s:%s"%(connect_type, connect_address, connect_port) |
167 |
187 |
168 def SetURI(self, idx): |
188 def SetURI(self, idx): |
169 self.LatestSelection = idx |
189 self.LatestSelection = idx |
170 svcname = self.getColumnText(idx, 0) |
|
171 connect_type = self.getColumnText(idx, 1) |
|
172 self.URI = str("%s://%s" % (connect_type, svcname + '.' + service_type)) |
|
173 |
190 |
174 def GetURI(self): |
191 def GetURI(self): |
175 if self.LatestSelection is not None: |
192 if self.LatestSelection is not None: |
176 if self.ByIPCheck.IsChecked(): |
193 if self.ByIPCheck.IsChecked(): |
177 self.URI = "%s://%s:%s" % tuple( |
194 svcname, scheme, host, port = \ |
178 map(lambda col:self.getColumnText(self.LatestSelection, col), |
195 map(lambda col:self.getColumnText(self.LatestSelection, col), |
179 (1, 2, 3))) |
196 range(4)) |
180 |
197 return ("%s://%s:%s#%s" % (scheme, host, port, svcname)) \ |
181 return self.URI |
198 if scheme[-1] == "S" \ |
|
199 else ("%s://%s:%s" % (scheme, host, port)) |
|
200 else: |
|
201 svcname = self.getColumnText(self.LatestSelection, 0) |
|
202 connect_type = self.getColumnText(self.LatestSelection, 1) |
|
203 return str("MDNS://%s" % svcname) |
182 return None |
204 return None |
183 |
205 |
184 def remove_service(self, zeroconf, _type, name): |
206 def remove_service(self, zeroconf, _type, name): |
185 wx.CallAfter(self._removeService, name) |
207 wx.CallAfter(self._removeService, name) |
186 |
208 |
208 ''' |
230 ''' |
209 called when a service with the desired type is discovered. |
231 called when a service with the desired type is discovered. |
210 ''' |
232 ''' |
211 info = self.ZeroConfInstance.get_service_info(_type, name) |
233 info = self.ZeroConfInstance.get_service_info(_type, name) |
212 svcname = name.split(".")[0] |
234 svcname = name.split(".")[0] |
213 typename = _type.split(".")[0][1:] |
235 typename = info.properties.get("protocol", None) |
214 ip = str(socket.inet_ntoa(info.address)) |
236 ip = str(socket.inet_ntoa(info.address)) |
215 port = info.port |
237 port = info.port |
216 |
238 |
217 num_items = self.ServicesList.GetItemCount() |
239 num_items = self.ServicesList.GetItemCount() |
218 |
240 |