I have never written a Home Assistant Integration. However, the Home Assistant Team have made it as easy as possible . I own an EMU M-Bus center which polls several M-Bus Power meters in my main breaker panel and offers the polled data over a REST API.
I want to consume this REST API into a Home Assistant Integration. I know there are REST sensors in HA that you can configure in YAML, but I want to get the experience of writing am Integration. Also, I’m supposed to learn Python and need a good Project to keep me motivated. The end goal is to have the Integration be able to read an arbitrairy amount of EMU Allrounder Power meters (because that’s all I have available for testing). I want to publish the Integration on the Home Assistant Community Store.
All right. This is the first day I am spending time with the project. I’m glad the dec31st deadline has been extended, otherwise it would probably not be possible.
Home assistant have created a nice Dev environment and guides on how to write integrations, so I’m setting up the environment and reading the documentation.
Lesson 1: doing an integration for HACS is not the same as doing one for HA. the latter would mean a PR into the core repo. HACS have their own style guide published here and their own (optional) dev contianer here
Okay. I strongly suggest you go the config route. in the directory where HA is installed, there is a fille called configuration.yaml. there you can define your own sensors. Depending on the interface available on your battery and inverter you’d integrate them there (if there is not already an integration for those devices).
Next hurdle: The dev container i am using is a bit older. you must run git config --global url.https://github.com/.insteadOf git://github.com/ before you can run the task Run Home Assistant on port 9123 for the first time.
Also you should update the version pf pip that comes with it.
and lastly you need to add a “version” field to the manifest.json so your custom integration loads properly.
I found some more resources and made some more progress. Home assistant maintains a repo with example configurations for custom components. On the one hand this is great, because it’s info straight from the horse’s mouth, on the other hand it shows just how outdated the cookiecutter template and its dev container are.
On the issue of the devcontainer: while I did get it to run yesterday, it seems that the changes made in VScode are not automatically reflected in the custom component in the HA instance of the dev container. this is an issue. So today I have set up a HA instance on a RasPi4, SCPd my custom component to it and was immediately greeted with an error. Yay, my changes get reflected, but it also meand I borked it.
Tomorrow I wont be working on this, but friday I intend to dive deeper in the framework and API provided by home assistant with the goal of getting the Installation wizard to run trough.
I have now added the additional info as attributes and am now doing a fair bit of error handling for the GET requests as well as some good validation of the response. The integration now works on a basic level.
Up next:
see if I can do multiple sensors with only one config entry
It is now possible to create multiple sensors with one config entry. This will clean up the config in a big way and also pave the way for a smooth setup workflow.
I have fixed the issues and uploaded some logos. The PR for the logos has already been accepted into the Home Assistant logos Repo. This means that all checks for the PR into the HACS repo now pass.
While I wait for this PR to be accepted, I will do further work on the config flow. However, the documentation I find (mostly this and this page) does not provide the level of handholding I seem to need. I am therefore looking at existing integrations to figure out what does what and which methods get called in which event.
I made some good progress on the config flow today. I had gotten stuck because I did not understand the sequence in which the methods get called.
At the root of my problem was a different one: when you switch to a config flow (as opposed to configuration via YAML), Home Assistant highly encourages you to also implement some other modernities, namely non-blocking functions and the use of a coordinator (say an API call returns a JSON with multiple values. Each of those values gets mapped to its own entity in Home Assistant. The old way would have been to either call the API for every entity you want to update or to choose one main value, create one entity for that value and put all the other values into attibutes for that one entity (which is the route I had chosen in my MVP). The Coordinator lets you make one API call and then distribute the return value to multiple individual HA entities).
I have now started doing all that. the coordinator is already working, but i have some unsolved problems with the async python, which I have zero experience with.
And for future reference, the thing I got stuck on was this: When the config_flow.async_step_user() method has run trough, the next thing that gets called is __init__.async_setup_entry() and not, as I had thought, sensor.async_setup_entry().
In have slain the beast that is async python. or at least: it works in my very narrow use case, in this very protected environment. The Sensors all get their update, from one single API call, that is even non-blocking.
My PR has beed merged, my repo is now in the default HACS list, which means it can now (or as soon as HACS has done their refresh of the reop list) be installed trough the HA UI without any weird trickery.
Meanwhile, I am still working on the config flow. I’ve gotten it to work in a way that you have to input the IP of the center and all the sensors you want to add, formatted as a python dict. that is not very user-friendly. So I have added a scanning feature that checks the center for connected sensors. I am working on incorporating that into my workflow.
EDIT: The Repo now shows up in the HACS list:
I know the logo looks atrocious. It was really done with light mode in mind:
I have already issued a PR to add dark theme logos, it is currently waiting for approval.
I am making some progress in the Config flow. For the first time today I achieved that my prompts actually have my labels. It was not working due to two things. first: apparently, home assistant is looking for the labels in ./translations/en.json and not, as the template has suggested in ./strings.json. Second: As is documented, the frontend caches heavily. This apparently also applies to the labels (but not the entry fields themselves) of the config flow. so any time you have changed something on the labels of the config flow, you must do a full cache refresh of your browser after the restart of the home assistant instance. This is cumbersome, but now that I know it, I know what I have to do.
Lastly: As I have talked about i have incorporated a scanning feature to discover all the sensors. Now I need to figure out how I can add the Sensor ID as a label to those dynamically generated fields. My fear is rising that the correct way to do things is not to discover all the sensors during the config flow, but to implement the autodiscover flow. I really dont want to have to do this for the initial release.
I have finished the config flow, written the documentation and made a new release. All my work is now available in HACS. I have therefore reached the goal I set.
for anyone reading this in the future: I have certainly made the right decision to only publish this integration in the HACS repo and not targeting an induction into core HA. first i only had one kind of sensor to test with and to release an integration to Core that only covers a small portion of the Center’s abilities would have been ill-advised. second and more importantly for my sistuation: there are development guides that would have been hard for me to achieve. first and foremost, they dont want the integration to make calls to the center’s API directly. Instead, one must write an API wrapper, publish that to PyPi and then use that wrapper in your integration, which would have been another hurdle for me to overcome, as this is another thing I have never done.