Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
118 commits
Select commit Hold shift + click to select a range
a0995ed
replace gmqtt with aiomqtt
bj00rn Jul 14, 2025
7ce9d6c
Set default port for TLS to 8883, fallback to 1883 if no port and TCP…
bj00rn Jul 14, 2025
131bb22
fix keepalive
bj00rn Oct 7, 2025
619c4d6
fix client not reconnecting
bj00rn Nov 18, 2025
b033b55
retain birth/keepalive message
bj00rn Nov 25, 2025
5243c6c
fix(tests): update publish mock signatures for aiomqtt migration
nanomad Jun 24, 2026
85e6091
fix: restore *, retain=True defaults on all publish_* methods
nanomad Jun 24, 2026
2a87077
style: apply ruff formatting
nanomad Jun 24, 2026
9ee28e6
fix(log_publisher): forward retain flag to internal_publish
nanomad Jun 24, 2026
9113cfd
fix(mqtt_publisher): read message.retain directly in _on_message
nanomad Jun 24, 2026
30f7bc2
fix(mqtt_publisher): call on_mqtt_reconnected after broker reconnect
nanomad Jun 24, 2026
6310e7f
fix(mqtt_publisher): restore imported_energy_topic subscription
nanomad Jun 24, 2026
ec833b3
fix(mqtt_publisher): add missing f-prefix to subscribe error log
nanomad Jun 24, 2026
3fd5a96
fix(mqtt_publisher): guard connect() against unconfigured host
nanomad Jun 24, 2026
3f62666
fix(tests): correct misleading test name for clear_topic
nanomad Jun 24, 2026
105627d
fix(mqtt_publisher): remove spurious "a" suffix from client identifier
nanomad Jun 24, 2026
5cc59b0
fix(mqtt_publisher): exit immediately on permanent MQTT connect failures
nanomad Jun 24, 2026
dcddcca
refactor(mqtt_publisher): use paho CONNACK constants instead of magic…
nanomad Jun 24, 2026
a8bc3c6
refactor(mqtt_publisher): replace paho constants with local MQTT 3.1.…
nanomad Jun 24, 2026
7e55aee
fix(mqtt_publisher): exponential backoff on reconnect with 5 min cap
nanomad Jun 24, 2026
27677a6
fix(mqtt_publisher): use create_task instead of run_coroutine_threadsafe
nanomad Jun 24, 2026
4fecfcd
fix(mqtt_publisher): fix fatal CONNACK detection and connect() hang
nanomad Jun 24, 2026
8968d78
Merge pull request #457 from SAIC-iSmart-API/chore/aiomqtt
nanomad Jun 24, 2026
17bd181
fix(ha): emit retain as JSON boolean in discovery payloads
nanomad Jun 24, 2026
11af448
style: apply ruff formatting
nanomad Jun 24, 2026
cdeb882
Merge pull request #459 from SAIC-iSmart-API/fix/ha-discovery-retain-…
nanomad Jun 24, 2026
77c4581
fix(extractors): suppress SoC kWh warning for non-EV vehicles
nanomad Jun 24, 2026
5acfbd4
fix(command): publish command results with retain=False
nanomad Jun 24, 2026
923038a
Merge pull request #461 from SAIC-iSmart-API/fix/command-result-retai…
nanomad Jun 24, 2026
2070279
fix(tls): only apply tls_insecure when a custom CA cert is configured
nanomad Jun 24, 2026
c16515e
fix(tls): warn on hostname-check bypass regardless of custom CA cert
nanomad Jun 24, 2026
d806387
Merge pull request #462 from SAIC-iSmart-API/fix/tls-insecure-require…
nanomad Jun 24, 2026
d202027
Merge pull request #460 from SAIC-iSmart-API/fix/extract-soc-kwh-warn…
nanomad Jun 24, 2026
953a24b
chore: add 0.13.0 release notes to CHANGELOG
nanomad Jun 24, 2026
937f450
chore: document release process and add /release skill
nanomad Jun 24, 2026
94b96b9
fix(mqtt_publisher): pass tls_insecure=None for non-TLS connections
csutcliff Jul 6, 2026
adcb02e
Merge pull request #465 from csutcliff/fix/tls-insecure-plain-tcp
nanomad Jul 16, 2026
54193ca
fix(docker): install only the main dependency group in the image
joszz Oct 5, 2026
5650415
fix(abrp): report which inputs are missing when a push is skipped
nanomad Oct 17, 2026
e662a62
Merge pull request #470 from SAIC-iSmart-API/fix/abrp-skip-reason
nanomad Oct 17, 2026
80c8a98
feat(ha): mark charging entities unavailable when charge data goes stale
nanomad Oct 17, 2026
d3a622b
Merge pull request #469 from joszz/fix/docker-only-main-deps
nanomad Oct 17, 2026
46d835a
fix(ha): keep Charging switch available when charge data is stale
nanomad Oct 17, 2026
f6c76f7
Merge pull request #471 from SAIC-iSmart-API/feat/charging-data-avail…
nanomad Oct 17, 2026
511637c
docs: document observed chargingType values
nanomad Oct 17, 2026
201015c
docs: check chargingType values against logged data
nanomad Oct 17, 2026
37db37f
fix: log SAIC API timeouts as a short warning
nanomad Oct 17, 2026
e177859
fix: log SAIC API timeouts as a short warning
nanomad Oct 17, 2026
a22e332
Revert "fix: log SAIC API timeouts as a short warning"
nanomad Oct 17, 2026
e2bbc7a
docs: ground chargingType values in raw API responses
nanomad Oct 17, 2026
a3d72ff
Merge pull request #473 from SAIC-iSmart-API/fix/api-timeout-log-message
nanomad Oct 17, 2026
18faa1c
Merge pull request #472 from SAIC-iSmart-API/docs/charging-type-values
nanomad Oct 17, 2026
f67723a
fix: also log SAIC retry-later errors as a short warning
nanomad Oct 17, 2026
f9ea4d7
fix: log failed commands on transient API errors at ERROR level
nanomad Oct 17, 2026
e8797cc
Merge pull request #474 from SAIC-iSmart-API/fix/api-retry-exception-log
nanomad Oct 17, 2026
ddc119f
fix: tolerate transient poll failures before marking vehicle offline
nanomad Oct 17, 2026
623f8b9
docs: describe tolerance of transient poll failures
nanomad Oct 17, 2026
a9374c0
Merge pull request #475 from SAIC-iSmart-API/fix/tolerate-transient-p…
nanomad Oct 17, 2026
6d254e1
docs: document vehicle and charging data availability
nanomad Oct 17, 2026
4f23b63
Merge pull request #476 from SAIC-iSmart-API/docs/charging-data-avail…
nanomad Oct 17, 2026
750b3ea
fix: do not report cable lock state for models that do not provide it
nanomad Oct 17, 2026
8ca1a49
chore: allow saic-ismart-client-ng 0.10.x
nanomad Oct 17, 2026
08d6884
feat: close the SAIC API client on shutdown
nanomad Oct 17, 2026
f5d1c52
Merge pull request #477 from SAIC-iSmart-API/fix/cable-lock-state
nanomad Oct 17, 2026
f166d3a
chore: require saic-ismart-client-ng >= 0.10.0 for close()
nanomad Oct 17, 2026
8d77e0a
Merge pull request #479 from SAIC-iSmart-API/chore/saic-client-ng-0.10
nanomad Oct 17, 2026
4f46efe
chore: upgrade dev dependencies
nanomad Oct 17, 2026
9d4fdce
chore: adapt to ruff 0.17 and drop unused gmqtt mypy override
nanomad Oct 17, 2026
0eee734
ci: bump actions and pre-commit hooks, add Dependabot
nanomad Oct 17, 2026
298a61f
Merge pull request #480 from SAIC-iSmart-API/chore/dev-deps-and-ci
nanomad Oct 17, 2026
2a22df5
fix: shut down gracefully on SIGTERM
nanomad Oct 17, 2026
a813b73
chore: keep .git, .claude and tests out of the Docker build context
nanomad Oct 17, 2026
1010e95
Merge pull request #481 from SAIC-iSmart-API/fix/graceful-sigterm-shu…
nanomad Oct 17, 2026
4ee130d
chore: add 0.13.0 release notes for rc3
nanomad Oct 17, 2026
d88d470
chore: create a GitHub release in /release and document it
nanomad Oct 17, 2026
a9db0ed
Merge pull request #482 from SAIC-iSmart-API/chore/release-command-gh…
nanomad Oct 17, 2026
4931859
ci: make Dependabot open PRs against develop
nanomad Oct 17, 2026
23cec0e
Merge pull request #483 from SAIC-iSmart-API/chore/dependabot-target-…
nanomad Oct 17, 2026
bd63611
fix: run scheduled charging job on the event loop
nanomad Oct 18, 2026
b57692e
chore: exclude .claude directory from ruff
nanomad Oct 18, 2026
d65952e
chore: drop empty kwargs from scheduled charging job
nanomad Oct 18, 2026
9a9a405
Merge pull request #486 from SAIC-iSmart-API/chore/ruff-exclude-claud…
nanomad Oct 18, 2026
f5158f7
fix: raise a clear error when publishing outside the event loop
nanomad Oct 18, 2026
9f0b770
refactor: reuse the running loop in MqttPublisher.__publish
nanomad Oct 18, 2026
352b761
test: publish tasks are released once done
nanomad Oct 18, 2026
6daaa8c
Merge pull request #485 from SAIC-iSmart-API/fix/484-scheduled-chargi…
nanomad Oct 18, 2026
3d484c5
Merge pull request #487 from SAIC-iSmart-API/fix/mqtt-publish-off-loo…
nanomad Oct 18, 2026
372865c
fix: keep a reference to the enable_commands task
nanomad Oct 18, 2026
009de30
refactor: add BackgroundTasks helper for fire-and-forget tasks
nanomad Oct 18, 2026
0b36750
fix: don't hang connect() when the MQTT loop crashes before connecting
nanomad Oct 18, 2026
406eaad
refactor: add BackgroundTasks.wait and use it instead of sleep(0) in …
nanomad Oct 18, 2026
a02ecb7
Merge pull request #489 from SAIC-iSmart-API/fix/mqtt-connect-hang-on…
nanomad Oct 18, 2026
4d5bc09
Merge pull request #488 from SAIC-iSmart-API/fix/hold-enable-commands…
nanomad Oct 18, 2026
172c84c
refactor: extract log_task_failure and log MQTT loop crashes
nanomad Oct 18, 2026
b0790c4
Merge pull request #490 from SAIC-iSmart-API/refactor/log-task-failure
nanomad Oct 18, 2026
aa11ab9
fix: exit when the MQTT loop task ends
nanomad Oct 18, 2026
e945e47
refactor: avoid logging the MQTT loop crash twice
nanomad Oct 18, 2026
6f542bb
Merge pull request #491 from SAIC-iSmart-API/fix/exit-on-mqtt-loop-fa…
nanomad Oct 18, 2026
e087f0d
fix: flush pending MQTT publishes on shutdown
nanomad Oct 18, 2026
03f91e5
fix: flush on early cancellation and always close after a flush cancel
nanomad Oct 18, 2026
73e3fc3
Merge pull request #492 from SAIC-iSmart-API/fix/flush-mqtt-publishes…
nanomad Oct 18, 2026
47a2c78
fix: prefer RELEASE_VERSION over package metadata for the gateway ver…
nanomad Oct 18, 2026
2cb4dec
Merge pull request #493 from SAIC-iSmart-API/fix/gateway-version-env-…
nanomad Oct 18, 2026
fe104e7
chore: add changelog entries for 0.13.0-rc4
nanomad Oct 18, 2026
0b87277
refactor: use PEP 695 type parameter syntax for __anonymize
nanomad Oct 18, 2026
69f054b
Merge pull request #494 from SAIC-iSmart-API/pep695-typevar
nanomad Oct 18, 2026
7fedf27
ci: test on Python 3.15 (non-blocking)
nanomad Oct 18, 2026
dc40f3d
ci: build Arm images on native Arm runners, drop QEMU
nanomad Oct 18, 2026
29f0b9e
build: use Poetry 2.5.1 base image
nanomad Oct 18, 2026
f70992a
Merge pull request #496 from SAIC-iSmart-API/native-arm-runners
nanomad Oct 18, 2026
b635b17
Merge pull request #495 from SAIC-iSmart-API/add-python-3.15
nanomad Oct 18, 2026
feee489
chore: add changelog entries for 0.13.0-rc5
nanomad Oct 18, 2026
5c6ceaf
ci: make Python 3.15 tests blocking
nanomad Oct 18, 2026
4f1b38a
Merge pull request #497 from SAIC-iSmart-API/gateway-3.15-required-re…
nanomad Oct 18, 2026
d74acfa
chore: ignore local Claude Code settings and worktrees
nanomad Oct 18, 2026
9ea7745
chore: drop settings.json ignore entry
nanomad Oct 18, 2026
6641391
chore: add shared Claude Code permissions
nanomad Oct 18, 2026
2b43a69
Merge pull request #498 from SAIC-iSmart-API/gitignore-claude
nanomad Oct 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .claude/commands/release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Cut a release candidate or stable release.

