How to call Home Assistant entities in an MQTT class?

I defined, in Home Assistant, and automation which emits an event when an MQTT message is received. This was done to allow listening to these events in AppDaemon and act accordingly.

I now would like to move the MQTT part to AppDaemon (as MQTT is supported there as well).

The problem: I do not know how to call HA entities from within a class which herits from mqtt.MQTT. Consider the following code and the comments at the end:

import appdaemon.plugins.mqtt.mqttapi as mqtt
# import appdaemon.plugins.hass.hassapi as hass

class RF433Button(mqtt.Mqtt):

    def initialize(self):
        self.listen_event(self.guess_button, 'MQTT_MESSAGE', topic="rfbridge-1/rfin")

    def guess_button(self, event_name, data, kwargs):
        code = data.decode()
        self.log(f"received RF433 code {code}")
        # a typical scenario
        if code.endswith("3CCBA1"):
            self.toggle("switch.chambre_1_main")  
            # ^-- here I would like to call switch.chambre_1_main defined in HA, 
            # but it is not available as the class inherits from mqtt.MQTT and not hass.HASS

How should I approach this?

thats where the namespaces come in.
it should be working with:

self.toggle("switch.chambre_1_main", namepace = "the namespace that you did set for the hass plugin")

i hope that toggle also has been changed, if not it will throw an error.
in that case there is a workaround option:

self.set_namespace("the namespace that you did set for the hass plugin")
self.toggle("switch.chambre_1_main")
self.set_namespace("the namespace that you did set for the mqtt plugin")

if you did not set the namespace for 1 of the plugins it will be called “default”

I approach it from the other direction, and base my app on appdaemon.plugins.hass.hassapi, but use the namespace to set up the listen_event.

        self.listen_event(self.bedroom_ceiling_light_on,
                "MQTT_MESSAGE", payload="10045220", topic="home/433toMQTT", namespace="mqtt")

I don’t see how setting the namespace could change the inheritance path of the current class.

setting the namespace defines which version of the functions like listen_event, listen_state, etc. will be used.
with set_namepace you only set the default namespace that is used when you dont specify it in the function.

for example something i got in 1 of my apps that inherits from mqtt:

        if not self.entity_exists(entity,namespace = "hass"):
            self.log("the light: {} doesnt exist in HA create it".format(entity))

Yes, but entity_exists is defined in the mqtt class. turn_on is not.

$ find . -name \*.py | xargs egrep 'def\s+entity_exists'
./appdaemon/plugins/mqtt/mqttapi.py:    def entity_exists(self, entity_id, **kwargs):
./appdaemon/plugins/dummy/dummyapi.py:    def entity_exists(self, entity_id, **kwargs):
./appdaemon/plugins/hass/hassapi.py:    def entity_exists(self, entity_id, **kwargs):
./appdaemon/appdaemon.py:    def entity_exists(self, namespace, entity):
graham@naos-kde:~/dev/appdaemon
$ find . -name \*.py | xargs egrep 'def\s+turn_on'
./appdaemon/plugins/hass/hassapi.py:    def turn_on(self, entity_id, **kwargs):

If the method does not exist, python is going to reject it before the namespace flag is even read.

1 Like

i guess your right.
sometimes its hard for me to keep track. Andrew is working so hard to change things on the background, that i get confused :wink:

turn_on and toggle are part from the hass api. so in version 3.0.2 its easier to stay with hass.Hass and use some parts from mqtt.

in the future we can use all API from all plugins, but i think thats not the case in 3.0.2

@ReneTode @gpbenton Thanks a lot for the namespace hint – problem solved!

2 Likes

Is this solution still valid or has AppDaemon changed since then? I can’t get it to work, the callback is never executed even though AppDaemon receives Mqtt messages.

import appdaemon.plugins.hass.hassapi as hass
import appdaemon.plugins.mqtt.mqttapi as mqtt


class rtl433Restarter(hass.Hass):


    def initialize(self):
        self.listen_event(self.my_callback,
                          "MQTT_MESSAGE",
                          topic="homeassistant/sensor/rtl433/#",
                          namespace="mqtt")
        self.log("Init complete")

    def my_callback(self):
        self.log("Message received")

