jfarmer08 / homebridge-wyze-smart-home

Wyze Smart Home plugin for Homebridge.
MIT License
102 stars 17 forks source link

Thermostat Support #49

Closed carTloyal123 closed 1 year ago

carTloyal123 commented 1 year ago

Is your feature request related to a problem? Please describe:

It looks like there is some basic low level support for adding Thermostat support to this library. Is it possible to go ahead and add full support? Describe the solution you'd like:

Full Homekit support for Wyze Thermostat. Describe alternatives you've considered:

I am interested in writing my own plugin for Thermostat support but I think it makes more sense to build on this plugin to keep everything in one place. I will try to add a PR if I have time.

Let me know if this is possible and if help would be needed, thanks!

github-actions[bot] commented 1 year ago

Message that will be displayed on users' first issue

jfarmer08 commented 1 year ago

If you are really interested I have a lot more of the translation done. I have not checked that code in git and it's a mess.

carTloyal123 commented 1 year ago

The translation of the API calls you mean? That would be great. I have been developing using wyze-sdk from @shauntarves which includes thermostat support so perhaps some merging of knowledge would be useful to tackle this. I would be glad to help out if you are interested in putting your work on a dev branch or something to collaborate on

jfarmer08 commented 1 year ago

That is correct. I check my code in, but like I said its a mess currently. Feel free to take a look at it.

jfarmer08 commented 1 year ago

https://github.com/jfarmer08/homebridge-wyze-smart-home/tree/reModel

jfarmer08 commented 1 year ago

I have checkin more code today, that will hep with Thermostat support. Let me know if you need anything from me if you decided to work on this.

carTloyal123 commented 1 year ago

Thanks for sending that in. Yes I am slowly working my way through to add thermostat. Both as a learning exercise and since this would be useful to have in general. Will keep you updated

jfarmer08 commented 1 year ago

I took a look at your repo and it looks like you have a pretty good start on what it will take to add thermostat control into this addon. Just figuring out the calls that need to be made. If I had a thermostat I would help, but even without again let me know if you need something.

carTloyal123 commented 1 year ago

That sounds good. I just finished up a working version in my repo and will be porting that logic over to this project to consolidate it all. Thanks for the help!

garrlker commented 1 year ago

@carTloyal123 you are awesome. Can't wait to test out thermostat support

carTloyal123 commented 1 year ago

@jfarmer08 how much work in this remodel branch have you done to prepare for thermostat support? I see that you have some methods for thermostatGetIotProp. Is this tested and actually returns data from the wyze backend? I also see that you have methods to set modes on the thermostat in the WyzeAccessory base class, is any of this tested?

So far I have created an accessory class for the thermostat and done some basic setup to support it in the WyzeSmartHome class. You can see progress in my forked repo on the remodel branch. Thanks and excited to help!

jfarmer08 commented 1 year ago

@carTloyal123

I cannot fully test the methods since I don't have a Thermostat, but the calls are similar to the Wall Switch. The crypto and payload calls should be good in theory. The calls below should work for setting.

` async setPreset() { const response = await this.thermostatSetIotProp(this.mac, 'config_scenario', value) return response } // auto, on, off / / ['auto', 'circ', 'on'] async setFanMode() { const response = await this.thermostatSetIotProp(this.mac, 'fan_mode', value) return response } // auto, heat, cool async setHvacMode() { const response =await this.thermostatSetIotProp(this.mac, 'mode_sys', value) return response }

// heat stop point async setHeatPoint() { const response = await this.thermostatSetIotProp(this.mac, 'heat_sp', value) return response }

// Cool stop point async setCoolPoint() { const response = await this.thermostatSetIotProp(this.mac, 'cool_sp', value) return response }`

This should be able to return the state of if, but it still needs to be parsed out. Since I don't know the return async thermostatGetIotProp() { // Might need to copy wallSwitchGetIotProp = and store context const keys = 'trigger_off_val,emheat,temperature,humidity,time2temp_val,protect_time,mode_sys,heat_sp,cool_sp, current_scenario,config_scenario,temp_unit,fan_mode,iot_state,w_city_id,w_lat,w_lon,working_state, dev_hold,dev_holdtime,asw_hold,app_version,setup_state,wiring_logic_id,save_comfort_balance, kid_lock,calibrate_humidity,calibrate_temperature,fancirc_time,query_schedule' const response = await this.plugin.client.getIotProp(this.mac, keys) return response }

