async def _async_find_data_for_device_id( device_id: str, max_seconds: int = 10, async_zeroconf_instance: AsyncZeroconf = None) -> tuple[str, int]: """Try to find a HomeKit Accessory via Bonjour. The process is time boxed by the second parameter which sets an upper limit of `max_seconds` before it times out. The runtime of the function may be longer because of the Bonjour handling code. """ our_aio_zc = async_zeroconf_instance or AsyncZeroconf() found_device_event = asyncio.Event() listener = CollectingListener(device_id=device_id, found_device_event=found_device_event) async_service_browser = AsyncServiceBrowser(our_aio_zc.zeroconf, HAP_TYPE, listener) with contextlib.suppress(asyncio.TimeoutError): await asyncio.wait_for(found_device_event.wait(), timeout=max_seconds) device_id_bytes = device_id.encode() try: for info in listener.get_data(): if _service_info_id_matches(info, device_id_bytes): logging.debug("Located Homekit IP accessory %s", info.properties) return _build_data_from_service_info(info) finally: await async_service_browser.async_cancel() if not async_zeroconf_instance: await our_aio_zc.async_close() raise AccessoryNotFoundError( "Device not found via Bonjour within 10 seconds")
async def async_discover_homekit_devices( max_seconds: int = 10, async_zeroconf_instance: AsyncZeroconf = None) -> list[Any]: """Discovers all HomeKit Accessories. It browses for devices in the _hap._tcp.local. domain and checks if all required fields are set in the text record. It one field is missing, it will be excluded from the result list. :param max_seconds: the number of seconds we will wait for the devices to be discovered :return: a list of dicts containing all fields as described in table 5.7 page 69 """ if async_zeroconf_instance and async_zeroconf_has_hap_service_browser( async_zeroconf_instance): return await _async_homekit_devices_from_cache(async_zeroconf_instance) our_aiozc = async_zeroconf_instance or AsyncZeroconf() listener = CollectingListener() service_browser = AsyncServiceBrowser(our_aiozc.zeroconf, HAP_TYPE, listener) await asyncio.sleep(max_seconds) tmp = [] try: for info in listener.get_data(): if not _service_info_is_homekit_device(info): continue data = _build_data_from_service_info(info) logging.debug("found Homekit IP accessory %s", data) tmp.append(data) finally: await service_browser.async_cancel() if not async_zeroconf_instance: await our_aiozc.async_close() return tmp
async def register_services(self, infos: List[AsyncServiceInfo]) -> None: self.aiozc = AsyncZeroconf(ip_version=self.ip_version) tasks = [ self.aiozc.async_register_service(info, allow_name_change=True) for info in infos ] background_tasks = await asyncio.gather(*tasks) await asyncio.gather(*background_tasks)
async def async_connect(self, session_instance: httpx.AsyncClient | None = None) -> None: """ Connect to a device asynchronous. :param: session_instance: Session client instance to be potentially reused. """ self._session_instance = session_instance self._session = self._session_instance or httpx.AsyncClient() if not self._zeroconf_instance: self._zeroconf = AsyncZeroconf() elif isinstance(self._zeroconf_instance, Zeroconf): self._zeroconf = AsyncZeroconf(zc=self._zeroconf_instance) else: self._zeroconf = self._zeroconf_instance await asyncio.gather(self._get_device_info(), self._get_plcnet_info()) if not self.device and not self.plcnet: raise DeviceNotFound(f"The device {self.ip} did not answer.") self._connected = True
async def register_services(self) -> None: self.aiozc = AsyncZeroconf(ip_version=self.ip_version, interfaces=[self.interface]) # type: ignore tasks = [ self.aiozc.async_register_service(info, cooperating_responders=True, ttl=25) for info in self.services ] background_tasks = await asyncio.gather(*tasks) await asyncio.gather(*background_tasks) logger.info("Finished registration, press Ctrl-C to exit...")
async def open(self): """Open the zeroconf browser and begin listening """ if self.running: return loop = self.loop = asyncio.get_event_loop() azc = self.async_zeroconf = AsyncZeroconf() self.zeroconf = azc.zeroconf fqdn = PROCAM_FQDN listener = self.listener = ProcamListener(discovery=self) await azc.async_add_service_listener(fqdn, listener) self.running = True
async def _create_zc_with_cache( records: List[DNSRecord], ) -> Tuple[AsyncZeroconf, AsyncServiceBrowser]: aiozc = AsyncZeroconf(interfaces=["127.0.0.1"]) browser = AsyncServiceBrowser( aiozc.zeroconf, ALL_MDNS_SERVICES, None, DummyListener(), ) aiozc.zeroconf.cache.async_add_records(records) await aiozc.zeroconf.async_wait_for_start() return aiozc, browser
async def open(self): if self.running: return zc = self.zeroconf = AsyncZeroconf() coros = set() for service in self.services.values(): if service.published: logger.info(f'Register zc service: {service.info!r}') coros.add(zc.async_register_service(service.info)) if len(coros): await asyncio.gather(*coros) self.running = True
async def start(self): if self.options.enabled: log.info( hp.lc( "Enabling Zeroconf service discovery", hostname=f"{socket.getfqdn()}.", ipaddress=self.options.ip_address, port=self.port, sd=self.options.name, )) self.zeroconf = AsyncZeroconf(ip_version=IPVersion.V4Only) await self.zeroconf.async_register_service( await self.get_interactor_service_info())
async def async_start(self): """Starts the accessory. - Call the accessory's run method. - Start handling accessory events. - Start the HAP server. - Publish a mDNS advertisement. - Print the setup QR code if the accessory is not paired. All of the above are started in separate threads. Accessory thread is set as daemon. """ self._validate_start() self.aio_stop_event = asyncio.Event() logger.info( "Starting accessory %s on address %s, port %s.", self.accessory.display_name, self.state.address, self.state.port, ) # Start listening for requests logger.debug("Starting server.") await self.http_server.async_start(self.loop) # Advertise the accessory as a mDNS service. logger.debug("Starting mDNS.") self.mdns_service_info = AccessoryMDNSServiceInfo(self.accessory, self.state) if not self.advertiser: zc_args = {} if self.interface_choice is not None: zc_args["interfaces"] = self.interface_choice self.advertiser = AsyncZeroconf(**zc_args) await self.advertiser.async_register_service( self.mdns_service_info, cooperating_responders=True ) # Print accessory setup message if not self.state.paired: self.accessory.setup_message() # Start the accessory so it can do stuff. logger.debug("Starting accessory %s", self.accessory.display_name) self.add_job(self.accessory.run) logger.debug( "AccessoryDriver for %s started successfully", self.accessory.display_name )
def __init__(self, logger): self.logger = logger self.loop = asyncio.get_event_loop() self.async_zeroconf = AsyncZeroconf() self.zeroconf = Zeroconf()