23 # along with this program; if not, write to the Free Software |
23 # along with this program; if not, write to the Free Software |
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
25 |
25 |
26 |
26 |
27 import socket |
27 import socket |
|
28 import weakref |
28 import wx |
29 import wx |
29 import wx.lib.mixins.listctrl as listmix |
30 import wx.lib.mixins.listctrl as listmix |
30 from zeroconf import ServiceBrowser, Zeroconf, get_all_addresses |
31 from zeroconf import ServiceBrowser, Zeroconf, get_all_addresses |
31 |
32 |
32 service_type = '_Beremiz._tcp.local.' |
33 service_type = '_Beremiz._tcp.local.' |
33 |
34 |
|
35 class ZeroConfListenerClass: |
|
36 def __init__(self, dialog): |
|
37 self.dialog = weakref.ref(dialog) |
|
38 self.ZeroConfInstance = Zeroconf() |
|
39 self.Browser = ServiceBrowser(self.ZeroConfInstance, service_type, self) |
|
40 |
|
41 def clean(self): |
|
42 if self.Browser is not None: |
|
43 self.Browser.cancel() |
|
44 self.Browser = None |
|
45 if self.ZeroConfInstance is not None: |
|
46 self.ZeroConfInstance.close() |
|
47 self.ZeroConfInstance = None |
|
48 |
|
49 def __del__(self): |
|
50 self.clean() |
|
51 |
|
52 def update_service(self, zeroconf, _type, name): |
|
53 self.remove_service(zeroconf, _type, name) |
|
54 self.add_service(zeroconf, _type, name) |
|
55 |
|
56 def add_service(self, zeroconf, _type, name): |
|
57 info = self.ZeroConfInstance.get_service_info(_type, name) |
|
58 if info is None: |
|
59 return |
|
60 typename = info.properties.get(b"protocol", None).decode() |
|
61 ip = str(info.parsed_addresses()[0]) |
|
62 port = info.port |
|
63 |
|
64 wx.CallAfter(self._add_service, typename, ip, port, name) |
|
65 |
|
66 def _add_service(self, typename, ip, port, name): |
|
67 dialog = self.dialog() |
|
68 if not dialog: return |
|
69 dialog._addService(typename, ip, port, name) |
|
70 |
|
71 def remove_service(self, zeroconf, _type, name): |
|
72 wx.CallAfter(self._remove_service, name) |
|
73 |
|
74 def _remove_service(self, name): |
|
75 dialog = self.dialog() |
|
76 if not dialog: return |
|
77 dialog._removeService(name) |
34 |
78 |
35 class AutoWidthListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin): |
79 class AutoWidthListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin): |
36 def __init__(self, parent, name, pos=wx.DefaultPosition, |
80 def __init__(self, parent, name, pos=wx.DefaultPosition, |
37 size=wx.DefaultSize, style=0): |
81 size=wx.DefaultSize, style=0): |
38 wx.ListCtrl.__init__(self, parent, wx.ID_ANY, pos, size, style, name=name) |
82 wx.ListCtrl.__init__(self, parent, wx.ID_ANY, pos, size, style, name=name) |
114 |
158 |
115 self.itemDataMap = {} |
159 self.itemDataMap = {} |
116 self.nextItemId = 0 |
160 self.nextItemId = 0 |
117 |
161 |
118 self.URI = None |
162 self.URI = None |
119 self.Browser = None |
|
120 self.ZeroConfInstance = None |
|
121 |
163 |
122 self.RefreshList() |
164 self.RefreshList() |
123 self.LatestSelection = None |
165 self.LatestSelection = None |
124 |
166 |
125 self.IfacesMonitorState = None |
167 self.IfacesMonitorState = None |
126 self.IfacesMonitorTimer = wx.Timer(self) |
168 self.IfacesMonitorTimer = wx.Timer(self) |
127 self.IfacesMonitorTimer.Start(2000) |
169 self.IfacesMonitorTimer.Start(2000) |
|
170 self.ZeroConfListener = None |
128 self.Bind(wx.EVT_TIMER, self.IfacesMonitor, self.IfacesMonitorTimer) |
171 self.Bind(wx.EVT_TIMER, self.IfacesMonitor, self.IfacesMonitorTimer) |
129 |
172 |
130 def _cleanup(self): |
173 def _cleanup(self): |
131 if self.IfacesMonitorTimer is not None: |
174 if self.IfacesMonitorTimer is not None: |
132 self.IfacesMonitorTimer.Stop() |
175 self.IfacesMonitorTimer.Stop() |
133 self.IfacesMonitorTimer = None |
176 self.IfacesMonitorTimer = None |
134 if self.Browser is not None: |
177 if self.ZeroConfListener is not None: |
135 self.Browser.cancel() |
178 self.ZeroConfListener.clean() |
136 self.Browser = None |
179 self.ZeroConfListener = None |
137 if self.ZeroConfInstance is not None: |
|
138 self.ZeroConfInstance.close() |
|
139 self.ZeroConfInstance = None |
|
140 |
180 |
141 def __del__(self): |
181 def __del__(self): |
142 self._cleanup() |
182 self._cleanup() |
143 |
183 |
144 def Destroy(self): |
184 def Destroy(self): |
158 self.IfacesMonitorState = NewState |
198 self.IfacesMonitorState = NewState |
159 event.Skip() |
199 event.Skip() |
160 |
200 |
161 def RefreshList(self): |
201 def RefreshList(self): |
162 self.ServicesList.DeleteAllItems() |
202 self.ServicesList.DeleteAllItems() |
163 if self.Browser is not None: |
203 self.ZeroConfListener = ZeroConfListenerClass(self) |
164 self.Browser.cancel() |
|
165 if self.ZeroConfInstance is not None: |
|
166 self.ZeroConfInstance.close() |
|
167 self.ZeroConfInstance = Zeroconf() |
|
168 self.Browser = ServiceBrowser(self.ZeroConfInstance, service_type, self) |
|
169 |
204 |
170 def OnRefreshButton(self, event): |
205 def OnRefreshButton(self, event): |
171 self.RefreshList() |
206 self.RefreshList() |
172 |
207 |
173 # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py |
208 # Used by the ColumnSorterMixin, see wx/lib/mixins/listctrl.py |
177 def getColumnText(self, index, col): |
212 def getColumnText(self, index, col): |
178 item = self.ServicesList.GetItem(index, col) |
213 item = self.ServicesList.GetItem(index, col) |
179 return item.GetText() |
214 return item.GetText() |
180 |
215 |
181 def OnItemSelected(self, event): |
216 def OnItemSelected(self, event): |
182 self.SetURI(event.m_itemIndex) |
217 self.SetURI(event.GetIndex()) |
183 event.Skip() |
218 event.Skip() |
184 |
219 |
185 def OnItemActivated(self, event): |
220 def OnItemActivated(self, event): |
186 self.SetURI(event.m_itemIndex) |
221 self.SetURI(event.GetIndex()) |
187 self.parent.EndModal(wx.ID_OK) |
222 self.parent.EndModal(wx.ID_OK) |
188 event.Skip() |
223 event.Skip() |
189 |
224 |
190 # def SetURI(self, idx): |
225 # def SetURI(self, idx): |
191 # connect_type = self.getColumnText(idx, 1) |
226 # connect_type = self.getColumnText(idx, 1) |
209 # svcname = self.getColumnText(self.LatestSelection, 0) |
244 # svcname = self.getColumnText(self.LatestSelection, 0) |
210 # connect_type = self.getColumnText(self.LatestSelection, 1) |
245 # connect_type = self.getColumnText(self.LatestSelection, 1) |
211 # return str("MDNS://%s" % svcname) |
246 # return str("MDNS://%s" % svcname) |
212 return None |
247 return None |
213 |
248 |
214 def remove_service(self, zeroconf, _type, name): |
|
215 wx.CallAfter(self._removeService, name) |
|
216 |
|
217 def _removeService(self, name): |
249 def _removeService(self, name): |
218 ''' |
250 ''' |
219 called when a service with the desired type goes offline. |
251 called when a service with the desired type goes offline. |
220 ''' |
252 ''' |
221 |
253 |
229 |
261 |
230 if item_name == name: |
262 if item_name == name: |
231 self.ServicesList.DeleteItem(idx) |
263 self.ServicesList.DeleteItem(idx) |
232 break |
264 break |
233 |
265 |
234 def add_service(self, zeroconf, _type, name): |
266 |
235 wx.CallAfter(self._addService, _type, name) |
267 def _addService(self, typename, ip, port, name): |
236 |
|
237 def _addService(self, _type, name): |
|
238 ''' |
268 ''' |
239 called when a service with the desired type is discovered. |
269 called when a service with the desired type is discovered. |
240 ''' |
270 ''' |
241 info = self.ZeroConfInstance.get_service_info(_type, name) |
|
242 if info is None: |
|
243 return |
|
244 svcname = name.split(".")[0] |
271 svcname = name.split(".")[0] |
245 typename = info.properties.get("protocol", None) |
|
246 ip = str(socket.inet_ntoa(info.address)) |
|
247 port = info.port |
|
248 |
272 |
249 num_items = self.ServicesList.GetItemCount() |
273 num_items = self.ServicesList.GetItemCount() |
250 |
274 |
251 # display the new data in the list |
275 # display the new data in the list |
252 new_item = self.ServicesList.InsertStringItem(num_items, svcname) |
276 new_item = self.ServicesList.InsertStringItem(num_items, svcname) |