rgerganov / py-air-control

Command line app for controlling Philips air purifiers
MIT License
266 stars 53 forks source link

AC2889/10 no longer working #93

Open xxcite opened 3 years ago

xxcite commented 3 years ago

I was using py-air-control for some months to control my AC2889/10 without problems. Since today it does not work any longer although I have changed absolutely nothing. I can switch on and switch off the air purifier using

airctrl --ipaddr 192.168.0.84 --protocol coap --pwr (0|1)

But when calling airctrl --ipaddr 192.168.0.84 --protocol coap I now get the following almost on every call:

2021-07-05 13:44:32,524 - MainThread - coapthon.layers.messagelayer - DEBUG - send_request - From None, To ('192.168.0.84', 5683), None-None, POST-kR, [Uri-Path: sys, Uri-Path: dev, Uri-Path: sync, ] A0852A76...8 bytes
2021-07-05 13:44:32,525 - MainThread - coapthon.client.coap - DEBUG - send_datagram - From None, To ('192.168.0.84', 5683), CON-49660, POST-kR, [Uri-Path: sys, Uri-Path: dev, Uri-Path: sync, ] A0852A76...8 bytes
2021-07-05 13:44:32,526 - Thread-1   - coapthon.client.coap - DEBUG - Start receiver Thread
2021-07-05 13:44:32,527 - MainThread-Retry-49660 - coapthon.client.coap - DEBUG - retransmit loop ... enter
2021-07-05 13:44:34,837 - MainThread-Retry-49660 - coapthon.client.coap - DEBUG - retransmit loop ... retransmit Request
2021-07-05 13:44:34,837 - MainThread-Retry-49660 - coapthon.client.coap - DEBUG - send_datagram - From None, To ('192.168.0.84', 5683), CON-49660, POST-kR, [Uri-Path: sys, Uri-Path: dev, Uri-Path: sync, ] A0852A76...8 bytes
2021-07-05 13:44:37,532 - MainThread-Retry-49660 - coapthon.client.coap - WARNING - Give up on message From None, To ('192.168.0.84', 5683), CON-49660, POST-kR, [Uri-Path: sys, Uri-Path: dev, Uri-Path: sync, ] A0852A76...8 bytes
2021-07-05 13:44:37,534 - MainThread-Retry-49660 - coapthon.client.coap - DEBUG - retransmit loop ... exit
2021-07-05 13:44:37,535 - Thread-1   - coapthon.client.coap - DEBUG - Exiting receiver Thread due to request
Traceback (most recent call last):
  File "/home/pi/.local/bin/airctrl", line 10, in <module>
    sys.exit(main())
  File "/home/pi/.local/lib/python3.7/site-packages/pyairctrl/airctrl.py", line 448, in main
    c = CoAPCli(device["ip"], debug=args.debug)
  File "/home/pi/.local/lib/python3.7/site-packages/pyairctrl/airctrl.py", line 15, in __init__
    self._client = CoAPAirClient(host, port, debug)
  File "/home/pi/.local/lib/python3.7/site-packages/pyairctrl/coap_client.py", line 80, in __init__
    self._sync()
  File "/home/pi/.local/lib/python3.7/site-packages/pyairctrl/coap_client.py", line 96, in _sync
    raise Exception("sync timeout")
Exception: sync timeout

But this does not happen on every call. A small number of calls (roundabout 1 of 20) succeed and return the correct status:

