Skip to main content

Python client for Dimplex heating controllers (GDHV IoT)

Project description

Dimplex Controller Python Client

PyPI version Python versions License: MIT CI Tests Downloads

Async Python client for controlling Glen Dimplex Heating & Ventilation (GDHV) appliances via the Dimplex cloud API.


What does this do?

dimplex-controller-py is an asynchronous Python client that talks to the GDHV IoT cloud platform. It handles Azure B2C authentication (including automatic token refresh), discovers your Hubs, Zones and Appliances, and lets you read telemetry and send control commands — all from a script or a larger application.

It is the engine behind the Dimplex Hub Home Assistant integration and is published to PyPI as dimplex-controller.

Note: This is an unofficial library and is not affiliated with or endorsed by Glen Dimplex Heating & Ventilation (GDHV). Use it at your own risk.

Contents

Features

  • Authentication — Azure B2C login with automatic token refresh and secure token persistence.
  • Discovery — List Hubs, Zones and Appliances linked to your account.
  • Real-time status — Fetch room temperature, setpoints, comfort status, boost/away modes and EcoStart state.
  • Control — Set operation modes, activate Boost and Away, toggle EcoStart and Open Window Detection, and programme timer schedules.
  • Energy telemetry — Pull Time Series Insights (TSI) energy reports with a robust telemetry parser that adapts to varying firmware formats.

Installation

From PyPI (recommended)

pip install dimplex-controller

From source

git clone https://github.com/KRoperUK/dimplex-controller-py.git
cd dimplex-controller-py
pip install .

Development install

git clone https://github.com/KRoperUK/dimplex-controller-py.git
cd dimplex-controller-py
pip install -e ".[dev]"

Requires: Python 3.10 or later.

Quick start

The library uses asyncio and aiohttp. Here is the smallest example that lists your Hubs and Zones:

import asyncio
from aiohttp import ClientSession
from dimplex_controller import DimplexControl

async def main() -> None:
    async with ClientSession() as session:
        client = DimplexControl(session, refresh_token="YOUR_REFRESH_TOKEN")

        hubs = await client.get_hubs()
        for hub in hubs:
            print(f"Hub: {hub.Name}")
            zones = await client.get_hub_zones(hub.HubId)
            for zone in zones:
                print(f"  Zone: {zone.ZoneName}")

if __name__ == "__main__":
    asyncio.run(main())

Usage guide

Authentication

Dimplex uses Azure AD B2C. You cannot log in purely programmatically with just a username and password — the library must first capture a short-lived authorisation code.

The recommended approach is to run the included demo script once:

python demo.py

The script walks you through opening a browser, signing in, and pasting the resulting redirect URL back into the terminal. On success, it saves a dimplex_tokens.json file in the current working directory.

Subsequent runs read the refresh token from that file automatically. You can also pass the refresh token directly to DimplexControl if you prefer to manage storage yourself.

Discovery

hubs = await client.get_hubs()
for hub in hubs:
    print(f"Hub: {hub.Name} ({hub.HubId})")
    zones = await client.get_hub_zones(hub.HubId)
    for zone in zones:
        print(f"  Zone: {zone.ZoneName} ({zone.ZoneId})")
        appliances = zone.Appliances
        for appliance in appliances:
            print(f"    Appliance: {appliance.ApplianceId}")

Reading status

from dimplex_controller.models import ApplianceStatus

status_list = await client.get_appliance_overview(hub_id, [appliance_id_1, appliance_id_2])

for status in status_list:
    print(f"Room temperature: {status.RoomTemperature}°C")
    print(f"Target temperature: {status.ActiveSetPointTemperature}°C")
    print(f"EcoStart enabled: {status.EcoStartEnabled}")
    print(f"Comfort status: {status.ComfortStatus}")

A note on empty responses: when every requested appliance is offline (e.g. radiators switched off at the wall) the cloud returns HTTP 200 with an empty list. get_appliance_overview surfaces that as [] — it is not an error. If you need a stable id → status mapping, use get_appliance_overview_map(...), which fills in None for missing ids.

