Notes as I go: my attempt to develop a Home Assistant integration, part 1

Set up Development Environment says the “easiest way to get started with development is to use Visual Studio Code with devcontainers”, but I dislike both those things, so I go with Manual Environment instead.

I’m not doing core development or even integration PRs for now, so I don’t need a fork; instead I just git clone --depth 1 https://github.com/home-assistant/core.git.

For the dependencies, sudo apt install python3-pip python3-dev python3-venv autoconf libssl-dev libxml2-dev libxslt1-dev libjpeg-dev libffi-dev libudev-dev zlib1g-dev pkg-config libavformat-dev libavcodec-dev libavdevice-dev libavutil-dev libswscale-dev libswresample-dev libavfilter-dev ffmpeg


I then try to run script/setup, except that’s not going to work, because it’s not in my path; to call it from the core root directory using a relative path, it has to be ./script/setup

Some python packages get installed, and in the end it craps out with “no such option: –config-settings”. Apparently Python 3.10, which my Ubuntu 22.04 desktop has, is no longer supported, so I need to redo all of the above on my laptop (which is running Ubuntu 23.10 with Python 3.11).


I then try python3 -m script.scaffold integration, which is what I’m here for, but it fails with “ModuleNotFoundError: No module named ‘attr'”. Nothing useful comes up on Google, but then I realize I need to source venv/bin/activate first.

After I answer the scaffolding questions, the script begins building the scaffold, but that also craps out, this time with “ModuleNotFoundError: No module named ‘numpy'”. pip3 list reveals this to be true, so I pip3 install numpy.

I restart python3 -m script.scaffold integration, which this time only asks the first question, then (correctly) assumes my previous answers to the others are still valid, and finally generates the scaffolding. Doesn’t tell me where it is, but git status shows theres new stuff in homeassistant/components/ and in tests/components/.

“The next step is to look at the files and deal with all areas marked as TODO.”


I rsync the scaffolding back to my desktop and open __init__.py in VSCodium. The first TODO says “List the platforms that you want to support.” By default it’s set to LIGHT, but my thing is a button, so I go looking for the platform alternatives. “There are lights, switches, covers, climate devices, and many more.”

The “many more” link goes to an entities introduction page, so I assume the side menu listing of Entities is what I have to choose from. As I said, I’m developing a button, so you’d think, a Button, right?

Of course not:

“not suitable for implementing actual physical buttons; the sole purpose of a button entity is to provide a virtual button inside Home Assistant.”

“If you want to represent something that can be turned on and off (and thus have an actual state), you should use a switch entity instead.”

I don’t, because mine doesn’t, so no.

“If you want to integrate a real, physical, stateless button device in Home Assistant, you can do so by firing custom events. The entity button entity isn’t suitable for these cases.”

Well, I’d like to fire events, but this only seems to draw further away from what “platform” I need to use for my integration.

The scaffolding imports Platform from homeassistant.const, so I find the Platform enumeration in const.py, but it just lists the same entities as the documentation side panel, so I’m out of luck.

No, wait, Event is also listed. So I’ll go with Platform.EVENT for now. That means I’ll need to “create a file with the domain name of the integration that you are building a platform for” — so in this case, event.py.

Actually, Integration File Structure says

“If the integration only offers a platform, you can keep [__init__.py] limited to a docstring introducing the integration”.

So that means I don’t need any of the __init__.py scaffolding?


Whatever, I’ll take a look at the manifest instead.

Integration type was not set up by the scaffolding script (despite the documentation saying it should be set, and becoming mandatory in the future). I’m going to pick device, which seems unambiguous, for once.

Version is mandatory, I’ll go with 0.1.

My button communicates via Bluetooth, and Best practices for this says I need bluetooth_adapters in my dependencies.

I’ve used bluetoothctl to sniff out what name the device uses, so I’m also defining a local_name matcher in a Bluetooth section. There’s also connectable, but it defaults to true, which is what it should be for my button.

In the scaffolding, I’d previously set iot_class to local_polling, and I’m going to leave it as such, although for now, I’m treating the button as if didn’t have any state.


Next, config_flow.py.

Discoverable integrations that require no authentication” looks useful, so I go back to my laptop, source venv/bin/activate again, then python3 -m script.scaffold config_flow_discovery, and finally rsync the new files (now named EXAMPLE_*) back again. The only changed file was config_flow.py, which is now substantially smaller, defining mostly just _async_has_devices().

The only TODO there is “Check if there are any devices that can be discovered in the network.” Spying from one core integration, my guess is that _async_has_devices() receives a bunch of discovered devices, and should return true only if any of those devices are ones that my integration is actually able to configure.

Why/whether this isn’t already handled by the matcher definition in the manifest, I don’t know; I found at least one integration that always returns true, with a comment “MQTT is set as dependency, so that should be sufficient.”


I minimally tweak the MyEvent example and save it as event.py. I think it’s missing at least imports of EventEntity and EventDeviceClass. Also, there’s a tip about being “sure to deregister any callbacks when the entity is removed from Home Assistant”, so why isn’t that incorporated into the example?