[APP][Pro] Wallbox Charger - Smart EV charging technology made simple

Hi,

You’re probably doing nothing wrong. The app is broken and the original creator of the app doesn’t have the time or posiibility to fix it.

If you read the last 20-something posts here in this topic, you’ll have an idea of what your options are.

Few people have updated the app locally, but I don’t know how to do that.

I had Gemini, Grok & Claude.AI make a script (also somewhere here among the last posts) that I run using flows & arguments. It’s not flawless, but it works for me…

ERE (Emissiereductie-eenheden) is a way to get some money for every kWh you put in your car.

I give up on this WallBox app as it is not working for me.
I go for the Zaptec Go2 which has an official Homey app supporting it.

The day before yesterday I lost connection to the app and since then I cannot log in. Whenever I try, I get a quick reply, “404”. e845a815-9a7f-40ac-8c0c-ae9e1697df8e

Is there anything I can do to fix this? Everything has been running ok until now.

Same here with my own script. Stopped working yesterday. Even after a cooldown-period of 12+ hours always getting a 404… which is a bummer ofcourse.

Looks nice, would even be better if you have provided it as a PR.

Hey guys; the 404 error codes is because Wallbox changed their API’s. That broke the current implementation. I’m currently reverse engineering the APIs in order to fix it.

Please note that there is already an Github issue for it so please refrain from creating duplicates.

A new version which includes the fixes is available at:
* Wallbox Charger | Homey

Thank you, it’s seems to be working now. :slight_smile:

Thank You!!! Great work.

The app functions again correctly

Updated the script as well.

It will still not win a beauty-contest - i know -, but it works…

// === WALLBOX PULSAR PLUS - HOMEYSCRIPT (bijgewerkt juni 2026) ===
// Wijziging: nieuw auth-endpoint (user-api.wall-box.com/users/signin)

const username = 'login';       // myWallbox e-mail
const password = 'password'; // wijzig dit naar je nieuwe wachtwoord!
const MYCHARGERID = charger_id;

const API_BASE  = "https://api.wall-box.com/";
const AUTH_BASE = "https://user-api.wall-box.com/";

const URL_AUTH        = 'users/signin';
const URL_GROUPS      = 'v3/chargers/groups';
const URL_REMOTE      = `v3/chargers/${MYCHARGERID}/remote-action`;
const URL_SET_CURRENT = `v2/charger/${MYCHARGERID}`;

function toBase64(str) {
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
  let output = '';
  let i = 0;
  str = unescape(encodeURIComponent(str));
  while (i < str.length) {
    const a = str.charCodeAt(i++);
    const b = str.charCodeAt(i++);
    const c = str.charCodeAt(i++);
    const triplet = (a << 16) | (isNaN(b) ? 0 : b << 8) | (isNaN(c) ? 0 : c);
    output += chars.charAt((triplet >> 18) & 63) +
              chars.charAt((triplet >> 12) & 63) +
              (isNaN(b) ? '=' : chars.charAt((triplet >> 6) & 63)) +
              (isNaN(c) ? '=' : chars.charAt(triplet & 63));
  }
  return output;
}

async function getToken() {

  const auth = Buffer.from(username + ":" + password).toString("base64");

  const response = await fetch("https://user-api.wall-box.com/users/signin", {
    method: "GET",
    headers: {
      "Authorization": "Basic " + auth,
      "Partner": "wallbox",
      "Accept": "application/json"
    }
  });

  const raw = await response.text();

  console.log("LOGIN STATUS:", response.status);

  let data;

  try {
    data = JSON.parse(raw);
  } catch (e) {
    throw new Error("Login is not JSON: " + raw);
  }

  if (
    !data.data ||
    !data.data.attributes ||
    !data.data.attributes.token
  ) {
    throw new Error("No token received: " + raw);
  }

  return data.data.attributes.token;
}

