Ryobi Garage Door Opener - Websocket-Based for Cloud-Push updates

Ryobi Garage Door Opener - HA Custom Component

This is a custom component with a Websocket-based connection to Ryobi’s Cloud Service (TiwiConnect) to receive push updates and control the Garage Door Opener. This was reverse-engineered from their Android App. The TiwiConnect is a bit shoddy and so is my code. I’ve been using it for about a week and it’s moderately stable (no restarts required so far).

This integration will request all of the Garage Door devices linked to your Ryobi account and automatically add them into Home Assistant.

This is a work-in-progress! Currently, you can view the state (OPEN, CLOSED, OPENING, CLOSING) and the current position (% open) of the door. You can also open or close the door but not set a position. The Entity also has extra attributes such as Last Set, Last Value, Vacation Mode, Sensor Flag, Light State, and Light Timer. The Light entity is not setup and therefore not controllable yet. See the Github page for more details.

Tested Devices

  • GDO125

I am returning to work after Christmas break tomorrow so updates will take longer. Please feel free to contribute to the project

1 Like

Still looking at maintaining this? I’d love to point people to this in the other thread I have since websockets are more efficient. I mainly maintain the repo for the other integration since it was removed from core previously and I needed something myself. Hell I barely know python. If there is something I can help with, please let me know as I would love to see this come to fruition. I’m sad I didn’t find this earlier!

I definitely want to keep this going.

The integration needs some improvements. The example integrations I looked at to make this seem to have a different interaction with their server than the GDO with Ryobi servers. Right now the integration tries to create a websocket for both the garage door and the light (each device). It also seems to lose connection once in a while but reconnects right away.

Stability isn’t perfect but it works good enough for me for now. Definitely need to take another look at it and make some improvements.

I am the same with programming - know just enough to fiddle… Hopefully I can find someone familiar with WebSockets to reach out to to suggest some ways to fix it so it’s more stable as this is definitely a viable way to control the GDO… Plus I am sure there are more feature I can add (stop function, set position, safety sensor reading, etc).

Unfortunately I won’t have time to look at it again until school ends (~2 months) as I’m a teacher.

@Madj42 Well actually I would love to make minor adjustments now. I just need some help and direction to get started again.

Yeah, no problem. Totally get the time thing as I have two small kids that are demanding. From what I remember, the app does this same behavior and the http calls are mainly to start the connection. Maybe posting something in the other forum post would get someone’s attention as there was a guy who fixed some things with the other integration recently.

I did figure out how to keep the websocket open permanently… however the integration I ‘copied’ to create this on did thing a bit wonky. Again it mostly works if you only want to control the door. If I can find a device that uses a websocket like the Ryobi GDO (one device - multiple entities) - I’d likely be able to use it as reference for creating a proper integration…

I’ll try posting in the other thread.

Edit: @Madj42 Just tried looking through the other thread - not sure who you mean… Maybe @zimmer62? Sounds like we all have jobs and 2 young kids :slight_smile:

I messaged you the name. Yeah, that’s why I haven’t invested more time in this and learning more python. I’ve been able to pick up other languages for my job but don’t have a need for python there. I managed to hack together the smartthings code when my daughter was first born and I was up with her. The home assistant stuff came when my son was born and it was mainly finding and hacking together the old official code from someone else. Kids, they leave no time for anything else. Lol

Yeah, I’ve just been too busy to think about this. I would have loved to detect when the door is open very quickly so I can kick off an automation to turn on the lights. I decided to just put a zwave door sensor on it and that gets me what I need.

Are you able to publish the changes to GitHub for the websocket connection? I know it’s not what you planned on doing in the end but I was planning on taking a look at the light portion to see if I could fiddle enough to get it to work. I did manage to get this to work and it appears to work just fine. The only thing I did notice was that the status didn’t update for the door when it opened or closed. Not sure if that was due to the websocket connection.

Ultimately in the end, I think you’d have to create some sort of server portion like the z-wave js Integration uses and have it manage the websocket connections instead of the individual entities managing their own.

I just checked Github with my local copy - should be the most updated version.

I think the main problem is how I am currently handling keeping the connection open. Because I was referencing another integration to make mine, I did some of the things they did, but those don’t make sense in this case…

I was able to get the light working pretty easily. However, for some reason I can’t get the status to update after the light is switched on or off. Still looking into this when I have time.

I think it’s definitely do-able using the current implementation… I need to decouple the web socket form the individual entity and makes sure that it’s ‘linked’ to the device which then creates multiple entities (cover, light, safety sensor, etc)… this is how it’s supposed to be done. Right now, it does not function this way.

That might be why the Light isn’t working correctly. It might be creating 2 sockets at the same time?

I just tried to setup a GDO 201 and it fails

Logger: homeassistant.setup
Source: custom_components/ryobi_garage/ryobiapi.py:161
Integration: ryobi_garage
First occurred: 8:44:01 AM (1 occurrences)
Last logged: 8:44:01 AM

Error during setup of component ryobi_garage
Traceback (most recent call last):
File “/usr/src/homeassistant/homeassistant/setup.py”, line 257, in _async_setup_component
result = await task
File “/config/custom_components/ryobi_garage/init.py”, line 59, in async_setup
device_info = await ryobi_api.get_device(device[“d_id”])
File “/config/custom_components/ryobi_garage/ryobiapi.py”, line 146, in get_device
return self.extract_device_info(resp)
File “/config/custom_components/ryobi_garage/ryobiapi.py”, line 161, in extract_device_info
device_info[“serial”] = master_unit[“serialNumber”][“value”]
KeyError: ‘serialNumber’

You may want to try this again. Ryobi’s backend was having issues around this time.

I have a couple of modules installed (tire inflator and park assist lasers) and they appear to be throwing errors in my log:
(run_forever)) [custom_components.ryobi_garage.ryobiapi] RyobiApi (WSS) parse update: Did not recognized module: modulePort_3.portStatus
(run_forever)) [custom_components.ryobi_garage.ryobiapi] RyobiApi (WSS) parse update: Did not recognized module: masterUnit.modulesInstalled
(run_forever)) [custom_components.ryobi_garage.ryobiapi] RyobiApi (WSS) parse update: Did not recognized module: modulePort_3.moduleId
(run_forever)) [custom_components.ryobi_garage.ryobiapi] RyobiApi (WSS) parse update: Did not recognized module: parkAssistLaser_3.autoOn
(run_forever)) [custom_components.ryobi_garage.ryobiapi] RyobiApi (WSS) parse update: Did not recognized module: parkAssistLaser_3.laserTimer

What’s the best way to either fix or deactivate these modules as they are creating thousands of errors in my logs daily.

Getting these errors after reboot and initial setup. Any help would be greatly appreciated.

Source: setup.py:288
First occurred: 8:28:41 PM (1 occurrences)
Last logged: 8:28:41 PM

Error during setup of component ryobi_garage
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/setup.py", line 288, in _async_setup_component
    result = await task
             ^^^^^^^^^^
  File "/config/custom_components/ryobi_garage/__init__.py", line 59, in async_setup
    device_info = await ryobi_api.get_device(device["d_id"])
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/ryobi_garage/ryobiapi.py", line 146, in get_device
    return self.extract_device_info(resp)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/ryobi_garage/ryobiapi.py", line 174, in extract_device_info
    garage_door = first_result["deviceTypeMap"][f"garageDoor_{port}"]["at"]
                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
KeyError: 'garageDoor_0'