rgerganov / py-air-control

Command line app for controlling Philips air purifiers
MIT License
257 stars 52 forks source link

Unable to get CoAP to work with AC2889/60 #35

Closed ryancdotorg closed 4 years ago

ryancdotorg commented 4 years ago

Firmware (from the app) reports as

Firmware Version: AWS_Philips_AIR@54.2 Device Version: 1.0.7 Model: AC2889/10

Any attempt to send a command just gets back {"status":"failed"} (looking at it via tcpdump). Also from tcpdump, the app appears to be exclusively using the cloud based system to talk to it. If I put both on a wifi network not connected to the internet, even the app doesn't work.

rgerganov commented 4 years ago

Thanks for the info! It's really sad that users can no longer control their own devices but need to rely on cloud services. I won't recommend anyone buying such device.

How is the initial pairing between the mobile app and the device is done?

ryancdotorg commented 4 years ago

I won't recommend anyone buying such device.

Indeed, I am looking into getting a refund for it.

How is the initial pairing between the mobile app and the device is done?

I was unfortunately not able to sniff that.

ryancdotorg commented 4 years ago

I have two possible solutions to sniff the paring process, will give it a try this weekend. Have you looked at all at the firmware update code in the app? I'm wondering if a downgrade is possible, but my reversing skills are pretty weak.

rgerganov commented 4 years ago

Yeah, there is an update functionality in the app but currently I don't have the time and motivation to reverse it. I believe a more plausible approach in this direction would be hacking the physical device (see issue #3) in case you want to void your warranty.

In any case keep us posted about your findings for the pairing process!

ryancdotorg commented 4 years ago

I've got a friend who does Android reversing who is willing to take at least a brief look at the app. I can go looking for a uart if I can't return the AC2889. Will probably open up the AC3259/60 regardless to add a switch for the speaker.

ryancdotorg commented 4 years ago

This is for the /40 - do you know what the different / numbers mean? Don't see an obvious system UART, but the photos are super blurry.

https://fccid.io/2AICSAC2889/Internal-Photos/Internal-photos-3222110

ryancdotorg commented 4 years ago

@DerPaco, @michalboronski or @apfelsheriff - can you share the first six characters (excluding the colons) of the mac address for your AC2889 with us? I believe the --wifi command will show it.

dsadsa897897r commented 4 years ago

I can share mine if useful?

ryancdotorg commented 4 years ago

Yes, please, if you've got it working with this project I would really appreciate that.

dsadsa897897r commented 4 years ago

e8:c1:d7:

ryancdotorg commented 4 years ago

Hmm, that's what I was afraid of.... That MAC prefix is registered to Phillips. My unit has one starting with b0:f8:93 which is registered to Shanghai MXCHIP Information Technology Co., Ltd. I'm wondering if the hardware is different.

laurence-syree commented 4 years ago

I have an AC2889/10 also encountering the {"status":"failed"} issue. My MAC prefix is also B0:F8:93. Happy to help with any information I can provide.

michalboronski commented 4 years ago

@DerPaco, @michalboronski or @apfelsheriff - can you share the first six characters (excluding the colons) of the mac address for your AC2889 with us? I believe the --wifi command will show it.

Sure, no problem : E8:C1:D7

dvd2000k commented 4 years ago

Have an ac4550, "status:failed" here. From inet the Mac address start with b0:f8:93, Shanghai MXCHIP Information Technology Co., Ltd.

holomekc commented 4 years ago

I can read values but I cannot write values. When I try to change pwr for example I also get {"status":"failed"}

But reading works. But I wrote my own tool because the python script also did not work for me. For /dev/info:

{"product_id":"<longNumber>","device_id":"<longNumber>","name":"Wohnzimmer","type":"AC3033","modelid":"AC3033/10","swversion":"Ms3105","option":"1"}

And for /dev/status:

{"state":{"reported":{"name":"Wohnzimmer","type":"AC3033","modelid":"AC3033/10","swversion":"Ms3105","language":"EN","DeviceVersion":"1.0.5","om":"1","pwr":"1","cl":false,"aqil":0,"uil":"0","uaset":"A","mode":"AG","pm25":6,"iaql":2,"aqit":4,"tvoc":1,"ddp":"0","rddp":"0","err":0,"fltt1":"A3","fltt2":"none","fltsts0":352,"fltsts1":4800,"fltsts2":65535,"filna":"0","filid":"0","ota":"ck","Runtime":27871569,"WifiVersion":"AWS_Philips_AIR@54.2","ProductId":"<longNumber>","DeviceId":"<longNumber>","StatusType":"localcontrol","ConnectType":"Localcontrol"}}}

For writing it looks like the message "must" be of this structure (not sure though):

{"state":{"desired":{"CommandType":"app","DeviceId":"<longNumber>","EnduserId":"???","pwr":"0"}}}

I do not know what EnduserId should be...

edit:

it works!!!

{"status":"success"}

EnduserId seems to be irrelevant. But you need to sync an id when sending a message. The id is received via /dev/sync. The message send then needs to contain id + 1

Cyber1000 commented 4 years ago

@holomekc Hi I came yesterday to a similar conclusion (for me it was /sys/dev/sync not /dev/sync), but I'm unclear if there aren' different firmware versions around. My conclusion is here: https://github.com/rgerganov/py-air-control/issues/21#issuecomment-619284974 I managed to get data back, but it's no json, it's also no json what the app gets back.

Could you point me in the right direction? What device to you have? What do you mean with "The message send then needs to contain id + 1". What does the message to the device look like? Do you have a sample of your code? Would be interesting if you get back a json string directly ... Thanks!

holomekc commented 4 years ago

From your example:

encodedCounter: substring(0, 8) '23D56232'

decodedCounter: remove zeros in front if they exist. hex -> decimal = 601186866

const keyAndIv = CleanHomeCoapHelper.bufferToHexString(CleanHomeCoapHelper.toMD5('JiangPan' + encodedCounter));
const secretKey = keyAndIv.substring(0, keyAndIv.length / 2);
const iv = keyAndIv.substring(keyAndIv.length / 2, keyAndIv.length);
const encodedMessage = response.substring(8, response.length - 64);
return CleanHomeCoapHelper.fromAES(encodedMessage, secretKey, iv);