Usage:
- `/release rc` — tag the current `develop` HEAD as the next RC (e.g. `0.13.0-rc2`)
- `/release stable` — merge `develop` into `main` and tag the final release

## RC release (`/release rc`)

1. Confirm the current branch is `develop` and is up to date with `origin/develop`.
2. Read CHANGELOG.md to find the version being prepared (the top-most `## X.Y.Z` entry).
3. List existing tags to determine the next RC number (e.g. if `0.13.0-rc1` exists, use `rc2`).
4. Run `poetry run pytest tests` and check that CI on `develop` HEAD is green (`gh run list --branch develop --limit 3`) — do not tag if either fails.
5. Tag `develop` HEAD as `<version>-rc<N>` and push the tag.
6. Create the GitHub pre-release with generated notes:
`gh release create <tag> --verify-tag --prerelease --title <tag> --generate-notes --notes-start-tag <prev-tag>`
7. Report the tag name, the release URL and the `git log --oneline <prev-tag>..HEAD` summary. Mention that the "build dev images" run was started by the tag.

## Stable release (`/release stable`)

1. Confirm CHANGELOG.md has a section for the version being released.
2. Open a PR from `develop` → `main` titled `chore: release <version>`.
3. After the user confirms the PR is merged, tag `main` HEAD as `<version>` and push.
4. Create the GitHub release with generated notes, starting from the previous stable tag (not an RC) so the notes cover the whole release:
`gh release create <version> --verify-tag --latest --title <version> --generate-notes --notes-start-tag <prev-stable-tag>`
5. Report the tag and the GitHub release URL.

