MQTT broker for telldus-core with MQTT Discovery

I recently created a MQTT broker for telldus-core that supports the MQTT discovery in Home Assistant. I only have temperature and humidity sensors and some on/off switches. Would be great if some one could try with some other devices or sensors :slight_smile:

Project: https://github.com/mliljedahl/telldus-core-mqtt
Docker: Docker Hub

1 Like

Thank you for this!

I know I’m a bit late to the party, but I just installed this on a second rpi, in order to use HAOS as the OS on my Home Assistant rpi. It allows me to use my old Telldus Duo (USB) to publish to MQTT.

However, the current version of MQTT in HA fails. It tries to cast eg. strings like {"temperature": "8.5"} to float. Like, the whole string… I assume the MQTT message should be crafted somewhat differently?

This is what I get from main.py from your repo (I run it in a python3.9 venv):

2023-10-13 18:12:47,418 INFO: Send "{"humidity": "71"}" to topic "telldus/167/humidity/state"
2023-10-13 18:12:47,419 INFO: Send "{"humidity": "71"}" to topic "telldus/167/humidity/state"

And in HA:

2023-10-13 20:48:47.316 INFO (MainThread) [homeassistant.components.mqtt.discovery] Component has already been discovered: sensor 167_telldus temperature, sending update
2023-10-13 20:48:47.320 INFO (MainThread) [homeassistant.components.mqtt.discovery] Component has already been discovered: sensor 167_telldus humidity, sending update
2023-10-13 20:49:04.245 INFO (MainThread) [homeassistant.components.mqtt.discovery] Component has already been discovered: sensor 136_telldus temperature, sending update
2023-10-13 20:49:18.333 INFO (MainThread) [homeassistant.components.mqtt.discovery] Component has already been discovered: sensor 199_telldus temperature, sending update
2023-10-13 20:49:18.376 ERROR (MainThread) [homeassistant.components.mqtt.models] Exception raised when updating state of sensor.199_temperaturehumidity_telldus_199_temperature, topic: 'telldus/199/temperature/state' with payload: b'{"temperature": "15.5"}'
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/sensor/__init__.py", line 593, in state
    numerical_value = float(value)  # type:ignore[arg-type]
                      ^^^^^^^^^^^^
