webdjoe / pyvesync

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

Add support for Levoit EverestAir™ Smart True HEPA Air Purifier if possible. #170

Closed wtstreetglow closed 4 months ago

wtstreetglow commented 1 year ago

Hello,

Would it be possible to add support for the subject air purifier to home assistant?

If so, what kinds of things would you need from myself. I do own the device but am not a programmer but trade.

Thank you for any insights you can give. It is very much appreciated.

webdjoe commented 1 year ago

Please follow directions here: https://github.com/webdjoe/pyvesync#feature-requests and post packet captures (in code blocks). Please ensure to redact your token, accountId, cid & uuid.

wtstreetglow commented 1 year ago

Thank you for this guidance. I will take care of this over the next week or so. Very much appreciate it!!Sent from my iPhoneOn Dec 26, 2022, at 2:43 PM, Joe Trabulsy @.***> wrote: Please follow directions here: https://github.com/webdjoe/pyvesync#feature-requests and post packet captures (in code blocks). Please ensure to redact your token, accountId, cid & uuid.

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you authored the thread.Message ID: @.***>

spencer-scott commented 1 year ago

I have the same air purifier. I tried to use Charles Proxy and I'm getting the same error when using the trusted certificate as in #177

webdjoe commented 1 year ago

I haven't tried this myself, but this is a new way to capture packets that's been proposed: #180

kasper79 commented 1 year ago

I just picked one of the EverestAirs up, does the Charles Proxy still work or no? If it does i'll buy the app (is there a free one?) and get the info needed, but looks like only Android method works now?

spencer-scott commented 1 year ago

Charles Proxy doesn't work for this. I have an android device, but I believe you have to have a rooted android device in order to perform the packet capture which I do not have nor do I know how to make a device rooted. Fingers crossed someone with a rooted android comes along.

jasonjhofmann commented 1 year ago

It worked for me on iOS using Charles Proxy for iOS. I have full captures and was attempting to develop support myself but was still familiarizing myself with the code base. Would be very happy to share the captures instead - I verified that they are complete.

On Tue, May 23 2023 at 7:28 AM, Spencer Reynolds < @.*** > wrote:

Charles Proxy doesn't work for this. I have an android device, but I believe you have to have a rooted android device in order to perform the packet capture which I do not have nor do I know how to make a device rooted. Fingers crossed someone with a rooted android comes along.

