I need a class which gives me a list of avahi services of a certain type which a currently available. Therefor I run a gobject.MainLoop()
(line 23-25) in a separate thread an add a browser for each service I am interested in (line 27,28). This works in principle.
My problem is, that I do not get always all services. Sometimes all services that are available are listed, sometimes none of them, sometimes just a few. My guess is, that the browser starts iterating the services (line 36) before the appropriate signals are connected (line 41-44), but I have no clue how to fix this. Below a minimal example which shows the failure.
Most examples I have seen on the net (e.g.: Stopping the Avahi service and return a list of elements) run the MainLoop after the browser is set up and the signals are connected. The loop is then quit when the "AllForNow" signal is received. This is not an option for me as the Browser should keep running and listen for new or removed services (which works reliable by the way, just the initial query is problematic).
#!/usr/bin/python
import dbus
from dbus.mainloop.glib import DBusGMainLoop
import avahi
import gobject
import threading
gobject.threads_init()
dbus.mainloop.glib.threads_init()
class ZeroconfBrowser:
def __init__(self):
self.service_browsers = set()
self.services = {}
self.lock = threading.Lock()
loop = DBusGMainLoop(set_as_default=True)
self._bus = dbus.SystemBus(mainloop=loop)
self.server = dbus.Interface(
self._bus.get_object(avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER),
avahi.DBUS_INTERFACE_SERVER)
thread = threading.Thread(target=gobject.MainLoop().run)
thread.daemon = True
thread.start()
self.browse("_ssh._tcp")
self.browse("_http._tcp")
def browse(self, service):
if service in self.service_browsers:
return
self.service_browsers.add(service)
with self.lock:
browser = dbus.Interface(self._bus.get_object(avahi.DBUS_NAME,
self.server.ServiceBrowserNew(avahi.IF_UNSPEC,
avahi.PROTO_UNSPEC, service, 'local', dbus.UInt32(0))),
avahi.DBUS_INTERFACE_SERVICE_BROWSER)
browser.connect_to_signal("ItemNew", self.item_new)
browser.connect_to_signal("ItemRemove", self.item_remove)
browser.connect_to_signal("AllForNow", self.all_for_now)
browser.connect_to_signal("Failure", self.failure)
def resolved(self, interface, protocol, name, service, domain, host,
aprotocol, address, port, txt, flags):
print "resolved", interface, protocol, name, service, domain, flags
def failure(self, exception):
print "Browse error:", exception
def item_new(self, interface, protocol, name, stype, domain, flags):
with self.lock:
self.server.ResolveService(interface, protocol, name, stype,
domain, avahi.PROTO_UNSPEC, dbus.UInt32(0),
reply_handler=self.resolved, error_handler=self.resolve_error)
def item_remove(self, interface, protocol, name, service, domain, flags):
print "removed", interface, protocol, name, service, domain, flags
def all_for_now(self):
print "all for now"
def resolve_error(self, *args, **kwargs):
with self.lock:
print "Resolve error:", args, kwargs
import time
def main():
browser = ZeroconfBrowser()
while True:
time.sleep(3)
for key, value in browser.services.items():
print key, str(value)
if __name__ == '__main__':
main()