async function getStatus(token) {
  const response = await fetch(API_BASE + URL_GROUPS, {
    method: 'GET',
    headers: { 'Authorization': 'Bearer ' + token, 'Accept': 'application/json' }
  });

  if (!response.ok) {
    const err = 'Groups fout: HTTP ' + response.status;
    console.log(err);
    await tag('Wallbox_error', err);
    throw new Error(err);
  }

  const data = await response.json();
  let found = false;
  let chargerStatus = {
    status: 0, chargingPower: 0, maxChargingCurrent: 0, addedEnergy: 0,
    locked: false, online: false, isCharging: false, carConnected: false
  };

  if (data.result && data.result.groups) {
    for (const group of data.result.groups) {
      if (group.chargers) {
        for (const ch of group.chargers) {
          if (ch.id === MYCHARGERID) {
            found = true;
            const isCharging = ch.status === 182;
            const isOnline = ch.status !== 0;
            chargerStatus = {
              status: ch.status || 0,
              chargingPower: ch.chargingPower || 0,
              maxChargingCurrent: ch.maxChargingCurrent || 0,
              addedEnergy: ch.addedEnergy || 0,
              locked: ch.locked === 1,
              online: isOnline,
              isCharging: isCharging,
              carConnected: isCharging
            };
            break;
          }
        }
      }
    }
  }

  if (!found) {
    const err = 'Charger niet gevonden in groups';
    console.log(err);
    await tag('Wallbox_error', err);
    throw new Error(err);
  }

  return chargerStatus;
}

async function startCharging(token) {
  const response = await fetch(API_BASE + URL_REMOTE, {
    method: 'POST',
    headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' },
    body: JSON.stringify({ action: 1 })
  });
  if (!response.ok) {
    const err = 'Start fout: HTTP ' + response.status;
    console.log(err);
    await tag('Wallbox_error', err);
    throw new Error(err);
  }
}

async function stopCharging(token) {
  const response = await fetch(API_BASE + URL_REMOTE, {
    method: 'POST',
    headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' },
    body: JSON.stringify({ action: 2 })
  });
  if (!response.ok) {
    const err = 'Stop fout: HTTP ' + response.status;
    console.log(err);
    await tag('Wallbox_error', err);
    throw new Error(err);
  }
}

async function setAmps(token, amps) {
  if (amps < 6 || amps > 32) {
    const err = 'Amps moet tussen 6 en 32 liggen';
    console.log(err);
    await tag('Wallbox_error', err);
    throw new Error(err);
  }
  const response = await fetch(API_BASE + URL_SET_CURRENT, {
    method: 'PUT',
    headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' },
    body: JSON.stringify({ maxChargingCurrent: amps })
  });
  if (!response.ok) {
    const err = 'Set amps fout: HTTP ' + response.status;
    console.log(err);
    await tag('Wallbox_error', err);
    throw new Error(err);
  }
}

if (!args || args.length === 0 || typeof args[0] !== 'string') {
  const err = 'Geef een argument: status | start | stop | set 6 | set 10';
  console.log(err);
  await tag('Wallbox_error', err);
  throw new Error(err);
}

const arg = args[0].trim().toLowerCase();

try {
  const token = await getToken();

  if (arg === 'status') {
    const status = await getStatus(token);
    await tag('Wallbox_status', status.status);
    await tag('Wallbox_chargingPower', status.chargingPower);
    await tag('Wallbox_maxCurrent', status.maxChargingCurrent);
    await tag('Wallbox_addedEnergy', status.addedEnergy);
    await tag('Wallbox_locked', status.locked ? 'true' : 'false');
    await tag('Wallbox_online', status.online ? 'true' : 'false');
    await tag('Wallbox_isCharging', status.isCharging ? 'true' : 'false');
    await tag('Wallbox_carConnected', status.carConnected ? 'true' : 'false');
    await tag('Wallbox_lastUpdate', new Date().toLocaleString());
    await tag('Wallbox_error', '');
    console.log('Status tags aangemaakt:', status);
    return 'Status opgehaald en tags gezet';

  } else if (arg === 'start') {
    await startCharging(token);
    await tag('Wallbox_lastAction', 'Gestart');
    await tag('Wallbox_error', '');
    return 'Gestart';

  } else if (arg === 'stop') {
    await stopCharging(token);
    await tag('Wallbox_lastAction', 'Gestopt');
    await tag('Wallbox_error', '');
    return 'Gestopt';

  } else if (arg.startsWith('set ')) {
    const ampsStr = arg.split(' ')[1];
    const amps = parseInt(ampsStr, 10);
    if (isNaN(amps)) throw new Error('Ongeldig amps: ' + ampsStr);
    await setAmps(token, amps);
    await tag('Wallbox_lastAction', `Set ${amps}A`);
    await tag('Wallbox_maxCurrent', amps);
    await tag('Wallbox_error', '');
    return `Set ${amps}A`;

  } else {
    throw new Error('Ongeldig argument: ' + arg);
  }

} catch (err) {
  console.log('Hoofdfout:', err.message);
  await tag('Wallbox_error', err.message);
  throw err;
}

