Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

This is great - could maybe have more in the readme.md ... #2

Open
ajfhodgson opened this issue Feb 1, 2022 · 33 comments
Open

This is great - could maybe have more in the readme.md ... #2

ajfhodgson opened this issue Feb 1, 2022 · 33 comments

Comments

@ajfhodgson
Copy link

Not an issue - just some kudos!

I had used https://github.com/skydiver/ewelink-api successfully in node/javascript, but needed a Python version.

Initially I found https://github.com/lucien2k/sonoff-python and spent a day failing to make it work.

But this port just worked!

It could maybe do with a few more instructions in the readme, to indicate how very easy this is to make work, e.g.

import ewelink
import json
email = 'your_email'
password = 'your_password'
region = 'eu' # or 'us' or 'cn'
connection = ewelink.Ewelink(email, password, region)
devices = connection.getDevices()
for device in devices['devicelist']:
    print(f'{device["deviceid"]}, {device["name"]}')
resp = connection.getDevice(devices['devicelist'][0]["deviceid"])
print(f'{json.dumps(resp)}')

Boom! Excellent!

This deserves to be better known.

@PJanisio
Copy link

PJanisio commented Mar 31, 2022

Using this simple script I have got this errors, at least Im logged in :)

[root@testowy ewelink-api-python]# python3 test.py
{'log': 'ok', 'status': 'success'}
Traceback (most recent call last):
  File "test.py", line 7, in <module>
    devices = connection.getDevices()
  File "/root/ewelink-api-python/ewelink/ewelink.py", line 28, in getDevices
    return Object(res.json())
  File "/root/ewelink-api-python/ewelink/models/object.py", line 7, in __init__
    self.__recurse_to_self()
  File "/root/ewelink-api-python/ewelink/models/object.py", line 44, in __recurse_to_self
    l = list(map(Object, value.copy()))
  File "/root/ewelink-api-python/ewelink/models/object.py", line 7, in __init__
    self.__recurse_to_self()
  File "/root/ewelink-api-python/ewelink/models/object.py", line 44, in __recurse_to_self
    l = list(map(Object, value.copy()))
  File "/root/ewelink-api-python/ewelink/models/object.py", line 5, in __init__
    super().__init__(data)
ValueError: dictionary update sequence element #0 has length 1; 2 is required

@AceExpert
Copy link
Owner

@PJanisio and @ajfhodgson well i have a lot of work, that's why im not able to focus on it. Janisio you might have not got that error if u would have used the repo before the latest commit which broke stuff unknowingly cause i left somethings incomplete. 😔
Well im planning an asynchronous re-write of this. That will make it stable and accurate.

@PJanisio
Copy link

Dont let it die :)

@AceExpert
Copy link
Owner

I have done an asynchronous re write of this. Its under development still, check out the new examples.

@AceExpert
Copy link
Owner

AceExpert commented Apr 16, 2022

And, this isn't a port anymore, its a independent wrapper around the API. Independent of what ewelink-api does. It will have its own features. I cannot assure anything about its future

@ajfhodgson
Copy link
Author

I appreciate the time these things soak up!

Well, the version I downloaded continues to successfully poll two humidity sensors under my house every 30 minutes, and has since January, 2022, so I have no reason to upgrade until that stops working! :-)

To encourage you, here's what you've enabled for me:
image

Red is humidity outside the house (from openweather.org), purple and green are under my house. Blue is amount of rain (London, UK) - it's been a VERY dry spring!

@PJanisio
Copy link

Thanks for udpates, but damn im newbie at python, so maybe you can take a look at this syntax error I got:

[root@testowy ewelink-api-python]# python3 test.py
Traceback (most recent call last):
  File "test.py", line 14, in <module>
    import ewelink
  File "/root/ewelink-api-python/ewelink/__init__.py", line 1, in <module>
    from .client import Client, login
  File "/root/ewelink-api-python/ewelink/client.py", line 14, in <module>
    from .models import ClientUser, Device, Devices, Region
  File "/root/ewelink-api-python/ewelink/models/__init__.py", line 2, in <module>
    from .user import AppInfo, ClientInfo, ClientUser
  File "/root/ewelink-api-python/ewelink/models/user.py", line 47
    if extra := data.get('extra', None):
              ^
