Wallbox Pulsar plus charger lock/unlock pause/resume

Had a quick look as I didn’t have much time.
I’ve updated the script so when you unlock the Wallbox it will set three tags:

  • This month total energy in kWh ( needs to /1000 )
  • This months charging time in seconds
  • This months total sessions
    It will be set and updated globally whenever you unlock you wallbox.
    If you need the values you can call them wherever you can use tags and look for HomeyScript tags. You will find them there.
    afbeelding
const username = '<your username (usually email address)';
const password = '<your password>';
const MYCHARGERID = 000000; // ID of your charger
const BASEURL = "https://api.wall-box.com/";
const URL_AUTHENTICATION = 'auth/token/user';
const URL_UNLOCKCHARGER = 'v2/charger/';
const URL_PAUSECHARGER = 'v3/chargers/';
const URL_PAUSECHARGER_ACTION = '/remote-action';

var token = '';
var unlocked = { 'locked': 0 };
var locked = { 'locked': 1 };
var resume = { 'action': 1 };
var pause = { 'action': 2 };


/**
 * Get JWT Token from wallbox using login and password
 */
async function connectToWallbox() {
  let connect = await fetch(BASEURL + URL_AUTHENTICATION, {
    headers: {
      'Authorization': 'Basic ' + Buffer.from(username + ":" + password).toString('base64'),
      'Accept': 'application/json, text/plain, */*',
      'Content-Type': 'application/json;charset=utf-8',
    }
  }).then(response => response.json());
  return connect.jwt;
}

/**
 * set wallbox maxCharging function with token from Connect function
 */
async function setMaxCharging(token, max) {
  let maxCharging = { 'maxChargingCurrent': max };
  let mC = await fetch(BASEURL + URL_UNLOCKCHARGER + MYCHARGERID, {

    method: 'PUT',
    headers: {
      'Authorization': 'Bearer ' + token,
      'Accept': 'application/json, text/plain, */*',
      'Content-Type': 'application/json;charset=utf-8',

    },
    body: JSON.stringify(maxCharging)
  }).then(response => response.json());
 
  if (mC.error === true) {
    return mC.message;
  }
  return true;
}

/**
 * Unlock wallbox function with token from Connect function
 */
async function unlockWallbox(token) {
  let unlock = await fetch(BASEURL + URL_UNLOCKCHARGER + MYCHARGERID, {

    method: 'PUT',
    headers: {
      'Authorization': 'Bearer ' + token,
      'Accept': 'application/json, text/plain, */*',
      'Content-Type': 'application/json;charset=utf-8',

    },
    body: JSON.stringify(unlocked)
  }).then(response => response.json());
  if (unlock.error === true) {
    return unlock.message;
  }
  await tag('WBTotalSessionsMonth', unlock.data.chargerData.resume.totalSessions );
  await tag('WBTotalCharchingMonth', unlock.data.chargerData.resume.charchingTime );
  await tag('WBTotalEnergyMonth', unlock.data.chargerData.resume.totalEnergy );
  return true;
}

/**
 * Lock wallbox function with token from Connect function
 */
async function lockWallbox(token) {
  let lock = await fetch(BASEURL + URL_UNLOCKCHARGER + MYCHARGERID, {

    method: 'PUT',
    headers: {
      'Authorization': 'Bearer ' + token,
      'Accept': 'application/json, text/plain, */*',
      'Content-Type': 'application/json;charset=utf-8',

    },
    body: JSON.stringify(locked)
  }).then(response => response.json());
  if (lock.error === true) {
    return lock.message;
  }
  return true;
}

/**
 * Resume wallbox function with token from Connect function
 */
async function resumeWallbox(token) {
  let wbResume = await fetch(BASEURL + URL_PAUSECHARGER + MYCHARGERID + URL_PAUSECHARGER_ACTION, {

    method: 'POST',
    headers: {
      'Authorization': 'Bearer ' + token,
      'Accept': 'application/json, text/plain, */*',
      'Content-Type': 'application/json;charset=utf-8',

    },
    body: JSON.stringify(resume)
  }).then(response => response.json());
  if ( wbResume.error === true ) {
    return wbResume.message;
  }
  return true;
}

/**
 * Pause wallbox function with token from Connect function
 */
async function pauseWallbox(token) {
  let wbPause = await fetch(BASEURL + URL_PAUSECHARGER + MYCHARGERID + URL_PAUSECHARGER_ACTION, {

    method: 'POST',
    headers: {
      'Authorization': 'Bearer ' + token,
      'Accept': 'application/json, text/plain, */*',
      'Content-Type': 'application/json;charset=utf-8',

    },
    body: JSON.stringify(pause)
  }).then(response => response.json());
  if ( wbPause.error === true ) {
    return wbPause.message;
  }
  return true;
}

// Make sure this script works with 1 single argument
let actionArgs = args[0].split('=');

// Make sure this script works with 1 single argument
if (typeof actionArgs[0] !== 'string') {
  throw new Error('Script must be run from a flow with a string variable');
}