Thanks for the update. I think there is many that appreciate you work, but…a little but. Your ‘Stop Charging’ card return (still) with a 403, but in version 0.3.1 you had a ‘Pause Charging’ card, that did the work, stopped the charging. If you find time, could you kindly restore the ‘Pause Charging’ card, or find a fix for the Stop Charging card :slight_smile:

Hey, this is a weerd bug. So basically I removed the card because Homey added new capabilities specific to pausing and resuming charging. So instead of app specific cards I now rely on the Homey default capabilities. Within the app this still uses the exact same function as the pause /resume action cards. In that functionality nothing has changed. Checking the API this still looks the appropriate API call in order to do it ( I have to rely on other opensource code that describes the Wallbox API because I don’t have access to the official API documentation).

Unfortunately, my wallbox has been disconnected for quite some time and I can’t actively debug it on my own. Hopefully that changes in the foreseeable future, but until then; I kinda hope that someone else could debug this issue and make a nice PR.

I also updated the script. This time it reflects the correct statusses. I noticed in my previous script there were statusses that didn’t reflect the real status (connected/charging/…). This should now be ok.

// === WALLBOX PULSAR PLUS - HOMEYSCRIPT (bijgewerkt juni 2026) ===

const username = 'login';       // myWallbox e-mail
const password = 'password'; // wijzig dit naar je nieuwe wachtwoord!
const MYCHARGERID = chargerid;

const API_BASE  = "https://api.wall-box.com/";
const URL_GROUPS      = 'v3/chargers/groups';
const URL_REMOTE      = `v3/chargers/${MYCHARGERID}/remote-action`;
const URL_SET_CURRENT = `v2/charger/${MYCHARGERID}`;

function toBase64(str) {
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
  let output = '';
  let i = 0;
  str = unescape(encodeURIComponent(str));
  while (i < str.length) {
    const a = str.charCodeAt(i++);
    const b = str.charCodeAt(i++);
    const c = str.charCodeAt(i++);
    const triplet = (a << 16) | (isNaN(b) ? 0 : b << 8) | (isNaN(c) ? 0 : c);
    output += chars.charAt((triplet >> 18) & 63) +
              chars.charAt((triplet >> 12) & 63) +
              (isNaN(b) ? '=' : chars.charAt((triplet >> 6) & 63)) +
              (isNaN(c) ? '=' : chars.charAt(triplet & 63));
  }
  return output;
}

async function getToken() {

  const auth = Buffer.from(username + ":" + password).toString("base64");

  const response = await fetch("https://user-api.wall-box.com/users/signin", {
    method: "GET",
    headers: {
      "Authorization": "Basic " + auth,
      "Partner": "wallbox",
      "Accept": "application/json"
    }
  });

  const raw = await response.text();

  let data;

  try {
    data = JSON.parse(raw);
  } catch (e) {
    throw new Error("Login is not JSON: " + raw);
  }

  if (
    !data.data ||
    !data.data.attributes ||
    !data.data.attributes.token
  ) {
    throw new Error("No token received: " + raw);
  }

  return data.data.attributes.token;
}

async function getStatus(token) {
  const response = await fetch(API_BASE + URL_GROUPS, {
    method: 'GET',
    headers: { 'Authorization': 'Bearer ' + token, 'Accept': 'application/json' }
  });

  if (!response.ok) {
    const err = 'Groups fout: HTTP ' + response.status;
    console.log(err);
    await tag('Wallbox_error', err);
    throw new Error(err);
  }

  const data = await response.json();
  let found = false;
  let chargerStatus = {
  status: 0,
  chargingPower: 0,
  maxChargingCurrent: 0,
  addedEnergy: 0,
  locked: false,
  online: false,
  isCharging: false,
  carConnected: false
};

  if (data.result && data.result.groups) {
    for (const group of data.result.groups) {
      if (group.chargers) {
        for (const ch of group.chargers) {
         if (ch.id === MYCHARGERID) {
  found = true;

const statusCode = ch.status || 0;

const isOnline = statusCode !== 0;

// Alleen werkelijk laden als er effectief vermogen loopt
const isCharging = (ch.chargingPower || 0) > 100;

// Auto verbonden?
const carConnected = statusCode !== 161;

chargerStatus = {
  status: statusCode,
  chargingPower: ch.chargingPower || 0,
  maxChargingCurrent: ch.maxChargingCurrent || 0,
  addedEnergy: ch.addedEnergy || 0,
  locked: ch.locked === 1,
  online: isOnline,
  isCharging: isCharging,
  carConnected: carConnected
};

break;
}

        }
      }
    }
  }

  if (!found) {
    const err = 'Charger niet gevonden in groups';
    console.log(err);
    await tag('Wallbox_error', err);
    throw new Error(err);
  }

  return chargerStatus;
}