— Reply to this email directly, view it on GitHub ( https://github.com/webdjoe/pyvesync/issues/170#issuecomment-1559528770 ) , or unsubscribe ( https://github.com/notifications/unsubscribe-auth/AD3FRFE3RDHRT4P7HK5UR2DXHTCSPANCNFSM6AAAAAATA2IN3Q ). You are receiving this because you are subscribed to this thread. Message ID: <webdjoe/pyvesync/issues/170/1559528770 @ github. com>

spencer-scott commented 1 year ago

How did you get it to work if you don't mind me asking? I will share my captures if I can get it to work. In the meantime please do share your captures! I would really like to get this working in Home Assistant for the EverestAir.

webdjoe commented 1 year ago

Feel free to email me captures webdjoe at gmail . I will work on adding the device to the library

chipdarippa commented 1 year ago

Hi, all! I just recently picked one of these up and am interested in helping getting it integrated into HA. @webdjoe, did you ever receive those captures from @jasonjhofmann? If not, I can try to work on getting you what you need! Thanks!

webdjoe commented 1 year ago

This is next on the agenda, just finishing up with the Vital100S/200S

gpala7077 commented 1 year ago

Just picked this up myself too! Would love this to be integrated!

If there is something you need I can try to provide it.

webdjoe commented 1 year ago

No I haven't received any captures. The only way to capture is to install the vesync APK on an Android Emulator (like Android Studio) using a generic android OS version. Then using frida as shown in Sections 4,5 in Ultimate Guide. After SSL pinning is disabled, capture packets with SSL proxy with charles or some other software that installs a root certificate.

Check out this for setting frida up. I never used genymotion, only Android Studio which requires a generic android OS to root the device.

weberjam007 commented 1 year ago

No I haven't received any captures. The only way to capture is to install the vesync APK on an Android Emulator (like Android Studio) using a generic android OS version. Then using frida as shown in Sections 4,5 in Ultimate Guide. After SSL pinning is disabled, capture packets with SSL proxy with charles or some other software that installs a root certificate.

Check out this for setting frida up. I never used genymotion, only Android Studio which requires a generic android OS to root the device.

Hi @webdjoe, add me to the list of people with an EverestAir. I'm a former software engineer for a light control company so I have some experience with smart devices and am happy to help if it is still needed. I'll look at these instructions and try to get you the needed captures. Please let me know if you already have what you need.

chipdarippa commented 1 year ago

While we are awaiting captures, my app tells me that there is a firmware update available for my device. Of course I can't find a changelog or anything. Looking through the app menus I think that it's currently on firmware 1.0.06 and the update is 1.0.10. I would like to fix any issues that may be present with it, but without having a changelog...I also don't want to take the risk of locking it out from this workaround to integration...any thoughts?

webdjoe commented 1 year ago

@weberjam007 I haven't received any captures, happy to integrate if you are successful.

@chipdarippa Firmware updates should be fine, haven't seen any issues with this ever

M1K3SD1 commented 1 year ago

Hi folks, I heard you need help with captures, I might be able to help since my lab is still active for capturing other VeSync devices.

I will need someone to provide credentials for the device with a disposable email and approval for me to use it.

penguinglacier commented 1 year ago

@M1K3SD1 Thank you for offering to assist. I have setup a throwaway account and shared the device with that user (sharing should work..does not need to be the only/direct owner...correct?). Are you able to email me at pyvesync170@gmail.com and I will send you the VeSync password. Appreciate your assistance with the captures.

jasonjhofmann commented 1 year ago

@webdjoe Apologies for not replying sooner. My Charles Web Proxy iOS captures are from December, I haven't tried since. It's likely I got lucky (performed these captures before they implemented TLS cert pinning in the app).

The reason I got stuck and was unable to implement the changes myself, is that this isn't a straightforward change (as previous diffs for adding new model support would suggest).

For this model, they completely changed the request and response schemas. I don't understand this code well enough to implement such a large change.

I could not find any metadata in the deviceList Arrays (in any of the various API call responses FW, Room, etc)- that would allow one to determine which syntax to use for which device. Perhaps it's a hardcoded list in the app?

Compare the following:

Date of capture: December 27th, 2022 appVersion: VeSync 4.1.52 build4 POST URL for both: https://smartapi.vesync.com/cloud/v2/deviceManaged/bypassV2 method for both: getPurifierStatus

Model: Levoit Core 600S (WFON_APR_LAP-C601S-WUS_US)

Request

{
    "acceptLanguage": "en",
    "deviceRegion": "US",
    "appVersion": "VeSync 4.1.52 build4",
    "method": "bypassV2",
    "traceId": “1111111111111”,
    "timeZone": "America\/Los_Angeles",
    "configModule": "WFON_APR_LAP-C601S-WUS_US",
    "phoneOS": "iOS 16.0.2",
    "cid": “aaaa11a111111a1111aaaaa1a11a111a",
    "token": “xxxxxxxxxxx”,
    "debugMode": false,
    "accountID": “11111111”,
    "payload": {
        "data": {},
        "source": "APP",
        "method": "getPurifierStatus"
    },
    "userCountryCode": "US",
    "phoneBrand": "iPhone 14 Pro"
}

Response

{
    "traceId": "1111111111111",
    "code": 0,
    "msg": "request success",
    "module": null,
    "stacktrace": null,
    "result": {
        "traceId": "1111111111111",
        "code": 0,
        "result": {
            "enabled": true,
            "filter_life": 73,
            "mode": "manual",
            "level": 1,
            "air_quality": 1,
            "air_quality_value": 1,
            "display": true,
            "child_lock": false,
            "configuration": {
                "display": true,
                "display_forever": true,
                "display_usable": true,
                "light_detection": false,
                "auto_preference": {
                    "type": "default",
                    "room_size": 0
                }
            },
            "extension": {
                "schedule_count": 0,
                "timer_remain": 0,
                "efficient_mode_time_remain": 0,
                "eco_mode_run_time": 0
            },
            "device_error_code": 0
        }
    }
}

Model: Levoit EverestAir (VS_WFON_APR_LAP-EL551S-AUS_US)

Request

{
    "context": {
        "token": "xxxxxxxxxxx",
        "appVersion": "VeSync 4.1.52 build4",
        "userCountryCode": "US",
        "phoneOS": "iOS16.0.2",
        "acceptLanguage": "en",
        "timeZone": "America\/Los_Angeles",
        "traceId": "1111111111111",
        "phoneBrand": "iPhone 14 Pro",
        "method": "bypassV2",
        "debugMode": false,
        "accountID": "11111111"
    },
    "data": {
        "configModule": "VS_WFON_APR_LAP-EL551S-AUS_US",
        "cid": “bbbb11a111111a1111aaaaa1a11a111a",
        "payload": {
            "method": "getPurifierStatus",
            "data": {},
            "source": "APP"
        }
    }
}

Response

{
    "traceId": "1111111111111",
    "code": 0,
    "msg": "request success",
    "module": null,
    "stacktrace": null,
    "result": {
        "traceId": "1111111111111",
        "code": 0,
        "result": {
            "fanRotateAngle": 75,
            "filterOpenState": 0,
            "powerSwitch": 1,
            "filterLifePercent": 94,
            "workMode": "manual",
            "manualSpeedLevel": 1,
            "fanSpeedLevel": 1,
            "AQLevel": 1,
            "AQPercent": 98,
            "PM25": 3,
            "PM1": 1,
            "PM10": 5,
            "screenState": 0,
            "childLockSwitch": 0,
            "screenSwitch": 1,
            "lightDetectionSwitch": 1,
            "environmentLightState": 1,
            "autoPreference": {
                "autoPreferenceType": "default",
                "roomSize": 0
            },
            "routine": {
                "routineType": "normal",
                "runSeconds": 0
            },
            "scheduleCount": 0,
            "timerRemain": 0,
            "efficientModeTimeRemain": 0,
            "ecoModeRunTime": 0,
            "errorCode": 0
        }
    }
}
ptr727 commented 1 year ago

Just got an EverestAir, would like to see it in HA, subbing here for updates.

M1K3SD1 commented 1 year ago

@penguinglacier just sent you the file in the email :)