Sending control commands

from dimplex_controller.models import ApplianceModeSettings

# Enable EcoStart
await client.set_eco_start(hub_id, [appliance_id], True)

# Enable Open Window Detection
await client.set_open_window_detection(hub_id, [appliance_id], True)

# Activate Boost
await client.set_boost(hub_id, [appliance_id], temperature=25.0, duration_minutes=60)

# Set target temperature (rewrites timer period setpoints)
await client.set_target_temperature(hub_id, appliance_id, 21.5)

Energy reports

from dimplex_controller import parse_telemetry_points, summarise_energy

report = await client.get_tsi_energy_report(hub_id)
for appliance_id, telemetry in report.ApplianceTelemetryData.items():
    points = parse_telemetry_points(telemetry)
    daily = summarise_energy(points, mode="daily")
    lifetime = summarise_energy(points, mode="lifetime")
    print(f"{appliance_id}: today={daily.total_kwh} kWh, lifetime={lifetime.total_kwh} kWh")

parse_telemetry_points normalises firmware-varying point shapes. summarise_energy builds daily (local midnight) and lifetime totals per register. T1 (off-peak / cheaper) and T2 (peak / more expensive) must not be summed; parse with VALUE_KEY_T1 / VALUE_KEY_T2. With include_previous_period=True the cloud often returns full history — filter client-side rather than trusting days_back alone.

Compatibility

See docs/compatibility.md for the library ↔ Home Assistant version matrix.

Configuration

Environment variable Purpose
DIMPLEX_TOKENS_FILE Path to the JSON token store. Defaults to dimplex_tokens.json.

API reference

DimplexControl

Main client class. Construct with an aiohttp.ClientSession and a refresh_token.

Method Description
get_hubs() Returns list[Hub].
get_hub_zones(hub_id) Returns list[Zone] for a Hub.
get_zone(hub_id, zone_id) Returns a single Zone.
get_appliance_overview(hub_id, appliance_ids) Returns list[ApplianceStatus].
get_user_context() Returns UserContext.
get_appliance_features(hub_id, appliance_ids) Returns raw appliance feature data.
set_mode(hub_id, appliance_ids, mode, temperature) Set operation mode.
set_target_temperature(...) Placeholder for future target temperature control.
set_appliance_mode(hub_id, appliance_ids, settings) Send full mode settings.
set_eco_start(hub_id, appliance_ids, enabled) Toggle EcoStart.
set_open_window_detection(hub_id, appliance_ids, enabled) Toggle Open Window Detection.
get_tsi_energy_report(hub_id) Returns TsiEnergyReport.

Models

  • Hub — Hub metadata.
  • Zone — Zone metadata with linked Appliances.
  • Appliance — Appliance metadata.
  • ApplianceStatus — Live telemetry (room temperature, setpoints, comfort, etc.).
  • ApplianceModeSettings — Payload for mode changes.
  • TimerPeriod / TimerModeSettings — Timer schedule structures.
  • UserContext — Authenticated user profile.
  • TsiEnergyReport — Energy telemetry keyed by appliance.

Exceptions

  • DimplexError — Base exception.
  • DimplexAuthError — Authentication or token errors.
  • DimplexApiError — API returned a non-success status. Contains status and message.
  • DimplexConnectionError — Network-level failures.

Troubleshooting

Authentication failures

  • Verify that the refresh token in dimplex_tokens.json has not expired. Delete the file and re-run demo.py to capture a fresh one.
  • Ensure your network can reach login.microsoftonline.com and the Dimplex API endpoints.
  • If you have multi-factor authentication (MFA) enabled on your Dimplex account, the headless flow should still work because it uses a browser session you control manually.

DimplexAuthError

This means the API rejected the token. Common causes:

  • Token file is missing or corrupt.
  • The refresh token has expired (Azure B2C refresh tokens typically last 90 days).
  • The token was revoked from the Azure portal.

Fix: Delete dimplex_tokens.json and re-run the demo.py flow.

DimplexConnectionError

