Closed pcmoore closed 1 year ago
Hey there @gabe565, mind taking a look at this issue as it has been labeled with an integration (ruckus_unleashed
) you are listed as a code owner for? Thanks!
(message by CodeOwnersMention)
ruckus_unleashed documentation ruckus_unleashed source (message by IssueLinks)
I'm seeing the same issue with 2023.6.
Same issue with Home Assistant 2023.6.0 docker container. Here is debug logs :
2023-06-08 17:56:27.175 ERROR (MainThread) [homeassistant.config_entries] Error setting up entry Mesh-Backbone for ruckus_unleashed
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/config_entries.py", line 387, in async_setup
result = await component.async_setup_entry(hass, self)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/ruckus_unleashed/init.py", line 31, in async_setup_entry
ruckus = await Ruckus.create(
^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pyruckus/init.py", line 44, in create
await ruckus.connect()
File "/usr/local/lib/python3.11/site-packages/pyruckus/init.py", line 50, in connect
result = await ssh.login(
^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pyruckus/RuckusSSH.py", line 48, in login
i = await self.expect(login_regex_array, timeout=logintimeout, async=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pexpect/spawnbase.py", line 343, in expect
return self.expect_list(compiled_pattern_list,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pexpect/spawnbase.py", line 369, in expect_list
from ._async import expect_async
File "/usr/local/lib/python3.11/site-packages/pexpect/_async.py", line 7, in
This problem was not present before the update to HA 2023.6.0
As per above:
2023-06-09 05:48:12.511 ERROR (MainThread) [homeassistant.config_entries] Error setting up entry Mesh-Backbone for ruckus_unleashed
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/config_entries.py", line 387, in async_setup
result = await component.async_setup_entry(hass, self)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/src/homeassistant/homeassistant/components/ruckus_unleashed/init.py", line 31, in async_setup_entry
ruckus = await Ruckus.create(
^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pyruckus/init.py", line 44, in create
await ruckus.connect()
File "/usr/local/lib/python3.11/site-packages/pyruckus/init.py", line 50, in connect
result = await ssh.login(
^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pyruckus/RuckusSSH.py", line 48, in login
i = await self.expect(login_regex_array, timeout=logintimeout, async=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pexpect/spawnbase.py", line 340, in expect
return self.expect_list(compiled_pattern_list,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/pexpect/spawnbase.py", line 366, in expect_list
from ._async import expect_async
File "/usr/local/lib/python3.11/site-packages/pexpect/_async.py", line 6, in
I can confirm that downgrading HA core to v2023.5.4 restores the Ruckus Unleashed integration.
I can confirm same issue exists also on 2023.6.1
Any hope of a resolution to this in the near future?
One more confirmation if needed on 2023.06.1 same error log, I removed the integration and am unable to add it again with the very same error log. I confirmed the user/pass is still valid on ssh. Will wait and hope someone fixes this as I am happy having setup my NAs in 2023.6 so not really willing to downgrade but thx for confirming this is a viable fix.
@andornaut - I'm no py dev, but I can't follow the bouncing ball here... Trying to follow the breadcrumbs of the linked PIP libraries... and it looks ... fine (?!)
the pyruckus package library, RuckusSSH.py, imports the 'pexpect' package for the 'spawn' function ( https://github.com/gabe565/pyruckus/blob/main/pyruckus/RuckusSSH.py )
the pexpect package library spawnbase.py, calls the pexpect library _async.py for the 'expect_async' function ( https://github.com/pexpect/pexpect/blob/master/pexpect/spawnbase.py )
the pexpect package library _async.py imports _async_w_await.py which EXPLICITLY contains the comment:
"""Implementation of coroutines using async def
/await
keywords.
These keywords replaced @asyncio.coroutine
and yield from
from
Python 3.5 onwards.
"""
As you've suggested - the @asyncio.coroutine needs to be updated to the replacement - async def ...and this appears to be the case. (unless I'm looking at the wrong branches, ?!)
I don't understand where is the deprecated @asyncio.coroutine being called from? Unfortunately, I run HA in the OS(?) mode, and don't have access to the python libraries in the shell to see what's happening, versions, etc... Do we need to spin up HA in Supervised or Core mode or similar to debug ?
The issue is 100% in pexpect within _async.py. I monkey patched _async.py to
/usr/local/lib/python3.11/site-packages/pexpect/_async.py
import asyncio
import errno
from pexpect import EOF
async def expect_async(expecter, timeout=None):
# First process data that was previously read - if it maches, we don't need
# async stuff.
previously_read = expecter.spawn.buffer
expecter.spawn._buffer = expecter.spawn.buffer_type()
expecter.spawn._before = expecter.spawn.buffer_type()
idx = expecter.new_data(previously_read)
if idx is not None:
return idx
if not expecter.spawn.async_pw_transport:
pw = PatternWaiter()
pw.set_expecter(expecter)
transport, pw = await asyncio.get_event_loop()\
.connect_read_pipe(lambda: pw, expecter.spawn)
expecter.spawn.async_pw_transport = pw, transport
else:
pw, transport = expecter.spawn.async_pw_transport
pw.set_expecter(expecter)
transport.resume_reading()
try:
return (await asyncio.wait_for(pw.fut, timeout))
except asyncio.TimeoutError as e:
transport.pause_reading()
return expecter.timeout(e)
class PatternWaiter(asyncio.Protocol):
transport = None
def set_expecter(self, expecter):
self.expecter = expecter
self.fut = asyncio.Future()
def found(self, result):
if not self.fut.done():
self.fut.set_result(result)
self.transport.pause_reading()
def error(self, exc):
if not self.fut.done():
self.fut.set_exception(exc)
self.transport.pause_reading()
def connection_made(self, transport):
self.transport = transport
def data_received(self, data):
spawn = self.expecter.spawn
s = spawn._decoder.decode(data)
spawn._log(s, 'read')
if self.fut.done():
spawn._buffer.write(s)
return
try:
index = self.expecter.new_data(s)
if index is not None:
# Found a match
self.found(index)
except Exception as e:
self.expecter.errored()
self.error(e)
def eof_received(self):
# N.B. If this gets called, async will close the pipe (the spawn object)
# for us
try:
self.expecter.spawn.flag_eof = True
index = self.expecter.eof()
except EOF as e:
self.error(e)
else:
self.found(index)
def connection_lost(self, exc):
if isinstance(exc, OSError) and exc.errno == errno.EIO:
# We may get here without eof_received being called, e.g on Linux
self.eof_received()
elif exc is not None:
self.error(exc)
This works perfectly. The bigger issue seems to be upstream. There is some blocker in pexpect where they are waiting for Pypy to get updated so that unit tests will pass. Here's the Pypy issue https://foss.heptapod.net/pypy/pypy/-/issues/3931 which is blocking the pexpect issue https://github.com/pexpect/pexpect/pull/732.
Essentially this is a bit of dependency hell.
The quick fix is my monkey patch if folks feel comfortable running a shell into the homeassistant container. EDIT: Understand, if you do monkey patch, you'll need to monkey patch after every HA upgrade until the upstream is fixed as the new container will nuke the change. /EDIT.
The longer fix is wait for the whole dependency deal to work itself out OR rewrite the integration to use an alternative to pexpect (Fabric?) that is better maintained OR fork pyexpect... or 100 other work arounds.
Thanks @Bubbgump209 - am happy to clobber the _async.py file ... Stupid question. Can this be done with the HassOS installation? When I use the SSH AddOn from within the UI, (I guess), I'm only accessing the SSH AddOn container. The /usr/local/lib directory is relatively empty. If I access the console and type 'login' to get a root shell - there isn't even a /usr/local folder ?! Not being a docker pundit, where are the python libraries even located?!? Are you doing this from HassOS, or another installation method? (apologies if I've messed up my terminologies)
In my case I am running supervised. So I connected to the container:
docker exec -d homeassistant /bin/bash
Then edit /usr/local/lib/python3.11/site-packages/pexpect/_async.py. I did have to install an editor using apk add nano
because I am too dumb to be good at vim.
I don't know what the HassOS install looks like. I imagine if you can SSH into the base OS that is running Docker in HassOS, then the same can be done.
One way to workaround this issue is by installling pexpect from the 'master' branch:
pip install https://github.com/pexpect/pexpect/archive/master.zip
Here's an example from an Ansible role that works around this issue:
- name: "Install pexpect from 'master' branch. Workaround 1/2 for: https://github.com/home-assistant/core/issues/94264"
community.docker.docker_container_exec:
container: homeassistant
argv:
- /bin/bash
- "-c"
- "pip install https://github.com/pexpect/pexpect/archive/master.zip"
- name: "Restart the homeassistant container. Workaround 2/2 for: https://github.com/home-assistant/core/issues/94264"
community.docker.docker_container:
name: homeassistant
state: started
restart: true
Thanks @Bubbgump209 I'll try and make sense of the docker rabbit holes in the HassOS to find the offending file. A 'find' from the console login returns 16 instances of _async.py in a number of containers. I'll try to find the 'homeassistant' container and work from there.
Must be a generational thing. I've 20 years of vi experience being my preferred *nix IDE. These days I generally don't bother with volatile languages like python, and tend to prefer hypervisors to containers.
Thanks for the fix!
@andornaut - are you doing this from the HassOS installation, or a supervised install, like @Bubbgump209 ?
@andornaut You're a god. I was about to start creating a playbook for this as I have a feeling this won't be fixed upstream for quite some time.
@andornaut - are you doing this from the HassOS installation, or a supervised install, like @Bubbgump209 ?
I'm running Home Assistant in a Docker container. A similar approach should work for a HassOS installation, but I haven't used HassOS myself, sorry.
Thanks @andornaut I'll consider a supervised install, as the HassOS appears to have been obfuscated to disuade ludites like myself from messing around.
I'll second your sentiments @Bubbgump209 : @andornaut - You're a god :)
For anybody using HassOS Go to the console ha > login
homeassistant:/config# pip install https://github.com/pexpect/pexpect/archive/master.zip
homeassistant:/config# exit
# shutdown -r now
Thanks for the solutions!
I'll second your sentiments @Bubbgump209 : @andornaut - You're a god :)
For anybody using HassOS Go to the console ha > login # docker exec -it homeassistant /bin/bash homeassistant:/config# pip install https://github.com/pexpect/pexpect/archive/master.zip homeassistant:/config# exit # shutdown -r now
Thanks for the solutions!
Is that a "safe" package to install, meaning it won't be likely to cause other errors or issues? Will we need to back out this change down the road?
Is that a "safe" package to install, meaning it won't be likely to cause other errors or issues? Will we need to back out this change down the road?
@Coder84619 This package - but an older version - is already installed by the Ruckus integration. The new version hasn't caused any issues for me personally, and I don't think it's likely that it will be any more likely to cause problems than the version that is already installed.
No one can answer this question definitively, unfortunately.
Will we need to back out this change down the road?
The change discussed above is a workaround. If the underlying issue is addressed in the future, then it'd be a good idea to back out this change (or, more precisely: not apply this workaround on top of the updated version of this integration).
Is that a "safe" package to install, meaning it won't be likely to cause other errors or issues? Will we need to back out this change down the road?
You will not need to back this out. Home Assistant is entirely Docker based so when you run an upgrade to Home Assistant it will overwrite this change. See my warning above how this fix will need to be applied every time one upgrades as it will be overwritten.
Exactly as @Bubbgump209 said. The update of the Core Docker container to 2023.6.2 bought with it the older pexpect 4.6.0, breaking the Ruckus Integration. The fix provided by @andornaut was quickly reapplied, bumping the pexpect PIP module to 4.8.0 and all is well with the Ruckus Integration again. Thankfully I don't appear to have any Integrations that rely on the older pexpect 4.6.0 Do we know what facilitated the change to pexpect 4.6.0 ? Can we, as Ruckus users, challenge those-that-broke-our-Integration to a cage match or something? Winner gets their preferred pexpect version in the Core Docker container? Apologies if I'm offending a structured, democratic method of dependancy conflict resolution, I'm new to HA / github / docker / comptuery things.
Looking in https://github.com/home-assistant/core/blob/dev/requirements_all.txt Lines 1403 - 1407 . # homeassistant.components.aruba . # homeassistant.components.cisco_ios . # homeassistant.components.pandora . # homeassistant.components.unifi_direct pexpect==4.6.0
Again - not a python dev, but are the commented Integragtions above the PIP the Integrations that reply on the library?
EDIT: I guess not, because the same entry exists in the 2023.5.4 branch where thr Ruckus Integration was working: https://github.com/home-assistant/core/blob/2023.5.4/requirements_all.txt
... walking away.
@faithless01 as I explained above, this is an issue with upstream Python. Python changed how they do things in newer versions. And turn this broke Pexpect which is a library that all these integrations are using to facilitate SSH and pulling back data structures. Pexpect is a generally available Python library and has no relation whatsoever to Home Assistant. Interestingly enough the master branch is ahead of the 4.8 release though still reports as 4.8. Otherwise it would be a very simple fix - bump the version requirement in pyruckus.
So see above as far as what the options are. Wait for pexpect to merge the fix and put out a new release (4.9?), rewrite the integration to use different libraries, or monkey patch while waiting.
Thanks @Bubbgump209 . From years of dabbling with Python, enough to pull something apart, written with an unmaintained library that has been deprcated, and is now broken due to security updates of the core language, then recode that company dependant tool in perl/CPAN or Bash, I've tried to avoid Python. I'm not a dev by trade, and Python has always seemed very volitile, mostly with very funky libraries written by transient developers, abandoned as they graduate and move to corporate roles leaving their libraries unmaintained. As @andornaut wrote: https://github.com/gabe565/pyruckus/issues/21#issuecomment-1589580374
Updating the requirement.txt file to point to pexpect @ https://github.com/pexpect/pexpect/archive/2532721.zip fixes the problem. I was hoping we could find why the requirements file in the HA Core Docker container refernces the older 4.6.0 But you're saying the version pf pexpect used has nothing to do with HA? I'm confused as to why the Core Docker requirements file couldn't use the link @andornaut referenced.
As you've suggested, I'll leave it to someone smarter to correct the problem and continue to clobber the library with a version that works in the interim. Thanks Again!
I've been working on a python library to parse connected clients from Ruckus Unleashed APs using the web API, instead of scraping the SSH output like pyruckus
does. So far I've only tested it with my R500 and R600, but if anyone else wants to help me test it, it should be far more stable than pyruckus
and I can submit a PR to use it here.
Thanks @lanrat I had a problem with line 14 of the example.py script, but once I removed that, the API calls did their thing. I have a couple of R600's. I made the call to the floating Unleashed master IP with an "admin" account. user@debian:~/Dev$ ./example.py {'name': 'KL130B', 'mac': '68:ff:7b:xx:xx:xx'} {'name': 'Cam2', 'mac': 'e8:ca:c8:xx:xx:xx'} {'name': 'Wiz Bedroom Candle 01', 'mac': '44:4f:8e:xx:xx:xx'} {'name': '', 'mac': '10:08:c1:xx:xx:xx'} {'name': '', 'mac': 'e0:98:06:xx:xx:xx'} {'name': '', 'mac': 'e8:db:84:xx:xx:xx'} {'name': 'HS100', 'mac': '1c:3b:f3:xx:xx:xx'} {'name': '', 'mac': '50:02:91:xx:xx:xx'} {'name': '', 'mac': '44:d5:cc:xx:xx:xx'} {'name': 'Study Desk Lamp', 'mac': 'b0:95:75:xx:xx:xx'} {'name': 'JT-Note8', 'mac': '04:d6:aa:xx:xx:xx'} {'name': 'Cam1', 'mac': '04:39:26:xx:xx:xx'} {'name': 'HS100', 'mac': '1c:3b:f3:xx:xx:xx'} {'name': '', 'mac': 'e8:db:84:xx:xx:xx'} {'name': 'KL110', 'mac': 'd8:0d:17:xx:xx:xx'} {'name': '', 'mac': 'a4:cf:12:xx:xx:xx'} {'name': 'HS110', 'mac': '74:da:88:xx:xx:xx'} {'name': '', 'mac': 'a4:cf:12:xx:xx:xx'} {'name': 'ESP VR Motion Detection', 'mac': 'a4:cf:12:xx:xx:xx'} {'name': 'KL110B', 'mac': 'cc:32:e5:xx:xx:xx'} {'name': 'Wiz Bedroom Candle 02', 'mac': 'd8:a0:11:xx:xx:xx'} {'name': 'Dyson Fan', 'mac': 'c8:ff:77:xx:xx:xx'} {'name': 'KL130B', 'mac': '84:d8:1b:xx:xx:xx'} {'name': '', 'mac': 'b8:5f:98:xx:xx:xx'} {'name': '', 'mac': 'e8:db:84:xx:xx:xx'}
I'm new to github, and python, so I'll take this offline for a while and do some learnin'. I'll figure out how to contact you via your github account / repo ... Apologies if its an indutry standard or similar, but is the Elmtree XML parser as long-term reliable as basic json for API calls?
Can you extend the functionality of ruckus.py to allow 302 redirects as well as 200 ok for login_resp ?
EDIT: There is a bit of useful information in the returned api to help diagnose which AP devices are connected, which ssid they're on, their subnet, signal strength, etc
{'name': 'KL130B', 'mac': '68:ff:7b:xx:xx:xx', 'ip': '192.168.102.xxx', 'ssid': 'FranklinThings', 'AP': 'R600-1', 'signal': 'excellent'} {'name': 'Cam2', 'mac': 'e8:ca:c8:xx:xx:xx', 'ip': '192.168.101.xxx', 'ssid': 'FranklinWing', 'AP': 'R600-1', 'signal': 'excellent'} {'name': 'Wiz Bedroom Candle 01', 'mac': '44:4f:8e:xx:xx:xx', 'ip': '192.168.102.xxx', 'ssid': 'FranklinThings', 'AP': 'R600-1', 'signal': 'excellent'} {'name': '', 'mac': '10:08:c1:xx:xx:xx', 'ip': '192.168.101.xxx', 'ssid': 'FranklinWing', 'AP': 'R600-1', 'signal': 'excellent'} {'name': '', 'mac': 'e0:98:06:xx:xx:xx', 'ip': '192.168.102.xxx', 'ssid': 'FranklinThings', 'AP': 'R600-1', 'signal': 'excellent'} {'name': '', 'mac': 'e8:db:84:xx:xx:xx', 'ip': '192.168.102.xxx', 'ssid': 'FranklinThings', 'AP': 'R600-0', 'signal': 'excellent'} {'name': 'HS100', 'mac': '1c:3b:f3:xx:xx:xx', 'ip': '192.168.102.xxx', 'ssid': 'FranklinThings', 'AP': 'R600-0', 'signal': 'excellent'} {'name': '', 'mac': '50:02:91:xx:xx:xx', 'ip': '192.168.102.xxx', 'ssid': 'FranklinThings', 'AP': 'R600-0', 'signal': 'excellent'} {'name': '', 'mac': '44:d5:cc:xx:xx:xx', 'ip': '192.168.101.xxx', 'ssid': 'FranklinWing', 'AP': 'R600-0', 'signal': 'excellent'} {'name': 'Study Desk Lamp', 'mac': 'b0:95:75:xx:xx:xx', 'ip': '192.168.102.xxx', 'ssid': 'FranklinThings', 'AP': 'R600-1', 'signal': 'excellent'} {'name': 'JT-Note8', 'mac': '04:d6:aa:xx:xx:xx', 'ip': '192.168.100.xxx', 'ssid': 'FranklinManor', 'AP': 'R600-0', 'signal': 'excellent'} {'name': 'Cam1', 'mac': '04:39:26:xx:xx:xx', 'ip': '192.168.101.xxx', 'ssid': 'FranklinWing', 'AP': 'R600-0', 'signal': 'excellent'} {'name': 'HS100', 'mac': '1c:3b:f3:xx:xx:xx', 'ip': '192.168.102.xxx', 'ssid': 'FranklinThings', 'AP': 'R600-0', 'signal': 'excellent'} {'name': '', 'mac': 'e8:db:84:xx:xx:xx', 'ip': '192.168.102.xxx', 'ssid': 'FranklinThings', 'AP': 'R600-0', 'signal': 'excellent'} {'name': 'KL110', 'mac': 'd8:0d:17:xx:xx:xx', 'ip': '192.168.102.xxx', 'ssid': 'FranklinThings', 'AP': 'R600-1', 'signal': 'excellent'} {'name': '', 'mac': 'a4:cf:12:xx:xx:xx', 'ip': '192.168.102.xxx', 'ssid': 'FranklinThings', 'AP': 'R600-1', 'signal': 'excellent'} {'name': 'HS110', 'mac': '74:da:88:xx:xx:xx', 'ip': '192.168.102.xxx', 'ssid': 'FranklinThings', 'AP': 'R600-1', 'signal': 'excellent'} {'name': '', 'mac': 'a4:cf:12:xx:xx:xx', 'ip': '192.168.102.xxx', 'ssid': 'FranklinThings', 'AP': 'R600-0', 'signal': 'excellent'} {'name': 'ESP VR Motion Detection', 'mac': 'a4:cf:12:xx:xx:xx', 'ip': '192.168.102.xxx', 'ssid': 'FranklinThings', 'AP': 'R600-1', 'signal': 'excellent'} {'name': 'KL110B', 'mac': 'cc:32:e5:xx:xx:xx', 'ip': '192.168.102.xxx', 'ssid': 'FranklinThings', 'AP': 'R600-1', 'signal': 'excellent'} {'name': 'Wiz Bedroom Candle 02', 'mac': 'd8:a0:11:xx:xx:xx', 'ip': '192.168.102.xxx', 'ssid': 'FranklinThings', 'AP': 'R600-0', 'signal': 'excellent'} {'name': 'Dyson Fan', 'mac': 'c8:ff:77:xx:xx:xx', 'ip': '192.168.101.xxx', 'ssid': 'FranklinWing', 'AP': 'R600-0', 'signal': 'excellent'} {'name': 'KL130B', 'mac': '84:d8:1b:xx:xx:xx', 'ip': '192.168.102.xxx', 'ssid': 'FranklinThings', 'AP': 'R600-0', 'signal': 'excellent'} {'name': '', 'mac': 'b8:5f:98:xx:xx:xx', 'ip': '192.168.101.xxx', 'ssid': 'FranklinWing', 'AP': 'R600-0', 'signal': 'excellent'} {'name': '', 'mac': 'e8:db:84:xx:xx:xx', 'ip': '192.168.102.xxx', 'ssid': 'FranklinThings', 'AP': 'R600-1', 'signal': 'excellent'}
This is a great tool you're creating @lanrat - kudos!
I created a library which is completely async, using aiohttp, specifically so it could replace pyruckus here (and I put AP LED, client blocking and WLAN disabling into the API). If you don't want to reinvent the wheel then you might want to have a look.
@ms264556 You did a great job. If use ssh cli, there are some limitation. For example, can not set block clients in system acl-list. Your solution can do. This help me to solve to create a switch by blocking client.
aioruckus looks better than my code and is what I think would be a good path forward.
I can start a draft PR to move this component to it, unless @ms264556 wants to do it.
I don't particularly want to do the PR - I had an earlier try and it wasn't accepted. So if you're keen then that'd be great.
I can definitely make any changes which would make your job easier (e.g. if you want a synchronous context manager so that it's more plug-and-play with existing pyruckus code). I even have some nasty "pyruckus compatibility" methods which I can commit, if you need the easiest possible switch.
I can add you as a maintainer of aioruckus if you think that'd help, but I'm generally pretty quick at fixing issues, and I have a large collection of APs to test changes against.
@ms264556 thats fine, I'll work on the PR.
Do you mind linking the earlier PR you mentioned you did that was not accepted so that I can avoid any of the same mistakes?
It was against an older version of aioruckus, which looked quite different (actually, looked quite like your current code 😀), and I killed it when I got feedback that the tests were no good and I should update pyruckus rather than start a new library.
You can see my attempt here:-
@ms264556 I'm working on a PR that is currently heavily based on your prior PR you linked to. However I am getting an exception in what appears to be one of the dependencies of aioruckus
when I call ruckus.api.get_active_clients()
Do you have any insights into this? I can provide my full code if you want, but its pretty minimal just requesting the client list so far.
I can't figure out where that missing str should be set.
Can you easily see what version of aiohttp is in site-packages?
I can setup a HA dev environment so I can investigate. Will take a little while. If you're able to commit your changes to a branch on your fork then that'd make it faster for me to look.
Nevermind. I can repro the issue if I try to call AjaxSession.async_create()
directly instead of using it as an async context manager.
Let me work up some code which works in that way.
Otherwise I'll create a new aioruckus release which includes a non-async context, so this usage will work OK.
I'm testing with a R600 and R500 running version 200.7.10.202.
@ms264556 I was able to fix the bug from my prior comment. I found the fix was to explicitly call await ruckus.login()
after AjaxSession.async_create(...)
@ms264556 I also noticed some of the dictionary keys from my APs and clients are different than the ones from your prior PR.
I've changed the relevant ones to use the keys from my setup, but from your experience, is this something that may be different for other users?
For example, you used serial-number
for the AP serial number, but on my setup, its just serial
, I'm also missing firmware-version
and instead just have version
and no hardware-version
, as well as a few others.
Yes, was just coming to write the same: if you're not inside an async with
then you need an await ruckus.login()
. Also try to call await ruckus.close()
if you're killing the session.
The original PR used a minimal pyruckus compatibility shim, which transformed the AJAX results so they looked like what pyruckus had produced.
So yes, the properties will all have different names than what pyruckus used.
Here are the shims I wrote so you can see the mappings:-
async def system_info(self) -> dict:
warn("Use get_system_info()", DeprecationWarning)
sysinfo = await self.get_system_info(SystemStat.SYSINFO, SystemStat.IDENTITY)
return {"system_overview": {"name": sysinfo["identity"]["name"], "version": sysinfo["sysinfo"]["version"], "serial_number": sysinfo["sysinfo"]["serial"]}}
async def mesh_info(self) -> dict:
warn("Use get_mesh_info() or get_system_info(SystemStat.MESH_POLICY)", DeprecationWarning)
meshinfo = await self.get_mesh_info()
meshpolicy = await self.get_system_info(SystemStat.MESH_POLICY)
return {"mesh_settings": {"mesh_status": "Enabled" if meshpolicy["mesh-policy"]["enabled"] == "true" else "Disabled", "mesh_name_essid": meshinfo["name"], "zero_touch_mesh_pre_approved_serial_number_list": {"serial_number": "unsupported"}}}
async def mesh_name(self) -> str:
warn("Use get_mesh_info()['name']", DeprecationWarning)
mesh_info = await self.get_mesh_info()
return mesh_info["name"] if "name" in mesh_info else "Ruckus Mesh"
async def current_active_clients(self) -> dict:
warn("Use get_active_client_info()", DeprecationWarning)
clientstats = await self.get_active_client_info()
return {"current_active_clients": {"clients": [{"mac_address": c["mac"], "host_name": c["hostname"], "user_ip": c["ip"], "access_point": c["vap-mac"]} for c in clientstats]}}
async def ap_info(self) -> dict:
warn("Use get_ap_info()", DeprecationWarning)
apstats = await self.get_ap_info()
return {"ap": {"id": {a["id"]: {"mac_address": a["mac"], "device_name": a["devname"], "model": a["model"], "network_setting": {"gateway": a["gateway"]}} for a in apstats}}}
Regarding the keys being different for other users...
I test on ZoneDirector 9.10, 9.13, 10.1, 10.3, 10.5.1 & Unleashed 200.7, 200.12, 200.13, 200.14, 200.14 Dedicated Master.
These releases cover ~8 years, and these keys have never changed, so I'm pretty confident we're OK.
@ms264556 thanks for the info. I added await ruckus.close()
too to be safe.
I'm not using any of the methods you mentioned for the compatibility shim, just using your functions directly.
Do your APs return firmware-version
and hardware-version
from api.get_aps()
? Mine do not.
Sorry, misunderstood what you were asking for.
api.get_aps()
is a config call so won't show those properties.
If you call api.get_ap_stats()
then it'll include the extra information you want.
I'm not sure we need api.get_ap_stats()
, when I call api.get_aps()
I get everything I need. It just had a few things named a little differently.
I have a branch that as far as I can tell, works perfectly for my home setup with a lot of manual testing.
I'm now working on updating the automated tests. @ms264556 I see that you tried to use a RuckusApi
object to mimic your API responses. How should it be instantiated for mocks? The old commits you linked to don't seem to have it defined anywhere.
@ms264556 I've pushed my current WIP changes to my fork here: https://github.com/lanrat/hass_core/tree/ruckus_unleashed_py3.11
You should be able to subclass AbcSession
and have the api()
method return your mock.
Honestly, I didn't 100% understand what HA required to be tested, so I failed miserably at that part of the PR.
You should be able to subclass AbcSession and have the api() method return your mock.
@ms264556 I have very little experience with python mock tests. I'm trying to figure this out, but I'm lost so far. Any other hints you can give me would be appreciated. I'll push the updated tests I have so far to my branch, but they all fail as I'm not sure what to do about RuckusApi
yet.
I have very little experience with python mock tests
Ha! I have very little experience with python, so I definitely feel your pain.
I've only learned enough python to write this library + a couple of other hacky bits and pieces.
If you commit the broken tests to your branch then I'll patch them up so they work. I'm not at home, so it won't be for 3 or 4 hours sorry.
The problem
After upgrading my HA instance to v2023.6.0 yesterday my Ruckus Unleashed integration stopped working with the "Failed to set up" error on the integration page; reloading the integration, restarting HA, and rebooting the entire device did not resolve the problem.
If I enable debug logging for the integration, reload the integration, and check the logs I see the following two entries which appear relevant.
The first entry is coming from the Ruckus integration itself:
The second entry doesn't appear to be directly related to the Ruckus integration, but it appears similar to other outstanding GH issues for the integration:
What version of Home Assistant Core has the issue?
core-2023.6.0
What was the last working version of Home Assistant Core?
core-2023.5.4
What type of installation are you running?
Home Assistant OS
Integration causing the issue
Ruckus Unleashed
Link to integration documentation on our website
https://www.home-assistant.io/integrations/ruckus_unleashed
Diagnostics information
No response
Example YAML snippet
No response
Anything in the logs that might be useful for us?
No response
Additional information
No response