ValueError: could not convert string to float: '{"temperature": "15.5"}'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/components/mqtt/models.py", line 305, in process_write_state_requests
    entity.async_write_ha_state()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 779, in async_write_ha_state
    self._async_write_ha_state()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 879, in _async_write_ha_state
    state, attr = self._async_generate_attributes()
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 820, in _async_generate_attributes
    state = self._stringify_state(available)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 785, in _stringify_state
    if (state := self.state) is None:
                 ^^^^^^^^^^
  File "/usr/src/homeassistant/homeassistant/components/sensor/__init__.py", line 595, in state
    raise ValueError(
ValueError: Sensor sensor.199_temperaturehumidity_telldus_199_temperature has device class 'temperature', state class 'None' unit '°C' and suggested precision 'None' thus indicating it has a numeric value; however, it has the non-numeric value: '{"temperature": "15.5"}' (<class 'str'>)

From mosquitto_sub:

$ mosquitto_sub -h 192.168.1.2  -F '@Y-@m-@dT@H:@M:@S@z : %I : %t %p '  -t telldus/\#
2023-10-13T20:49:23+0200 : 2023-10-13T20:49:23+0200 : telldus/1/switch/state {"switch": 2} 
2023-10-13T20:49:23+0200 : 2023-10-13T20:49:23+0200 : telldus/151/temperature/state {"temperature": "8.5"} 
2023-10-13T20:49:23+0200 : 2023-10-13T20:49:23+0200 : telldus/199/temperature/state {"temperature": "15.5"} 
2023-10-13T20:49:23+0200 : 2023-10-13T20:49:23+0200 : telldus/199/humidity/state {"humidity": "58"} 
2023-10-13T20:49:23+0200 : 2023-10-13T20:49:23+0200 : telldus/167/temperature/state {"temperature": "12.3"} 
2023-10-13T20:49:23+0200 : 2023-10-13T20:49:23+0200 : telldus/167/humidity/state {"humidity": "72"} 
2023-10-13T20:49:23+0200 : 2023-10-13T20:49:23+0200 : telldus/135/temperature/state {"temperature": "22.5"} 
2023-10-13T20:49:23+0200 : 2023-10-13T20:49:23+0200 : telldus/135/humidity/state {"humidity": "56"} 
2023-10-13T20:49:23+0200 : 2023-10-13T20:49:23+0200 : telldus/247/temperature/state {"temperature": "23.7"} 
2023-10-13T20:49:23+0200 : 2023-10-13T20:49:23+0200 : telldus/247/humidity/state {"humidity": "48"} 
2023-10-13T20:49:23+0200 : 2023-10-13T20:49:23+0200 : telldus/67/temperature/state {"temperature": "22.2"} 
2023-10-13T20:49:23+0200 : 2023-10-13T20:49:23+0200 : telldus/67/humidity/state {"humidity": "53"} 
2023-10-13T20:49:23+0200 : 2023-10-13T20:49:23+0200 : telldus/232/temperature/state {"temperature": "-16.9"} 
2023-10-13T20:49:23+0200 : 2023-10-13T20:49:23+0200 : telldus/136/temperature/state {"temperature": "7.7"} 
2023-10-13T20:49:23+0200 : 2023-10-13T20:49:23+0200 : telldus/159/temperature/state {"temperature": "8.4"} 

I guess I can debug the code myself, and will post back on my findings. Actually, I should perhaps open an issue in your github repo, I just got so excited and wanted to thank you here right now for this, I’m sure I’ll get it to work for me soon.

I found this container a few weeks ago, and I’m now running it on a few Pi’s. Thank you very much for this, @mliljedahl :+1: Or många tack! :wink: It solved a problem for me, I had a bunch of Tellstick Duos, but the Pi’s running them had to be kept on Stretch because the later versions did not work with the Telldus system for Linux.

What I do is to set it up so it doesn’t get discovered by Hass and instead filter the output through Node-RED, with a flow that’s at the bottom of this mesage. My Docker container is set up like this:

version: '3'
services:
  telldus-core-mqtt:
    image: mliljedahl/telldus-core-mqtt:1.2.1
    container_name: telldus-core-mqtt
    restart: unless-stopped
    environment:
      - TDM_BASE_TOPIC=nodered
      - TDM_STATE_TOPIC=telldus
      - TDM_MQTT_SERVER=192.168.1.101
      - TDM_MQTT_USER=""
      - TDM_MQTT_PASS=""
      - TDM_REPEAT_CMD=1
    devices:
      - /dev/bus/usb:/dev/bus/usb:rwm
    volumes:
      - ./tellstick.conf:/etc/tellstick.conf:ro
[
    {
        "id": "f95a94b1.1ac588",
        "type": "mqtt out",
        "z": "b61f4f80.052e5",
        "name": "",
        "topic": "eg/Tellstick.Temperatur",
        "qos": "",
        "retain": "",
        "broker": "36159a16.2aa98e",
        "x": 820,
        "y": 200,
        "wires": []
    },
    {
        "id": "c2aec37e.1e3df",
        "type": "debug",
        "z": "b61f4f80.052e5",
        "name": "",
        "active": true,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "x": 730,
        "y": 40,
        "wires": []
    },
    {
        "id": "bba8ff458f65e729",
        "type": "function",
        "z": "b61f4f80.052e5",
        "name": "Telldus-temperatursensorer med fuktighet",
        "func": "var protocol = \"Med fuktighet\";\nvar id = msg.topic.split('/')[1];\nvar tmp = msg.payload[0].temperature;\nvar hum = msg.payload[1].humidity;\nmsg.payload =  protocol+','+id+','+tmp+','+hum;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 580,
        "y": 100,
        "wires": [
            [
                "af2ff4441ca446f2"
            ]
        ]
    },
    {
        "id": "4cfad1d29a46a937",
        "type": "mqtt in",
        "z": "b61f4f80.052e5",
        "name": "",
        "topic": "telldus/#",
        "qos": "2",
        "datatype": "auto-detect",
        "broker": "36159a16.2aa98e",
        "nl": false,
        "rap": true,
        "rh": 0,
        "inputs": 0,
        "x": 80,
        "y": 40,
        "wires": [
            [
                "45dad75475f19265"
            ]
        ]
    },
    {
        "id": "9ff6da3936482a75",
        "type": "debug",
        "z": "b61f4f80.052e5",
        "name": "debug 9",
        "active": false,
        "tosidebar": true,
        "console": false,
        "tostatus": false,
        "complete": "false",
        "statusVal": "",
        "statusType": "auto",
        "x": 540,
        "y": 200,
        "wires": []
    },
    {
        "id": "45dad75475f19265",
        "type": "switch",
        "z": "b61f4f80.052e5",
        "name": "",
        "property": "topic",
        "propertyType": "msg",
        "rules": [
            {
                "t": "cont",
                "v": "temperature",
                "vt": "str"
            },
            {
                "t": "cont",
                "v": "humidity",
                "vt": "str"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 230,
        "y": 40,
        "wires": [
            [
                "5863c2ed79913b0e"
            ],
            [
                "ebc36c14ac7d40bd"
            ]
        ]
    },
    {
        "id": "806ee8564dc7bc10",
        "type": "join",
        "z": "b61f4f80.052e5",
        "name": "",
        "mode": "custom",
        "build": "array",
        "property": "payload",
        "propertyType": "msg",
        "key": "payload",
        "joiner": "\\n",
        "joinerType": "str",
        "accumulate": false,
        "timeout": "1",
        "count": "2",
        "reduceRight": false,
        "reduceExp": "",
        "reduceInit": "",
        "reduceInitType": "num",
        "reduceFixup": "",
        "x": 90,
        "y": 120,
        "wires": [
            [
                "9ff6da3936482a75",
                "ebf20d56dac64f35"
            ]
        ]
    },
    {
        "id": "af2ff4441ca446f2",
        "type": "rbe",
        "z": "b61f4f80.052e5",
        "name": "",
        "func": "rbe",
        "gap": "",
        "start": "",
        "inout": "out",
        "septopics": false,
        "property": "payload",
        "topi": "topic",
        "x": 850,
        "y": 120,
        "wires": [
            [
                "c2aec37e.1e3df",
                "f95a94b1.1ac588"
            ]
        ]
    },
    {
        "id": "ebf20d56dac64f35",
        "type": "switch",
        "z": "b61f4f80.052e5",
        "name": "",
        "property": "payload[1].humidity",
        "propertyType": "msg",
        "rules": [
            {
                "t": "nnull"
            },
            {
                "t": "else"
            }
        ],
        "checkall": "true",
        "repair": false,
        "outputs": 2,
        "x": 270,
        "y": 120,
        "wires": [
            [
                "bba8ff458f65e729"
            ],
            [
                "4a29b078af7dafe1"
            ]
        ]
    },
    {
        "id": "4a29b078af7dafe1",
        "type": "function",
        "z": "b61f4f80.052e5",
        "name": "Telldus-temperatursensorer uten fuktighet",
        "func": "var protocol = \"Uten fuktighet\";\nvar id = msg.topic.split('/')[1];\nvar tmp = msg.payload[0].temperature;\nmsg.payload =  protocol+','+id+','+tmp;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 580,
        "y": 140,
        "wires": [
            [
                "af2ff4441ca446f2"
            ]
        ]
    },
    {
        "id": "5863c2ed79913b0e",
        "type": "rbe",
        "z": "b61f4f80.052e5",
        "name": "",
        "func": "rbe",
        "gap": "",
        "start": "",
        "inout": "out",
        "septopics": true,
        "property": "payload",
        "topi": "payload[0].temperature",
        "x": 370,
        "y": 20,
        "wires": [
            [
                "806ee8564dc7bc10"
            ]
        ]
    },
    {
        "id": "ebc36c14ac7d40bd",
        "type": "rbe",
        "z": "b61f4f80.052e5",
        "name": "",
        "func": "rbe",
        "gap": "",
        "start": "",
        "inout": "out",
        "septopics": true,
        "property": "payload",
        "topi": "payload[0].humidity",
        "x": 370,
        "y": 60,
        "wires": [
            [
                "806ee8564dc7bc10"
            ]
        ]
    },
    {
        "id": "36159a16.2aa98e",
        "type": "mqtt-broker",
        "name": "Hytte-Pi",
        "broker": "192.168.1.101",
        "port": "1883",
        "clientid": "",
        "usetls": false,
        "compatmode": true,
        "keepalive": "60",
        "cleansession": true,
        "birthTopic": "",
        "birthQos": "0",
        "birthPayload": "",
        "closeTopic": "",
        "closePayload": "",
        "willTopic": "",
        "willQos": "0",
        "willPayload": ""
    }
]

There is one problem, if the broker is restarted, then it won’t reconnect. That has been adressed in a fork, but I have no idea how to integrate it, since there is no Docker image, only a changed file that I can’t build because the submodules errors out. :frowning:

I just head back from Dst6se! That can be fixed by adding a second line to the volumes in the docker-compose file with his changed main.py

    volumes:
      - ./tellstick.conf:/etc/tellstick.conf:ro
      - ./main.py:/usr/src/telldus-core-mqtt/main.py:ro

Ok, so I came up this this solution, alterering the publish_mqtt method, since HA doesn’t seem to care about the state_value_template that is sent in the mqtt config message anyways.
Essentially, I just send the raw data value instead of the json:

def publish_mqtt(client, topic, msg):
    with THREADING_RLOCK:
        message = msg
        if topic.endswith('/state'):
            try:
                dd = json.loads(msg)
                assert len(dd.keys()) == 1, 'Error in parsing json'
            except:
                logging.warning(f'Failed to parse as json: {msg}')
            first_value = next(iter(dd.values()))
            message = first_value
        result = client.publish(topic, message, retain=True)

        if result[0] == 0:
            logging.info('Send "%s" to topic "%s"', message, topic)
        else:
            logging.error('Failed to send message to topic "%s"', topic)

Hey :slight_smile:
Love this, but having problems getting it to work.

I get this.

ERROR: for telldus-core-mqtt  Cannot start service telldus-core-mqtt: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error mounting "/home/echnics/telldus/tellstick.conf" to rootfs at "/etc/tellstick.conf": mount /home/echnics/telldus/tellstick.conf:/etc/tellstick.conf (via /proc/self/fd/6), flags: 0x5001: not a directory: unknown: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type
ERROR: Encountered errors while bringing up the project.

Can anybody using it guide me through the install?

I have docker running on a PI, and have successfully launched other docker containers.

Got it running now. No messages in mqtt,but connected.
What now?