[name]                        Name: Luftreiniger
[type]                        Type: AC2889
[modelid]                     ModelId: AC2889/10
[swversion]                   Version: 1.0.7
[range]                       range: Comfort
[Runtime]                     Runtime: 0.84 hours
[WifiVersion]                 WifiVersion: AWS_Philips_AIR@64.3
[ProductId]                   ProductId: be10acb2e62411e8a1e3061302926720
[DeviceId]                    DeviceId: 47bf972690a911eb8f480aa926c8d24c
[StatusType]                  StatusType: status
[ConnectType]                 ConnectType: Online
[om]                          Fan speed: 0
[pwr]                         Power: OFF
[cl]                          Child lock: False
[aqil]                        Light brightness: 100
[uil]                         Buttons light: ON
[mode]                        Mode: allergen
[pm25]                        PM25: 4
[iaql]                        Allergen index: 1
[aqit]                        Air quality notification threshold: 7
[ddp]                         Used index: IAI
[fltt1]                       HEPA filter type: NanoProtect Filter Series 3 (FY2422)
[fltt2]                       Active carbon filter type: NanoProtect Filter AC (FY2420)
[fltsts0]                     Pre-filter and Wick: clean in 233 hours
[fltsts1]                     HEPA filter: replace in 4313 hours
[fltsts2]                     Active carbon filter: replace in 1913 hours

Is this a known problem and there is a solution for that?

Setcover commented 3 years ago

I can confirm this issue.

AFAIK: Philips released a new firmware version which installed without user interaction. My last FW version was 62.1 the new FW version is 64.3 You can check your FW version by selecting your air purifyer in the app -> wrench symbol top right corner -> scroll down and click "diagnostics via e-mail" it will create an e-mail (but not send it) where you can find "Firmware Version".

If anybody could give me any hint how to decode the new interaction. Help would be much appreciated.

My router supports packet capture and exporting to wireshark compatible file type. But i have no idea what to do from there on.

Best regards, Peter

Setcover commented 3 years ago

TL;DR: Proposed hotfix at the end.

Unfortunately i have no aircleaner remaining with FW 62.1, the FW update on the last one started while i was starting troubleshooting hours ago (that's why i know the old FW was 62.1) so i have no way to know how the messages looked like while everything was working. However i can compare airctrl to the smartphone app.

In advance, please note the last ip.addr block of my devices:

Using airctrl to change the mode (/sys/dev/control) does work. Getting the new status does not work (/sys/dev/status). Airctrl does send /sys/dev/status without token and never gets an answer with corresponding MessageID. Successful control commands marked dark green, unsuccessful status commands marked red. grafik

Comparing the behaviour to the smartphone app, the status request is sent with token but there is again no answer with corresponding MessageID. I get the feeling the problem is: "the aircleaner simply does not respond to status requests but instead answers to really old status requests" ... notice those "Retransmissions". grafik To me it looks like Philips screwed up some queue handling... but again i have no aircleaner with old firmware to compare to.

If somebody wants to see the same packet snapshot sorted by time, starting with the first Message with MessageID 638 it looks like this: grafik

I also noticed the aircleaner is no longer listed as "MICO" in my router but instead "mxchip".

I propose to implement a hotfix solution that, regarding /sys/dev/status, ignores the MessageID but instead accepts any message containing /sys/dev/status (and maybe /sys/dev/info).

Best regards, Peter

Olgidos commented 3 years ago

Fortunately, I blocked internet access for the cleaner some weeks ago and I still have the FW 62.1. I have never done such a network analysis, but if you could step me through, I would like to provide the data for the old firmware.

Setcover commented 3 years ago
  1. You have to acquire a package capture. If you do not use a fritzbox router you have to google how to capture packages on your router or within linux running your airctrl script. If your router is a fritzbox go to http://fritz.box/support.lua and login, scroll down to "Paketmitschnitte" and click the link, scroll down to "WLAN" and click "Start" in the row containing "2.4 GHz". A download will start, save it to your disk.
  2. execute your airctrl command (in my case airctrl --ipaddr 192.168.6.151 --protocol coap )
  3. click "Stopp" in your fritzbox browser window, the download will stop
  4. if you don't have wireshark: download and install it, you do not need any of those package sniffing drivers wireshark could install
  5. open the downloaded file in wireshark
  6. apply a suitable filter by specifying the ip address of your aircleaner and your airctrl running machine, (in my case it was ip.addr == 192.168.6.151 && ip.addr == 192.168.6.140 ) grafik
  7. left click any coap package, left klick (or double click??) to expand "Constrained Application Protocol ...", right click on the Message ID, left klick "Apply as Column" (Als Spalte anwenden) grafik now you can sort by Message ID by clicking on the corresponding column label
  8. to color the aircleaner when it is sending: click "view" -> "Coloring Rules..." -> "+" in the bottom left corner, name it however you want to, as filter add "ip.src == 192.168.6.151" (replace the ip address with the ip address of your aircleaner), hit enter, highlight the row containing the new rule, click "Background" in the bottom left corner and choose a color that is not light blue, hit "OK" to close the color selection window, hit "OK" to close the Coloring Rules window.
  9. make sure your messages are sorted by message id, look for a /sys/dev/sync with a /sys/dev/sync response from your aircleaner followed by a /sys/dev/status from your airctrl and then ....... the package we are looking for. I highlighted two unsuccessful examples for you grafik
  10. post a screenshot of the result

What ever you do, do not post the package capture file itself to the internet, it contains ANYTHING transmitted by WLAN in the regarding timeframe.

Best regards, Peter

P.S.: I really hope you don't hit a roadblock at step 1

NikDevx commented 3 years ago

I have the same error from yesterday. AC3829/50

airctrl --ipaddr 192.168.0.10 --protocol coap Unexpected error:'NoneType' object has no attribute 'payload' Give up on message From None, To ('192.168.0.10', 5683), CON-61742, GET-None, [Uri-Path: sys, Uri-Path: dev, Uri-Path: status, Observe: 0, ] No payload

I think philips updated firmware and something was changed.

Olgidos commented 3 years ago

All right, I did this packet sniffing, thanks for this awsome tutorial! Sorry, that it took some days, i was a little bit busy and needed to reset my linux :/

Here my snapshot of the sync

Capture

if you require further information please give me feedback :)

