Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
573fa79
Move Webasto Connect Card assets to dedicated card folder
MTrab Mar 2, 2026
8e4979e
Add language-aware translations to Webasto Connect Card
MTrab Mar 2, 2026
5c71ba6
Refactor card translations using localize module and language files
MTrab Mar 2, 2026
11cc43f
Switch card translations to JSON files for Lokalise compatibility
MTrab Mar 2, 2026
6f45dec
Add standalone build pipeline for Webasto Connect Card
MTrab Mar 2, 2026
ee62dcc
Bundle card under integration and auto-install with version check
MTrab Mar 2, 2026
86f8f86
Use main output friendly_name instead of hardcoded mode labels
MTrab Mar 2, 2026
ef67324
Move card source to card-src and keep only built bundle in integration
MTrab Mar 2, 2026
e980663
Read card version from JS marker and remove VERSION file
MTrab Mar 2, 2026
12ee2d5
Add debug logs for card install status during setup
MTrab Mar 2, 2026
1a53da0
Inject card version from package.json during build
MTrab Mar 2, 2026
a1f3619
Auto-register Lovelace resource for Webasto Connect Card
MTrab Mar 2, 2026
2d9f29a
Add Lovelace visual editor for Webasto Connect Card
MTrab Mar 2, 2026
525b98d
Handle missing main_output_entity gracefully in card UI
MTrab Mar 2, 2026
8beb9f0
Use button gray ring color when output is off
MTrab Mar 2, 2026
c1b892c
Add temperature battery and location info rows to card
MTrab Mar 2, 2026
6b24368
Use homeassistant.toggle for configured main_output_entity
MTrab Mar 2, 2026
ec4e0b5
Add versioned Lovelace resource URL for card cache busting
MTrab Mar 2, 2026
972a54e
Add lovelace to after_dependencies for hassfest
MTrab Mar 2, 2026
bff457b
Update develop
MTrab Mar 2, 2026
4482278
feat(card): improve editor/time labels and install flow; add Node in …
MTrab Mar 2, 2026
8593137
feat(card): remove Geo-fence label from top-left quadrant
MTrab Mar 2, 2026
de0ea4b
feat(card): iterate map popup behavior and document known issues
MTrab Mar 2, 2026
872afb7
test: align setup refresh tests with card install bootstrap
MTrab Mar 2, 2026
625043a
ci: enforce committed card bundle and add release card build
MTrab Mar 2, 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
6 changes: 4 additions & 2 deletions .devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,14 @@
},
"remoteUser": "vscode",
"features": {
"ghcr.io/devcontainers/features/rust:1": {}
"ghcr.io/devcontainers/features/node:1": {
"version": "lts"
}
},
"mounts": [
"source=${localWorkspaceFolder}/config,target=/config,type=bind,consistency=cached",
"source=${localEnv:HOME}/.codex/auth.json,target=/etc/codex/auth.json,readonly,type=bind",
"source=${localWorkspaceFolder}/../pywebasto,target=/development/github/homeassistant/pywebasto,type=bind",
"source=gh-config,target=/home/vscode/.config/gh,type=volume"
]
}
}
15 changes: 15 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v6

- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: "20"

- name: Set up Python
uses: actions/setup-python@v6
with:
Expand All @@ -26,6 +31,16 @@ jobs:
python -m pip install --upgrade pip
python -m pip install -r requirements.txt

- name: Build Webasto Connect Card
run: |
cd card-src
npm install
npm run build

- name: Verify card bundle is committed
run: |
git diff --exit-code -- custom_components/webastoconnect/card/webasto-connect-card.js

- name: Run Ruff
run: ruff check .

Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ jobs:
steps:
- name: Check out repository
uses: actions/checkout@v6
- name: Set up Node.js
uses: actions/setup-node@v6
with:
node-version: "20"
- name: Update manifest.json version to ${{ github.event.release.tag_name }}
run: |
python3 ${{ github.workspace }}/.github/scripts/update_hacs_manifest.py --version ${{ github.event.release.tag_name }} --path /custom_components/webastoconnect/
Expand All @@ -25,6 +29,11 @@ jobs:
git add ./custom_components/webastoconnect/manifest.json
git commit -m "Updated manifest.json"
git push origin HEAD:main
- name: Build Webasto Connect Card
run: |
cd card-src
npm install
npm run build
- name: Create zip
run: |
cd custom_components/webastoconnect
Expand Down
33 changes: 15 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

