From cbad23f407b40b896cd552bd98039758639e2d1c Mon Sep 17 00:00:00 2001 From: Svhelge Date: Sun, 3 May 2026 15:06:30 +0200 Subject: [PATCH 01/45] Add support for Charge --- src/devices/futurehome.ts | 134 +++++++++++++++++++++++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 515720b4fe31b..26a94e0ab860d 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -1,11 +1,20 @@ +import {Zcl} from "zigbee-herdsman"; import * as exposes from "../lib/exposes"; import * as m from "../lib/modernExtend"; import * as tuya from "../lib/tuya"; -import type {DefinitionWithExtend, Fz} from "../lib/types"; +import type {DefinitionWithExtend, Fz, ModernExtend, Tz} from "../lib/types"; const e = exposes.presets; const ea = exposes.access; +interface FuturehomeHaApplianceControl { + attributes: { + autoCharge: number; + }; + commands: never; + commandResponses: never; +} + const localValueConverters = { energyMonotonic: { from: (value: number, meta: Fz.Meta) => { @@ -22,6 +31,72 @@ const localValueConverters = { }, }; +const futurehomeExtend = { + chargerStatus: (): ModernExtend => { + return { + isModernExtend: true, + fromZigbee: [ + { + cluster: "haApplianceControl", + type: ["commandSignalStateNotification", "commandSignalStateRsp"], + convert(model, msg, publish, options, meta) { + const status = msg?.data?.applianceStatus; + if (status === undefined || status === null) return; + let chargerStatus: "plugged_out" | "plugged_in" | "plugged_in_charging" | "plugged_in_paused" = "plugged_out"; + let charging = false; + + switch (status) { + // case 0x01: // Off + case 0x02: // StandBy → charging + chargerStatus = "plugged_in_charging"; + charging = true; + break; + + case 0x03: // Programmed (paused by user) + chargerStatus = "plugged_in_paused"; + charging = false; + break; + + case 0x04: // ProgrammedWaitingToStart + chargerStatus = "plugged_in"; + charging = false; + break; + + // case 0x08: // Failure + default: + chargerStatus = "plugged_out"; + charging = false; + } + return {charger_status: chargerStatus, charging}; + }, + } satisfies Fz.Converter<"haApplianceControl", undefined, ["commandSignalStateNotification", "commandSignalStateRsp"]>, + ], + exposes: [ + exposes + .enum("charger_status", ea.STATE, ["plugged_out", "plugged_in", "plugged_in_charging", "plugged_in_paused"]) + .withDescription("Current EV charger state"), + ], + }; + }, + charging: (): ModernExtend => { + return { + isModernExtend: true, + fromZigbee: [], + toZigbee: [ + { + key: ["charging"], + convertSet: async (entity, key, value, meta) => { + const isStart = value === "Start"; + await entity.command("haApplianceControl", "executionOfCommand", {commandId: isStart ? 0x01 : 0x03}); + return {state: {charging: isStart ? "Start" : "Pause"}}; + }, + } satisfies Tz.Converter, + ], + exposes: [exposes.binary("charging", ea.STATE_SET, "Start", "Pause").withDescription("Start or pause charging.")], + }; + }, +}; + export const definitions: DefinitionWithExtend[] = [ { fingerprint: tuya.fingerprint("TS0601", ["_TZE204_e5hpkc6d", "_TZE200_4hbx5cvx", "_TZE200_e5hpkc6d"]), @@ -106,4 +181,61 @@ export const definitions: DefinitionWithExtend[] = [ ota: true, extend: [m.light({configureReporting: true})], }, + { + zigbeeModel: ["Charge"], + model: "Charge", + vendor: "Futurehome", + description: "Futurehome Charge (EV Charger)", + extend: [ + m.deviceAddCustomCluster("haApplianceControl", { + name: "haApplianceControl", + ID: Zcl.Clusters.haApplianceControl.ID, + attributes: { + autoCharge: { + name: "autoCharge", + ID: 0xef0c, + type: Zcl.DataType.UINT8, + manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, + write: true, + }, + }, + commands: {}, + commandsResponse: {}, + }), + futurehomeExtend.chargerStatus(), + futurehomeExtend.charging(), + m.binary({ + name: "cable_locked", + cluster: "closuresDoorLock", + attribute: "operatingMode", + valueOff: ["UNLOCK", 0x00], + valueOn: ["LOCK", 0x02], + description: "Permanently lock cable when not charging.", + }), + m.numeric({ + name: "current_limit", + cluster: "genAnalogOutput", + attribute: "maxPresentValue", + description: "Maximum charging current.", + unit: "A", + access: "ALL", + valueMin: 6, + valueMax: 32, + valueStep: 1, + }), + m.binary<"haApplianceControl", FuturehomeHaApplianceControl>({ + name: "auto_charge", + cluster: "haApplianceControl", + attribute: "autoCharge", + description: "Automatically start charging when a car is connected.", + valueOff: ["OFF", 0], + valueOn: ["ON", 1], + zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, + }), + m.electricityMeter({ + power: {cluster: "electrical"}, + threePhase: true, + }), + ], + }, ]; From 95b487f1fbd154cc5fcd5adf68a7ac9e97c50269 Mon Sep 17 00:00:00 2001 From: Svhelge Date: Sun, 3 May 2026 23:48:28 +0200 Subject: [PATCH 02/45] Use totalActivePower for power --- src/devices/futurehome.ts | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 26a94e0ab860d..be64b6876ce76 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -73,7 +73,7 @@ const futurehomeExtend = { ], exposes: [ exposes - .enum("charger_status", ea.STATE, ["plugged_out", "plugged_in", "plugged_in_charging", "plugged_in_paused"]) + .enum("charger_status", ea.STATE_GET, ["plugged_out", "plugged_in", "plugged_in_charging", "plugged_in_paused"]) .withDescription("Current EV charger state"), ], }; @@ -212,6 +212,17 @@ export const definitions: DefinitionWithExtend[] = [ valueOn: ["LOCK", 0x02], description: "Permanently lock cable when not charging.", }), + // lockState (2), actuatorEnabled (176), supportedOperatingModes (261) + m.numeric({ + name: "actual_current", + cluster: "genAnalogOutput", + attribute: "presentValue", + description: "Actual charging current ??", + unit: "A", + access: "STATE", + reporting: {min: "10_SECONDS", max: "1_HOUR", change: 1}, + }), + m.numeric({ name: "current_limit", cluster: "genAnalogOutput", @@ -222,7 +233,9 @@ export const definitions: DefinitionWithExtend[] = [ valueMin: 6, valueMax: 32, valueStep: 1, + reporting: {min: "10_SECONDS", max: "1_HOUR", change: 1}, }), + // genAnalogOutput: outOfService 0 m.binary<"haApplianceControl", FuturehomeHaApplianceControl>({ name: "auto_charge", cluster: "haApplianceControl", @@ -232,8 +245,17 @@ export const definitions: DefinitionWithExtend[] = [ valueOn: ["ON", 1], zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), + m.numeric<"haElectricalMeasurement", undefined>({ + name: "power", + cluster: "haElectricalMeasurement", + attribute: "totalActivePower", + description: "Power", + unit: "W", + access: "STATE_GET", + reporting: {min: 5, max: "1_HOUR", change: 1}, + }), m.electricityMeter({ - power: {cluster: "electrical"}, + power: false, threePhase: true, }), ], From 17d5bf94d1ae2299fdddd27c049b65e9793ca4eb Mon Sep 17 00:00:00 2001 From: Svhelge Date: Mon, 4 May 2026 00:39:35 +0200 Subject: [PATCH 03/45] Add divisor=1000 for energy --- src/devices/futurehome.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index be64b6876ce76..fbe6d41de0e45 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -255,6 +255,7 @@ export const definitions: DefinitionWithExtend[] = [ reporting: {min: 5, max: "1_HOUR", change: 1}, }), m.electricityMeter({ + energy: {divisor: 1000, multiplier: 1}, power: false, threePhase: true, }), From fae29d64b39534fd65e42fe0ac268af92d0bbfeb Mon Sep 17 00:00:00 2001 From: Svhelge Date: Mon, 4 May 2026 00:50:41 +0200 Subject: [PATCH 04/45] Increase divisor to 1000000 --- src/devices/futurehome.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index fbe6d41de0e45..4ad480b8d5829 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -255,7 +255,7 @@ export const definitions: DefinitionWithExtend[] = [ reporting: {min: 5, max: "1_HOUR", change: 1}, }), m.electricityMeter({ - energy: {divisor: 1000, multiplier: 1}, + energy: {divisor: 1000000, multiplier: 1}, power: false, threePhase: true, }), From 5fdebe066eeba1c48cf7b530141f799e4245ea95 Mon Sep 17 00:00:00 2001 From: Svhelge Date: Mon, 4 May 2026 02:34:04 +0200 Subject: [PATCH 05/45] fix reporting maxPresentValue --- src/devices/futurehome.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 4ad480b8d5829..5c3252813b08f 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -233,7 +233,6 @@ export const definitions: DefinitionWithExtend[] = [ valueMin: 6, valueMax: 32, valueStep: 1, - reporting: {min: "10_SECONDS", max: "1_HOUR", change: 1}, }), // genAnalogOutput: outOfService 0 m.binary<"haApplianceControl", FuturehomeHaApplianceControl>({ From 6614a781c080547e3eb8c33721119eb165d823c4 Mon Sep 17 00:00:00 2001 From: Svhelge Date: Mon, 4 May 2026 02:38:52 +0200 Subject: [PATCH 06/45] fix divisor for energy... --- src/devices/futurehome.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 5c3252813b08f..3b5636dbab7c0 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -254,7 +254,7 @@ export const definitions: DefinitionWithExtend[] = [ reporting: {min: 5, max: "1_HOUR", change: 1}, }), m.electricityMeter({ - energy: {divisor: 1000000, multiplier: 1}, + energy: {divisor: 1000, multiplier: 1}, power: false, threePhase: true, }), From 987e48ea938ddb0ea0957270f3a5f7db5e83cdab Mon Sep 17 00:00:00 2001 From: Svhelge Date: Tue, 5 May 2026 13:54:13 +0200 Subject: [PATCH 07/45] Minor fixes --- src/devices/futurehome.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 3b5636dbab7c0..a0fc04e232b6d 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -187,6 +187,16 @@ export const definitions: DefinitionWithExtend[] = [ vendor: "Futurehome", description: "Futurehome Charge (EV Charger)", extend: [ + // m.numeric({ + // "name":"analog_output_1", + // "valueMax":2143289344, + // "cluster":"genAnalogOutput", + // "attribute":"presentValue", + // "reporting":{"min":"MIN","max":"MAX","change":1}, + // "description":"Analog Output analog_output_1 on endpoint 1","access":"ALL" + // }), + // m.lock({"pinCodeCount":50}), + // m.electricityMeter(), m.deviceAddCustomCluster("haApplianceControl", { name: "haApplianceControl", ID: Zcl.Clusters.haApplianceControl.ID, @@ -213,8 +223,11 @@ export const definitions: DefinitionWithExtend[] = [ description: "Permanently lock cable when not charging.", }), // lockState (2), actuatorEnabled (176), supportedOperatingModes (261) + // 'closuresDoorLock': {"lockState":2,"supportedOperatingModes":261,"actuatorEnabled":176} + // 'genMultistateValue': {"numberOfStates":8,"outOfService":0,"presentValue":5} + // 'genMultistateInput': {"numberOfStates":6,"outOfService":0,"presentValue":65} m.numeric({ - name: "actual_current", + name: "charging_current", cluster: "genAnalogOutput", attribute: "presentValue", description: "Actual charging current ??", From 41c7e4814be37e7ee9936b468e91afb7940b324d Mon Sep 17 00:00:00 2001 From: Svhelge Date: Thu, 7 May 2026 21:59:35 +0200 Subject: [PATCH 08/45] Change charging function, use enum for more options --- src/devices/futurehome.ts | 45 +++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index a0fc04e232b6d..fcc165d60082f 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -2,7 +2,7 @@ import {Zcl} from "zigbee-herdsman"; import * as exposes from "../lib/exposes"; import * as m from "../lib/modernExtend"; import * as tuya from "../lib/tuya"; -import type {DefinitionWithExtend, Fz, ModernExtend, Tz} from "../lib/types"; +import type {DefinitionWithExtend, Fz, KeyValueAny, ModernExtend, Tz} from "../lib/types"; const e = exposes.presets; const ea = exposes.access; @@ -42,27 +42,47 @@ const futurehomeExtend = { convert(model, msg, publish, options, meta) { const status = msg?.data?.applianceStatus; if (status === undefined || status === null) return; - let chargerStatus: "plugged_out" | "plugged_in" | "plugged_in_charging" | "plugged_in_paused" = "plugged_out"; + let chargerStatus: + | "plugged_out" + | "1X: Off" + | "4: plugged_in" + | "2: plugged_in_charging" + | "3: plugged_in_paused" + | "5X: running" + | "8X: failure" = "plugged_out"; let charging = false; switch (status) { - // case 0x01: // Off + case 0x01: // Off + chargerStatus = "1X: Off"; + charging = false; + break; + case 0x02: // StandBy → charging - chargerStatus = "plugged_in_charging"; + chargerStatus = "2: plugged_in_charging"; charging = true; break; case 0x03: // Programmed (paused by user) - chargerStatus = "plugged_in_paused"; + chargerStatus = "3: plugged_in_paused"; charging = false; break; case 0x04: // ProgrammedWaitingToStart - chargerStatus = "plugged_in"; + chargerStatus = "4: plugged_in"; + charging = false; + break; + + case 0x05: // Running + chargerStatus = "5X: running"; + charging = false; + break; + + case 0x08: // Failure + chargerStatus = "8X: failure"; charging = false; break; - // case 0x08: // Failure default: chargerStatus = "plugged_out"; charging = false; @@ -86,13 +106,16 @@ const futurehomeExtend = { { key: ["charging"], convertSet: async (entity, key, value, meta) => { - const isStart = value === "Start"; - await entity.command("haApplianceControl", "executionOfCommand", {commandId: isStart ? 0x01 : 0x03}); - return {state: {charging: isStart ? "Start" : "Pause"}}; + // const isStart = value === "Start"; + const lookup: KeyValueAny = {Start: "0x01", Stop: "0x02", Pause: "0x03"}; + // result.mode_after_dry = lookup[data[0x1007] as number]; + await entity.command("haApplianceControl", "executionOfCommand", {commandId: lookup[value as keyof typeof lookup]}); + return {state: {charging: value}}; }, } satisfies Tz.Converter, ], - exposes: [exposes.binary("charging", ea.STATE_SET, "Start", "Pause").withDescription("Start or pause charging.")], + exposes: [exposes.enum("charging", ea.STATE_SET, ["Start", "Pause", "Stop"]).withDescription("Start or pause charging.")], + // .enum("dimmer_mode", ea.ALL, ["auto", "rc", "rl", "rl_led"]) }; }, }; From b82243f73cb255fa231ab38706a015cca658e147 Mon Sep 17 00:00:00 2001 From: Svhelge Date: Fri, 8 May 2026 01:47:54 +0200 Subject: [PATCH 09/45] Add more status codes --- src/devices/futurehome.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index fcc165d60082f..65de98afa094e 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -106,16 +106,13 @@ const futurehomeExtend = { { key: ["charging"], convertSet: async (entity, key, value, meta) => { - // const isStart = value === "Start"; const lookup: KeyValueAny = {Start: "0x01", Stop: "0x02", Pause: "0x03"}; - // result.mode_after_dry = lookup[data[0x1007] as number]; await entity.command("haApplianceControl", "executionOfCommand", {commandId: lookup[value as keyof typeof lookup]}); return {state: {charging: value}}; }, } satisfies Tz.Converter, ], exposes: [exposes.enum("charging", ea.STATE_SET, ["Start", "Pause", "Stop"]).withDescription("Start or pause charging.")], - // .enum("dimmer_mode", ea.ALL, ["auto", "rc", "rl", "rl_led"]) }; }, }; From e3622d14896fb5faf8bde90d96638c3389c27424 Mon Sep 17 00:00:00 2001 From: Svhelge Date: Sat, 9 May 2026 12:48:23 +0200 Subject: [PATCH 10/45] Update description and name for attribute presentValue (genAnalogOutput). --- src/devices/futurehome.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 65de98afa094e..95eac4f007399 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -242,15 +242,14 @@ export const definitions: DefinitionWithExtend[] = [ valueOn: ["LOCK", 0x02], description: "Permanently lock cable when not charging.", }), - // lockState (2), actuatorEnabled (176), supportedOperatingModes (261) // 'closuresDoorLock': {"lockState":2,"supportedOperatingModes":261,"actuatorEnabled":176} // 'genMultistateValue': {"numberOfStates":8,"outOfService":0,"presentValue":5} // 'genMultistateInput': {"numberOfStates":6,"outOfService":0,"presentValue":65} m.numeric({ - name: "charging_current", + name: "setpoint_charging_current", cluster: "genAnalogOutput", attribute: "presentValue", - description: "Actual charging current ??", + description: "Setpoint charging current (as defined by charger) ??", unit: "A", access: "STATE", reporting: {min: "10_SECONDS", max: "1_HOUR", change: 1}, From 2cb908ed2c2ba3edefe94aafa25c71dd2df3b7b4 Mon Sep 17 00:00:00 2001 From: Svhelge Date: Fri, 29 May 2026 22:22:50 +0200 Subject: [PATCH 11/45] fix charging current --- src/devices/futurehome.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 95eac4f007399..80723fe157116 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -242,21 +242,27 @@ export const definitions: DefinitionWithExtend[] = [ valueOn: ["LOCK", 0x02], description: "Permanently lock cable when not charging.", }), - // 'closuresDoorLock': {"lockState":2,"supportedOperatingModes":261,"actuatorEnabled":176} + // 'closuresDoorLock': + // {"lockState":2,"supportedOperatingModes":261,"actuatorEnabled":176} + // 'under lading {"actuatorEnabled":177,"supportedOperatingModes":261,"operatingMode":0,"lockType":null,"lockState":1} + // 'genMultistateValue': {"numberOfStates":8,"outOfService":0,"presentValue":5} // 'genMultistateInput': {"numberOfStates":6,"outOfService":0,"presentValue":65} m.numeric({ name: "setpoint_charging_current", cluster: "genAnalogOutput", attribute: "presentValue", - description: "Setpoint charging current (as defined by charger) ??", + description: "Setpoint charging current", unit: "A", - access: "STATE", - reporting: {min: "10_SECONDS", max: "1_HOUR", change: 1}, + access: "ALL", + valueMin: 6, + valueMax: 32, + valueStep: 1, + // reporting: {min: "10_SECONDS", max: "1_HOUR", change: 1}, }), m.numeric({ - name: "current_limit", + name: "charging_current_limit", cluster: "genAnalogOutput", attribute: "maxPresentValue", description: "Maximum charging current.", @@ -266,7 +272,7 @@ export const definitions: DefinitionWithExtend[] = [ valueMax: 32, valueStep: 1, }), - // genAnalogOutput: outOfService 0 + // genAnalogOutput': {"presentValue":8,"maxPresentValue":32,"outOfService":0,"statusFlags":0} m.binary<"haApplianceControl", FuturehomeHaApplianceControl>({ name: "auto_charge", cluster: "haApplianceControl", From 37b10105b7ee7e8c706a4b2f42fe76ef2094e098 Mon Sep 17 00:00:00 2001 From: Svhelge Date: Fri, 29 May 2026 22:22:50 +0200 Subject: [PATCH 12/45] fix charging current --- src/devices/futurehome.ts | 45 ++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 95eac4f007399..fda85a5719950 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -91,6 +91,14 @@ const futurehomeExtend = { }, } satisfies Fz.Converter<"haApplianceControl", undefined, ["commandSignalStateNotification", "commandSignalStateRsp"]>, ], + toZigbee: [ + { + key: ["charger_status"], + convertGet: async (entity, key, meta) => { + await entity.command("haApplianceControl", "signalState", {}); + }, + } satisfies Tz.Converter, + ], exposes: [ exposes .enum("charger_status", ea.STATE_GET, ["plugged_out", "plugged_in", "plugged_in_charging", "plugged_in_paused"]) @@ -110,6 +118,9 @@ const futurehomeExtend = { await entity.command("haApplianceControl", "executionOfCommand", {commandId: lookup[value as keyof typeof lookup]}); return {state: {charging: value}}; }, + convertGet: async (entity, key, meta) => { + await entity.command("haApplianceControl", "signalState", {}); + }, } satisfies Tz.Converter, ], exposes: [exposes.enum("charging", ea.STATE_SET, ["Start", "Pause", "Stop"]).withDescription("Start or pause charging.")], @@ -241,22 +252,38 @@ export const definitions: DefinitionWithExtend[] = [ valueOff: ["UNLOCK", 0x00], valueOn: ["LOCK", 0x02], description: "Permanently lock cable when not charging.", + entityCategory: "config", + zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), - // 'closuresDoorLock': {"lockState":2,"supportedOperatingModes":261,"actuatorEnabled":176} - // 'genMultistateValue': {"numberOfStates":8,"outOfService":0,"presentValue":5} - // 'genMultistateInput': {"numberOfStates":6,"outOfService":0,"presentValue":65} + // 'closuresDoorLock': + // {"lockState":2,"supportedOperatingModes":261,"actuatorEnabled":176} + // 'under lading {"actuatorEnabled":177,"supportedOperatingModes":261,"operatingMode":0,"lockType":null,"lockState":1} + + // genAnalogOutput': + // Under lading {"maxPresentValue":32,"outOfService":0,"presentValue":7,"statusFlags":0} + + // 'genMultistateValue': + // {"numberOfStates":8,"outOfService":0,"presentValue":5} + // under lading '{"numberOfStates":8,"outOfService":0,"presentValue":5,"reliability":0,"statusFlags":0} + + // 'genMultistateInput': + // {"numberOfStates":6,"outOfService":0,"presentValue":65} + // under lading {"numberOfStates":6,"outOfService":0,"presentValue":67,"statusFlags":0} m.numeric({ name: "setpoint_charging_current", cluster: "genAnalogOutput", attribute: "presentValue", - description: "Setpoint charging current (as defined by charger) ??", + description: "Setpoint charging current", unit: "A", - access: "STATE", - reporting: {min: "10_SECONDS", max: "1_HOUR", change: 1}, + access: "ALL", + valueMin: 6, + valueMax: 32, + valueStep: 1, + // reporting: {min: "10_SECONDS", max: "1_HOUR", change: 1}, }), m.numeric({ - name: "current_limit", + name: "charging_current_limit", cluster: "genAnalogOutput", attribute: "maxPresentValue", description: "Maximum charging current.", @@ -265,8 +292,10 @@ export const definitions: DefinitionWithExtend[] = [ valueMin: 6, valueMax: 32, valueStep: 1, + entityCategory: "config", + zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), - // genAnalogOutput: outOfService 0 + // genAnalogOutput': {"presentValue":8,"maxPresentValue":32,"outOfService":0,"statusFlags":0} m.binary<"haApplianceControl", FuturehomeHaApplianceControl>({ name: "auto_charge", cluster: "haApplianceControl", From fd2e2927505335f571dc4f51df097168ec931d49 Mon Sep 17 00:00:00 2001 From: Svhelge Date: Sun, 31 May 2026 13:42:17 +0200 Subject: [PATCH 13/45] Add polling for charger_status --- src/devices/futurehome.ts | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 09a85a58d9abf..219ff2f9b85e5 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -104,10 +104,30 @@ const futurehomeExtend = { for (const endpoint of device.endpoints) { if (endpoint.supportsInputCluster("haApplianceControl")) { await endpoint.bind("haApplianceControl", coordinatorEndpoint); + try { + await endpoint.command("haApplianceControl", "signalState", {}); + } catch { + // do nothing + } } } }, ], + onEvent: m.poll({ + key: "charger_status_poll", + optionKey: "charger_status_poll_interval", + option: e + .numeric("charger_status_poll_interval", ea.SET) + .withValueMin(-1) + .withDescription("Polling interval charger status (default: 60s, -1 to disable)"), + defaultIntervalSeconds: 60, + poll: async (device) => { + const endpoint = device.endpoints.find((e) => e.supportsInputCluster("haApplianceControl")); + if (endpoint) { + await endpoint.command("haApplianceControl", "signalState", {}); + } + }, + }).onEvent, exposes: [ exposes .enum("charger_status", ea.STATE_GET, [ @@ -134,6 +154,11 @@ const futurehomeExtend = { convertSet: async (entity, key, value, meta) => { const lookup: KeyValueAny = {Start: "0x01", Stop: "0x02", Pause: "0x03"}; await entity.command("haApplianceControl", "executionOfCommand", {commandId: lookup[value as keyof typeof lookup]}); + try { + await entity.command("haApplianceControl", "signalState", {}); + } catch { + // do nothing + } return {state: {charging: value}}; }, convertGet: async (entity, key, meta) => { @@ -236,14 +261,6 @@ export const definitions: DefinitionWithExtend[] = [ vendor: "Futurehome", description: "Futurehome Charge (EV Charger)", extend: [ - // m.numeric({ - // "name":"analog_output_1", - // "valueMax":2143289344, - // "cluster":"genAnalogOutput", - // "attribute":"presentValue", - // "reporting":{"min":"MIN","max":"MAX","change":1}, - // "description":"Analog Output analog_output_1 on endpoint 1","access":"ALL" - // }), // m.lock({"pinCodeCount":50}), // m.electricityMeter(), m.deviceAddCustomCluster("haApplianceControl", { From 6fe9dbdaac57be4e22772579e893b0e08a83667d Mon Sep 17 00:00:00 2001 From: Svhelge Date: Sun, 31 May 2026 16:00:56 +0200 Subject: [PATCH 14/45] clean --- src/devices/futurehome.ts | 117 +++++++++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 1 deletion(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 219ff2f9b85e5..9deb0e53a6ab7 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -32,7 +32,7 @@ const localValueConverters = { }; const futurehomeExtend = { - chargerStatus: (): ModernExtend => { + chargerStatus1: (): ModernExtend => { return { isModernExtend: true, fromZigbee: [ @@ -113,6 +113,12 @@ const futurehomeExtend = { } }, ], + options: [ + e + .numeric("charger_status_poll_interval", ea.SET) + .withValueMin(-1) + .withDescription("Polling interval charger status (default: 60s, -1 to disable)"), + ], onEvent: m.poll({ key: "charger_status_poll", optionKey: "charger_status_poll_interval", @@ -144,6 +150,115 @@ const futurehomeExtend = { ], }; }, + chargerStatus: (): ModernExtend => { + const extend: ModernExtend = { + isModernExtend: true, + fromZigbee: [ + { + cluster: "haApplianceControl", + type: ["commandSignalStateNotification", "commandSignalStateRsp"], + convert(model, msg, publish, options, meta) { + const status = msg?.data?.applianceStatus; + if (status === undefined || status === null) return; + let chargerStatus: + | "plugged_out" + | "1X: Off" + | "4: plugged_in" + | "2: plugged_in_charging" + | "3: plugged_in_paused" + | "5X: running" + | "8X: failure" = "plugged_out"; + let chargingOn = false; + + switch (status) { + case 0x01: // Off + chargerStatus = "1X: Off"; + chargingOn = false; + break; + case 0x02: // StandBy → charging + chargerStatus = "2: plugged_in_charging"; + chargingOn = true; + break; + case 0x03: // Programmed (paused by user) + chargerStatus = "3: plugged_in_paused"; + chargingOn = false; + break; + case 0x04: // ProgrammedWaitingToStart + chargerStatus = "4: plugged_in"; + chargingOn = false; + break; + case 0x05: // Running + chargerStatus = "5X: running"; + chargingOn = false; + break; + case 0x08: // Failure + chargerStatus = "8X: failure"; + chargingOn = false; + break; + default: + chargerStatus = "plugged_out"; + chargingOn = false; + } + return {charger_status: chargerStatus, charging_on: chargingOn}; + }, + } satisfies Fz.Converter<"haApplianceControl", undefined, ["commandSignalStateNotification", "commandSignalStateRsp"]>, + ], + toZigbee: [ + { + key: ["charger_status"], + convertGet: async (entity, key, meta) => { + await entity.command("haApplianceControl", "signalState", {}); + }, + } satisfies Tz.Converter, + ], + configure: [ + async (device, coordinatorEndpoint, logger) => { + for (const endpoint of device.endpoints) { + if (endpoint.supportsInputCluster("haApplianceControl")) { + await endpoint.bind("haApplianceControl", coordinatorEndpoint); + try { + await endpoint.command("haApplianceControl", "signalState", {}); + } catch { + // do nothing + } + } + } + }, + ], + exposes: [ + exposes + .enum("charger_status", ea.STATE_GET, [ + "plugged_out", + "1X: Off", + "2: plugged_in_charging", + "3: plugged_in_paused", + "4: plugged_in", + "5X: running", + "8X: failure", + ]) + .withDescription("Current EV charger state"), + exposes.binary("charging_on", ea.STATE, "ON", "OFF").withDescription("Indicates if the charger is actively delivering power"), + ], + }; + const pollExtend = m.poll({ + key: "charger_status_poll", + optionKey: "charger_status_poll_interval", + option: e + .numeric("charger_status_poll_interval", ea.SET) + .withValueMin(-1) + .withDescription("Polling interval charger status (default: 60s, -1 to disable)"), + defaultIntervalSeconds: 60, + poll: async (device) => { + const endpoint = device.endpoints.find((e) => e.supportsInputCluster("haApplianceControl")); + if (endpoint) { + await endpoint.command("haApplianceControl", "signalState", {}); + } + }, + }); + extend.onEvent = pollExtend.onEvent; + extend.options = pollExtend.options; + return extend; + }, charging: (): ModernExtend => { return { isModernExtend: true, From e36b37468f033c39f74cecbae0c337fb4f7d8a7e Mon Sep 17 00:00:00 2001 From: Svhelge Date: Sun, 31 May 2026 16:30:06 +0200 Subject: [PATCH 15/45] change expose for charging --- src/devices/futurehome.ts | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 9deb0e53a6ab7..8923a97f4ec7e 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -2,7 +2,7 @@ import {Zcl} from "zigbee-herdsman"; import * as exposes from "../lib/exposes"; import * as m from "../lib/modernExtend"; import * as tuya from "../lib/tuya"; -import type {DefinitionWithExtend, Fz, KeyValueAny, ModernExtend, Tz} from "../lib/types"; +import type {DefinitionWithExtend, Fz, ModernExtend, Tz} from "../lib/types"; const e = exposes.presets; const ea = exposes.access; @@ -260,28 +260,46 @@ const futurehomeExtend = { return extend; }, charging: (): ModernExtend => { + const commandLookup: {[key: string]: number} = { + start: 0x01, + stop: 0x02, + pause: 0x03, + }; return { isModernExtend: true, fromZigbee: [], toZigbee: [ { - key: ["charging"], + // key: ["charging"], + key: ["charging_start", "charging_stop", "charging_paused"], convertSet: async (entity, key, value, meta) => { - const lookup: KeyValueAny = {Start: "0x01", Stop: "0x02", Pause: "0x03"}; - await entity.command("haApplianceControl", "executionOfCommand", {commandId: lookup[value as keyof typeof lookup]}); + const action = key.replace("charging_", ""); // "start", "stop", or "paused" + const normalizedAction = action === "paused" ? "pause" : action; + const commandId = commandLookup[normalizedAction]; + // const lookup: KeyValueAny = {Start: "0x01", Stop: "0x02", Pause: "0x03"}; + // await entity.command("haApplianceControl", "executionOfCommand", {commandId: lookup[value as keyof typeof lookup]}); + await entity.command("haApplianceControl", "executionOfCommand", {commandId: commandId}); try { await entity.command("haApplianceControl", "signalState", {}); } catch { // do nothing } - return {state: {charging: value}}; + return; // {state: {charging: value}}; }, convertGet: async (entity, key, meta) => { await entity.command("haApplianceControl", "signalState", {}); }, } satisfies Tz.Converter, ], - exposes: [exposes.enum("charging", ea.STATE_SET, ["Start", "Pause", "Stop"]).withDescription("Start or pause charging.")], + // exposes: [exposes.enum("charging", ea.STATE_SET, ["Start", "Pause", "Stop"]).withDescription("Start or pause charging.")], + exposes: [ + exposes + .composite("charging_commands", "charging_commands", ea.SET) + .withDescription("Execute charging control actions") + .withFeature(exposes.enum("start", ea.SET, ["press"]).withDescription("Start charging session")) + .withFeature(exposes.enum("stop", ea.SET, ["press"]).withDescription("Stop charging session")) + .withFeature(exposes.enum("paused", ea.SET, ["press"]).withDescription("Pause charging session")), + ], }; }, }; From 39bdbefa21f03e286be904518ab02665ac29d6cb Mon Sep 17 00:00:00 2001 From: Svhelge Date: Sun, 31 May 2026 16:40:05 +0200 Subject: [PATCH 16/45] expose for HA --- src/devices/futurehome.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 8923a97f4ec7e..6dbbecd0a31eb 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -293,12 +293,15 @@ const futurehomeExtend = { ], // exposes: [exposes.enum("charging", ea.STATE_SET, ["Start", "Pause", "Stop"]).withDescription("Start or pause charging.")], exposes: [ - exposes - .composite("charging_commands", "charging_commands", ea.SET) - .withDescription("Execute charging control actions") - .withFeature(exposes.enum("start", ea.SET, ["press"]).withDescription("Start charging session")) - .withFeature(exposes.enum("stop", ea.SET, ["press"]).withDescription("Stop charging session")) - .withFeature(exposes.enum("paused", ea.SET, ["press"]).withDescription("Pause charging session")), + exposes.enum("charging_start", ea.SET, ["start"]).withDescription("Press to start charging"), + exposes.enum("charging_stop", ea.SET, ["stop"]).withDescription("Press to stop charging"), + exposes.enum("charging_paused", ea.SET, ["pause"]).withDescription("Press to pause charging"), + // exposes + // .composite("charging_commands", "charging_commands", ea.SET) + // .withDescription("Execute charging control actions") + // .withFeature(exposes.enum("start", ea.SET, ["press"]).withDescription("Start charging session")) + // .withFeature(exposes.enum("stop", ea.SET, ["press"]).withDescription("Stop charging session")) + // .withFeature(exposes.enum("paused", ea.SET, ["press"]).withDescription("Pause charging session")), ], }; }, From 6ffb27388bb64ec36686296d5c8c1da75d8e8b88 Mon Sep 17 00:00:00 2001 From: Svhelge Date: Sun, 31 May 2026 16:40:05 +0200 Subject: [PATCH 17/45] expose for HA --- src/devices/futurehome.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 8923a97f4ec7e..992d3563fb97d 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -146,7 +146,7 @@ const futurehomeExtend = { "8X: failure", ]) .withDescription("Current EV charger state"), - exposes.binary("charging_on", ea.STATE, "ON", "OFF").withDescription("Indicates if the charger is actively delivering power"), + exposes.binary("charging_on", ea.STATE, "true", "false").withDescription("Indicates if the charger is actively delivering power"), ], }; }, @@ -293,12 +293,15 @@ const futurehomeExtend = { ], // exposes: [exposes.enum("charging", ea.STATE_SET, ["Start", "Pause", "Stop"]).withDescription("Start or pause charging.")], exposes: [ - exposes - .composite("charging_commands", "charging_commands", ea.SET) - .withDescription("Execute charging control actions") - .withFeature(exposes.enum("start", ea.SET, ["press"]).withDescription("Start charging session")) - .withFeature(exposes.enum("stop", ea.SET, ["press"]).withDescription("Stop charging session")) - .withFeature(exposes.enum("paused", ea.SET, ["press"]).withDescription("Pause charging session")), + exposes.enum("charging_start", ea.SET, ["start"]).withDescription("Press to start charging"), + exposes.enum("charging_stop", ea.SET, ["stop"]).withDescription("Press to stop charging"), + exposes.enum("charging_paused", ea.SET, ["pause"]).withDescription("Press to pause charging"), + // exposes + // .composite("charging_commands", "charging_commands", ea.SET) + // .withDescription("Execute charging control actions") + // .withFeature(exposes.enum("start", ea.SET, ["press"]).withDescription("Start charging session")) + // .withFeature(exposes.enum("stop", ea.SET, ["press"]).withDescription("Stop charging session")) + // .withFeature(exposes.enum("paused", ea.SET, ["press"]).withDescription("Pause charging session")), ], }; }, @@ -420,7 +423,6 @@ export const definitions: DefinitionWithExtend[] = [ valueOff: ["UNLOCK", 0x00], valueOn: ["LOCK", 0x02], description: "Permanently lock cable when not charging.", - entityCategory: "config", zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), // 'closuresDoorLock': From 2f4e955cd349fd598859c10717187a0ed71b9a81 Mon Sep 17 00:00:00 2001 From: Svhelge Date: Sun, 31 May 2026 17:06:00 +0200 Subject: [PATCH 18/45] Command texts --- src/devices/futurehome.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 0645c3b5d67fc..6b64a191bb2ea 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -293,9 +293,9 @@ const futurehomeExtend = { ], // exposes: [exposes.enum("charging", ea.STATE_SET, ["Start", "Pause", "Stop"]).withDescription("Start or pause charging.")], exposes: [ - exposes.enum("charging_start", ea.SET, ["start"]).withLabel("start_charging").withDescription("Press to start charging"), - exposes.enum("charging_stop", ea.SET, ["stop"]).withLabel("stop_charging").withDescription("Press to stop charging"), - exposes.enum("charging_paused", ea.SET, ["pause"]).withLabel("pause_charging").withDescription("Press to pause charging"), + exposes.enum("charging_start", ea.SET, ["start"]).withLabel("Start charging").withDescription("Press to start charging"), + exposes.enum("charging_stop", ea.SET, ["stop"]).withLabel("Stop charging").withDescription("Press to stop charging"), + exposes.enum("charging_paused", ea.SET, ["pause"]).withLabel("Pause charging").withDescription("Press to pause charging"), // exposes // .composite("charging_commands", "charging_commands", ea.SET) // .withDescription("Execute charging control actions") From 9eea542f77d6459f6bed8aa77db555362897cbcc Mon Sep 17 00:00:00 2001 From: Svhelge Date: Sun, 31 May 2026 17:17:59 +0200 Subject: [PATCH 19/45] fix chargingOn --- src/devices/futurehome.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 6b64a191bb2ea..2b18917f71d1c 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -168,38 +168,38 @@ const futurehomeExtend = { | "3: plugged_in_paused" | "5X: running" | "8X: failure" = "plugged_out"; - let chargingOn = false; + let chargingOn = 0; switch (status) { case 0x01: // Off chargerStatus = "1X: Off"; - chargingOn = false; + chargingOn = 0; break; case 0x02: // StandBy → charging chargerStatus = "2: plugged_in_charging"; - chargingOn = true; + chargingOn = 1; break; case 0x03: // Programmed (paused by user) chargerStatus = "3: plugged_in_paused"; - chargingOn = false; + chargingOn = 0; break; case 0x04: // ProgrammedWaitingToStart chargerStatus = "4: plugged_in"; - chargingOn = false; + chargingOn = 0; break; case 0x05: // Running chargerStatus = "5X: running"; - chargingOn = false; + chargingOn = 0; break; case 0x08: // Failure chargerStatus = "8X: failure"; - chargingOn = false; + chargingOn = 0; break; default: chargerStatus = "plugged_out"; - chargingOn = false; + chargingOn = 0; } - return {charger_status: chargerStatus, charging_on: chargingOn}; + return {charger_status: chargerStatus, charging_on: chargingOn === 1}; }, } satisfies Fz.Converter<"haApplianceControl", undefined, ["commandSignalStateNotification", "commandSignalStateRsp"]>, ], From 1ecb70e51a17c2150f67e856e238feac726766fe Mon Sep 17 00:00:00 2001 From: Svhelge Date: Sun, 31 May 2026 23:47:08 +0200 Subject: [PATCH 20/45] tuning --- src/devices/futurehome.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 2b18917f71d1c..8b9b2ee8ddf39 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -205,7 +205,7 @@ const futurehomeExtend = { ], toZigbee: [ { - key: ["charger_status"], + key: ["charger_status", "charging_on"], convertGet: async (entity, key, meta) => { await entity.command("haApplianceControl", "signalState", {}); }, From 7180f65463b5832fbdd1f0fee77ff4f75251f763 Mon Sep 17 00:00:00 2001 From: Svhelge Date: Mon, 1 Jun 2026 00:10:36 +0200 Subject: [PATCH 21/45] fix expose --- src/devices/futurehome.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 8b9b2ee8ddf39..aee62cac95d39 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -168,38 +168,38 @@ const futurehomeExtend = { | "3: plugged_in_paused" | "5X: running" | "8X: failure" = "plugged_out"; - let chargingOn = 0; + let chargingOn = false; switch (status) { case 0x01: // Off chargerStatus = "1X: Off"; - chargingOn = 0; + chargingOn = false; break; case 0x02: // StandBy → charging chargerStatus = "2: plugged_in_charging"; - chargingOn = 1; + chargingOn = true; break; case 0x03: // Programmed (paused by user) chargerStatus = "3: plugged_in_paused"; - chargingOn = 0; + chargingOn = false; break; case 0x04: // ProgrammedWaitingToStart chargerStatus = "4: plugged_in"; - chargingOn = 0; + chargingOn = false; break; case 0x05: // Running chargerStatus = "5X: running"; - chargingOn = 0; + chargingOn = false; break; case 0x08: // Failure chargerStatus = "8X: failure"; - chargingOn = 0; + chargingOn = false; break; default: chargerStatus = "plugged_out"; - chargingOn = 0; + chargingOn = false; } - return {charger_status: chargerStatus, charging_on: chargingOn === 1}; + return {charger_status: chargerStatus, charging_on: chargingOn === true ? "ON" : "OFF"}; }, } satisfies Fz.Converter<"haApplianceControl", undefined, ["commandSignalStateNotification", "commandSignalStateRsp"]>, ], From 17d9520a0fb34c8f1671910e08028254b1e3d3a3 Mon Sep 17 00:00:00 2001 From: Svhelge Date: Mon, 1 Jun 2026 18:31:12 +0200 Subject: [PATCH 22/45] clean code --- src/devices/futurehome.ts | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index aee62cac95d39..2ce98de00e303 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -57,32 +57,26 @@ const futurehomeExtend = { chargerStatus = "1X: Off"; chargingOn = false; break; - case 0x02: // StandBy → charging chargerStatus = "2: plugged_in_charging"; chargingOn = true; break; - case 0x03: // Programmed (paused by user) chargerStatus = "3: plugged_in_paused"; chargingOn = false; break; - case 0x04: // ProgrammedWaitingToStart chargerStatus = "4: plugged_in"; chargingOn = false; break; - case 0x05: // Running chargerStatus = "5X: running"; chargingOn = false; break; - case 0x08: // Failure chargerStatus = "8X: failure"; chargingOn = false; break; - default: chargerStatus = "plugged_out"; chargingOn = false; @@ -246,6 +240,7 @@ const futurehomeExtend = { option: e .numeric("charger_status_poll_interval", ea.SET) .withValueMin(-1) + .withUnit("s") .withDescription("Polling interval charger status (default: 60s, -1 to disable)"), defaultIntervalSeconds: 60, poll: async (device) => { @@ -271,10 +266,9 @@ const futurehomeExtend = { toZigbee: [ { // key: ["charging"], - key: ["charging_start", "charging_stop", "charging_paused"], + key: ["charging_start", "charging_stop", "charging_pause"], convertSet: async (entity, key, value, meta) => { - const action = key.replace("charging_", ""); // "start", "stop", or "paused" - const normalizedAction = action === "paused" ? "pause" : action; + const normalizedAction = key.replace("charging_", ""); // "start", "stop", or "pause" const commandId = commandLookup[normalizedAction]; // const lookup: KeyValueAny = {Start: "0x01", Stop: "0x02", Pause: "0x03"}; // await entity.command("haApplianceControl", "executionOfCommand", {commandId: lookup[value as keyof typeof lookup]}); @@ -295,13 +289,7 @@ const futurehomeExtend = { exposes: [ exposes.enum("charging_start", ea.SET, ["start"]).withLabel("Start charging").withDescription("Press to start charging"), exposes.enum("charging_stop", ea.SET, ["stop"]).withLabel("Stop charging").withDescription("Press to stop charging"), - exposes.enum("charging_paused", ea.SET, ["pause"]).withLabel("Pause charging").withDescription("Press to pause charging"), - // exposes - // .composite("charging_commands", "charging_commands", ea.SET) - // .withDescription("Execute charging control actions") - // .withFeature(exposes.enum("start", ea.SET, ["press"]).withDescription("Start charging session")) - // .withFeature(exposes.enum("stop", ea.SET, ["press"]).withDescription("Stop charging session")) - // .withFeature(exposes.enum("paused", ea.SET, ["press"]).withDescription("Pause charging session")), + exposes.enum("charging_pause", ea.SET, ["pause"]).withLabel("Pause charging").withDescription("Press to pause charging"), ], }; }, @@ -397,8 +385,6 @@ export const definitions: DefinitionWithExtend[] = [ vendor: "Futurehome", description: "Futurehome Charge (EV Charger)", extend: [ - // m.lock({"pinCodeCount":50}), - // m.electricityMeter(), m.deviceAddCustomCluster("haApplianceControl", { name: "haApplianceControl", ID: Zcl.Clusters.haApplianceControl.ID, @@ -449,9 +435,9 @@ export const definitions: DefinitionWithExtend[] = [ valueMin: 6, valueMax: 32, valueStep: 1, - // reporting: {min: "10_SECONDS", max: "1_HOUR", change: 1}, + reporting: {min: "10_SECONDS", max: "1_HOUR", change: 1}, + zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), - m.numeric({ name: "charging_current_limit", cluster: "genAnalogOutput", @@ -465,7 +451,6 @@ export const definitions: DefinitionWithExtend[] = [ entityCategory: "config", zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), - // genAnalogOutput': {"presentValue":8,"maxPresentValue":32,"outOfService":0,"statusFlags":0} m.binary<"haApplianceControl", FuturehomeHaApplianceControl>({ name: "auto_charge", cluster: "haApplianceControl", From 57b06b11cd7867e527bb20a28788321e6182ef1e Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Thu, 18 Jun 2026 19:05:37 +0200 Subject: [PATCH 23/45] test haApplianceControl attr --- src/devices/futurehome.ts | 49 ++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 2ce98de00e303..02abaefb96ecd 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -396,6 +396,41 @@ export const definitions: DefinitionWithExtend[] = [ manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, write: true, }, + a0: { + name: "a0", + ID: 0xef00, + type: Zcl.DataType.UINT8, + manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, + write: true, + }, + a1: { + name: "a1", + ID: 0xef01, + type: Zcl.DataType.UINT8, + manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, + write: true, + }, + a2: { + name: "a2", + ID: 0xef02, + type: Zcl.DataType.UINT8, + manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, + write: true, + }, + aa: { + name: "aa", + ID: 0xef0a, + type: Zcl.DataType.UINT8, + manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, + write: true, + }, + ab: { + name: "ab", + ID: 0xef0b, + type: Zcl.DataType.UINT8, + manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, + write: true, + }, }, commands: {}, commandsResponse: {}, @@ -411,20 +446,6 @@ export const definitions: DefinitionWithExtend[] = [ description: "Permanently lock cable when not charging.", zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), - // 'closuresDoorLock': - // {"lockState":2,"supportedOperatingModes":261,"actuatorEnabled":176} - // 'under lading {"actuatorEnabled":177,"supportedOperatingModes":261,"operatingMode":0,"lockType":null,"lockState":1} - - // genAnalogOutput': - // Under lading {"maxPresentValue":32,"outOfService":0,"presentValue":7,"statusFlags":0} - - // 'genMultistateValue': - // {"numberOfStates":8,"outOfService":0,"presentValue":5} - // under lading '{"numberOfStates":8,"outOfService":0,"presentValue":5,"reliability":0,"statusFlags":0} - - // 'genMultistateInput': - // {"numberOfStates":6,"outOfService":0,"presentValue":65} - // under lading {"numberOfStates":6,"outOfService":0,"presentValue":67,"statusFlags":0} m.numeric({ name: "setpoint_charging_current", cluster: "genAnalogOutput", From 74933bd6267534ff4afe89fbe80cd44293a1e77e Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Thu, 18 Jun 2026 19:27:30 +0200 Subject: [PATCH 24/45] more haApplianceControl attr --- src/devices/futurehome.ts | 42 ++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 02abaefb96ecd..fcd8352f4239c 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -396,13 +396,13 @@ export const definitions: DefinitionWithExtend[] = [ manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, write: true, }, - a0: { - name: "a0", - ID: 0xef00, - type: Zcl.DataType.UINT8, - manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, - write: true, - }, + // a0: { + // name: "a0", + // ID: 0xef00, + // type: Zcl.DataType.UINT8, + // manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, + // write: true, + // }, a1: { name: "a1", ID: 0xef01, @@ -417,6 +417,34 @@ export const definitions: DefinitionWithExtend[] = [ manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, write: true, }, + a3: { + name: "a3", + ID: 0xef03, + type: Zcl.DataType.UINT8, + manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, + write: true, + }, + a4: { + name: "a4", + ID: 0xef04, + type: Zcl.DataType.UINT8, + manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, + write: true, + }, + a5: { + name: "a5", + ID: 0xef05, + type: Zcl.DataType.UINT8, + manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, + write: true, + }, + a6: { + name: "a6", + ID: 0xef06, + type: Zcl.DataType.UINT8, + manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, + write: true, + }, aa: { name: "aa", ID: 0xef0a, From 7c8626e0e99f1bb215f5ed3c5acab37ca41f560c Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Thu, 18 Jun 2026 19:58:41 +0200 Subject: [PATCH 25/45] more attr --- src/devices/futurehome.ts | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index fcd8352f4239c..4e515a4a4a402 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -445,6 +445,27 @@ export const definitions: DefinitionWithExtend[] = [ manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, write: true, }, + a7: { + name: "a7", + ID: 0xef07, + type: Zcl.DataType.UINT8, + manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, + write: true, + }, + a8: { + name: "a8", + ID: 0xef08, + type: Zcl.DataType.UINT8, + manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, + write: true, + }, + a9: { + name: "a9", + ID: 0xef09, + type: Zcl.DataType.UINT8, + manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, + write: true, + }, aa: { name: "aa", ID: 0xef0a, @@ -459,6 +480,27 @@ export const definitions: DefinitionWithExtend[] = [ manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, write: true, }, + ac: { + name: "ac", + ID: 0xef0c, + type: Zcl.DataType.UINT8, + manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, + write: true, + }, + ad: { + name: "ad", + ID: 0xef0d, + type: Zcl.DataType.UINT8, + manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, + write: true, + }, + ae: { + name: "ae", + ID: 0xef0e, + type: Zcl.DataType.UINT8, + manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, + write: true, + }, }, commands: {}, commandsResponse: {}, From 5e5044ec6ab64a08c199bc7cb1960ef50f65fa97 Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Thu, 18 Jun 2026 22:23:49 +0200 Subject: [PATCH 26/45] more attr --- src/devices/futurehome.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 4e515a4a4a402..24ef2104c8791 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -501,6 +501,27 @@ export const definitions: DefinitionWithExtend[] = [ manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, write: true, }, + af: { + name: "af", + ID: 0xef0f, + type: Zcl.DataType.UINT8, + manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, + write: true, + }, + a10: { + name: "a10", + ID: 0xef10, + type: Zcl.DataType.UINT8, + manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, + write: true, + }, + a11: { + name: "a11", + ID: 0xef11, + type: Zcl.DataType.UINT8, + manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, + write: true, + }, }, commands: {}, commandsResponse: {}, From 73c5c16f77b13ef88670434df1878d3841d9fdbb Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Thu, 18 Jun 2026 22:30:29 +0200 Subject: [PATCH 27/45] more attr --- src/devices/futurehome.ts | 51 +++++++-------------------------------- 1 file changed, 9 insertions(+), 42 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 24ef2104c8791..187e2cd659008 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -389,20 +389,7 @@ export const definitions: DefinitionWithExtend[] = [ name: "haApplianceControl", ID: Zcl.Clusters.haApplianceControl.ID, attributes: { - autoCharge: { - name: "autoCharge", - ID: 0xef0c, - type: Zcl.DataType.UINT8, - manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, - write: true, - }, - // a0: { - // name: "a0", // ID: 0xef00, - // type: Zcl.DataType.UINT8, - // manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, - // write: true, - // }, a1: { name: "a1", ID: 0xef01, @@ -417,19 +404,17 @@ export const definitions: DefinitionWithExtend[] = [ manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, write: true, }, - a3: { - name: "a3", + previousAccumulatedEnergy: { + name: "previousAccumulatedEnergy", ID: 0xef03, type: Zcl.DataType.UINT8, manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, - write: true, }, - a4: { - name: "a4", + latestAccumulatedEnergy: { + name: "latestAccumulatedEnergy", ID: 0xef04, type: Zcl.DataType.UINT8, manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, - write: true, }, a5: { name: "a5", @@ -480,34 +465,16 @@ export const definitions: DefinitionWithExtend[] = [ manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, write: true, }, - ac: { - name: "ac", + autoCharge: { + name: "autoCharge", ID: 0xef0c, type: Zcl.DataType.UINT8, manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, write: true, }, - ad: { - name: "ad", - ID: 0xef0d, - type: Zcl.DataType.UINT8, - manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, - write: true, - }, - ae: { - name: "ae", - ID: 0xef0e, - type: Zcl.DataType.UINT8, - manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, - write: true, - }, - af: { - name: "af", - ID: 0xef0f, - type: Zcl.DataType.UINT8, - manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, - write: true, - }, + // ID: 0xef0d, + // ID: 0xef0e, + // ID: 0xef0f, a10: { name: "a10", ID: 0xef10, From b2cd3c18c4f1a89f1cc3e046f867cd24d1966f11 Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Fri, 19 Jun 2026 01:17:09 +0200 Subject: [PATCH 28/45] ++ --- src/devices/futurehome.ts | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 187e2cd659008..7d47f6b4ce8cf 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -393,27 +393,27 @@ export const definitions: DefinitionWithExtend[] = [ a1: { name: "a1", ID: 0xef01, - type: Zcl.DataType.UINT8, + type: Zcl.DataType.UINT32, manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, write: true, }, a2: { name: "a2", ID: 0xef02, - type: Zcl.DataType.UINT8, + type: Zcl.DataType.UINT32, manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, write: true, }, previousAccumulatedEnergy: { name: "previousAccumulatedEnergy", ID: 0xef03, - type: Zcl.DataType.UINT8, + type: Zcl.DataType.UINT32, manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, }, latestAccumulatedEnergy: { name: "latestAccumulatedEnergy", ID: 0xef04, - type: Zcl.DataType.UINT8, + type: Zcl.DataType.UINT32, manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, }, a5: { @@ -482,13 +482,7 @@ export const definitions: DefinitionWithExtend[] = [ manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, write: true, }, - a11: { - name: "a11", - ID: 0xef11, - type: Zcl.DataType.UINT8, - manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, - write: true, - }, + // ID: 0xef11, }, commands: {}, commandsResponse: {}, From ffe1e5005a6ddf4fdef94024fb4a8671bba26503 Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Fri, 19 Jun 2026 16:22:22 +0200 Subject: [PATCH 29/45] session energy --- src/devices/futurehome.ts | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 7d47f6b4ce8cf..b647d26899192 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -265,13 +265,10 @@ const futurehomeExtend = { fromZigbee: [], toZigbee: [ { - // key: ["charging"], key: ["charging_start", "charging_stop", "charging_pause"], convertSet: async (entity, key, value, meta) => { const normalizedAction = key.replace("charging_", ""); // "start", "stop", or "pause" const commandId = commandLookup[normalizedAction]; - // const lookup: KeyValueAny = {Start: "0x01", Stop: "0x02", Pause: "0x03"}; - // await entity.command("haApplianceControl", "executionOfCommand", {commandId: lookup[value as keyof typeof lookup]}); await entity.command("haApplianceControl", "executionOfCommand", {commandId: commandId}); try { await entity.command("haApplianceControl", "signalState", {}); @@ -285,7 +282,6 @@ const futurehomeExtend = { }, } satisfies Tz.Converter, ], - // exposes: [exposes.enum("charging", ea.STATE_SET, ["Start", "Pause", "Stop"]).withDescription("Start or pause charging.")], exposes: [ exposes.enum("charging_start", ea.SET, ["start"]).withLabel("Start charging").withDescription("Press to start charging"), exposes.enum("charging_stop", ea.SET, ["stop"]).withLabel("Stop charging").withDescription("Press to stop charging"), @@ -293,6 +289,25 @@ const futurehomeExtend = { ], }; }, + previousSessionEnergy: (): ModernExtend => { + return { + isModernExtend: true, + fromZigbee: [ + { + cluster: "haApplianceControl", + type: ["attributeReport", "readResponse"], + convert: (model, msg, publish, options, meta) => { + const prev_session_energy = (msg.data.latestAccumulatedEnergy - msg.data.previousAccumulatedEnergy) / 1000; + return { + previous_session_energy: prev_session_energy, + }; + }, + }, + ], + toZigbee: [], + exposes: [exposes.numeric("previous_session_energy", ea.STATE)], + }; + }, }; export const definitions: DefinitionWithExtend[] = [ @@ -533,6 +548,7 @@ export const definitions: DefinitionWithExtend[] = [ valueOn: ["ON", 1], zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), + futurehomeExtend.previousSessionEnergy(), m.numeric<"haElectricalMeasurement", undefined>({ name: "power", cluster: "haElectricalMeasurement", From b4bc81afcb1535a9862eb47019d2ff56313f7d5f Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Wed, 24 Jun 2026 22:03:01 +0200 Subject: [PATCH 30/45] add attrributes to charger --- src/devices/futurehome.ts | 134 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 128 insertions(+), 6 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index b647d26899192..37601e5775ed6 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -10,6 +10,18 @@ const ea = exposes.access; interface FuturehomeHaApplianceControl { attributes: { autoCharge: number; + energyMeterStart: number; + energyMeterNow: number; + a1: number; + a2: number; + a5: number; + a6: number; + a7: number; + a8: number; + a9: number; + aa: number; + ab: number; + a10: number; }; commands: never; commandResponses: never; @@ -297,7 +309,7 @@ const futurehomeExtend = { cluster: "haApplianceControl", type: ["attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { - const prev_session_energy = (msg.data.latestAccumulatedEnergy - msg.data.previousAccumulatedEnergy) / 1000; + const prev_session_energy = (msg.data.energyMeterNow - msg.data.energyMeterStart) / 1000; return { previous_session_energy: prev_session_energy, }; @@ -305,7 +317,9 @@ const futurehomeExtend = { }, ], toZigbee: [], - exposes: [exposes.numeric("previous_session_energy", ea.STATE)], + exposes: [ + exposes.numeric("previous_session_energy", ea.STATE).withLabel("Session energy").withDescription("Previous session").withUnit("kWh"), + ], }; }, }; @@ -419,14 +433,14 @@ export const definitions: DefinitionWithExtend[] = [ manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, write: true, }, - previousAccumulatedEnergy: { - name: "previousAccumulatedEnergy", + energyMeterStart: { + name: "energyMeterStart", ID: 0xef03, type: Zcl.DataType.UINT32, manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, }, - latestAccumulatedEnergy: { - name: "latestAccumulatedEnergy", + energyMeterNow: { + name: "energyMeterNow", ID: 0xef04, type: Zcl.DataType.UINT32, manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, @@ -563,6 +577,114 @@ export const definitions: DefinitionWithExtend[] = [ power: false, threePhase: true, }), + m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ + name: "energy_meter_start", + cluster: "haApplianceControl", + attribute: "energyMeterStart", + description: "energyMeterStart", + access: "STATE_GET", + reporting: {min: 5, max: "1_HOUR", change: 1}, + zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, + }), + m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ + name: "energy_meter_now", + cluster: "haApplianceControl", + attribute: "energyMeterNow", + description: "energyMeterNow", + access: "STATE_GET", + reporting: {min: 5, max: "1_HOUR", change: 1}, + zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, + }), + m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ + name: "a1", + cluster: "haApplianceControl", + attribute: "a1", + description: "a1", + access: "STATE_GET", + reporting: {min: 5, max: "1_HOUR", change: 1}, + zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, + }), + m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ + name: "a2", + cluster: "haApplianceControl", + attribute: "a2", + description: "a2", + access: "STATE_GET", + reporting: {min: 5, max: "1_HOUR", change: 1}, + zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, + }), + m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ + name: "a5", + cluster: "haApplianceControl", + attribute: "a5", + description: "a5", + access: "STATE_GET", + reporting: {min: 5, max: "1_HOUR", change: 1}, + zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, + }), + m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ + name: "a6", + cluster: "haApplianceControl", + attribute: "a6", + description: "a6", + access: "STATE_GET", + reporting: {min: 5, max: "1_HOUR", change: 1}, + zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, + }), + m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ + name: "a7", + cluster: "haApplianceControl", + attribute: "a7", + description: "a7", + access: "STATE_GET", + reporting: {min: 5, max: "1_HOUR", change: 1}, + zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, + }), + m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ + name: "a8", + cluster: "haApplianceControl", + attribute: "a8", + description: "a8", + access: "STATE_GET", + reporting: {min: 5, max: "1_HOUR", change: 1}, + zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, + }), + m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ + name: "a9", + cluster: "haApplianceControl", + attribute: "a9", + description: "a9", + access: "STATE_GET", + reporting: {min: 5, max: "1_HOUR", change: 1}, + zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, + }), + m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ + name: "aa", + cluster: "haApplianceControl", + attribute: "aa", + description: "aa", + access: "STATE_GET", + reporting: {min: 5, max: "1_HOUR", change: 1}, + zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, + }), + m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ + name: "ab", + cluster: "haApplianceControl", + attribute: "ab", + description: "ab", + access: "STATE_GET", + reporting: {min: 5, max: "1_HOUR", change: 1}, + zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, + }), + m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ + name: "a10", + cluster: "haApplianceControl", + attribute: "a10", + description: "a10", + access: "STATE_GET", + reporting: {min: 5, max: "1_HOUR", change: 1}, + zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, + }), ], }, ]; From 96e9ee8f46c9c0a9199d862eee4426a592f249fe Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Wed, 24 Jun 2026 22:07:02 +0200 Subject: [PATCH 31/45] Update schema version in biome.json --- biome.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/biome.json b/biome.json index f1b3c5b2b9b5e..45d130e1fc919 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.4.14/schema.json", + "$schema": "https://biomejs.dev/schemas/2.5.0/schema.json", "vcs": { "enabled": true, "clientKind": "git", From 3af326136aeddf880457f0936339884b84059a3d Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Wed, 24 Jun 2026 23:45:54 +0200 Subject: [PATCH 32/45] update calculations of session energy --- src/devices/futurehome.ts | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 37601e5775ed6..5f730584e8986 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -316,7 +316,17 @@ const futurehomeExtend = { }, }, ], - toZigbee: [], + toZigbee: [ + { + key: ["prev_session_energy"], + convertGet: async (entity, key, meta) => { + await entity.read<"haApplianceControl", FuturehomeHaApplianceControl>("haApplianceControl", [ + "energyMeterNow", + "energyMeterStart", + ]); + }, + } satisfies Tz.Converter, + ], exposes: [ exposes.numeric("previous_session_energy", ea.STATE).withLabel("Session energy").withDescription("Previous session").withUnit("kWh"), ], @@ -583,6 +593,7 @@ export const definitions: DefinitionWithExtend[] = [ attribute: "energyMeterStart", description: "energyMeterStart", access: "STATE_GET", + scale: 1000, reporting: {min: 5, max: "1_HOUR", change: 1}, zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), @@ -592,6 +603,7 @@ export const definitions: DefinitionWithExtend[] = [ attribute: "energyMeterNow", description: "energyMeterNow", access: "STATE_GET", + scale: 1000, reporting: {min: 5, max: "1_HOUR", change: 1}, zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), @@ -619,7 +631,7 @@ export const definitions: DefinitionWithExtend[] = [ attribute: "a5", description: "a5", access: "STATE_GET", - reporting: {min: 5, max: "1_HOUR", change: 1}, + // reporting: {min: 5, max: "1_HOUR", change: 1}, not reportable zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ @@ -628,7 +640,7 @@ export const definitions: DefinitionWithExtend[] = [ attribute: "a6", description: "a6", access: "STATE_GET", - reporting: {min: 5, max: "1_HOUR", change: 1}, + // reporting: {min: 5, max: "1_HOUR", change: 1}, not reportable zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ @@ -637,7 +649,7 @@ export const definitions: DefinitionWithExtend[] = [ attribute: "a7", description: "a7", access: "STATE_GET", - reporting: {min: 5, max: "1_HOUR", change: 1}, + // reporting: {min: 5, max: "1_HOUR", change: 1}, not reportable zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ @@ -646,7 +658,7 @@ export const definitions: DefinitionWithExtend[] = [ attribute: "a8", description: "a8", access: "STATE_GET", - reporting: {min: 5, max: "1_HOUR", change: 1}, + // reporting: {min: 5, max: "1_HOUR", change: 1}, not reportable zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ @@ -655,7 +667,7 @@ export const definitions: DefinitionWithExtend[] = [ attribute: "a9", description: "a9", access: "STATE_GET", - reporting: {min: 5, max: "1_HOUR", change: 1}, + reporting: {min: 5, max: "1_HOUR", change: 1}, // reportable zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ @@ -673,7 +685,7 @@ export const definitions: DefinitionWithExtend[] = [ attribute: "ab", description: "ab", access: "STATE_GET", - reporting: {min: 5, max: "1_HOUR", change: 1}, + // reporting: {min: 5, max: "1_HOUR", change: 1}, not reportable zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ @@ -682,7 +694,7 @@ export const definitions: DefinitionWithExtend[] = [ attribute: "a10", description: "a10", access: "STATE_GET", - reporting: {min: 5, max: "1_HOUR", change: 1}, + // reporting: {min: 5, max: "1_HOUR", change: 1}, not reportable zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), ], From da24f89c0fa97e86e136a19bb939952bbc678a57 Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Thu, 25 Jun 2026 00:17:42 +0200 Subject: [PATCH 33/45] update.. --- src/devices/futurehome.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 5f730584e8986..03375c1bf4468 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -309,12 +309,15 @@ const futurehomeExtend = { cluster: "haApplianceControl", type: ["attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { - const prev_session_energy = (msg.data.energyMeterNow - msg.data.energyMeterStart) / 1000; - return { - previous_session_energy: prev_session_energy, - }; + // const prev_session_energy = (msg.data.energyMeterNow - msg.data.energyMeterStart) / 1000; + if (msg.data.energyMeterNow !== undefined && msg.data.energyMeterStart !== undefined) { + const prev_session_energy = msg.data.energyMeterNow - msg.data.energyMeterStart; + return { + previous_session_energy: prev_session_energy, + }; + } }, - }, + } satisfies Fz.Converter<"haApplianceControl", FuturehomeHaApplianceControl, ["attributeReport", "readResponse"]>, ], toZigbee: [ { @@ -592,6 +595,7 @@ export const definitions: DefinitionWithExtend[] = [ cluster: "haApplianceControl", attribute: "energyMeterStart", description: "energyMeterStart", + unit: "kWh", access: "STATE_GET", scale: 1000, reporting: {min: 5, max: "1_HOUR", change: 1}, @@ -602,6 +606,7 @@ export const definitions: DefinitionWithExtend[] = [ cluster: "haApplianceControl", attribute: "energyMeterNow", description: "energyMeterNow", + unit: "kWh", access: "STATE_GET", scale: 1000, reporting: {min: 5, max: "1_HOUR", change: 1}, From cdaf8343424cb841b69deee643903fe172ce519d Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Thu, 25 Jun 2026 00:24:17 +0200 Subject: [PATCH 34/45] expose --- src/devices/futurehome.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 03375c1bf4468..74171ee32420b 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -331,7 +331,11 @@ const futurehomeExtend = { } satisfies Tz.Converter, ], exposes: [ - exposes.numeric("previous_session_energy", ea.STATE).withLabel("Session energy").withDescription("Previous session").withUnit("kWh"), + exposes + .numeric("previous_session_energy", ea.STATE_GET) + .withLabel("Session energy") + .withDescription("Previous session") + .withUnit("kWh"), ], }; }, From 496f54d55f48e4bc7eeeb6936f07eed74063a327 Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Fri, 26 Jun 2026 02:07:57 +0200 Subject: [PATCH 35/45] sensor fix --- src/devices/futurehome.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 74171ee32420b..efd00b0be672f 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -622,6 +622,8 @@ export const definitions: DefinitionWithExtend[] = [ attribute: "a1", description: "a1", access: "STATE_GET", + scale: 1.0, + unit: "s", reporting: {min: 5, max: "1_HOUR", change: 1}, zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), @@ -631,6 +633,8 @@ export const definitions: DefinitionWithExtend[] = [ attribute: "a2", description: "a2", access: "STATE_GET", + scale: 1.0, + unit: "s", reporting: {min: 5, max: "1_HOUR", change: 1}, zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), From 1d26cdae3cc8f6e5131cbaca7a3973fd437b5305 Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Fri, 26 Jun 2026 13:12:01 +0200 Subject: [PATCH 36/45] update description for unknown attributes not reportable --- src/devices/futurehome.ts | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index efd00b0be672f..86e752cd65286 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -642,9 +642,8 @@ export const definitions: DefinitionWithExtend[] = [ name: "a5", cluster: "haApplianceControl", attribute: "a5", - description: "a5", + description: "a5 - not reportable", access: "STATE_GET", - // reporting: {min: 5, max: "1_HOUR", change: 1}, not reportable zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ @@ -653,34 +652,30 @@ export const definitions: DefinitionWithExtend[] = [ attribute: "a6", description: "a6", access: "STATE_GET", - // reporting: {min: 5, max: "1_HOUR", change: 1}, not reportable zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ name: "a7", cluster: "haApplianceControl", attribute: "a7", - description: "a7", + description: "a7 - not reportable", access: "STATE_GET", - // reporting: {min: 5, max: "1_HOUR", change: 1}, not reportable zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ name: "a8", cluster: "haApplianceControl", attribute: "a8", - description: "a8", + description: "a8 - not reportable", access: "STATE_GET", - // reporting: {min: 5, max: "1_HOUR", change: 1}, not reportable zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ name: "a9", cluster: "haApplianceControl", attribute: "a9", - description: "a9", + description: "a9 - not reportable", access: "STATE_GET", - reporting: {min: 5, max: "1_HOUR", change: 1}, // reportable zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ @@ -696,18 +691,16 @@ export const definitions: DefinitionWithExtend[] = [ name: "ab", cluster: "haApplianceControl", attribute: "ab", - description: "ab", + description: "ab - not reportable", access: "STATE_GET", - // reporting: {min: 5, max: "1_HOUR", change: 1}, not reportable zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ name: "a10", cluster: "haApplianceControl", attribute: "a10", - description: "a10", + description: "a10 - not reportable", access: "STATE_GET", - // reporting: {min: 5, max: "1_HOUR", change: 1}, not reportable zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), ], From bc4d654fd88eacb3262cdf1f04cd0485fc4744f4 Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Fri, 3 Jul 2026 11:50:36 +0200 Subject: [PATCH 37/45] update stop status --- src/devices/futurehome.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 86e752cd65286..6be175331e7f8 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -172,7 +172,7 @@ const futurehomeExtend = { | "4: plugged_in" | "2: plugged_in_charging" | "3: plugged_in_paused" - | "5X: running" + | "5: stopped" | "8X: failure" = "plugged_out"; let chargingOn = false; @@ -194,7 +194,7 @@ const futurehomeExtend = { chargingOn = false; break; case 0x05: // Running - chargerStatus = "5X: running"; + chargerStatus = "5: stopped"; chargingOn = false; break; case 0x08: // Failure @@ -239,7 +239,7 @@ const futurehomeExtend = { "2: plugged_in_charging", "3: plugged_in_paused", "4: plugged_in", - "5X: running", + "5: stopped", "8X: failure", ]) .withDescription("Current EV charger state"), @@ -524,7 +524,7 @@ export const definitions: DefinitionWithExtend[] = [ a10: { name: "a10", ID: 0xef10, - type: Zcl.DataType.UINT8, + type: Zcl.DataType.UINT16, // tested with write manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, write: true, }, From 9c4595d71edcd51cae434ed7828980e2da93eed2 Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Fri, 10 Jul 2026 13:49:28 +0200 Subject: [PATCH 38/45] add attribute for status --- src/devices/futurehome.ts | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 6be175331e7f8..4a8fcc647e7bd 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -18,7 +18,7 @@ interface FuturehomeHaApplianceControl { a6: number; a7: number; a8: number; - a9: number; + status: number; aa: number; ab: number; a10: number; @@ -490,8 +490,8 @@ export const definitions: DefinitionWithExtend[] = [ manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, write: true, }, - a9: { - name: "a9", + status: { + name: "status", ID: 0xef09, type: Zcl.DataType.UINT8, manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, @@ -616,6 +616,21 @@ export const definitions: DefinitionWithExtend[] = [ reporting: {min: 5, max: "1_HOUR", change: 1}, zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), + m.enumLookup<"haApplianceControl", FuturehomeHaApplianceControl>({ + name: "status", + cluster: "haApplianceControl", + attribute: "status", + description: "Status - not reportable", + lookup: { + plugged_out: 0x00, + plugged_in_charging: 0x02, + plugged_in_paused: 0x03, + plugged_in: 0x04, + stopped: 0x05, + }, + access: "STATE_GET", + zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, + }), m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ name: "a1", cluster: "haApplianceControl", @@ -670,14 +685,6 @@ export const definitions: DefinitionWithExtend[] = [ access: "STATE_GET", zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), - m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ - name: "a9", - cluster: "haApplianceControl", - attribute: "a9", - description: "a9 - not reportable", - access: "STATE_GET", - zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, - }), m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ name: "aa", cluster: "haApplianceControl", From d0f8c1388158b425de304fb0bc4db508babe8308 Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Fri, 10 Jul 2026 13:49:28 +0200 Subject: [PATCH 39/45] add attribute for status --- src/devices/futurehome.ts | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 6be175331e7f8..7a372ce74dd9f 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -18,7 +18,7 @@ interface FuturehomeHaApplianceControl { a6: number; a7: number; a8: number; - a9: number; + status: number; aa: number; ab: number; a10: number; @@ -490,8 +490,8 @@ export const definitions: DefinitionWithExtend[] = [ manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, write: true, }, - a9: { - name: "a9", + status: { + name: "status", ID: 0xef09, type: Zcl.DataType.UINT8, manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, @@ -616,6 +616,21 @@ export const definitions: DefinitionWithExtend[] = [ reporting: {min: 5, max: "1_HOUR", change: 1}, zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), + m.enumLookup<"haApplianceControl", FuturehomeHaApplianceControl>({ + name: "status", + cluster: "haApplianceControl", + attribute: "status", + description: "Status - not reportable", + lookup: { + plugged_out: 0x00, + plugged_in_charging: 0x02, + plugged_in_paused: 0x03, + plugged_in: 0x04, + stopped: 0x05, + }, + access: "STATE_GET", + zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, + }), m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ name: "a1", cluster: "haApplianceControl", @@ -670,14 +685,6 @@ export const definitions: DefinitionWithExtend[] = [ access: "STATE_GET", zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), - m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ - name: "a9", - cluster: "haApplianceControl", - attribute: "a9", - description: "a9 - not reportable", - access: "STATE_GET", - zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, - }), m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ name: "aa", cluster: "haApplianceControl", From dc82f23fba4d0c9e46d3a4350d34e570e36bcf5f Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Fri, 10 Jul 2026 11:50:29 +0000 Subject: [PATCH 40/45] [autofix.ci] apply automated fixes --- src/devices/futurehome.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 4a8fcc647e7bd..7a372ce74dd9f 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -622,8 +622,8 @@ export const definitions: DefinitionWithExtend[] = [ attribute: "status", description: "Status - not reportable", lookup: { - plugged_out: 0x00, - plugged_in_charging: 0x02, + plugged_out: 0x00, + plugged_in_charging: 0x02, plugged_in_paused: 0x03, plugged_in: 0x04, stopped: 0x05, From 0bf2b2badaa2766ea1883717e8dc45d6b9124193 Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Fri, 10 Jul 2026 14:26:08 +0200 Subject: [PATCH 41/45] add start and end time --- src/devices/futurehome.ts | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 7a372ce74dd9f..6a4ccb73009d8 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -12,8 +12,8 @@ interface FuturehomeHaApplianceControl { autoCharge: number; energyMeterStart: number; energyMeterNow: number; - a1: number; - a2: number; + chargingSessionStartT: number; + chargingSessionEndT: number; a5: number; a6: number; a7: number; @@ -436,19 +436,17 @@ export const definitions: DefinitionWithExtend[] = [ ID: Zcl.Clusters.haApplianceControl.ID, attributes: { // ID: 0xef00, - a1: { - name: "a1", + chargingSessionStartT: { + name: "chargingSessionStartT", ID: 0xef01, type: Zcl.DataType.UINT32, manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, - write: true, }, - a2: { - name: "a2", + chargingSessionEndT: { + name: "chargingSessionEndT", ID: 0xef02, type: Zcl.DataType.UINT32, manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, - write: true, }, energyMeterStart: { name: "energyMeterStart", @@ -632,10 +630,10 @@ export const definitions: DefinitionWithExtend[] = [ zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ - name: "a1", + name: "start_t", cluster: "haApplianceControl", - attribute: "a1", - description: "a1", + attribute: "chargingSessionStartT", + description: "Start time of charging session. Time in seconds. Should be converted to date and time.", access: "STATE_GET", scale: 1.0, unit: "s", @@ -643,10 +641,10 @@ export const definitions: DefinitionWithExtend[] = [ zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ - name: "a2", + name: "end_t", cluster: "haApplianceControl", - attribute: "a2", - description: "a2", + attribute: "chargingSessionEndT", + description: "End time of charging session. Time in seconds. Should be converted to date and time.", access: "STATE_GET", scale: 1.0, unit: "s", From 8e12c5cd10bcb7b30043451c90ba8b2758791ff1 Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Fri, 10 Jul 2026 14:26:08 +0200 Subject: [PATCH 42/45] add start and end time --- src/devices/futurehome.ts | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 7a372ce74dd9f..0889062e93917 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -12,8 +12,8 @@ interface FuturehomeHaApplianceControl { autoCharge: number; energyMeterStart: number; energyMeterNow: number; - a1: number; - a2: number; + chargingSessionStartT: number; + chargingSessionEndT: number; a5: number; a6: number; a7: number; @@ -436,19 +436,17 @@ export const definitions: DefinitionWithExtend[] = [ ID: Zcl.Clusters.haApplianceControl.ID, attributes: { // ID: 0xef00, - a1: { - name: "a1", + chargingSessionStartT: { + name: "chargingSessionStartT", ID: 0xef01, type: Zcl.DataType.UINT32, manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, - write: true, }, - a2: { - name: "a2", + chargingSessionEndT: { + name: "chargingSessionEndT", ID: 0xef02, type: Zcl.DataType.UINT32, manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS, - write: true, }, energyMeterStart: { name: "energyMeterStart", @@ -620,7 +618,7 @@ export const definitions: DefinitionWithExtend[] = [ name: "status", cluster: "haApplianceControl", attribute: "status", - description: "Status - not reportable", + description: "Status", lookup: { plugged_out: 0x00, plugged_in_charging: 0x02, @@ -632,10 +630,10 @@ export const definitions: DefinitionWithExtend[] = [ zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ - name: "a1", + name: "start_t", cluster: "haApplianceControl", - attribute: "a1", - description: "a1", + attribute: "chargingSessionStartT", + description: "Start time of charging session. Time in seconds. Should be converted to date and time.", access: "STATE_GET", scale: 1.0, unit: "s", @@ -643,10 +641,10 @@ export const definitions: DefinitionWithExtend[] = [ zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, }), m.numeric<"haApplianceControl", FuturehomeHaApplianceControl>({ - name: "a2", + name: "end_t", cluster: "haApplianceControl", - attribute: "a2", - description: "a2", + attribute: "chargingSessionEndT", + description: "End time of charging session. Time in seconds. Should be converted to date and time.", access: "STATE_GET", scale: 1.0, unit: "s", From c076b17e08864d34e014e85c939e406129fcf6df Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Fri, 10 Jul 2026 18:24:20 +0200 Subject: [PATCH 43/45] update session energy --- src/devices/futurehome.ts | 41 +++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index 0889062e93917..c332ebe712d95 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -310,33 +310,32 @@ const futurehomeExtend = { type: ["attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { // const prev_session_energy = (msg.data.energyMeterNow - msg.data.energyMeterStart) / 1000; - if (msg.data.energyMeterNow !== undefined && msg.data.energyMeterStart !== undefined) { - const prev_session_energy = msg.data.energyMeterNow - msg.data.energyMeterStart; + // if (msg.data.energyMeterNow !== undefined && msg.data.energyMeterStart !== undefined) { + // const prev_session_energy = msg.data.energyMeterNow - msg.data.energyMeterStart; + // return { + // previous_session_energy: prev_session_energy, + // }; + // } + let start = meta.state?.energy_meter_start !== undefined ? meta.state.energy_meter_start : null; + let now = meta.state?.energy_meter_now !== undefined ? meta.state.energy_meter_now : null; + if (Object.hasOwn(msg.data, "energyMeterStart")) { + start = msg.data.energyMeterStart / 1000; + } + if (Object.hasOwn(msg.data, "energyMeterNow")) { + now = msg.data.energyMeterNow / 1000; + } + if (start !== null && now !== null) { + const consumed = (now as number) - (start as number); return { - previous_session_energy: prev_session_energy, + // toFixed(3) prevents floating-point precision issues in JavaScript (e.g., 0.300000000004) + session_energy: Number.parseFloat(consumed.toFixed(3)), }; } + return; }, } satisfies Fz.Converter<"haApplianceControl", FuturehomeHaApplianceControl, ["attributeReport", "readResponse"]>, ], - toZigbee: [ - { - key: ["prev_session_energy"], - convertGet: async (entity, key, meta) => { - await entity.read<"haApplianceControl", FuturehomeHaApplianceControl>("haApplianceControl", [ - "energyMeterNow", - "energyMeterStart", - ]); - }, - } satisfies Tz.Converter, - ], - exposes: [ - exposes - .numeric("previous_session_energy", ea.STATE_GET) - .withLabel("Session energy") - .withDescription("Previous session") - .withUnit("kWh"), - ], + exposes: [exposes.numeric("session_energy", ea.STATE).withLabel("Session energy").withDescription("Previous session").withUnit("kWh")], }; }, }; From 2d36a2463b25fc9d6030c0b7479ed679325c342a Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Mon, 13 Jul 2026 14:57:50 +0200 Subject: [PATCH 44/45] add charging duration --- src/devices/futurehome.ts | 172 ++++++++------------------------------ 1 file changed, 35 insertions(+), 137 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index c332ebe712d95..4d91be9ab8d53 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -2,7 +2,7 @@ import {Zcl} from "zigbee-herdsman"; import * as exposes from "../lib/exposes"; import * as m from "../lib/modernExtend"; import * as tuya from "../lib/tuya"; -import type {DefinitionWithExtend, Fz, ModernExtend, Tz} from "../lib/types"; +import type {DefinitionWithExtend, Fz, KeyValue, ModernExtend, Tz} from "../lib/types"; const e = exposes.presets; const ea = exposes.access; @@ -44,118 +44,6 @@ const localValueConverters = { }; const futurehomeExtend = { - chargerStatus1: (): ModernExtend => { - return { - isModernExtend: true, - fromZigbee: [ - { - cluster: "haApplianceControl", - type: ["commandSignalStateNotification", "commandSignalStateRsp"], - convert(model, msg, publish, options, meta) { - const status = msg?.data?.applianceStatus; - if (status === undefined || status === null) return; - let chargerStatus: - | "plugged_out" - | "1X: Off" - | "4: plugged_in" - | "2: plugged_in_charging" - | "3: plugged_in_paused" - | "5X: running" - | "8X: failure" = "plugged_out"; - let chargingOn = false; - - switch (status) { - case 0x01: // Off - chargerStatus = "1X: Off"; - chargingOn = false; - break; - case 0x02: // StandBy → charging - chargerStatus = "2: plugged_in_charging"; - chargingOn = true; - break; - case 0x03: // Programmed (paused by user) - chargerStatus = "3: plugged_in_paused"; - chargingOn = false; - break; - case 0x04: // ProgrammedWaitingToStart - chargerStatus = "4: plugged_in"; - chargingOn = false; - break; - case 0x05: // Running - chargerStatus = "5X: running"; - chargingOn = false; - break; - case 0x08: // Failure - chargerStatus = "8X: failure"; - chargingOn = false; - break; - default: - chargerStatus = "plugged_out"; - chargingOn = false; - } - return {charger_status: chargerStatus, charging_on: chargingOn}; - }, - } satisfies Fz.Converter<"haApplianceControl", undefined, ["commandSignalStateNotification", "commandSignalStateRsp"]>, - ], - toZigbee: [ - { - key: ["charger_status"], - convertGet: async (entity, key, meta) => { - await entity.command("haApplianceControl", "signalState", {}); - }, - } satisfies Tz.Converter, - ], - configure: [ - async (device, coordinatorEndpoint, logger) => { - for (const endpoint of device.endpoints) { - if (endpoint.supportsInputCluster("haApplianceControl")) { - await endpoint.bind("haApplianceControl", coordinatorEndpoint); - try { - await endpoint.command("haApplianceControl", "signalState", {}); - } catch { - // do nothing - } - } - } - }, - ], - options: [ - e - .numeric("charger_status_poll_interval", ea.SET) - .withValueMin(-1) - .withDescription("Polling interval charger status (default: 60s, -1 to disable)"), - ], - onEvent: m.poll({ - key: "charger_status_poll", - optionKey: "charger_status_poll_interval", - option: e - .numeric("charger_status_poll_interval", ea.SET) - .withValueMin(-1) - .withDescription("Polling interval charger status (default: 60s, -1 to disable)"), - defaultIntervalSeconds: 60, - poll: async (device) => { - const endpoint = device.endpoints.find((e) => e.supportsInputCluster("haApplianceControl")); - if (endpoint) { - await endpoint.command("haApplianceControl", "signalState", {}); - } - }, - }).onEvent, - exposes: [ - exposes - .enum("charger_status", ea.STATE_GET, [ - "plugged_out", - "1X: Off", - "2: plugged_in_charging", - "3: plugged_in_paused", - "4: plugged_in", - "5X: running", - "8X: failure", - ]) - .withDescription("Current EV charger state"), - exposes.binary("charging_on", ea.STATE, "true", "false").withDescription("Indicates if the charger is actively delivering power"), - ], - }; - }, chargerStatus: (): ModernExtend => { const extend: ModernExtend = { isModernExtend: true, @@ -168,7 +56,7 @@ const futurehomeExtend = { if (status === undefined || status === null) return; let chargerStatus: | "plugged_out" - | "1X: Off" + | "1: Off" | "4: plugged_in" | "2: plugged_in_charging" | "3: plugged_in_paused" @@ -178,7 +66,7 @@ const futurehomeExtend = { switch (status) { case 0x01: // Off - chargerStatus = "1X: Off"; + chargerStatus = "1: Off"; chargingOn = false; break; case 0x02: // StandBy → charging @@ -235,7 +123,7 @@ const futurehomeExtend = { exposes .enum("charger_status", ea.STATE_GET, [ "plugged_out", - "1X: Off", + "1: Off", "2: plugged_in_charging", "3: plugged_in_paused", "4: plugged_in", @@ -309,33 +197,42 @@ const futurehomeExtend = { cluster: "haApplianceControl", type: ["attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { - // const prev_session_energy = (msg.data.energyMeterNow - msg.data.energyMeterStart) / 1000; - // if (msg.data.energyMeterNow !== undefined && msg.data.energyMeterStart !== undefined) { - // const prev_session_energy = msg.data.energyMeterNow - msg.data.energyMeterStart; - // return { - // previous_session_energy: prev_session_energy, - // }; - // } - let start = meta.state?.energy_meter_start !== undefined ? meta.state.energy_meter_start : null; - let now = meta.state?.energy_meter_now !== undefined ? meta.state.energy_meter_now : null; - if (Object.hasOwn(msg.data, "energyMeterStart")) { - start = msg.data.energyMeterStart / 1000; + const result: KeyValue = {}; + let energyStart = meta.state?.energy_meter_start !== undefined ? meta.state.energy_meter_start : null; + let energyNow = meta.state?.energy_meter_now !== undefined ? meta.state.energy_meter_now : null; + let startT = meta.state?.start_t !== undefined ? meta.state.start_t : null; + let endT = meta.state?.end_t !== undefined ? meta.state.now_t : null; + if (msg.data.energyMeterStart !== undefined) { + energyStart = msg.data.energyMeterStart / 1000; } - if (Object.hasOwn(msg.data, "energyMeterNow")) { - now = msg.data.energyMeterNow / 1000; + if (msg.data.energyMeterNow !== undefined) { + energyNow = msg.data.energyMeterNow / 1000; } - if (start !== null && now !== null) { - const consumed = (now as number) - (start as number); - return { - // toFixed(3) prevents floating-point precision issues in JavaScript (e.g., 0.300000000004) - session_energy: Number.parseFloat(consumed.toFixed(3)), - }; + if (msg.data.chargingSessionStartT !== undefined) { + startT = msg.data.chargingSessionStartT; } - return; + if (msg.data.chargingSessionEndT !== undefined) { + endT = msg.data.chargingSessionEndT; + } + if (energyStart !== null && energyNow !== null) { + // const consumed = (energy_now as number) - (energy_start as number); + result.session_energy = ((energyNow as number) - (energyStart as number)).toFixed(3); + // return { + // // toFixed(3) prevents floating-point precision issues in JavaScript (e.g., 0.300000000004) + // session_energy: consumed.toFixed(3), + // }; + } + if (startT !== null && startT !== null) { + result.charging_duration = (endT as number) - (startT as number); + } + return result; }, } satisfies Fz.Converter<"haApplianceControl", FuturehomeHaApplianceControl, ["attributeReport", "readResponse"]>, ], - exposes: [exposes.numeric("session_energy", ea.STATE).withLabel("Session energy").withDescription("Previous session").withUnit("kWh")], + exposes: [ + exposes.numeric("session_energy", ea.STATE).withLabel("Session energy").withDescription("Previous session").withUnit("kWh"), + exposes.numeric("charging_duration", ea.STATE).withDescription("Charging duration last session").withUnit("s"), + ], }; }, }; @@ -620,6 +517,7 @@ export const definitions: DefinitionWithExtend[] = [ description: "Status", lookup: { plugged_out: 0x00, + off: 0x01, plugged_in_charging: 0x02, plugged_in_paused: 0x03, plugged_in: 0x04, From 352d7db8b268f8a3cc7b25d2eb3d34461cf85c81 Mon Sep 17 00:00:00 2001 From: svhelge <79386304+svhelge@users.noreply.github.com> Date: Mon, 13 Jul 2026 14:57:50 +0200 Subject: [PATCH 45/45] add charging duration --- src/devices/futurehome.ts | 175 ++++++++------------------------------ 1 file changed, 34 insertions(+), 141 deletions(-) diff --git a/src/devices/futurehome.ts b/src/devices/futurehome.ts index c332ebe712d95..10c1ce35fccc4 100644 --- a/src/devices/futurehome.ts +++ b/src/devices/futurehome.ts @@ -2,7 +2,7 @@ import {Zcl} from "zigbee-herdsman"; import * as exposes from "../lib/exposes"; import * as m from "../lib/modernExtend"; import * as tuya from "../lib/tuya"; -import type {DefinitionWithExtend, Fz, ModernExtend, Tz} from "../lib/types"; +import type {DefinitionWithExtend, Fz, KeyValue, ModernExtend, Tz} from "../lib/types"; const e = exposes.presets; const ea = exposes.access; @@ -44,118 +44,6 @@ const localValueConverters = { }; const futurehomeExtend = { - chargerStatus1: (): ModernExtend => { - return { - isModernExtend: true, - fromZigbee: [ - { - cluster: "haApplianceControl", - type: ["commandSignalStateNotification", "commandSignalStateRsp"], - convert(model, msg, publish, options, meta) { - const status = msg?.data?.applianceStatus; - if (status === undefined || status === null) return; - let chargerStatus: - | "plugged_out" - | "1X: Off" - | "4: plugged_in" - | "2: plugged_in_charging" - | "3: plugged_in_paused" - | "5X: running" - | "8X: failure" = "plugged_out"; - let chargingOn = false; - - switch (status) { - case 0x01: // Off - chargerStatus = "1X: Off"; - chargingOn = false; - break; - case 0x02: // StandBy → charging - chargerStatus = "2: plugged_in_charging"; - chargingOn = true; - break; - case 0x03: // Programmed (paused by user) - chargerStatus = "3: plugged_in_paused"; - chargingOn = false; - break; - case 0x04: // ProgrammedWaitingToStart - chargerStatus = "4: plugged_in"; - chargingOn = false; - break; - case 0x05: // Running - chargerStatus = "5X: running"; - chargingOn = false; - break; - case 0x08: // Failure - chargerStatus = "8X: failure"; - chargingOn = false; - break; - default: - chargerStatus = "plugged_out"; - chargingOn = false; - } - return {charger_status: chargerStatus, charging_on: chargingOn}; - }, - } satisfies Fz.Converter<"haApplianceControl", undefined, ["commandSignalStateNotification", "commandSignalStateRsp"]>, - ], - toZigbee: [ - { - key: ["charger_status"], - convertGet: async (entity, key, meta) => { - await entity.command("haApplianceControl", "signalState", {}); - }, - } satisfies Tz.Converter, - ], - configure: [ - async (device, coordinatorEndpoint, logger) => { - for (const endpoint of device.endpoints) { - if (endpoint.supportsInputCluster("haApplianceControl")) { - await endpoint.bind("haApplianceControl", coordinatorEndpoint); - try { - await endpoint.command("haApplianceControl", "signalState", {}); - } catch { - // do nothing - } - } - } - }, - ], - options: [ - e - .numeric("charger_status_poll_interval", ea.SET) - .withValueMin(-1) - .withDescription("Polling interval charger status (default: 60s, -1 to disable)"), - ], - onEvent: m.poll({ - key: "charger_status_poll", - optionKey: "charger_status_poll_interval", - option: e - .numeric("charger_status_poll_interval", ea.SET) - .withValueMin(-1) - .withDescription("Polling interval charger status (default: 60s, -1 to disable)"), - defaultIntervalSeconds: 60, - poll: async (device) => { - const endpoint = device.endpoints.find((e) => e.supportsInputCluster("haApplianceControl")); - if (endpoint) { - await endpoint.command("haApplianceControl", "signalState", {}); - } - }, - }).onEvent, - exposes: [ - exposes - .enum("charger_status", ea.STATE_GET, [ - "plugged_out", - "1X: Off", - "2: plugged_in_charging", - "3: plugged_in_paused", - "4: plugged_in", - "5X: running", - "8X: failure", - ]) - .withDescription("Current EV charger state"), - exposes.binary("charging_on", ea.STATE, "true", "false").withDescription("Indicates if the charger is actively delivering power"), - ], - }; - }, chargerStatus: (): ModernExtend => { const extend: ModernExtend = { isModernExtend: true, @@ -168,7 +56,7 @@ const futurehomeExtend = { if (status === undefined || status === null) return; let chargerStatus: | "plugged_out" - | "1X: Off" + | "1: Off" | "4: plugged_in" | "2: plugged_in_charging" | "3: plugged_in_paused" @@ -178,7 +66,7 @@ const futurehomeExtend = { switch (status) { case 0x01: // Off - chargerStatus = "1X: Off"; + chargerStatus = "1: Off"; chargingOn = false; break; case 0x02: // StandBy → charging @@ -235,7 +123,7 @@ const futurehomeExtend = { exposes .enum("charger_status", ea.STATE_GET, [ "plugged_out", - "1X: Off", + "1: Off", "2: plugged_in_charging", "3: plugged_in_paused", "4: plugged_in", @@ -309,33 +197,37 @@ const futurehomeExtend = { cluster: "haApplianceControl", type: ["attributeReport", "readResponse"], convert: (model, msg, publish, options, meta) => { - // const prev_session_energy = (msg.data.energyMeterNow - msg.data.energyMeterStart) / 1000; - // if (msg.data.energyMeterNow !== undefined && msg.data.energyMeterStart !== undefined) { - // const prev_session_energy = msg.data.energyMeterNow - msg.data.energyMeterStart; - // return { - // previous_session_energy: prev_session_energy, - // }; - // } - let start = meta.state?.energy_meter_start !== undefined ? meta.state.energy_meter_start : null; - let now = meta.state?.energy_meter_now !== undefined ? meta.state.energy_meter_now : null; - if (Object.hasOwn(msg.data, "energyMeterStart")) { - start = msg.data.energyMeterStart / 1000; + const result: KeyValue = {}; + let energyStart = meta.state?.energy_meter_start !== undefined ? meta.state.energy_meter_start : null; + let energyNow = meta.state?.energy_meter_now !== undefined ? meta.state.energy_meter_now : null; + let startT = meta.state?.start_t !== undefined ? meta.state.start_t : null; + let endT = meta.state?.end_t !== undefined ? meta.state.now_t : null; + if (msg.data.energyMeterStart !== undefined) { + energyStart = msg.data.energyMeterStart / 1000; + } + if (msg.data.energyMeterNow !== undefined) { + energyNow = msg.data.energyMeterNow / 1000; } - if (Object.hasOwn(msg.data, "energyMeterNow")) { - now = msg.data.energyMeterNow / 1000; + if (msg.data.chargingSessionStartT !== undefined) { + startT = msg.data.chargingSessionStartT; } - if (start !== null && now !== null) { - const consumed = (now as number) - (start as number); - return { - // toFixed(3) prevents floating-point precision issues in JavaScript (e.g., 0.300000000004) - session_energy: Number.parseFloat(consumed.toFixed(3)), - }; + if (msg.data.chargingSessionEndT !== undefined) { + endT = msg.data.chargingSessionEndT; } - return; + if (energyStart !== null && energyNow !== null) { + result.session_energy = ((energyNow as number) - (energyStart as number)).toFixed(3); + } + if (startT !== null && endT !== null) { + result.charging_duration = (endT as number) - (startT as number); + } + return result; }, } satisfies Fz.Converter<"haApplianceControl", FuturehomeHaApplianceControl, ["attributeReport", "readResponse"]>, ], - exposes: [exposes.numeric("session_energy", ea.STATE).withLabel("Session energy").withDescription("Previous session").withUnit("kWh")], + exposes: [ + exposes.numeric("session_energy", ea.STATE).withLabel("Session energy").withDescription("Previous session").withUnit("kWh"), + exposes.numeric("charging_duration", ea.STATE).withDescription("Charging duration last session").withUnit("s"), + ], }; }, }; @@ -597,7 +489,7 @@ export const definitions: DefinitionWithExtend[] = [ attribute: "energyMeterStart", description: "energyMeterStart", unit: "kWh", - access: "STATE_GET", + access: "STATE", scale: 1000, reporting: {min: 5, max: "1_HOUR", change: 1}, zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, @@ -608,7 +500,7 @@ export const definitions: DefinitionWithExtend[] = [ attribute: "energyMeterNow", description: "energyMeterNow", unit: "kWh", - access: "STATE_GET", + access: "STATE", scale: 1000, reporting: {min: 5, max: "1_HOUR", change: 1}, zigbeeCommandOptions: {manufacturerCode: Zcl.ManufacturerCode.FUTUREHOME_AS}, @@ -620,6 +512,7 @@ export const definitions: DefinitionWithExtend[] = [ description: "Status", lookup: { plugged_out: 0x00, + off: 0x01, plugged_in_charging: 0x02, plugged_in_paused: 0x03, plugged_in: 0x04, @@ -633,7 +526,7 @@ export const definitions: DefinitionWithExtend[] = [ cluster: "haApplianceControl", attribute: "chargingSessionStartT", description: "Start time of charging session. Time in seconds. Should be converted to date and time.", - access: "STATE_GET", + access: "STATE", scale: 1.0, unit: "s", reporting: {min: 5, max: "1_HOUR", change: 1}, @@ -644,7 +537,7 @@ export const definitions: DefinitionWithExtend[] = [ cluster: "haApplianceControl", attribute: "chargingSessionEndT", description: "End time of charging session. Time in seconds. Should be converted to date and time.", - access: "STATE_GET", + access: "STATE", scale: 1.0, unit: "s", reporting: {min: 5, max: "1_HOUR", change: 1},