jfarmer08 commented 1 year ago

You can look at async wallSwitchGetIotProp() to see how I parsed it out. I am adding each part that could be used or needed to the accessory context. That way its a stored value, and then in WyzeThermostat you can just pull from there when it needs a update.

I am adding more items to context in hope start can be a little quicker with the information we already have. If you feel this approach is bad let me know.

carTloyal123 commented 1 year ago

All those methods look fine. They also line up with the python library information from @saurntarves repo that I was previously using. I have everything setup so I will do some testing today hopefully shortly and update you. I will push my changes in just a few minutes if you want to check them out

jfarmer08 commented 1 year ago

Sure I can take a look at it. Thanks for the work on this. I might not have one, but a lot of other do and this will help. There is so much cleanup to be done after this is finished. Instead of adding more features I might start doing some of the clean up.

I will also push my latest changes. Hopefully it does not mess you up to bad.

carTloyal123 commented 1 year ago

Sounds good, thanks!

Quick question, for some accessory implementations you use this device.device_params dictionary to update characteristics. Is this the raw device_params dictionary key that is returned from the Wyze api call?

jfarmer08 commented 1 year ago

yes it is, but for the Wall switch and Camera I have added more params from the getLockInfo and getIotProp.

carTloyal123 commented 1 year ago

Gotcha. So for the thermostat I can actually use the device.device_params.temperature in updateValue without having to do any custom property parsing since the data comes straight from wyze like that. That definitely makes life a little easier.

Just pushed my latest changes. I also merged in your remodel branch and everything looks to be okay still. Will update soon with tests

jfarmer08 commented 1 year ago

You can do it that way. I am starting to store everything in context so it can be reused. So say that someone turns a plug off we make a call to device properties to see the return. It then sends all the data down for all devices. If we store that data and then a check against time then we might not have to call it again for the next device. This will keep the calls down and make sure we are not hitting the api to hard and get into a bad place with wyze.

Would post the response of the thermostat device property and the response of getthermostatIotProp

It would help me plan for what needs to be done.

carTloyal123 commented 1 year ago

Yeah that makes sense. I will post the output as soon as I get my testing up and running. Been working through the onSet functions for the thermostat using what you have so far

carTloyal123 commented 1 year ago
{
                "mac": "CO_EA1_41374c391138323331473846",
                "first_activation_ts": 1661549846000,
                "first_binding_ts": 1661549846000,
                "enr": "",
                "nickname": "Thermostat",
                "timezone_name": "America/Chicago",
                "product_model": "CO_EA1",
                "product_model_logo_url": "",
                "product_type": "Thermostat",
                "hardware_ver": "0.0.0.0",
                "firmware_ver": "1.1.8",
                "user_role": 2,
                "binding_user_nickname": "email@gmail.com",
                "conn_state": 1,
                "conn_state_ts": 1671161039574,
                "push_switch": 1,
                "device_params": {},
                "is_in_auto": 0,
                "event_master_switch": 1,
                "parent_device_mac": "",
                "parent_device_enr": "",
                "binding_ts": 1661554636000,
                "timezone_gmt_offset": -6
            },

This is the thermostat portion in API Response, getting an error trying to print the getThermostatIotProp. Will update shortly

jfarmer08 commented 1 year ago

So there is no device properties that come back with it. That relate to temperature

carTloyal123 commented 1 year ago

It looks like there is one more layer of api calls to get the device states based on the library I was using previously. Let me see if I can find anything special

jfarmer08 commented 1 year ago

You need to change the thermostatGetIotProp call to something like below. Then use If statements or Switch Cases to return the response and store the context. Example of setting the context. this.homeKitAccessory.context.device_params.temperature= properties[prop]

You can use the Wall Switch an example on how I use the context. It's easy, just calling this.temperature in the WyzeThermostat class would pull the temperature context that you set in the example above.

