Add-on: kodi2home

Kodi2home

In home assistant you can connect to Kodi and control Kodi from Home assistant, but you cannot trigger an action when pressing a button on Kodi. This is where Kodi2home comes in to play. Kodi2home connects to the websocket from Kodi and to the websocket from home assistant. When it receives a “notifyall” from Kodi, it will trigger an automation on home assistant. In Kodi you change the keymap so that when you press a button it wil send the “notifyall” message. This whay you can turn on a light with the remote from Kodi.

mermaid-diagram-2023-11-17-210154

Note: When you change a button in the keymap of Kodi, it will override the existing function of that button. But you can control Kodi in your home assistant automation, and still do the same action. See Example

Install

You Need the following things to make this work:

  • Home assitant
  • Kodi
  • A Usb remote connected to kodi
    (IR or Bluetooth problobly possible but not tested)
  • A whay to change the keymap on kodi

    (example: ssh in to libreelec or notepad on windows)

Install kodi2home by going to Settings → Addon’s → Add-on store → Vertical … (upperright) → repositories → fill in the url: https://github.com/DJJo14/kodi2home.
After a refresh the Kodi2home is now in you add-on store. click on it and press install. After installing you must configurate the add-on the configuration tab.
Example of the configuration:

kodi_adress: youre kodi ip adress
kodi_http_port: 8080
kodi_ws_port: 9090
kodi_username: Youre username
kodi_password: Youre password
home_adress: 'ws://supervisor/core/api/websocket'
home_ssl: false

If you are using hassio you do not need to change the home_adress and home_ssl. The values are already set to the add-on values.

You need to change the keymap on kodi. More info about the keymap. There is a “Add-on:Keymap Editor” but you can not set it to send the NotifyAll message. So you need to change it in the keyboard.xml (or other .xml name)
put somting like this in your keymap of kodi:

<volume_up>NotifyAll("kodi2home", "kodi_call_home", {"trigger":"automation.volume_up"})</volume_up>

Add an automation on home assistant with the id/name: “volume_up”.(you can leave automation triggers empty) Start up the kodi2home add-on and press the volume_up button. The automation will be triggerd on Home Assistant. Home Assistant run you action that you put in you automation.

Why

Home assitant can control kodi, but when you press a button on kodi you can not trigger a action on home assistant. Kodi2home is a man-in-the-middel. I found this a missing feature of Home assistent. This way you can turn on a light with a kodi remote. Or change the volume on your amplifier instead of kodi. Or much smarter things.

Why this way

With Kodi you can call scripts and with that do the same. For example call a script that calls a webtrigger. Because it has to start the script and then connect, it can be a sec later before the action is done. I created a video to see the difference. Watch closely to number change. Volume up is kodi2home and down is a script.
Call a script vs kodi2home

NotifyAll?

The hard part is to send out a message from kodi, without calling a script. Because calling a script from kodi takes almost 1 sec. This is where the NotifyAll comes in, this is a buildin message from kodi sending to all of the connected (web-)sockets.

How is it done

Kodi2home is not more than two websockets connected to each other. One side is lisening for the “NotifyAll” and the other side sending it to Home Assistant. Both of the websockets are already connected. So there is less delay there.
The script is programed just like Home Assistant kodi intergration, so if it is liked, it can be intergated in to Home Assistant kodi intergration.

Reload keymap

When you start the addon the keymap of kodi gets reloaded. So no need to restart all of kodi when changing the keymap(.xml), just restart the addon.

Known issue

Possibly fixed in 0.3.0, let me know. For some reason Home Assistant disconnects when you fire to may, automations at ones. And it response to that with a disconnect, i do not know a better way to then to just reconnect.

Example

This is a example how I use it:
mermaid-diagram-2023-11-17-210131

The remote controls the Smart TV or Kodi depending on withs the tv gets is source from. This way you control Smart-TV apps (like Netfix) and Kodi with the same remote and the same buttons.
In webos_example_automation.yaml is the example of the automations that switches the remote between kodi(at the HDMI) and the Smart-TV apps. This way you can control the tv with one remote. In webos_example_keymap.xml is the kodi keymap to use with a Measy remote.
Measy-remote

Update 2023-11-17: I Do not think any one understands what this was. I re-written the text in the hope that more people will understand what this is.