**RC tags always go on `develop`. Stable tags always go on `main`.**
17 changes: 17 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"permissions": {
"allow": [
"Bash(poetry run pytest:*)",
"Bash(poetry run mypy:*)",
"Bash(poetry run ruff check:*)",
"Bash(poetry run ruff format:*)",
"Bash(git status:*)",
"Bash(git diff:*)",
"Bash(git log:*)"
],
"deny": [
"Read(./.env)",
"Read(./.env.*)"
]
}
}
7 changes: 6 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -187,4 +187,9 @@ fabric.properties
# Test results
junit/

.github/
.github/

# Not needed to build the image
.git/
.claude/
tests/
20 changes: 20 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
target-branch: develop
schedule:
interval: weekly
- package-ecosystem: pip
directory: /
target-branch: develop
schedule:
interval: weekly
groups:
dev-dependencies:
dependency-type: development
- package-ecosystem: docker
directory: /
target-branch: develop
schedule:
interval: weekly
11 changes: 6 additions & 5 deletions .github/workflows/build_and_test_python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@ jobs:

runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: [ "3.12", "3.13", "3.14" ]
python-version: [ "3.12", "3.13", "3.14", "3.15" ]

steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
Expand All @@ -38,10 +39,10 @@ jobs:

- name: Load cached venv
id: cached-poetry-dependencies
uses: actions/cache@v5
uses: actions/cache@v6
with:
path: .venv
key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}
key: venv-${{ runner.os }}-py${{ matrix.python-version }}-${{ hashFiles('**/poetry.lock') }}

