@@ -2,7 +2,7 @@ import {Zcl} from "zigbee-herdsman";
22import * as exposes from "../lib/exposes" ;
33import * as m from "../lib/modernExtend" ;
44import * as tuya from "../lib/tuya" ;
5- import type { DefinitionWithExtend , Fz , ModernExtend , Tz } from "../lib/types" ;
5+ import type { DefinitionWithExtend , Fz , KeyValue , ModernExtend , Tz } from "../lib/types" ;
66
77const e = exposes . presets ;
88const ea = exposes . access ;
@@ -44,118 +44,6 @@ const localValueConverters = {
4444} ;
4545
4646const futurehomeExtend = {
47- chargerStatus1 : ( ) : ModernExtend => {
48- return {
49- isModernExtend : true ,
50- fromZigbee : [
51- {
52- cluster : "haApplianceControl" ,
53- type : [ "commandSignalStateNotification" , "commandSignalStateRsp" ] ,
54- convert ( model , msg , publish , options , meta ) {
55- const status = msg ?. data ?. applianceStatus ;
56- if ( status === undefined || status === null ) return ;
57- let chargerStatus :
58- | "plugged_out"
59- | "1X: Off"
60- | "4: plugged_in"
61- | "2: plugged_in_charging"
62- | "3: plugged_in_paused"
63- | "5X: running"
64- | "8X: failure" = "plugged_out" ;
65- let chargingOn = false ;
66-
67- switch ( status ) {
68- case 0x01 : // Off
69- chargerStatus = "1X: Off" ;
70- chargingOn = false ;
71- break ;
72- case 0x02 : // StandBy → charging
73- chargerStatus = "2: plugged_in_charging" ;
74- chargingOn = true ;
75- break ;
76- case 0x03 : // Programmed (paused by user)
77- chargerStatus = "3: plugged_in_paused" ;
78- chargingOn = false ;
79- break ;
80- case 0x04 : // ProgrammedWaitingToStart
81- chargerStatus = "4: plugged_in" ;
82- chargingOn = false ;
83- break ;
84- case 0x05 : // Running
85- chargerStatus = "5X: running" ;
86- chargingOn = false ;
87- break ;
88- case 0x08 : // Failure
89- chargerStatus = "8X: failure" ;
90- chargingOn = false ;
91- break ;
92- default :
93- chargerStatus = "plugged_out" ;
94- chargingOn = false ;
95- }
96- return { charger_status : chargerStatus , charging_on : chargingOn } ;
97- } ,
98- } satisfies Fz . Converter < "haApplianceControl" , undefined , [ "commandSignalStateNotification" , "commandSignalStateRsp" ] > ,
99- ] ,
100- toZigbee : [
101- {
102- key : [ "charger_status" ] ,
103- convertGet : async ( entity , key , meta ) => {
104- await entity . command ( "haApplianceControl" , "signalState" , { } ) ;
105- } ,
106- } satisfies Tz . Converter ,
107- ] ,
108- configure : [
109- async ( device , coordinatorEndpoint , logger ) => {
110- for ( const endpoint of device . endpoints ) {
111- if ( endpoint . supportsInputCluster ( "haApplianceControl" ) ) {
112- await endpoint . bind ( "haApplianceControl" , coordinatorEndpoint ) ;
113- try {
114- await endpoint . command ( "haApplianceControl" , "signalState" , { } ) ;
115- } catch {
116- // do nothing
117- }
118- }
119- }
120- } ,
121- ] ,
122- options : [
123- e
124- . numeric ( "charger_status_poll_interval" , ea . SET )
125- . withValueMin ( - 1 )
126- . withDescription ( "Polling interval charger status (default: 60s, -1 to disable)" ) ,
127- ] ,
128- onEvent : m . poll ( {
129- key : "charger_status_poll" ,
130- optionKey : "charger_status_poll_interval" ,
131- option : e
132- . numeric ( "charger_status_poll_interval" , ea . SET )
133- . withValueMin ( - 1 )
134- . withDescription ( "Polling interval charger status (default: 60s, -1 to disable)" ) ,
135- defaultIntervalSeconds : 60 ,
136- poll : async ( device ) => {
137- const endpoint = device . endpoints . find ( ( e ) => e . supportsInputCluster ( "haApplianceControl" ) ) ;
138- if ( endpoint ) {
139- await endpoint . command ( "haApplianceControl" , "signalState" , { } ) ;
140- }
141- } ,
142- } ) . onEvent ,
143- exposes : [
144- exposes
145- . enum ( "charger_status" , ea . STATE_GET , [
146- "plugged_out" ,
147- "1X: Off" ,
148- "2: plugged_in_charging" ,
149- "3: plugged_in_paused" ,
150- "4: plugged_in" ,
151- "5X: running" ,
152- "8X: failure" ,
153- ] )
154- . withDescription ( "Current EV charger state" ) ,
155- exposes . binary ( "charging_on" , ea . STATE , "true" , "false" ) . withDescription ( "Indicates if the charger is actively delivering power" ) ,
156- ] ,
157- } ;
158- } ,
15947 chargerStatus : ( ) : ModernExtend => {
16048 const extend : ModernExtend = {
16149 isModernExtend : true ,
@@ -168,7 +56,7 @@ const futurehomeExtend = {
16856 if ( status === undefined || status === null ) return ;
16957 let chargerStatus :
17058 | "plugged_out"
171- | "1X : Off"
59+ | "1 : Off"
17260 | "4: plugged_in"
17361 | "2: plugged_in_charging"
17462 | "3: plugged_in_paused"
@@ -178,7 +66,7 @@ const futurehomeExtend = {
17866
17967 switch ( status ) {
18068 case 0x01 : // Off
181- chargerStatus = "1X : Off" ;
69+ chargerStatus = "1 : Off" ;
18270 chargingOn = false ;
18371 break ;
18472 case 0x02 : // StandBy → charging
@@ -235,7 +123,7 @@ const futurehomeExtend = {
235123 exposes
236124 . enum ( "charger_status" , ea . STATE_GET , [
237125 "plugged_out" ,
238- "1X : Off" ,
126+ "1 : Off" ,
239127 "2: plugged_in_charging" ,
240128 "3: plugged_in_paused" ,
241129 "4: plugged_in" ,
@@ -309,33 +197,42 @@ const futurehomeExtend = {
309197 cluster : "haApplianceControl" ,
310198 type : [ "attributeReport" , "readResponse" ] ,
311199 convert : ( model , msg , publish , options , meta ) => {
312- // const prev_session_energy = (msg.data.energyMeterNow - msg.data.energyMeterStart) / 1000;
313- // if (msg.data.energyMeterNow !== undefined && msg.data.energyMeterStart !== undefined) {
314- // const prev_session_energy = msg.data.energyMeterNow - msg.data.energyMeterStart;
315- // return {
316- // previous_session_energy: prev_session_energy,
317- // };
318- // }
319- let start = meta . state ?. energy_meter_start !== undefined ? meta . state . energy_meter_start : null ;
320- let now = meta . state ?. energy_meter_now !== undefined ? meta . state . energy_meter_now : null ;
321- if ( Object . hasOwn ( msg . data , "energyMeterStart" ) ) {
322- start = msg . data . energyMeterStart / 1000 ;
200+ const result : KeyValue = { } ;
201+ let energyStart = meta . state ?. energy_meter_start !== undefined ? meta . state . energy_meter_start : null ;
202+ let energyNow = meta . state ?. energy_meter_now !== undefined ? meta . state . energy_meter_now : null ;
203+ let startT = meta . state ?. start_t !== undefined ? meta . state . start_t : null ;
204+ let endT = meta . state ?. end_t !== undefined ? meta . state . now_t : null ;
205+ if ( msg . data . energyMeterStart !== undefined ) {
206+ energyStart = msg . data . energyMeterStart / 1000 ;
323207 }
324- if ( Object . hasOwn ( msg . data , " energyMeterNow" ) ) {
325- now = msg . data . energyMeterNow / 1000 ;
208+ if ( msg . data . energyMeterNow !== undefined ) {
209+ energyNow = msg . data . energyMeterNow / 1000 ;
326210 }
327- if ( start !== null && now !== null ) {
328- const consumed = ( now as number ) - ( start as number ) ;
329- return {
330- // toFixed(3) prevents floating-point precision issues in JavaScript (e.g., 0.300000000004)
331- session_energy : Number . parseFloat ( consumed . toFixed ( 3 ) ) ,
332- } ;
211+ if ( msg . data . chargingSessionStartT !== undefined ) {
212+ startT = msg . data . chargingSessionStartT ;
333213 }
334- return ;
214+ if ( msg . data . chargingSessionEndT !== undefined ) {
215+ endT = msg . data . chargingSessionEndT ;
216+ }
217+ if ( energyStart !== null && energyNow !== null ) {
218+ // const consumed = (energy_now as number) - (energy_start as number);
219+ result . session_energy = ( ( energyNow as number ) - ( energyStart as number ) ) . toFixed ( 3 ) ;
220+ // return {
221+ // // toFixed(3) prevents floating-point precision issues in JavaScript (e.g., 0.300000000004)
222+ // session_energy: consumed.toFixed(3),
223+ // };
224+ }
225+ if ( startT !== null && startT !== null ) {
226+ result . charging_duration = ( endT as number ) - ( startT as number ) ;
227+ }
228+ return result ;
335229 } ,
336230 } satisfies Fz . Converter < "haApplianceControl" , FuturehomeHaApplianceControl , [ "attributeReport" , "readResponse" ] > ,
337231 ] ,
338- exposes : [ exposes . numeric ( "session_energy" , ea . STATE ) . withLabel ( "Session energy" ) . withDescription ( "Previous session" ) . withUnit ( "kWh" ) ] ,
232+ exposes : [
233+ exposes . numeric ( "session_energy" , ea . STATE ) . withLabel ( "Session energy" ) . withDescription ( "Previous session" ) . withUnit ( "kWh" ) ,
234+ exposes . numeric ( "charging_duration" , ea . STATE ) . withDescription ( "Charging duration last session" ) . withUnit ( "s" ) ,
235+ ] ,
339236 } ;
340237 } ,
341238} ;
@@ -620,6 +517,7 @@ export const definitions: DefinitionWithExtend[] = [
620517 description : "Status" ,
621518 lookup : {
622519 plugged_out : 0x00 ,
520+ off : 0x01 ,
623521 plugged_in_charging : 0x02 ,
624522 plugged_in_paused : 0x03 ,
625523 plugged_in : 0x04 ,
0 commit comments