NikDevx commented 3 years ago

My snapshot of the sync

Снимок экрана 2021-07-09 в 02 58 14

ThumbGen commented 3 years ago

I have the same error from yesterday. AC3829/50

airctrl --ipaddr 192.168.0.10 --protocol coap Unexpected error:'NoneType' object has no attribute 'payload' Give up on message From None, To ('192.168.0.10', 5683), CON-61742, GET-None, [Uri-Path: sys, Uri-Path: dev, Uri-Path: status, Observe: 0, ] No payload

I think philips updated firmware and something was changed.

Yes, indeed, my AC1214/10 started to show the same error, right after performing the firmware update (WiFi WPA3).

roscarraig commented 3 years ago

I've been in the same situation, on the new firmware and I lost local control most of the time (it would occasionally work), so I took a closer look. It seems the requests are timing out so increasing the hard-coded timeouts to 10s in coap_client (and adding a timeout argument on line 177) restores operation for me.

NikDevx commented 3 years ago

I've been in the same situation, on the new firmware and I lost local control most of the time (it would occasionally work), so I took a closer look. It seems the requests are timing out so increasing the hard-coded timeouts to 10s in coap_client (and adding a timeout argument on line 177) restores operation for me.

Can you show what did you added?

roscarraig commented 3 years ago

The first couple of lines were just some trailing whitespace which was triggering syntastic, but the remainder is the relevant part:

index 9d5b142..d2f76eb 100644
--- a/pyairctrl/coap_client.py
+++ b/pyairctrl/coap_client.py
@@ -83,15 +83,15 @@ class CoAPAirClient(HTTPAirClientBase):
     def __del__(self):
         # TODO call a close method explicitly instead
         if self.response:
-            self.client.cancel_observing(self.response, True)        
-        self.client.stop()        
+            self.client.cancel_observing(self.response, True)
+        self.client.stop()

     def _create_coap_client(self, host, port):
         return HelperClient(server=(host, port))

     def _sync(self):
         self.syncrequest = binascii.hexlify(os.urandom(4)).decode("utf8").upper()
-        resp = self.client.post("/sys/dev/sync", self.syncrequest, timeout=5)
+        resp = self.client.post("/sys/dev/sync", self.syncrequest, timeout=10)
         if resp:
             self.client_key = resp.payload
         else:
@@ -145,7 +145,7 @@ class CoAPAirClient(HTTPAirClientBase):
         try:
             request = self.client.mk_request(defines.Codes.GET, path)
             request.observe = 0
-            self.response = self.client.send_request(request, None, 2)
+            self.response = self.client.send_request(request, None, 10)
             encrypted_payload = self.response.payload
             decrypted_payload = self._decrypt_payload(encrypted_payload)
         except WrongDigestException:
@@ -174,7 +174,7 @@ class CoAPAirClient(HTTPAirClientBase):
                 }
             }
             encrypted_payload = self._encrypt_payload(json.dumps(payload))
-            response = self.client.post(path, encrypted_payload)
+            response = self.client.post(path, encrypted_payload, timeout=10)
             if self.debug:
                 print(response)
             return response.payload == '{"status":"success"}'

Ideally, I'd suggest using a defined constant but I thought I'd see what people thought before proposing something more concrete.

NikDevx commented 3 years ago

The first couple of lines were just some trailing whitespace which was triggering syntastic, but the remainder is the relevant part:

index 9d5b142..d2f76eb 100644
--- a/pyairctrl/coap_client.py
+++ b/pyairctrl/coap_client.py
@@ -83,15 +83,15 @@ class CoAPAirClient(HTTPAirClientBase):
     def __del__(self):
         # TODO call a close method explicitly instead
         if self.response:
-            self.client.cancel_observing(self.response, True)        
-        self.client.stop()        
+            self.client.cancel_observing(self.response, True)
+        self.client.stop()

     def _create_coap_client(self, host, port):
         return HelperClient(server=(host, port))

     def _sync(self):
         self.syncrequest = binascii.hexlify(os.urandom(4)).decode("utf8").upper()
-        resp = self.client.post("/sys/dev/sync", self.syncrequest, timeout=5)
+        resp = self.client.post("/sys/dev/sync", self.syncrequest, timeout=10)
         if resp:
             self.client_key = resp.payload
         else:
@@ -145,7 +145,7 @@ class CoAPAirClient(HTTPAirClientBase):
         try:
             request = self.client.mk_request(defines.Codes.GET, path)
             request.observe = 0
-            self.response = self.client.send_request(request, None, 2)
+            self.response = self.client.send_request(request, None, 10)
             encrypted_payload = self.response.payload
             decrypted_payload = self._decrypt_payload(encrypted_payload)
         except WrongDigestException:
@@ -174,7 +174,7 @@ class CoAPAirClient(HTTPAirClientBase):
                 }
             }
             encrypted_payload = self._encrypt_payload(json.dumps(payload))
-            response = self.client.post(path, encrypted_payload)
+            response = self.client.post(path, encrypted_payload, timeout=10)
             if self.debug:
                 print(response)
             return response.payload == '{"status":"success"}'

Ideally, I'd suggest using a defined constant but I thought I'd see what people thought before proposing something more concrete.

I ping airtctl 10 times and 2 times I got Unexpected error:'NoneType' object has no attribute 'payload' I changed to 15 sec timeout and 10 times ping airtctl and got 0 error.

Thanks.

michaelschefczyk commented 3 years ago

Both changes seem to go into the right direction (thanks!!). Nevertheless, I found more timeouts than before. I use the connection to (a) change the level based on time of day and (b) track air quality via FHEM. As I send the change command three times in a row, that seems to be more stable. Tracking misses many data points, unfortunately.

TravisWilder commented 3 years ago

FYI .. timeout with 60 does not help on my new Model: AC2889/10 (AC2889/10) Device Version: 1.0.7

mbirnbacher commented 3 years ago

Same problem here with the AC2939/10 and Firmware 64.3.

MrMEScott commented 3 years ago

My AC2889/10 still only responds less than half of the time after changing timeouts to 60 seconds. When it does respond, the time taken seems to be totally random - occasionally it's almost immediate, other times anything up to the timeout limit. Most of my testing is done when the device is "off", and I'm wondering what else it's doing that would prevent it responding to a status inquiry.

