|
1 #!/usr/bin/env python |
|
2 # -*- coding: utf-8 -*- |
|
3 |
|
4 # See COPYING file for copyrights details. |
|
5 |
|
6 import weakref |
|
7 from zeroconf import ServiceBrowser, Zeroconf, get_all_addresses |
|
8 import threading |
|
9 |
|
10 service_type = '_Beremiz._tcp.local.' |
|
11 |
|
12 class ZeroConfListenerClass: |
|
13 def __init__(self, dialog): |
|
14 self.dialog = weakref.ref(dialog) |
|
15 |
|
16 self.IfacesMonitorState = None |
|
17 self.IfacesMonitorTimer = None |
|
18 self.Browser = None |
|
19 self.ZeroConfInstance = None |
|
20 self.PublishedServices = set() |
|
21 |
|
22 self.start() |
|
23 |
|
24 def __del__(self): |
|
25 self.stop() |
|
26 |
|
27 def start(self): |
|
28 self.ZeroConfInstance = Zeroconf() |
|
29 self.Browser = ServiceBrowser(self.ZeroConfInstance, service_type, self) |
|
30 # Start the ifaces_monitor timer thread |
|
31 self.IfacesMonitorTimer = threading.Timer(1.0, self.ifaces_monitor) |
|
32 self.IfacesMonitorTimer.start() |
|
33 |
|
34 def stop(self): |
|
35 if self.IfacesMonitorTimer is not None: |
|
36 self.IfacesMonitorTimer.cancel() |
|
37 self.IfacesMonitorTimer = None |
|
38 |
|
39 if self.Browser is not None: |
|
40 self.Browser.cancel() |
|
41 self.Browser = None |
|
42 |
|
43 if self.ZeroConfInstance is not None: |
|
44 self.ZeroConfInstance.close() |
|
45 self.ZeroConfInstance = None |
|
46 |
|
47 def update_service(self, zeroconf, _type, name): |
|
48 self.remove_service(zeroconf, _type, name) |
|
49 self.add_service(zeroconf, _type, name) |
|
50 |
|
51 def add_service(self, zeroconf, _type, name): |
|
52 dialog = self.dialog() |
|
53 if not dialog: |
|
54 return |
|
55 |
|
56 info = self.ZeroConfInstance.get_service_info(_type, name) |
|
57 if info is None: |
|
58 return |
|
59 |
|
60 typename = info.properties.get(b"protocol", None).decode() |
|
61 ip = str(info.parsed_addresses()[0]) |
|
62 port = info.port |
|
63 dialog.addService(typename, ip, port, name) |
|
64 self.PublishedServices.add(name) |
|
65 |
|
66 def remove_service(self, zeroconf, _type, name): |
|
67 dialog = self.dialog() |
|
68 if not dialog: |
|
69 return |
|
70 |
|
71 if name in self.PublishedServices: |
|
72 dialog.removeService(name) |
|
73 self.PublishedServices.discard(name) |
|
74 |
|
75 def ifaces_monitor(self): |
|
76 dialog = self.dialog() |
|
77 if not dialog: |
|
78 return |
|
79 |
|
80 NewState = get_all_addresses() |
|
81 OldState = self.IfacesMonitorState |
|
82 self.IfacesMonitorState = NewState |
|
83 do_restart = False |
|
84 |
|
85 if OldState is not None: |
|
86 # detect if a new address appeared |
|
87 for addr in NewState: |
|
88 if addr not in OldState: |
|
89 do_restart = True |
|
90 break |
|
91 else: |
|
92 OldState.remove(addr) |
|
93 # detect if an address disappeared |
|
94 if len(OldState) > 0: |
|
95 do_restart = True |
|
96 |
|
97 |
|
98 if do_restart: |
|
99 self.stop() |
|
100 |
|
101 while self.PublishedServices: |
|
102 dialog.removeService(self.PublishedServices.pop()) |
|
103 |
|
104 self.start() |
|
105 else: |
|
106 # Restart the ifaces_monitor timer thread |
|
107 self.IfacesMonitorTimer = threading.Timer(1.0, self.ifaces_monitor) |
|
108 self.IfacesMonitorTimer.start() |