From a5308b72e797ffe2a8eacad42f7a3945aac844c9 Mon Sep 17 00:00:00 2001 From: Tycho Bom Date: Sat, 11 Apr 2026 16:49:12 +0200 Subject: [PATCH 1/9] Fixed circular reference in build command. The app now builds correctly. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1756791..28ac040 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "test": "mocha", "prettier": "prettier --write **/*.ts", "validate": "homey app validate --level verified", - "build": "homey app build", + "build": "tsc", "start": "homey app run", "publish": "homey app publish" }, From ec80953cce0c04f35993bf4141f98e3112f6e49d Mon Sep 17 00:00:00 2001 From: Tycho Bom Date: Sat, 11 Apr 2026 17:45:43 +0200 Subject: [PATCH 2/9] Added calculated meter to get live energy updates instead of once per hour based on the signed meter value. --- .../capabilities/calculated_meter_value.json | 21 ++++ app.json | 25 ++++- drivers/go2/device.ts | 105 ++++++++++++++---- drivers/go2/driver.compose.json | 4 +- 4 files changed, 130 insertions(+), 25 deletions(-) create mode 100644 .homeycompose/capabilities/calculated_meter_value.json diff --git a/.homeycompose/capabilities/calculated_meter_value.json b/.homeycompose/capabilities/calculated_meter_value.json new file mode 100644 index 0000000..93882b4 --- /dev/null +++ b/.homeycompose/capabilities/calculated_meter_value.json @@ -0,0 +1,21 @@ +{ + "title": { + "en": "Calculated meter value", + "no": "Beregnet målerverdi" + }, + "type": "number", + "desc": { + "en": "Live calculated total energy consumption combining signed meter value and current session", + "no": "Beregnet total energiforbruk basert på målerverdi og aktiv ladeøkt" + }, + "units": { + "en": "kWh" + }, + "decimals": 3, + "setable": false, + "getable": true, + "insights": true, + "uiComponent": "sensor", + "chartType": "stepLine", + "icon": "../assets/icon.svg" +} diff --git a/app.json b/app.json index 36ac3c5..512eff2 100644 --- a/app.json +++ b/app.json @@ -2369,6 +2369,7 @@ }, "capabilities": [ "meter_power.current_session", + "calculated_meter_value", "meter_power", "meter_power.last_session", "measure_power", @@ -2469,8 +2470,7 @@ "title": { "en": "Signed meter value", "no": "Målerstand" - }, - "uiComponent": "none" + } }, "measure_signal_strength": { "title": { @@ -3201,6 +3201,27 @@ "no": "Kabel ikke låst til lader" } }, + "calculated_meter_value": { + "title": { + "en": "Calculated meter value", + "no": "Beregnet målerverdi" + }, + "type": "number", + "desc": { + "en": "Live calculated total energy consumption combining signed meter value and current session", + "no": "Beregnet total energiforbruk basert på målerverdi og aktiv ladeøkt" + }, + "units": { + "en": "kWh" + }, + "decimals": 3, + "setable": false, + "getable": true, + "insights": true, + "uiComponent": "sensor", + "chartType": "stepLine", + "icon": "../assets/icon.svg" + }, "charge_mode": { "type": "enum", "title": { diff --git a/drivers/go2/device.ts b/drivers/go2/device.ts index fbdb552..6c23c81 100644 --- a/drivers/go2/device.ts +++ b/drivers/go2/device.ts @@ -17,6 +17,9 @@ export class Go2Charger extends Homey.Device { private cronTasks: cron.ScheduledTask[] = []; private api?: ZaptecApi; private tokenRenewalTimeout: NodeJS.Timeout | undefined; + private calculatedMeterValue: number = 0; + private lastObservedSessionEnergy: number = 0; + private lastSignedMeterValue: number = 0; /** * onInit is called when the device is initialized. @@ -34,7 +37,7 @@ export class Go2Charger extends Homey.Device { await this.migrateEnergy(); await this.migrateCapabilities(); - + this.registerCapabilityListeners(); this.cronTasks.push( @@ -52,6 +55,9 @@ export class Go2Charger extends Homey.Device { // Do initial slow poll at start, we don't know how long ago we read it out. this.pollSlowValues(); + // Initialize calculated meter value tracking + this.initializeCalculatedMeterValue(); + this.log('Go2Charger has been initialized'); } @@ -63,29 +69,56 @@ export class Go2Charger extends Homey.Device { private async migrateCapabilities() { //get lastInstalledVersion from settings const lastInstalledVersion = this.getSetting('lastInstalledVersion') || '0.0.0'; - + // Log version information for debugging this.logToDebug(`Migration: Current version is ${this.homey.app.manifest.version}, previously installed version was ${lastInstalledVersion}`); - + if (lastInstalledVersion <= '1.8.1' && !this.hasCapability('charging_mode')) { await this.addCapability('charging_mode'); } + + // Add calculated meter value capability for live energy reporting + if (!this.hasCapability('calculated_meter_value')) { + await this.addCapability('calculated_meter_value'); + } } /** * Migrate energy settings from the old settings format to the new one. */ private async migrateEnergy() { const energyConfig = this.getEnergy(); - if (energyConfig?.cumulative !== true || energyConfig?.evCharger !== true || energyConfig?.meterPowerImportedCapability !== "meter_power.signed_meter_value") { + if (energyConfig?.cumulative !== true || energyConfig?.evCharger !== true || energyConfig?.meterPowerImportedCapability !== "calculated_meter_value") { this.setEnergy({ evCharger: true, - meterPowerImportedCapability: "meter_power.signed_meter_value" + meterPowerImportedCapability: "calculated_meter_value" }).catch((e) => { this.logToDebug(`Failed to migrate energy: ${e}`); }); } } + /** + * Initialize the calculated meter value tracking. + * Sets up the baseline from signed meter value if available. + */ + private initializeCalculatedMeterValue() { + // Initialize calculated meter value from existing capability or 0 + const existingSignedValue = this.getCapabilityValue('meter_power.signed_meter_value'); + const existingCalculatedValue = this.getCapabilityValue('calculated_meter_value'); + + if (existingCalculatedValue !== null && existingCalculatedValue !== undefined) { + this.calculatedMeterValue = existingCalculatedValue; + } else if (existingSignedValue !== null && existingSignedValue !== undefined) { + this.calculatedMeterValue = existingSignedValue; + this.setCapabilityValue('calculated_meter_value', this.calculatedMeterValue).catch(e => + this.logToDebug(`Failed to initialize calculated_meter_value: ${e}`) + ); + } + + this.lastSignedMeterValue = existingSignedValue || 0; + this.lastObservedSessionEnergy = this.getCapabilityValue('meter_power.current_session') || 0; + } + /** * Assign reactions to capability changes triggered by others. @@ -154,19 +187,19 @@ export class Go2Charger extends Homey.Device { await this.removeCapability('measure_current.phase3'); } } - + // Handle changes to authentication requirement if (changes.changedKeys.some((k) => k === 'requireAuthentication')) { try { if (!this.hasCapability('available_installation_current')) { throw new Error(this.homey.__('errors.missing_installation_access')); } - + const requireAuthValue = changes.newSettings.requireAuthentication; - const requireAuth = typeof requireAuthValue === 'string' - ? requireAuthValue === 'true' + const requireAuth = typeof requireAuthValue === 'string' + ? requireAuthValue === 'true' : Boolean(requireAuthValue); - + await this.setInstallationAuthenticationRequirement(requireAuth); this.logToDebug(`Updated authentication requirement to ${requireAuth} via settings`); } catch (e) { @@ -237,7 +270,7 @@ export class Go2Charger extends Homey.Device { protected pollValues() { try { if (this.api === undefined) return; - + // Poll charger info this.api .getCharger(this.getData().id) @@ -247,7 +280,7 @@ export class Go2Charger extends Homey.Device { }); }) .catch((e) => { - this.logToDebug(`Failed to poll charger info: ${e}`); + this.logToDebug(`Failed to poll charger info: ${e}`); this.setUnavailable( this.homey.__('errors.authentication_failed') ); @@ -308,7 +341,7 @@ export class Go2Charger extends Homey.Device { }) .catch((e) => { this.logToDebug(`Failed to poll installation: ${e}`); - if (this.hasCapability('available_installation_current')) + if (this.hasCapability('available_installation_current')) this.removeCapability('available_installation_current'); if (this.hasCapability('charging_mode')) this.removeCapability('charging_mode'); @@ -418,10 +451,21 @@ export class Go2Charger extends Homey.Device { break; case ApolloDeviceObservation.TotalChargePowerSession: + const currentSessionEnergy = Number(state.ValueAsString); await this.setCapabilityValue( 'meter_power.current_session', - Number(state.ValueAsString), + currentSessionEnergy, ); + + // Update calculated meter value with the energy delta from this session + // Only apply positive deltas (energy should only increase during a session) + const sessionDelta = currentSessionEnergy - this.lastObservedSessionEnergy; + if (sessionDelta > 0) { + this.calculatedMeterValue += sessionDelta; + await this.setCapabilityValue('calculated_meter_value', this.calculatedMeterValue); + this.logToDebug(`Updated calculated meter: +${sessionDelta.toFixed(3)} kWh (session: ${currentSessionEnergy.toFixed(3)} kWh, total: ${this.calculatedMeterValue.toFixed(3)} kWh)`); + } + this.lastObservedSessionEnergy = currentSessionEnergy; break; case ApolloDeviceObservation.Humidity: @@ -487,9 +531,9 @@ export class Go2Charger extends Homey.Device { if (this.api === undefined || !this.hasCapability('available_installation_current')) return; const info = await this.api .getInstallation(this.getData().installationId) - .catch((e) => { + .catch((e) => { if (e instanceof ApiError && e.message.indexOf('Unknown object') >= 0 && this.hasCapability('available_installation_current')) - this.removeCapability('available_installation_current'); + this.removeCapability('available_installation_current'); this.logToDebug( `Failed to get installation info when updating available current: ${e}`, ); @@ -610,6 +654,11 @@ export class Go2Charger extends Homey.Device { await this.homey.flow .getDeviceTriggerCard('go2_car_disconnects') .trigger(this, tokens); + + // Reset session energy tracking when car disconnects + // The calculated meter value will be corrected by the next signed meter update + this.lastObservedSessionEnergy = 0; + this.logToDebug(`Car disconnected - reset session energy tracking`); } } @@ -659,7 +708,7 @@ export class Go2Charger extends Homey.Device { ST?: string; }[]; } = JSON.parse(jsonStr); - + const rv = ocmf.RD?.[0]?.RV; if (rv !== undefined) { const num = Number(rv); @@ -668,6 +717,20 @@ export class Go2Charger extends Homey.Device { signedMeterValue: formatted, }).then(() => { this.setCapabilityValue('meter_power.signed_meter_value', num); + + // Correct calculated meter value when receiving new signed value with no car connected + // This corrects any accumulated rounding errors or drift + const isCarConnected = this.getCapabilityValue('alarm_generic.car_connected'); + const isNewSignedValue = num !== this.lastSignedMeterValue; + + if (!isCarConnected && isNewSignedValue) { + this.logToDebug(`Correcting calculated meter value from ${this.calculatedMeterValue.toFixed(3)} to signed value ${num.toFixed(3)} (no car connected, new signed value received)`); + this.calculatedMeterValue = num; + this.setCapabilityValue('calculated_meter_value', num).catch(e => + this.logToDebug(`Failed to update calculated_meter_value: ${e}`) + ); + } + this.lastSignedMeterValue = num; }) .catch((e) => { this.logToDebug(`Failed to get OCMF-signed value: ${e}`); @@ -752,7 +815,7 @@ export class Go2Charger extends Homey.Device { /** * Sets whether the installation requires authentication for charging - * + * * @param {boolean} requireAuthentication - true if authentication is required, false otherwise * @returns {Promise} - true if the operation succeeded */ @@ -772,7 +835,7 @@ export class Go2Charger extends Homey.Device { /** * Sends a command to reboot the charger - * + * * @returns {Promise} - true if the operation succeeded */ public async rebootCharger() { @@ -785,10 +848,10 @@ export class Go2Charger extends Homey.Device { throw new Error(`Failed to reboot charger: ${e}`); }); } - + /** * Sets the charging mode for the installation - * + * * @param {Feature} chargingMode - the charging mode to set * @returns {Promise} - true if the operation succeeded */ diff --git a/drivers/go2/driver.compose.json b/drivers/go2/driver.compose.json index c7db170..f087e2c 100644 --- a/drivers/go2/driver.compose.json +++ b/drivers/go2/driver.compose.json @@ -8,6 +8,7 @@ "class": "evcharger", "capabilities": [ "meter_power.current_session", + "calculated_meter_value", "meter_power", "meter_power.last_session", "measure_power", @@ -108,8 +109,7 @@ "title": { "en": "Signed meter value", "no": "Målerstand" - }, - "uiComponent": "none" + } }, "measure_signal_strength": { "title": { From cfbe8e46c0fdaf6bf78c99390e4305c6c4c11749 Mon Sep 17 00:00:00 2001 From: Tycho Bom Date: Sat, 11 Apr 2026 19:26:03 +0200 Subject: [PATCH 3/9] Moved total energy this year to a separate capability and used the calculated meter value as the generic meter_power to add support for the Power by the Hour app. --- .../capabilities/calculated_meter_value.json | 21 ------- .../meter_power_total_this_year.json | 16 +++++ app.json | 45 +++++++------- drivers/go2/device.ts | 59 ++++++++----------- drivers/go2/driver.compose.json | 10 +++- 5 files changed, 72 insertions(+), 79 deletions(-) delete mode 100644 .homeycompose/capabilities/calculated_meter_value.json create mode 100644 .homeycompose/capabilities/meter_power_total_this_year.json diff --git a/.homeycompose/capabilities/calculated_meter_value.json b/.homeycompose/capabilities/calculated_meter_value.json deleted file mode 100644 index 93882b4..0000000 --- a/.homeycompose/capabilities/calculated_meter_value.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "title": { - "en": "Calculated meter value", - "no": "Beregnet målerverdi" - }, - "type": "number", - "desc": { - "en": "Live calculated total energy consumption combining signed meter value and current session", - "no": "Beregnet total energiforbruk basert på målerverdi og aktiv ladeøkt" - }, - "units": { - "en": "kWh" - }, - "decimals": 3, - "setable": false, - "getable": true, - "insights": true, - "uiComponent": "sensor", - "chartType": "stepLine", - "icon": "../assets/icon.svg" -} diff --git a/.homeycompose/capabilities/meter_power_total_this_year.json b/.homeycompose/capabilities/meter_power_total_this_year.json new file mode 100644 index 0000000..d991465 --- /dev/null +++ b/.homeycompose/capabilities/meter_power_total_this_year.json @@ -0,0 +1,16 @@ +{ + "title": { + "en": "Energy this year", + "no": "Energiforbruk i år" + }, + "type": "number", + "units": { + "en": "kWh" + }, + "decimals": 2, + "setable": false, + "getable": true, + "insights": true, + "uiComponent": "sensor", + "chartType": "stepLine" +} diff --git a/app.json b/app.json index 512eff2..3fec9f8 100644 --- a/app.json +++ b/app.json @@ -2369,8 +2369,8 @@ }, "capabilities": [ "meter_power.current_session", - "calculated_meter_value", "meter_power", + "meter_power_total_this_year", "meter_power.last_session", "measure_power", "charging_button", @@ -2399,6 +2399,12 @@ } }, "meter_power": { + "title": { + "en": "Total energy", + "no": "Total energi" + } + }, + "meter_power_total_this_year": { "title": { "en": "Energy this year", "no": "Energiforbruk i år" @@ -3201,27 +3207,6 @@ "no": "Kabel ikke låst til lader" } }, - "calculated_meter_value": { - "title": { - "en": "Calculated meter value", - "no": "Beregnet målerverdi" - }, - "type": "number", - "desc": { - "en": "Live calculated total energy consumption combining signed meter value and current session", - "no": "Beregnet total energiforbruk basert på målerverdi og aktiv ladeøkt" - }, - "units": { - "en": "kWh" - }, - "decimals": 3, - "setable": false, - "getable": true, - "insights": true, - "uiComponent": "sensor", - "chartType": "stepLine", - "icon": "../assets/icon.svg" - }, "charge_mode": { "type": "enum", "title": { @@ -3332,6 +3317,22 @@ "insights": false, "uiComponent": "sensor", "icon": "/assets/current_limit.svg" + }, + "meter_power_total_this_year": { + "title": { + "en": "Energy this year", + "no": "Energiforbruk i år" + }, + "type": "number", + "units": { + "en": "kWh" + }, + "decimals": 2, + "setable": false, + "getable": true, + "insights": true, + "uiComponent": "sensor", + "chartType": "stepLine" } } } \ No newline at end of file diff --git a/drivers/go2/device.ts b/drivers/go2/device.ts index 6c23c81..dea58a9 100644 --- a/drivers/go2/device.ts +++ b/drivers/go2/device.ts @@ -17,7 +17,7 @@ export class Go2Charger extends Homey.Device { private cronTasks: cron.ScheduledTask[] = []; private api?: ZaptecApi; private tokenRenewalTimeout: NodeJS.Timeout | undefined; - private calculatedMeterValue: number = 0; + private totalMeterValue: number = 0; private lastObservedSessionEnergy: number = 0; private lastSignedMeterValue: number = 0; @@ -56,7 +56,7 @@ export class Go2Charger extends Homey.Device { this.pollSlowValues(); // Initialize calculated meter value tracking - this.initializeCalculatedMeterValue(); + this.initializeTotalMeterValue(); this.log('Go2Charger has been initialized'); } @@ -77,9 +77,9 @@ export class Go2Charger extends Homey.Device { await this.addCapability('charging_mode'); } - // Add calculated meter value capability for live energy reporting - if (!this.hasCapability('calculated_meter_value')) { - await this.addCapability('calculated_meter_value'); + // Add total energy this year capability + if (!this.hasCapability('meter_power_total_this_year')) { + await this.addCapability('meter_power_total_this_year'); } } /** @@ -87,10 +87,10 @@ export class Go2Charger extends Homey.Device { */ private async migrateEnergy() { const energyConfig = this.getEnergy(); - if (energyConfig?.cumulative !== true || energyConfig?.evCharger !== true || energyConfig?.meterPowerImportedCapability !== "calculated_meter_value") { + if (energyConfig?.cumulative !== true || energyConfig?.evCharger !== true || energyConfig?.meterPowerImportedCapability !== "meter_power") { this.setEnergy({ evCharger: true, - meterPowerImportedCapability: "calculated_meter_value" + meterPowerImportedCapability: "meter_power" }).catch((e) => { this.logToDebug(`Failed to migrate energy: ${e}`); }); @@ -98,20 +98,20 @@ export class Go2Charger extends Homey.Device { } /** - * Initialize the calculated meter value tracking. + * Initialize the total meter value tracking. * Sets up the baseline from signed meter value if available. */ - private initializeCalculatedMeterValue() { - // Initialize calculated meter value from existing capability or 0 + private initializeTotalMeterValue() { + // Initialize total meter value from existing capability or 0 const existingSignedValue = this.getCapabilityValue('meter_power.signed_meter_value'); - const existingCalculatedValue = this.getCapabilityValue('calculated_meter_value'); + const existingTotalValue = this.getCapabilityValue('meter_power'); - if (existingCalculatedValue !== null && existingCalculatedValue !== undefined) { - this.calculatedMeterValue = existingCalculatedValue; + if (existingTotalValue !== null && existingTotalValue !== undefined) { + this.totalMeterValue = existingTotalValue; } else if (existingSignedValue !== null && existingSignedValue !== undefined) { - this.calculatedMeterValue = existingSignedValue; - this.setCapabilityValue('calculated_meter_value', this.calculatedMeterValue).catch(e => - this.logToDebug(`Failed to initialize calculated_meter_value: ${e}`) + this.totalMeterValue = existingSignedValue; + this.setCapabilityValue('meter_power', this.totalMeterValue).catch(e => + this.logToDebug(`Failed to initialize meter_power: ${e}`) ); } @@ -357,7 +357,7 @@ export class Go2Charger extends Homey.Device { .then((charges) => { const yearlyEnergy = charges?.reduce((sum, charge) => sum + charge.Energy, 0) || 0; - return this.setCapabilityValue('meter_power', yearlyEnergy); + return this.setCapabilityValue('meter_power_total_this_year', yearlyEnergy); }) .then(() => { this.logToDebug(`Got yearly power history`); @@ -457,24 +457,17 @@ export class Go2Charger extends Homey.Device { currentSessionEnergy, ); - // Update calculated meter value with the energy delta from this session + // Update total meter value with the energy delta from this session // Only apply positive deltas (energy should only increase during a session) const sessionDelta = currentSessionEnergy - this.lastObservedSessionEnergy; if (sessionDelta > 0) { - this.calculatedMeterValue += sessionDelta; - await this.setCapabilityValue('calculated_meter_value', this.calculatedMeterValue); - this.logToDebug(`Updated calculated meter: +${sessionDelta.toFixed(3)} kWh (session: ${currentSessionEnergy.toFixed(3)} kWh, total: ${this.calculatedMeterValue.toFixed(3)} kWh)`); + this.totalMeterValue += sessionDelta; + await this.setCapabilityValue('meter_power', this.totalMeterValue); + this.logToDebug(`Updated total meter: +${sessionDelta.toFixed(3)} kWh (session: ${currentSessionEnergy.toFixed(3)} kWh, total: ${this.totalMeterValue.toFixed(3)} kWh)`); } this.lastObservedSessionEnergy = currentSessionEnergy; break; - case ApolloDeviceObservation.Humidity: - await this.setCapabilityValue( - 'measure_humidity', - Number(state.ValueAsString), - ); - break; - case ApolloDeviceObservation.TemperatureInternal5: await this.setCapabilityValue( 'measure_temperature', @@ -718,16 +711,16 @@ export class Go2Charger extends Homey.Device { }).then(() => { this.setCapabilityValue('meter_power.signed_meter_value', num); - // Correct calculated meter value when receiving new signed value with no car connected + // Correct total meter value when receiving new signed value with no car connected // This corrects any accumulated rounding errors or drift const isCarConnected = this.getCapabilityValue('alarm_generic.car_connected'); const isNewSignedValue = num !== this.lastSignedMeterValue; if (!isCarConnected && isNewSignedValue) { - this.logToDebug(`Correcting calculated meter value from ${this.calculatedMeterValue.toFixed(3)} to signed value ${num.toFixed(3)} (no car connected, new signed value received)`); - this.calculatedMeterValue = num; - this.setCapabilityValue('calculated_meter_value', num).catch(e => - this.logToDebug(`Failed to update calculated_meter_value: ${e}`) + this.logToDebug(`Correcting total meter value from ${this.totalMeterValue.toFixed(3)} to signed value ${num.toFixed(3)} (no car connected, new signed value received)`); + this.totalMeterValue = num; + this.setCapabilityValue('meter_power', num).catch(e => + this.logToDebug(`Failed to update meter_power: ${e}`) ); } this.lastSignedMeterValue = num; diff --git a/drivers/go2/driver.compose.json b/drivers/go2/driver.compose.json index f087e2c..f2155b1 100644 --- a/drivers/go2/driver.compose.json +++ b/drivers/go2/driver.compose.json @@ -8,8 +8,8 @@ "class": "evcharger", "capabilities": [ "meter_power.current_session", - "calculated_meter_value", "meter_power", + "meter_power_total_this_year", "meter_power.last_session", "measure_power", "charging_button", @@ -38,8 +38,12 @@ } }, "meter_power": { - "title": { - "en": "Energy this year", + "title": { "en": "Total energy", + "no": "Total energi" + } + }, + "meter_power_total_this_year": { + "title": { "en": "Energy this year", "no": "Energiforbruk i år" } }, From ee2925524f0a2c54eedf904a78d484911afac47e Mon Sep 17 00:00:00 2001 From: Tycho Bom Date: Sat, 11 Apr 2026 19:33:42 +0200 Subject: [PATCH 4/9] Removed specific translations for meter_power so that it uses the out of the box labels in Homey. --- app.json | 6 ------ drivers/go2/driver.compose.json | 5 ----- 2 files changed, 11 deletions(-) diff --git a/app.json b/app.json index 3fec9f8..ee3fc3c 100644 --- a/app.json +++ b/app.json @@ -2398,12 +2398,6 @@ "no": "Energibruk ladeøkt" } }, - "meter_power": { - "title": { - "en": "Total energy", - "no": "Total energi" - } - }, "meter_power_total_this_year": { "title": { "en": "Energy this year", diff --git a/drivers/go2/driver.compose.json b/drivers/go2/driver.compose.json index f2155b1..5a57a07 100644 --- a/drivers/go2/driver.compose.json +++ b/drivers/go2/driver.compose.json @@ -37,11 +37,6 @@ "no": "Energibruk ladeøkt" } }, - "meter_power": { - "title": { "en": "Total energy", - "no": "Total energi" - } - }, "meter_power_total_this_year": { "title": { "en": "Energy this year", "no": "Energiforbruk i år" From dbe87b8ad8496fb7e91d235adfd5c7361d989422 Mon Sep 17 00:00:00 2001 From: Tycho Bom Date: Mon, 13 Apr 2026 21:18:32 +0200 Subject: [PATCH 5/9] fix for correction of meter_power not happening in case the signed meter value is the same as before disconnecting the car. --- drivers/go2/device.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/go2/device.ts b/drivers/go2/device.ts index dea58a9..2f4567d 100644 --- a/drivers/go2/device.ts +++ b/drivers/go2/device.ts @@ -711,13 +711,12 @@ export class Go2Charger extends Homey.Device { }).then(() => { this.setCapabilityValue('meter_power.signed_meter_value', num); - // Correct total meter value when receiving new signed value with no car connected + // Correct total meter value when receiving signed value with no car connected // This corrects any accumulated rounding errors or drift const isCarConnected = this.getCapabilityValue('alarm_generic.car_connected'); - const isNewSignedValue = num !== this.lastSignedMeterValue; - if (!isCarConnected && isNewSignedValue) { - this.logToDebug(`Correcting total meter value from ${this.totalMeterValue.toFixed(3)} to signed value ${num.toFixed(3)} (no car connected, new signed value received)`); + if (!isCarConnected && this.totalMeterValue !== num) { + this.logToDebug(`Correcting total meter value from ${this.totalMeterValue.toFixed(3)} to signed value ${num.toFixed(3)} (no car connected, signed value received)`); this.totalMeterValue = num; this.setCapabilityValue('meter_power', num).catch(e => this.logToDebug(`Failed to update meter_power: ${e}`) From 2143d0065de6fbdfe38522ea7788d376e0de661c Mon Sep 17 00:00:00 2001 From: Tycho Bom Date: Mon, 13 Apr 2026 21:35:01 +0200 Subject: [PATCH 6/9] Clean-up of variables --- drivers/go2/device.ts | 50 ++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/drivers/go2/device.ts b/drivers/go2/device.ts index 2f4567d..a6b1feb 100644 --- a/drivers/go2/device.ts +++ b/drivers/go2/device.ts @@ -17,9 +17,6 @@ export class Go2Charger extends Homey.Device { private cronTasks: cron.ScheduledTask[] = []; private api?: ZaptecApi; private tokenRenewalTimeout: NodeJS.Timeout | undefined; - private totalMeterValue: number = 0; - private lastObservedSessionEnergy: number = 0; - private lastSignedMeterValue: number = 0; /** * onInit is called when the device is initialized. @@ -98,25 +95,20 @@ export class Go2Charger extends Homey.Device { } /** - * Initialize the total meter value tracking. + * Initialize meter value tracking. * Sets up the baseline from signed meter value if available. */ private initializeTotalMeterValue() { - // Initialize total meter value from existing capability or 0 + // Initialize meter_power from signed meter value if not already set const existingSignedValue = this.getCapabilityValue('meter_power.signed_meter_value'); const existingTotalValue = this.getCapabilityValue('meter_power'); - if (existingTotalValue !== null && existingTotalValue !== undefined) { - this.totalMeterValue = existingTotalValue; - } else if (existingSignedValue !== null && existingSignedValue !== undefined) { - this.totalMeterValue = existingSignedValue; - this.setCapabilityValue('meter_power', this.totalMeterValue).catch(e => + if ((existingTotalValue === null || existingTotalValue === undefined) && + existingSignedValue !== null && existingSignedValue !== undefined) { + this.setCapabilityValue('meter_power', existingSignedValue).catch(e => this.logToDebug(`Failed to initialize meter_power: ${e}`) ); } - - this.lastSignedMeterValue = existingSignedValue || 0; - this.lastObservedSessionEnergy = this.getCapabilityValue('meter_power.current_session') || 0; } @@ -452,20 +444,22 @@ export class Go2Charger extends Homey.Device { case ApolloDeviceObservation.TotalChargePowerSession: const currentSessionEnergy = Number(state.ValueAsString); - await this.setCapabilityValue( - 'meter_power.current_session', - currentSessionEnergy, - ); // Update total meter value with the energy delta from this session // Only apply positive deltas (energy should only increase during a session) - const sessionDelta = currentSessionEnergy - this.lastObservedSessionEnergy; + const previousSessionEnergy = this.getCapabilityValue('meter_power.current_session') || 0; + const sessionDelta = currentSessionEnergy - previousSessionEnergy; if (sessionDelta > 0) { - this.totalMeterValue += sessionDelta; - await this.setCapabilityValue('meter_power', this.totalMeterValue); - this.logToDebug(`Updated total meter: +${sessionDelta.toFixed(3)} kWh (session: ${currentSessionEnergy.toFixed(3)} kWh, total: ${this.totalMeterValue.toFixed(3)} kWh)`); + const currentMeterPower = this.getCapabilityValue('meter_power') || 0; + const newMeterPower = currentMeterPower + sessionDelta; + await this.setCapabilityValue('meter_power', newMeterPower); + this.logToDebug(`Updated total meter: +${sessionDelta.toFixed(3)} kWh (session: ${currentSessionEnergy.toFixed(3)} kWh, total: ${newMeterPower.toFixed(3)} kWh)`); } - this.lastObservedSessionEnergy = currentSessionEnergy; + + await this.setCapabilityValue( + 'meter_power.current_session', + currentSessionEnergy, + ); break; case ApolloDeviceObservation.TemperatureInternal5: @@ -647,11 +641,6 @@ export class Go2Charger extends Homey.Device { await this.homey.flow .getDeviceTriggerCard('go2_car_disconnects') .trigger(this, tokens); - - // Reset session energy tracking when car disconnects - // The calculated meter value will be corrected by the next signed meter update - this.lastObservedSessionEnergy = 0; - this.logToDebug(`Car disconnected - reset session energy tracking`); } } @@ -714,15 +703,14 @@ export class Go2Charger extends Homey.Device { // Correct total meter value when receiving signed value with no car connected // This corrects any accumulated rounding errors or drift const isCarConnected = this.getCapabilityValue('alarm_generic.car_connected'); + const currentMeterPower = this.getCapabilityValue('meter_power') || 0; - if (!isCarConnected && this.totalMeterValue !== num) { - this.logToDebug(`Correcting total meter value from ${this.totalMeterValue.toFixed(3)} to signed value ${num.toFixed(3)} (no car connected, signed value received)`); - this.totalMeterValue = num; + if (!isCarConnected && currentMeterPower !== num) { + this.logToDebug(`Correcting total meter value from ${currentMeterPower.toFixed(3)} to signed value ${num.toFixed(3)} (no car connected, signed value received)`); this.setCapabilityValue('meter_power', num).catch(e => this.logToDebug(`Failed to update meter_power: ${e}`) ); } - this.lastSignedMeterValue = num; }) .catch((e) => { this.logToDebug(`Failed to get OCMF-signed value: ${e}`); From 4d5a5897af8a78d59c266bb76ace249cf1c0b150 Mon Sep 17 00:00:00 2001 From: Tycho Bom Date: Mon, 13 Apr 2026 21:38:20 +0200 Subject: [PATCH 7/9] Change capability sequence --- app.json | 2 +- drivers/go2/driver.compose.json | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app.json b/app.json index ee3fc3c..1e68fa6 100644 --- a/app.json +++ b/app.json @@ -2370,8 +2370,8 @@ "capabilities": [ "meter_power.current_session", "meter_power", - "meter_power_total_this_year", "meter_power.last_session", + "meter_power_total_this_year", "measure_power", "charging_button", "cable_permanent_lock", diff --git a/drivers/go2/driver.compose.json b/drivers/go2/driver.compose.json index 5a57a07..368657b 100644 --- a/drivers/go2/driver.compose.json +++ b/drivers/go2/driver.compose.json @@ -9,8 +9,8 @@ "capabilities": [ "meter_power.current_session", "meter_power", - "meter_power_total_this_year", "meter_power.last_session", + "meter_power_total_this_year", "measure_power", "charging_button", "cable_permanent_lock", @@ -38,7 +38,8 @@ } }, "meter_power_total_this_year": { - "title": { "en": "Energy this year", + "title": { + "en": "Energy this year", "no": "Energiforbruk i år" } }, From 031e3a35f857de262c6f1f5a70cb73e79bb6bab4 Mon Sep 17 00:00:00 2001 From: Tycho Bom Date: Tue, 28 Apr 2026 21:21:12 +0200 Subject: [PATCH 8/9] Moved total this year to a sub capability of meter_power to enable default icons and homey logic. Co-authored-by: Copilot --- .../meter_power_total_this_year.json | 16 --------------- app.json | 20 ++----------------- drivers/go2/device.ts | 13 +++++++----- drivers/go2/driver.compose.json | 4 ++-- 4 files changed, 12 insertions(+), 41 deletions(-) delete mode 100644 .homeycompose/capabilities/meter_power_total_this_year.json diff --git a/.homeycompose/capabilities/meter_power_total_this_year.json b/.homeycompose/capabilities/meter_power_total_this_year.json deleted file mode 100644 index d991465..0000000 --- a/.homeycompose/capabilities/meter_power_total_this_year.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "title": { - "en": "Energy this year", - "no": "Energiforbruk i år" - }, - "type": "number", - "units": { - "en": "kWh" - }, - "decimals": 2, - "setable": false, - "getable": true, - "insights": true, - "uiComponent": "sensor", - "chartType": "stepLine" -} diff --git a/app.json b/app.json index 1e68fa6..09b7db1 100644 --- a/app.json +++ b/app.json @@ -2371,7 +2371,7 @@ "meter_power.current_session", "meter_power", "meter_power.last_session", - "meter_power_total_this_year", + "meter_power.total_this_year", "measure_power", "charging_button", "cable_permanent_lock", @@ -2398,7 +2398,7 @@ "no": "Energibruk ladeøkt" } }, - "meter_power_total_this_year": { + "meter_power.total_this_year": { "title": { "en": "Energy this year", "no": "Energiforbruk i år" @@ -3311,22 +3311,6 @@ "insights": false, "uiComponent": "sensor", "icon": "/assets/current_limit.svg" - }, - "meter_power_total_this_year": { - "title": { - "en": "Energy this year", - "no": "Energiforbruk i år" - }, - "type": "number", - "units": { - "en": "kWh" - }, - "decimals": 2, - "setable": false, - "getable": true, - "insights": true, - "uiComponent": "sensor", - "chartType": "stepLine" } } } \ No newline at end of file diff --git a/drivers/go2/device.ts b/drivers/go2/device.ts index a6b1feb..88d6ed5 100644 --- a/drivers/go2/device.ts +++ b/drivers/go2/device.ts @@ -23,6 +23,10 @@ export class Go2Charger extends Homey.Device { */ async onInit() { this.log('Go2Charger is initializing'); + + // Migrate capabilities FIRST before anything else + await this.migrateCapabilities(); + const appVersion = this.homey.app.manifest.version; this.api = new ZaptecApi(appVersion, this.homey); this.renewToken(); @@ -33,7 +37,6 @@ export class Go2Charger extends Homey.Device { ); await this.migrateEnergy(); - await this.migrateCapabilities(); this.registerCapabilityListeners(); @@ -74,9 +77,9 @@ export class Go2Charger extends Homey.Device { await this.addCapability('charging_mode'); } - // Add total energy this year capability - if (!this.hasCapability('meter_power_total_this_year')) { - await this.addCapability('meter_power_total_this_year'); + // Add total energy this year capability if it doesn't exist + if (!this.hasCapability('meter_power.total_this_year')) { + await this.addCapability('meter_power.total_this_year'); } } /** @@ -349,7 +352,7 @@ export class Go2Charger extends Homey.Device { .then((charges) => { const yearlyEnergy = charges?.reduce((sum, charge) => sum + charge.Energy, 0) || 0; - return this.setCapabilityValue('meter_power_total_this_year', yearlyEnergy); + return this.setCapabilityValue('meter_power.total_this_year', yearlyEnergy); }) .then(() => { this.logToDebug(`Got yearly power history`); diff --git a/drivers/go2/driver.compose.json b/drivers/go2/driver.compose.json index 368657b..8cb8fdc 100644 --- a/drivers/go2/driver.compose.json +++ b/drivers/go2/driver.compose.json @@ -10,7 +10,7 @@ "meter_power.current_session", "meter_power", "meter_power.last_session", - "meter_power_total_this_year", + "meter_power.total_this_year", "measure_power", "charging_button", "cable_permanent_lock", @@ -37,7 +37,7 @@ "no": "Energibruk ladeøkt" } }, - "meter_power_total_this_year": { + "meter_power.total_this_year": { "title": { "en": "Energy this year", "no": "Energiforbruk i år" From 99c37b397f355ca5f185aeeaed21fbe5bdbd2943 Mon Sep 17 00:00:00 2001 From: Tycho Bom Date: Sat, 2 May 2026 15:35:55 +0200 Subject: [PATCH 9/9] added retrieval of state after changing the current limit to make active load balancing possible Co-authored-by: Copilot --- drivers/go2/device.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/go2/device.ts b/drivers/go2/device.ts index 88d6ed5..5f9fd34 100644 --- a/drivers/go2/device.ts +++ b/drivers/go2/device.ts @@ -747,6 +747,11 @@ export class Go2Charger extends Homey.Device { // Poll new value to see what was actually set await this.pollInstallationValues(); this.logToDebug(`Updated available current`); + + // Schedule additional state polls to capture charger response to new limit + setTimeout(() => this.pollValues(), 7000); // 7 seconds + setTimeout(() => this.pollValues(), 12000); // 12 seconds + return true; }) .catch((e) => {