async thermostatGetIotProp() { // Might need to copy wallSwitchGetIotProp = and store context const keys = 'trigger_off_val,emheat,temperature,humidity,time2temp_val,protect_time,mode_sys,heat_sp,cool_sp, current_scenario,config_scenario,temp_unit,fan_mode,iot_state,w_city_id,w_lat,w_lon,working_state, dev_hold,dev_holdtime,asw_hold,app_version,setup_state,wiring_logic_id,save_comfort_balance, kid_lock,calibrate_humidity,calibrate_temperature,fancirc_time,query_schedule' let response try { this.updating = true response = await this.plugin.client.getIotProp(this.mac, keys) let properties = response.data.props const prop_key = Object.keys(properties); for (const element of prop_key) { const prop = element; console.log(prop) } this.lastTimestamp = response.ts } finally { this.updating = false return response } }

carTloyal123 commented 1 year ago

I will see if that helps. It still seems odd though that none of the desired data is in the api response. Is that correct or is there more here?

Oh so instead of storing the data inside the body of the response they actually store it inside the http props associated with the request, is this more accurate?

jfarmer08 commented 1 year ago

I didn't expect the getObjectList() to return anything important for the thermostat, but the thermostatGetIotProp() should contain what you need. Its calling getIotProp(this.mac, keys) from the WyzeApi.js (renamed to index.js) in the Wyze-Api folder. This is the same call that the Wall Switch is using so I know the base call is working.

carTloyal123 commented 1 year ago

Gotcha, it looks like there are no props associated with a call to the thermostat which does not seem right. Getting a

Cannot read properties of null (reading 'props')

Referring to let properties = response.data.props

It looks like the standard getIotProp(mac, keys) does not work with the thermostat. I thought there was a method called get thermostatIotProp that had a special api call similar to how the lock uses getLockInfo

(Update: will continue to debug tomorrow)

I've pushed all my work to my remodel branch in my forked repo if you want to take a look and see if something looks off.

carTloyal123 commented 1 year ago

So it appears that the getIotProp does not like the thermostat information.

      response = await this.plugin.client.getIotProp(this.mac, keys)
      this.plugin.log.debug("[DEBUG Thermostat] " + JSON.stringify(response))

Produces the following json response:

{
    "code": 1008,
    "current": 0,
    "data": null,
    "hash": "1",
    "instance_id": "ae4e8da52118416f81a7a96693bc96b8",
    "isShowToast": 0,
    "message": "Data is not found",
    "toastMsg": null,
    "total": 0,
    "version": 1
}

Obviously Data is not found does not seem like a good sign here. I am digging into the calls made by the other library to compare notes

Update: looks like it is just a url change and that should do it for the getIotProp so just going to have another method to use for the earth service calls. The url was correct for the thermostateSetIotProp. Looks like the call is working now so I can add a context manager to set device properties just like the wall switch.

garrlker commented 1 year ago

If needed, I have a Wyze thermostat I can test these changes with.

carTloyal123 commented 1 year ago

Hey @garrlker thanks for your support! Thankfully I have one as well which makes it pretty painless to test stuff out. I have a working version that I need to do a little testing on then we can see about distributing it to you or others interested.

@jfarmer08 I think I have all the parts in place. Please review the latest push to my remodel branch when you get a chance and see what we need to clean up before a release of some kind.

carTloyal123 commented 1 year ago

Looks like getting data from wyze and setting in Homekit works well. But sending updates from Homekit to wyze needs some work on the API calls. I will keep looking at it later today but all updates are pushed. You can receive your thermostat states just fine in Homekit but when you try to change the heating or cooling setpoints, the api call response is a 400 error.

{        
        "current": 0,
        "data": null,
        "hash": "1",
        "instance_id": "b8dc8c2332ba49c4baf5f07e326d0b18",
        "isShowToast": 0,
        "message": "receive device model is error, please check",
        "toastMsg": null,       
         "total": 0,
         "version": 1
}

Looks like some params of the call need to be adjusted but shouldn't be too bad.
carTloyal123 commented 1 year ago

It looks like I have everything basically working functionally but I am running into one production issue. When I change a value in my homekit app, the value is sent to wyze as expected but then a few moments later in Homekit the thermostat says no response and reverts back to whatever the state was before I changed anything. Its like the state in the plugin is not syncing correctly with the real data. As far as why the thing becomes unresponsive, I have no idea. Its like homekit is asking for data from homebridge but homebridge is not saying anything. Very odd.

jfarmer08 commented 1 year ago

So I have seen this, when you set the value of something Homekit will call get update. And if your update is not there, it will assume the device is no longer online. So you might need a delay in the callback. So you could test this by adding in a delay>await this.sleep(250) in your set function.

jfarmer08 commented 1 year ago

Later on tonight, I can take a look at the code later. I need to finish my changes to the camera support before I can push out a new Beta Release. I have made this branch so messing by adding features all over the place. It might make more sense to cherry-pick what is needed and create a new branch. Not Sure.

carTloyal123 commented 1 year ago

Sounds good. Let me know what you think. I think I actually know what's going on with the strange timeout and no response. I was following your implementation of the humidity sensor I think and didn't actually assign any getters for the thermostat characteristics so that's exactly what's wrong. HomeKit is saying hey run the getter for this property but those getters don't exist. It only seems like it is working because every update interval each characteristic runs updateValue. I'll restructure hopefully this weekend at some point or feel free to grab any relevant parts but I consider this not worth even being a beta until one of us can rework it.

jfarmer08 commented 1 year ago

It might be good to adopt this page. https://developers.homebridge.io/#/service/Thermostat. I have some code in the WyzeCamera.js.

carTloyal123 commented 1 year ago

I followed that page for my original implementation and I think it flows nicely. I was just trying to stay with the style that you had used for some of the other devices. I'll refactor it in the next few days or if you create a new branch to cherry pick features ping me and I'll start fresh there.

jfarmer08 commented 1 year ago

Keep in mind that I didn't create this project. I just decided to take it over since it was not being maintained and I wanted new features. So I am changing things as I see fit. So far I have converted lock and wall switch over from the examples.

jfarmer08 commented 1 year ago

How is this going?

carTloyal123 commented 1 year ago

It's going well. Apologies as I basically only have time to work on the weekends. I have copied over my previous implementation which followed the example structure to this project on a local branch. It isn't quite done but I will finish the copy and push it up tomorrow. All the features are done, just copying and adjusting as needed since my old implementation needs the new Wyze api calls. Will update this thread tomorrow.

jfarmer08 commented 1 year ago

Sounds good. I plan on merging master into the dev branch. Then I will create a pull request on dev for my switch changes. After that is finished if you could clone dev and then create a branch off that. Then add only what is needed for the thermostat on your new branch. After you confirmed everything is working then you can create a pull request into dev from your branch. This will help keep things cleaner. I should have been following that same process, but I was the only one working on it.

I will start using dev as the beta branch. After beta is confirmed working for users then it can be merged into release.

The current rebase branch is out of control with changes. So far it has the wall switch, HMS, more camera features like flood light and spotlight control, and so many more clean up and just pure changes to the current design. It's kinda if all over the place. I took the next 2 weeks off from my normal job and plan on having a big beta release on January 1.

carTloyal123 commented 1 year ago

I just made a pull request to the remodel branch which soon seems to be irrelevant but wanted to get my messy changes into that branch for safe keeping. I will pull the updated dev branch once that is ready for updates and do thermostat work there instead. Current status of thermostat is that all functionality works well and the code is much cleaner than what I had done originally so feel free to take a look and let me know what you think. After starting from scratch it looks like the weird desync/no-response problem has gone away but needs more test time to confirm.

carTloyal123 commented 1 year ago

See PR: #56

jfarmer08 commented 1 year ago

I pulled the PR into the rebase branch. I will push the sdk into the dev branch in the next day or two. Then you can submit a pr against dev for a beta version. I don't have a thermostat to do any testing. I am thinking about pulling more functions into the sdk to allow for more actions.

jfarmer08 commented 1 year ago

@carTloyal123 so I updated the dev branch and push my changes for the wall switch. I ask that you will create a new branch for the thermostat and create a pull request into dev. I will push dev into the beta branch and create a release.

carTloyal123 commented 1 year ago

@jfarmer08 apolgies as I have been out of town away from my workstation. I will have some time tomorrow to get the thermostat changes put into the new branch then I'll get it into a PR for review and testing on dev.

jfarmer08 commented 1 year ago

@jfarmer08 apolgies as I have been out of town away from my workstation. I will have some time tomorrow to get the thermostat changes put into the new branch then I'll get it into a PR for review and testing on dev.

No problem at all.

jfarmer08 commented 1 year ago

https://www.npmjs.com/package/homebridge-wyze-smart-home/v/0.5.25-dev.0

@carTloyal123 Dev Release with supporting Thermostat

carTloyal123 commented 1 year ago

@garrlker feel free to test out thermostat support using the Dev release. Just warning that it will be a bit rocky but any feedback would be appreciated! Go ahead and add new issues if you find anything and we will close this one since support has been added.