miele
v0.1.0
Published
A lightweight TypeScript client for the Miele 3rd Party API
Maintainers
Readme
miele
A lightweight, fully-typed TypeScript client for the Miele 3rd Party API — control and monitor Miele@home appliances (washing machines, ovens, dishwashers, fridges, robot vacuums, ...) from Node.js.
- Zero runtime dependencies — uses the global
fetch. - Promise-based,
async/awaitfriendly. - Ships ESM and CJS builds with bundled type declarations.
- Covers devices, actions, programs, filling levels, and the live Server-Sent-Events stream.
Installation
npm install mieleRequires Node.js 18 or later.
Getting API credentials
You need a client id/secret from Miele's developer portal: register at https://www.miele.com/f/com/en/register_api.aspx. Miele uses the OAuth2 Authorization Code flow — your app redirects the user to Miele to log in with their Miele@home account, then exchanges the returned code for an access token.
import { getAuthorizationUrl, exchangeAuthorizationCode } from "miele";
// 1. Redirect the user here:
const url = getAuthorizationUrl({
clientId: process.env.MIELE_CLIENT_ID!,
redirectUri: "https://your-app.example.com/callback",
// The locale the user's Miele@home account was registered with, e.g. "en-GB", "de-DE".
vg: "en-GB",
state: "csrf-token",
});
// 2. In your redirect_uri handler, exchange the `code` query param for a token:
const token = await exchangeAuthorizationCode({
clientId: process.env.MIELE_CLIENT_ID!,
clientSecret: process.env.MIELE_CLIENT_SECRET!,
code: req.query.code,
redirectUri: "https://your-app.example.com/callback",
vg: "en-GB",
});
// token.access_token, token.refresh_token, token.expires_in, ...Access tokens expire; use refreshAccessToken with the stored refresh_token
to get a new one without involving the user again:
import { refreshAccessToken } from "miele";
const refreshed = await refreshAccessToken({
clientId: process.env.MIELE_CLIENT_ID!,
clientSecret: process.env.MIELE_CLIENT_SECRET!,
refreshToken: token.refresh_token!,
vg: "en-GB",
});Usage
import { MieleClient, ProcessAction, Light } from "miele";
const client = new MieleClient({
// A static token, or a function called before every request — useful to
// plug in your own refresh-on-demand logic.
token: () => getCurrentAccessToken(),
language: "en",
});
const devices = await client.listDevices();
for (const [deviceId, device] of Object.entries(devices)) {
console.log(device.ident.deviceName || device.ident.type.value_localized);
console.log("status:", device.state.status.value_localized);
}
// Fetch a single device
const device = await client.getDevice(deviceId);
// See what's currently controllable
const actions = await client.getActions(deviceId);
// Start / stop / pause
await client.start(deviceId);
await client.stop(deviceId);
await client.pause(deviceId);
// Power
await client.powerOn(deviceId);
await client.powerOff(deviceId);
// Fridge/freezer target temperature
await client.setTargetTemperature(deviceId, 4);
// Hood light and fan
await client.setLight(deviceId, Light.Enable);
await client.setVentilationStep(deviceId, 2);
// Low-level escape hatch — send any combination of action fields
await client.sendAction(deviceId, { processAction: ProcessAction.Start });
// Remote-start an available program
const programs = await client.getPrograms(deviceId);
await client.startProgram(deviceId, { programId: programs[0].programId });
// Consumables (dishwasher salt/rinse aid, washer detergent, hood filters, ...)
const fillingLevels = await client.getFillingLevels(deviceId);Live updates (Server-Sent Events)
const controller = new AbortController();
for await (const event of client.listenEvents({ signal: controller.signal })) {
if (event.type === "devices") {
console.log("device update", event.data);
} else if (event.type === "actions") {
console.log("actions update", event.data);
}
// event.type === "ping" keeps the connection alive and can be ignored
}Error handling
Requests that receive a non-2xx response reject with MieleApiError
(status, statusText, body); token requests reject with MieleAuthError.
import { MieleApiError } from "miele";
try {
await client.start(deviceId);
} catch (err) {
if (err instanceof MieleApiError) {
console.error(err.status, err.body);
}
throw err;
}API coverage
| Method | Endpoint |
| --- | --- |
| listDevices() | GET /devices |
| getDevice(id) | GET /devices/{id} |
| getActions(id) | GET /devices/{id}/actions |
| sendAction(id, action) | PUT /devices/{id}/actions |
| getPrograms(id) | GET /devices/{id}/programs |
| startProgram(id, request) | PUT /devices/{id}/programs |
| getRooms(id) / setRoom(id, data) | GET/PUT /devices/{id}/rooms |
| getFillingLevels([id]) | GET /devices/[{id}/]fillingLevels |
| getFailureDetails(id) | GET /devices/{id}/failureDetails |
| getCamera(id) | GET /devices/{id}/camera |
| listenEvents() | GET /devices/all/events (SSE) |
Plus convenience wrappers around sendAction: start, stop, pause,
powerOn, powerOff, setLight, setVentilationStep, setTargetTemperature.
License
MIT
