# Thermia Diplomat heat pump control via Homey

**URL:** <https://community.homey.app/t/thermia-diplomat-heat-pump-control-via-homey/152739>\
**Category:** Devices\
**Tags:** homeyscript, flow, homey-pro\
**Created:** [March 20, 2026, 4:21pm UTC](https://community.homey.app/t/thermia-diplomat-heat-pump-control-via-homey/152739 "2026-03-20T16:21:06Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![bentrik](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/bentrik/32/120862_2.png) [@bentrik](https://community.homey.app/u/bentrik)\
**Post date:** [March 20, 2026, 4:21pm UTC](https://community.homey.app/t/thermia-diplomat-heat-pump-control-via-homey/152739/1 "2026-03-20T16:21:06Z")

</div>

I wanted to control the hot water production on my Thermia Diplomat Inverter M heat pump from Homey Pro, but there was no official Homey app available. After some digging through the Thermia Online web app using browser DevTools, I managed to reverse-engineer the API and build a working HomeyScript integration. Sharing it here in case others find it useful.

Tested on:

- Homey Pro 2026

- Thermia Diplomat Inverter M [230]

- Thermia Online account (online.thermia.se)

* * *

**What it does**

- Turns hot water production ON or OFF via Homey Flows

- Reads temperatures from the heat pump every 10 minutes (outdoor, supply line, brine in, hot water)

- Stores temperatures as Homey Logic variables — visible in Insights with graphs over time

- Auto-renews the authentication token so it never expires

- Sends a push notification if authentication fails

My setup turns hot water OFF at 06:00 and ON again at 22:00 to avoid peak electricity hours, with a safety flow that turns it back on if the water temperature drops below 50°C during the day.

* * *

**Prerequisites**

- HomeyScript app installed on your Homey

- A Thermia Online account at online.thermia.se

- A PC/Mac browser with DevTools (Chrome or Edge recommended)

- Basic familiarity with Homey Flows

* * *

**Step 1 — Get your refresh token**

The Thermia API uses token-based authentication. You need to get an initial refresh token from your browser:

1. Go to online.thermia.se and log in

2. Open DevTools (F12) → Network tab

3. Refresh the page

4. Find the request named **token** in the list

5. Click it → **Response** tab

6. Copy the value of the `refresh_token` field — it is a long ~1400 character string with 5 dot-separated segments starting with `eyJ`

⚠ Do not copy the `access_token` or `id_token` — these are shorter 3-part strings and will not work.

* * *

**Step 2 — Find your installation ID**

1. In DevTools with the Network tab still open, look for requests with URLs containing `/installationstatus/` or `/Registers/Installations/`

2. Your installation ID is the number in those URLs — for example `/Registers/Installations/XXXXXXX/Groups/`

3. Note this number — you will need to replace `XXXXXXX` with it in the scripts below

* * *

**Step 3 — Create Homey Logic variables**

Create these variables in Homey → Logic → Variables:

| Name | Type |
| --- | --- |
| `thermia_refresh_token` | Text |
| `thermia_hotwater_command` | Text |
| `thermia_hotwater_status` | Text |
| `thermia_temp_hot_water` | Number |
| `thermia_temp_outdoor` | Number |
| `thermia_temp_supply` | Number |
| `thermia_temp_brine_in` | Number |

* * *

**Step 4 — Store the refresh token**

Create a new HomeyScript called `thermia_set_token` and run it once with your token pasted in:

javascript

```auto
const newRefreshToken = "PASTE_YOUR_REFRESH_TOKEN_HERE";

const allVars = Object.values(await Homey.logic.getVariables());
const refreshVar = allVars.find(v => v.name === "thermia_refresh_token");

await Homey.logic.updateVariable({
  id: refreshVar.id,
  variable: { value: newRefreshToken }
});

log("Stored length: " + newRefreshToken.length);
log("Segments: " + newRefreshToken.split(".").length);
log("✅ Done — expect length ~1400 and segments 5");

```

* * *

**Step 5 — The scripts**

⚠ Replace `XXXXXXX` with your own installation ID in both scripts before running them.

**Script 1: `thermia_read_temperatures`**

javascript

```auto
const token_var = await Homey.logic.getVariables();
const allVars = Object.values(token_var);
const refreshVar = allVars.find(v => v.name === "thermia_refresh_token");

const loginBody = new URLSearchParams();
loginBody.append("grant_type", "refresh_token");
loginBody.append("client_id", "09ea4903-9e95-45fe-ae1f-e3b7d32fa385");
loginBody.append("refresh_token", refreshVar.value.trim());
loginBody.append("redirect_uri", "https://www.online.thermia.se/login");

const loginRaw = await fetch("https://thermialogin.b2clogin.com/thermialogin.onmicrosoft.com/b2c_1a_signuporsigninonline/oauth2/v2.0/token", {
  method: "POST",
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
  body: loginBody
});
const loginData = JSON.parse(await loginRaw.text());

if (!loginData.access_token) {
  log("❌ Token refresh failed - paste a new refresh_token into Homey Logic");
  log(JSON.stringify(loginData));
  await Homey.notifications.createNotification({
    excerpt: "⚠️ Thermia token expired — paste a new refresh_token into Homey Logic"
  });
  return;
}

if (loginData.refresh_token) {
  await Homey.logic.updateVariable({
    id: refreshVar.id,
    variable: { value: loginData.refresh_token }
  });
  log("✅ Refresh token renewed (" + loginData.refresh_token.length + " chars)");
}

const BASE = "https://online-classic-serviceapi.azurewebsites.net/api/v1";
const INSTALL = "XXXXXXX"; // ← Replace with your installation ID
const headers = { "Authorization": "Bearer " + loginData.access_token, "Accept": "application/json" };

const res = await fetch(BASE + "/Registers/Installations/" + INSTALL + "/Groups/REG_GROUP_TEMPERATURES", { headers });
const data = JSON.parse(await res.text());

const mapping = {
  "REG_HOT_WATER_TEMPERATURE": "thermia_temp_hot_water",
  "REG_OUTDOOR_TEMPERATURE": "thermia_temp_outdoor",
  "REG_SUPPLY_LINE": "thermia_temp_supply",
  "REG_BRINE_IN": "thermia_temp_brine_in"
};

for (const reg of data) {
  const varName = mapping[reg.registerName];
  if (!varName) continue;
  const homeyVar = allVars.find(v => v.name === varName);
  if (!homeyVar) { log("⚠️ Variable not found: " + varName); continue; }
  await Homey.logic.updateVariable({ id: homeyVar.id, variable: { value: reg.registerValue } });
  log(varName + " = " + reg.registerValue + "°C");
}

```

**Script 2: `thermia_hotwater`**

javascript

```auto
// ── CONFIGURATION ───────────────────────────────────────────────
const CLIENT_ID = "09ea4903-9e95-45fe-ae1f-e3b7d32fa385";
const TOKEN_URL = "https://thermialogin.b2clogin.com/thermialogin.onmicrosoft.com/b2c_1a_signuporsigninonline/oauth2/v2.0/token";
const REDIRECT = "https://www.online.thermia.se/login";
const BASE = "https://online-classic-serviceapi.azurewebsites.net/api/v1";
const INSTALL = "XXXXXXX"; // ← Replace with your installation ID
const REG_SPEC_ID = 29442; // Hot water on/off register — may differ on other models

// ── STEP 1: READ HOMEY LOGIC VARIABLES ──────────────────────────
const allVars = Object.values(await Homey.logic.getVariables());
const refreshVar = allVars.find(v => v.name === "thermia_refresh_token");
const commandVar = allVars.find(v => v.name === "thermia_hotwater_command");
const statusVar = allVars.find(v => v.name === "thermia_hotwater_status");

if (!refreshVar) { log("❌ Missing variable: thermia_refresh_token"); return; }
if (!commandVar) { log("❌ Missing variable: thermia_hotwater_command"); return; }

const command = (commandVar.value || "").trim().toLowerCase();
log("Command: " + command);

// ── STEP 2: AUTHENTICATE WITH THERMIA ───────────────────────────
const loginBody = new URLSearchParams();
loginBody.append("grant_type", "refresh_token");
loginBody.append("client_id", CLIENT_ID);
loginBody.append("refresh_token", refreshVar.value.trim());
loginBody.append("redirect_uri", REDIRECT);

const loginRaw = await fetch(TOKEN_URL, {
  method: "POST",
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
  body: loginBody
});
const loginData = JSON.parse(await loginRaw.text());

if (!loginData.access_token) {
  log("❌ Token refresh failed - paste a new refresh_token into Homey Logic");
  log(JSON.stringify(loginData));
  await Homey.notifications.createNotification({
    excerpt: "⚠️ Thermia token expired — paste a new refresh_token into Homey Logic"
  });
  return;
}

if (loginData.refresh_token) {
  await Homey.logic.updateVariable({
    id: refreshVar.id,
    variable: { value: loginData.refresh_token }
  });
  log("✅ Refresh token renewed (" + loginData.refresh_token.length + " chars)");
}

const headers = {
  "Authorization": "Bearer " + loginData.access_token,
  "Accept": "application/json",
  "Content-Type": "application/json"
};

// ── STEP 3: READ CURRENT HOT WATER STATE ────────────────────────
const getRes = await fetch(BASE + "/Registers/Installations/" + INSTALL + "/Groups/REG_GROUP_HOT_WATER", { headers });
const getData = JSON.parse(await getRes.text());
const currentValue = getData[0].registerValue;
log("Current: " + (currentValue === 1 ? "ON" : "OFF"));

// ── STEP 4: HANDLE "STATUS" COMMAND ─────────────────────────────
if (command === "status") {
  const s = currentValue === 1 ? "on" : "off";
  if (statusVar) await Homey.logic.updateVariable({ id: statusVar.id, variable: { value: s } });
  log("Status: " + s);
  return s;
}

// ── STEP 5: VALIDATE THE COMMAND ────────────────────────────────
const targetValue = command === "on" ? 1 : command === "off" ? 0 : -1;
if (targetValue === -1) {
  log("❌ Unknown command: " + command + " (use on/off/status)");
  return;
}

// ── STEP 6: SKIP IF ALREADY IN TARGET STATE ──────────────────────
if (currentValue === targetValue) {
  log("Already " + command.toUpperCase() + " — no change needed");
  return command;
}

// ── STEP 7: SEND THE COMMAND TO THE HEAT PUMP ───────────────────
const uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c => {
  const r = Math.random() * 16 | 0;
  return (c === "x" ? r : (r & 0x3 | 0x8)).toString(16);
});

const postRes = await fetch(BASE + "/Registers/Installations/" + INSTALL + "/Registers", {
  method: "POST",
  headers,
  body: JSON.stringify({
    registerSpecificationId: REG_SPEC_ID,
    registerValue: targetValue,
    clientUuid: uuid
  })
});
log("Set " + command.toUpperCase() + ": HTTP " + postRes.status);

// ── STEP 8: SAVE THE NEW STATE TO HOMEY ─────────────────────────
if (statusVar) {
  await Homey.logic.updateVariable({ id: statusVar.id, variable: { value: command } });
}

return command;

```

* * *

**Step 6 — Set up Flows**

**Temperature polling (every 10 minutes):**

- WHEN: Every 10 minutes

- THEN: Run script `thermia_read_temperatures`

**Hot water OFF at 06:00:**

- WHEN: Time is 06:00

- THEN: Set `thermia_hotwater_command` to `off`

- THEN: Run script `thermia_hotwater`

**Hot water ON at 22:00:**

- WHEN: Time is 22:00

- THEN: Set `thermia_hotwater_command` to `on`

- THEN: Run script `thermia_hotwater`

**Optional — Safety rescue if water gets too cold:**

- WHEN: Every 10 minutes

- AND: Time is between 06:01 and 21:59

- AND: `thermia_temp_hot_water` is less than 50

- THEN: Set `thermia_hotwater_command` to `on`

- THEN: Run script `thermia_hotwater`

* * *

**Notes**

- The `client_id` in the scripts is the public Thermia Online app ID — it is the same for all users and not a secret

- The `REG_SPEC_ID` (29442) is the register ID for hot water on/off on the Diplomat Inverter M — it may be different on other Thermia models. If it doesn’t work, check the DevTools network traffic when toggling hot water in the Thermia web app and look for the `registerSpecificationId` value in the POST request payload

- The refresh token auto-renews on every script run, so you should never need to paste a new one manually as long as the polling flow runs at least once a day

- Temperature graphs appear automatically in Homey Insights once the polling flow has been running for a while

---

<div class="post-metadata">

**Author:** ![system](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/system/32/1_2.png) [@system](https://community.homey.app/u/system)\
**Post date:** [March 20, 2026, 4:22pm UTC](https://community.homey.app/t/thermia-diplomat-heat-pump-control-via-homey/152739/2 "2026-03-20T16:22:55Z")

</div>

Unlisted on March 20, 2026, 4:22pm UTC



---

<div class="post-metadata">

**Author:** ![system](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/system/32/1_2.png) [@system](https://community.homey.app/u/system)\
**Post date:** [March 21, 2026, 4:11am UTC](https://community.homey.app/t/thermia-diplomat-heat-pump-control-via-homey/152739/3 "2026-03-21T04:11:15Z")

</div>

Listed on March 21, 2026, 4:11am UTC



---

<div class="post-metadata">

**Author:** ![FlorisVandeVijver](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/florisvandevijver/32/60097_2.png) [@FlorisVandeVijver](https://community.homey.app/u/FlorisVandeVijver)\
**Post date:** [March 21, 2026, 10:43am UTC](https://community.homey.app/t/thermia-diplomat-heat-pump-control-via-homey/152739/4 "2026-03-21T10:43:09Z")

</div>

an alternative would be to use husdata hardware ([https://husdata.se](https://husdata.se)) with husdata app ( [Husdata | Homey](https://homey.app/en-be/app/se.husdata/Husdata/) ) which works for different types of heatpumps, including Thermia

---

<div class="post-metadata">

**Author:** ![bentrik](https://sea1.discourse-cdn.com/flex025/user_avatar/community.homey.app/bentrik/32/120862_2.png) [@bentrik](https://community.homey.app/u/bentrik)\
**Post date:** [March 22, 2026, 8:39am UTC](https://community.homey.app/t/thermia-diplomat-heat-pump-control-via-homey/152739/5 "2026-03-22T08:39:16Z")

</div>

I did find that solution before trying this, but thought it was unnecessary to buy additional hardware when the web interface existed.

That said though, I still struggle a bit with the token expiration. The best would be to find a way for homey to log in with my credentials, but I haven’t found a way to do so yet.