result: {
  encodedCounter: '23D56232',
  keyAndIv: '10917AAD9D5AD7FB36F62901C8072233',
  secretKey: '10917AAD9D5AD7FB',
  iv: '36F62901C8072233',
  encodedMessage: '3CC15DD4C1E80B11EF5D2E70493CF5C470671A5B2AD54578006A3B9C965D8F39D99D0152F37AA5286C1AEEF6C5D5A608FA5AB38206532E88C27C4FDA3437561F687D59CDA0BCA2F09E78B2BA5EEC6AFD99354646A66231031375CBACA80D6BCE66244EB08269A121F326D089971C6F9BE1D6649D453C98D961FA3D4767D3A8512B5E92FF7F1D285FD420FD527057C419A3B2063EB87831B7C6E843830E6DEAC187DD63CE2B8F0B756F8224C2C40799812923A4398298A493C7DD38A240BCA5AB182BC9582687F2EF1DDB0D2FD945CD4F616BA8CE66D4731148BB93B001D47B1B28C4A8C621298A8EBB41C4AFA3696F593E6C60397B8361DE2FF4A98BE99FD84874B10A70111139154E30E61E0A10E85CD6E8942A9943F076FEDC8E31008BAA5C068A96C117042EF80AE6E2D897C97660F24775DE571F09C32064C2FEB85A4EDA80AD1C5EA2CA385DA83C1883601B4D5A5EDFD5DA5FB02DBA44FFA4980142D1A157B6EBC48D3D71CA91A4433E7E5542732CCE6EAB9C27171AAEDFE360A9A07919A2BC75466EF6AE7ED8FBD1577477AE75D857B54FEB43476E6A5E41D331F1A6CBE0B700D87FF74882C0D0EBAE09D33ED17DC55DC00CE28420000BD414F513682B40B6EFF436331A708522C02F80D2408CBAE4DF4259A5E23285BD385AEDCC64B2653C71A4FBE38094EF2B1C00036DA2B0E4C39F916454C64A48707B66A587A894',
  message: '{"state":{"reported":{"name":"Luftreiniger","type":"AC2889","modelid":"AC2889/10","swversion":"1.0.7","om":"1","pwr":"1","cl":false,"aqil":0,"uil":"0","dt":0,"dtrs":0,"mode":"A","pm25":1,"iaql":1,"aqit":4,"ddp":"0","err":0,"fltt1":"A3","fltt2":"C7","fltsts0":358,"fltsts1":4798,"fltsts2":2398,"ota":"no","Runtime":8842135,"WifiVersion":"AWS_Philips_AIR@54.2","ProductId":"<longNumber>","DeviceId":"<longNumber>","StatusType":"localcontrol","ConnectType":"Localcontrol"}}}'
}
Cyber1000 commented 4 years ago

@holomekc Ah I see, you seem to have decompiled the app, the 'JiangPan' and the substrings with 8 and 64 look quite familar to me. And the CleanHomeCoapHelper seems to be something you wrote. Seems like we are having similar firmware versions. I'll have a try lateron, I think that won't be too hard with your code snippet. Thanks!

rgerganov commented 4 years ago

This is great progress, PRs are welcome!

timbuktu-t commented 4 years ago

Great work with the decryption! I'm not a python guy myself, but if it helps - I've put together a java sample here, which works with my AC3829/10.

o0v7r0o commented 4 years ago

@timbuktu-t I have just tried your java implementation with my AC2729, it works! I think a python coap library might help to make it work for >2019 models.

