@khalido/home 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/.env.example +25 -0
  2. package/CHANGELOG.md +25 -0
  3. package/LICENSE +21 -0
  4. package/README.md +77 -2
  5. package/dist/clock.d.ts +17 -0
  6. package/dist/clock.js +43 -0
  7. package/dist/config.d.ts +273 -0
  8. package/dist/config.js +392 -0
  9. package/dist/connector.d.ts +34 -0
  10. package/dist/connector.js +47 -0
  11. package/dist/connectors/amber/client.d.ts +61 -0
  12. package/dist/connectors/amber/client.js +106 -0
  13. package/dist/connectors/amber/index.d.ts +30 -0
  14. package/dist/connectors/amber/index.js +94 -0
  15. package/dist/connectors/cast/codec.d.ts +38 -0
  16. package/dist/connectors/cast/codec.js +102 -0
  17. package/dist/connectors/cast/index.d.ts +60 -0
  18. package/dist/connectors/cast/index.js +426 -0
  19. package/dist/connectors/cast/schemas.d.ts +167 -0
  20. package/dist/connectors/cast/schemas.js +87 -0
  21. package/dist/connectors/cast/serve.d.ts +11 -0
  22. package/dist/connectors/cast/serve.js +57 -0
  23. package/dist/connectors/cast/state.d.ts +62 -0
  24. package/dist/connectors/cast/state.js +133 -0
  25. package/dist/connectors/cast/tls.d.ts +3 -0
  26. package/dist/connectors/cast/tls.js +67 -0
  27. package/dist/connectors/cast/writes.d.ts +80 -0
  28. package/dist/connectors/cast/writes.js +390 -0
  29. package/dist/connectors/daikin/client.d.ts +65 -0
  30. package/dist/connectors/daikin/client.js +221 -0
  31. package/dist/connectors/daikin/index.d.ts +41 -0
  32. package/dist/connectors/daikin/index.js +108 -0
  33. package/dist/connectors/enphase/client.d.ts +65 -0
  34. package/dist/connectors/enphase/client.js +173 -0
  35. package/dist/connectors/enphase/index.d.ts +43 -0
  36. package/dist/connectors/enphase/index.js +174 -0
  37. package/dist/connectors/enphase/schemas.d.ts +51 -0
  38. package/dist/connectors/enphase/schemas.js +34 -0
  39. package/dist/connectors/hue/client.d.ts +48 -0
  40. package/dist/connectors/hue/client.js +95 -0
  41. package/dist/connectors/hue/events.d.ts +41 -0
  42. package/dist/connectors/hue/events.js +156 -0
  43. package/dist/connectors/hue/index.d.ts +42 -0
  44. package/dist/connectors/hue/index.js +205 -0
  45. package/dist/connectors/hue/schemas.d.ts +65 -0
  46. package/dist/connectors/hue/schemas.js +48 -0
  47. package/dist/connectors/hue/state.d.ts +43 -0
  48. package/dist/connectors/hue/state.js +84 -0
  49. package/dist/connectors/printers/client.d.ts +28 -0
  50. package/dist/connectors/printers/client.js +74 -0
  51. package/dist/connectors/printers/codec.d.ts +76 -0
  52. package/dist/connectors/printers/codec.js +246 -0
  53. package/dist/connectors/printers/found.d.ts +15 -0
  54. package/dist/connectors/printers/found.js +35 -0
  55. package/dist/connectors/printers/index.d.ts +52 -0
  56. package/dist/connectors/printers/index.js +282 -0
  57. package/dist/connectors/printers/state.d.ts +60 -0
  58. package/dist/connectors/printers/state.js +99 -0
  59. package/dist/connectors/spotify/changes.d.ts +32 -0
  60. package/dist/connectors/spotify/changes.js +84 -0
  61. package/dist/connectors/spotify/client.d.ts +199 -0
  62. package/dist/connectors/spotify/client.js +270 -0
  63. package/dist/connectors/spotify/index.d.ts +92 -0
  64. package/dist/connectors/spotify/index.js +745 -0
  65. package/dist/connectors/up/client.d.ts +183 -0
  66. package/dist/connectors/up/client.js +308 -0
  67. package/dist/connectors/up/index.d.ts +91 -0
  68. package/dist/connectors/up/index.js +213 -0
  69. package/dist/connectors/up/spend.d.ts +78 -0
  70. package/dist/connectors/up/spend.js +213 -0
  71. package/dist/connectors/up/webhook.d.ts +13 -0
  72. package/dist/connectors/up/webhook.js +32 -0
  73. package/dist/connectors/weather/blocks.d.ts +51 -0
  74. package/dist/connectors/weather/blocks.js +106 -0
  75. package/dist/connectors/weather/bom.d.ts +37 -0
  76. package/dist/connectors/weather/bom.js +237 -0
  77. package/dist/connectors/weather/ftp.d.ts +5 -0
  78. package/dist/connectors/weather/ftp.js +46 -0
  79. package/dist/connectors/weather/google.d.ts +31 -0
  80. package/dist/connectors/weather/google.js +364 -0
  81. package/dist/connectors/weather/index.d.ts +53 -0
  82. package/dist/connectors/weather/index.js +267 -0
  83. package/dist/connectors/weather/open-meteo.d.ts +15 -0
  84. package/dist/connectors/weather/open-meteo.js +285 -0
  85. package/dist/connectors/weather/rain.d.ts +19 -0
  86. package/dist/connectors/weather/rain.js +51 -0
  87. package/dist/connectors/weather/source.d.ts +149 -0
  88. package/dist/connectors/weather/source.js +77 -0
  89. package/dist/connectors/weather/sun.d.ts +48 -0
  90. package/dist/connectors/weather/sun.js +65 -0
  91. package/dist/discover.d.ts +195 -0
  92. package/dist/discover.js +446 -0
  93. package/dist/energy.d.ts +61 -0
  94. package/dist/energy.js +162 -0
  95. package/dist/finders-entry.d.ts +1 -0
  96. package/dist/finders-entry.js +2 -0
  97. package/dist/finders.d.ts +45 -0
  98. package/dist/finders.js +173 -0
  99. package/dist/health.d.ts +28 -0
  100. package/dist/health.js +35 -0
  101. package/dist/home.d.ts +81 -0
  102. package/dist/home.js +114 -0
  103. package/dist/index.d.ts +11 -0
  104. package/dist/index.js +11 -0
  105. package/dist/lane.d.ts +17 -0
  106. package/dist/lane.js +74 -0
  107. package/dist/locate.d.ts +27 -0
  108. package/dist/locate.js +59 -0
  109. package/dist/manifest.d.ts +31 -0
  110. package/dist/manifest.js +4 -0
  111. package/dist/manifests.d.ts +5 -0
  112. package/dist/manifests.js +23 -0
  113. package/dist/node-fetch.d.ts +9 -0
  114. package/dist/node-fetch.js +25 -0
  115. package/dist/poll.d.ts +10 -0
  116. package/dist/poll.js +33 -0
  117. package/docs/cast.md +133 -0
  118. package/docs/cli.md +39 -0
  119. package/docs/config.md +36 -0
  120. package/docs/decisions.md +150 -0
  121. package/docs/devices.md +128 -0
  122. package/docs/enphase-notes.md +24 -0
  123. package/docs/google.md +92 -0
  124. package/docs/ideas.md +69 -0
  125. package/docs/matter.md +55 -0
  126. package/docs/performance.md +12 -0
  127. package/docs/philosophy.md +23 -0
  128. package/docs/up.md +84 -0
  129. package/docs/using.md +65 -0
  130. package/docs/weather-probe.md +63 -0
  131. package/examples/home.jsonc +74 -0
  132. package/examples/home.svg +76 -0
  133. package/home.schema.json +474 -0
  134. package/llms.txt +32 -0
  135. package/package.json +143 -4
  136. package/skills/draw-home/README.md +26 -0
  137. package/skills/draw-home/SKILL.md +82 -0
  138. package/skills/use-home/SKILL.md +86 -0
  139. package/src/cli/cache.ts +29 -0
  140. package/src/cli/cast.ts +172 -0
  141. package/src/cli/config.ts +52 -0
  142. package/src/cli/context.ts +118 -0
  143. package/src/cli/discover.ts +175 -0
  144. package/src/cli/enphase.ts +62 -0
  145. package/src/cli/hue.ts +124 -0
  146. package/src/cli/locate.ts +47 -0
  147. package/src/cli/main.ts +182 -0
  148. package/src/cli/printers.ts +21 -0
  149. package/src/cli/probe.ts +274 -0
  150. package/src/cli/spotify.ts +76 -0
  151. package/src/cli/status.ts +228 -0
  152. package/src/cli/up.ts +350 -0
  153. package/src/clock.ts +62 -0
  154. package/src/config.ts +578 -0
  155. package/src/connector.ts +77 -0
  156. package/src/connectors/amber/README.md +40 -0
  157. package/src/connectors/amber/client.ts +157 -0
  158. package/src/connectors/amber/index.ts +118 -0
  159. package/src/connectors/cast/README.md +43 -0
  160. package/src/connectors/cast/codec.ts +126 -0
  161. package/src/connectors/cast/index.ts +551 -0
  162. package/src/connectors/cast/schemas.ts +101 -0
  163. package/src/connectors/cast/serve.ts +72 -0
  164. package/src/connectors/cast/state.ts +203 -0
  165. package/src/connectors/cast/tls.ts +100 -0
  166. package/src/connectors/cast/writes.ts +572 -0
  167. package/src/connectors/daikin/README.md +78 -0
  168. package/src/connectors/daikin/client.ts +295 -0
  169. package/src/connectors/daikin/index.ts +145 -0
  170. package/src/connectors/enphase/README.md +64 -0
  171. package/src/connectors/enphase/client.ts +244 -0
  172. package/src/connectors/enphase/index.ts +241 -0
  173. package/src/connectors/enphase/schemas.ts +45 -0
  174. package/src/connectors/hue/README.md +50 -0
  175. package/src/connectors/hue/client.ts +157 -0
  176. package/src/connectors/hue/events.ts +182 -0
  177. package/src/connectors/hue/index.ts +261 -0
  178. package/src/connectors/hue/schemas.ts +60 -0
  179. package/src/connectors/hue/state.ts +124 -0
  180. package/src/connectors/printers/README.md +46 -0
  181. package/src/connectors/printers/client.ts +114 -0
  182. package/src/connectors/printers/codec.ts +292 -0
  183. package/src/connectors/printers/found.ts +46 -0
  184. package/src/connectors/printers/index.ts +385 -0
  185. package/src/connectors/printers/state.ts +182 -0
  186. package/src/connectors/spotify/README.md +80 -0
  187. package/src/connectors/spotify/changes.ts +127 -0
  188. package/src/connectors/spotify/client.ts +387 -0
  189. package/src/connectors/spotify/index.ts +929 -0
  190. package/src/connectors/up/README.md +35 -0
  191. package/src/connectors/up/client.ts +518 -0
  192. package/src/connectors/up/index.ts +370 -0
  193. package/src/connectors/up/spend.ts +280 -0
  194. package/src/connectors/up/webhook.ts +62 -0
  195. package/src/connectors/weather/README.md +84 -0
  196. package/src/connectors/weather/blocks.ts +173 -0
  197. package/src/connectors/weather/bom.ts +344 -0
  198. package/src/connectors/weather/ftp.ts +48 -0
  199. package/src/connectors/weather/google.ts +425 -0
  200. package/src/connectors/weather/index.ts +380 -0
  201. package/src/connectors/weather/open-meteo.ts +309 -0
  202. package/src/connectors/weather/rain.ts +73 -0
  203. package/src/connectors/weather/source.ts +236 -0
  204. package/src/connectors/weather/sun.ts +114 -0
  205. package/src/discover.ts +634 -0
  206. package/src/energy.ts +221 -0
  207. package/src/finders-entry.ts +2 -0
  208. package/src/finders.ts +247 -0
  209. package/src/health.ts +74 -0
  210. package/src/home.ts +205 -0
  211. package/src/index.ts +62 -0
  212. package/src/lane.ts +110 -0
  213. package/src/locate.ts +85 -0
  214. package/src/manifest.ts +43 -0
  215. package/src/manifests.ts +27 -0
  216. package/src/node-fetch.ts +41 -0
  217. package/src/poll.ts +33 -0