[type]                        Type: AC2889
[modelid]                     ModelId: AC2889/10
[swversion]                   Version: 1.0.7
[range]                       range: Comfort
[Runtime]                     Runtime: 1.97 hours
[WifiVersion]                 WifiVersion: AWS_Philips_AIR@64.3
sieren commented 3 years ago

I've spent a few hours now looking into this, what's a tad more "reliable/speedy" than the increased timeout is a recursive call to establish the connection on error/timeout, but that's not really pretty either. I believe Philips or MXChip screwed this up and since the Philip's App seems to be having similar issues at times (device disappears as disconnected, then reappears, data not updating in a timely manner etc.)

maaaax commented 3 years ago

Just got a new AC2889/10 with FW 1.0.7. The app doesn't even find the device in the lan: The whole communication is: 23:29:46.354185 IP 192.168.21.125.mdns > 224.0.0.251.mdns: 0 PTR (QM)? _philipscondor._tcp.local. (43) 23:29:46.354215 IP 192.168.21.125.mdns > 224.0.0.251.mdns: 0 PTR (QM)? _philipscondor._tcp.local. (43) 23:29:46.383845 IP 192.168.21.125.mdns > 224.0.0.251.mdns: 0 PTR (QM)? _philipscondor._tcp.local. (43) 23:29:46.383871 IP 192.168.21.125.mdns > 224.0.0.251.mdns: 0 PTR (QM)? _philipscondor._tcp.local. (43) 23:29:46.762127 IP 192.168.21.125.mdns > 224.0.0.251.mdns: 0 PTR (QM)? _philipscondor._tcp.local. (43) 23:29:46.762159 IP 192.168.21.125.mdns > 224.0.0.251.mdns: 0 PTR (QM)? _philipscondor._tcp.local. (43) 23:29:46.966910 IP 192.168.21.125.mdns > 224.0.0.251.mdns: 0 [1n] ANY (QM)? CommLib mDNS.local. (52) 23:29:47.581296 IP 192.168.21.125.mdns > 224.0.0.251.mdns: 0- [0q] 1/0/0 (Cache flush) A 192.168.21.125 (46) 23:29:48.416567 IP 192.168.21.125.mdns > 224.0.0.251.mdns: 0- [0q] 1/0/0 (Cache flush) A 192.168.21.125 (46) 23:29:49.424930 IP 192.168.21.125.mdns > 224.0.0.251.mdns: 0*- [0q] 1/0/0 (Cache flush) A 192.168.21.125 (46) 23:30:37.144549 IP 192.168.21.106 > 224.0.0.1: igmp v2 report 224.0.0.1 where .125 is the phone and .106 is the AC2889 After that some SoftAP timeout error.. On .106, all 65535 tcp-ports are closed but mdns is responding.

Setcover commented 3 years ago

I have a workaround... using another package. Looks like the FW update from Philips is indeed a bugfix and not a bug. Looks like the purifier only sends a new message if something changed, els it will repeat the last message. So if you have good air quality the values will not change often and py-aircontrol will timeout.

I am now using this package https://github.com/betaboon/aioairctrl which has a small bug which i fixed here https://github.com/Peter-J/aioairctrl (output was not flushed so piping it to another script did not work). You can install it using

pip3 install -U git+https://github.com/......./.........

Biggest feature is that aioairctrl can continously listen to the air purifyer. Again, if your airquality is too god / does not change you will have to wait a long time for the first message. Changing your fanspeed will force new messages to be send.

I included my scripts in the examples folder. Don't expect too much in regards of code quality / readability and so on but i have not a single faild send or recieve. Zero user intervention since.

If you find any bugs / have suggestions please let me know. Will make a pull-request in a few days.

Best regards, Peter

kongo09 commented 2 years ago

Thanks, @Peter-J , for your efforts. Unfortunately, your version of aioairctrl doesn't work for my AC2889 and AC2729. When I call the command, it just never returns.

Both devices are on firmware version 67.5

Edit: it sometimes does indeed work, and then again stops working.

kongo09 commented 2 years ago

both devices still don't work all the time (most of the time not)