[main] INFO philipsair.PhilipsAir - initializing
[main] INFO philipsair.PhilipsAir - synchronizing with coap://172.27.0.163:5683/sys/dev/sync
[main] INFO org.eclipse.californium.core.network.config.NetworkConfig - loading properties from file /tmp/java/Californium.properties
[main] INFO org.eclipse.californium.core.network.RandomTokenGenerator - using tokens of 8 bytes in length
[main] INFO org.eclipse.californium.core.network.CoapEndpoint - coap CoapEndpoint uses udp plain
[main] INFO org.eclipse.californium.core.network.stack.BlockwiseLayer - BlockwiseLayer uses MAX_MESSAGE_SIZE=1024, PREFERRED_BLOCK_SIZE=512, BLOCKWISE_STATUS_LIFETIME=300000, MAX_RESOURCE_BODY_SIZE=8192, BLOCKWISE_STRICT_BLOCK2_OPTION=false
[main] INFO org.eclipse.californium.core.network.stack.ReliabilityLayer - ReliabilityLayer uses ACK_TIMEOUT=2000, ACK_RANDOM_FACTOR=1.5, and ACK_TIMEOUT_SCALE=2.0 as default
[main] INFO org.eclipse.californium.core.network.CoapEndpoint - coap Endpoint [coap://0.0.0.0:0] requires an executor to start, using default single-threaded daemon executor
[main] INFO org.eclipse.californium.elements.UDPConnector - UDPConnector starts up 1 sender threads and 1 receiver threads
[main] INFO org.eclipse.californium.elements.UDPConnector - UDPConnector listening on 0.0.0.0/0.0.0.0:50776, recv buf = 106496, send buf = 106496, recv packet size = 2048
[main] INFO org.eclipse.californium.core.network.CoapEndpoint - coap Started endpoint at coap://0.0.0.0:50776
[main] INFO org.eclipse.californium.core.network.EndpointManager - created implicit endpoint coap://0.0.0.0:50776 for coap
[main] INFO philipsair.PhilipsAir - synchronized with client id 1079002D and server id 1DDB5E50
[main] INFO philipsair.PhilipsAir - observing coap://172.27.0.163:5683/sys/dev/status
{"state":{"reported":{"name":"Room","type":"AC2729","modelid":"AC2729/10","swversion":"0.2.1","om":"0","pwr":"0","cl":false,"aqil":100,"uil":"1","dt":0,"dtrs":0,"mode":"A","func":"P","rhset":50,"rh":69,"temp":22,"pm25":13,"iaql":7,"aqit":4,"ddp":"0","rddp":"0","err":0,"wl":100,"fltt1":"A3","fltt2":"C7","fltsts0":59,"fltsts1":4126,"fltsts2":4126,"wicksts":4126,"ota":"ck","Runtime":17131730,"WifiVersion":"AWS_Philips_AIR@56.4","ProductId":"---xxx---","DeviceId":"---xxx---","StatusType":"localcontrol","ConnectType":"Localcontrol"}}}
[main] INFO philipsair.PhilipsAir - disposing
Cyber1000 commented 4 years ago

@timbuktu-t Hi thanks for your sample, seems that AC3829 and AC2889 and obviously also the AC2729 share the same program, at least the newer versions. I've seen the original android-code uses "AES/CBC/PKCS7PADDING" instead of AES/CBC/PKCS5PADDING, but in this case it doesn't seem to matter.

@o0v7r0o The coap library is already in the master of this repo, I get encrypted messages after some code changes, the tricky part seems to be the decoding, at least in python ( or at least for me ๐Ÿ˜„ )

I had some time today, the AES-step is still a problem for me, I'm assuming python has some differences here, I'm just getting garbage for now (for easy testing I've used a returned string from my AC2889) It looks similar to the java-code, but I can't convert the result to utf-8, there must be something missing. Don't know how much time I'll have during the week, hope next weekend I have a solution.

Any ideas? I've tested with this small program including an encrypted message - result should be the "message" part of this comment: https://github.com/rgerganov/py-air-control/issues/35#issuecomment-619494963

import hashlib
from Cryptodome.Cipher import AES

payload='23D562323CC15DD4C1E80B11EF5D2E70493CF5C470671A5B2AD54578006A3B9C965D8F39D99D0152F37AA5286C1AEEF6C5D5A608FA5AB38206532E88C27C4FDA3437561F687D59CDA0BCA2F09E78B2BA5EEC6AFD99354646A66231031375CBACA80D6BCE66244EB08269A121F326D089971C6F9BE1D6649D453C98D961FA3D4767D3A8512B5E92FF7F1D285FD420FD527057C419A3B2063EB87831B7C6E843830E6DEAC187DD63CE2B8F0B756F8224C2C40799812923A4398298A493C7DD38A240BCA5AB182BC9582687F2EF1DDB0D2FD945CD4F616BA8CE66D4731148BB93B001D47B1B28C4A8C621298A8EBB41C4AFA3696F593E6C60397B8361DE2FF4A98BE99FD84874B10A70111139154E30E61E0A10E85CD6E8942A9943F076FEDC8E31008BAA5C068A96C117042EF80AE6E2D897C97660F24775DE571F09C32064C2FEB85A4EDA80AD1C5EA2CA385DA83C1883601B4D5A5EDFD5DA5FB02DBA44FFA4980142D1A157B6EBC48D3D71CA91A4433E7E5542732CCE6EAB9C27171AAEDFE360A9A07919A2BC75466EF6AE7ED8FBD1577477AE75D857B54FEB43476E6A5E41D331F1A6CBE0B700D87FF74882C0D0EBAE09D33ED17DC55DC00CE28420000BD414F513682B40B6EFF436331A708522C02F80D2408CBAE4DF4259A5E23285BD385AEDCC64B2653C71A4FBE38094EF2B1C00036DA2B0E4C39F916454C64A48707B66A587A8944FF20E8EE3460A2C65482FA56EC5B6EA0A16E17AC2B4331F2B3FF8831F83F9B1'

encodedCounter=payload[0:8]
keyAndIv = hashlib.md5(('JiangPan' + encodedCounter).encode()).hexdigest();
keylen=len(keyAndIv)
print(int(len(keyAndIv) / 2))
secretKey = keyAndIv[0:int(len(keyAndIv) / 2)];
iv = keyAndIv[int(len(keyAndIv) / 2):len(keyAndIv)];
encodedMessage = payload[8:-64];
prepmessage=bytes.fromhex(encodedMessage.upper())
decodedMessage=AES.new(bytes(secretKey.encode('utf8')), AES.MODE_CBC, bytes(iv.encode('utf8'))).decrypt(prepmessage)
timbuktu-t commented 4 years ago

@o0v7r0o Glad to hear it works! I'm curious though, did you get just one reading in those ten seconds? Once I start observing, my device sends one packet every 2-3 seconds, so I usually have 4-5 readings in that time. However, sometimes I get pauses for a few seconds as well, where I can see in Wireshark that udp packets are retransmitted. Could you maybe increase the sleep time in line 214 to e.g. a minute (using "Thread.sleep(60000)") to see if more readings arrive? And did you per chance try sending a poweroff command (as decribed in line 218) to your device as well?

@Cyber1000 Thanks for the tip! I did not reverse the app, just built upon the discoveries you and @holomekc posted in the forum (and a bit of Wireshark), so PKCS5 and UTF8 are just wild guesses. I did not test using device names with special characters, so UTF8 might still be wrong and yield character encoding problems.

Regarding your snippet: The method hexdigest() returns lowercase hexdigits, You have to upper() the keyAndIv, then it works and print(decodedMessage) yields:

b'{"state":{"reported":{"name":"Luftreiniger","type":"AC2889","modelid":"AC2889/10",...}}}\x01'

I have no clue about the prefix "b'" and suffix "\x01'" though. I assume, this is how python indicates a byte array?

rgerganov commented 4 years ago

I have no clue about the prefix "b'" and suffix "\x01'" though. I assume, this is how python indicates a byte array?

Yes, b means it's a byte array. The \x01 at the end is padding, you should unpad with Crypto.Util.Padding.unpad(data, 16, style='pkcs7')

@Cyber1000 Could you please submit a PR when you get this working with your device?

o0v7r0o commented 4 years ago

@o0v7r0o Glad to hear it works! I'm curious though, did you get just one reading in those ten seconds? Once I start observing, my device sends one packet every 2-3 seconds, so I usually have 4-5 readings in that time. However, sometimes I get pauses for a few seconds as well, where I can see in Wireshark that udp packets are retransmitted. Could you maybe increase the sleep time in line 214 to e.g. a minute (using "Thread.sleep(60000)") to see if more readings arrive? And did you per chance try sending a poweroff command (as decribed in line 218) to your device as well?

I have indeed successfully tested power on/off. However, as I remember I've got only one status update during these 10 seconds.

sal0max commented 4 years ago

Awesome work, @timbuktu-t !

Working on my newly purchased AC1214/10:

{
   "state": {
      "reported": {
         "name": "XXX",
         "type": "AC1214",
         "modelid": "AC1214/10",
         "swversion": "2.0.0",
         "om": "1",
         "pwr": "1",
         "cl": false,
         "aqil": 25,
         "uil": "1",
         "dt": 0,
         "dtrs": 0,
         "mode": "P",
         "pm25": 2,
         "iaql": 1,
         "aqit": 4,
         "ddp": "0",
         "err": 0,
         "fltt1": "A3",
         "fltt2": "C7",
         "fltsts0": 23,
         "fltsts1": 4463,
         "fltsts2": 2063,
         "ota": "ck",
         "Runtime": 19976679,
         "WifiVersion": "AWS_Philips_AIR@54.2",
         "ProductId": "XXX",
         "DeviceId": "XXX",
         "StatusType": "localcontrol",
         "ConnectType": "Localcontrol"
      }
   }
}
timbuktu-t commented 4 years ago

Cool! So now we know of four models this works for. Out of curiosity, how many readings did you get during the 10 seconds window?

Credit should got to @Cyber1000 and @holomekc though, as they figured out the decryption :-)

sal0max commented 4 years ago
michalboronski commented 4 years ago

I'm starting work on Coap support for the Openhab binding. However as I have an older version without Coap I cannot phisycally test it, so I'm working on some mock simualator to make at least dry implementation. @timbuktu-t or @sal0max could you provide me DEBUG logs from the poc code of yours so that it includes whole communication log? You can add it here : https://github.com/michalboronski/openhab2-addons/issues/2

biemond commented 4 years ago

me too, but then in node and for homey instead. Already got it working for last year devices. So the security part is still the same?

Cyber1000 commented 4 years ago

Much is going on in this thread ๐Ÿ˜„

@timbuktu-t Yes the code looks similar to yours: image A little bit different in the flow, but this may also due to decompiling processes. UTF8 should be just fine.

And sometimes things can get so simple, many thanks ๐Ÿ‘ for the hint with upper, that's all I was missing, I looked so often at @holomekc result, that I didn't notice the difference between my lower case and his upper-case keys. upper was missing and in the end also this one:

response = unpad(decodedMessage, 16)
print(response.decode('utf8'))

padding/unpadding is done directly in java but not in python. Now I get a "normal" string without the \x01 at the end.

As said I'm still in my small sample, I'll need to get this back in your code and clean this up @rgerganov

Cyber1000 commented 4 years ago

So it seems that we have these versions around:

timbuktu-t commented 4 years ago

@Cyber1000 I poked around a bit regarding PKCS#7. Apparently you have to specify PKCS5Padding in Oracle JDK even when you actually want to use PKCS#7, which is a superset for longer block sizes. For details see Stack Overflow or the Oracle Bug Database.

@michalboronski I'll send you debug logs next chance I get.

shexbeer commented 4 years ago

@Cyber1000 Iam running this

[modelid]     ModelId: AC2729/10
[swversion]   Version: 0.2.1
Cyber1000 commented 4 years ago

@timbuktu-t Ok nice to know, sorry for misleading, java is not my main language, thanks for the information.

sal0max commented 4 years ago

@michalboronski what exactly do you need?

btw: Had time to check out controlling my AC1214/10. Works great: powering on/off, changing modes & manual speeds all works flawlessly.

Zun1k commented 4 years ago

Hi all, I tried to run java tool from @timbuktu-t and it work well now. I can turn on/off and other, but can't reset a scheduler...

I got this:

{"state":{"reported":{"name":"xxx","type":"AC1214","modelid":"AC1214/10","swversion":"2.0.0","om":"2","pwr":"0","cl":false,"aqil":25,"uil":"1","dt":0,"dtrs":0,"mode":"M","pm25":7,"iaql":2,"aqit":7,"ddp":"0","err":0,"fltt1":"A3","fltt2":"C7","fltsts0":169,"fltsts1":4247,"fltsts2":1847,"ota":"no","Runtime":406669706,"WifiVersion":"AWS_Philips_AIR@54.2","ProductId":"xxx","DeviceId":"xxx","StatusType":"localcontrol","ConnectType":"Localcontrol"}}}

Air purifier is configured to power on every night at 22:35 and I dont know where its configured. Also checked from mobile applications (cleanHome, air matters) Even when I set via app some schedule, it still return "dt":0,"dtrs":0

  1. Can somebody help me where its stored?
  2. How to clean it or how reset whole air purifier?
  3. Is there a possibility to set sound volume down?

Thanks

Cyber1000 commented 4 years ago

Good news: I've forked the project using a new git branch: https://github.com/Cyber1000/py-air-control/tree/coap_1_0_7

general info (Everyone is invited to test ๐Ÿงช )

Installation (maybe you need sudo):

git clone -b coap_1_0_7 https://github.com/Cyber1000/py-air-control.git && cd py-air-control
python3 setup.py install

What works for now:

airctrl --ipaddr 192.168.11.190 --version 1.0.7 -d
airctrl --ipaddr 192.168.11.190 --version 1.0.7

So you are getting information in json and list-format, writing to your device won't work for now.

technical info (for everyone who likes to develop and improve my code/spike)

laurence-syree commented 4 years ago

Can confirm that this gets data on my AC2889/10 on version 1.0.7. Thanks for all the work guys.

szogi commented 4 years ago

Nice work! I tried with AC2729/50; Device version: 0.2.1; Firmware: AWS_Philips_AIR@54.2, airctrl from @Cyber1000 's repo:

2020-05-06 01:36:40,980 - MainThread - coapthon.layers.messagelayer - DEBUG - send_request - From None, To ('172.16.0.140', 5683), None-None, EMPTY-None, [] No payload 2020-05-06 01:36:40,980 - MainThread - coapthon.client.coap - DEBUG - send_datagram - From None, To ('172.16.0.140', 5683), CON-3889, EMPTY-None, [] No payload 2020-05-06 01:36:40,981 - Thread-1 - coapthon.client.coap - DEBUG - Start receiver Thread 2020-05-06 01:36:40,982 - MainThread - coapthon.layers.messagelayer - DEBUG - send_request - From None, To ('172.16.0.140', 5683), ACK-None, GET-kdsk, [Uri-Path: sys, Uri-Path: dev, Uri-Path: status, Observe: 0, ] No payload 2020-05-06 01:36:40,982 - MainThread - coapthon.client.coap - DEBUG - send_datagram - From None, To ('172.16.0.140', 5683), ACK-3890, GET-kdsk, [Uri-Path: sys, Uri-Path: dev, Uri-Path: status, Observe: 0, ] No payload 2020-05-06 01:36:40,983 - MainThread-Retry-3889 - coapthon.client.coap - DEBUG - retransmit loop ... enter 2020-05-06 01:36:40,983 - Thread-1 - coapthon.layers.messagelayer - DEBUG - receive_empty - From ('172.16.0.140', 5683), To None, RST-3889, EMPTY-None, [] No payload 2020-05-06 01:36:40,984 - MainThread-Retry-3889 - coapthon.client.coap - DEBUG - retransmit loop ... exit 2020-05-06 01:36:40,998 - Thread-1 - coapthon.client.coap - DEBUG - receive_datagram - From ('172.16.0.140', 5683), To None, ACK-1110, CONTENT-kdsk, [Observe: 87, Content-Type: 50, Max-Age: 60, ] 391309FB4544FA01BD4D...1137 bytes 2020-05-06 01:36:40,998 - Thread-1 - coapthon.layers.messagelayer - DEBUG - receive_response - From ('172.16.0.140', 5683), To None, ACK-1110, CONTENT-kdsk, [Observe: 87, Content-Type: 50, Max-Age: 60, ] 391309FB4544FA01BD4D...1137 bytes 2020-05-06 01:36:41,099 - Thread-1 - coapthon.client.coap - DEBUG - Exiting receiver Thread due to request Traceback (most recent call last): File "./airctrl.py", line 771, in main() File "./airctrl.py", line 767, in main c.get_status(debug=args.debug) File "./airctrl.py", line 599, in get_status status = self._get() File "./airctrl.py", line 337, in _get return json.loads(response.payload)["state"]["reported"] File "/usr/lib/python3.7/json/init.py", line 348, in loads return _default_decoder.decode(s) File "/usr/lib/python3.7/json/decoder.py", line 340, in decode raise JSONDecodeError("Extra data", s, end) json.decoder.JSONDecodeError: Extra data: line 1 column 7 (char 6)

I rebooted the device, It was auto-upgraded to Firmware: AWS_Philips_AIR@56.4:

airctrl.py --ipaddr 172.16.0.140 --version 0.2.1 -d 2020-05-06 01:52:23,865 - MainThread - coapthon.layers.messagelayer - DEBUG - send_request - From None, To ('172.16.0.140', 5683), None-None, EMPTY-None, [] No payload 2020-05-06 01:52:23,865 - MainThread - coapthon.client.coap - DEBUG - send_datagram - From None, To ('172.16.0.140', 5683), CON-38532, EMPTY-None, [] No payload 2020-05-06 01:52:23,866 - Thread-1 - coapthon.client.coap - DEBUG - Start receiver Thread 2020-05-06 01:52:23,867 - MainThread - coapthon.layers.messagelayer - DEBUG - send_request - From None, To ('172.16.0.140', 5683), ACK-None, GET-AdDP, [Uri-Path: sys, Uri-Path: dev, Uri-Path: status, Observe: 0, ] No payload 2020-05-06 01:52:23,867 - MainThread-Retry-38532 - coapthon.client.coap - DEBUG - retransmit loop ... enter 2020-05-06 01:52:23,867 - MainThread - coapthon.client.coap - DEBUG - send_datagram - From None, To ('172.16.0.140', 5683), ACK-38533, GET-AdDP, [Uri-Path: sys, Uri-Path: dev, Uri-Path: status, Observe: 0, ] No payload 2020-05-06 01:52:24,688 - Thread-1 - coapthon.layers.messagelayer - DEBUG - receive_empty - From ('172.16.0.140', 5683), To None, RST-38532, EMPTY-None, [] No payload 2020-05-06 01:52:24,689 - MainThread-Retry-38532 - coapthon.client.coap - DEBUG - retransmit loop ... exit 2020-05-06 01:52:25,891 - Thread-1 - coapthon.client.coap - DEBUG - Exiting receiver Thread due to request

Raw status: {}


airctrl.py --ipaddr 172.16.0.140 --version 1.0.7 --debug 2020-05-06 02:02:55,149 - MainThread - coapthon.layers.messagelayer - DEBUG - send_request - From None, To ('172.16.0.140', 5683), None-None, POST-dC, [Uri-Path: sys, Uri-Path: dev, Uri-Path: sync, ] 410C4BEF...8 bytes 2020-05-06 02:02:55,150 - MainThread - coapthon.client.coap - DEBUG - send_datagram - From None, To ('172.16.0.140', 5683), CON-23894, POST-dC, [Uri-Path: sys, Uri-Path: dev, Uri-Path: sync, ] 410C4BEF...8 bytes 2020-05-06 02:02:55,151 - Thread-1 - coapthon.client.coap - DEBUG - Start receiver Thread 2020-05-06 02:02:55,152 - MainThread-Retry-23894 - coapthon.client.coap - DEBUG - retransmit loop ... enter 2020-05-06 02:02:55,155 - Thread-1 - coapthon.client.coap - DEBUG - receive_datagram - From ('172.16.0.140', 5683), To None, NON-23894, CONTENT-dC, [Content-Type: 0, ] 5668C3D4...8 bytes 2020-05-06 02:02:55,155 - Thread-1 - coapthon.layers.messagelayer - DEBUG - receive_response - From ('172.16.0.140', 5683), To None, NON-23894, CONTENT-dC, [Content-Type: 0, ] 5668C3D4...8 bytes 2020-05-06 02:02:55,156 - Thread-1 - coapthon.client.coap - DEBUG - Waiting for retransmit thread to finish ... 2020-05-06 02:02:55,156 - MainThread-Retry-23894 - coapthon.client.coap - DEBUG - retransmit loop ... exit 2020-05-06 02:02:55,167 - MainThread - coapthon.layers.messagelayer - DEBUG - send_request - From None, To ('172.16.0.140', 5683), ACK-None, GET-rhCN, [Uri-Path: sys, Uri-Path: dev, Uri-Path: status, Observe: 0, ] No payload 2020-05-06 02:02:55,167 - MainThread - coapthon.client.coap - DEBUG - send_datagram - From None, To ('172.16.0.140', 5683), ACK-23895, GET-rhCN, [Uri-Path: sys, Uri-Path: dev, Uri-Path: status, Observe: 0, ] No payload 2020-05-06 02:02:55,184 - Thread-1 - coapthon.client.coap - DEBUG - receive_datagram - From ('172.16.0.140', 5683), To None, ACK-112, CONTENT-rhCN, [Observe: 65, Content-Type: 50, Max-Age: 60, ] 410C4BF0DC53FFB0BC9F...1137 bytes 2020-05-06 02:02:55,185 - Thread-1 - coapthon.layers.messagelayer - DEBUG - receive_response - From ('172.16.0.140', 5683), To None, ACK-112, CONTENT-rhCN, [Observe: 65, Content-Type: 50, Max-Age: 60, ] 410C4BF0DC53FFB0BC9F...1137 bytes 2020-05-06 02:02:55,286 - Thread-1 - coapthon.client.coap - DEBUG - Exiting receiver Thread due to request Traceback (most recent call last): File "./airctrl.py", line 771, in main() File "./airctrl.py", line 767, in main c.get_status(debug=args.debug) File "./airctrl.py", line 636, in get_status status = self._get() File "./airctrl.py", line 677, in _get decrypted_payload = self._decrypt_payload(encrypted_payload) File "./airctrl.py", line 663, in _decrypt_payload decoded_message = AES.new(bytes(secret_key.encode('utf8')), AES.MODE_CBC, bytes(iv.encode('utf8'))).decrypt(bytes.fromhex(encoded_message)) ValueError: non-hexadecimal number found in fromhex() arg at position 1065


I tried @timbuktu-t 's java version, seems to work:

[main] INFO philipsair.PhilipsAir - initializing [main] INFO philipsair.PhilipsAir - synchronizing with coap://172.16.0.140:5683/sys/dev/sync [main] INFO org.eclipse.californium.core.network.config.NetworkConfig - writing properties to file C:\Users\szogi\Downloads\poc-philipsair-master\Californium.properties [main] INFO org.eclipse.californium.core.network.RandomTokenGenerator - using tokens of 8 bytes in length [main] INFO org.eclipse.californium.core.network.CoapEndpoint - coap CoapEndpoint uses udp plain [main] INFO org.eclipse.californium.core.network.stack.BlockwiseLayer - BlockwiseLayer uses MAX_MESSAGE_SIZE=1024, PREFERRED_BLOCK_SIZE=512, BLOCKWISE_STATUS_LIFETIME=300000, MAX_RESOURCE_BODY_SIZE=8192, BLOCKWISE_STRICT_BLOCK2_OPTION=false [main] INFO org.eclipse.californium.core.network.stack.ReliabilityLayer - ReliabilityLayer uses ACK_TIMEOUT=2000, ACK_RANDOM_FACTOR=1.5, and ACK_TIMEOUT_SCALE=2.0 as default [main] INFO org.eclipse.californium.core.network.CoapEndpoint - coap Endpoint [coap://0.0.0.0:0] requires an executor to start, using default single-threaded daemon executor [main] INFO org.eclipse.californium.elements.UDPConnector - UDPConnector starts up 4 sender threads and 4 receiver threads [main] INFO org.eclipse.californium.elements.UDPConnector - UDPConnector listening on 0.0.0.0/0.0.0.0:63851, recv buf = 65536, send buf = 65536, recv packet size = 2048 [main] INFO org.eclipse.californium.core.network.CoapEndpoint - coap Started endpoint at coap://0.0.0.0:63851 [main] INFO org.eclipse.californium.core.network.EndpointManager - created implicit endpoint coap://0.0.0.0:63851 for coap [main] INFO philipsair.PhilipsAir - synchronized with client id B3E24779 and server id 5BB802AF [main] INFO philipsair.PhilipsAir - observing coap://172.16.0.140:5683/sys/dev/status {"state":{"reported":{"name":"Bedroom","type":"AC2729","modelid":"AC2729/50","swversion":"0.2.1","om":"1","pwr":"1","cl":false,"aqil":100,"uil":"1","dt":0,"dtrs":0,"mode":"A","func":"P","rhset":50,"rh":29,"temp":23,"pm25":1,"iaql":1,"aqit":4,"ddp":"1","rddp":"1","err":49408,"wl":0,"fltt1":"A3","fltt2":"C7","fltsts0":307,"fltsts1":1327,"fltsts2":1327,"wicksts":1327,"ota":"no","Runtime":1896792,"WifiVersion":"AWS_Philips_AIR@56.4","ProductId":"XXXXXXXX","DeviceId":"XXXXXXXXXXX","StatusType":"localcontrol","ConnectType":"Localcontrol"}}} {"state":{"reported":{"name":"Bedroom","type":"AC2729","modelid":"AC2729/50","swversion":"0.2.1","om":"1","pwr":"1","cl":false,"aqil":100,"uil":"1","dt":0,"dtrs":0,"mode":"A","func":"P","rhset":50,"rh":29,"temp":23,"pm25":2,"iaql":1,"aqit":4,"ddp":"1","rddp":"1","err":49408,"wl":0,"fltt1":"A3","fltt2":"C7","fltsts0":307,"fltsts1":1327,"fltsts2":1327,"wicksts":1327,"ota":"no","Runtime":1903897,"WifiVersion":"AWS_Philips_AIR@56.4","ProductId":"XXXXXXXXXXXXX","DeviceId":"XXXXXXXX","StatusType":"localcontrol","ConnectType":"Localcontrol"}}} [main] INFO philipsair.PhilipsAir - disposing


Can I help testing? :)

