'Platform not found' for simple custom component in 0.94.2

I’m testing version 0.94.2, specifically its ability to use a custom_component and failing to succeed.

For this experiment, I’m just using a copy of the existing MQTT HVAC climate.py file. In other words, I’m using the stock 0.94.2 version of climate.py, not a customized version of it, and just putting a copy of it in custom_component/whatever directory.

In the directory custom_component/whatever/ there are two files:

  1. climate.py
  2. manifest.json

The contents of manifest.json:

{
  "domain": "whatever",
  "name": "Whatever",
  "documentation": "https://www.home-assistant.io/components/climate.mqtt/",
  "dependencies": [],
  "codeowners": [],
  "requirements": []
}

Theoretically, all this does is define a new platform called whatever containing one climate component (that is a verbatim copy from the MQTT platform).

In the config file I’ve defined a climate entity and configured it as you normally would if it were using the MQTT platform.

climate:
 - platform: whatever
   name: "Dummy"
   qos: 0
   payload_on: 1
   payload_off: 0
   modes:
     - auto
     - heat
     - cool
     - 'off'
   ... etc etc ...

In theory, 0.94.2 should load the custom_component whatever and then create an entity called climate.dummy based on the whatever platform.

It reports it loads the custom component:

You are using a custom integration for whatever which has not been tested by Home Assistant.

However, it fails to actually use it to create the climate entity. Either I’ve misunderstood how this is supposed to work or I’ve overlooked a critical detail.

When I restart Home Assistant, it logs an error message indicating:

Platform not found: climate.whatever

Running Config Check reports the same error:
Screenshot%20from%202019-06-13%2012-09-46

Just for fun, I added a blank __init__.py file to the whatever directory but that didn’t help.

What am I doing wrong?


NOTE
The interesting thing is if I put my customized MQTT HVAC climate.py file, which is based on code from version 0.89, into the whatever directory, it works perfectly. :man_shrugging:

The reason I’m not using the 0.89 code with 0.94 is because it contains some deprecated MQTT code. I get warning messages in the log reminding me the code is deprecated (some of the functions use a new signature). So I need to update it. However, so far I can’t even get the stock 0.94 climate.py to be handled as a custom component.

Now that I got all that off my chest, it helped clear my mind and made me realize the clue must be the fact the 0.89 code works whereas the 0.94.2 code does not.

There’s a critical difference between the two and in the 0.89 version the dependencies are spelled out within the code.

DEPENDENCIES = ['mqtt']

In 0.9X they are defined in manifest.json:

{
  "domain": "mqtt",
  "name": "MQTT",
  "config_flow": true,
  "documentation": "https://www.home-assistant.io/components/mqtt",
  "requirements": [
    "hbmqtt==0.9.4",
    "paho-mqtt==1.4.0"
  ],
  "dependencies": [
    "http"
  ],
  "codeowners": [
    "@home-assistant/core"
  ]
}

Perhaps all I need to do is use a copy of MQTT’s manifest.json and just change the values of domain and name to whatever.

Stay tuned …

No success.

Neither of the following two versions of manifest.json improved anything. Home Assistant reports it loads the whatever custom component but fails to create a climate.dummy entity.

Version 1:

{
  "domain": "whatever",
  "name": "Whatever",
  "config_flow": true,
  "documentation": "https://www.home-assistant.io/components/mqtt",
  "requirements": [],
  "dependencies": ["mqtt"],
  "codeowners": []
}

Version 2:

{
  "domain": "whatever",
  "name": "Whatever",
  "config_flow": true,
  "documentation": "https://www.home-assistant.io/components/mqtt",
  "requirements": [
    "hbmqtt==0.9.4",
    "paho-mqtt==1.4.0"
  ],
  "dependencies": [
    "http"
  ],
  "codeowners": [ ]
}

At the very least, don’t define “config_flow” as true unless you’re also supplying a config_flow.py file in your whatever folder. To simplify things, I’d change “config_flow” to false.

You might also try a quick experiment. From a terminal window, cd into your config directory, get into HA’s venv, run python, then try import custom_components.whatever and see what happens. Basically any errors that occur when the HA loader tries to load your whatever package will cause the platform not found error. And BTW, you do need an __init__.py file. I’d at least have a """This is my whatever integration.""" in it.

1 Like

Many thanks for the tips.

I haven’t tried the first suggestion yet (import the whatever component from the command line). As for the second one, I had tried it without the config_flow option (full disclosure, I don’t even know its purpose so it seemed prudent to remove it for what should be a barebones manifest) and it didn’t improve anything. I’ve now tried it again using an init file containing the suggested line and still no success (still fails to create the climate entity).

I’m going to say I was wrong about this message ‘You are using a custom integration for whatever …’ being an indication that the custom component was loaded. I say that because of this in syslog:

2019-06-13 14:34:01 WARNING (MainThread) [homeassistant.loader] You are using a custom integration for customizer which has not been tested by Home Assistant. This component might cause stability problems, be sure to disable it if you do experience issues with Home Assistant.

2019-06-13 14:34:01 INFO (MainThread) [homeassistant.loader] Loaded customizer from custom_components.customizer

The customizer component gets two messages whereas all I get for the whatever component is the first message but not the second one (the one stating it actually got loaded).

So my investigation needs to focus on why it can’t load the whatever custom component. My next task is to try your first suggestion.


UPDATE

I fully admit I’m operating above my paygrade now and need to be led by the hand.

  • I’m running 0.94.2 in a docker container.
  • Using Portainer, I opened a container console (default user is root)
  • I changed directory to /config/custom_components/whatever (not sure why but it seemed like good idea).
  • I ran python3
  • I entered import custom_components.whatever
  • The result is:

ModuleNotFoundError: No module named ‘custom_components’

I need additional guidance for this step.

Starting with HA 0.94.0, the “Platform not found” error should show the exact exception that caused it. See these lines. With __init__.py, and with the manifest.json as you think it should be, what are you seeing now in the “Platform not found” error?

Don’t cd to /config/custom_components/whatever, cd to /config, and try it again.

EDIT: Also, it’s important to be in the exact same Python context that HA will run in. Is that a venv? If you’re not in that environment, then any imports in the custom component will probably fail.

manifest.json contents:

{
  "domain": "whatever",
  "name": "Whatever",
  "documentation": "https://www.home-assistant.io/components/mqtt",
  "requirements": [],
  "dependencies": ["mqtt"],
  "codeowners": []
}

__init__.py contents:

"""This is my whatever integration."""

Screenshot of error message after a restart:
Screenshot%20from%202019-06-13%2015-18-49

Screenshot of the container console after carrying out your suggestion:
Screenshot%20from%202019-06-13%2015-16-57

Nothing reported in the console so not sure if that’s good or bad or a bit of both.

FWIW, I restarted the container (yet again) and examined the log more closely. In fact, it does claim to have loaded the whatever component. I just failed to see it previously because it appears earlier in the log:

2019-06-13 15:30:10 INFO (MainThread) [homeassistant.setup] Setting up timer
2019-06-13 15:30:10 INFO (SyncWorker_9) [homeassistant.loader] Loaded whatever from custom_components.whatever
2019-06-13 15:30:10 INFO (MainThread) [homeassistant.setup] Setting up input_boolean
2019-06-13 15:30:10 INFO (MainThread) [homeassistant.setup] Setting up python_script
2019-06-13 15:30:10 INFO (MainThread) [homeassistant.setup] Setting up sun
2019-06-13 15:30:10 INFO (MainThread) [homeassistant.setup] Setup of domain sun took 0.0 seconds.
2019-06-13 15:30:10 INFO (MainThread) [homeassistant.setup] Setting up alarm_control_panel
2019-06-13 15:30:10 INFO (MainThread) [homeassistant.setup] Setting up lovelace
2019-06-13 15:30:10 INFO (MainThread) [homeassistant.setup] Setup of domain lovelace took 0.0 seconds.
2019-06-13 15:30:10 INFO (MainThread) [homeassistant.setup] Setting up discovery
2019-06-13 15:30:11 INFO (MainThread) [homeassistant.setup] Setup of domain discovery took 0.0 seconds.
2019-06-13 15:30:11 INFO (MainThread) [homeassistant.setup] Setting up group
2019-06-13 15:30:11 INFO (MainThread) [homeassistant.setup] Setting up input_select
2019-06-13 15:30:11 WARNING (MainThread) [homeassistant.loader] You are using a custom integration for whatever which has not been tested by Home Assistant. This component might cause stability problems, be sure to disable it if you do experience issues with Home Assistant.
2019-06-13 15:30:11 INFO (SyncWorker_7) [homeassistant.loader] Loaded syslog from homeassistant.components.syslog

I assume the fact it loaded the component jibes with the results of carrying out your test where it raised no fuss with import custom_components.whatever.

Nevertheless, it still fails to create the defined climate entity using the whatever platform.

Suffering cats! Makes me want to fall back to using my component, based on 0.89, with 0.94.2 and just ignore the logged errors about ‘deprecation’. I realize I’m just delaying the inevitable and in some future version (0.95? 0.96?) it’ll probably escalate from ‘deprecated’ to ‘unsupported’.

I can’t put my finger on it yet but some silly little thing is making this needlessly difficult …

In case anyone is still following along, here’s another thing that failed to resolve this issue.

Before the advent of manifest.json, dependencies were specified within the component’s code. So I added it to the climate component’s code (i.e. the one using the 0.94.2 version of MQTT HVAC). So now it’s specified in two places, in the code and in manifest.json.

_LOGGER = logging.getLogger(__name__)

DEPENDENCIES = ['mqtt']

DEFAULT_NAME = 'MQTT HVAC'

