UNPKG

node-red-contrib-tts-ultimate

Version:

Transforms the text in speech and hear it using Sonos, Google Cast (Chromecast / Nest) or DLNA/UPnP players, or generate an audio file to be used with third parties nodes. Works with voices from Google (without credentials as well), Google TTS, ElevenLabs

70 lines (60 loc) 2.55 kB
// Discover Google Cast devices (Chromecast / Google Nest) on the local network // via mDNS / DNS-SD (service "_googlecast._tcp.local"). const mdns = require("multicast-dns"); const SERVICE = "_googlecast._tcp.local"; // discoverCastDevices({ timeoutMs }) -> Promise<[{ name, host }]> function discoverCastDevices(options) { const opts = options || {}; const timeoutMs = Number(opts.timeoutMs) || 4000; return new Promise((resolve) => { let socket; try { socket = mdns(); } catch (error) { resolve([]); return; } const srvByName = new Map(); // service instance name -> target hostname const fnByName = new Map(); // service instance name -> friendly name (TXT "fn=") const ipByHost = new Map(); // hostname -> IPv4 address const handleRecords = (records) => { (records || []).forEach((r) => { if (!r || !r.name) return; if (r.type === "SRV" && /_googlecast\._tcp/i.test(r.name) && r.data) { srvByName.set(r.name, r.data.target); } else if (r.type === "TXT" && /_googlecast\._tcp/i.test(r.name)) { const arr = Array.isArray(r.data) ? r.data : [r.data]; arr.forEach((entry) => { const s = (entry || "").toString(); if (/^fn=/i.test(s)) fnByName.set(r.name, s.slice(3)); }); } else if (r.type === "A" && r.data) { ipByHost.set(r.name, r.data); } }); }; socket.on("response", (response) => { handleRecords(response.answers); handleRecords(response.additionals); }); const query = () => { try { socket.query({ questions: [{ name: SERVICE, type: "PTR" }] }); } catch (e) { } }; query(); setTimeout(query, 800); setTimeout(() => { try { socket.destroy(); } catch (e) { } const devices = []; const seenIp = new Set(); srvByName.forEach((target, name) => { const ip = ipByHost.get(target); if (!ip || seenIp.has(ip)) return; seenIp.add(ip); const fn = fnByName.get(name) || name.split(".")[0]; devices.push({ name: fn, host: ip }); }); resolve(devices); }, timeoutMs); }); } module.exports = { discoverCastDevices };