FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

chore: brand new async examples, fresh readme example and DNSText repr · python-zeroconf/python-zeroconf@117a60f · GitHub

Repository navigation

Commit 117a60f

Browse files
committed
chore: brand new async examples, fresh readme example and DNSText repr
1 parent fa1e253 commit 117a60f

4 files changed

Lines changed: 162 additions & 0 deletions

File tree

‎README.rst‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,24 @@ Here's how to discover services:
9292

9393
.. code-block:: python
9494
95+
from zeroconf import ServiceBrowser, ServiceListener, Zeroconf
96+
97+
98+
class EventLogger(ServiceListener):
99+
def add_service(self, zc: Zeroconf, type_: str, name: str) -> None:
100+
info = zc.get_service_info(type_, name)
101+
print(f"discovered {name}: {info}")
102+
103+
def remove_service(self, zc: Zeroconf, type_: str, name: str) -> None:
104+
print(f"lost {name}")
105+
106+
def update_service(self, zc: Zeroconf, type_: str, name: str) -> None:
107+
print(f"refreshed {name}")
108+
109+
110+
with Zeroconf() as zc:
111+
ServiceBrowser(zc, "_http._tcp.local.", EventLogger())
112+
input("browsing, press enter to stop\n")
95113
96114
.. note::
97115