5 Likes

Wouldn’t this be simpler adding actions via kodi’s callbacks addon?

But you end up calling a script. And that has to set up a connection (bij calling a http).I dit do it this was but sometimes it cost seconds to before the script is called.

This way the connection is already made and it only have tot send some date over it.

I made a video to see the difference https://youtu.be/MlcBf1nm40w
volume up is using kodi2home
volume down is calling a script from the keymap.xml (and then doing a http call)

A Some small update 0.2.1. Which also reconnects Home Assistant when the connection is lost.

One small problem, with a current workaround: when you fire too much automations Home Assistant cuts the connection. This is what i get in the log of Home Assistant

Home Assistant WebSocket API error: Received message 8:1011 is not WSMsgType.TEXT

The current workaround is, just reconnection. But let me know is some one has better ideas.

Examples

On the github page I have a example of the automations that switches the remote between kodi(at the HDMI) and the webos apps. This way you can control the tv with one remote. For example a Measy GP811 or a RII usb wireless keyboard. In webos_example_keymap.xml is the kodi keymap to use with that.

I hope i made my self clear in what the addon does

Hi! This looks great.
I’m just wondering can it be used to call services directly, or is it only for triggering automations?
For example rather than {"trigger":"automation.volume_up"} could I do something like {"call_service":"media_player.volume_up"} with an entity_id? How would the syntax look if that is possible?
If not I will make a feature request, as it would save adding many extra automations to be able to call a service directly :slight_smile:

Currently it is not possible to call a service. And this is done on purpose, because of security reasons, if someone has access to the json_rpc in kodi, it can control also all of home assistant. it was done in this way because there is a trigger function in home assistant, “kodi_call_method_result” but this only response to a “call_method”. and i wanted a asynchronous way.

If you you want this, i think the correct way to do this is to create white and black list. Witch will take sometime to code (i do not go a lot of spare time), so maybe in the far future. Now i think of this a white or black list is also not a bad idea for the current automations.

You can create a fork and do this youre self (it shut not be that hard, if you know python), and maybe i can accept this in the code.

Currently it it only automations, so it can be a lot of copy paste work.

Automations may actually suit perfectly, as, apart from the security reasons you mentioned, I can add some logic to control different devices depending on device states.

I’ve just moved to an Ubuntu server install and of all addons I miss this the most. If I can support you to develop this as an integration do let me know. :v:

Still missing this addon. Any tips on how I might make it work as a standalone docker container? Been trying to rework your code to see if i could get it running but its a shot in the dark really…

I will try to help you sinds i think you are one of my only users. (I think there needs to be some better explaining over how this works). Do you have some experience running code or creating a docker container?

Developt this add on without a container. So i thing you can run it without a container, just the python script it self. I does not matter on with machine in your network it runs as long as it can connect to both servers.

Take a look in the “kodi2home” Dir in my repository.

You need to create a “options.json” (normaly the homeasistant takes care of that.)
This is what needs to be in the options.json

{
  	"kodi_adress": "youre kodi ip adress",
  	"kodi_http_port": 8080,
  	"kodi_ws_port": 9090,
  	"kodi_username": "Youre username",
  	"kodi_password": "Youre password",
  	"home_adress": "ws://homeassistnat>/core/api/websocket",
  	"home_ssl": false
  	}

Run the “kodi2home.py” with python 3 and your api as argument. example

python3 kodi2home.py ajfjdfklss1111apicode111

This shut work, you need some python packages or run it in a docker container

python3 -m pip install --no-cache --upgrade pip setuptools websockets pykodi

Thank you so much for this!
I think this will get me on the way.
I had been exploring your code a little, and had a docker running, but I was missing certain key parts such as the API argument. I hope to get this running standalone or perhaps in appdaemon.
I am away from my home server at the moment but look forward to try out your suggestions next week.
Thanks again.

Confirmed and working in a standalone docker container. Thank you for your help.

Version 0.3.0

  • now handling the messages from home assistant
  • Fixed Random disconnects from home assistant
    (Let me,know if it worked)
3 Likes

The addon looks really useful, however I have problems with authentication.
Do you have an idea where can be the problem?
log:

Traceback (most recent call last):
  File "/kodi2home.py", line 2, in <module>
    from pykodi import get_kodi_connection, Kodi, CannotConnectError, InvalidAuthError
  File "/usr/lib/python3.11/site-packages/pykodi/__init__.py", line 1, in <module>
    from .kodi import get_kodi_connection, Kodi, CannotConnectError, InvalidAuthError
  File "/usr/lib/python3.11/site-packages/pykodi/kodi.py", line 8, in <module>
    import jsonrpc_websocket
  File "/usr/lib/python3.11/site-packages/jsonrpc_websocket/__init__.py", line 1, in <module>
    from .jsonrpc import Server, TransportError  # noqa: F401, F403
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/jsonrpc_websocket/jsonrpc.py", line 7, in <module>
    import async_timeout
ModuleNotFoundError: No module named 'async_timeout'

I am using HA supervised running on Debian 11. Connectivity to kodi in browser with same credentials works correctly.
Addon config is quite simple:

kodi_adress: 192.168.1.x
kodi_http_port: 8080
kodi_ws_port: 9090
kodi_username: kodi
kodi_password: pwd
home_adress: ws://supervisor/core/api/websocket
home_ssl: false

Thanks.

BTW I found event triggering in docs of kodi integration, which seems to be using the same logic

Is there any difference with your implementation?

Your configuration looks correct, but there seems to be something wrong with the docker container. What architecture are you running it from? arm64 (raspberry pi 4) or amd64?

@NoFace Yes you are correct there is an event trigger, but it only starts listening when you trigger/send something. And here we have just async triggers. This took me also a couple of hours to figure this out, and reading thourge the code. This is what i mend with:

The script is programed just like Home Assistant kodi intergration, so if it is liked, it can be intergated in to Home Assistant kodi intergration.

Architecture is amd64.
NUC like PC

0.3.1

  • add async-timeout to build, needed?
  • do not print passwords…
  • password in config.json

@NoFace I hope this build will solve you problem. But i found it strange that, the dockerbuilds of ours are not the same…

Thanks, it works now. I’ll give it a try.

I’ve noticed, that addon is crashing after reboot of HA, when Start on boot is enabled. I am getting the following error.

s6-rc: info: service s6rc-oneshot-runner: starting
s6-rc: info: service s6rc-oneshot-runner successfully started
s6-rc: info: service fix-attrs: starting
s6-rc: info: service fix-attrs successfully started
s6-rc: info: service legacy-cont-init: starting
s6-rc: info: service legacy-cont-init successfully started
s6-rc: info: service legacy-services: starting
s6-rc: info: service legacy-services successfully started
2024-01-21 10:42:18,717 kodi settings: 192.168.1.49, 8080 9090, kodi_ha, *
2024-01-21 10:42:18,719 Home assistant settings: ws://supervisor/core/api/websocket,  False, *
Traceback (most recent call last):
  File "/kodi2home.py", line 178, in main
    loop.run_until_complete( tasks )
  File "/usr/lib/python3.11/asyncio/base_events.py", line 653, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/kodi2home.py", line 112, in run_recv_home
    await self.connect_to_home()
  File "/kodi2home.py", line 66, in connect_to_home
    self.websocket = await websockets.connect(
                     ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/websockets/legacy/client.py", line 647, in __await_impl_timeout__
    return await self.__await_impl__()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/websockets/legacy/client.py", line 654, in __await_impl__
    await protocol.handshake(
  File "/usr/lib/python3.11/site-packages/websockets/legacy/client.py", line 325, in handshake
    raise InvalidStatusCode(status_code, response_headers)
websockets.exceptions.InvalidStatusCode: server rejected WebSocket connection: HTTP 502

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/kodi2home.py", line 185, in <module>
    main()
  File "/kodi2home.py", line 180, in main
    loop = asyncio.get_running_loop()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
RuntimeError: no running event loop
s6-rc: info: service legacy-services: stopping
s6-rc: info: service legacy-services successfully stopped
s6-rc: info: service legacy-cont-init: stopping
s6-rc: info: service legacy-cont-init successfully stopped
s6-rc: info: service fix-attrs: stopping
s6-rc: info: service fix-attrs successfully stopped
s6-rc: info: service s6rc-oneshot-runner: stopping
s6-rc: info: service s6rc-oneshot-runner successfully stopped

Manual start works, but my expectation was that enabled watchdog should start it automatically.