dvd2000k commented 4 years ago

Confirm its working on my ac4550/10 and version 1.0.7

bobzomer commented 4 years ago

Unfortunately does not work with my AC3039/10.

$  airctrl --ipaddr 192.168.1.24 --version 1.0.7 -d
2020-05-06 12:05:36,810 - MainThread - coapthon.layers.messagelayer - DEBUG - send_request - From None, To ('192.168.1.24', 5683), None-None, POST-To, [Uri-Path: sys, Uri-Path: dev, Uri-Path: sync, ] AE0B4A85...8 bytes
2020-05-06 12:05:36,811 - MainThread - coapthon.client.coap - DEBUG - send_datagram - From None, To ('192.168.1.24', 5683), CON-49605, POST-To, [Uri-Path: sys, Uri-Path: dev, Uri-Path: sync, ] AE0B4A85...8 bytes
2020-05-06 12:05:36,812 - Thread-1   - coapthon.client.coap - DEBUG - Start receiver Thread
2020-05-06 12:05:36,812 - MainThread-Retry-49605 - coapthon.client.coap - DEBUG - retransmit loop ... enter
2020-05-06 12:05:36,915 - Thread-1   - coapthon.client.coap - DEBUG - receive_datagram - From ('192.168.1.24', 5683), To None, NON-49605, CONTENT-To, [Content-Type: 0, ] 57B77750...8 bytes
2020-05-06 12:05:36,915 - Thread-1   - coapthon.layers.messagelayer - DEBUG - receive_response - From ('192.168.1.24', 5683), To None, NON-49605, CONTENT-To, [Content-Type: 0, ] 57B77750...8 bytes
2020-05-06 12:05:36,916 - Thread-1   - coapthon.client.coap - DEBUG - Waiting for retransmit thread to finish ...
2020-05-06 12:05:36,916 - MainThread-Retry-49605 - coapthon.client.coap - DEBUG - retransmit loop ... exit
2020-05-06 12:05:36,928 - MainThread - coapthon.layers.messagelayer - DEBUG - send_request - From None, To ('192.168.1.24', 5683), ACK-None, GET-HtFD, [Uri-Path: sys, Uri-Path: dev, Uri-Path: status, Observe: 0, ] No payload
2020-05-06 12:05:36,928 - MainThread - coapthon.client.coap - DEBUG - send_datagram - From None, To ('192.168.1.24', 5683), ACK-49606, GET-HtFD, [Uri-Path: sys, Uri-Path: dev, Uri-Path: status, Observe: 0, ] No payload
2020-05-06 12:05:37,136 - Thread-1   - coapthon.client.coap - DEBUG - receive_datagram - From ('192.168.1.24', 5683), To None, ACK-1316, CONTENT-HtFD, [Observe: 35, Content-Type: 50, Max-Age: 60, ] AE0B4A860B22951ED81E...1137 bytes
2020-05-06 12:05:37,136 - Thread-1   - coapthon.layers.messagelayer - DEBUG - receive_response - From ('192.168.1.24', 5683), To None, ACK-1316, CONTENT-HtFD, [Observe: 35, Content-Type: 50, Max-Age: 60, ] AE0B4A860B22951ED81E...1137 bytes
2020-05-06 12:05:37,237 - Thread-1   - coapthon.client.coap - DEBUG - Exiting receiver Thread due to request
Traceback (most recent call last):
  File "/home/Lorianne/philips/bin/airctrl", line 8, in <module>
    sys.exit(main())
  File "/home/Lorianne/philips/lib/python3.8/site-packages/pyairctrl/airctrl.py", line 767, in main
    c.get_status(debug=args.debug)
  File "/home/Lorianne/philips/lib/python3.8/site-packages/pyairctrl/airctrl.py", line 636, in get_status
    status = self._get()
  File "/home/Lorianne/philips/lib/python3.8/site-packages/pyairctrl/airctrl.py", line 677, in _get
    decrypted_payload = self._decrypt_payload(encrypted_payload)
  File "/home/Lorianne/philips/lib/python3.8/site-packages/pyairctrl/airctrl.py", line 663, in _decrypt_payload
    decoded_message = AES.new(bytes(secret_key.encode('utf8')), AES.MODE_CBC, bytes(iv.encode('utf8'))).decrypt(bytes.fromhex(encoded_message))