webdjoe commented 1 year ago

@M1K3SD1 @penguinglacier Thank you for getting this to me but for some reason the chlsx file that I received had the body JSON stripped from all of the responses. I looked through the chlsx file in a text editor and none of the calls have the response body

M1K3SD1 commented 1 year ago

Weird, sorry about that, let me recapture it.

M1K3SD1 commented 1 year ago

@webdjoe just sent you a new file, my bad. I was in a hurry and saved it wrong.

FYI @penguinglacier

webdjoe commented 1 year ago

Sorry for delay, please test out this branch. Should have most of the features except the vent rotate, that wasn't part of the captures.

M1K3SD1 commented 1 year ago

Anyone who has such a device is able to test the branch, please?

ptr727 commented 1 year ago

Can you provide test instructions, ideally to run in a VSCode dev container?

erictrudeau commented 1 year ago

Sure thing. Thanks for all the effort getting this updated @webdjoe!

Not sure if it's normal, but when running a command to change something, the device changes immediately, but the terminal doesn't return for about 5 seconds. - Edit: never mind, after power cycling the purifier this seems to have gone away.

Full disclosure, my two Everest Air purifiers are on firmware "1.0.06(1.0.09)". I've avoided updating them because I've wanted to try to dump the ESP32 firmware that run them. At least in the older firmware, the eFuses seemed to not be blown, so I was concerned an update may change that. Because of the older firmware, that may cause issues. Some things in the VeSync app like resetting the filter don't seem to work.