- name: Install dependencies
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
Expand Down
19 changes: 10 additions & 9 deletions .github/workflows/build_docker_images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,20 @@ env:

jobs:
build:
runs-on: ubuntu-latest
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm64
- linux/arm/v7
include:
# Native Arm runners build both Arm platforms without QEMU
- platform: linux/amd64
runner: ubuntu-latest
- platform: linux/arm64
runner: ubuntu-24.04-arm
- platform: linux/arm/v7
runner: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- name: Set platform slug
id: platform
Expand All @@ -50,9 +54,6 @@ jobs:
id: repo
run: echo "lowercase=${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT"

- name: Set up QEMU
uses: docker/setup-qemu-action@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4

Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,7 @@ junit/
.run/

# macOS
.DS_Store
.DS_Store
# Claude Code local state (commands/ is tracked)
.claude/settings.local.json
.claude/worktrees/
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ default_language_version:

repos:
- repo: https://github.com/python-poetry/poetry
rev: '2.1.3'
rev: '2.5.1'
hooks:
- id: poetry-check
- id: poetry-lock
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.12.0
rev: v0.17.1
hooks:
- id: ruff
args:
Expand All @@ -38,7 +38,7 @@ repos:
pass_filenames: false
stages: [pre-push]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
rev: v6.0.0
hooks:
- id: check-yaml
- id: check-added-large-files
100 changes: 100 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,105 @@
# Change Log

