{"id":50954545,"url":"https://github.com/technoo10201/ha-apsystems-ds3","last_synced_at":"2026-06-18T05:01:23.114Z","repository":{"id":363135085,"uuid":"1262082450","full_name":"technoo10201/ha-apsystems-ds3","owner":"technoo10201","description":"Home Assistant integration for APsystems DS3 micro-inverters via CC2530+CC2591 Zigbee dongle (Kadsol firmware). No ECU, no cloud.","archived":false,"fork":false,"pushed_at":"2026-06-07T16:51:58.000Z","size":454,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-07T17:04:41.927Z","etag":null,"topics":["apsystems","cc2530","ds3","hacs","home-assistant","solar","zigbee"],"latest_commit_sha":null,"homepage":null,"language":"C","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/technoo10201.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-07T14:50:51.000Z","updated_at":"2026-06-07T16:52:02.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/technoo10201/ha-apsystems-ds3","commit_stats":null,"previous_names":["technoo10201/hacs-aps-ds3"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/technoo10201/ha-apsystems-ds3","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/technoo10201%2Fha-apsystems-ds3","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/technoo10201%2Fha-apsystems-ds3/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/technoo10201%2Fha-apsystems-ds3/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/technoo10201%2Fha-apsystems-ds3/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/technoo10201","download_url":"https://codeload.github.com/technoo10201/ha-apsystems-ds3/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/technoo10201%2Fha-apsystems-ds3/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34476728,"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-06-18T02:00:06.871Z","response_time":128,"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":["apsystems","cc2530","ds3","hacs","home-assistant","solar","zigbee"],"created_at":"2026-06-18T05:01:21.172Z","updated_at":"2026-06-18T05:01:23.108Z","avatar_url":"https://github.com/technoo10201.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# APsystems DS3 — Home Assistant HACS plugin\n\nHome Assistant custom component that talks **directly** to a CC2530 USB-serial\nZigbee dongle flashed with the proprietary **Kadsol** firmware\n(`CC2530ZNP-with-SBL.hex`) to poll **APsystems DS3** micro-inverters. No ECU,\nno cloud, no ESP32 bridge.\n\n\u003e **v0.2.0 — first HACS-ready release.** Install via HACS Custom\n\u003e Repositories (see [HACS install](#hacs-install) below). The repo lives\n\u003e on `main` (squash-merged from `develop`); active development continues\n\u003e on `develop` and lands as future tagged releases.\n\n![Integration hub view — 5 DS3 inverters paired and reporting](img/screen_1.png)\n\n*The integration in Home Assistant after pairing 5 DS3 inverters — each\ninverter is its own device with 15 entities, fed live by a single CC2530\ndongle.*\n\n---\n\n## Why\n\nAPsystems DS3 micro-inverters speak a proprietary Zigbee dialect that ZHA and\nZigbee2MQTT do not understand. The widely-used\n[`patience4711/ESP32-read-APS-inverters`](https://github.com/patience4711/ESP32-read-APS-inverters)\nfirmware solves it on an ESP32 + CC2530 combo and re-exposes the data over\nMQTT/HTTP. This project ports that whole protocol stack to pure async Python\nso you can plug the CC2530 module straight into your Home Assistant host —\nthe ESP32 disappears from the chain.\n\n---\n\n## Hardware shopping list\n\n### 1. Zigbee module (mandatory)\n\nA bare **CC2530 + CC2591** module is enough. Reference build used by the\nauthor:\n\n- [CC2530 + CC2591 module on AliExpress](https://fr.aliexpress.com/item/1005002387334208.html)\n  (~ 5,40 €). The CC2591 is a PA/LNA front-end that gives ~100 m line-of-sight\n  range — important when the inverters live in the roof and the host stays in\n  the living room.\n\nThe module ships **blank** (Texas Instruments factory firmware) — you must\nflash the Kadsol firmware before it can pair with APsystems inverters.\n\n\u003e ⚠️ The module is **not** a USB dongle. It is a 3.3 V serial peripheral. You\n\u003e will need a USB-TTL bridge for everyday use, and a one-shot programmer for\n\u003e the initial flash. See below.\n\n### 2. USB-TTL bridge (mandatory, for everyday use)\n\nAny 3.3 V USB-UART adapter works. Reference build:\n\n- [CH340E USB Type-C adapter on AliExpress](https://fr.aliexpress.com/item/1005006603844179.html)\n  (~ 2 €). Same `ch341` Linux driver as the classic CH340G, but with a\n  USB-C connector — handier than the usual USB-A dongles. Verify the board\n  exposes a **3.3 V** output (some CH340E breakouts default to 5 V; that\n  would damage the CC2530).\n\nOther tested chipsets:\n\n- **CP2102** (Silicon Labs) — `cp210x` driver, ubiquitous on Linux.\n- **CH340 / CH340G / CH9102** — `ch341` / `ch34x` driver.\n- **FT232RL** (FTDI) — `ftdi_sio` driver.\n\nWhat matters:\n\n- Output **3.3 V logic** on TX/RX (most boards have a jumper for 3.3 V vs 5 V).\n  Forcing 5 V on the CC2530 will damage it.\n- The bridge enumerates as `/dev/ttyUSB*` on Linux. We map it into Home\n  Assistant by its stable `/dev/serial/by-id/...` symlink.\n\n### 3. Programmer (one-shot, for the initial flash)\n\nA **~ 4–5 € ESP32-C3 dev board** from AliExpress. Native 3.3 V GPIOs\nmatch the CC2530 logic level exactly — no level shifter, no USB-UART\nadapter, no TI tooling. Compatible with any ESP32-C3 variant (DevKit M,\nSuper Mini, XIAO ESP32-C3, …); pinout is configurable in\n`platformio.ini`.\n\nThe bundled standalone flasher at\n[`tools/cc2530-flasher/`](./tools/cc2530-flasher/) is a PlatformIO\nproject that embeds the Kadsol firmware in the ESP32 binary. Once the\nESP32-C3 has the sketch, the rest is `pio device monitor`, type `FLASH`,\nwatch the LED turn green. Armed-mode protection: the CC2530 keeps\nrunning normally unless you explicitly send `FLASH` on the serial\nconsole, so an accidental ESP32 reboot can never wipe the dongle.\n\nYou only need the ESP32-C3 once for the initial flash — but if you\nleave it wired permanently you can re-flash the CC2530 over SSH any\ntime. Step-by-step procedure below at **Step 2**.\n\n\u003e Other community paths exist (TI CC Debugger via\n\u003e [`cc-tool`](https://github.com/dashesy/cc-tool), Raspberry Pi GPIO via\n\u003e [`jmichault/flash_cc2531`](https://github.com/jmichault/flash_cc2531),\n\u003e Arduino + [`CCLib`](https://github.com/wavesoft/CCLib)) — they all\n\u003e drive the same CC2530 debug protocol. They are not documented in this\n\u003e repo because they have not been tested as part of this project; reach\n\u003e for them only if you already own and know the hardware.\n\n### Bonus tip — CC2591 PA power rail\n\nIf you observe USB brownouts during the first CC2530 channel scan (the\ndongle disappears from `dmesg` with `disconnect, errno -19` mid-init),\nthe CC2591 PA front-end is drawing more current than your USB-TTL bridge\ncan sustain on its built-in 3.3 V regulator. Wiring the CC2591's `VCC`\nto an external 3.3 V rail — the **same ESP32-C3** you may already have\non the bench for flashing is a clean source — fixes it. Share `GND`\nwith the USB-TTL bridge so signals reference the same ground. The CH340E\nbridge then only has to drive its UART pins, not the radio's peak\ncurrent.\n\n---\n\n## Wiring\n\nFor the **flashing wiring** (ESP32-C3 ↔ CC2530 debug pins) see\n[`tools/cc2530-flasher/README.md`](./tools/cc2530-flasher/).\n\nFor **everyday operation** (USB-TTL ↔ module):\n\n```\n   USB-TTL 3.3 V                CC2530+CC2591 module\n   ┌──────────┐                 ┌──────────────────┐\n   │ VCC  3V3 │ ──────────►    │ VCC              │\n   │ GND      │ ──────────►    │ GND              │\n   │ TX       │ ──────────►    │ RX  (P0_2)       │\n   │ RX       │ ◄──────────    │ TX  (P0_3)       │\n   └──────────┘                 └──────────────────┘\n```\n\nBaud rate is 115 200 / 8N1 (hard-coded in the Kadsol firmware and in our\nZNP layer).\n\n\u003e ✋ Do **not** wire the USB-TTL `RTS` / `DTR` to the module's `RESET` pin\n\u003e unless you know what you are doing — some adapters pulse those at every\n\u003e port open and will reboot the dongle mid-poll.\n\n---\n\n## Flashing the Kadsol firmware\n\n### Step 1 — Get the firmware\n\nThe Kadsol build is `CC2530ZNP-with-SBL.hex`. Convenient mirrors:\n\n- [`patience4711/read-APSystems-YC600-QS1-DS3` → `cc25xx_firmware.zip`](https://github.com/patience4711/read-APSystems-YC600-QS1-DS3/blob/main/cc25xx_firmware.zip)\n  — what the reference ESP32 firmware was built against.\n- [Zigbee2MQTT community bundle](https://github.com/Koenkk/zigbee2mqtt/files/10193677/discord-09-12-2022.zip)\n  — same file, more discoverable.\n\nExtract the archive; you want the `.hex` file (not the `.bin` companion).\n\n### Step 2 — Flash from an ESP32-C3 — *tested*\n\nA ~ 4–5 € ESP32-C3 dev board acts as a software CC Debugger, with the\nKadsol firmware embedded directly in the ESP32 binary. No TI tools, no\nCC Debugger, no Raspberry Pi.\n\nThe complete PlatformIO project is bundled in this repo at\n[`tools/cc2530-flasher/`](./tools/cc2530-flasher/). Once the ESP32-C3\nhas the sketch, the rest is `pio device monitor`, type `FLASH`, watch\nthe LED turn green.\n\n```\nESP32-C3                   CC2530 + CC2591\n========                   ===============\nGPIO 3   ───── 5 cm ─────► DD   (Debug Data)\nGPIO 4   ───── 5 cm ─────► DC   (Debug Clock)\nGPIO 5   ───── 5 cm ─────► RST  (Reset)\n3V3      ───────────────► VCC  (3.3 V only — never 5 V)\nGND      ───────────────► GND\n```\n\n```bash\npip install platformio        # or: pipx install platformio\ncd tools/cc2530-flasher\npio run -t upload             # flashes the ESP32 over USB-CDC\npio device monitor            # 115200 baud — prompt waits for FLASH\n# Type FLASH + Enter. Blue LED pulses during write/verify.\n# Green LED solid = success. Red blink = failure (see serial output).\n```\n\nThe ESP32-C3 boots in **armed mode** — its DD / DC / RST pins are\nhigh-impedance until the `FLASH` command is received, so the CC2530\nkeeps running normally even with the ESP32 wired in permanently. A\nuseful side effect: if you leave the ESP32 in your install, you can\nre-flash the CC2530 over SSH later (open `pio device monitor` from the\nHA host).\n\nSee [`tools/cc2530-flasher/README.md`](./tools/cc2530-flasher/) for the\nfull reference (alternate ESP32 envs, pinout overrides, troubleshooting,\nregenerating the embedded firmware blob from a different `.hex`).\n\n### Step 3 — Verify\n\nAfter flashing, disconnect the ESP32-C3's 3 V3 rail from the CC2530 (or\nunwire it completely if you don't plan to leave it in for remote\nre-flashing), connect the USB-TTL bridge for everyday operation, and\nsanity-check the module from a terminal:\n\n```bash\n# Send a \"ZB_READ_CONFIGURATION (item 0)\" ping: FE 00 27 00 27\nprintf '\\xFE\\x00\\x27\\x00\\x27' | sudo tee /dev/ttyUSB0 \u003e /dev/null\nsleep 0.5\nsudo cat /dev/ttyUSB0 | xxd | head\n```\n\nA live dongle replies with a `FE 01 66 05 ...` packet. Silence means either\nwrong wiring (RX/TX swapped) or wrong voltage on VCC.\n\n---\n\n## Validating the wiring (CLI, no Home Assistant required)\n\nBefore installing the integration in Home Assistant, validate every layer of\nthe stack from a terminal with the bundled diagnostic CLI. It only needs the\n`pyserial-asyncio` Python package — same one Home Assistant will use.\n\n```bash\ngit clone git@github.com:technoo10201/ha-apsystems-ds3.git\ncd ha-apsystems-ds3\npython3 -m venv .venv\n.venv/bin/pip install -r requirements.txt\n# The wrapper auto-detects .venv and uses its Python.\n./scripts/aps-cli --help\n```\n\nSix sub-commands. Run them in this order on first setup:\n\n```bash\n# 1. Spot your bridge — look for /dev/serial/by-id/usb-...CH340E...\n./scripts/aps-cli list-ports\n\n# 2. End-to-end health check: opens the port, runs the CC2530 init\n#    sequence, pings the coordinator. Verdict: OK or FAIL.\n./scripts/aps-cli doctor --port /dev/serial/by-id/usb-..._CH340E-if00\n\n# 3. Pair an inverter by its 12-digit serial. Save the (serial, inv_id)\n#    mapping to a JSON file so you can poll it later without retyping.\n./scripts/aps-cli pair --port /dev/serial/by-id/usb-..._CH340E-if00 \\\n    --save inverters.json 408000158211\n\n# 4. Poll once and check the values look plausible (DC ≈ 30–40 V,\n#    AC ≈ 220–240 V, freq ≈ 50 Hz, temperature in a sane range).\n./scripts/aps-cli poll --port /dev/serial/by-id/usb-..._CH340E-if00 3A10\n\n# 5. Or run it in a loop every 60 s (Ctrl-C to stop). Useful before\n#    leaving for the day to confirm the wiring is stable.\n./scripts/aps-cli poll --port /dev/serial/by-id/usb-..._CH340E-if00 3A10 --loop\n```\n\nTwo offline helpers that don't touch the hardware:\n\n```bash\n# Decode a captured frame to debug a value you don't trust.\n./scripts/aps-cli decode 'FE0164010064FE034480001401D2...3969fefe'\n\n# Or read it from a file (handy for long captures).\n./scripts/aps-cli decode --from-file capture.hex\n```\n\nAdd `--json` to any sub-command to get machine-readable output (useful for\npiping into `jq`, automating a check, or sharing a frame for support).\n\nAdd `--debug` to see every ZNP frame transmitted and received on stderr —\nthis is what to attach to a bug report.\n\nOnce `doctor` says OK and a single `poll` returns sane values, the wiring is\nsound and you can move on to the Home Assistant install.\n\n---\n\n## Installing the Home Assistant integration\n\n\u003e Don't want to risk breaking your production Home Assistant during\n\u003e development? Spin up a **disposable second instance** on port 8124 with\n\u003e [`docker/test-instance/`](./docker/test-instance/) — turnkey\n\u003e `docker compose` + `rsync` workflow that leaves your prod 8123 untouched.\n\n### Manual install (works today)\n\nAssuming Home Assistant Container (Docker) and the dongle plugged into the\nhost:\n\n1. Find the stable serial path:\n   ```bash\n   ls -l /dev/serial/by-id/\n   # e.g. /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller-if00-port0\n   ```\n2. Make sure your Home Assistant container has access to it. In your\n   `docker-compose.yml`:\n   ```yaml\n   services:\n     homeassistant:\n       devices:\n         - /dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller-if00-port0:/dev/ttyUSB0\n   ```\n   Then `docker compose up -d`.\n3. Copy `custom_components/aps_zigbee/` (this directory) into your HA\n   config folder: `\u003cHA-config\u003e/custom_components/aps_zigbee/`.\n4. Restart Home Assistant.\n5. **Settings → Devices \u0026 Services → Add integration → APsystems Zigbee\n   (CC2530 direct)**.\n\n### HACS install\n\nThis repo ships as a **HACS Custom Repository** (not yet in the HACS\ndefault list — that needs a separate PR cycle against\n[`hacs/default`](https://github.com/hacs/default) and is on the roadmap\npost-v0.2.0).\n\n1. Open Home Assistant → **HACS** → top-right `⋮` menu → **Custom\n   repositories**.\n2. Paste the repo URL: `https://github.com/technoo10201/ha-apsystems-ds3`.\n3. Pick category **Integration** → click **Add**.\n4. The \"APsystems Zigbee (CC2530 direct)\" card appears in the HACS list.\n   Open it → **Download** → confirm the latest version → wait for HACS\n   to fetch.\n5. Restart Home Assistant.\n6. **Settings → Devices \u0026 Services → Add integration → APsystems Zigbee\n   (CC2530 direct)** to start the config flow described above.\n\nFuture versions are picked up automatically by HACS once a new GitHub\nRelease is tagged on `main`. Read the release notes before upgrading —\nbreaking changes will be flagged there.\n\n---\n\n## Configuring\n\n### Initial setup\n\nThe config flow asks for three things:\n\n| Field | What | Default |\n| --- | --- | --- |\n| Serial port | The `/dev/serial/by-id/...` path of your USB-TTL bridge | dropdown of detected ports |\n| ECU id | 12-hex-char identity the coordinator advertises to inverters | `D8A3011B9780` (matches the upstream firmware) |\n| Polling interval | Seconds between poll cycles | `60` |\n\n\u003e If you have already paired inverters against an older\n\u003e `ESP32-read-APS-inverters` setup, **keep the same ECU id** as that firmware\n\u003e used (default `D8A3011B9780`) — otherwise the inverters won't recognise\n\u003e the new coordinator and you'll have to re-pair them.\n\n### Adding inverters\n\nAfter setup, open **Settings → Devices \u0026 Services → APsystems Zigbee → Configure**:\n\n- **Pair a new inverter**: enter the 12-digit serial printed on the inverter,\n  add an optional friendly name. The Zigbee handshake runs live against the\n  dongle; success persists the pairing in the config entry.\n- **Forget a paired inverter**: remove it from the list.\n- **Polling interval**: change the cycle without re-pairing.\n\nYou can also automate pairing via the service `aps_zigbee.pair_inverter`\n(see **Developer Tools → Services**).\n\n---\n\n## Entities exposed\n\n![Per-inverter device view in Home Assistant](img/screen_2.png)\n\n*One inverter's device page in HA: device info (model, manufacturer,\nserial), the **Refresh now** and **Reboot inverter** controls, and the\nfull sensor list. Values show `Indisponible` at night — the inverter is\nunpowered (no sun, no PV), which is normal — see\n[Night-time behaviour](#night-time-behaviour) below.*\n\nFor each paired inverter the integration creates a device with up to 13\nentities:\n\n| Entity | Unit | Device class | State class |\n| --- | --- | --- | --- |\n| `DC voltage panel 1/2` | V | voltage | measurement |\n| `DC current panel 1/2` | A | current | measurement |\n| `Power panel 1/2/total` | W | power | measurement |\n| `Energy panel 1/2` | kWh | energy | total_increasing |\n| `AC voltage` | V | voltage | measurement |\n| `Grid frequency` | Hz | frequency | measurement |\n| `Temperature` | °C | temperature | measurement |\n| `Zigbee signal quality` | % | — | measurement (disabled by default) |\n\nEnergy uses `total_increasing` so the **Energy** dashboard handles\ninverter-side resets automatically.\n\nThe `Power total` sensor carries three extra attributes you can use in\ntemplates / automations:\n\n- `state` — one of `ok` / `stale` / `idle` / `dead` (see *Night-time behaviour*\n  below for the semantics).\n- `last_seen` — ISO timestamp of the last successful poll.\n- `consecutive_failures` — daytime failure streak; reset to zero on success\n  and at sunrise.\n\n### Per-inverter buttons\n\nIn addition to the sensors, each device exposes two buttons:\n\n- **Refresh now** — bypass the polling interval and the backoff window to\n  poll *this* inverter immediately. Useful when you want a fresh reading\n  without waiting (e.g. before reading the value in an automation).\n- **Reboot inverter** — send the proprietary reboot command. Same effect\n  as the `aps_zigbee.reboot_inverter` service, but scoped to the device\n  card in the UI.\n\n---\n\n## Night-time behaviour\n\nAPsystems DS3 micro-inverters power themselves directly from the PV panels:\nno sun, no power, no Zigbee chatter. **This is by design**, observed and\ndocumented by `patience4711` in the upstream ESP32 firmware project. An\ninverter that goes silent overnight is not a fault.\n\nThe integration uses your Home Assistant location (`sun.sun`) to suppress\nthe \"inverter dead\" detection while the sun is below the horizon:\n\n- During the day, after **5 consecutive failed polls** an inverter is marked\n  `dead` — its sensors go *unavailable* and a real fault is signalled.\n- During the night, failures don't count toward that threshold and the\n  inverter's `state` attribute stays `idle`. Sensors keep their last daytime\n  values, available but stale, until sunrise.\n- At sunrise, the failure counter is reset so morning warm-up gets a fresh\n  five chances.\n\nIf you have no location configured in Home Assistant, the `sun.sun` entity\nwon't exist and the integration falls back to treating every poll attempt as\ndaytime — you'll see false \"dead\" alarms overnight. Configure your\nlatitude/longitude under **Settings → System → General** to avoid that.\n\n---\n\n## Resilience features\n\nThese are running silently in the background, but worth knowing about when\ndebugging.\n\n- **Exponential backoff per inverter.** A failing inverter is retried with\n  increasing delays (1 s, 2 s, 4 s, 8 s, … capped at 5 min) instead of\n  hammered every cycle.\n- **Coordinator watchdog.** Every 5 min, if no successful polling cycle was\n  recorded in that window, the integration pings the CC2530 directly. If the\n  dongle doesn't answer, a full coordinator recovery (close → reopen →\n  re-init) runs with its own backoff schedule (5 s, 30 s, 2 min, 10 min).\n- **Hot-plug recovery.** When the USB-TTL bridge is unplugged, the next read\n  or write surfaces as a clean `ZNPError`, the transport is marked closed,\n  and the watchdog re-opens the port at the next tick. Plug it back in and\n  the polling resumes within a minute, no Home Assistant restart needed.\n- **Startup retry.** `async_setup_entry` retries the dongle bring-up three\n  times (2 s apart) before failing with `ConfigEntryNotReady` — handy after\n  a host reboot where the USB device takes a moment to enumerate.\n\n---\n\n## Services\n\n| Service | Use |\n| --- | --- |\n| `aps_zigbee.pair_inverter` | Pair a new inverter (`serial`, optional `name`). |\n| `aps_zigbee.repoll` | Force an immediate refresh cycle. |\n| `aps_zigbee.reboot_inverter` | Send the proprietary reboot command (`serial`). |\n\n---\n\n## Troubleshooting\n\n| Symptom | Likely cause | Fix |\n| --- | --- | --- |\n| `Cannot open the serial port` | Wrong path, or another integration grabbed it. | Confirm `/dev/serial/by-id/...` and that ZHA / Zigbee2MQTT is not pointing at the same dongle. |\n| `CC2530 dongle did not come up` | Wrong firmware, wrong voltage, RX/TX swap. | Re-do step 3 of the flashing guide; verify 3.3 V on VCC. |\n| `pairing_failed` for every inverter | Wrong ECU id, or inverter out of range. | Use `D8A3011B9780` unless you know what you changed; move the dongle closer to validate range first. |\n| Energy sensor never increases at night | Normal — the inverter is off and the timestamp doesn't move. | Expected; check during daylight. |\n| Power jumps to a huge value right after restart | The first sample after restart has a stale \"previous\" reading. | The coordinator handles inverter-side resets but a HA restart resets *our* state; one polling cycle later the values are correct. |\n| `state` attribute is `idle` | Sun is below the horizon — normal night-time behaviour. | Wait for sunrise; the inverter will resume on its own. |\n| `state` attribute stays `dead` during the day | Real loss of Zigbee link (range, dead inverter, dongle issue). | Check `signal_quality` history; move the dongle closer; press **Reboot inverter** in the device card; failing that, re-pair. |\n| All inverters go silent at the same time | Coordinator wedged (firmware lock-up) or USB-TTL bridge unplugged. | The watchdog will recover automatically within 5 min. Speed it up by reloading the integration. |\n\nEnable verbose protocol logs:\n\n```yaml\n# configuration.yaml\nlogger:\n  default: warning\n  logs:\n    custom_components.aps_zigbee: debug\n    custom_components.aps_zigbee.aps_protocol: debug\n```\n\n---\n\n## How it works (architecture)\n\n```\n┌──────────────────────────────────────────────────────────────┐\n│ Home Assistant (Docker container)                            │\n│  ┌───────────────────────────────────────────────────────┐   │\n│  │ custom_components/aps_zigbee/                         │   │\n│  │  ├─ config_flow.py   ← UI: pick port, ECU id, pair    │   │\n│  │  ├─ coordinator.py   ← DataUpdateCoordinator          │   │\n│  │  ├─ sensor.py        ← 13 SensorEntity per inverter   │   │\n│  │  └─ aps_protocol/    ← pure Python (HA-free)          │   │\n│  │      ├─ znp.py        ZNP framing + async serial      │   │\n│  │      ├─ frames.py     proprietary command builders    │   │\n│  │      ├─ coordinator.py CC2530 init sequence           │   │\n│  │      ├─ pairing.py    APS 4-command pairing handshake │   │\n│  │      ├─ polling.py    poll request + response gates   │   │\n│  │      └─ decode_ds3.py field offsets + scaling         │   │\n│  └───────────────────────────────────────────────────────┘   │\n│                       │ pyserial-asyncio                     │\n└───────────────────────┼──────────────────────────────────────┘\n                        │ /dev/serial/by-id/...\n                ┌───────▼────────┐\n                │ USB-TTL bridge │ (CP2102 / CH340 / FT232RL)\n                └───────┬────────┘\n                        │ 115200 / 8N1 / 3.3 V\n                ┌───────▼─────────────┐\n                │ CC2530 + CC2591     │ ← Kadsol firmware\n                │ (Zigbee coordinator) │\n                └───────┬─────────────┘\n                        │ proprietary APS Zigbee\n                ┌───────▼───────┐  ┌─────────────┐  ┌─────────────┐\n                │ DS3 inverter  │  │ DS3 inverter│  │ DS3 inverter│ ...\n                │   (×N)        │  │             │  │             │\n                └───────────────┘  └─────────────┘  └─────────────┘\n```\n\nThe proprietary protocol is documented in\n[`patience4711/ESP32-read-APS-inverters`](https://github.com/patience4711/ESP32-read-APS-inverters)\n(see `ZIGBEE_*.ino` and `AAA_DECODE.ino`); we port it 1:1 to async Python.\n\n---\n\n## Development\n\n```bash\ngit clone git@github.com:technoo10201/ha-apsystems-ds3.git\ncd ha-apsystems-ds3\npython3 -m venv .venv\n.venv/bin/pip install -r requirements-dev.txt\n.venv/bin/pytest          # 68 unit tests on the protocol layer\n.venv/bin/ruff check .    # lint\n.venv/bin/ruff format .   # format\n```\n\nThe `aps_protocol/` package is deliberately HA-free so the protocol layer is\ntestable (and shippable, e.g. as a CLI) without pulling in `homeassistant`.\nThe bundled `scripts/aps-cli` wrapper exposes the protocol layer end-to-end\nagainst a live dongle — see [Validating the wiring](#validating-the-wiring-cli-no-home-assistant-required)\nabove for the six sub-commands.\n\n---\n\n## Credits\n\n- Protocol reverse-engineering and reference firmware:\n  [patience4711/ESP32-read-APS-inverters](https://github.com/patience4711/ESP32-read-APS-inverters)\n  and [patience4711/read-APSystems-YC600-QS1-DS3](https://github.com/patience4711/read-APSystems-YC600-QS1-DS3).\n- CC2530 ZNP firmware: **Kadsol**, distributed through the Zigbee2MQTT\n  community.\n\n---\n\n## License\n\nMIT — see [`LICENSE`](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftechnoo10201%2Fha-apsystems-ds3","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftechnoo10201%2Fha-apsystems-ds3","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftechnoo10201%2Fha-apsystems-ds3/lists"}