# unsure if they work ev_living.air_quality --> 0 (not sure what this should be. % on the screen is 96-99%) type(ev_living.current_firm_version) --> None ev_living.get_timer() --> not sure what this does, only update() refreshes the timer value ev_living.set_night_light --> this might work, but not sure what it does beyond enabling light detection ev_living.filter_life --> 0 (I haven't been able to reset mine with the app (see above))

Full details attached from everything I tested. There's also some instructions for getting this setup for anyone interested in testing. I'm on an older Intel Mac, with Python 3.10. everestair-testing.txt

Edit 2: Looking at .details, the air quality info there, so maybe it just needs to be mapped? air_quality_value, pm1, pm10, aq_percent

ev_living.details --> {'filter_life': 0, 'mode': 'manual', 'level': 0, 'display': False, 'child_lock': False, 'night_light': 'off', 'air_quality': 0, 'light_detection_switch': True, 'environment_light_state': False, 'screen_switch': True, 'air_quality_value': 6, 'pm1': 4, 'pm10': 8, 'aq_percent': 95, 'fan_rotate_angle': 0, 'filter_open_state': False, 'auto_preference_type': 'default', 'light_detection': True}

erictrudeau commented 1 year ago

@M1K3SD1 any chance you could do another capture while setting the angle? That would be a nice feature to have. Thanks!

webdjoe commented 1 year ago

Thank you for the detailed debug, it's a huge help. Regarding the firmware version, I usually notice an improvement on performance and consistency but I'm not sure how it affects the ability to flash the device. It's been awhile since I've messed with esp firmware but I would imagine that it would be tough to flash custom firmware and keep all of the functionality. It is a huge drawback that these are cloud based but at least vesync is doing well so I don't think they are going to disappear anytime soon like insteon.

M1K3SD1 commented 1 year ago

@erictrudeau sure, should I use the same creds?

webdjoe commented 1 year ago

Any updates on testing?

gpala7077 commented 1 year ago

Yes.

I've tested this branch with home assitant and Pyscript, I was able to integrate the code in a 'band-aid' style approach. I have full function of my Everest Air purifier. I have only been using the basic control functions.

Every function has worked as expected. I have found no errors.

Creating a manager instance. Successful. VeSync(user,pass, 'America/Chicago', debug=False, redact=True)

Logging in and updating all devices. Successful

manager.login()
manager.update()

Retrieving devices. Successful manager.fans Retrieving air quality details from the details dictionary. Successful fan.device_details

Controlling Device. Successful

fan.turn_off()
fan.turn_on()
fan.sleep_mode()
fan.manual_mode()
fan.turbo_mode()
fan.change_fan_speed(speed=speed)

Snippet of the code being utilized in my smart home instance. Successfully running.

@pyscript_executor
def login_vesync():
    try:
        manager = VeSync(
            load_secrets('vesync_email'),
            load_secrets('vesync_password'),
            "America/Chicago",
            debug=False,
            redact=True
        )

        manager.login()
        manager.update()

        return manager

    except Exception as e:
        return

def get_vesync_device(device_name, device_type='fan'):
    try:
        devices = {
            'fan': manager.fans,
        }

        for device in devices[device_type]:
            if device.device_name == device_name:
                return device

    except Exception as e:
        return

def exception_device_logic(pm2_5, fan_percentage, mode='manual'):
    try:
        current_mode = get_details('mode')

        # Set the fan speed based on PM2.5 value
        if fan_percentage == 0:
            response = exception_device_turn_off()
            return

        response = exception_device_turn_on()

        if mode in ['sleep', 'turbo']:
            if current_mode != 'sleep':
                response = exception_device_sleep_mode()

            elif current_mode != 'turbo':
                response = exception_device_turbo_mode()
            return

        response = exception_device_manual_node()  # Ensure the device is in manual mode

        if pm2_5 > 100:
            response = exception_device_turbo_mode()

        elif fan_percentage <= 33.33:
            response = exception_device_change_fan_speed(speed=1)

        elif fan_percentage <= 66.66:
            response = exception_device_change_fan_speed(speed=2)

        else:
            response = exception_device_change_fan_speed(speed=3)

        if not response:
            log_info(
                app=app_name,
                message=f"""
                        EVEREST PURIFIER RETURNED FALSE.

                        End of function. Something went wrong.
                        {response}
                        """,
                level='DEBUG',
                log_room='living_room',
                function='exception_device_logic'
            )

    except Exception as e:
        log.debug(f'Exception in exception_device_logic: {e}')

