webdjoe / pyvesync

pyvesync is a python library to manage Etekcity & Levoit smart devices
MIT License
175 stars 83 forks source link

Add support for the Classic 300S humidifier #66

Closed rwl4 closed 3 years ago

rwl4 commented 3 years ago

It would be great to have support for the Levoit Classic 300S humidifier. In fact, if one of you wants to reach out to me, I'll have Amazon ship you one at my expense! Let me know!

webdjoe commented 3 years ago

I appreciate that, but I'd be more that happy to add support if you posted the packet captures

SandroGrzicic commented 3 years ago

I've sent the packet captures for the various functions to webdjoe.

Here are roughly the modes of the humidifier:

It has a setting to turn on/off the front display (except in sleep mode where it's off).

It has a setting to turn off the mini night light, or set it to a "dim" or "medium" level.

It has a timer for turning on or off in x mins. It has a scheduler for turning it on or off (with specific settings) at a certain time of day. There's also a push notification if the water level is zero. Presumably these features don't need to be supported though. :)

Thanks!

zpatten commented 3 years ago

@webdjoe I'd love to help get support for this added. I recently purchased 4 of these for my house and am itching to get them into HA. You mentioned packet captures; do you mean you need to see what API calls the VeSync app is making to control the units?

webdjoe commented 3 years ago

I was just sent the packet captures so I am working on it. These new devices have slightly different call structures that make the coding a little different than the library was built. I don't want to put a quick patch that makes the code difficult so I am redesigning some of the underlying structure that will make adding devices easier

zpatten commented 3 years ago

Awesome; thanks for working on this, it is much appreciated. Please let me know if I can help in any way.

JonasQuin42 commented 3 years ago

Also happy to help in any way possible.

theearthwanderer commented 3 years ago

Thank you @webdjoe for working on this.

theearthwanderer commented 3 years ago

Hi @SandroGrzicic would you mind sending me the packet captures you have for various functions? Thanks a lot!

webdjoe commented 3 years ago

Thank you for wanting to assist. I refactored the main vesync class to make it easier to add devices. I’m also working with mark to transfer the repo to my account and add CI. Would you mind holding off until I merge the changes? I should have the master updated by tonight or tomorrow the latest.

On Thu, Jan 28, 2021 at 9:12 AM Sandro Gržičić notifications@github.com wrote:

Hi @SandroGrzicic https://github.com/SandroGrzicic would you mind sending me the packet captures you have for various functions? Thanks a lot!

Sure, just send me an email at ivan.abrahimovic (gmail) and I'll e-mail you the ZIP.

— You are receiving this because you were mentioned.

Reply to this email directly, view it on GitHub https://github.com/markperdue/pyvesync/issues/66#issuecomment-769080324, or unsubscribe https://github.com/notifications/unsubscribe-auth/AB6JJBQDJGF3PMEW4LJHIODS4FWGFANCNFSM4VJHKL5A .

webdjoe commented 3 years ago

I've updated the master branch with CI and new changes. I have a rough update for the 300S humidifier, please test it out here: https://github.com/webdjoe-bot/pyvesync/tree/Levoit300S

obi11235 commented 3 years ago

I tried it out with this script

from pyvesync import VeSync

manager = VeSync("","")
manager.login()
manager.update()

for device in manager.fans:
    if device.device_type == "Classic300S":
        device.display()
        device.turn_off()

I had to make the fix in this pr linked below to make it work, but i think there is more wrong than that. I have a different version of the app, 3.0.54 vs the 2.5.1 that I think the code was written to so i'm not sure if the requests body's are different because of a bug in the code or because the api is different for the two versions.