ValueError: non-hexadecimal number found in fromhex() arg at position 1065

/sys/dev/info returns {"product_id":"660c6902649b11e9a1e3061302926720","device_id":"0b529c58af8111e9a1e3061302926720","name":"Maison","type":"AC3039","modelid":"AC3039/10","swversion":"Ms3104","option":"1"}

Zun1k commented 4 years ago

Confirm its working on my ac4550/10 and version 1.0.7

[modelid]: AC1214/10 works too

Cyber1000 commented 4 years ago

ok so it seems to work sometimes, with some bad experiences for @szogi and @bobzomer

@szogi - Interesting ... You tested with both 0.2.1 and 1.0.7

@bobzomer You seem to have the same problem, question ii would apply to you too, btw your sw-version looks interesting

I think there may be some "filling" characters or some other type of clutter around.

I don't expect having much time till tommorow evening or friday. Thanks for everyone's input so far ๐Ÿ‘

timbuktu-t commented 4 years ago
{"state":{"reported":{"name":"Bedroom","type":"AC2729","modelid":"AC2729/50","swversion":"0.2.1","om":"1","pwr":"1","cl":false,"aqil":100,"uil":"1","dt":0,"dtrs":0,"mode":"A","func":"P","rhset":50,"rh":29,"temp":23,"pm25":1,"iaql":1,"aqit":4,"ddp":"1","rddp":"1","err":49408,"wl":0,"fltt1":"A3","fltt2":"C7","fltsts0":307,"fltsts1":1327,"fltsts2":1327,"wicksts":1327,"ota":"no","Runtime":1896792,"WifiVersion":"AWS_Philips_AIR@56.4","ProductId":"XXXXXXXX","DeviceId":"XXXXXXXXXXX","StatusType":"localcontrol","ConnectType":"Localcontrol"}}}
{"state":{"reported":{"name":"Bedroom","type":"AC2729","modelid":"AC2729/50","swversion":"0.2.1","om":"1","pwr":"1","cl":false,"aqil":100,"uil":"1","dt":0,"dtrs":0,"mode":"A","func":"P","rhset":50,"rh":29,"temp":23,"pm25":2,"iaql":1,"aqit":4,"ddp":"1","rddp":"1","err":49408,"wl":0,"fltt1":"A3","fltt2":"C7","fltsts0":307,"fltsts1":1327,"fltsts2":1327,"wicksts":1327,"ota":"no","Runtime":1903897,"WifiVersion":"AWS_Philips_AIR@56.4","ProductId":"XXXXXXXXXXXXX","DeviceId":"XXXXXXXX","StatusType":"localcontrol","ConnectType":"Localcontrol"}}}