I had to wrap the functions so they could run in native python.

def exception_device_turn_off():
    return task.executor(exception_device.turn_off)

def exception_device_turn_on():
    return task.executor(exception_device.turn_on)

def exception_device_sleep_mode():
    return task.executor(exception_device.sleep_mode)

def exception_device_turn_on_display():
    return task.executor(exception_device.turn_on_display)
gpala7077 commented 1 year ago

I got it working with home assistant. I expanded your home assistant integration to include Everest Air Purifier.

image

saladm2 commented 1 year ago

I got it working with home assistant. I expanded your home assistant integration to include Everest Air Purifier.

image

Awesome! Glad to see this is coming along. How can I go about using this for my Everest? Or are we not there yet

gpala7077 commented 1 year ago

@saladm2 I'm not sure, I was just testing the everest branch @webdjoe had posted. It seems to be working. What I did was:

1) locally clone the everest branch into my HA instance 2) locally clone the vesync integration as a custom component. https://github.com/home-assistant/core/tree/dev/homeassistant/components/vesync

I took his already awesome home assistant integration and applied the everest branch to it. Expanded it to the Everest models. I'm getting the humidifiers in there too since I have those as well.

It would be up to @webdjoe I believe to Push the updates to the main branch in both this library and in home assistant. I'd be happy to help him out. I think I am getting a grasp on his code.

I am not associated with this repo. I'm am an eager enthusiast.

jasonjhofmann commented 10 months ago

Is this ready to be merged / pushed to main?

Lurick73 commented 7 months ago

Any update by chance on when this might hit the main branch?

saladm2 commented 5 months ago

Hi, I do not have any details - I'm not the author of the integration. I'm waiting for it to be updated as well

On Sun, Jun 2, 2024, 5:50 AM Vladimir Hirner @.***> wrote:

@saladm2 https://github.com/saladm2 I'm not sure, I was just testing the everest branch @webdjoe https://github.com/webdjoe had posted. It seems to be working. What I did was:

  1. locally clone the everest branch into my HA instance
  2. locally clone the vesync integration as a custom component. https://github.com/home-assistant/core/tree/dev/homeassistant/components/vesync

I took his already awesome home assistant integration and applied the everest branch to it. Expanded it to the Everest models. I'm getting the humidifiers in there too since I have those as well.

It would be up to @webdjoe https://github.com/webdjoe I believe to Push the updates to the main branch in both this library and in home assistant. I'd be happy to help him out. I think I am getting a grasp on his code.

I am not associated with this repo. I'm am an eager enthusiast.

Hi @saladm2 https://github.com/saladm2, what exactly you changed in vesync integration? I just copied everest branch into HA instance and thought its enough, but it's not unfortunately.

Any reason why these changes are still not merged to master?

— Reply to this email directly, view it on GitHub https://github.com/webdjoe/pyvesync/issues/170#issuecomment-2143778044, or unsubscribe https://github.com/notifications/unsubscribe-auth/APVF2WFJ6DLNESERHTUHMBTZFLTFBAVCNFSM6AAAAAATA2IN3SVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCNBTG43TQMBUGQ . You are receiving this because you were mentioned.Message ID: @.***>

HuffYk commented 5 months ago

Sorry, question was meant for @gpala7077 regarding what kind of changes were done to integration. But I was able to do it myself (adjusting core vesync integration) - any idea why is it still not merged to master?

image

HuffYk commented 5 months ago

@webdjoe could you please check https://github.com/webdjoe/pyvesync/compare/master...HuffYk:pyvesync:master ? I don't want to create pull-request as you already have there one for "everest-air" but I already tried to merge your master and everest-air branches, so please check it.

I created also fork of custom_vesync: https://github.com/HuffYk/custom_vesync which is using this pyvesync and works ok for Everest Air - adding screenshot