## 0.13.0

### Added

* Publish `drivetrain/chargingDataAvailable` (`online`/`offline`). The Battery
Charging sensor, Remaining charging time and Charging finished entities go
unavailable in Home Assistant after 3 consecutive failed charge status
requests, for example when the car falls into deep sleep right after a
charge, instead of showing a stale value. The Charging switch stays
available (#471).

* Document the observed `chargingType` values and the availability topics in
the README (#472, #476).

### Changed

* Replace gmqtt with aiomqtt for MQTT connectivity. The new client uses a
pure-asyncio architecture — no more `run_coroutine_threadsafe` cross-thread
calls. Reconnect now uses exponential backoff (5 s → 10 s → … → 5 min)
instead of a fixed interval (#457).

* Mark a vehicle `offline` only after 3 consecutive failed polls, so a single
"try again later" reply from the SAIC server no longer makes every entity
unavailable in Home Assistant (#475).

* Require `saic-ismart-client-ng` 0.10.0 and close its HTTP client on shutdown
(#479).

* Shut down gracefully on SIGTERM, so `docker stop` no longer ends in a
SIGKILL after 10 s (#481).

* Upgrade development dependencies (ruff 0.17, mypy 2.4), bump the GitHub
Actions and pre-commit hooks, and add Dependabot (#480).

* Build the Docker image on the Poetry 2.5.1 base image, build the Arm images
on native Arm runners instead of QEMU, and run the tests on Python 3.15 as a
non-blocking check (#495, #496).

### Fixed

* Exit immediately on permanent MQTT connection failures (bad credentials,
unknown protocol version, identifier rejected, not authorised) instead of
looping forever (#457).

* Restore MQTT subscriptions and trigger HA re-discovery on broker reconnect
after a transient disconnect (#457).

* Suppress spurious `WARNING: Could not extract a valid SoC kWh` log on every
poll for non-EV vehicles. The warning is preserved for EVs where the BMS
returns invalid data (#460).

* Publish command result topics (`Success` / `Failed: …`) with `retain=False`
so a stale result from a previous session does not persist on the broker
across restarts (#461).

* Emit `retain` as a JSON boolean (`true`/`false`) in Home Assistant MQTT
discovery payloads instead of the string `"true"`/`"false"` that the HA
schema requires (#459).

* Do not publish the charging cable lock state for models that do not report
it, such as the MG ZS EV, instead of always publishing "unlocked". The
commanded state is published after a lock or unlock command (#477).

* List what is missing when an ABRP update is skipped, instead of always
reporting a missing configuration (#470).

* Log SAIC API timeouts and "try again later" replies as a short warning
without a traceback, and log failed commands on transient API errors at
ERROR level (#473, #474).

* Fix the scheduled charging job failing with `no running event loop` since
the aiomqtt migration, which meant the gateway never switched to `force`
refresh at the scheduled charging start time (#484, #485).

* Exit when the MQTT connection task ends, so Docker or Kubernetes can restart
the gateway instead of it running without MQTT. Running with MQTT disabled
is not affected (#491).

* Do not hang at startup when the MQTT connection task crashes before the first
connection (#489), and log such crashes instead of leaving them unreported
(#488, #490).

* Keep references to the tasks that publish to MQTT, so they cannot be
garbage collected before they finish, and report a clear error when
publishing from outside the event loop (#487, #488).

* Wait up to 2 s for pending MQTT publishes on shutdown, including when the
shutdown happens during startup, instead of dropping them (#492).

* Prefer the `RELEASE_VERSION` set at build time over the package metadata for
the reported gateway version (#493).

* Install only the main dependency group in the Docker image (#469).

* Warn when TLS hostname verification is disabled regardless of whether a
custom CA certificate is configured. Self-signed cert users (who typically
have no CA file) now see the warning too (#462).

**Full Changelog**: https://github.com/SAIC-iSmart-API/saic-python-mqtt-gateway/compare/0.12.0...0.13.0

## 0.12.0

### Added
Expand Down
9 changes: 9 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

**Always branch from `develop` for features and bugfixes.** PRs must target `develop`, not `main`. The only exception is a hotfix that must go directly to `main`.

## Release process

1. Merge all relevant PRs into `develop`.
2. Add a `## <version>` section to `CHANGELOG.md` and commit it directly to `develop`.
3. Tag `develop` as `<version>-rc<N>` (e.g. `0.13.0-rc1`) and push the tag — **RC tags live on `develop`, not `main`**.
4. After the RC is validated, open a PR from `develop` → `main` and merge it.
5. Tag the resulting `main` commit as the final release (e.g. `0.13.0`) and push the tag.
6. Create a GitHub release for every tag, RC or final, with generated notes (`gh release create <tag> --verify-tag --generate-notes`). Add `--prerelease` for RCs and `--latest` for the final release. `/release` does this.

## Commands

```bash
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ARG POETRY_VERSION=2.3.2
ARG POETRY_VERSION=2.5.1
ARG PYTHON_VERSION=3.14

FROM nanomad/poetry:${POETRY_VERSION}-python-${PYTHON_VERSION} AS builder
Expand All @@ -12,7 +12,7 @@ COPY pyproject.toml poetry.lock /usr/src/app/

# Install the dependencies and clear the cache afterwards.
# This may save some MBs.
RUN poetry install --no-root && rm -rf $POETRY_CACHE_DIR
RUN poetry install --no-root --only main && rm -rf $POETRY_CACHE_DIR

# Now let's build the runtime image from the builder.
# We'll just copy the env and the PATH reference.
Expand Down
37 changes: 36 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ The following are the known available endpoints:
|-----------------------|----------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| --ha-discovery | HA_DISCOVERY_ENABLED | Home Assistant auto-discovery is enabled (True) by default. It can be disabled (False) with this parameter. |
| --ha-discovery-prefix | HA_DISCOVERY_PREFIX | The default MQTT prefix for Home Assistant auto-discovery is 'homeassistant'. Another prefix can be configured with this parameter |
| --ha-show-unavailable | HA_SHOW_UNAVAILABLE | Show entities as Unavailable in Home Assistant when car polling fails. Enabled (True) by default. Can be disabled, to retain the pre 0.6.x behaviour, but do that at your own risk. |
| --ha-show-unavailable | HA_SHOW_UNAVAILABLE | Show entities as Unavailable in Home Assistant after 3 consecutive failed polls (a single failed poll is tolerated). Enabled (True) by default. Can be disabled, to retain the pre 0.6.x behaviour, but do that at your own risk. |

### A Better Route Planner (ABRP) integration

Expand Down Expand Up @@ -229,6 +229,27 @@ with the default vehicle prefix: `saic/<saic_user>/vehicles/<vehicle_id>`
| /refresh/period/inActiveGrace/set | grace period (sec) | After the vehicle has been shutdown, the gateway continues to query the state for 600 seconds (default value). The duration of this extended query period can be modified with this topic. |
| /location/findMyCar/set | [activate,lights_only,horn_only,stop] | Activate 'find my car' with lights and horn (activate), with lights only (lights_only), with horn only (horn_only) or deactivate it (stop). |

## Charging type values

The `/drivetrain/chargingType` topic publishes the raw `chargingType` value reported by the SAIC API. The API does not
document it. The meanings below were reported by users in
[#398](https://github.com/SAIC-iSmart-API/saic-python-mqtt-gateway/issues/398) and checked against about 89k logged
API responses from one vehicle (Mar 2024 – Oct 2026). They may differ between models.

| Value | Reported meaning | Checked against logged data |
|-------|----------------------------------------------------|----------------------------------------------------------------------------------|
| 0 | Car in deep sleep | Not supported: 31 isolated samples between values 1 and 3, not a sustained state |
| 1 | Car just parked | Consistent (near-zero battery current) |
| 3 | HV battery in use (driving, or remote A/C on) | Consistent (battery current under load) |
| 6 | AC charging | Confirmed (on-board charger input current present) |
| 7 | DC charging | Confirmed (about 200 A battery current, no AC input) |
| 9 | Charging stopped from the infotainment | Unclear: both samples follow DC charging while plugged in |
| 10 | Charging ended | Supported: 17 of 24 samples follow AC charging while plugged in |
| 16 | Car unplugged while charging was still in progress | Never observed |

Values 2, 4 and 12 were seen only 1 to 3 times each. 4 and 12 appeared around the start or end of charging, which could
mean a transition state. Their meaning is unknown.

## Home Assistant auto-discovery

The gateway supports [Home Assistant MQTT discovery](https://www.home-assistant.io/integrations/mqtt#mqtt-discovery). It
Expand All @@ -252,3 +273,17 @@ This device provides visibility into the gateway's status even when no vehicles
### Vehicle devices

Each vehicle registered to your account appears as a separate device with sensors for drivetrain, climate, location, and more.

#### Availability

Two topics control when vehicle entities show as unavailable. Both publish `online` or `offline`, and both tolerate
short glitches: they switch to `offline` only after 3 consecutive failures, and back to `online` on the next success.

| Topic | Goes `offline` when | Entities affected |
|------------------------------------------|------------------------------------------------------|-----------------------------------------------------------------------------------------|
| `<vin>/available` | 3 consecutive polls of the vehicle API fail | All vehicle entities (unless `HA_SHOW_UNAVAILABLE` is disabled) |
| `<vin>/drivetrain/chargingDataAvailable` | 3 consecutive charge status requests fail | Battery Charging, Remaining charging time and Charging finished |

The charge status request is the one that fails when the car goes into deep sleep right after a charge. The vehicle stays
`online` in that case, so the two topics are separate. Other entities keep their last known value. The **Charging**
switch stays available, so you can still start or stop charging from Home Assistant while the car is asleep.
Loading
Loading