Old working components/platform quit working (July) quit after update (Dec '18)

Bottom line, I wrote a custom component back in July 2018 and all worked as expected. My next Home Assistant software update was December 2018.

The error is:

Log Details (ERROR)
Fri Jan 18 2019 07:58:34 GMT-0600 (Central Standard Time)

Error doing job: Task exception was never retrieved
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/homeassistant/helpers/discovery.py", line 151, in async_load_platform
    assert hass_config, 'You need to pass in the real hass config'
AssertionError: You need to pass in the real hass config

I will post some relevant details later, but first, let me set some context via a story. I did a custom component, I called “Almond_Plus”, that supported a switch and covers back in July of 2018. I have not had time to mess with Home Assistant in awhile. Knowing that things are changing fast (user login, Lovelace UI, entities naming, …), I did not want to far behind and then have to make a big jump. Too late. :slight_smile: When I did update, the switches and covers from my custom components quit showing up. At that time I assumed I needed to make some changes to my code. :slight_smile: I did not have time to mess with it and still don’t. But I did take a closer look at the log this morning. I have what I call a virtual switches. They are named “paul_on_way_home_switch” and “testswitch”. I can ask Google to trun on “my way home”, and the virtual switch is turned on and the correct automation ran. Under the current entities both “paul_on_way_home” and it’s debugging counterpart “testswitch” show up. I was following my debugging information from almond_plus that writes to the log.

Now for the details. I will attempt to present them in what I see as more relevant to “I don’t know if you need this but here it is”. :slight_smile:

Here is a snippet of the log focused on the virtual switches mixed in with the Almond_plus debug statements …

2019-01-18 07:58:32 DEBUG (SyncWorker_9) [custom_components.almond_plus] Loading switch platform
2019-01-18 07:58:32 DEBUG (SyncWorker_9) [custom_components.almond_plus] Loading cover platform
2019-01-18 07:58:34 DEBUG (SyncWorker_9) [custom_components.almond_plus] switch list 1: ['switch.paul_on_way_home_switch', 'switch.testswitch']
2019-01-18 07:58:34 DEBUG (SyncWorker_9) [custom_components.almond_plus] switch list 2: None
2019-01-18 07:58:34 DEBUG (SyncWorker_9) [custom_components.almond_plus] cover list 1: []
2019-01-18 07:58:34 DEBUG (SyncWorker_9) [custom_components.almond_plus] cover list 2: None
2019-01-18 07:58:34 DEBUG (SyncWorker_9) [custom_components.almond_plus] Setup ended with True
2019-01-18 07:58:34 INFO (MainThread) [homeassistant.setup] Setup of domain almond_plus took 4.5 seconds.

Here is the almond_plus.py set up code.

def setup(hass, config):
    """Set up the Almond+ component."""
    return_status = False
    try:
        global myhass_data
        myhass_data = {}
        myhass_data["config"] = config[DOMAIN]
        connect_url = config[DOMAIN][CONF_URL]
        myhass_data["almondplus_api"] = AlmondPlus(connect_url, almond_plus_call_back)
        myhass_data["almondplus_switch_entities"] = None
        myhass_data["almondplus_cover_entities"] = None
        hass.data[DATA_ALMONDPLUS] = myhass_data
        hass.data[DATA_ALMONDPLUS]["almondplus_api"].start()
        time.sleep(2)
        _LOGGER.debug("Test Almond+ setup: "+str(hass.data[DATA_ALMONDPLUS]["almondplus_api"].get_device_list()))
        _LOGGER.debug("Loading switch platform")
        load_platform(hass, 'switch', DOMAIN, discovered=None, hass_config=None)
        _LOGGER.debug("Loading cover platform")
        load_platform(hass, 'cover', DOMAIN, discovered=None, hass_config=None)
        time.sleep(2)

        switches = sorted(hass.states.async_entity_ids('switch'))
        _LOGGER.debug("switch list 1: "+str(switches))
        tmp_switches = myhass_data["almondplus_switch_entities"]
        _LOGGER.debug("switch list 2: "+str(tmp_switches))

        covers = sorted(hass.states.async_entity_ids('cover'))
        _LOGGER.debug("cover list 1: "+str(covers))
        tmp_covers = myhass_data["almondplus_cover_entities"]
        _LOGGER.debug("cover list 2: "+str(tmp_covers))

        return_status = True
    except Exception as e:
        _LOGGER.error("Error\n"
                      +"**************************\n"
                      + str(e) + "\n"
                      + traceback.format_exc()
                      + "**************************")
    _LOGGER.debug("Setup ended with "+str(return_status))
    return return_status

Looking at the code, I expect “Switch list 1:” to be the list of current switches. Then “Switch list 2:” should show the switches that were read created in the platform for the switches.

Here is the code for that.

def setup_platform(hass, config, add_devices, discovery_info=None):
    global my_almond_plus
    return_value = False
    try:
        _LOGGER.debug("Started - find me 2")
        my_almond_plus = hass.data[DATA_ALMONDPLUS]["almondplus_api"]
        switches = []
        _LOGGER.debug("looking for devices (switch)")
        for almond_key, almond_entity in my_almond_plus.get_device_list().items():
            if almond_entity.value_type == "1":
                tmp = AlmondPlusSwitch(almond_entity)
                _LOGGER.debug("Device -"+tmp.id+", "+tmp.device_id+", "+tmp.state+", "+tmp.name)
                switches.append(tmp)
            #switches.append(AlmondPlusSwitch(almond_entity))
        if len(switches) > 0:
            add_devices(switches)
            hass.data[DATA_ALMONDPLUS]["almondplus_switch_entities"] = switches
        return_value = True
    except Exception as e:
        _LOGGER.error("Error\n"
                      + "**************************\n"
                      + str(e) + "\n"
                      + traceback.format_exc()
                      + "**************************")
    _LOGGER.debug("Setup ended with " + str(return_value))
    return return_value

One other note, the AlmondPlusSwitch class code is …

class AlmondPlusSwitch(SwitchDevice):
    """Representation of an Almond+ switch."""

    def __init__(self, device):
        self._id = None
        self._device_id = None
        self._name = None
        self._state = None

        """Attributes"""
        self.friendly_device_type = None
        self.type = None
        self.location = None
        self.last_active_epoch = None
        self.model = None
        self.value_name = None
        self.value_value = None
        self.value_type = None
        self._update_properties(device)

    def _update_properties(self, device):
        self._id = device.id
        self._device_id = device.device_id
        self._name = DOMAIN+"_"+device.name + '_' + device.id + '_' + device.device_id
        self._state = ''
        self.set_state(device.value_value)

        """Attributes"""
        self.friendly_device_type = device.friendly_device_type
        self.type = device.type
        self.location = device.location
        self.last_active_epoch = device.last_active_epoch
        self.model = device.model
        self.value_name = device.value_name
        self.value_value = device.value_value
        self.value_type = device.value_type

    @property
    def name(self):
        """Get the name of the pin."""
        return self._name

    @property
    def id(self):
        """Get the name of the pin."""
        return self._id

    @property
    def device_id(self):
        """Get the name of the pin."""
        return self._device_id

    @property
    def is_on(self):
        """Return true if pin is high/on."""
        _LOGGER.debug("is_on "+self.name+"-"+self._state)
        return self._state == "on"

    @property
    def should_poll(self):
        return False

    def turn_on(self, **kwargs):
        _LOGGER.debug("Turn on")
        my_almond_plus.set_device(self.id, self.device_id, "true")

    def turn_off(self, **kwargs):
        _LOGGER.debug("Turn off")
        self._state = 'off'
        my_almond_plus.set_device(self.id, self.device_id, "false")

    @property
    def device_state_attributes(self):
        """Return the optional state attributes."""
        data = {}
        data["id"] = self._id
        data["device_id"] = self._device_id
        data["friendly_device_type"] = self.friendly_device_type
        data["type"] = self.type
        data["location"] = self.location
        data["last_active_epoch"] = self.last_active_epoch
        data["last_active_datetime"] = datetime.datetime.fromtimestamp(int(self.last_active_epoch))
        data["model"] = self.model
        data["value_name"] = self.value_name
        data["value_value"] = self.value_value
        data["value_type"] = self.value_type
        return data

    def update(self, device):
        _LOGGER.debug("Switch Update -"+self._id+"-"+self.device_id+"-")
        self._update_properties(device)
        self.schedule_update_ha_state()

    def set_state(self, value_value):
        _LOGGER.debug("Setting State -"+value_value+"-"+value_value.lower()+"-")
        if value_value.lower() == "true":
            self._state = 'on'
            #self.turn_on()
            _LOGGER.debug("Setting on "+self._state)
        elif value_value.lower() == "false":
            self._state = 'off'
            #self.turn_off()
            _LOGGER.debug("Setting off "+self._state)
        else:
            self._state = 'unknown'
            _LOGGER.debug("Setting unknown " + self._state)
        _LOGGER.debug("Setting State Finish "+self._state)

This next part of the log I am showing is kind of long. It is the payload that makes it look complicated. The 30,000-foot view of it is that a message is sent and the Almond+ responded.

2019-01-18 07:58:31 DEBUG (SyncWorker_9) [custom_components.almond_plus] sending {"MobileInternalIndex":"239d7b4e-1b29-11e9-82a5-080027c659bb","CommandType":"DeviceList"}
2019-01-18 07:58:31 DEBUG (SyncWorker_9) [custom_components.almond_plus] Sent
2019-01-18 07:58:31 DEBUG (SyncWorker_9) [custom_components.almond_plus] After send
2019-01-18 07:58:31 DEBUG (Thread-3) [custom_components.almond_plus] {"MobileInternalIndex":"239d7b4e-1b29-11e9-82a5-080027c659bb","CommandType":"DeviceList","Devices" : {"2":{"Data":{"ID":"2","Name":"Under Cabinet MultiSwitch","FriendlyDeviceType":"BinarySwitch","Type":"43","Location":"Under Cabinet","LastActiveEpoch":"1547819073","Model":"Unknown: type=2017,","Version":"4","Manufacturer":"YALE"},"DeviceValues":{"1":{"Name":"SWITCH_BINARY1","Value":"true","Type":"1"},"2":{"Name":"SWITCH_BINARY2","Value":"false","Type":"1"}}},"4":{"Data":{"ID":"4","Name":"GarageDoorOpener Two Car","FriendlyDeviceType":"GarageDoorOpener","Type":"53","Location":"Default","LastActiveEpoch":"1547819063","Model":"Unknown: type=4744,","Version":"0","Manufacturer":"Linear"},"DeviceValues":{"1":{"Name":"BARRIER OPERATOR","Value":"0","Type":"44"}}},"5":{"Data":{"ID":"5","Name":"Garage Lights Inside/Outside","FriendlyDeviceType":"BinarySwitch","Type":"43","Location":"Default","LastActiveEpoch":"1547813298","Model":"Unknown: type=2017,","Version":"4","Manufacturer":"YALE"},"DeviceValues":{"1":{"Name":"SWITCH_BINARY1","Value":"false","Type":"1"},"2":{"Name":"SWITCH_BINARY2","Value":"false","Type":"1"}}},"6":{"Data":{"ID":"6","Name":"Vaulted Ceiling/Porch Light","FriendlyDeviceType":"BinarySwitch","Type":"43","Location":"Default","LastActiveEpoch":"1546315987","Model":"Unknown: type=2017,","Version":"4","Manufacturer":"YALE"},"DeviceValues":{"1":{"Name":"SWITCH_BINARY1","Value":"false","Type":"1"},"2":{"Name":"SWITCH_BINARY2","Value":"false","Type":"1"}}},"7":{"Data":{"ID":"7","Name":"Almond Click Purp","FriendlyDeviceType":"SecurifiButton","Type":"61","Location":"Default","LastActiveEpoch":"1547819073","Model":"ZB2-BU01 ","Version":"0","Manufacturer":"Securifi L"},"DeviceValues":{"1":{"Name":"PRESS","Value":"3","Type":"91"},"2":{"Name":"LOW BATTERY","Value":"false","Type":"12"},"3":{"Name":"TAMPER","Value":"false","Type":"9"}}},"8":{"Data":{"ID":"8","Name":"Outside outlet Entryway","FriendlyDeviceType":"BinarySwitch","Type":"1","Location":"Default","LastActiveEpoch":"1547816950","Model":"Unknown: type=2017,","Version":"4","Manufacturer":"YALE"},"DeviceValues":{"1":{"Name":"SWITCH BINARY","Value":"false","Type":"1"}}},"9":{"Data":{"ID":"9","Name":"Power Strip 1","FriendlyDeviceType":"BinarySwitch","Type":"1","Location":"Default","LastActiveEpoch":"1546349943","Model":"ZFM-80","Version":"4","Manufacturer":"Remotec"},"DeviceValues":{"1":{"Name":"SWITCH BINARY","Value":"false","Type":"1"}}},"10":{"Data":{"ID":"10","Name":"yellow click","FriendlyDeviceType":"SecurifiButton","Type":"61","Location":"Default","LastActiveEpoch":"1542916780","Model":"ZB2-BU01 ","Version":"0","Manufacturer":"Securifi L"},"DeviceValues":{"1":{"Name":"PRESS","Value":"0","Type":"91"},"2":{"Name":"LOW BATTERY","Value":"false","Type":"12"},"3":{"Name":"TAMPER","Value":"false","Type":"9"}}},"11":{"Data":{"ID":"11","Name":"GarageDoorOpener One  Car","FriendlyDeviceType":"GarageDoorOpener","Type":"53","Location":"Default","LastActiveEpoch":"1547819864","Model":"Unknown: type=4744,","Version":"0","Manufacturer":"Linear"},"DeviceValues":{"1":{"Name":"BARRIER OPERATOR","Value":"0","Type":"44"}}}}}
2019-01-18 07:58:31 DEBUG (Thread-3) [custom_components.almond_plus] load send rec: 239d7b4e-1b29-11e9-82a5-080027c659bb-{"MobileInternalIndex": "239d7b4e-1b29-11e9-82a5-080027c659bb", "CommandType": "DeviceList", "Devices": {"2": {"Data": {"ID": "2", "Name": "Under Cabinet MultiSwitch", "FriendlyDeviceType": "BinarySwitch", "Type": "43", "Location": "Under Cabinet", "LastActiveEpoch": "1547819073", "Model": "Unknown: type=2017,", "Version": "4", "Manufacturer": "YALE"}, "DeviceValues": {"1": {"Name": "SWITCH_BINARY1", "Value": "true", "Type": "1"}, "2": {"Name": "SWITCH_BINARY2", "Value": "false", "Type": "1"}}}, "4": {"Data": {"ID": "4", "Name": "GarageDoorOpener Two Car", "FriendlyDeviceType": "GarageDoorOpener", "Type": "53", "Location": "Default", "LastActiveEpoch": "1547819063", "Model": "Unknown: type=4744,", "Version": "0", "Manufacturer": "Linear"}, "DeviceValues": {"1": {"Name": "BARRIER OPERATOR", "Value": "0", "Type": "44"}}}, "5": {"Data": {"ID": "5", "Name": "Garage Lights Inside/Outside", "FriendlyDeviceType": "BinarySwitch", "Type": "43", "Location": "Default", "LastActiveEpoch": "1547813298", "Model": "Unknown: type=2017,", "Version": "4", "Manufacturer": "YALE"}, "DeviceValues": {"1": {"Name": "SWITCH_BINARY1", "Value": "false", "Type": "1"}, "2": {"Name": "SWITCH_BINARY2", "Value": "false", "Type": "1"}}}, "6": {"Data": {"ID": "6", "Name": "Vaulted Ceiling/Porch Light", "FriendlyDeviceType": "BinarySwitch", "Type": "43", "Location": "Default", "LastActiveEpoch": "1546315987", "Model": "Unknown: type=2017,", "Version": "4", "Manufacturer": "YALE"}, "DeviceValues": {"1": {"Name": "SWITCH_BINARY1", "Value": "false", "Type": "1"}, "2": {"Name": "SWITCH_BINARY2", "Value": "false", "Type": "1"}}}, "7": {"Data": {"ID": "7", "Name": "Almond Click Purp", "FriendlyDeviceType": "SecurifiButton", "Type": "61", "Location": "Default", "LastActiveEpoch": "1547819073", "Model": "ZB2-BU01 ", "Version": "0", "Manufacturer": "Securifi L"}, "DeviceValues": {"1": {"Name": "PRESS", "Value": "3", "Type": "91"}, "2": {"Name": "LOW BATTERY", "Value": "false", "Type": "12"}, "3": {"Name": "TAMPER", "Value": "false", "Type": "9"}}}, "8": {"Data": {"ID": "8", "Name": "Outside outlet Entryway", "FriendlyDeviceType": "BinarySwitch", "Type": "1", "Location": "Default", "LastActiveEpoch": "1547816950", "Model": "Unknown: type=2017,", "Version": "4", "Manufacturer": "YALE"}, "DeviceValues": {"1": {"Name": "SWITCH BINARY", "Value": "false", "Type": "1"}}}, "9": {"Data": {"ID": "9", "Name": "Power Strip 1", "FriendlyDeviceType": "BinarySwitch", "Type": "1", "Location": "Default", "LastActiveEpoch": "1546349943", "Model": "ZFM-80", "Version": "4", "Manufacturer": "Remotec"}, "DeviceValues": {"1": {"Name": "SWITCH BINARY", "Value": "false", "Type": "1"}}}, "10": {"Data": {"ID": "10", "Name": "yellow click", "FriendlyDeviceType": "SecurifiButton", "Type": "61", "Location": "Default", "LastActiveEpoch": "1542916780", "Model": "ZB2-BU01 ", "Version": "0", "Manufacturer": "Securifi L"}, "DeviceValues": {"1": {"Name": "PRESS", "Value": "0", "Type": "91"}, "2": {"Name": "LOW BATTERY", "Value": "false", "Type": "12"}, "3": {"Name": "TAMPER", "Value": "false", "Type": "9"}}}, "11": {"Data": {"ID": "11", "Name": "GarageDoorOpener One  Car", "FriendlyDeviceType": "GarageDoorOpener", "Type": "53", "Location": "Default", "LastActiveEpoch": "1547819864", "Model": "Unknown: type=4744,", "Version": "0", "Manufacturer": "Linear"}, "DeviceValues": {"1": {"Name": "BARRIER OPERATOR", "Value": "0", "Type": "44"}}}}}
2019-01-18 07:58:31 DEBUG (Thread-3) [custom_components.almond_plus] receive ended

My guess is from the error I am not setting up the hass object with the new security the right way, but looking at the current dev docs, it looks like the way it is passed has not changed. I am hoping someone can point me in the right way.

I changed the name because I was not getting any traffic and I thought maybe the subject text was confusing.

Note: Before I sent this message, I found one of the mistakes. The example in the developer documents at the time I wrote this code the hass.config position was set to none. I did two changes, 1 was to actually pass the “hass.config”. The second was to change the discovery from a positional parameter to fixed parameter.
I am posting it for anyone else who might make the same mistake.
It still is not finding my cover platform. I will start a new thread if I don’t find my mistake.

Here is the rest of what I was going to post.

So looking at after upgrading to .88, I am now not getting any errors. I was not even getting the “move to custom” message. As I was rereading the developer docs and googling, I did run across the “The Great Migration” blog. I did get the custom running in the new .88 format.
So, I have a directory off of custom_components call almond_plus. Inside that directory is the init.py, switch.py, and covers.py. The log still looks like before I move the files. In the “def setup” I initialize the almond_plus websocket and the debug logs show that all works. Then I load two platforms with a debugs above and below both load statements. Then I have debugged statements in the “def setup_platform”.

Here is the snippet from init.py (component part)

def setup(hass, config):
    """Set up the Almond+ component."""
    return_status = False
    try:
        global myhass_data
        myhass_data = {}
        myhass_data["config"] = config[DOMAIN]
        connect_url = config[DOMAIN][CONF_URL]
        myhass_data["almondplus_api"] = AlmondPlus(connect_url, almond_plus_call_back)
        myhass_data["almondplus_switch_entities"] = None
        myhass_data["almondplus_cover_entities"] = None
        hass.data[DATA_ALMONDPLUS] = myhass_data
        hass.data[DATA_ALMONDPLUS]["almondplus_api"].start()
        time.sleep(2)
        _LOGGER.debug("Test Almond+ setup: "+str(hass.data[DATA_ALMONDPLUS]["almondplus_api"].get_device_list()))
        _LOGGER.debug("Start Loading switch platform")
        load_platform(hass, 'switch', DOMAIN, discovered=None, hass_config=None)
        _LOGGER.debug("Done Loading switch platform")
        _LOGGER.debug("Start Loading cover platform")
        load_platform(hass, 'cover', DOMAIN, discovered=None, hass_config=None)
        _LOGGER.debug("Done Loading cover platform")
        time.sleep(2)

        switches = sorted(hass.states.async_entity_ids('switch'))
        _LOGGER.debug("switch list 1: "+str(switches))
        tmp_switches = myhass_data["almondplus_switch_entities"]
        _LOGGER.debug("switch list 2: "+str(tmp_switches))

        covers = sorted(hass.states.async_entity_ids('cover'))
        _LOGGER.debug("cover list 1: "+str(covers))
        tmp_covers = myhass_data["almondplus_cover_entities"]
        _LOGGER.debug("cover list 2: "+str(tmp_covers))

        return_status = True
    except Exception as e:
        _LOGGER.error("Error\n"
                      +"**************************\n"
                      + str(e) + "\n"
                      + traceback.format_exc()
                      + "**************************")
    _LOGGER.debug("Setup ended with "+str(return_status))
    return return_status

This is from switch.py (platform part)

def setup_platform(hass, config, add_devices, discovery_info=None):
    global my_almond_plus
    return_value = False
    try:
        _LOGGER.debug("Started - find me 2")
        my_almond_plus = hass.data[DATA_ALMONDPLUS]["almondplus_api"]
        switches = []
        _LOGGER.debug("looking for devices (switch)")
        for almond_key, almond_entity in my_almond_plus.get_device_list().items():
            if almond_entity.value_type == "1":
                tmp = AlmondPlusSwitch(almond_entity)
                _LOGGER.debug("Device -"+tmp.id+", "+tmp.device_id+", "+tmp.state+", "+tmp.name)
                switches.append(tmp)
            #switches.append(AlmondPlusSwitch(almond_entity))
        if len(switches) > 0:
            add_devices(switches)
            hass.data[DATA_ALMONDPLUS]["almondplus_switch_entities"] = switches
        return_value = True
    except Exception as e:
        _LOGGER.error("Error\n"
                      + "**************************\n"
                      + str(e) + "\n"
                      + traceback.format_exc()
                      + "**************************")
    _LOGGER.debug("Setup ended with " + str(return_value))
    return return_value