image

spencer-scott commented 4 months ago

@HuffYk can you detail what you did to get the Everest Air to work? I tried by adding your repo as a custom repo to HACS after uninstalling the standard Vesync integration and I'm still not seeing the Everest in the list after reinstalling the Integration after installing your custom repo. It's still picking up my Core Air Purifiers and my Humidifiers, just not the Everest. Thanks!

HuffYk commented 4 months ago

@HuffYk can you detail what you did to get the Everest Air to work? I tried by adding your repo as a custom repo to HACS after uninstalling the standard Vesync integration and I'm still not seeing the Everest in the list after reinstalling the Integration after installing your custom repo. It's still picking up my Core Air Purifiers and my Humidifiers, just not the Everest. Thanks!

Hi @spencer-scott , you need to manually update also pyvesync from my fork in HA docker image. Otherwise it will use official one which doesn't support Everest yet.

spencer-scott commented 4 months ago

@HuffYk that did it! Thank you! One note, I had to change the manifest.json to pyvesync==2.1.11 after I did the manual installation of your pyvesync, but afterwards the Everest showed up when adding the Integration and all available features are working. Thanks for you changes!

HuffYk commented 4 months ago

@HuffYk that did it! Thank you! One note, I had to change the manifest.json to pyvesync==2.1.11 after I did the manual installation of your pyvesync, but afterwards the Everest showed up when adding the Integration and all available features are working. Thanks for you changes!

I see, good to know :). I usually delete pycache folder, where pyvesync is installed and that works also after restarting HA.

webdjoe commented 4 months ago

238 Has been merged adding support for Everest. Apologies for the delay, life has been hectic. I am releasing to pypi now, V2.1.11

I anyone finds more bugs please open a new issue. Thank you to all who contributed

erictrudeau commented 3 months ago

So I noticed that @cdnninja bumped the pyvesync version in HA from 2.1.10 to 2.1.12, which was included in HA 2024.8.0. I've been counting down the days until the release but was bummed when the EverestAir still didn't show up. Decided to take matters into my own hands tonight figure out development for HA. I was able to get basic functionality added.. so things like on/off, all fan speeds, and PM2.5, but unfortunately it's fairly limited past that. So no PM1.0, PM10, display, vent angle, etc.

Looking at the screenshots in this thread made me realize someone else seemingly got all functionality working, but it seems that's a different code base than the main HA integration. Now that I know the basics maybe I'll poke around more and try to get it fully working, been dreaming of my purifiers being integrated since I got them. I know Python well, but had never tried to develop for HA until tonight. The community has actually made it quite easy to get going!

weberjam007 commented 2 months ago

I do see an error referencing the EverestAir in my logs. Not sure if that is related image

The source of the error is in fan.py from the main VeySync component.
Logger: homeassistant.components.vesync.fan
Source: components/vesync/fan.py:76
integration: VeSync (documentation, issues)
First occurred: 2:38:23 PM (1 occurrences)
Last logged: 2:38:23 PM

EverestAir - Unknown device type - LAP-EL551S-AUS

If the fix to include the EverestAir was another code base that was update as I believe @erictrudeau mentioned, then you can ignore this post.

weberjam007 commented 2 months ago

I just went to look at the code and saw an update from two days ago that likely addresses this. Maybe we are only a few days away from getting the fix delivered in a Home Assistant update. image

erictrudeau commented 2 months ago

Yes, it should be coming soon. Looks like the change was merged into the HA dev branch a few days ago.

Like I mentioned, this will only be basic functionality for now. The EverestAir has a lot of auxiliary settings and sensors that can be incorporated, but that's going to require more work and getting up to speed on how the VeSync integration does what it does.

I've been running the custom_vesync Integration mentioned above the past few weeks, but it's been fairly buggy when poking at it. I like how that codebase tries to detect what features a device supports rather than requiring them to be configured explicitly for each device. It's possible we can merge some of the EverestAir functionality into the built-in VeSync integration if @HuffYk's license and the integration maintainers are onboard. But at first look, the custom_vesync integration has diverged quite a bit from the built-in one.