{"id":50351203,"url":"https://github.com/marcomc/ha-mqtt-agent","last_synced_at":"2026-05-29T21:01:38.398Z","repository":{"id":358876938,"uuid":"1241812239","full_name":"marcomc/ha-mqtt-agent","owner":"marcomc","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-19T11:34:05.000Z","size":65,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-19T14:36:23.722Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/marcomc.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"docs/roadmap.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-17T21:03:44.000Z","updated_at":"2026-05-19T11:29:24.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/marcomc/ha-mqtt-agent","commit_stats":null,"previous_names":["marcomc/ha-mqtt-agent"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/marcomc/ha-mqtt-agent","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcomc%2Fha-mqtt-agent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcomc%2Fha-mqtt-agent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcomc%2Fha-mqtt-agent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcomc%2Fha-mqtt-agent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marcomc","download_url":"https://codeload.github.com/marcomc/ha-mqtt-agent/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcomc%2Fha-mqtt-agent/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33670211,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-29T02:00:06.066Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-29T21:01:36.968Z","updated_at":"2026-05-29T21:01:38.368Z","avatar_url":"https://github.com/marcomc.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Home Assistant MQTT Agent\n\n## Table of Contents\n\n- [Overview](#overview)\n- [Runtime Flow](#runtime-flow)\n- [Features](#features)\n- [Requirements](#requirements)\n- [Quick Install](#quick-install)\n- [Install Modes](#install-modes)\n- [Installation](#installation)\n- [Configuration](#configuration)\n- [Authorizing Wi-Fi SSID Access](#authorizing-wi-fi-ssid-access)\n- [Usage](#usage)\n- [Home Assistant Entities](#home-assistant-entities)\n- [Running as a macOS Service](#running-as-a-macos-service)\n- [Troubleshooting](#troubleshooting)\n- [Development](#development)\n- [Release Notes](#release-notes)\n- [License](#license)\n\n## Overview\n\n`Home Assistant MQTT Agent` publishes local Mac telemetry to an MQTT broker\nusing Home Assistant MQTT discovery.\n\nThe current provider reads macOS AppleSmartBattery and user-space network\ntelemetry. It publishes current power in watts, keeps a persistent total energy\ncounter in kWh, and exposes battery, uptime, Wi-Fi, Ethernet, and external ping\nchecks as Home Assistant entities.\n\nThe default broker host is `mqtt.example.local:1883`, but every MQTT setting is\nconfigurable so the tool can be reused with any Home Assistant setup that has\nMQTT discovery enabled.\n\nThe current release is telemetry-only.\n\n## Runtime Flow\n\nThis is the runtime path after `make install` installs and starts the\nper-user LaunchAgent.\n\n```mermaid\nflowchart LR\n  accTitle: Runtime telemetry flow\n  accDescr: Shows how the LaunchAgent publishes Mac telemetry to Home Assistant through MQTT.\n  install[\"make install\"] --\u003e plist[\"Write LaunchAgent plist\"]\n  plist --\u003e launchd[\"macOS launchd starts ha-mqtt-agent run\"]\n  launchd --\u003e battery[\"Read AppleSmartBattery telemetry\"]\n  launchd --\u003e network[\"Read network telemetry\"]\n  network --\u003e helper{\"Wi-Fi helper authorized?\"}\n  helper --\u003e|Yes| ssid[\"Read SSID through CoreWLAN\"]\n  helper --\u003e|No| fallback[\"Use user-space fallback values\"]\n  battery --\u003e energy[\"Update local kWh state\"]\n  ssid --\u003e payload[\"Build MQTT state payload\"]\n  fallback --\u003e payload\n  energy --\u003e payload\n  payload --\u003e mqtt[\"Publish discovery and state to MQTT broker\"]\n  mqtt --\u003e ha[\"Home Assistant updates MQTT entities\"]\n```\n\n## Features\n\n- Home Assistant MQTT discovery for all sensors.\n- Current power sensor with `device_class: power`, `state_class: measurement`,\n  and unit `W`.\n- Total energy sensor with `device_class: energy`,\n  `state_class: total_increasing`, and unit `kWh`.\n- Battery charge, maximum capacity, raw maximum capacity, cycle count, and\n  status sensors.\n- Battery temperature, battery virtual temperature, and system uptime sensors.\n- Wi-Fi SSID, Wi-Fi signal in `dBm`, and Wi-Fi signal as a percentage.\n- Wi-Fi BSSID, local IPv4 addresses, default gateway, gateway MAC, and a\n  configurable home-network presence binary sensor.\n- Optional latitude, longitude, location accuracy, and geocoded location\n  sensors.\n- Active wired Ethernet interface count and active interface list.\n- Configurable external ping latency sensors, with Google and Cloudflare DNS\n  targets enabled by default.\n- Persistent local energy accumulator that survives restarts.\n- Packaged command-line app exposed as `ha-mqtt-agent`.\n\nOnly macOS is supported in this release. Linux and Raspberry Pi hosts\nneed a future provider that does not depend on AppleSmartBattery telemetry.\n\n## Requirements\n\nFor users:\n\n- Python `3.11` or newer\n- `make`\n- Xcode Command Line Tools with `swiftc` and `codesign` for the Wi-Fi SSID\n  helper\n- macOS with `ioreg` for the current telemetry provider\n- an MQTT broker reachable from the Mac\n- Home Assistant MQTT integration with discovery enabled\n\nFor maintainers:\n\n- `markdownlint`\n- `shellcheck`\n- Xcode Command Line Tools with `swiftc`\n\n## Quick Install\n\nClone the repository on the Mac you want to publish, then run the installer:\n\n```bash\ngit clone \u003crepo-url\u003e\ncd ha-mqtt-agent\n./scripts/install.sh\n```\n\nThe script is a user-friendly wrapper around `make install`. It checks the\nlocal prerequisites, installs the standalone runtime, creates the config\ntemplate if needed, and starts the per-user LaunchAgent.\n\nEdit the MQTT and device settings:\n\n```bash\n$EDITOR ~/.config/ha-mqtt-agent/config.toml\n```\n\nAt minimum, set:\n\n```toml\nmqtt_host = \"mqtt.example.local\"\ndevice_id = \"workstation\"\ndevice_name = \"Workstation\"\n```\n\nThen restart the service:\n\n```bash\nmake restart-agent\n```\n\n## Install Modes\n\nThe current supported install mode is a source install for users who can build\nand locally sign the Wi-Fi helper on their Mac. There is not yet a prebuilt,\nDeveloper ID signed, notarized installer for non-developer users.\n\n### Current Source Install\n\n`./scripts/install.sh` and `make install` expect local build tools:\n\n- Python `3.11` or newer for the packaged CLI runtime. The installer\n  auto-detects a compatible `python3.14`, `python3.13`, `python3.12`,\n  `python3.11`, or `uv`-managed Python before falling back to `python3`.\n- `make` to run the project install targets.\n- `swiftc` to compile the Wi-Fi SSID helper app.\n- `codesign` to apply the helper's local ad-hoc signature.\n\nThe source install builds the helper during installation, then signs it with an\nad-hoc local signature. This is enough for the local Mac to run the helper and\nrequest the macOS Location permission needed to read the current Wi-Fi SSID. It\nis not a public distribution signature and does not require an Apple Developer\naccount.\n\nThe installer checks for these tools before installing. On macOS, `make`,\n`swiftc`, and `codesign` are normally provided by Xcode Command Line Tools:\n\n```bash\nxcode-select --install\n```\n\nAfter installing the command line tools, rerun:\n\n```bash\n./scripts/install.sh\n```\n\nIf more than one Python is installed, you can still force the standalone runtime\ninterpreter:\n\n```bash\nmake restart-agent STANDALONE_PYTHON=/path/to/python3.12\n```\n\n### Future Prebuilt Install\n\nA non-developer install path is planned but not shipped yet. That path should\nprovide a prebuilt helper app signed with the maintainer's Developer ID\nApplication certificate and notarized before release. In that future mode,\nusers should not need `swiftc`, local helper compilation, or local signing.\n\nThe backlog item is tracked as [HMA-009](TODO.md#hma-009-prebuilt-notarized-macos-installer).\n\n## Installation\n\nFor the complete install, use the Make target directly:\n\n```bash\nmake install\n```\n\n`make install`:\n\n- builds the Wi-Fi SSID helper app from `macos/WifiHelper/`\n- signs the helper locally with an ad-hoc signature and the Location entitlement\n- asks macOS to authorize the helper for Wi-Fi SSID access\n- creates a standalone virtual environment in\n  `~/.local/share/ha-mqtt-agent/venv`\n- installs the packaged CLI into that standalone runtime\n- does not require `uv` at runtime\n- links the command to `~/.local/bin/ha-mqtt-agent`\n- installs a config template to `~/.config/ha-mqtt-agent/config.toml` if it\n  does not exist yet\n- installs and starts the per-user macOS LaunchAgent\n\nUse `make install-cli` only when you want the standalone CLI runtime without\ninstalling the config, Wi-Fi helper, or LaunchAgent.\n\nIf `~/.local/bin` is not on your `PATH`, `make check-deps` prints the shell\nsnippet to add it.\n\nThis installs a per-user macOS LaunchAgent named\n`com.marcomc.ha-mqtt-agent`.\n\n### Editable Development Install\n\n```bash\nmake install-dev\n```\n\nThis points `~/.local/bin/ha-mqtt-agent` at the project-local `.venv` so source\nedits are reflected immediately.\n\n## Configuration\n\nThe CLI reads optional config from:\n\n- `~/.config/ha-mqtt-agent/config.toml`\n- or the file passed with `--config`\n\nStart from the example file in this repository:\n\n- [config.toml.example](config.toml.example)\n- [config.schema.json](config.schema.json)\n\nExample:\n\n```toml\nmqtt_host = \"mqtt.example.local\"\nmqtt_port = 1883\ndevice_id = \"workstation\"\ndevice_name = \"Workstation\"\nsample_interval_seconds = 5\nexpire_after_seconds = 15\nnetwork_interval_seconds = 60\nping_timeout_seconds = 1\nwifi_helper_path = \"~/.local/share/ha-mqtt-agent/HaMqttAgentWifiHelper.app/Contents/MacOS/HaMqttAgentWifiHelper\"\nstate_path = \"~/.local/state/ha-mqtt-agent/state.json\"\nverbose = false\nhome_ssids = []\nhome_ipv4_cidrs = []\nhome_gateways = []\nhome_bssids = []\nhome_gateway_macs = []\npublish_location = false\nlocation_timeout_seconds = 3\n\nping_targets = [\n  { id = \"cloudflare_dns\", host = \"1.1.1.1\", name = \"Cloudflare DNS\" },\n  { id = \"cloudflare_dns_secondary\", host = \"1.0.0.1\", name = \"Cloudflare DNS secondary\" },\n  { id = \"google_dns\", host = \"8.8.8.8\", name = \"Google DNS\" },\n  { id = \"google_dns_secondary\", host = \"8.8.4.4\", name = \"Google DNS secondary\" }\n]\n```\n\n`sample_interval_seconds` defaults to `5` and may be set as low as `1`.\n`expire_after_seconds` defaults to `15`, so Home Assistant marks sensors\nunavailable after about three missed publishes.\n`network_interval_seconds` defaults to `60`; Wi-Fi, Ethernet, and ping probes\nare cached between those slower network samples while the normal telemetry loop\nkeeps publishing. `ping_timeout_seconds` defaults to `1`.\nAfter an MQTT publish failure, the service uses a lightweight broker connection\nprobe before trying the next full telemetry publish. This keeps local sampling\nquiet while the broker is unreachable and lets the LaunchAgent resume promptly\nwhen MQTT connectivity returns.\nIf `mqtt_client_id` is omitted, the runtime MQTT client ID is derived from\n`device_id`; one-shot publish commands add a short process suffix so they do\nnot disconnect the background LaunchAgent while you are debugging.\n\nEach `ping_targets` entry creates a separate Home Assistant latency sensor named\nfrom its `id`. To configure a longer list quickly, `ping_targets` can also be a\nplain host list, for example:\n\n```toml\nping_targets = [\"192.168.1.1\", \"1.1.1.1\", \"8.8.8.8\", \"9.9.9.9\"]\n```\n\nHome-network presence is published as a binary sensor named\n`Home network present`. It turns on when any configured home SSID, BSSID, IPv4\nCIDR, default gateway, or gateway MAC matches the current local network sample.\nLeave lists empty to disable that specific match method.\n\nFor example:\n\n```toml\nhome_ssids = [\"Home WiFi\", \"Home WiFi 5G\"]\nhome_ipv4_cidrs = [\"192.168.1.0/24\"]\nhome_gateways = [\"192.168.1.1\"]\n```\n\n`publish_location` defaults to `false`. Set it to `true` only when you want this\nMac to publish latitude, longitude, and horizontal accuracy to Home Assistant.\nLocation data uses the same macOS Location Services permission as the Wi-Fi\nhelper. The agent publishes both standalone latitude/longitude sensors and an\nMQTT `device_tracker` named `Location` with GPS attributes, so Home Assistant\ncan place the Mac on map cards. If macOS temporarily reports that the location\nis unknown after one valid fix has been seen, the agent keeps publishing the\nlast known coordinates and marks `Location cached` as on. `Location last seen`\nand the `device_tracker` `last_seen` attribute show when the coordinate was last\nrefreshed. `Location error` shows the current CoreLocation error, if any. The\nsame setting also enables a `Geocoded location` sensor built from macOS reverse\ngeocoding, with address-style attributes such as country, locality, postal code,\nstreet, areas of interest, and time zone. When the coordinate is cached because\nCoreLocation is temporarily unavailable, the geocoded location is reused only\nwith that cached coordinate and is marked with `Geocoded location cached`.\n\nOn newer macOS versions, SSID access requires the macOS Location permission for\nthe bundled Wi-Fi helper app. Signal strength is still published from the\nfallback user-space probes even before the SSID helper is authorized.\n\n## Authorizing Wi-Fi SSID Access\n\nmacOS treats Wi-Fi SSID, BSSID, and geographic coordinates as location-adjacent\ndata. `make install` installs a small signed helper app at:\n\n```text\n~/.local/share/ha-mqtt-agent/HaMqttAgentWifiHelper.app\n```\n\n`make install` runs the authorization command automatically after\ninstalling the helper. Approve the Location Services prompt for **Home\nAssistant MQTT Agent Wi-Fi Helper** when it appears.\n\nIf the prompt does not appear, or if the helper was rebuilt after macOS had\nalready recorded an older local signature, run:\n\n```bash\nha-mqtt-agent authorize-wifi\n```\n\nThen open **System Settings \u003e Privacy \u0026 Security \u003e Location Services** and\nenable that helper there if needed, then restart the LaunchAgent:\n\n```bash\nmake restart-agent\n```\n\nWithout that permission, macOS may return `\u003credacted\u003e` for the SSID and omit\nBSSID or location while still allowing the app to publish Wi-Fi signal strength.\n\nFor brokers with authentication, set:\n\n```toml\nmqtt_username = \"homeassistant\"\nmqtt_password = \"change-me\"\n```\n\nRestart the LaunchAgent after changing the installed config:\n\n```bash\nmake restart-agent\n```\n\nChanging `device_id` changes MQTT topics and Home Assistant unique IDs, so Home\nAssistant will discover a new device. Remove the old MQTT device from Home\nAssistant if you no longer need it.\n\n## Usage\n\nInspect the resolved configuration:\n\n```bash\nha-mqtt-agent info\n```\n\nRead one local telemetry sample without publishing:\n\n```bash\nha-mqtt-agent sample\nha-mqtt-agent sample --json\n```\n\nPublish Home Assistant discovery and one state update:\n\n```bash\nha-mqtt-agent publish-once\n```\n\nRun continuously:\n\n```bash\nha-mqtt-agent run\n```\n\n## Home Assistant Entities\n\nThe discovery payloads create one Home Assistant device named by `device_name`\nwith these entities:\n\n- Power: current power in `W`.\n- Energy: accumulated energy in `kWh`, suitable for the Energy dashboard.\n- Battery: current battery charge in `%`.\n- Battery maximum capacity: reported maximum battery capacity in `%`.\n- Battery maximum capacity mAh: raw maximum charge capacity in `mAh`.\n- Battery design capacity: design charge capacity in `mAh`.\n- Battery temperature: battery temperature in `°C`.\n- Battery virtual temperature: Apple battery virtual temperature in `°C`.\n- Battery cycle count.\n- Battery status: `charging`, `charged`, `plugged_in`, or `discharging`.\n- Uptime: system uptime in seconds.\n- Wi-Fi SSID.\n- Wi-Fi BSSID.\n- Wi-Fi signal in `dBm`.\n- Wi-Fi signal percent in `%`.\n- IPv4 addresses.\n- Default gateways.\n- Default gateway interfaces.\n- Gateway MACs.\n- Home network present.\n- Location device tracker for Home Assistant map cards.\n- Latitude, longitude, location accuracy, last seen time, cache state, and\n  location error when `publish_location` is enabled.\n- Geocoded location, geocoded cache state, and geocoded error when\n  `publish_location` is enabled.\n- Ethernet active count.\n- Ethernet active interfaces.\n- One ping latency sensor in `ms` for each configured `ping_targets` entry.\n\nThe energy entity is the one to add under Home Assistant's Energy dashboard.\nHome Assistant long-term statistics are fed by the `total_increasing` kWh\nsensor.\n\nSensors use `expire_after_seconds` in MQTT discovery. The default is `15`, so\nHome Assistant marks them unavailable after about three missed publishes.\nAfter MQTT publish failures, the LaunchAgent retries with a lightweight broker\nconnection probe before doing another full telemetry sample. The recovery probe\nkeeps running while the broker is unreachable and the retry backoff is capped at\n60 seconds.\n\nCPU, GPU, memory, SSD, palm-rest, and fan sensors are not exposed by the\ndefault LaunchAgent because macOS does not provide those detailed thermal\nchannels to this app without a privileged sensor source. The default publisher\nstays user-scoped and does not require root.\n\nFor the complete Home Assistant setup path, including MQTT discovery checks and\nEnergy dashboard configuration, see\n[Home Assistant Setup](docs/home-assistant-setup.md).\n\n## Running as a macOS Service\n\nThe supported background mode is a per-user LaunchAgent, not a root\nLaunchDaemon. The app reads macOS user-space battery telemetry, stores state in\nthe user's home directory, and does not need root privileges.\n\nInstall and start it:\n\n```bash\nmake install\n```\n\nCheck it:\n\n```bash\nmake agent-status\n```\n\nRestart it:\n\n```bash\nmake restart-agent\n```\n\nUse this after editing `~/.config/ha-mqtt-agent/config.toml`; the LaunchAgent\nloads config only when the process starts.\n\nStop and remove it:\n\n```bash\nmake uninstall-agent\n```\n\nThe generated plist is written to\n`~/Library/LaunchAgents/com.marcomc.ha-mqtt-agent.plist`. Logs are written to\n`~/Library/Logs/ha-mqtt-agent/`.\n\n## Troubleshooting\n\nCheck the installed configuration:\n\n```bash\nha-mqtt-agent info\n```\n\nPublish one sample manually:\n\n```bash\nha-mqtt-agent publish-once\n```\n\nCheck the background service:\n\n```bash\nmake agent-status\ntail -n 100 ~/Library/Logs/ha-mqtt-agent/err.log\n```\n\nConfirm that the Mac can reach the MQTT broker:\n\n```bash\nnc -vz mqtt.example.local 1883\n```\n\nIf Home Assistant still shows stale values, confirm the discovery payload has\nthe expected `expire_after` value and restart the LaunchAgent after config\nchanges.\n\nIf Home Assistant shows the Mac as unavailable and the LaunchAgent is still\nrunning, check whether the log contains MQTT reachability errors such as\n`No route to host`, DNS lookup failures, or connection-loss messages. The\nservice will keep using lightweight recovery probes until the broker is\nreachable again.\n\n## Development\n\nSync the environment and run the default quality gate:\n\n```bash\nmake check\n```\n\nCommon commands:\n\n```bash\nmake sync\nmake test\nmake lint\nmake run\n```\n\nFuture work is tracked in [TODO.md](TODO.md) and expanded in\n[Roadmap](docs/roadmap.md).\n\n## Release Notes\n\nBefore tagging a release:\n\n1. update the version in `pyproject.toml`\n2. update `src/ha_mqtt_agent/__init__.py`\n3. add release notes to `CHANGELOG.md`\n4. verify `make check`\n\n## License\n\nThis project is released under the MIT License. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcomc%2Fha-mqtt-agent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarcomc%2Fha-mqtt-agent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcomc%2Fha-mqtt-agent/lists"}