@Cyber1000 If you mean these two lines printed by my sample, I do not thing this is a mistake. The java program sets the observe option, which instructs the device to continue sending status notifications as long as the client is alive. I tried a simple GET to receive just one reading, but that was rejected by my AC3829/10. @szogi probably got two separate readings during the 10 second window the program is running, my device sends a notification approx. every 1-3 seconds. The device sets the confirmable option on the notifications, so Eclipse Californium automatically acknowledges them (you can see that in Wireshark, but not in my sample code). This way the device knows the client is still listening. You can find more information in RFC 7641.

bobzomer commented 4 years ago

@Cyber1000 Here is the encrypted payload:

$ airctrl --ipaddr 192.168.1.24 --version 1.0.7 -d
073CBCB1DCABB8CE96A60C81CBF8A6A9E57F4C9D0F0AD86C4B1A1F005874061AD6063DC72432138ABDD0A5AFEDD1D3333D430758DE3BD294BE026B2D1630A71C089EE74036C02F5D8954FEBA93A2886D3F8252263F770F3320ACEFB643FC65C97F0AC762B312AB18203D98F303DC3E1C7C8A8E3E35C86ED5C126121632088D569B50CFED6B72C4E44727E31E0AEE800B022B8B8F71D45CE19A50A0E94471661D9D4CA6694A75C81E5347FAE48E9F4706F6B169B8232BD23CCC9DD6353E2776199A5281EF351AA19AA8E1E003F7DAC6C93A05958EA71E1E55F0A78F63EC4F7D6258FD7F85D994AFF1815DFBC9714F85465FF0955884A3B39F55DDB62F19C7AD3A24AB1E66BEC78FA6FC727AF8C5BD28DB74655F836B71DD8872002F61FFCDF4799D40214F692953B4690A6B00FFBA5C260E5CDD998C2D8FB813EBC8E658B3B5E10907695268DA5DE213D5F134704AAA6E47B3DE42208BB9694F7E0F0414F42292E865A983EAFF0044D01E93F2E0ACF2F8FA4263064749ACE8A5A05FDD36EF1CF301A3311BE5FCE5E2B71085F752E1D4020A25BCECA9DDA4D577046395C82698B413666D7C3DAA0E7A4CEA7C1FD9DCB4FD791D82C12FA385E827F6B22D5C8F4FC6E0DE967BEEC5F365C39317CED6C2F6D86F3C03440724D9AC026A692C38D71BE227C4E59E828F02BB4CCB156919B1D989D76E4585DB6874FABFAD2495FC0365A53F542FACB244EBD35C80F4BEFA2CCB7A44A12A8B6D77D23388137600419878F3D946F9303C8003A7FB920105C2167DA0C9F0C6A238126DE0
Traceback (most recent call last):
  File "/home/Lorianne/philips/bin/airctrl", line 8, in <module>
    sys.exit(main())
  File "/home/Lorianne/philips/lib/python3.8/site-packages/pyairctrl/airctrl.py", line 768, in main
    c.get_status(debug=args.debug)
  File "/home/Lorianne/philips/lib/python3.8/site-packages/pyairctrl/airctrl.py", line 636, in get_status
    status = self._get()
  File "/home/Lorianne/philips/lib/python3.8/site-packages/pyairctrl/airctrl.py", line 678, in _get
    decrypted_payload = self._decrypt_payload(encrypted_payload)
  File "/home/Lorianne/philips/lib/python3.8/site-packages/pyairctrl/airctrl.py", line 664, in _decrypt_payload
    decoded_message = AES.new(bytes(secret_key.encode('utf8')), AES.MODE_CBC, bytes(iv.encode('utf8'))).decrypt(bytes.fromhex(encoded_message))
  File "/home/Lorianne/philips/lib/python3.8/site-packages/Cryptodome/Cipher/_mode_cbc.py", line 246, in decrypt
    raise ValueError("Data must be padded to %d byte boundary in CBC mode" % self.block_size)
