shauntarves / wyze-sdk Goto Github PK
View Code? Open in Web Editor NEWA modern Python client for controlling Wyze devices.
License: The Unlicense
A modern Python client for controlling Wyze devices.
License: The Unlicense
Unknown device type detected - Lock Gateway spews immediately after call to client.devices_list()
I’m new to python and don’t know what (if anything) I can do to fix or work around these issues.
This has been working for quite a while without any changes. A few weeks ago it started failing and I haven't changed a thing.
This is basically the same code as the example code from the README pages.
I am able to log onto the wyze app with the same login.
/usr/local/bin/python3.9 /Users/tcarlin/PycharmProjects/wyze-sdk/wyze_devices.py
Traceback (most recent call last):
File "/Users/tcarlin/PycharmProjects/wyze-sdk/wyze_devices.py", line 7, in
client = Client(email=os.environ['WYZE_EMAIL'], password=os.environ['WYZE_PASSWORD'])
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/wyze_sdk/api/client.py", line 59, in init
self.login()
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/wyze_sdk/api/client.py", line 139, in login
response = self._auth_client().user_login(email=self._email, password=self._password, totp_key=self._totp_key)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/wyze_sdk/service/auth_service.py", line 77, in user_login
if response['access_token']:
TypeError: 'NoneType' object is not subscriptable
Is there a way to turn the thermostat fan on/off independent of heating? The only choices appear to be AUTO, CYCLE, ON.
ON appears straight forward.
AUTO I assume means something like, "let the furnace decide" or "turn on when heating, possibly after a delay."
CYCLE I'm guessing is "run for X minutes hourly.
I haven't discovered (is it possible?) to turn the fan on/off independently of heating or cooling.
Thank you very much for your work on this project; it's great!
Seems something changed because I have an automation that log in automatically in my account and it stopped working. The password is fine and I can use the Wyze application as normal. Probably an API changed?
Hello
I m trying to get the get_records working for the scale. when i call the function it seems it doesn't take into account the start_time, i can put anything the result is always the same if provides all the records. When i enter the end_time with it i get and error as bellow :
Traceback (most recent call last):
File "./scale.py", line 23, in
print(f"weight: {client.scales.get_records(start_time='2021,12,10',end_time='2021,12,17')}")
File "/home/efreeze/.local/lib/python3.8/site-packages/wyze_sdk/api/devices/scales.py", line 89, in get_records
return [ScaleRecord(**record) for record in super()._scale_client().get_records(user_id=user_id if user_id is not None else self._user_id, start_time=start_time, end_time=end_time)["data"]]
File "/home/efreeze/.local/lib/python3.8/site-packages/wyze_sdk/service/scale_service.py", line 166, in get_records
kwargs.update({'start_time': str(0), 'end_time': str(datetime_to_epoch(end_time))})
File "/home/efreeze/.local/lib/python3.8/site-packages/wyze_sdk/models/init.py", line 20, in datetime_to_epoch
return int(datetime.timestamp() * 1000)
AttributeError: 'str' object has no attribute 'timestamp'
Any idea on what am i doing wrong ? it seems maybe that
Greetings,
I recently picked up a few Wyze light strips (regular and pro) and am hoping to see support for these in the future. I would be happy to help develop and/or test.
I also have the battery powered outdoor cams, doorbell, cam pan v2 and a Wyze car. If support for any of these are in the roadmap please let me know how I can help out.
Email: anthony "at" stabile "dot" com
Thanks again Shaun for developing this superb module.
Cheers!
Specifically heart rate monitor data, I'm trying to bake that into a bot that'll tell me when my BPM is spiking instead of...I guess buying a cuff / alarm like a normal person. I looked around but didn't see a way to access it in here, sorry if I missed it!
Traceback (most recent call last):
File "/Users/tcarlin/PycharmProjects/wyze-sdk/wyze_cameras.py", line 17, in <module>
print(f'motion: {camera.has_motion}')
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/wyze_sdk/models/devices/base.py", line 454, in has_motion
return self.motion_state
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/wyze_sdk/models/devices/base.py", line 458, in motion_state
return False if self._motion_state is None else self._motion_state.value
AttributeError: 'Camera' object has no attribute '_motion_state'
I am trying to install your SDK on my RPi and it fails, saying it can't find the distribution. The same command
works fine on my Mac.
python3 --version = 3.7.3 on Rpi, = 3.8.3 on Mac
Using the following command: python3 -m pip install wyze-sdk
I also tried: sudo python3 -m pip install wyze-sdk
and got the same results. The documentation indicates that using "wyze_sdk" for the distribution name would work and it does not.
Any suggestions?
I haven't been able to find anything in the provided bulb info that indicates whether a mesh bulb is currently in color mode or white mode. Wondering if there's anything the bulb can give you that would allow you to expose it via the API? It's something that would be useful to know for my application, if it's possible to do.
Is there a way to access the Rules used with a device? I've been searching and not finding any methods which access rules or properties of devices where rules are specified.
Rather than returning None
for any devices we don't explicitly support, let's return a generic Device
object
I am trying to get images/videos from the camera events, and I cannot get it to work. I am able to get to the EventFile list, and I see image and video files associated with events. The EventFile objects have a URL, but when I try to access the URL (in S3), I get the error:
The request signature we calculated does not match the signature you provided.
Looks like the URL has a signature embedded, but the signature does not work. I just played with the Beta version of the Wyze web view, and in debug mode I can get the URLs for the same images with the correct signature. So looks like the response this API is getting is with a signature that for some reason does not work.
Question is - what needs to change to get a proper signature?
File "/Users/allenfarmer/Library/Python/3.9/lib/python/site-packages/wyze_sdk/api/client.py", line 181, in devices_list
return [DeviceParser.parse(device) for device in self._api_client().get_object_list()["data"]["device_list"]]
File "/Users/allenfarmer/Library/Python/3.9/lib/python/site-packages/wyze_sdk/api/client.py", line 181, in <listcomp>
return [DeviceParser.parse(device) for device in self._api_client().get_object_list()["data"]["device_list"]]
File "/Users/allenfarmer/Library/Python/3.9/lib/python/site-packages/wyze_sdk/models/devices/__init__.py", line 52, in parse
return Thermostat(**device)
File "/Users/allenfarmer/Library/Python/3.9/lib/python/site-packages/wyze_sdk/models/devices/thermostats.py", line 203, in __init__
self.fan_mode = super()._extract_property(ThermostatProps.fan_mode, others)
File "/Users/allenfarmer/Library/Python/3.9/lib/python/site-packages/wyze_sdk/models/devices/thermostats.py", line 224, in fan_mode
self._fan_mode = ThermostatFanMode.parse(value.value)
AttributeError: 'NoneType' object has no attribute 'value'
I do not see any way to connect and interact with the base station that is included with the Wyze Outdoor Cam.
I'm seeing a behavior I can't explain. (Spoiler alert: by the end of this post I think I've explained it)
ThermostatFanMode.ON
therm = client.thermostats.info(device_mac='CO_EA1_3130XXXXX')
print(therm.fan_mode)
AUTO
:client = Client(email='XXXX', password='XXXX')
client.thermostats.set_fan_mode(
device_mac='CO_EA1_31304635103637394a414c5b',
device_model='CO_EA1',
fan_mode=ThermostatFanMode.AUTO
)
ThermostatFanMode.AUTO
therm = client.thermostats.info(device_mac='CO_EA1_3130XXXXX')
print(therm.fan_mode)
All of that makes sense. What doesn't make sense is:
client.thermostats.info
show the right status, the app shows the right status, and the unit on the wall shows the right status.How the heck is the unit on the wall not updating from this wyze-sdk, but is updating from the app?
I set up a proxy to see what was going on.
Here's a request from the wyze-sdk via set_fan_mode()
:
| POST /plugin/earth/set_iot_prop HTTP/1.1
-- | --
Host | wyze-earth-service.wyzecam.com
User-Agent | okhttp/4.7.2
Accept-Encoding | gzip
Accept | */*
Connection | keep-alive
appid | 9319141212m2ik
appinfo | wyze_android_2.19.14
phoneid | XXXX
access_token | XXXXXX==
requestid | ea37483189b62b407c2ea0f9251c7e0c
signature2 | ebf081f4a8f63431a1dd7bac3fa1d26a
Content-Length | 128
Content-Type | application/json
{
"did": "CO_EA1_3130XXXX",
"model": "CO_EA1",
"props": {
"fan_mode": "auto"
},
"is_sub_device": 0,
"nonce": "1620348740786"
}
and here's a request from my iphone app:
:method | POST
-- | --
:scheme | https
:path | /plugin/earth/set_iot_prop_by_topic
:authority | wyze-earth-service.wyzecam.com
content-type | application/json
appid | XXXX
appinfo | wyze_ios_2.19.24
accept | */*
requestid | 7defc485b1315390b1a95445f78b276f
accept-language | en-US;q=1, el-US;q=0.9
accept-encoding | gzip, deflate, br
content-length | 128
user-agent | Wyze/2.19.24 (iPhone; iOS 14.4.2; Scale/3.00)
signature2 | bd6e10ca3b7f9968d547fed975b1ae03
phoneid | XXXX
access_token | XXXX==
{
"did": "CO_EA1_3130XXXX",
"nonce": "1620349892154",
"props": {
"fan_mode": "auto"
},
"model": "CO_EA1",
"is_sub_device": 0
}
The big difference I see here is the endpoint set_iot_prop_by_topic
vs set_iot_prop
. It looks like when setting one topic, the two endpoints accept the same signature of props. So I updated my locally running wyze-sdk to use set_iot_prop_by_topic
instead, and voila! It updates my wall unit.
If I had to guess, maybe the set_iot_prop
endpoint has a bug where it updates the state server side, which works for any clients that are polling, but doesn't push out any updates to the actual wall unit.
Not sure if you want to update all instances of set_iot_prop
to set_iot_prop_by_topic
, or just wait around for Wyze to maybe fix their endpoint.
Version.py references 1.2.1, not, 1.2.2
Everytime I connect to the Wyze service I get hit with the Enter Wyze SMS 2FA Verification Code, which requires manually entereing the code sent via SMS, into the running app.
Is there a way to either disable the 2FA requirment or maybe create an authentication certificate to avoid this?
Is that possible?
Hello,
I'm looking to control the pincode for the door lock over the web. I want to be able to automate the creation and removal of pins programmatically.
Thank you.
I'm exploring options to control the Wyze Car using a raspberryPI running between the Camera using the RTSP firmware and Car. Looking into what options are available what insight or suggestions you might have to offer based on your experience
Unknown device type detected ({'mac': 'x', 'first_activation_ts': x, 'first_binding_ts': x, 'enr': 'x', 'nickname': 'Outdoor Base 2 Back', 'timezone_name': 'America/New_York', 'product_model': 'WVODB1', 'product_model_logo_url': 'https://s3-us-west-2.amazonaws.com/wyze-file/system-logo/device/small/wyze_icon_device_wvodb1.png', 'product_type': 'BaseStation', 'hardware_ver': '0.0.0.2', 'firmware_ver': '4.16.1.22', 'user_role': 1, 'binding_user_nickname': '[email protected]', 'conn_state': 1, 'conn_state_ts': x, 'push_switch': 1, 'device_params': {'p2p_id': 'x', 'p2p_type': 3, 'ssid': '', 'ip': '192.168.123.161', 'power_switch': 1, 'temperature': '0', 'humidity': '0', 'temp_humi_room_type': 1, 'comfort_standard_level': 2, 'is_temperature_humidity': '0', 'records_event_switch': 0, 'motion_alarm_switch': 1, 'audio_alarm_switch': 0, 'smoke_alarm_switch': 0, 'co_alarm_switch': 0, 'electricity': '100', 'battery_charging_status': '0', 'is_link_toy_car': 0, 'power_saving_mode_switch': 0, 'camera_thumbnails': {'thumbnails_url': None, 'thumbnails_ts': 0}}, 'is_in_auto': 0, 'event_master_switch': 1, 'parent_device_mac': '', 'parent_device_enr': '', 'binding_ts': x, 'timezone_gmt_offset': -4.0})
Here's the error:
Traceback (most recent call last):
File "wyze.py", line 51, in <module>
main()
File "wyze.py", line 10, in main
get_devices()
File "wyze.py", line 21, in get_devices
test_bulb(device.mac)
File "wyze.py", line 36, in test_bulb
client.bulbs.set_color(device_mac=bulb.mac, device_model=bulb.product.model, color='ff00ff')
File "/Users/torgo/.pyenv/versions/3.8.9/lib/python3.8/site-packages/wyze_sdk/api/devices/bulbs.py", line 178, in set_color
prop_def = BulbProps.color()()
TypeError: 'PropDef' object is not callable
And the code:
import os
from dotenv import load_dotenv
from wyze_sdk import Client
from wyze_sdk.errors import WyzeApiError
load_dotenv()
client = Client(email=os.environ['WYZE_EMAIL'], password=os.environ['WYZE_PASSWORD'])
def main() :
get_devices()
def get_devices() :
try:
response = client.devices_list()
for device in client.devices_list():
print(f"nickname: {device.nickname}")
test_bulb(device.mac)
except WyzeApiError as e:
# You will get a WyzeApiError is the request failed
print(f"Got an error: {e}")
def test_bulb(mac) :
try:
bulb = client.bulbs.info(device_mac=mac)
print(f"power: {bulb.is_on}")
print(f"online: {bulb.is_online}")
print(f"brightness: {bulb.brightness}")
print(f"temp: {bulb.color_temp}")
print(f"color: {bulb.color}")
client.bulbs.set_brightness(device_mac=bulb.mac, device_model=bulb.product.model, brightness=100)
client.bulbs.set_color(device_mac=bulb.mac, device_model=bulb.product.model, color='ff00ff') # <-- ERROR ON THIS LINE
# client.bulbs.set_color_temp(device_mac=bulb.mac, device_model=bulb.product.model, color_temp=4400)
except WyzeApiError as e:
# You will get a WyzeApiError is the request failed
print(f"Got an error: {e}")
if __name__ == "__main__" :
main()
Tried it on python 3.8.9 and 3.9.4, getting the same error on both. Devices are all WLPA19C color bulbs, all on firmware version 1.2.0.339. Bulbs are all online and turned on. When I comment out the set_color line and uncomment set_color_temp, the latter works just fine. No errors and it does what it's supposed to.
Not sure if it's a bug or if I'm missing something obvious...
@shauntarves this a really notable Python package!
Any plan for adding push notification from the events?
Just one more question. Sorry to bother you, I am guessing when the document methods link is up again I won't be a pest (LOL). Is there any way to get the charge state of a vacuum's battery charge level from the device method? I did find some other related properties on the internet (ie IP address) but nothing in detail. Thanks
Will
Home Assistant defaults to python 3.8, and there's no good reason we shouldn't be able to run on that version as well.
vacuum.voltage = 100 on 1.0.1
vacuum.voltage = None on 1.0.2
I've looked through the changes but didn't see anything that would stand out as causing the issue.
More of a feature request, but if the vacuum returns to the dock to charge during a cleaning, there doesn't appear to be a way to cancel that cleaning via wyze-sdk. If dock is called, it's already docked so nothing is done. In the app, if you hit stop, it will cancel the cleaning routine (i.e. it won't pick back up where it left off after reaching 20% charge). Hoping this is something that can be added.
Several lines show client.plug
instead of client.plugs
To have better support, adoption, and contribution, it would be nice to have a set of unit tests to work against.
tox
pytest
I am looking to add the vacuum support provided here into https://github.com/RMCob/homebridge-wyze-connected-home-op and wanted to check if anyone was already working on that. Thanks in advance for any replies...
Current Wzye Device Groups:
Maybe it makes more sense to raise a WyzeFeatureNotSupportedError
Would be nice to have family_member_id and measure_ts also.
The family_member_id is a key into the profile record. This will provide the name of the family member that recorded their weight. measure_ts is the date of the event. Will be useful for plotting a history of. weights.
Traceback (most recent call last):
File “/Users/tcarlin/PycharmProjects/wyze-sdk/wyze_devices.py”, line 9, in <module>
response = client.devices_list()
File “/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/wyze_sdk/api/client.py”, line 181, in devices_list
return [DeviceParser.parse(device) for device in self._api_client().get_object_list()[“data”][“device_list”]]
File “/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/wyze_sdk/api/client.py”, line 181, in <listcomp>
return [DeviceParser.parse(device) for device in self._api_client().get_object_list()[“data”][“device_list”]]
File “/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/wyze_sdk/models/devices/__init__.py”, line 40, in parse
return LockGateway(**device)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/wyze_sdk/models/devices/locks.py", line 314, in __init__
self.locks = locks if locks is not None else super()._extract_attribute('locks', others)
File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/wyze_sdk/models/devices/locks.py", line 327, in locks
self._locks = [Lock(**lock) for lock in value]
TypeError: 'NoneType' object is not iterable
These are in the device object but don't make it to the API data
These attributes are very useful to track wifi coverage issues.
When I do this call, the Outdoor Cam (battery operated) doesn't show up.
client = Client(email=os.environ['WYZE_EMAIL'], password=os.environ['WYZE_PASSWORD'])
try:
cameras = client.cameras.list()
for camera in cameras:
print('{} {}'.format(camera.nickname, camera.mac))
could not cast value `` into expected type <class 'int'>
could not cast value `` into expected type <class 'int'>
could not cast value `` into expected type <class 'int'>
could not cast value `` into expected type <class 'int'>
Garage Cam V3 7C78B224A99F
Back Yard Cam 2CAA8EE7ABB8
Office Cam 2CAA8E08992C
Test Cam A4DA22301020
When I do this call, it returns None.
camera = client.cameras.info(device_mac='2CAA8E08992C')
I verified that this camera is available to the Wyze app and I verified that the Mac address is correct.
Everything works for the other four cameras.
Also the lines "could not cast value....." are coming from wyze-sdk not my code.
wyze-sdk is the latest version as of yesterday using "pip3"
I am pretty sure it worked fine before the upgrade. Not 100% sure. I know those "could not cast value...." lines are new as of the latest release.
To try and keep on the good side of Wyze, the sdk should implement caching on the api "get" calls (where states are not changing, etc), and add args to methods to override cache (global override for cache time/disabling cache, as well as per method calls). Could even add caching to "set" calls if the args/props (device, color, etc) are the same as well with short expiry. I am having a hard time thinking of a scenario where one would need to send the exact same "set" type request to the same device, within a timespan of say, 1 second. Could even experiment with increasing the defaults overtime as behavior becomes more apparent.
There are several methods for doing this (decorators, inline, etc), as well as existing modules/libraries from pypi that could be used.
Every attempt that can be made to allow the SDK to naturally be kind to the API (even if the SDK is poorly implemented by the end user), should be taken with best efforts. I don't think Wyze is naturally against projects like this, but these kind of implementations can go a long way into garnering "unofficial official" support.
Hey! really excited to see more progress on Wyze reverse engineering.
I would love to use wyze-sdk
instead of my own code as the first step in kroo/wyzecam (which I just pushed live, btw!), but I need to hit a slightly different get_user_info
endpoint, as newer versions of the Wyze firmware require a 'open_user_id' property only provided by https://api.wyzecam.com/app/user/get_user_info
, and not https://wyze-platform-service.wyzecam.com/app/v2/platform/get_user_profile
, from what I can tell. I haven't had a huge amount of time to read through the wyze-sdk code yet–but would love to remove the Web API calls from wyzecam, and just point to wyze-sdk
for that.
Congrats on the release!
Creating an issue for tracking purposes, but would be nice to have a sprinkler client added.
Hi I downloaded the python sdk as per instruction when I execute the routine to get a list of devices it looks like I am having an authentication issue. The error is as follows:
Traceback (most recent call last):
File "P:\wyze2.py", line 5, in
client = Client(email=os.environ['[email protected]'], password=os.environ['xxxx'])
File "c:\python39\lib\os.py", line 679, in getitem
raise KeyError(key) from None
KeyError: '[email protected]
I am using my wyze credentials for email=os.environ and password=os.environ
Any thoughts
Thanks
Will
Hi,
Is there a way to get a live video stream from a camera?
John
When we set thermostat lock state using the API it goes through fine and updates the key in info as well but same does not reflect on mobile application.
Apologies if this is in the docs, but from skimming the code it doesn't support getting access to the camera's events, or live video stream, does it? Been loking for a way to do this forever, and I've always come up empty handed
lock.is_open
for the locks is not reporting correctly. It will only report the lock as being is_open
: False
Traceback (most recent call last):
File “/Users/tcarlin/PycharmProjects/wyze-sdk/wyze_switch.py”, line 18, in <module>
client.plugs.turn_off(device_mac=plug.mac, device_model=plug.product.model, after=timedelta(hours=3))
File “/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/wyze_sdk/api/devices/plugs.py”, line 91, in turn_off
return super()._api_client().set_device_timer(mac=device_mac, plan_execute_ts=datetime.now() + after, action_type=1, action_value=0)
TypeError: set_device_timer() missing 1 required keyword-only argument: ‘delay_time’
As best as I can tell, there is no way to get the open_close_state_ts (timestamp) value of a contact sensor. This is part of the device_params array attribute. There is an attribute for open_close_ts but I don't understand how to use it.
This value is useful for automations that want to trigger off the switch has been open for X time. E.g. The refrigerator has been left open for 5 minutes.
First I want to thank you for putting this project together. I am able to get the vacuum to vacuum a specific room based on the syntax you posted. Is there anyway to just have it sweep all rooms on the map?
Thanks
Will
vacuum.voltage = 100 on 1.0.1
vacuum.voltage = None on 1.2.1
This has been an issue before: #9
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.