It didn’t help. Home Assistant continues to report:

Platform not found: climate.whatever

I then edited manifest.json and eliminated the dependency on mqtt.

{
  "domain": "whatever",
  "name": "Whatever",
  "documentation": "https://www.home-assistant.io/components/mqtt",
  "requirements": [],
  "dependencies": [],
  "codeowners": []
}

Now the reference to a dependency is only in one place, in the code. That didn’t fix it either.

So far this has been in an exercise in documenting everything that does not work to correct this problem …

I re-read this section in the Great Migration blog: Note for custom component developers, specifically the third bullet point:

If you want to share an adjusted version of a Home Assistant integration, copy over ALL the files. Do your users a favor and stick to relative imports to avoid having your component break during an upgrade. Example of a relative import is from . import DATA_BRIDGE .

Relative imports? Uh-oh!

If you copy a stock component into custom_components, you’ve changed its location. If you don’t copy any other part of the integration it belongs to, then it now stands alone. If it uses relative imports, it will look for them relative to its new location in custom_components … and won’t find them.

I checked the code in climate.py version 0.94.2 and, sure enough, it uses two relative imports:

from . import (
    ATTR_DISCOVERY_HASH, CONF_QOS, CONF_RETAIN, CONF_UNIQUE_ID,
    MQTT_BASE_PLATFORM_SCHEMA, MqttAttributes, MqttAvailability,
    MqttDiscoveryUpdate, MqttEntityDeviceInfo, subscription)
from .discovery import MQTT_DISCOVERY_NEW, clear_discovery_hash

I checked the code in my functional climate.py based on code from version 0.89 and those two imports use absolute references, not relative:

from homeassistant.components.mqtt import (
    ATTR_DISCOVERY_HASH, CONF_QOS, CONF_RETAIN, CONF_STATE_TOPIC,
    CONF_UNIQUE_ID, MQTT_BASE_PLATFORM_SCHEMA, MqttAttributes,
    MqttAvailability, MqttDiscoveryUpdate, MqttEntityDeviceInfo, subscription)
from homeassistant.components.mqtt.discovery import (
    MQTT_DISCOVERY_NEW, clear_discovery_hash)

I commented out the relative imports, pasted in the absolute ones, restarted Home Assistant and, ta-dah, it works now. It found the whatever platform and created the climate.dummy entity.

It all seems so obvious now but so many other possibilities had to be eliminated to get to this point!

3 Likes

Really enjoyed this post, you describe how lots of things work with good example. It has been entertaining following along and see what is required to update, pretty sure this will help more people who upgrade and/or use custom_components! :smiley:

Yeah, I was wondering about that. Specifically, that you chose a piece of a large integration and not the whole thing and wondered if that might be the problem – climate.py was importing stuff from the other pieces. But I thought, if that were the case, you’d see that when you did the import test. I guess the test was flawed. Maybe if I had suggested:

from custom_components.whatever import climate

or even:

import custom_components.whatever.climate

it would have provided more useful feedback. D’oh! Sorry about that. I guess I wasn’t thinking. The thought was if you imported the “package”, then it would import everything in the package. But really, if __init__.py didn’t import anything from climate.py, that wouldn’t be the case.

To be precise, this problem may occur in a very specific situation and not for all custom_components. I’m taking just one component (climate.py) of a large integration (mqtt) and identifying it as a new platform (in this example I called it whatever).

The code inside the climate.py file contains references to other parts of the mqtt integration. Two of those references were relative to the file’s location. It’s those two references that caused the problem because my copy of climate.py is located here:

custom_components/whatever

and not in its original location here:

components/mqtt

So this problem may occur only for people creating custom_components for portions of existing, large integrations (and those portions contain relative imports).


@pnbruckner

I reverted the code to use relative imports in order to test your suggestion to use:

import custom_components.whatever.climate

In this case, the python3 interpreter did report an error, but not one that points to the issue of relative imports. It carps that it can’t find the homeassistant module.

FWIW, the other command (from custom_components.whatever import climate) prodcued the same error message.

That’s probably because you’re not in the same Python context as HA normally runs in. What type of install are you using? If it’s a venv, then first activate that venv, then run Python and the import command.

It’s a docker container. I’m using Portainer’s ‘Container Console’ to access the container’s command line (defaults to user root). All files related to Home Assistant are owned by root.

Well, you’ve already figured out your issue, but for the future, you might want to figure out how to get into HA’s Python context. I don’t use Docker, so I have no idea how that works (i.e., how HA is launched in that context.)

That makes two of us. The container is a black box and I know very little about how Home Assistant is configured within it. There’s this reference but the only thing meaningful to me is the last line which suggests a very vanilla startup, running as the default user (root):


CMD [ "python", "-m", "homeassistant", "--config", "/config" ]

We might be beating a dead horse here, but try these and see what you get:

python -m site
python3 -m site