SyntaxError: invalid syntax

@sputh-the-pigeon
Copy link
Contributor

@PJanisio make sure your Python version is 3.9+

@PJanisio
Copy link

@PJanisio make sure your Python version is 3.9+

Damn, mine is 3.6.8 and unfortunatelly I can not update it to 3.9 so far completely. I think i need to run it in its own 3.9 environment.

@ajfhodgson
Copy link
Author

Hmmm... Python assignment is simply
extra = data.get('extra', None)
no colon!!!

@PJanisio
Copy link

Well okay, Python 3.9.6 installed, credentials are ok -sorry to bother :)

[root@testowy ewelink-api-python]# python3 test.py
Traceback (most recent call last):
  File "/www/python/ewelink-api-python/test.py", line 1, in <module>
    import ewelink
  File "/www/python/ewelink-api-python/ewelink/__init__.py", line 1, in <module>
    from .client import Client, login
  File "/www/python/ewelink-api-python/ewelink/client.py", line 14, in <module>
    from .models import ClientUser, Device, Devices, Region
  File "/www/python/ewelink-api-python/ewelink/models/__init__.py", line 2, in <module>
    from .user import AppInfo, ClientInfo, ClientUser
  File "/www/python/ewelink-api-python/ewelink/models/user.py", line 6, in <module>
    from ..http import HttpClient
  File "/www/python/ewelink-api-python/ewelink/http.py", line 27, in <module>
    'email': str | None,
TypeError: unsupported operand type(s) for |: 'type' and 'NoneType'
[root@testowy ewelink-api-python]# python3 --version
Python 3.9.6

@sputh-the-pigeon
Copy link
Contributor

Hmmm... Python assignment is simply extra = data.get('extra', None) no colon!!!

Sorry, but := is the walrus operator added in Python 3.8. You may check about them in the docs.

@sputh-the-pigeon
Copy link
Contributor

@PJanisio i m not sure why that error occurs. Im not able to reproduce it

@sputh-the-pigeon
Copy link
Contributor

@PJanisio oh damn, sorry im really sorry, this totally forgot. Its not python 3.9+ you need python 3.10+ for this library. Really very sorry for your troubles

@PJanisio
Copy link

PJanisio commented Apr 24, 2022

@PJanisio oh damn, sorry im really sorry, this totally forgot. Its not python 3.9+ you need python 3.10+ for this library. Really very sorry for your troubles

