async def discover(self, timeout): """Start discovery of devices and services.""" zeroconf = Zeroconf(self.loop, address_family=[netifaces.AF_INET]) try: ServiceBrowser(zeroconf, HOMESHARING_SERVICE, self) ServiceBrowser(zeroconf, DEVICE_SERVICE, self) ServiceBrowser(zeroconf, MEDIAREMOTE_SERVICE, self) ServiceBrowser(zeroconf, AIRPLAY_SERVICE, self) _LOGGER.debug('Discovering devices for %d seconds', timeout) await asyncio.sleep(timeout) finally: await zeroconf.close() return self._found_devices
async def _discover(id: str, dns_type: str, single: bool): queue = asyncio.Queue() conf = Zeroconf(asyncio.get_event_loop(), address_family=[AF_INET]) async def add_service(service_type, name): info = await conf.get_service_info(service_type, name) await queue.put(info) def sync_change_handler(_, service_type, name, state_change): if state_change is ServiceStateChange.Added: asyncio.create_task(add_service(service_type, name)) try: ServiceBrowser(conf, dns_type, handlers=[sync_change_handler]) match = f'{id}.local.'.lower() if id else None while True: info = await queue.get() addr = inet_ntoa(info.address) if addr == '0.0.0.0': continue # discard simulators if match is None or info.server.lower() == match: serial = info.server[:-len('.local.')] yield addr, info.port, serial if single: return finally: await conf.close()
def __start_service_discovery(self): '''Start zeroconf service discovery''' self.__zc = Zeroconf(self.__loop, address_family=[netifaces.AF_INET]) self.__browser = ServiceBrowser( self.__zc, self.__service_type, handlers=[self.__on_service_state_change])
async def run_me(loop): type_ = "_http._tcp.local." registration_name = "xxxyyy.%s" % type_ def on_service_state_change(zeroconf, service_type, state_change, name): if name == registration_name: if state_change is ServiceStateChange.Added: service_added.set() elif state_change is ServiceStateChange.Removed: service_removed.set() zeroconf_browser = Zeroconf(loop, [netifaces.AF_INET], iface="lo") browser = ServiceBrowser(zeroconf_browser, type_, [on_service_state_change]) zeroconf_registrar = Zeroconf(loop, [netifaces.AF_INET], iface="lo") desc = {'path': '/~paulsm/'} info = ServiceInfo(type_, registration_name, socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local.") zeroconf_registrar.register_service(info) try: await asyncio.sleep(1) assert service_added.is_set() # Don't remove service, allow close() to cleanup finally: await zeroconf_registrar.close() browser.cancel() await zeroconf_browser.close()
def asyncio_zeroconf(): """Builds an aiozeroconf service instance and starts browsing for WoT Servient services. Provides a deque that contains the service state change history.""" from aiozeroconf import Zeroconf, ServiceBrowser from wotpy.wot.discovery.dnssd.service import DNSSDDiscoveryService loop = tornado.ioloop.IOLoop.current() service_history = collections.deque([]) def on_change(zc, service_type, name, state_change): service_history.append((service_type, name, state_change)) aio_zc = Zeroconf(loop.asyncio_loop, address_family=[socket.AF_INET]) ServiceBrowser(aio_zc, DNSSDDiscoveryService.WOT_SERVICE_TYPE, handlers=[on_change]) yield {"zeroconf": aio_zc, "service_history": service_history} @tornado.gen.coroutine def close(): yield aio_zc.close() loop.run_sync(close)
def main(): parser = argparse.ArgumentParser( description="Zeroconf service discovery tool") parser.add_argument('-i', "--iface", default="", help="Name of the inteface to use.") parser.add_argument('-p', "--protocol", choices=['ipv4', 'ipv6', 'both'], default="ipv4", help="What IP protocol to use.") parser.add_argument("-s", "--service", default="_http._tcp.local.", help="The service to browse.") parser.add_argument("-f", "--find", action='store_true', default=False, help="Find services") parser.add_argument("-d", "--debug", action='store_true', default=False, help="Set debug mode.") try: opts = parser.parse_args() except Exception as e: parser.error("Error: " + str(e)) if opts.protocol == "ipv4": proto = [netifaces.AF_INET] elif opts.protocol == "ipv6": proto = [netifaces.AF_INET6] else: proto = [netifaces.AF_INET, netifaces.AF_INET6] loop = asyncio.get_event_loop() logging.basicConfig(level=logging.CRITICAL) if opts.debug: logging.getLogger('zeroconf').setLevel(logging.DEBUG) loop.set_debug(True) zc = Zeroconf(loop, proto, iface=opts.iface) print("\nBrowsing services, press Ctrl-C to exit...\n") try: if opts.find: loop.run_until_complete(list_service(zc)) else: ServiceBrowser(zc, guess(opts.service), handlers=[on_service_state_change]) loop.run_forever() except KeyboardInterrupt: print("Unregistering...") loop.run_until_complete(do_close(zc)) finally: loop.close()
async def discover(self, timeout): """Start discovery of devices and services.""" zeroconf = Zeroconf(self.loop, address_family=[netifaces.AF_INET]) browsers = [] try: browsers += [ ServiceBrowser(zeroconf, HOMESHARING_SERVICE, self), ServiceBrowser(zeroconf, DEVICE_SERVICE, self), ServiceBrowser(zeroconf, MEDIAREMOTE_SERVICE, self), ServiceBrowser(zeroconf, AIRPLAY_SERVICE, self), ] _LOGGER.debug("Discovering devices for %d seconds", timeout) await asyncio.sleep(timeout) if self.pending: await asyncio.wait(self.pending) finally: for browser in browsers: browser.cancel() await zeroconf.close() return self._found_devices
async def find(cls, zc, service, timeout=5, stop_after_first=STOP_AFTER_FIRST): """ Return all of the advertised services on any local networks. :param zc: Zeroconf() instance. Pass in an instance running :param timeout: seconds to wait for any responses :return: tuple of service type strings """ listener = cls() browser = ServiceBrowser(zc, service, listener=listener) # wait for responses for i in range(100): await asyncio.sleep(timeout / 100) if len(listener.found_services) > 0 and STOP_AFTER_FIRST: break browser.cancel() # print(listener.found_services) return listener.found_services
async def _discover( desired_id: Optional[str], dns_type: str, ) -> Generator[ConnectInfo, None, None]: queue: asyncio.Queue[ServiceInfo] = asyncio.Queue() conf = Zeroconf(asyncio.get_event_loop(), address_family=[AF_INET]) async def add_service(service_type, name): info = await conf.get_service_info(service_type, name) await queue.put(info) def sync_change_handler(_, service_type, name, state_change): if state_change is ServiceStateChange.Added: asyncio.create_task(add_service(service_type, name)) try: ServiceBrowser(conf, dns_type, handlers=[sync_change_handler]) while True: info = await queue.get() if info.address in [None, SIM_ADDR]: continue # discard unknown addresses and simulators addr = inet_ntoa(info.address) id = info.properties.get(b'ID', bytes()).decode().lower() if not id: LOGGER.error( f'Invalid device: {info.name} @ {addr}:{info.port} has no ID TXT property' ) continue elif desired_id is None or desired_id.lower() == id: LOGGER.info(f'Discovered {id} @ {addr}:{info.port}') yield ConnectInfo(addr, info.port, id) else: LOGGER.info(f'Discarding {info.name} @ {addr}:{info.port}') finally: await conf.close()
async def scan_devices(timeout=5): """ Scan for devices on the network. :return: A list of devices found. """ loop = asyncio.get_running_loop() zeroconf = Zeroconf(loop) devices = [] def on_service_state_change(zc, service_type, name, change): host = name.split(".")[0] + ".local" host = socket.gethostbyname(host) device = Device(base_url=f"http://{host}") devices.append(device) ServiceBrowser(zeroconf, "_ohm-led._tcp.local.", handlers=[on_service_state_change]) await asyncio.sleep(timeout) await zeroconf.close() return devices
async def run_me(loop): # instantiate a zeroconf instance zc = Zeroconf(loop, [netifaces.AF_INET], iface="lo") # create a bunch of servers type_ = "_my-service._tcp.local." name = 'a wonderful service' server_count = 300 self.generate_many_hosts(zc, type_, name, server_count) # verify that name changing works self.verify_name_change(zc, type_, name, server_count) # we are going to monkey patch the zeroconf send to check packet sizes old_send = zc.send # needs to be a list so that we can modify it in our phony send longest_packet = [0, None] def send(out, addr=r._MDNS_ADDR, port=r._MDNS_PORT): """Sends an outgoing packet.""" packet = out.packet() if longest_packet[0] < len(packet): longest_packet[0] = len(packet) longest_packet[1] = out old_send(out, addr=addr, port=port) # monkey patch the zeroconf send zc.send = send # dummy service callback def on_service_state_change(zeroconf, service_type, state_change, name): pass # start a browser browser = ServiceBrowser(zc, type_, [on_service_state_change]) # wait until the browse request packet has maxed out in size sleep_count = 0 while sleep_count < 100 and \ longest_packet[0] < r._MAX_MSG_ABSOLUTE - 100: sleep_count += 1 await asyncio.sleep(0.1) browser.cancel() await asyncio.sleep(0.5) import zeroconf zeroconf.log.debug('sleep_count %d, sized %d', sleep_count, longest_packet[0]) # now the browser has sent at least one request, verify the size assert longest_packet[0] <= r._MAX_MSG_ABSOLUTE assert longest_packet[0] >= r._MAX_MSG_ABSOLUTE - 100 # mock zeroconf's logger warning() and debug() from mock import patch patch_warn = patch('zeroconf.log.warning') patch_debug = patch('zeroconf.log.debug') mocked_log_warn = patch_warn.start() mocked_log_debug = patch_debug.start() # now that we have a long packet in our possession, let's verify the # exception handling. out = longest_packet[1] out.data.append(b'\0' * 1000) # mock the zeroconf logger and check for the correct logging backoff call_counts = mocked_log_warn.call_count, mocked_log_debug.call_count # try to send an oversized packet zc.send(out) assert mocked_log_warn.call_count == call_counts[0] + 1 assert mocked_log_debug.call_count == call_counts[0] zc.send(out) assert mocked_log_warn.call_count == call_counts[0] + 1 assert mocked_log_debug.call_count == call_counts[0] + 1 # force a receive of an oversized packet packet = out.packet() s = zc._respond_sockets[0] # mock the zeroconf logger and check for the correct logging backoff call_counts = mocked_log_warn.call_count, mocked_log_debug.call_count # force receive on oversized packet s.sendto(packet, 0, (r._MDNS_ADDR, r._MDNS_PORT)) s.sendto(packet, 0, (r._MDNS_ADDR, r._MDNS_PORT)) await asyncio.sleep(2.0) zeroconf.log.debug('warn %d debug %d was %s', mocked_log_warn.call_count, mocked_log_debug.call_count, call_counts) assert mocked_log_debug.call_count > call_counts[0] # close our zeroconf which will close the sockets zc.close() # pop the big chunk off the end of the data and send on a closed socket out.data.pop() zc._GLOBAL_DONE = False # mock the zeroconf logger and check for the correct logging backoff call_counts = mocked_log_warn.call_count, mocked_log_debug.call_count # send on a closed socket (force a socket error) zc.send(out) zeroconf.log.debug('warn %d debug %d was %s', mocked_log_warn.call_count, mocked_log_debug.call_count, call_counts) assert mocked_log_warn.call_count > call_counts[0] assert mocked_log_debug.call_count > call_counts[0] zc.send(out) zeroconf.log.debug('warn %d debug %d was %s', mocked_log_warn.call_count, mocked_log_debug.call_count, call_counts) assert mocked_log_debug.call_count > call_counts[0] + 2 mocked_log_warn.stop() mocked_log_debug.stop() loop = asyncio.get_event_loop() loop.run_until_complete(run_me(loop))
else: print(" No info") print('\n') async def do_close(zc): await zc.close() if __name__ == '__main__': logging.basicConfig(level=logging.CRITICAL) if len(sys.argv) > 1: assert sys.argv[1:] == ['--debug'] logging.getLogger('zeroconf').setLevel(logging.DEBUG) loop = asyncio.get_event_loop() loop.set_debug(True) zc = Zeroconf(loop) print("\nBrowsing services, press Ctrl-C to exit...\n") browser = ServiceBrowser(zc, "_http._tcp.local.", handlers=[on_service_state_change]) try: loop.run_forever() except KeyboardInterrupt: print("Unregistering...") loop.run_until_complete(do_close(zc)) finally: loop.close()
def start_yeelight_discovery(handler, gone_handler=None, iface=None): loop = aio.get_event_loop() zeroconf = Zeroconf(loop, iface=iface) listener = YeelightListener(handler, gone_handler) browser = ServiceBrowser(zeroconf, "_miio._udp.local.", listener) return zeroconf, browser
async def find_hue(zc): browser = ServiceBrowser(zc, "_hue._tcp.local.", handlers=[on_service_state_change]) await asyncio.sleep(timeout) browser.cancel()
default=False, help="Set debug mode.") try: opts = parser.parse_args() except Exception as e: parser.error("Error: " + str(e)) if opts.protocol == "ipv4": proto = [netifaces.AF_INET] elif opts.protocol == "ipv6": proto = [netifaces.AF_INET6] else: proto = [netifaces.AF_INET, netifaces.AF_INET6] loop = asyncio.get_event_loop() logging.basicConfig(level=logging.CRITICAL) if opts.debug: logging.getLogger('zeroconf').setLevel(logging.DEBUG) loop.set_debug(True) zc = Zeroconf(loop, proto, iface=opts.iface) print("\nBrowsing services, press Ctrl-C to exit...\n") browser = ServiceBrowser(zc, opts.service, handlers=[on_service_state_change]) try: loop.run_forever() except KeyboardInterrupt: print("Unregistering...") loop.run_until_complete(do_close(zc)) finally: loop.close()