@@ -0,0 +1,65 @@
1
+ import { getPosition, getTimes } from "suncalc";
2
+ const DAY_MS = 86_400_000;
3
+ const isoOf = (d) => d?.toISOString();
4
+ /** One owner for the sun's numbers. `at` is epoch ms. */
5
+ export function sunNow(latitude, longitude, at) {
6
+ const date = new Date(at);
7
+ const times = getTimes(date, latitude, longitude);
8
+ const tomorrow = getTimes(new Date(at + DAY_MS), latitude, longitude);
9
+ const { azimuth, altitude } = getPosition(date, latitude, longitude);
10
+ const next = (name) => {
11
+ const today = times[name];
12
+ return today && today.getTime() > at
13
+ ? today.toISOString()
14
+ : isoOf(tomorrow[name]);
15
+ };
16
+ return {
17
+ at: date.toISOString(),
18
+ nightEnd: isoOf(times.nightEnd),
19
+ dawn: isoOf(times.dawn),
20
+ sunrise: isoOf(times.sunrise),
21
+ goldenHourEnd: isoOf(times.goldenHourEnd),
22
+ solarNoon: times.solarNoon.toISOString(),
23
+ goldenHour: isoOf(times.goldenHour),
24
+ sunset: isoOf(times.sunset),
25
+ dusk: isoOf(times.dusk),
26
+ night: isoOf(times.night),
27
+ azimuthDeg: azimuth,
28
+ altitudeDeg: altitude,
29
+ rising: at < times.solarNoon.getTime(),
30
+ daylight: altitude > 0,
31
+ next: {
32
+ dawn: next("dawn"),
33
+ sunrise: next("sunrise"),
34
+ sunset: next("sunset"),
35
+ dusk: next("dusk"),
36
+ },
37
+ alwaysUp: times.alwaysUp,
38
+ alwaysDown: times.alwaysDown,
39
+ };
40
+ }
41
+ /** How far a source's sunrise or sunset may be from the computed one before it is worth saying. */
42
+ export const SUN_TOLERANCE_MS = 2 * 60_000;
43
+ /**
44
+ * A source's sunrise and sunset checked against the computed sun, which stays the owner: a note per event more than
45
+ * two minutes out, else undefined. Each is compared with the nearest computed event of the solar days either side of it,
46
+ * so near solar midnight at high latitude, where the solar day containing it can be the neighbouring one, it still pairs.
47
+ */
48
+ export function sunDisagrees(source, latitude, longitude, reported) {
49
+ const notes = ["sunrise", "sunset"].flatMap((name) => {
50
+ const t = Date.parse(reported[name] ?? "");
51
+ if (Number.isNaN(t))
52
+ return [];
53
+ const nearest = [t - DAY_MS, t, t + DAY_MS]
54
+ .map((day) => sunNow(latitude, longitude, day)[name])
55
+ .filter((c) => c !== undefined)
56
+ .map((c) => ({ computed: c, offMs: Math.abs(t - Date.parse(c)) }))
57
+ .sort((a, b) => a.offMs - b.offMs)[0];
58
+ if (nearest && nearest.offMs <= SUN_TOLERANCE_MS)
59
+ return [];
60
+ return [
61
+ `${source} ${name} ${reported[name]} ${nearest ? `is ${(nearest.offMs / 60_000).toFixed(1)} min from the computed ${nearest.computed}` : "on a day the computed sun has none"}`,
62
+ ];
63
+ });
64
+ return notes.length ? notes.join("; ") : undefined;
65
+ }
@@ -0,0 +1,195 @@
1
+ import type { Clock } from "./clock.ts";
2
+ import type { HomeConfig } from "./config.ts";
3
+ /** mDNS service types worth looking for at home, and what this package does with each. Extend the list as needed. */
4
+ export declare const KNOWN_SERVICES: Record<string, {
5
+ what: string;
6
+ connector?: string;
7
+ }>;
8
+ /** A device a discovery answer named: its connector, the identity it reported, and where it answered; Cast adds its name, model, port and whether it is a group; a printer adds its port, whether it answered on `_ipps._tcp`, and its TXT record. */
9
+ export type Found = {
10
+ connector: Connector;
11
+ id: string;
12
+ host: string;
13
+ name?: string;
14
+ model?: string;
15
+ port?: number;
16
+ group?: boolean;
17
+ secure?: boolean;
18
+ txt?: Record<string, string>;
19
+ };
20
+ /** One connector's discovery, as the caller can run it (mDNS, UDP): every device of that kind that answered. */
21
+ export type Finder = () => Promise<Found[]>;
22
+ /** Which config field holds each connector's identity, as its discovery reports it. */
23
+ export declare const IDENTITY: {
24
+ readonly hue: "bridgeId";
25
+ readonly daikin: "mac";
26
+ readonly enphase: "serial";
27
+ readonly cast: "castId";
28
+ readonly printers: "uuid";
29
+ };
30
+ export type Connector = keyof typeof IDENTITY;
31
+ /** Connectors with many devices in one integration (every Cast device and group, every printer); the rest allow one device for now. */
32
+ export declare const MANY: ReadonlySet<string>;
33
+ /** An identity without case, separators or a `urn:uuid:` prefix: "00:17:88:B0" is "001788b0", "urn:uuid:AB-CD" is "abcd". */
34
+ export declare const idKey: (id: string) => string;
35
+ /** Identities compared by `idKey`. */
36
+ export declare const sameId: (a: string, b: string) => boolean;
37
+ export type Match = {
38
+ moved: {
39
+ index: number;
40
+ id: string;
41
+ connector: Connector;
42
+ from: string;
43
+ to: string;
44
+ }[];
45
+ renamed: {
46
+ index: number;
47
+ id: string;
48
+ connector: Connector;
49
+ from: string;
50
+ to: string;
51
+ }[];
52
+ added: {
53
+ found: Found;
54
+ entry: Record<string, string | number>;
55
+ }[];
56
+ skipped: {
57
+ found: Found;
58
+ why: string;
59
+ }[];
60
+ known: {
61
+ id: string;
62
+ connector: Connector;
63
+ hint?: string;
64
+ }[];
65
+ };
66
+ /**
67
+ * Match what discovery found against the config by identity. An entry with no identity yet is matched by host, and a
68
+ * device at an entry's host with a different identity is a conflict, not a match. New entries get ids no entry has.
69
+ */
70
+ export declare function matchFound(config: HomeConfig, found: Found[]): Match;
71
+ /**
72
+ * The config's text with moved hosts and renamed Cast entries updated and new devices appended, through jsonc-parser's edits: comments, unknown
73
+ * keys and every other line stay as they were. A new device goes on its own line at its neighbour's indent.
74
+ */
75
+ export declare function applyFound(text: string, match: Match, hasDevices: boolean): string;
76
+ /** mDNS and Daikin's broadcast drop answers now and then, so discovery runs this many passes by default… */
77
+ export declare const DISCOVERY_PASSES = 2;
78
+ /** …this far apart. */
79
+ export declare const DISCOVERY_GAP_MS = 5000;
80
+ /** A device's key, across discovery passes and in the address cache: its connector and identity, without case or separators. */
81
+ export declare const addressKey: (connector: Connector, id: string) => string;
82
+ export declare const foundKey: (f: Found) => string;
83
+ /** Where a device last answered, and when that was last confirmed. `name` is kept for Cast, whose config entry may be absent. */
84
+ export type CachedAddress = {
85
+ host: string;
86
+ port?: number;
87
+ name?: string;
88
+ seenAt: string;
89
+ };
90
+ /**
91
+ * Addresses kept between runs, so discovery stays rare. The caller persists them (the CLI: a file); the library writes
92
+ * no files. `write` gets the whole map each time an address is new or has changed.
93
+ */
94
+ export type AddressCache = {
95
+ read(): Record<string, CachedAddress>;
96
+ write(entries: Record<string, CachedAddress>): void;
97
+ };
98
+ /**
99
+ * One run's view of an address cache. `fresh` is a cached address confirmed since the config was edited (`configAt`,
100
+ * the config file's time; a config edited since wins); `seen` records an address that answered, writing through when it
101
+ * changed or was not fresh, so an address confirmed after a config edit is trusted again. A Cast name, when given, is the
102
+ * device's own (discovery's fn, or its own multizone status, never a config's), so a new one is written too; a call
103
+ * without a name keeps the one cached.
104
+ */
105
+ export declare function addressBook(cache: AddressCache | undefined, options?: {
106
+ configAt?: number;
107
+ now?: () => number;
108
+ }): {
109
+ fresh(connector: Connector, id?: string): CachedAddress | undefined;
110
+ seen(connector: Connector, id: string | undefined, address: {
111
+ host: string;
112
+ port?: number;
113
+ name?: string;
114
+ }): void;
115
+ entries: () => {
116
+ [x: string]: CachedAddress;
117
+ };
118
+ };
119
+ /**
120
+ * Run one discovery `passes` times, `gapMs` apart, and return the union (first answer wins). `onPass` sees each pass's
121
+ * additions as they come; `done` ends it early once what was looked for has answered. A pass that throws adds nothing;
122
+ * when every pass threw, discovery never ran, so it throws the last error rather than reporting nothing found.
123
+ */
124
+ export declare function discover<T = Found>(pass: () => Promise<T[]>, options: {
125
+ clock: Clock;
126
+ passes?: number;
127
+ gapMs?: number;
128
+ key?: (t: T) => string;
129
+ done?: (all: T[]) => boolean;
130
+ onPass?: (n: number, added: T[]) => void;
131
+ signal?: AbortSignal;
132
+ }): Promise<T[]>;
133
+ /** Discovery runs after this many failed reads in a row. */
134
+ export declare const RESCAN_AFTER_FAILURES = 3;
135
+ /** Then again after these delays, then hourly for as long as reads fail. */
136
+ export declare const RESCAN_DELAYS_MS: number[];
137
+ export declare const HOUR = 3600000;
138
+ /** The wait before the next scan after `attempt` scans have missed: 1, 5 and 15 minutes, then hourly. The relocator and Cast's re-discovery share it. */
139
+ export declare const rescanDelay: (attempt: number) => number;
140
+ /** After this long without a read, the detail says a day has passed; the scans carry on hourly. */
141
+ export declare const DAY_MS: number;
142
+ /** What `failed()` learned: a move, that a day has passed since the scans began, whether the last scan found it at its address, or why the last scan could not run. */
143
+ export type Lost = {
144
+ moved?: string;
145
+ overADay?: boolean;
146
+ seenHere?: boolean;
147
+ discoveryFailed?: string;
148
+ };
149
+ /**
150
+ * A polling connector's address, rescanned only on failure. `failed()` after each failed read runs `find` once reads
151
+ * have failed `RESCAN_AFTER_FAILURES` times in a row, then on the backoff, then hourly with no end, so a device that comes
152
+ * back is found without a restart. `ok()` after a good read, `reset()` on a refresh (the next failure scans at once).
153
+ * Without `find` or an identity it never scans. `cached`: the starting host came from an address cache, so the first
154
+ * failure scans at once. `failed(signal)` takes the run's signal, so a shutdown does not wait out a scan.
155
+ */
156
+ export declare function relocator(options: {
157
+ host: string;
158
+ id?: string;
159
+ find?: Finder;
160
+ clock: Clock;
161
+ cached?: boolean;
162
+ }): {
163
+ host: () => string;
164
+ ok: () => undefined;
165
+ reset: () => undefined;
166
+ failed(signal?: AbortSignal): Promise<Lost>;
167
+ };
168
+ /** A failed read's detail, saying what discovery found: never "not found" unless a scan looked and missed it. */
169
+ export declare function lostDetail(lost: Lost, error: unknown): string;
170
+ /** Instance names from `dns-sd -B`, each once. */
171
+ export declare const parseBrowse: (text: string) => string[];
172
+ /**
173
+ * `avahi-browse --parsable --resolve` (Linux): each resolved instance once, IPv4 only (the config keeps IPv4 addresses).
174
+ * A resolved line is `=;interface;IPv4;name;type;domain;hostname;address;port;"txt" "txt"`; names and TXT strings use
175
+ * the same escapes as `dns-sd` (`\059` for a `;` in a name, `\"` inside TXT). An instance seen on two interfaces is kept
176
+ * once, the first; loopback is skipped. Checked against avahi-utils on Debian trixie, 2026-10-16.
177
+ */
178
+ export declare function parseAvahi(text: string): {
179
+ instance: string;
180
+ hostname: string;
181
+ host: string;
182
+ port: number;
183
+ txt: Record<string, string>;
184
+ }[];
185
+ /** Instance names from `avahi-browse --parsable` (a `+` line per instance and interface), each once. */
186
+ export declare const parseAvahiBrowse: (text: string) => string[];
187
+ /**
188
+ * One `dns-sd -L` answer: the host and port it can be reached at, and its TXT record. TXT values come escaped (`\ ` for
189
+ * a space, `\226\128\153` for each byte of a UTF-8 character); these are undone, so `fn` reads as the Google Home app shows it.
190
+ */
191
+ export declare function parseResolve(text: string): {
192
+ hostname: string;
193
+ port: number;
194
+ txt: Record<string, string>;
195
+ } | undefined;