ValueError: Data must be padded to 16 byte boundary in CBC mode
iDevOps-pl commented 4 years ago

Model:AC2729/50 Device Version: 0.2.1 Wifi Version: AWS_Philips_AIR@56.4

Output: 2020-05-07 16:25:56,830 - MainThread - coapthon.layers.messagelayer - DEBUG - send_request - From None, To ('192.168.0.134', 5683), None-None, POST-WU, [Uri-Path: sys, Uri-Path: dev, Uri-Path: sync, ] 6CB55613...8 bytes 2020-05-07 16:25:56,830 - MainThread - coapthon.client.coap - DEBUG - send_datagram - From None, To ('192.168.0.134', 5683), CON-17786, POST-WU, [Uri-Path: sys, Uri-Path: dev, Uri-Path: sync, ] 6CB55613...8 bytes 2020-05-07 16:25:56,831 - Thread-1 - coapthon.client.coap - DEBUG - Start receiver Thread 2020-05-07 16:25:56,831 - MainThread-Retry-17786 - coapthon.client.coap - DEBUG - retransmit loop ... enter 2020-05-07 16:25:56,837 - Thread-1 - coapthon.client.coap - DEBUG - receive_datagram - From ('192.168.0.134', 5683), To None, NON-17786, CONTENT-WU, [Content-Type: 0, ] 76035E0A...8 bytes 2020-05-07 16:25:56,838 - Thread-1 - coapthon.layers.messagelayer - DEBUG - receive_response - From ('192.168.0.134', 5683), To None, NON-17786, CONTENT-WU, [Content-Type: 0, ] 76035E0A...8 bytes 2020-05-07 16:25:56,838 - Thread-1 - coapthon.client.coap - DEBUG - Waiting for retransmit thread to finish ... 2020-05-07 16:25:56,838 - MainThread-Retry-17786 - coapthon.client.coap - DEBUG - retransmit loop ... exit 2020-05-07 16:25:56,848 - MainThread - coapthon.layers.messagelayer - DEBUG - send_request - From None, To ('192.168.0.134', 5683), ACK-None, GET-VJyr, [Uri-Path: sys, Uri-Path: dev, Uri-Path: status, Observe: 0, ] No payload 2020-05-07 16:25:56,849 - MainThread - coapthon.client.coap - DEBUG - send_datagram - From None, To ('192.168.0.134', 5683), ACK-17787, GET-VJyr, [Uri-Path: sys, Uri-Path: dev, Uri-Path: status, Observe: 0, ] No payload 2020-05-07 16:25:56,878 - Thread-1 - coapthon.client.coap - DEBUG - receive_datagram - From ('192.168.0.134', 5683), To None, ACK-272, CONTENT-VJyr, [Observe: 14, Content-Type: 50, Max-Age: 60, ] 6CB55614FBE503D9FC3F...1137 bytes 2020-05-07 16:25:56,878 - Thread-1 - coapthon.layers.messagelayer - DEBUG - receive_response - From ('192.168.0.134', 5683), To None, ACK-272, CONTENT-VJyr, [Observe: 14, Content-Type: 50, Max-Age: 60, ] 6CB55614FBE503D9FC3F...1137 bytes 2020-05-07 16:25:56,979 - Thread-1 - coapthon.client.coap - DEBUG - Exiting receiver Thread due to request Traceback (most recent call last): File "./airctrl.py", line 771, in main() File "./airctrl.py", line 767, in main c.get_status(debug=args.debug) File "./airctrl.py", line 636, in get_status status = self._get() File "./airctrl.py", line 677, in _get decrypted_payload = self._decrypt_payload(encrypted_payload) File "./airctrl.py", line 663, in _decrypt_payload decoded_message = AES.new(bytes(secret_key.encode('utf8')), AES.MODE_CBC, bytes(iv.encode('utf8'))).decrypt(bytes.fromhex(encoded_message)) ValueError: non-hexadecimal number found in fromhex() arg at position 1065