The first big difference i saw is on the getHumidifierStatus call the code sets the two different method variables: { ... method: "devicedetail", "payload": { "method": "getHumidifierStatus", ... }

but the app i'm running does: { ... method: "bypassV2", "payload": { "method": "getHumidifierStatus", ... }

I didn't see a quick way to try to test changing that method.

I've only been looking at the request format I don't have a full set of packet captures at this time from the version of the app i have.

https://github.com/webdjoe-bot/pyvesync/pull/2

SandroGrzicic commented 3 years ago

I have tried the Levoit300S branch, however I have a problem when I call manager.update() - I think it's the same issue that obi11235 had:

PyDev console: starting.
Python 3.8.6 (tags/v3.8.6:db45529, Sep 23 2020, 15:52:53) [MSC v.1927 64 bit (AMD64)] on win32

from pyvesync import VeSync

manager = VeSync("<redacted>", "<redacted>")

manager.login()
True

manager.update()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "D:\_\HomeAssistant\pyvesync\src\pyvesync\vesync.py", line 285, in update
    device.update()
  File "D:\_\HomeAssistant\pyvesync\src\pyvesync\vesyncfan.py", line 360, in update
    self.get_details()
  File "D:\_\HomeAssistant\pyvesync\src\pyvesync\vesyncfan.py", line 344, in get_details
    inner_result = r.get('result', {}).get('result')
AttributeError: 'NoneType' object has no attribute 'get'

image

I had to make the fix in this pr linked below to make it work, but i think there is more wrong than that. I have a different version of the app, 3.0.54 vs the 2.5.1 that I think the code was written to so i'm not sure if the requests body's are different because of a bug in the code or because the api is different for the two versions.

The first big difference i saw is on the getHumidifierStatus call the code sets the two different method variables: { ... method: "devicedetail", "payload": { "method": "getHumidifierStatus", ... }

but the app i'm running does: { ... method: "bypassV2", "payload": { "method": "getHumidifierStatus", ... }

I think I can confirm that, by looking at my packet captures I can see the following HTTP request for getHumidifierStatus:

POST /cloud/v2/deviceManaged/bypassV2 HTTP/1.1 User-Agent: VeSync/VeSync 3.0.51(F5321;Android 8.0.0) Content-Type: application/json; charset=UTF-8 Content-Length: 476 Host: smartapi.vesync.com Connection: Keep-Alive Accept-Encoding: gzip

{"acceptLanguage":"en","accountID":"[redacted]","appVersion":"VeSync 3.0.51","cid":"[redacted]","configModule":"WiFiBTOnboardingNotify_AirHumidifier_Classic300S_US","deviceRegion":"US","phoneBrand":"F5321","phoneOS":"Android 8.0.0","timeZone":"[redacted]","token":"[redacted]","traceId":"[redacted]","method":"bypassV2","debugMode":false,"payload":{"method":"getHumidifierStatus","source":"APP","data":{}}}

Thanks!

obi11235 commented 3 years ago

I did some more testing with it, i think i got a clean fix for all of it. I tested all the functions and they seem to work for me. I have not tested that it collects the data correctly, but don't have time to look into that now.

This pr has all my changes in it https://github.com/webdjoe-bot/pyvesync/pull/2

webdjoe commented 3 years ago

Thank you @SandroGrzicic for testing and @obi11235 for your PR. I was hoping you could clear up a few things:

Does changing the changing the mist level via this library change the mode to manual?

Does the auto stop mode work correctly?

Should there be an off mode in the change mode method?


obi11235 commented 3 years ago

Changing the mist level via the library switches the unit to manual mode. Auto stop does flip the setting in the app.

there is no need for an off mode, turn_on and turn_off work. I tested this list of functions and they all seem to work:

manager.login()
manager.update()
for device in manager.fans:
    if device.device_type == "Classic300S":
        device.display()
        # device.turn_off()
        # device.turn_on()
        # device.set_humidity(40)
        # device.set_humidity_mode('auto')
        # device.automatic_stop_on()
        # device.automatic_stop_off()
        # device.set_mist_level(1)
obi11235 commented 3 years ago

I had a chance to test the data that was coming back in get_details method and updated my PR to get that to work now. So the data in self.details and self.config are correctly updated with the results from the api. To help debugging I added the display functions from the air filter in the same class and fixed a bug in the displayJSON() function on the filter since I have one to test it on.

SandroGrzicic commented 3 years ago

I can confirm @obi11235's branch works, I tried it with my 300S. A few notes:

So the only two app features that are currently "missing" are the:

Presumably these don't really need to be implemented, as nobody will presumably use them, since the timer or schedule functionality should be implemented in any automation software (such as HA). Therefore I would say that this is feature-complete. Thank you for all the work!

Schnup89 commented 3 years ago

Hi @webdjoe thank you for your library!

With the code for the Levoit Classic 300S the device status (displayed e.g. by displayJSON()) will only be updated on startup in the process_device function and then will never be updated again.

My quick'n'dirty fix:

vesyncfan.py - add the following lines on line 347:

r2, _ = Helpers.call_api(
            '/cloud/v1/deviceManaged/devices',
            'post',
            headers=Helpers.req_headers(self.manager),
            json=Helpers.req_body(self.manager, 'devicelist'),
        )
        if r2 and Helpers.code_check(r2):
            if 'result' in r2 and 'list' in r2['result']:
                 devices = r2['result']['list']
                 for dev in devices:
                       if dev["cid"] == self.cid:
                           self.device_status = dev["deviceStatus"]
formatBCE commented 3 years ago

Hi @webdjoe ! First of all - thank you for your work, it's amazing! Just wanted to ask, if there are plans to extend existing HA VeSync integration with this new functionality.

Thank you in advance!

webdjoe commented 3 years ago

I will do my best to get to it, but it will take some time. If anyone wants to share some work that they've already done, I will gladly contribute.

formatBCE commented 3 years ago

@webdjoe sorry for digging this out of grave, just wanted to paste link to related pull-request - both for you and those desperate people, who will come here seeking help. :) https://github.com/home-assistant/core/pull/49956

Not much done there, but proven to work, just needs polishing.