def __discover(duration): infos = [] class ServiceListener: def remove_service(self, zeroconf, type, name): pass def add_service(self, zeroconf, type, name): info = zeroconf.get_service_info(type, name) infos.append(info) zeroconf.notify_all() if Zeroconf is None: raise Exception( "Either not supported with this python version or package `zeroconf` is broken." ) zeroconf = Zeroconf(interfaces=InterfaceChoice.All) ServiceBrowser(zeroconf, "_blickfeld-lidar._tcp.local.", ServiceListener()) while duration > 0: start = time.time() zeroconf.wait(duration) duration = duration - (time.time() - start) zeroconf.close() return infos
def test_legacy_record_update_listener(): """Test a RecordUpdateListener that does not implement update_records.""" # instantiate a zeroconf instance zc = Zeroconf(interfaces=['127.0.0.1']) with pytest.raises(RuntimeError): r.RecordUpdateListener().update_record( zc, 0, r.DNSRecord('irrelevant', const._TYPE_SRV, const._CLASS_IN, const._DNS_HOST_TTL) ) updates = [] class LegacyRecordUpdateListener(r.RecordUpdateListener): """A RecordUpdateListener that does not implement update_records.""" def update_record(self, zc: 'Zeroconf', now: float, record: r.DNSRecord) -> None: nonlocal updates updates.append(record) listener = LegacyRecordUpdateListener() zc.add_listener(listener, None) # dummy service callback def on_service_state_change(zeroconf, service_type, state_change, name): pass # start a browser type_ = "_homeassistant._tcp.local." name = "MyTestHome" browser = ServiceBrowser(zc, type_, [on_service_state_change]) info_service = ServiceInfo( type_, '%s.%s' % (name, type_), 80, 0, 0, {'path': '/~paulsm/'}, "ash-2.local.", addresses=[socket.inet_aton("10.0.1.2")], ) zc.register_service(info_service) zc.wait(1) browser.cancel() assert len(updates) assert len([isinstance(update, r.DNSPointer) and update.name == type_ for update in updates]) >= 1 zc.remove_listener(listener) # Removing a second time should not throw zc.remove_listener(listener) zc.close()
def test_service_browser_is_aware_of_port_changes(): """Test that the ServiceBrowser is aware of port changes.""" # instantiate a zeroconf instance zc = Zeroconf(interfaces=['127.0.0.1']) # start a browser type_ = "_hap._tcp.local." registration_name = "xxxyyy.%s" % type_ callbacks = [] # dummy service callback def on_service_state_change(zeroconf, service_type, state_change, name): nonlocal callbacks if name == registration_name: callbacks.append((service_type, state_change, name)) browser = ServiceBrowser(zc, type_, [on_service_state_change]) desc = {'path': '/~paulsm/'} address_parsed = "10.0.1.2" address = socket.inet_aton(address_parsed) info = ServiceInfo(type_, registration_name, 80, 0, 0, desc, "ash-2.local.", addresses=[address]) def mock_incoming_msg(records) -> r.DNSIncoming: generated = r.DNSOutgoing(const._FLAGS_QR_RESPONSE) for record in records: generated.add_answer_at_time(record, 0) return r.DNSIncoming(generated.packets()[0]) _inject_response( zc, mock_incoming_msg([info.dns_pointer(), info.dns_service(), info.dns_text(), *info.dns_addresses()]), ) zc.wait(100) assert callbacks == [('_hap._tcp.local.', ServiceStateChange.Added, 'xxxyyy._hap._tcp.local.')] assert zc.get_service_info(type_, registration_name).port == 80 info.port = 400 _inject_response( zc, mock_incoming_msg([info.dns_service()]), ) zc.wait(100) assert callbacks == [ ('_hap._tcp.local.', ServiceStateChange.Added, 'xxxyyy._hap._tcp.local.'), ('_hap._tcp.local.', ServiceStateChange.Updated, 'xxxyyy._hap._tcp.local.'), ] assert zc.get_service_info(type_, registration_name).port == 400 browser.cancel() zc.close()
def request(self, zc: zeroconf.Zeroconf, timeout: float) -> bool: now = time.time() delay = 0.2 next_ = now + delay last = now + timeout try: zc.add_listener( self, zeroconf.DNSQuestion(self.name, zeroconf._TYPE_ANY, zeroconf._CLASS_IN), ) while self.address is None: if last <= now: # Timeout return False if next_ <= now: out = zeroconf.DNSOutgoing(zeroconf._FLAGS_QR_QUERY) out.add_question( zeroconf.DNSQuestion( self.name, zeroconf._TYPE_A, zeroconf._CLASS_IN ) ) out.add_answer_at_time( zc.cache.get_by_details( self.name, zeroconf._TYPE_A, zeroconf._CLASS_IN ), now, ) zc.send(out) next_ = now + delay delay *= 2 zc.wait(min(next_, last) - now) now = time.time() finally: zc.remove_listener(self) return True
class Beacon(object): def __init__(self, name="Ancilla", port=80, address=None, *args, **kwargs): self.conf = Zeroconf() # self.conf.unregister_all_services() self.registered = False self.listener = MyListener() self.num = 1 self.name = "{}".format(name) self.identifier = self.name self.type = "_ancilla._tcp.local." self.port = port self._info = None self._address = address def update_network(self, discovery=False, discoverable=False): if discovery: if hasattr(self, 'sb'): self.sb.cancel() self.run() if discoverable: if self.registered: self.update() else: self.register() def run(self): self.is_running = True self.listener = MyListener() self.sb = ServiceBrowser(zc=self.conf, type_=self.type, listener=self.listener) self.conf.wait(2000) @property def address(self): if not self._address: return '127.0.0.1' else: return self._address # s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # s.connect(("8.8.8.8", 80)) # return s.getsockname()[0] # return socket.gethostbyname( # socket.gethostname() # ) @address.setter def address(self, val): self._address = val # conf.get_service_info(ztype, "{}.{}".format(name, ztype)) @property def peers(self): return self.sb.services @property def instance_name(self): self.identifier = self.name.lower() # if len(self.peers) > 0: if self.num > 1: self.identifier = "{}-{}".format(self.name.lower(), self.num) # print(f"INSIDE instance_name {self.identifier}") # return name return "{}.{}".format(self.identifier, self.type) @property def domain(self): return "{}.local.".format(self.identifier.lower()) @property def info(self): print("## IP: {} // Ssss: {}".format(self.address, self.peers)) # name = self.instance_name self._info = ServiceInfo( self.type, self.instance_name, addresses=[socket.inet_aton(self.address)], port=self.port, server=self.domain ) return self._info def register(self): # self.conf.register_service(self.info, allow_name_change=False) try: self.conf.register_service(self.info, allow_name_change=False) self.registered = True print(f"RegisteService: {self.info}") except NonUniqueNameException as e: print("Unique Name EXception") self.num += 1 self.register() except Exception as e: template = "A Beacon exception of type {0} occurred. Arguments:\n{1!r}" message = template.format(type(e).__name__, e.args) print(f"Beacon: {message}") def update_name(self, name): self.name = name self.identifier = name self.num = 1 def unregister(self): if self.registered: self.conf.unregister_service(self.info) self.registered = False def close(self): self.unregister() self.conf.close() if hasattr(self, 'sb'): self.sb.cancel() self.is_running = False self.listener = None def update(self): try: self.conf.unregister_service(self._info) self.register() except Exception as e: print(f"BeaconUpdateEXception {str(e)}")
DNSText(info.name, _TYPE_TXT, _CLASS_IN, ttl, info.text), 0) if info.address: out.add_answer_at_time( DNSAddress(info.server, _TYPE_A, _CLASS_IN, ttl, info.address), 0) zc.send(out) i += 1 next_time += _REGISTER_TIME logger.debug("done registering service") desc = {'path': '/~paulsm/'} info = ServiceInfo("_http._tcp.local.", "Paul's Test Web Site._http._tcp.local.", socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local.") zeroconf = Zeroconf(interfaces=InterfaceChoice.All) logger.info("Registration of a service...") try: wait_seconds = 60 while True: register_service(zeroconf, info) zeroconf.wait(wait_seconds * 1000) finally: logger.info("Unregistering...") logger.error("Unregister not implemented") zeroconf.close() logger.info("Done.")
info.server), 0) out.add_answer_at_time(DNSText(info.name, _TYPE_TXT, _CLASS_IN, ttl, info.text), 0) if info.address: out.add_answer_at_time(DNSAddress(info.server, _TYPE_A, _CLASS_IN, ttl, info.address), 0) zc.send(out) i += 1 next_time += _REGISTER_TIME logger.debug("done registering service") desc = {'path': '/~paulsm/'} info = ServiceInfo("_http._tcp.local.", "Paul's Test Web Site._http._tcp.local.", socket.inet_aton("10.0.1.2"), 80, 0, 0, desc, "ash-2.local.") zeroconf = Zeroconf(interfaces=InterfaceChoice.All) logger.info("Registration of a service...") try: wait_seconds = 60 while True: register_service(zeroconf, info) zeroconf.wait(wait_seconds * 1000) finally: logger.info("Unregistering...") logger.error("Unregister not implemented") zeroconf.close() logger.info("Done.")