async function startCharging(token) {
  const response = await fetch(API_BASE + URL_REMOTE, {
    method: 'POST',
    headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' },
    body: JSON.stringify({ action: 1 })
  });
  if (!response.ok) {
    const err = 'Start fout: HTTP ' + response.status;
    console.log(err);
    await tag('Wallbox_error', err);
    throw new Error(err);
  }
}

async function stopCharging(token) {
  const response = await fetch(API_BASE + URL_REMOTE, {
    method: 'POST',
    headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' },
    body: JSON.stringify({ action: 2 })
  });
  if (!response.ok) {
    const err = 'Stop fout: HTTP ' + response.status;
    console.log(err);
    await tag('Wallbox_error', err);
    throw new Error(err);
  }
}

async function setAmps(token, amps) {
  if (amps < 6 || amps > 32) {
    const err = 'Amps moet tussen 6 en 32 liggen';
    console.log(err);
    await tag('Wallbox_error', err);
    throw new Error(err);
  }
  const response = await fetch(API_BASE + URL_SET_CURRENT, {
    method: 'PUT',
    headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' },
    body: JSON.stringify({ maxChargingCurrent: amps })
  });
  if (!response.ok) {
    const err = 'Set amps fout: HTTP ' + response.status;
    console.log(err);
    await tag('Wallbox_error', err);
    throw new Error(err);
  }
}

if (!args || args.length === 0 || typeof args[0] !== 'string') {
  const err = 'Geef een argument: status | start | stop | set 6 | set 10';
  console.log(err);
  await tag('Wallbox_error', err);
  throw new Error(err);
}

const arg = args[0].trim().toLowerCase();

try {
  const token = await getToken();

  if (arg === 'status') {
    const status = await getStatus(token);
    await tag('Wallbox_status', status.status);
    await tag('Wallbox_chargingPower', status.chargingPower);
    await tag('Wallbox_maxCurrent', status.maxChargingCurrent);
    await tag('Wallbox_addedEnergy', status.addedEnergy);
    await tag('Wallbox_locked', status.locked ? 'true' : 'false');
    await tag('Wallbox_online', status.online ? 'true' : 'false');
    await tag('Wallbox_isCharging', status.isCharging ? 'true' : 'false');
    await tag('Wallbox_carConnected', status.carConnected ? 'true' : 'false');
    await tag('Wallbox_lastUpdate', new Date().toLocaleString());
    await tag('Wallbox_error', '');
    console.log('Status tags aangemaakt:', status);
    return 'Status opgehaald en tags gezet';

  } else if (arg === 'start') {
    await startCharging(token);
    await tag('Wallbox_lastAction', 'Gestart');
    await tag('Wallbox_error', '');
    return 'Gestart';

  } else if (arg === 'stop') {
    await stopCharging(token);
    await tag('Wallbox_lastAction', 'Gestopt');
    await tag('Wallbox_error', '');
    return 'Gestopt';

  } else if (arg.startsWith('set ')) {
    const ampsStr = arg.split(' ')[1];
    const amps = parseInt(ampsStr, 10);
    if (isNaN(amps)) throw new Error('Ongeldig amps: ' + ampsStr);
    await setAmps(token, amps);
    await tag('Wallbox_lastAction', `Set ${amps}A`);
    await tag('Wallbox_maxCurrent', amps);
    await tag('Wallbox_error', '');
    return `Set ${amps}A`;

  } else {
    throw new Error('Ongeldig argument: ' + arg);
  }

} catch (err) {
  console.log('Hoofdfout:', err.message);
  await tag('Wallbox_error', err.message);
  throw err;
}

Okay, so finally I’ve fixed my own wallbox so I can do some proper testing again. The 403 for pausing / starting charging only occurs for me when no car is connected. When there is a car connected it executes with no error. The message could definitely improve so I would publish a new version with better feedback.

Thanks for sharing..