Don`t worry its not a priority :)
There is a step forward - im using compiled python 3.10.4.

[root@testowy ewelink-api-python]# python3.10 test.py

Traceback (most recent call last):

  File "/www/python/ewelink-api-python/test.py", line 4, in <module>
    @ewelink.login('xxxxxxxxxx', 'xxxxxxxxx')

  File "/www/python/ewelink-api-python/ewelink/client.py", line 67, in login
    asyncio.get_event_loop().run_until_complete(client.login())

  File "/usr/local/lib/python3.10/asyncio/base_events.py", line 646, in run_until_complete
    return future.result()

  File "/www/python/ewelink-api-python/ewelink/client.py", line 43, in login
    self.devices = Devices(

  File "/www/python/ewelink-api-python/ewelink/models/device.py", line 83, in __init__
    super().__init__(devices)

  File "/www/python/ewelink-api-python/ewelink/client.py", line 44, in <genexpr>
    Device(data = device, http = self.http, ws=self.ws) for device in (await self.http.get_devices()).get('devicelist', [])

  File "/www/python/ewelink-api-python/ewelink/models/device.py", line 49, in __init__
    self.state: PowerState = PowerState[data['params']['switch']]

KeyError: 'switch'

Maybe its a specific device issue. Below my devices list (model):

  • 4CH Pro
  • POWR2
  • BASICR2
  • Touch EU

@sputh-the-pigeon
Copy link
Contributor

@PJanisio hmm true. I just have sonoff and i thought 'switch' should probably be common to all devices, didnt know it wasnt
i will probably make a change such that this state becomes Optional

@Cainor
Copy link

Cainor commented Apr 27, 2022

I'd like to thank you for this great tool. I'm currently facing the same issue as "switch" key is not found.
In my case it is called "switches". Here is the details I received for "param":

   "params":{
      "bindInfos":{
         "gaction":[
            "HERE IS SOME KIND OF A KEY"
         ]
      },
      "version":8,
      "switches":[
         {
            "switch":"off",
            "outlet":0
         },
         {
            "switch":"off",
            "outlet":1
         },
         {
            "switch":"off",
            "outlet":2
         },
         {
            "switch":"off",
            "outlet":3
         }
      ],

I only have Sonoff Micro installed in my account.

@Cainor
Copy link

Cainor commented Apr 27, 2022

So after playing around for a bit. I've fixed the issue by replacing the code found in ewelink\models\device.py:50
From:

self.state: Power = Power[data['params']['switch']]

To:

self.state: Power = Power[data['params']['switches'][0]['switch']]

@Cainor
Copy link

Cainor commented Apr 27, 2022

Oh man .. I'd love to contribute to this, seems like the code is written in an advance way that I never tried to type with.
I'll try to learn something or two about asyncio maybe I can help :)

All the thanks to you 👍

@Cainor
Copy link

Cainor commented Apr 27, 2022

One thing I'm currently facing ..
I always get asyncio.exceptions.TimeoutError when trying to change the status of a device ..

Traceback (most recent call last):
  File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\asyncio\tasks.py", line 456, in wait_for
    return fut.result()
asyncio.exceptions.CancelledError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "c:\Users\USER\ewelink-api-python\sonoffy.py", line 6, in <module>
    async def main(client: Client):
  File "c:\Users\USER\ewelink-api-python\ewelink\client.py", line 69, in decorator
    result = asyncio.get_event_loop().run_until_complete(f(client))
  File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 646, in run_until_complete
    return future.result()
  File "c:\Users\USER\ewelink-api-python\sonoffy.py", line 19, in main
    await device.edit(Power.on)
  File "c:\Users\USER\ewelink-api-python\ewelink\models\device.py", line 68, in edit
    await self.ws.update_device_status(self.id,
  File "c:\Users\USER\ewelink-api-python\ewelink\ws.py", line 87, in update_device_status
    result = await asyncio.wait_for(fut, timeout = 30)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\asyncio\tasks.py", line 458, in wait_for
    raise exceptions.TimeoutError() from exc
asyncio.exceptions.TimeoutError

@Shisir99
Copy link

Shisir99 commented May 9, 2022

getting this error, any idea how to resolve this?

File "test.py", line 30, in
import ewelink
File "/home/shishir/ewelink/init.py", line 1, in
from .client import Client, login
File "/home/shishir/ewelink/client.py", line 15, in
from .models import ClientUser, Device, Devices, Region
File "/home/shishir/ewelink/models/init.py", line 4, in
from .user import AppInfo, ClientInfo, ClientUser
File "/home/shishir/ewelink/models/user.py", line 6, in
from .object import Object
File "/home/shishir/ewelink/models/object.py", line 3, in
class Object(dict):
File "/home/shishir/ewelink/models/object.py", line 5, in Object
def init(self, data: dict[Any, Any], name: str = "Object") -> None:
TypeError: 'type' object is not subscriptable

@sputh-the-pigeon
Copy link
Contributor

@Shisir99 make sure you on Python 3.10+

@AceExpert
Copy link
Owner

@PJanisio @Cainor the "switch" KeyError is fixed in the latest commit. Please update your libraries to the latest master branch of the repository.

@Cainor you can access the switches using:

switches = device.params.switches
print(switches[0].switch, switches[1].switch)

@PJanisio
Copy link

PJanisio commented May 9, 2022

@PJanisio @Cainor the "switch" KeyError is fixed in the latest commit. Please update your libraries to the latest master branch of the repository.

@Cainor you can access the switches using:

switches = device.params.switches
print(switches[0].switch, switches[1].switch)

Works like a charm ! :)
So using example script from readme I can see devices data, but at the end of script I got this error message.
Dunno what is NoneType device/object.

Traceback (most recent call last):
  File "/www/python/ewelink-api-python/test.py", line 5, in <module>
    async def main(client: Client):
  File "/www/python/ewelink-api-python/ewelink/client.py", line 94, in decorator
    result = asyncio.get_event_loop().run_until_complete(f(client))
  File "/usr/local/lib/python3.10/asyncio/base_events.py", line 646, in run_until_complete
    return future.result()
  File "/www/python/ewelink-api-python/test.py", line 12, in main
    print(device.params)
AttributeError: 'NoneType' object has no attribute 'params'

@Shisir99
Copy link

@Shisir99 make sure you on Python 3.10+

@sputh-the-pigeon yes , I am on python 3.10+ although getting the error.

@sputh-the-pigeon
Copy link
Contributor

@PJanisio the device is None. Means the device ID you provided is invalid and no such device with that ID exists.

@sputh-the-pigeon
Copy link
Contributor

@Shisir99 I do not get that error. Check if you have multiple python installations or probably any older version of python is running the code.

@JabLuszko
Copy link

you can access the switches using:

switches = device.params.switches
print(switches[0].switch, switches[1].switch)

Sure, this will work for accessing the state on/off as str, but switches can be turned off and on and current implementation seems to support only per device ON/OFF, not per switch - like for example 4 channel relays?

@PJanisio
Copy link

@PJanisio the device is None. Means the device ID you provided is invalid and no such device with that ID exists.

Exactly, I have done mistype in device id. My bad :)

@sputh-the-pigeon
Copy link
Contributor

@JabLuszko
I'm aware. I haven't implemented those because of my exams. Until those are done, you may use:

await client.ws.update_device_status(deviceid, switches = ...)

This is raw websocket method, all per channel, per device on off methods are built on top of this only, here's the source:

async def update_device_status(self, deviceid: str, **kwargs: list[dict[str, AnyStr]] | AnyStr) -> Response:

@sputh-the-pigeon
Copy link
Contributor

sputh-the-pigeon commented May 13, 2022

Here's the UIID Protocol for each device:
https://coolkit-technologies.github.io/eWeLink-API/#/en/UIIDProtocol?id=description-of-the-control-command-pass-through-parameters

You might be able to do something like:

await client.ws.update_device_status(deviceid, switches = [dict(switch = "on", outlet = 0), dict(switch = "off", outlet = 1)])

remember that this is only for time being, proper support will be added later

@AceExpert
Copy link
Owner

@JabLuszko added proper support for multichannel device control, check examples at https://github.com/AceExpert/ewelink-api-python/#multi-channel-device-control-first-method

@Elio-Chedid
Copy link

One thing I'm currently facing .. I always get asyncio.exceptions.TimeoutError when trying to change the status of a device ..

Traceback (most recent call last):
  File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\asyncio\tasks.py", line 456, in wait_for
    return fut.result()
asyncio.exceptions.CancelledError

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "c:\Users\USER\ewelink-api-python\sonoffy.py", line 6, in <module>
    async def main(client: Client):
  File "c:\Users\USER\ewelink-api-python\ewelink\client.py", line 69, in decorator
    result = asyncio.get_event_loop().run_until_complete(f(client))
  File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 646, in run_until_complete
    return future.result()
  File "c:\Users\USER\ewelink-api-python\sonoffy.py", line 19, in main
    await device.edit(Power.on)
  File "c:\Users\USER\ewelink-api-python\ewelink\models\device.py", line 68, in edit
    await self.ws.update_device_status(self.id,
  File "c:\Users\USER\ewelink-api-python\ewelink\ws.py", line 87, in update_device_status
    result = await asyncio.wait_for(fut, timeout = 30)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\asyncio\tasks.py", line 458, in wait_for
    raise exceptions.TimeoutError() from exc
asyncio.exceptions.TimeoutError

have you find a solution for this error? I'm facing the same problem

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants