Skip to content

Commit

Permalink
Updated docs
Browse files Browse the repository at this point in the history
  • Loading branch information
robertraaijmakers committed Feb 14, 2025
1 parent bd61ac3 commit 50656d3
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 58 deletions.
4 changes: 2 additions & 2 deletions .homeycompose/capabilities/authmode.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"id": "0",
"title": {
"en": "Plug and Charge",
"nl": "Inpluggen en laden"
"nl": "Inpluggen en Laden"
}
},
{
Expand All @@ -22,7 +22,7 @@
],
"getable": true,
"setable": true,
"uiComponent": "picker",
"uiComponent": "sensor",
"uiQuickAction": false,
"icon": "/assets/icon.svg"
}
4 changes: 2 additions & 2 deletions .homeycompose/capabilities/greenshare.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"type": "number",
"title": {
"en": "Green Share %",
"nl": "Groenestroom %"
"en": "Green Share",
"nl": "Aandeel Groen"
},
"units": "%",
"getable": true,
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Alfen
This app adds support for the Alfen charger (single connection) and will make sure the charger is shown as chargepoint in Homey Energy.
The app supports a couple of basic actions to regulate the way of charging. Please note that updating these settings through Homey might cause issues with your charge management system and is at own risk!

This app adds support for the Alfen charger (single connection) and will make sure the charger is shown as chargepoint in Homey Energy. The apps doesn't support any actions to update settings in the Alfen chargestation.
*Please note:* for this app to work your Homey and the Charger must be connected to the same SUBNET. So if you have different subnets within your household, make sure the charger and your Homey are in the same. Otherwise you cannot login, this is a limitation/security measure on Alfen side.

*Please note:* for this app to work your Homey and the Charger must be connected to the same SUBNET. So if you have different subnets within your household, make sure the charger and your Homey are in the same. Otherwise you cannot login, this is a limitation/security measure on Alfen side.
*Please note:* Alfen doesn't support multiple connections through the WebAPI. That means that you will encounter errors with refreshing and setting data once you have your Alfen app open on your smartphone simultanously when running this app on your Homey. Make sure to close your smartphone app when submitting changes and refreshing data.

More information can be found here: https://robertraaijmakers.github.io/com.alfen/
9 changes: 7 additions & 2 deletions README.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
This app adds support for the Alfen charger (single connection) and will make sure the charger is shown as chargepoint in Homey Energy. The apps doesn't support any actions to update settings in the Alfen chargestation.
This app adds support for the Alfen charger (single connection) and will make sure the charger is shown as chargepoint in Homey Energy.
The app supports a couple of basic actions to regulate the way of charging. Please note that updating these settings through Homey might cause issues with your charge management system and is at own risk!

Please note: for this app to work your Homey and the Charger must be connected to the same SUBNET. So if you have different subnets within your household, make sure the charger and your Homey are in the same. Otherwise you cannot login, this is a limitation/security measure on Alfen side.
Please note: for this app to work your Homey and the Charger must be connected to the same SUBNET. So if you have different subnets within your household, make sure the charger and your Homey are in the same. Otherwise you cannot login, this is a limitation/security measure on Alfen side.

Please note: Alfen doesn't support multiple connections through the WebAPI. That means that you will encounter errors with refreshing and setting data once you have your Alfen app open on your smartphone simultanously when running this app on your Homey. Make sure to close your smartphone app when submitting changes and refreshing data.

More information can be found through the links on the bottom of the Homey App Store Page.
8 changes: 4 additions & 4 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@
"id": "0",
"title": {
"en": "Plug and Charge",
"nl": "Inpluggen en laden"
"nl": "Inpluggen en Laden"
}
},
{
Expand All @@ -423,7 +423,7 @@
],
"getable": true,
"setable": true,
"uiComponent": "picker",
"uiComponent": "sensor",
"uiQuickAction": false,
"icon": "/assets/icon.svg"
},
Expand Down Expand Up @@ -482,8 +482,8 @@
"greenshare": {
"type": "number",
"title": {
"en": "Green Share %",
"nl": "Groenestroom %"
"en": "Green Share",
"nl": "Aandeel Groen"
},
"units": "%",
"getable": true,
Expand Down
15 changes: 11 additions & 4 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
# Alfen
This app will let you connect your Alfen charger over the local network with Homey. Currently it only supports reading of data and not updating settings.
This app will let you connect your Alfen charger over the local network with Homey. It will display the most important energy information and also gives you the ability to control some settings through a custom flow!

** NOTE ** The app will only work if your charger is of a supported type and if your charger lives in the same subnet as your Homey. This is a security limitation created by Alfen and there is no work-around.
Settings that you are able to set (USE AT OWN RISK):
- Authentication Mode (plug & charge and RFID);
- Charge Type (Normal, Comfort, Green)
- Comfort Charge Level (when charge type is Comfort, this is the minimum that will be charged regardless of how much redelivery takes place)
- Green Share (% of how much green energy should be used (I think applied when you set your thing to green… but not sure)
- Current Limit (1A - 32A): It can’t be set to 0. So the only way to fully stop charging is probably switching the charger to Charge Type: Green.

** NOTE ** Alfen only supports ONE connection at a time. This means that you can't use Homey and your Alfen (e.g. MyEve) app at the same time. If you encounter connection issues. Please logout of your Alfen Mobile App (if you want to use the Homey app) or temporarily turn off the Homey app (if you want to use your mobile app). This is a security limitation by Alfen and there is no work-around.
*NOTE* The app will only work if your charger is of a supported type and if your charger lives in the same subnet as your Homey. This is a security limitation created by Alfen and there is no work-around.

** NOTE ** The app can only connect to certain Alfen chargers. This is due to the fact that Alfen didn't publish their APIs so I can only add support for the type that I have at home. If you need support for other chargers of Alfen, please debug their API and add it to this app through a pull request.
*NOTE* Alfen only supports ONE connection at a time. This means that you can't use Homey and your Alfen (e.g. MyEve) app at the same time. If you encounter connection issues. Please logout of your Alfen Mobile App (if you want to use the Homey app) or temporarily turn off the Homey app (if you want to use your mobile app). This is a security limitation by Alfen and there is no work-around.

*NOTE* The app can only connect to certain Alfen chargers. This is due to the fact that Alfen didn't publish their APIs so I can only add support for the type that I have at home. If you need support for other chargers of Alfen, please debug their API and add it to this app through a pull request.
30 changes: 19 additions & 11 deletions drivers/chargepoint/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,21 @@ module.exports = class MyDevice extends Homey.Device {

async refreshDevice() {
this.log('Refresh Device');
let result: { capabilityId: string; value: string | number | boolean }[] | null = null;

try {
await this.alfenApi.apiLogin();
const result = await this.alfenApi.apiGetActualValues();
await this.alfenApi.apiLogout();

// Parse result values
await this.updateCapabilities(result);
result = await this.alfenApi.apiGetActualValues();
} catch (error) {
this.log('Error refreshing device:', error);
throw new Error(`${error}`);
} finally {
await this.alfenApi.apiLogout();
}

if (result == null) return;

// Parse result values
await this.updateCapabilities(result);
}

/**
Expand Down Expand Up @@ -196,10 +199,11 @@ module.exports = class MyDevice extends Homey.Device {
try {
await this.alfenApi.apiLogin();
await this.alfenApi.apiSetChargeType(value);
await this.alfenApi.apiLogout();
} catch (error) {
this.log('Error setting charge type:', error);
throw new Error(`${error}`);
} finally {
await this.alfenApi.apiLogout();
}
}

Expand All @@ -209,10 +213,11 @@ module.exports = class MyDevice extends Homey.Device {
try {
await this.alfenApi.apiLogin();
await this.alfenApi.apiSetComfortChargeLevel(value);
await this.alfenApi.apiLogout();
} catch (error) {
this.log('Error setting comfort charge level:', error);
throw new Error(`${error}`);
} finally {
await this.alfenApi.apiLogout();
}
}

Expand All @@ -222,10 +227,11 @@ module.exports = class MyDevice extends Homey.Device {
try {
await this.alfenApi.apiLogin();
await this.alfenApi.apiSetGreenSharePercentage(value);
await this.alfenApi.apiLogout();
} catch (error) {
this.log('Error setting green share percentage:', error);
throw new Error(`${error}`);
} finally {
await this.alfenApi.apiLogout();
}
}

Expand All @@ -235,10 +241,11 @@ module.exports = class MyDevice extends Homey.Device {
try {
await this.alfenApi.apiLogin();
await this.alfenApi.apiSetAuthMode(value);
await this.alfenApi.apiLogout();
} catch (error) {
this.log('Error setting auth mode:', error);
throw new Error(`${error}`);
} finally {
await this.alfenApi.apiLogout();
}
}

Expand All @@ -248,10 +255,11 @@ module.exports = class MyDevice extends Homey.Device {
try {
await this.alfenApi.apiLogin();
await this.alfenApi.apiSetCurrentLimit(value);
await this.alfenApi.apiLogout();
} catch (error) {
this.log('Error setting current limit:', error);
throw new Error(`${error}`);
} finally {
await this.alfenApi.apiLogout();
}

return true;
Expand Down
62 changes: 31 additions & 31 deletions lib/AlfenApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@ import { HttpsPromiseOptions, HttpsPromiseResponse, InfoResponse, PropertyRespon
import https from 'https';

const energyMeterCapabilitiesMap: { [key: string]: string } = {
'2062_0': 'measure_current.stationlimit',
'2062_0': 'measure_current.stationlimit', // Max. station limit
'2126_0': 'authmode',
'2129_0': 'measure_current.limit',
'2201_0': 'measure_temperature',
'2221_3': 'measure_voltage.l1',
'2221_4': 'measure_voltage.l2',
'2221_5': 'measure_voltage.l3',
'2221_10': 'measure_current.l1',
'2221_11': 'measure_current.l2',
'2221_12': 'measure_current.l3',
'2221_16': 'measure_power',
'2221_19': 'measure_power.l1',
'2221_20': 'measure_power.l2',
'2221_21': 'measure_power.l3',
'2221_22': 'meter_power',
'2129_0': 'measure_current.limit', // Custom Set Limit (A)
'2201_0': 'measure_temperature', // °C
'2221_3': 'measure_voltage.l1', // A
'2221_4': 'measure_voltage.l2', // A
'2221_5': 'measure_voltage.l3', // A
'2221_A': 'measure_current.l1', // V
'2221_B': 'measure_current.l2', // V
'2221_C': 'measure_current.l3', // V
'2221_13': 'measure_power.l1', // W
'2221_14': 'measure_power.l2', // W
'2221_15': 'measure_power.l3', // W
'2221_16': 'measure_power', // W (Power / Vermogen)
'2221_22': 'meter_power', // kWh (Energy / Energie)
'2501_2': 'operatingmode',
'3280_1': 'chargetype',
'3280_2': 'greenshare',
Expand Down Expand Up @@ -48,9 +48,15 @@ export class AlfenApi {
}

async apiLogin() {
this.#log(`Starting login process: ${this.#retrieving}`);

// Try handling multiple requests at the same time
if (this.#agent == null) this.#retrieving = 0;
this.#retrieving += 1;
if (this.#agent != null) return; // Already running another process and already loggedin

this.#log(`Creating new agent and start login: ${this.#retrieving}`);

this.#agent = new https.Agent({
keepAlive: true, // Enable connection keep-alive
maxSockets: 1, // Optionally limit the number of sockets (default is Infinity)
Expand Down Expand Up @@ -83,16 +89,19 @@ export class AlfenApi {
// Handle the response
this.#log('Login successful:', response.body);
} catch (error) {
this.#log('Login failed:', error);
throw new Error(`Login failed: ${error}`);
}
}

async apiLogout() {
this.#log(`Starting logout process: ${this.#retrieving}`);

this.#retrieving -= 1;
if (this.#retrieving > 0) return;
if (this.#retrieving < 0) this.#retrieving = 0;

this.#log(`Logout procedure, logout & clean-up agent.`);

// Define the options for the HTTPS request
const options = {
hostname: this.#ip,
Expand All @@ -115,7 +124,7 @@ export class AlfenApi {
} catch (error) {
this.#agent = null;
this.#log('Logout failed:', error);
throw new Error(`Logout failed: ${error}`);
//throw new Error(`Logout failed: ${error}`);
}
}

Expand All @@ -141,14 +150,13 @@ export class AlfenApi {
this.#log('Info retrieved successfully:', response.body);
return <InfoResponse>response.body;
} catch (error) {
this.#log('Request failed:', error);
throw new Error(`Request failed: ${error}`);
}
}

async apiGetActualValues() {
// Define the 'ids' parameter
const ids = '2056_0,2060_0,2062_0,2126_0,2129_0,2201_0,2221_3,2221_4,2221_5,2221_10,2221_11,2221_12,2221_13,2221_14,2221_15,221_16,2221_22,2501_2,3280_1,3280_2,3280_3';
const ids = '2056_0,2060_0,2062_0,2126_0,2129_0,2201_0,2221_3,2221_4,2221_5,2221_0A,2221_0B,2221_0C,2221_13,2221_14,2221_15,2221_16,2221_22,2501_2,3280_1,3280_2,3280_3';

// Define the options for the HTTPS request (no body, just headers)
const options: HttpsPromiseOptions = {
Expand All @@ -168,8 +176,6 @@ export class AlfenApi {
const response = await this.#httpsPromise(options);

// Handle the response
this.#log('Properties retrieved successfully:', response.body);

const bodyResult = <PropertyResponseBody>response.body;
const result = bodyResult.properties;
const capabilitiesData: Array<{
Expand All @@ -180,6 +186,8 @@ export class AlfenApi {
for (const prop of result) {
const capabilityId = energyMeterCapabilitiesMap[prop.id];

this.#log(`Property: ${prop.id}: ${prop.value}, Type: ${prop.type}, Category: ${prop.cat}, Access: ${prop.access}, Capability: ${capabilityId ?? 'Unknown'}`);

if (capabilityId) {
let value: string | number | boolean | null = null;

Expand All @@ -194,9 +202,9 @@ export class AlfenApi {
value = Math.round(prop.value); // rounding values, no decimal
break;
case '2201_0': // Temperature
case '2221_10': // Current L1
case '2221_11': // Current L2
case '2221_12': // Current L3
case '2221_A': // Current L1
case '2221_B': // Current L2
case '2221_C': // Current L3
case '2221_13': // Power L1 (watts)
case '2221_14': // Power L2 (watts)
case '2221_15': // Power L3 (watts)
Expand Down Expand Up @@ -241,7 +249,6 @@ export class AlfenApi {
try {
await this.#apiSetProperty(body);
} catch (e) {
this.#log('Error setting current limit:', e);
throw new Error(`Error setting current limit: ${e}`);
}

Expand All @@ -260,7 +267,6 @@ export class AlfenApi {
try {
await this.#apiSetProperty(body);
} catch (e) {
this.#log('Error setting charge type:', e);
throw new Error(`Error setting charge type: ${e}`);
}

Expand All @@ -281,7 +287,6 @@ export class AlfenApi {
try {
await this.#apiSetProperty(body);
} catch (e) {
this.#log('Error setting green share percentage:', e);
throw new Error(`Error setting green share percentage: ${e}`);
}

Expand All @@ -302,7 +307,6 @@ export class AlfenApi {
try {
await this.#apiSetProperty(body);
} catch (e) {
this.#log('Error setting comfort charge level:', e);
throw new Error(`Error setting comfort charge level: ${e}`);
}

Expand All @@ -321,7 +325,6 @@ export class AlfenApi {
try {
await this.#apiSetProperty(body);
} catch (e) {
this.#log('Error setting auth mode:', e);
throw new Error(`Error setting auth mode: ${e}`);
}

Expand Down Expand Up @@ -356,7 +359,6 @@ export class AlfenApi {
this.#log('Start reboot:', response.body);
return true;
} catch (error) {
this.#log('Reboot failed:', error);
throw new Error(`Reboot failed: ${error}`);
}
}
Expand All @@ -383,7 +385,6 @@ export class AlfenApi {
// Handle the response
this.#log('Written property:', response.body);
} catch (error) {
this.#log('Property write failed:', error);
throw new Error(`Property write failed: ${error}`);
}
}
Expand All @@ -402,7 +403,6 @@ export class AlfenApi {
}

let resBody = Buffer.concat(chunks).toString();
this.#log(resBody);

switch (res.headers['content-type']) {
case 'application/json':
Expand Down

0 comments on commit 50656d3

Please sign in to comment.