Decora wifi no longer working

I was having trouble with my Leviton Decora DW15S-1BZ switches (1st gen) and thought maybe Leviton changed the APIs and broke the python-decora_wifi that Home Assistant uses under the covers. I cloned down the repo and was able to run the cli-test.py just fine (it turns on every light in the house, probably shouldn’t have done that at 2AM!). So at least the hard part of the Leviton / Home Assistant integration should be in good shape still. I enabled debug logging in Home Assistant to investigate further, but after a restart Home Assistant is able to control these switches again. I’ll leave the logging enabled and see if it reveals anything interesting if it stops working again.

I was also able to get the Leviton switch working using the HomeKit integration. When you add it using the leviton_wifi, it is added as a light. Through HomeKit though, its actually added as a switch. The response is instantaneous.

I lost function of all my decora switches a couple days ago. Restarting HA would only see the decora_wifi component fail after 60 seconds of trying.
I am really a complete amatuer but did some digging and found another decora_wifi on github (GitHub - markcaldwell0017/python-decora_wifi-cli: Leviton Decora wifi switch command line interface, written in Python.) and downloaded the repository to try the scripts manually from a windows 11 machine.
Anyway, after modifying the __init__.py file, I was able to connect. On a whim, I created a “decora_wifi” folder in the “custom_components” folder of HA, and placed the modified __init__.py file in there and restarted.

All the lights are visible and controllable again. I do realize that this may just be a happy coincidence. I hope that it is not, and that this may fix the issue for others.

Not sure if I can add as an attachment so here is the content of the __init__.py file

# Python module for controlling Leviton Decora Smart WiFi Switches
# Reverse engineered from the myLeviton Android app.
#
# Copyright 2017 Tim Lyakhovetskiy <[email protected]>
#
# This code is released under the terms of the MIT license. See the LICENSE
# file for more details.

import json

import requests

from .person import Person


class DecoraWiFiSession:
    """This class represents an authorized HTTPS session with the LCS API."""

    LEVITON_ROOT = 'https://my.leviton.com/api'

    def __init__(self):
        """Initialize the session, all content is JSON."""
        self._session = requests.Session()
        """self._session.verify = False"""
        self._session.headers.update({'Content-Type': 'application/json'})
        self._email = None
        self._password = None
        self.user = None

    def call_api(self, api, payload=None, method='get'):
        """Generic method for calling LCS REST APIs."""
        # Sanity check parameters first...
        if (method != 'get' and method != 'post' and
           method != 'put' and method != 'delete'):
            msg = "Tried decora.call_api with bad method: {0}"
            raise ValueError(msg.format(method))

        if self.user is None and api != '/Person/login':
            raise ValueError('Tried an API call without a login.')

        uri = self.LEVITON_ROOT + api

        # Payload is always JSON
        if payload is not None:
            payload_json = json.dumps(payload)
        else:
            payload_json = ''

        response = getattr(self._session, method)(uri, data=payload_json)

        # Unauthorized
        if response.status_code == 401 or response.status_code == 403:
            # Maybe we got logged out? Let's try logging in.
            self.login(self._email, self._password)
            # Retry the request...
            response = getattr(self._session, method)(uri, data=payload_json)

        if response.status_code != 200 and response.status_code != 204:
            msg = "myLeviton API call ({0}) failed: {1}, {2}".format(
                          api, response.status_code, response.text)
            raise ValueError(msg)

        if response.text is not None and len(response.text) > 0:
            return json.loads(response.text)
        else:
            return None

    def login(self, email, password):
        """Login to LCS & save the token for future commands."""
        payload = {
            'email': email,
            'password': password
        }

        login_json = Person.login(self, payload)

        if login_json is None:
            return None

        self._session.headers.update({'authorization': login_json['id']})
        self._email = email
        self._password = password
        self.user = Person(self, login_json['userId'])
        self.user.refresh()

        return self.user
1 Like

I’ve been seeing this issue for a few years now.
Sometimes when I restart my HA container, I loose the Leviton lights; but usually I can just restart again and get them back. Today no luck. I also have nothing in the logs for Leviton, just the “Home Assistant is starting, not everything may be available yet” blurb on my buttons that never goes away.

Well after the 5th restart I have them back.
I am trying to go as much local control as possible because of issues like this. I have slowly moving over to Zwave light switches but it takes time and money.

1 Like

I have found once every two months or so I see the dreaded:

[homeassistant.components.light] Updating decora_wifi light took longer than the scheduled update interval

I got a chance to attach a debugger this time and found that the underlying decora_wifi library was getting stuck indefinitely making a routine API call. I sent a PR to add a timeout which should hopefully fix this issue if we can get it released: Add 15sec request timeout to all API calls, fixing indefinite hangs by mitchsw · Pull Request #22 · tlyakhov/python-decora_wifi · GitHub. :crossed_fingers:

2 Likes