<a href="https://www.buymeacoffee.com/mtrab" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>

This module provides a way of integrating with Webasto ThermoConnect devices.
This integration provides support for Webasto ThermoConnect devices.

# Please note
Webasto <b>DOES NOT</b> provide any public API or documentation of such, so I cannot provide any guarantees that this will continue to work for all eternity.
Webasto <b>DOES NOT</b> provide any public API or documentation for it, so I cannot provide any guarantees that this will continue to work long-term.

# Table of Content
# Table of Contents

**[Installation](#installation)**<br/>
**[Setup](#setup)**<br/>
**[My heater doesn't show up](#my-heater-doesnt-show-up)**<br/>
**[Known Issues](#known-issues)**<br/>

# Installation:

Expand All @@ -29,18 +29,20 @@ Webasto <b>DOES NOT</b> provide any public API or documentation of such, so I ca

# Setup

My Home Assistant shortcut:<br/>
Open setup in Home Assistant:<br/>
[![](https://my.home-assistant.io/badges/config_flow_start.svg)](https://my.home-assistant.io/redirect/config_flow_start/?domain=webastoconnect)

Or go to Home Assistant > Settings > Integrations

Add "Webasto Connect (ThermoConnect)" integration *(If it doesn't show, try CTRL+F5 to force a refresh of the page)*
Add "Webasto Connect (ThermoConnect)" integration *(If it doesn't appear, try a hard refresh with Ctrl+F5.)*

Enter your Webasto account email and password

# My heater doesn't show up
# Known Issues

If your heater doesn't show up in the integration, please make sure it is connected to the e-mail used.
## My heater doesn't show up

If your heater doesn't show up in the integration, please make sure it is connected to the email used.

* Login to https://my.webastoconnect.com _USING THE SAME EMAIL AND PASSWORD_ as used in the integration
* Press `Account`
Expand All @@ -51,19 +53,14 @@ If your device is NOT listed under devices:

* Open the ThermoConnect app on your phone
* Select the missing device (If you have more than one connected)
* Click on the `"My Webasto Connect` button in the lower left
* Click on the `My Webasto Connect` button in the lower left
* Choose `Login with mobile browser`
* Login with your existing email and password

The device should now be linked to your email account and will show up after a restart of Home Assistant, or after reloading the integration

# Development: Coverage in VS Code

The devcontainer includes `ryanluker.vscode-coverage-gutters`.
The device should now be linked to your email account and will show up after a Home Assistant restart or after reloading the integration.

To generate coverage for it:
## Webasto Connect Card map popup centering

* Run the VS Code task `Run tests with coverage.xml`, or run:
* `python3 -m pytest tests --cov=custom_components --cov-report=term-missing --cov-report=xml:coverage.xml`
When opening the `Map` popup in the custom `Webasto Connect Card`, the tracked entity marker can appear visually lower than expected instead of perfectly centered.

Coverage Gutters is configured to read `coverage.xml` from the workspace root.
This behavior comes from Home Assistant's built-in map rendering in popup or modal contexts. The integration currently uses the built-in map implementation and does not override this behavior.
1 change: 1 addition & 0 deletions card-src/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
57 changes: 57 additions & 0 deletions card-src/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Webasto Connect Card

Custom Home Assistant Lovelace card.

## Files
- `webasto-connect-card.js`: source for the custom card
- `../custom_components/webastoconnect/card/webasto-connect-card.js`: built single-file card module (generated)
- `localize/localize.js`: translation lookup and language fallback
- `translations/*.json`: per-language strings
- `webasto_connect_card.yaml`: example card configuration

## Build (separate from release)
From repository root:

```bash
cd card-src
npm install
npm run build
```

For iterative testing while developing:

```bash
cd card-src
npm run build:watch
```

Card version marker (`__WEBASTO_CONNECT_CARD_VERSION__`) is injected automatically
from `card-src/package.json` during build.

## Install in Home Assistant
The integration auto-installs card assets on load/update to:
- `config/www/webastoconnect/webasto-connect-card.js`

Manual install is optional:

From this repository root:

```bash
mkdir -p config/www/webastoconnect
cp custom_components/webastoconnect/card/webasto-connect-card.js config/www/webastoconnect/webasto-connect-card.js
```

Then add a Lovelace resource:
- URL: `/local/webastoconnect/webasto-connect-card.js`
- Type: `module`

Use the example from `card-src/webasto_connect_card.yaml` and set your own entity IDs.

## Language / translations
- The card auto-selects text from Home Assistant language (`hass.language`).
- Built-in translations currently include `da` and `en` (fallback: `en`).
- You can still override corner titles with:
- `title_geo_fence`
- `title_mode`
- `title_timers`
- `title_map`
1 change: 1 addition & 0 deletions card-src/VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.1.0
31 changes: 31 additions & 0 deletions card-src/build.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { build, context } from "esbuild";
import { readFileSync } from "node:fs";

const watch = process.argv.includes("--watch");
const packageJson = JSON.parse(readFileSync(new URL("./package.json", import.meta.url)));
const cardVersion = packageJson.version;

const buildOptions = {
entryPoints: ["webasto-connect-card.js"],
bundle: true,
format: "esm",
target: ["es2020"],
minify: true,
sourcemap: false,
outfile: "../custom_components/webastoconnect/card/webasto-connect-card.js",
logLevel: "info",
loader: {
".json": "json",
},
banner: {
js: `globalThis.__WEBASTO_CONNECT_CARD_VERSION__ = "${cardVersion}";`,
},
};

if (watch) {
const ctx = await context(buildOptions);
await ctx.watch();
console.log("Watching for changes...");
} else {
await build(buildOptions);
}
48 changes: 48 additions & 0 deletions card-src/localize/localize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import da from "../translations/da.json";
import en from "../translations/en.json";

const languages = {
da,
en,
};

function getNestedTranslation(obj, path) {
if (!obj) return undefined;

const keys = path.split(".");
let result = obj;

for (const key of keys) {
if (result === undefined || result === null || typeof result !== "object") {
return undefined;
}
result = result[key];
}

return typeof result === "string" ? result : undefined;
}

function resolveLanguage(language) {
const raw = String(language || "en").toLowerCase();
if (languages[raw]) return raw;

const short = raw.split("-")[0];
if (languages[short]) return short;

return "en";
}

export function localize(hass, key, vars = {}) {
const lang = resolveLanguage(hass?.language);

let translated =
getNestedTranslation(languages[lang], key) ??
getNestedTranslation(languages.en, key) ??
key;

Object.entries(vars).forEach(([name, value]) => {
translated = translated.replace(`{${name}}`, String(value));
});

return translated;
}
13 changes: 13 additions & 0 deletions card-src/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "webasto-connect-card",
"private": true,
"version": "0.1.0b19",
"type": "module",
"scripts": {
"build": "node build.mjs",
"build:watch": "node build.mjs --watch"
},
"devDependencies": {
"esbuild": "0.25.2"
}
}
19 changes: 19 additions & 0 deletions card-src/translations/da.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"card": {
"ui": {
"geo_fence": "Geo-fence",
"mode": "Modus",
"timers": "Timere",
"map": "Kort",
"active": "Aktiv",
"inactive": "Ikke aktiv",
"ending_now": "Slutter nu",
"left": "tilbage",
"main_output_missing": "Vælg Main output entity i kortindstillinger",
"output": "Output",
"toggle_output": "Skift output",
"close": "Luk",
"map_unavailable": "Lokation er ikke tilgængelig"
}
}
}
19 changes: 19 additions & 0 deletions card-src/translations/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"card": {
"ui": {
"geo_fence": "Geo-fence",
"mode": "Mode",
"timers": "Timers",
"map": "Map",
"active": "Active",
"inactive": "Inactive",
"ending_now": "Ending now",
"left": "left",
"main_output_missing": "Select Main output entity in card settings",
"output": "Output",
"toggle_output": "Toggle output",
"close": "Close",
"map_unavailable": "Location is unavailable"
}
}
}
Loading
Loading