rgerganov commented 4 years ago

@Cyber1000 Here is the encrypted payload:

$ airctrl --ipaddr 192.168.1.24 --version 1.0.7 -d
073CBCB1DCABB8CE96A60C81CBF8A6A9E57F4C9D0F0AD86C4B1A1F005874061AD6063DC72432138ABDD0A5AFEDD1D3333D430758DE3BD294BE026B2D1630A71C089EE74036C02F5D8954FEBA93A2886D3F8252263F770F3320ACEFB643FC65C97F0AC762B312AB18203D98F303DC3E1C7C8A8E3E35C86ED5C126121632088D569B50CFED6B72C4E44727E31E0AEE800B022B8B8F71D45CE19A50A0E94471661D9D4CA6694A75C81E5347FAE48E9F4706F6B169B8232BD23CCC9DD6353E2776199A5281EF351AA19AA8E1E003F7DAC6C93A05958EA71E1E55F0A78F63EC4F7D6258FD7F85D994AFF1815DFBC9714F85465FF0955884A3B39F55DDB62F19C7AD3A24AB1E66BEC78FA6FC727AF8C5BD28DB74655F836B71DD8872002F61FFCDF4799D40214F692953B4690A6B00FFBA5C260E5CDD998C2D8FB813EBC8E658B3B5E10907695268DA5DE213D5F134704AAA6E47B3DE42208BB9694F7E0F0414F42292E865A983EAFF0044D01E93F2E0ACF2F8FA4263064749ACE8A5A05FDD36EF1CF301A3311BE5FCE5E2B71085F752E1D4020A25BCECA9DDA4D577046395C82698B413666D7C3DAA0E7A4CEA7C1FD9DCB4FD791D82C12FA385E827F6B22D5C8F4FC6E0DE967BEEC5F365C39317CED6C2F6D86F3C03440724D9AC026A692C38D71BE227C4E59E828F02BB4CCB156919B1D989D76E4585DB6874FABFAD2495FC0365A53F542FACB244EBD35C80F4BEFA2CCB7A44A12A8B6D77D23388137600419878F3D946F9303C8003A7FB920105C2167DA0C9F0C6A238126DE0
Traceback (most recent call last):
  File "/home/Lorianne/philips/bin/airctrl", line 8, in <module>
    sys.exit(main())
  File "/home/Lorianne/philips/lib/python3.8/site-packages/pyairctrl/airctrl.py", line 768, in main
    c.get_status(debug=args.debug)
  File "/home/Lorianne/philips/lib/python3.8/site-packages/pyairctrl/airctrl.py", line 636, in get_status
    status = self._get()
  File "/home/Lorianne/philips/lib/python3.8/site-packages/pyairctrl/airctrl.py", line 678, in _get
    decrypted_payload = self._decrypt_payload(encrypted_payload)
  File "/home/Lorianne/philips/lib/python3.8/site-packages/pyairctrl/airctrl.py", line 664, in _decrypt_payload
    decoded_message = AES.new(bytes(secret_key.encode('utf8')), AES.MODE_CBC, bytes(iv.encode('utf8'))).decrypt(bytes.fromhex(encoded_message))
  File "/home/Lorianne/philips/lib/python3.8/site-packages/Cryptodome/Cipher/_mode_cbc.py", line 246, in decrypt
    raise ValueError("Data must be padded to %d byte boundary in CBC mode" % self.block_size)
ValueError: Data must be padded to 16 byte boundary in CBC mode

This can be decrypted if we get a bigger chunk from the payload:

msg = encrypted_payload[8:-8].upper()
cipher = AES.new(secret_key.encode('ascii'), AES.MODE_CBC, iv.encode('ascii'))
print(cipher.decrypt(msg))

this is the result:

b'{"state":{"reported":{"name":"Maison","type":"AC3039","modelid":"AC3039/10","swversion":"Ms3104","language":"EN","DeviceVersion":"1.0.4","om":"s","pwr":"1","cl":false,"aqil":0,"uil":"0","uaset":"A","mode":"S","pm25":11,"iaql":3,"aqit":4,"tvoc":1,"ddp":"1","rddp":"1","err":0,"fltt1":"A3","fltt2":"none","fltsts0":129,"fltsts1":4752,"fltsts2":65535,"filna":"0","filid":"0","ota":"ck","Runtime":1037325155,"WifiVersion":"AWS_Philips_AIR@54.2","ProductId":"660c6902649b11e9a1e3061302926720","DeviceId":"0b529c58af8111e9a1e3061302926720","StatusType":"localcontrol'

As you can see it is truncated. I guess this means we should either increase the COAP response buffer or make another request.

holomekc commented 4 years ago

I have written an ioBroker adapter for myself. I did not publish it because I have absolutely no time to maintain it. But if you are interested, I can upload the current version to GitHub. image

szogi commented 4 years ago

@rgerganov @Cyber1000

As you can see it is truncated. I guess this means we should either increase the COAP response buffer or make another request.

2020-05-09 01:10:50,491 - Thread-1 - coapthon.layers.messagelayer - DEBUG - receive_response - From ('172.16.0.139', 5683), To None, ACK-1, CONTENT-nUQg, [Observe: 0, Content-Type: 50, Max-Age: 60, ] 53D70E9486BFD0E51915... 1138 bytes

Here is my encrypted payload: 53D70E9486BFD0E51915E2FAA605B32F2FDCCBB20995C527D841FCC6B88C408D25A0631FAEAC44F2F69806936C67A473BEA4712F20E2B2847B59D48B200E5297E2BE8ED8B0CE0BCE52841CDDC29DDC245C0FECF0F784BEAC4197BD3041A533427B8974918D8D7249C3F033EA35BCC3D574FC610168DC963B01B4B3DDBA8EA442F3AA106F1F7ED7D2292824CD42EBC8ADE2F2352986D03ADB56698959F0BC3A551AD9406378EB39E72352154EBB220685090119086E68E09549D62895E079939463B383DDFBA33655D8971356B17EE13D086777CC33E4F7644AE699610E5CE6530A5DA80657432ACE6ED2BB31E783DDC7E2CF17835D6530340BD75934D5A9F7469F7D2FC3CDA9DB28321541918D0F78B29CA110052B7D86D192C8CEE4C5F6D8447CCFEC67234A29897369EECCB08CFD554EF6B088FB2DE4E2D417C9BBBD6765ED6102EE7FB43D87A58D40596063830F2F329FAF23D6EA0D0364033578BAA74EF710F40615128C7EBF93D4C8DB2D36A1C2E183CD7D6AE89239327EE032B0F0B0917793252F03AFD602990E9BCF578A923472DB5D9669EED4F89E5215521D079A25A28DC1BDA583E00AC1FFB85D9337F40FAA4FC1BA113714D76226601B451DBDF2C5E2161B0028B630CE5D72FCACEE654357067BD55804A6FCB9DFB2617AE8C690A95466CAE7FD5BB80B84660D4CF9CFC24AAE7A183C9C244D0270662E939229943D821C8396192F9885A7DED122FF25A432B496BD134A1255E003CEC34166DEF34D91BB922ED0F801983C4577F916F916130E3A7A674904FC81