Relevant part of appdaemon.yaml

    MQTT:
      type: mqtt
      namespace: mqtt

Do you see any errors in your log? I think so, because the callback you defined doesn’t accept any parameters. It should be something like:

def my_callback(self, event_name, data, kwargs):
    self.log("Message received")
    self.log(event_name)
    self.log(data)

Thanks for your reply. Unfortunately adding the parameters does not seem to change anything. There are no errors in the log:

2019-09-26 13:27:55.661309 INFO AppDaemon: Initializing app restart_rtl433 using class rtl433Restarter from module restart_rtl433
2019-09-26 13:27:55.667558 INFO restart_rtl433: Init complete
2019-09-26 13:27:57.882757 INFO AppDaemon: MQTT: Message Received: Topic = homeassistant/sensor/rtl433/Acurite_tower_sensor/11136, Payload = b'{"time" : "2019-09-26 13:27:57", "model" : "Acurite tower sensor", "id" : 11136, "sensor_id" : 11136, "channel" : "C", "temperature_C" : 19.400, "humidity" : 65, "battery_low" : 0}'
2019-09-26 13:27:58.189069 INFO AppDaemon: MQTT: Message Received: Topic = homeassistant/sensor/rtl433/Acurite_tower_sensor/11136, Payload = b'{"time" : "2019-09-26 13:27:57", "model" : "Acurite tower sensor", "id" : 11136, "sensor_id" : 11136, "channel" : "C", "temperature_C" : 19.400, "humidity" : 65, "battery_low" : 0}'

Sorry I don’t get it. Where does this come from in your log?

2019-09-26 13:27:57.882757 INFO AppDaemon: MQTT: Message Received: Topic = homeassistant/sensor/rtl433/Acurite_tower_sensor/11136, Payload = b'{"time" : "2019-09-26 13:27:57", "model" : "Acurite tower sensor", "id" : 11136, "sensor_id" : 11136, "channel" : "C", "temperature_C" : 19.400, "humidity" : 65, "battery_low" : 0}'

Isn’t this what you need?

That’s the AppDaemon MQTT plugin in verbose mode (INFO AppDaemon: MQTT:…), but not the actual app that I want to react to incoming messages (INFO restart_rtl433:…).

If my callback executed I’d expect it to show up like this:

INFO restart_rtl433: Message received
INFO restart_rtl433: [The event name]
INFO restart_rtl433: [The data]

So yeah it’s what I need but I can’t get my app to react to it.

Ah I understand. What happens if you change the topic from “homeassistant/sensor/rtl433/#” to “homeassistant/sensor/rtl433”?

Could you please also try withoud specifying a topic and see if something shows up in the logs?

1 Like

you exactly got the problem pinned @Burningstone

you cant use # in topic because then the filter wont work anymore.

2 Likes

Huh. I had tried it without the wildcard previously but there must have been another error stop it from working. Thank you @Burningstone and @ReneTode

1 Like

if you want to use wildcards to filter you can set topics in appdaemon.yaml in the plugin.

i use it like this:

    MQTT1:
      type: mqtt
      verbose: False
      client_host: 192.168.xx.xx
      client_port: xxx
      client_id: AD_mysensors_zolder
      namespace: mysensors-zolder
      event_name: mysensors-zolder
      client_topics:
        - mysensors-zolder-out/#
    MQTT2:
      type: mqtt
      verbose: False
      client_host: 192.168.xx.xx
      client_port: xxx
      client_id: AD_mysensors_kelder
      namespace: mysensors-kelder
      event_name: mysensors-kelder
      client_topics:
        - mysensors-kelder-out/#

off course you then need to listen to eventnames that are set there and not to MQTT-MESSAGE
and use the namespace that has the right filter.

Reading the AppDaemon MQTT API reference I see that wildcards are apparently supported thus:

self.listen_event(self.mqtt_message_recieved_event, "MQTT_MESSAGE", wildcard = 'homeassistant/#')

but when I tried this from HASS with namespace=“mqtt” it did not work. I can listen to events from individual topics fine.

Is this wildcard feature implemented?