‎examples/async_apple_scanner.py‎

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#!/usr/bin/env python
2+
3+
"""Scan for Apple devices and dump the device info records they advertise."""
4+
5+
from __future__ import annotations
6+
7+
import argparse
8+
import asyncio
9+
import contextlib
10+
import logging
11+
from typing import Any
12+
13+
from zeroconf import DNSQuestionType, IPVersion, ServiceStateChange, Zeroconf
14+
from zeroconf.asyncio import AsyncServiceBrowser, AsyncServiceInfo, AsyncZeroconf
15+
16+
HAP_TYPE = "_hap._tcp.local."
17+
DEVICE_INFO_TYPE = "_device-info._tcp.local."
18+
19+
log = logging.getLogger(__name__)
20+
21+
_background_tasks: set[asyncio.Task] = set()
22+
23+
24+
def on_service_state_change(
25+
zeroconf: Zeroconf, service_type: str, name: str, state_change: ServiceStateChange
26+
) -> None:
27+
print(f"{state_change.name}: {name}")
28+
if state_change is not ServiceStateChange.Added:
29+
return
30+
task = asyncio.create_task(dump_service_info(zeroconf, service_type, name))
31+
_background_tasks.add(task)
32+
task.add_done_callback(_background_tasks.discard)
33+
34+
35+
async def dump_service_info(zeroconf: Zeroconf, service_type: str, name: str) -> None:
36+
info = AsyncServiceInfo(service_type, name)
37+
if not await info.async_request(zeroconf, 3000, question_type=DNSQuestionType.QU):
38+
print(f" {name}: no response")
39+
return
40+
show_info(info)
41+
device_name = f"{info.server.rstrip('.')}.{DEVICE_INFO_TYPE}" if info.server else None
42+
if device_name:
43+
device_info = AsyncServiceInfo(DEVICE_INFO_TYPE, device_name)
44+
if await device_info.async_request(zeroconf, 3000, question_type=DNSQuestionType.QU):
45+
show_info(device_info)
46+
47+
48+
def show_info(info: AsyncServiceInfo) -> None:
49+
print(f" {info.name}")
50+
print(f" reachable at: {', '.join(info.parsed_scoped_addresses())}")
51+
print(f" host {info.server} port {info.port} (priority {info.priority}, weight {info.weight})")
52+
for key, value in info.decoded_properties.items():
53+
print(f" txt {key} = {value}")
54+
55+
56+
async def main(args: Any) -> None:
57+
ip_version = IPVersion.V6Only if args.v6_only else IPVersion.All
58+
aiozc = AsyncZeroconf(ip_version=ip_version)
59+
browser = AsyncServiceBrowser(aiozc.zeroconf, HAP_TYPE, handlers=[on_service_state_change])
60+
print(f"scanning for {HAP_TYPE}, press ctrl-c to exit")
61+
try:
62+
await asyncio.Event().wait()
63+
finally:
64+
await browser.async_cancel()
65+
await aiozc.async_close()
66+
67+
68+
if __name__ == "__main__":
69+
parser = argparse.ArgumentParser(description=__doc__)
70+
parser.add_argument("--debug", action="store_true", help="enable debug logging")
71+
parser.add_argument("--v6-only", action="store_true", help="use IPv6 only")
72+
args = parser.parse_args()
73+
74+
logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO)
75+
76+
with contextlib.suppress(KeyboardInterrupt):
77+
asyncio.run(main(args))
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/usr/bin/env python
2+
3+
"""Resolve every HTTP service currently on the network, on a fixed cadence."""
4+
5+
from __future__ import annotations
6+
7+
import argparse
8+
import asyncio
9+
import contextlib
10+
import logging
11+
12+
from zeroconf import IPVersion, Zeroconf
13+
from zeroconf.asyncio import AsyncServiceBrowser, AsyncServiceInfo, AsyncZeroconf
14+
15+
HTTP_TYPE = "_http._tcp.local."
16+
RESOLVE_INTERVAL = 5
17+
18+
19+
async def resolve_all(zeroconf: Zeroconf, names: set[str]) -> None:
20+
infos = [AsyncServiceInfo(HTTP_TYPE, name) for name in sorted(names)]
21+
await asyncio.gather(*(info.async_request(zeroconf, 3000) for info in infos))
22+
for info in infos:
23+
print(f" {info.name}")
24+
if not info.server:
25+
print(" (unresolved)")
26+
continue
27+
print(f" reachable at: {', '.join(info.parsed_scoped_addresses())}")
28+
print(f" host {info.server} port {info.port} (priority {info.priority}, weight {info.weight})")
29+
for key, value in info.decoded_properties.items():
30+
print(f" txt {key} = {value}")
31+
32+
33+
async def main(ip_version: IPVersion) -> None:
34+
seen: set[str] = set()
35+
aiozc = AsyncZeroconf(ip_version=ip_version)
36+
browser = AsyncServiceBrowser(
37+
aiozc.zeroconf,
38+
HTTP_TYPE,
39+
handlers=[lambda zeroconf, service_type, name, state_change: seen.add(name)],
40+
)
41+
print(f"collecting {HTTP_TYPE} services, resolving every {RESOLVE_INTERVAL}s; ctrl-c to exit")
42+
try:
43+
while True:
44+
await asyncio.sleep(RESOLVE_INTERVAL)
45+
print(f"resolving {len(seen)} known services:")
46+
await resolve_all(aiozc.zeroconf, seen)
47+
finally:
48+
await browser.async_cancel()
49+
await aiozc.async_close()
50+
51+
52+
if __name__ == "__main__":
53+
parser = argparse.ArgumentParser(description=__doc__)
54+
parser.add_argument("--debug", action="store_true", help="enable debug logging")
55+
parser.add_argument("--v6-only", action="store_true", help="use IPv6 only")
56+
args = parser.parse_args()
57+
58+
logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO)
59+
ip_version = IPVersion.V6Only if args.v6_only else IPVersion.All
60+
61+
with contextlib.suppress(KeyboardInterrupt):
62+
asyncio.run(main(ip_version))

‎src/zeroconf/_dns.py‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,11 @@ def __hash__(self) -> int:
541541
"""Hash to compare like DNSText."""
542542
return self._hash
543543

544+
def __repr__(self) -> str:
545+
if len(self.text) > 16:
546+
return self.to_string(f"{len(self.text)} bytes")
547+
return self.to_string(self.text)
548+
544549
def write(self, out: DNSOutgoing) -> None:
545550
out.write_string(self.text)
546551

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL