diff --git a/README.md b/README.md index ae9bd1f..1b2d779 100644 --- a/README.md +++ b/README.md @@ -237,6 +237,9 @@ The following endpoints (according to ...) are currently not implemented and ca * /aircon/get_day_paower_ex ## Changelog +### __WORK IN PROGRESS__ +* (@lleonis) Add support for mompow (momentary power consumption) sensor field and expose value in Watts + ### 2.2.2 (2025-05-25) * (@Matze2) Handles potential error case when using demand control data diff --git a/src/DaikinAC.ts b/src/DaikinAC.ts index 1787d5f..65340a7 100644 --- a/src/DaikinAC.ts +++ b/src/DaikinAC.ts @@ -116,6 +116,7 @@ export class DaikinAC { if (this._updateCallback) this._updateCallback(err); return; } + this.getACDemandControl((err, _info) => { this.initUpdateTimeout(); if (err && this._logger) { @@ -230,6 +231,18 @@ export class DaikinAC { if (this._logger) this._logger(JSON.stringify(daikinResponse)); this._currentACSensorInfo = daikinResponse; + // Apply high-level logic: set mompow to 0 when AC is powered off + // Only apply this if we have control info available + if ( + !err && + daikinResponse && + this._currentACControlInfo && + this._currentACControlInfo.power === false && + daikinResponse.mompow !== undefined + ) { + daikinResponse.mompow = 0; + } + if (callback) callback(err, daikinResponse); }); } diff --git a/src/models/responses/SensorInfoResponse.ts b/src/models/responses/SensorInfoResponse.ts index 0ccdfe4..07d533a 100644 --- a/src/models/responses/SensorInfoResponse.ts +++ b/src/models/responses/SensorInfoResponse.ts @@ -7,6 +7,7 @@ export class SensorInfoResponse { public outdoorTemperature?: number; public error?: number; public cmpfreq?: number; + public mompow?: number; public static parseResponse(dict: ResponseDict, cb: DaikinResponseCb): void { const result = new SensorInfoResponse(); @@ -15,6 +16,16 @@ export class SensorInfoResponse { result.outdoorTemperature = DaikinDataParser.resolveFloat(dict, 'otemp'); result.error = DaikinDataParser.resolveInteger(dict, 'err'); result.cmpfreq = DaikinDataParser.resolveInteger(dict, 'cmpfreq'); + + // Parse mompow field and convert from 0.1kW units to Watts + const rawMompow = DaikinDataParser.resolveInteger(dict, 'mompow'); + if (typeof rawMompow === 'number') { + // Convert mompow from 0.1kW units to Watts by multiplying by 100 + result.mompow = rawMompow * 100; + } else { + result.mompow = undefined; + } + cb(null, 'OK', result); } } diff --git a/test/testDaikinAC.test.ts b/test/testDaikinAC.test.ts index 24e5fdd..a7e9239 100644 --- a/test/testDaikinAC.test.ts +++ b/test/testDaikinAC.test.ts @@ -48,14 +48,16 @@ describe('Test DaikinAC', () => { 'ret=OK,pow=0,mode=3,adv=,stemp=23.0,shum=0,dt1=25.0,dt2=M,dt3=23.0,dt4=27.0,dt5=27.0,dt7=25.0,dh1=AUTO,dh2=50,dh3=0,dh4=0,dh5=0,dh7=AUTO,dhh=50,b_mode=3,b_stemp=23.0,b_shum=0,alert=255,f_rate=3,f_dir=0,b_f_rate=A,b_f_dir=0,dfr1=5,dfr2=5,dfr3=A,dfr4=5,dfr5=5,dfr6=5,dfr7=5,dfrh=5,dfd1=0,dfd2=0,dfd3=0,dfd4=0,dfd5=0,dfd6=0,dfd7=0,dfdh=0', ) .get('/aircon/get_sensor_info') - .reply(200, 'ret=OK,htemp=21.5,hhum=-,otemp=-,err=0,cmpfreq=0') + .reply(200, 'ret=OK,htemp=21.5,hhum=-,otemp=-,err=0,cmpfreq=0,mompow=150') + .get('/aircon/get_demand_control') + .replyWithError('Not supported') .get('/aircon/get_control_info') .reply( 200, 'ret=OK,pow=0,mode=3,adv=,stemp=24.0,shum=0,dt1=25.0,dt2=M,dt3=23.0,dt4=27.0,dt5=27.0,dt7=25.0,dh1=AUTO,dh2=50,dh3=0,dh4=0,dh5=0,dh7=AUTO,dhh=50,b_mode=3,b_stemp=23.0,b_shum=0,alert=255,f_rate=A,f_dir=0,b_f_rate=A,b_f_dir=0,dfr1=5,dfr2=5,dfr3=A,dfr4=5,dfr5=5,dfr6=5,dfr7=5,dfrh=5,dfd1=0,dfd2=0,dfd3=0,dfd4=0,dfd5=0,dfd6=0,dfd7=0,dfdh=0', ) .get('/aircon/get_sensor_info') - .reply(200, 'ret=OK,htemp=22.5,hhum=-,otemp=-,err=0,cmpfreq=0'); + .reply(200, 'ret=OK,htemp=22.5,hhum=-,otemp=-,err=0,cmpfreq=0,mompow=200'); const daikin = new DaikinAC('127.0.0.1', options, function (err) { expect(err).toBeNull(); expect(daikin.currentCommonBasicInfo).not.toBeNull(); @@ -75,6 +77,7 @@ describe('Test DaikinAC', () => { expect(daikin.currentACControlInfo!.targetTemperature).toEqual(23); expect(daikin.currentACControlInfo!.fanRate).toEqual(3); expect(daikin.currentACSensorInfo!.indoorTemperature).toEqual(21.5); + expect(daikin.currentACSensorInfo!.mompow).toEqual(0); } else { expect(cnt).toEqual(2); expect(daikin.currentACControlInfo).not.toBeNull(); @@ -82,13 +85,14 @@ describe('Test DaikinAC', () => { expect(daikin.currentACControlInfo!.targetTemperature).toEqual(24); expect(daikin.currentACControlInfo!.fanRate).toEqual('A'); expect(daikin.currentACSensorInfo!.indoorTemperature).toEqual(22.5); + expect(daikin.currentACSensorInfo!.mompow).toEqual(0); daikin.stopUpdate(); expect(req.isDone()).toBeTruthy(); expect(daikin.updateTimeout).toBeNull(); expect(daikin.currentACControlInfo).not.toBeNull(); expect(daikin.currentACSensorInfo).not.toBeNull(); expect(Object.keys(daikin.currentACControlInfo!).length).toEqual(42); - expect(Object.keys(daikin.currentACSensorInfo!).length).toEqual(5); + expect(Object.keys(daikin.currentACSensorInfo!).length).toEqual(6); done(); } }); @@ -191,7 +195,7 @@ describe('Test DaikinAC', () => { .get('/aircon/get_remote_method') .reply(200, 'ret=OK,method=home only,notice_ip_int=3600,notice_sync_int=60') .get('/aircon/get_sensor_info') - .reply(200, 'ret=OK,htemp=21.5,hhum=-,otemp=-,err=0,cmpfreq=0') + .reply(200, 'ret=OK,htemp=21.5,hhum=-,otemp=-,err=0,cmpfreq=0,mompow=300') .get('/aircon/get_model_info') .reply(200, 'ret=OK,model=NOTSUPPORT,type=N,pv=0,cpv=0,mid=NA,s_fdir=1,en_scdltmr=1') .get('/aircon/get_week_power') @@ -235,9 +239,10 @@ describe('Test DaikinAC', () => { daikin.getACSensorInfo(function (err, response) { expect(err).toBeNull(); - expect(Object.keys(response!).length).toEqual(5); + expect(Object.keys(response!).length).toEqual(6); expect(response!.indoorTemperature).toEqual(21.5); expect(response!.outdoorTemperature).toBeNaN(); + expect(response!.mompow).toEqual(30000); daikin.getACModelInfo(function (err, response) { expect(err).toBeNull(); diff --git a/test/testDaikinACGet.test.ts b/test/testDaikinACGet.test.ts index 197bcdf..937f24a 100644 --- a/test/testDaikinACGet.test.ts +++ b/test/testDaikinACGet.test.ts @@ -49,14 +49,16 @@ describe('Test DaikinAC', function () { 'ret=OK,pow=0,mode=3,adv=,stemp=23.0,shum=0,dt1=25.0,dt2=M,dt3=23.0,dt4=27.0,dt5=27.0,dt7=25.0,dh1=AUTO,dh2=50,dh3=0,dh4=0,dh5=0,dh7=AUTO,dhh=50,b_mode=3,b_stemp=23.0,b_shum=0,alert=255,f_rate=A,f_dir=0,b_f_rate=A,b_f_dir=0,dfr1=5,dfr2=5,dfr3=A,dfr4=5,dfr5=5,dfr6=5,dfr7=5,dfrh=5,dfd1=0,dfd2=0,dfd3=0,dfd4=0,dfd5=0,dfd6=0,dfd7=0,dfdh=0', ) .get('/aircon/get_sensor_info?lpw=') - .reply(200, 'ret=OK,htemp=21.5,hhum=-,otemp=-,err=0,cmpfreq=0') + .reply(200, 'ret=OK,htemp=21.5,hhum=-,otemp=-,err=0,cmpfreq=0,mompow=150') + .get('/aircon/get_demand_control?lpw=') + .replyWithError('Not supported') .get('/aircon/get_control_info?lpw=') .reply( 200, 'ret=OK,pow=0,mode=3,adv=,stemp=24.0,shum=0,dt1=25.0,dt2=M,dt3=23.0,dt4=27.0,dt5=27.0,dt7=25.0,dh1=AUTO,dh2=50,dh3=0,dh4=0,dh5=0,dh7=AUTO,dhh=50,b_mode=3,b_stemp=23.0,b_shum=0,alert=255,f_rate=A,f_dir=0,b_f_rate=A,b_f_dir=0,dfr1=5,dfr2=5,dfr3=A,dfr4=5,dfr5=5,dfr6=5,dfr7=5,dfrh=5,dfd1=0,dfd2=0,dfd3=0,dfd4=0,dfd5=0,dfd6=0,dfd7=0,dfdh=0', ) .get('/aircon/get_sensor_info?lpw=') - .reply(200, 'ret=OK,htemp=22.5,hhum=-,otemp=-,err=0,cmpfreq=0'); + .reply(200, 'ret=OK,htemp=22.5,hhum=-,otemp=-,err=0,cmpfreq=0,mompow=200'); const daikin = new DaikinAC('127.0.0.1', options, function (err) { expect(err).toBeNull(); expect(Object.keys(daikin.currentCommonBasicInfo!).length).toEqual(25); @@ -72,15 +74,17 @@ describe('Test DaikinAC', function () { if (cnt == 1) { expect(daikin.currentACControlInfo!.targetTemperature).toEqual(23); expect(daikin.currentACSensorInfo!.indoorTemperature).toEqual(21.5); + expect(daikin.currentACSensorInfo!.mompow).toEqual(0); } else { expect(cnt).toEqual(2); expect(daikin.currentACControlInfo!.targetTemperature).toEqual(24); expect(daikin.currentACSensorInfo!.indoorTemperature).toEqual(22.5); + expect(daikin.currentACSensorInfo!.mompow).toEqual(0); daikin.stopUpdate(); expect(req.isDone()).toBeTruthy(); expect(daikin.updateTimeout).toBeNull(); expect(Object.keys(daikin.currentACControlInfo!).length).toEqual(42); - expect(Object.keys(daikin.currentACSensorInfo!).length).toEqual(5); + expect(Object.keys(daikin.currentACSensorInfo!).length).toEqual(6); done(); } }); @@ -183,7 +187,7 @@ describe('Test DaikinAC', function () { .get('/aircon/get_remote_method?lpw=') .reply(200, 'ret=OK,method=home only,notice_ip_int=3600,notice_sync_int=60') .get('/aircon/get_sensor_info?lpw=') - .reply(200, 'ret=OK,htemp=21.5,hhum=-,otemp=-,err=0,cmpfreq=0') + .reply(200, 'ret=OK,htemp=21.5,hhum=-,otemp=-,err=0,cmpfreq=0,mompow=300') .get('/aircon/get_model_info?lpw=') .reply(200, 'ret=OK,model=NOTSUPPORT,type=N,pv=0,cpv=0,mid=NA,s_fdir=1,en_scdltmr=1') .get('/aircon/get_week_power?lpw=') @@ -227,9 +231,10 @@ describe('Test DaikinAC', function () { daikin.getACSensorInfo(function (err, response) { expect(err).toBeNull(); - expect(Object.keys(response!).length).toEqual(5); + expect(Object.keys(response!).length).toEqual(6); expect(response!.indoorTemperature).toEqual(21.5); expect(response!.outdoorTemperature).toBeNaN(); + expect(response!.mompow).toEqual(30000); daikin.getACModelInfo(function (err, response) { expect(err).toBeNull(); @@ -293,4 +298,42 @@ describe('Test DaikinAC', function () { }); }); }, 600000); + + it('power state logic test - mompow=0 when pow=0', function (done) { + const req = nock('http://127.0.0.1') + .get('/common/basic_info') + .reply( + 200, + 'ret=OK,type=aircon,reg=eu,dst=1,ver=2_6_0,pow=0,err=0,location=0,name=%4b%6c%69%6d%61%20%4a%61%6e%61,icon=0,method=home only,port=30050,id=,pw=,lpw_flag=1,adp_kind=2,pv=0,cpv=0,cpv_minor=00,led=0,en_setzone=1,mac=A408EACC91D4,adp_mode=run,en_hol=0,grp_name=%4b%69%6e%64%65%72,en_grp=1', + ) + .get('/aircon/get_model_info?lpw=') + .reply(200, 'ret=OK,model=NOTSUPPORT,type=N,pv=0,cpv=0,mid=NA,s_fdir=1,en_scdltmr=1') + .get('/aircon/get_control_info?lpw=') + .reply( + 200, + 'ret=OK,pow=0,mode=3,adv=,stemp=23.0,shum=0,dt1=25.0,dt2=M,dt3=23.0,dt4=27.0,dt5=27.0,dt7=25.0,dh1=AUTO,dh2=50,dh3=0,dh4=0,dh5=0,dh7=AUTO,dhh=50,b_mode=3,b_stemp=23.0,b_shum=0,alert=255,f_rate=A,f_dir=0,b_f_rate=A,b_f_dir=0,dfr1=5,dfr2=5,dfr3=A,dfr4=5,dfr5=5,dfr6=5,dfr7=5,dfrh=5,dfd1=0,dfd2=0,dfd3=0,dfd4=0,dfd5=0,dfd6=0,dfd7=0,dfdh=0', + ) + .get('/aircon/get_sensor_info?lpw=') + .reply(200, 'ret=OK,htemp=21.5,hhum=-,otemp=-,err=0,cmpfreq=0,mompow=1'); + const daikin = new DaikinAC('127.0.0.1', options, function (err) { + expect(err).toBeNull(); + + // First get control info to load power state + daikin.getACControlInfo(function (err, controlResponse) { + expect(err).toBeNull(); + expect(controlResponse!.power).toBeFalsy(); // Verify AC is off + + // Then get sensor info to test power state logic + daikin.getACSensorInfo(function (err, response) { + expect(err).toBeNull(); + expect(Object.keys(response!).length).toEqual(6); + expect(response!.indoorTemperature).toEqual(21.5); + expect(response!.mompow).toEqual(0); // Should be 0 due to power state logic even though device reports 1 + + expect(req.isDone()).toBeTruthy(); + done(); + }); + }); + }); + }); });