SickSwan commented 2 years ago
  • The AC2889 is now on firmware 69.1

    • The AC2729 is now on firmware 68.3

both devices still don't work all the time (most of the time not)

Im wondering if I have the same issue as you. Does your air purifier stay on the same readings for a long time? My replacement 2000i (1st one had a faulty sensor) is freezing on 4-IAI and 13-PM2.5 for a good 5 minutes before jumping to 2 on the Allergen Index and skipping 3. Is there some kind of buffer value built in so the fan speeds don't adjust too often when just above or below the thresholds for good and fair air quality? I'm trying to work out if its a fault or a feature but I'm getting nowhere with Philips support. Cheers

kongo09 commented 2 years ago

No, it doesn't freeze.

I'm pretty sure it is a bug in their implementation. When I capture the network traffic, at times the device stops sending CoaP telegrams, but keeps talking to some Amazon servers, which presumably are serving the cloud service of Philips.

image

The most stable solution would probably be to understand the cloud protocol.

sieren commented 2 years ago

https://github.com/betaboon/aioairctrl still works. But because its using the observer pattern as opposed to continously poll. Big reason I had to switch to this library instead. At least if my memory serves correctly...

kongo09 commented 2 years ago

Not working for me at the moment. But I guess, this is down to the Philips devices. If they don't transmit any CoaP, aioairctrl obviously can't pick it up either.

prghix commented 2 years ago

both ours AC5659/10 and2889/10 on 69.1. I should have disconnected them from getting on the WAN.

dfrommi commented 2 years ago

I think there has to be a command to request state re-transmission. I've done the following test: Run something that's querying for state, like

airctrl --ipaddr 192.168.1.2 --protocol coap --filters

This is stuck, waiting for updates...

But when you then open the Clean Home+ app on your smart phone, the airctrl command succeeds, showing the filter states.

patrolez commented 2 years ago

@dfrommi, @kongo09: Peter and siren wrote solution, I will let myself provide some examples.

Looks like the purifier only sends a new message if something changed

siren meant reding status that way [1️⃣]: python3 -u /usr/bin/aioairctrl --host 192.168.20.152 status-observe

I like airctrl to setup things because of its option to set parameters as flags. In my case, I have AC2959/53 and if pm2.5 readings are standing still, so there are no messages.

You can force message to appear some while using cmd [※1️⃣] on another terminal/panel, as it have to have be running before next step, which is an attempt to control yours purifiers, e.g.: airctrl --ipaddr 192.168.20.152 --protocol coap --uil 1

Unfortunately, sometimes I need to do it twice, so again: airctrl --ipaddr 192.168.20.152 --protocol coap --uil 1

Which will result that the status-observe handling in the aioairctl from cmd [※1️⃣] will print in a new line a complete status: {'name': 'Air Purifier', 'type': 'AC2959', 'modelid': 'AC2959/53', 'MCUBoot': True, 'swversion': 'Ms2104', 'language': 'EN', 'DeviceVersion': '1.0.4', 'range': 'MarsLE', 'Runtime': 1272165782, 'rssi': -52, 'otacheck': False, 'wifilog': False, 'free_memory': 53840, 'WifiVersion': 'AWS_Philips_AIR@71.3', 'ProductId': '...', 'DeviceId': '...', 'StatusType': 'control', 'ConnectType': 'Online', 'om': 's', 'pwr': '1', 'cl': False, 'aqil': 100, 'uil': '1', 'uaset': 'P', 'mode': 'AG', 'pm25': 1, 'iaql': 1, 'aqit': 4, ...}

zak333 commented 1 year ago

I have a AC2889/10 with updated firmware of course (I guess). So far I couldn't execute any command to get any helpful information at all. I don't have the possibility and don't want to install the app.

Is there any way at all to at least set a wifi network+pw without the app? There are 49 forks of this repo. No idea if there is one that works better than the main one?

Currently it seems the default wifi is always active and I cannot do anything about that. Thank you Philips.

kongo09 commented 1 year ago

So far I couldn't execute any command to get any helpful information at all.

In that case you're probably stuck.