The library could not reach the GDHV API. Check:

  • Internet connectivity.
  • DNS resolution for api.gdhv.io (or whatever endpoint is configured in const.py).
  • No corporate firewall or proxy is blocking HTTPS traffic.

Telemetry parsing errors

If parse_telemetry_points returns an empty list, the API likely returned an unexpected schema for your firmware version. Please open an issue with a redacted example of the raw response so the parser can be updated.

Rate limiting

The GDHV cloud API has rate limits. If you hit them, back off for a few minutes before retrying. The library does not currently implement automatic retries with back-off.

get_appliance_overview returns an empty list

This is the cloud's normal response when every requested appliance is offline (e.g. radiators turned off at the wall, or a hub that has dropped off the network). It is not an error — get_appliance_overview returns [] and get_appliance_overview_map returns a dict of None values. Treat the call as a successful poll; the appliances will reappear in subsequent calls once they come back online. See the note in Reading status for details.

CLI

Install the package (or an editable install) to get the dimplex console script:

pip install dimplex-controller
export DIMPLEX_REFRESH_TOKEN=...   # never commit this
dimplex login
dimplex hubs
dimplex zones --hub <hub-id> -v
dimplex status <hub-id> <appliance-id>
dimplex energy <hub-id> --days 30
# control writes require --yes
dimplex boost <hub-id> <appliance-id> --minutes 60 --yes

Tokens can also come from a JSON file (--tokens-file / DIMPLEX_TOKENS_FILE) with keys refresh_token, access_token, expires_at. Secrets are redacted in CLI output unless --show-tokens is passed.

Contributing

Branch protection (main)

Pull requests into main must keep the ci GitHub Actions check green.

  • Changes under dimplex_controller/, tests/, or CI config run lint, pre-commit, and the pytest matrix (Python 3.10–3.13). The ci job fails if any of those fail.
  • Docs-only PRs still report a green ci without running the full matrix.

Direct pushes to main are blocked (PR + squash only; no force-push/delete). Commits must be signed (repo-wide rule).

Contributions are welcome! Please read the contributing guidelines before opening a pull request.

Key points:

  • Use Conventional Commits (feat:, fix:, chore:, etc.) — this drives the automated changelog and PyPI releases.
  • Run ruff check, ruff format --check and pytest locally before pushing.
  • Pre-commit hooks are available — run pre-commit install once.

Changelog

See CHANGELOG.md for version history.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dimplex_controller-0.10.1.tar.gz (33.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dimplex_controller-0.10.1-py3-none-any.whl (34.3 kB view details)

Uploaded Python 3

File details

Details for the file dimplex_controller-0.10.1.tar.gz.

File metadata

  • Download URL: dimplex_controller-0.10.1.tar.gz
  • Upload date:
  • Size: 33.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for dimplex_controller-0.10.1.tar.gz
Algorithm Hash digest
SHA256 aa9d0ab64fd7ec2fd97e54552037d3c87e3432b7a14156d20912d6a98fd466be
MD5 2a65a39db12bf11284091bcb81d1be62
BLAKE2b-256 fbe2021a3e125a8248a100bf0644409de7bf65b3d7f76b6cdb2b50ee788498b0

See more details on using hashes here.

Provenance

The following attestation bundles were made for dimplex_controller-0.10.1.tar.gz:

Publisher: release-please.yml on KRoperUK/dimplex-controller-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dimplex_controller-0.10.1-py3-none-any.whl.

File metadata

File hashes

Hashes for dimplex_controller-0.10.1-py3-none-any.whl
Algorithm Hash digest
SHA256 332ba689cac90780ba49de1e456efb139c90c8455eebb80f3fa650be4a6231e1
MD5 e24ee8692bcd9209ec4866900ed5a0bb
BLAKE2b-256 de5c11abdb65fb9253fe00d17c2fb8cbb285a64a5e65e1432506158cf3e29da8

See more details on using hashes here.

Provenance

The following attestation bundles were made for dimplex_controller-0.10.1-py3-none-any.whl:

Publisher: release-please.yml on KRoperUK/dimplex-controller-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page