switch (actionArgs[0]) {
  case "unlock":
    token = await connectToWallbox();
    result = await unlockWallbox(token);
    // Optional. You can leave the next 3 lines out and place them in a flow
    if (result === true) {
      say('Wallbox is ontgrendeld');
    } else say( result );
    break;
  case "lock":
    token = await connectToWallbox();
    result = await lockWallbox(token);
    // Optional. You can leave the next 3 lines out and place them in a flow
    if (result === true) {
      say('Wallbox is vergrendeld');
    } else say( result );
    break;
  case "pause":
    token = await connectToWallbox();
    result = await pauseWallbox(token);
    // Optional. You can leave the next 3 lines out and place them in a flow
    if (result === true) {
      say('Wallbox is gepauzeerd');
    } else say( result );
    break;
  case "resume":
    token = await connectToWallbox();
    result = await resumeWallbox(token);
    // Optional. You can leave the next 3 lines out and place them in a flow
    if (result === true) {
      say('Wallbox is weer an het laden');
    } else say( result );
    break;
  case "setMaxCharge":
  
    if (typeof actionArgs[1] === 'undefined' ) {
      break;
    }
    token = await connectToWallbox();
    result = await setMaxCharging(token, actionArgs[1]);
    // Optional. You can leave the next 3 lines out and place them in a flow
    if (result === true) {
      say('Wallbox is ingesteld op maximaal laden van ' + actionArgs[1]);
    } else say( result );
    break;
}

Great working script!
Just followed your initial instruction and works like a charm!!

For the WallBox app user one can get the Serialnumber from the telephony app. You do not need to login into my.wallbox.com. And you do not need an UID, only the serial.

Just copied pasted your last version ot the script and it is still working.
You made my wife extremely happy; she can say now: "Hey Google … Zet laadpaal aan "

So good, thanks thanks

1 Like

Hello

I added the script in Homeyscript but when i click on ‘test i’, i have this error message

:x: Script Error
:warning: TypeError: Cannot read properties of undefined (reading ‘split’)
at myWallbox.js:142:26
at processTicksAndRejections (node:internal/process/task_queues:96:5)

Any idea ?

Login / password and uid are verified.

I have the same script error when testing the copied script. Does somebody know how to solve this?

So since I’ve gotten the Wallbox I’ve been working on a specific Homey App. Which already contains the triggers and lock/unlock mechanism. I was waiting to work on the pause/resume functionality before I officially publish it. I think I can release a version 0.1.0 of this app this week.

For anyone interested in the code, please see the GH.

2 Likes

Will check it out later when both the app and my Homey will be in. Since we have two electric cars, we ran in the problem that we need two accounts to seperate sessions (private/business). So, we first need to login with the right account before charging. Will it be possible to create flows for different accounts or is it just possible within Homey to add two devices each with their own account attached.

Yeah that is definitely possible. When you add a charger, you will then have to login with your respective account to look the charger up and add it to Homey. So basically following the same procedure you can add chargers from two different accounts.

1 Like

Good morning @rkokkelk ,

I installed your app, it’s awesome.

Did you have time to work on the /resume break?

Because I don’t want to work with WallBox planning to manage my loads.
Being French, I only recharge in “off-peak hours” from 11:30 p.m. to 7:30 a.m. and therefore this function interests me.

thanks in advance

I’ve just implemented it within the app and I’ve published it. It contains at least the following:

  • triggers when car connected/disconnected, charging started/resumed, etc
  • actions lock/unlock, pause, resume

For those interested this is the app:

This is just the initial version, so some bugs may be present, please fill in a github issue page if you found any.

Been away a bit and there’s now an app! Nice!
Tried it, but it won’t let me install a wall box when adding a device. I just see the icon with a red :exclamation: in a triangle

I’ve just verified my own installation and unfortunately I cannot reproduce the issue. Could you verify the same things as mentioned here:

I need some additional information to check what the specific issue is.

That is indeed what i also encounter.

Since the app has no configuration, there’s not much else i can test. Running homey Pro 2023. Also have the wall box homey script, that runs fine.

Okay, it should now be fixed, for further info and a working test version see the GH page.

1 Like

Excellent app. Works flawlessly. Thank you very much for your efforts to make this available!

1 Like

Yep! Thanks!

great job @rkokkelk

do you want me to do the French translation?

Yeah that would be great. I’ve just did the groundwork for it based on the NL translations. So if possible could you make a pull request on Github and then adding the appropriate “fr” translations in the app.json, driver and device compose json files and README.txt, the same way it’s document for the NL language.

If you’re unable to do a pull request feel free to share your translations with me personally and then I’ll add them to the app.

I have already done translations for other applications, I’ll look at it in the next few days

1 Like

Good morning @rkokkelk ,

the translations are pooled at github, I think everything is done.

Hi Moreau, yeah it helped out a lot, so thank you for your effort. If you would like to be mentioned as a translator please ping me with a PM.