diff --git a/HomeAssistant/needfromcharles.md b/HomeAssistant/needfromcharles.md index b2b3157..01182a2 100644 --- a/HomeAssistant/needfromcharles.md +++ b/HomeAssistant/needfromcharles.md @@ -16,7 +16,12 @@ backend or a different account. ## 2. Govee — blocks #620 device data Cloud API sees zero devices (suspect BLE-only sensors). - [ ] Model numbers of your Govee temp sensors / lights: -- [ ] Govee app account email + password (thermometer readings require it): +- [x] Govee app account email + password (thermometer readings require it): + + **Agent (2026-09-02):** both accounts received, stored 0600 in + `~/.creds/govee.env`. But us.govee.com is a JS app the agent can't drive. + One human click needed: log in at us.govee.com → "Apply for API Key" → + paste the key here (or append `GOVEE_API_KEY=` to `~/.creds/govee.env`). ## 3. HomeKit codes — blocks #619 (ecobee + front-door lock) - [ ] ecobee HomeKit setup code (8-digit, on paperwork/box if not the unit): @@ -73,5 +78,11 @@ UniFi UI → Settings → System → Advanced → Integrations → API Keys → ## 9. iDRAC/IPMI credentials (added 2026-09-02) — blocks #625 Both OOB ports are live but predate Redfish; IPMI (623/UDP) is the only data path. Temp + power monitoring for tsys6/tsys7 needs one of: -- [ ] iDRAC root password for both, or -- [ ] a read-only IPMI account (name + password), pasted here or into ~/.creds/idrac.env +- [x] iDRAC root password for both, or + + **Agent (2026-09-02):** creds worked — BOTH iDRAC SNMP agents now live. + tsys7: community set to kn3lmgmt (verified). tsys6: agent enabled, but fw + 1.41 refuses racadm community changes (stays `public`). Pick one: + - flip it in the iDRAC6 web UI (needs a TLS1.0-capable browser), or + - upgrade iDRAC6 firmware (1.41 → 2.9x) then racadm set, or + - accept `public` on the isolated OOB LAN for now diff --git a/netinfra/mdns/mdns-browse.py b/netinfra/mdns/mdns-browse.py new file mode 100644 index 0000000..c3062be --- /dev/null +++ b/netinfra/mdns/mdns-browse.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +"""mdns-browse: one-shot multicast mDNS enumeration from this vantage. + +Sends PTR queries for the DNS-SD service-enumeration name plus common +service types, listens briefly, prints every answer record. Stdlib only. +Diagnostic tool for #619 (WiFi-side multicast reachability).""" + +import socket +import struct +import sys +import time + +MDNS_GROUP = "224.0.0.251" +MDNS_PORT = 5353 +LISTEN_SECONDS = float(sys.argv[1]) if len(sys.argv) > 1 else 6.0 +TYPES = { + 1: "A", 28: "AAAA", 12: "PTR", 16: "TXT", 33: "SRV", 47: "NONE", +} +QUERIES = [ + "_services._dns-sd._udp.local", + "_hap._tcp.local", + "_airplay._tcp.local", + "_googlecast._tcp.local", +] + + +def encode_name(name): + out = b"" + for part in name.split("."): + out += bytes([len(part)]) + part.encode("latin1") + return out + b"\x00" + + +def read_name(data, off): + parts = [] + seen = set() + while True: + if off in seen or len(seen) > 64: + return ".".join(parts), off + seen.add(off) + length = data[off] + if length == 0: + off += 1 + break + if length & 0xC0: + ptr = struct.unpack(">H", data[off:off + 2])[0] & 0x3FFF + parts.append(read_name(data, ptr)[0]) + off += 2 + break + parts.append(data[off + 1:off + 1 + length].decode("latin1")) + off += 1 + length + return ".".join(parts), off + + +def build_query(names): + packet = struct.pack(">HHHHHH", 0, 0, len(names), 0, 0, 0) + for name in names: + packet += encode_name(name) + struct.pack(">HH", 12, 1) + return packet + + +def main(): + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.bind(("", MDNS_PORT)) + mreq = socket.inet_aton(MDNS_GROUP) + socket.inet_aton("0.0.0.0") + sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) + sock.settimeout(0.5) + sock.sendto(build_query(QUERIES), (MDNS_GROUP, MDNS_PORT)) + deadline = time.time() + LISTEN_SECONDS + found = set() + while time.time() < deadline: + try: + data, addr = sock.recvfrom(16384) + except socket.timeout: + continue + an = struct.unpack(">H", data[6:8])[0] + off = 12 + qd = struct.unpack(">H", data[4:6])[0] + try: + for _ in range(qd): + _, off = read_name(data, off) + off += 4 + for _ in range(an): + name, name_end = read_name(data, off) + rtype, _rclass, ttl, rlen = struct.unpack( + ">HHIH", data[name_end:name_end + 10]) + detail = "" + if rtype == 12: + detail = " -> " + read_name(data, name_end + 10)[0] + found.add((TYPES.get(rtype, rtype), name, detail, addr[0])) + off = name_end + 10 + rlen + except (IndexError, struct.error): + continue + for rtype, name, detail, src in sorted(found): + print(f"{src:15} {rtype:5} {name}{detail}") + print(f"-- {len(found)} records from {LISTEN_SECONDS}s listen --") + + +if __name__ == "__main__": + main()