MQTT timing issue: Wrong user for "new state"

2 lamps. The goal is that when the button of one lamp is pressed, the other one turns on/off as well. I use AppDaemon.
This works fine so far. The problem is in some circumstances the new_state reports wrong values.

The easiest way to reproduce this:
I toggle switch1 → switch2 turns on. Within 2 seconds I toggle switch2. In this case new_state.context.user_id is Not Null. But it should be Null.
But if i wait 5 seconds instead of 2, Everything works fine.

Is there a better way to detect a physical interaction? I understand that fast interaction is not a normal usecase, but either way it should not report wrong data.

Setup:

AppDaemon Script:

import appdaemon.plugins.hass.hassapi as hass

#https://community.home-assistant.io/t/interaction-attribute-for-entites/450159

# ID combos: https://community.home-assistant.io/t/work-with-triggered-by-in-automations/400352/8
#interaction	id	        parent_id	    user_id
#Physical	    Not Null	Null	        Null
#Automation	    Not Null	Not Null	    Null
#UI	            Not Null	Null	        Not Null

class MyTestClass(hass.Hass):

    def initialize(self):
        self.log("Hello from AppDaemon")
        self.listen_event(
            self.listen_state_change,
            "state_changed"
        )
        #self.switch_changed(self.test, "switch.schalter_1_kuche")

    def listen_state_change(self, event, data, cb_args):

        entity = data['entity_id']
        switch1 = "switch.schalter_1_kuche"
        switch2 = "switch.schalter_2_kuche"
        
        if entity == switch1 or entity == switch2:
            self.log("----------STATE CHANGED EVENT----------")

            context = data['new_state']['context']
            interaction, user = self.determine_interaction(context)
            self.log("NEW Interaction: {}, User: {}, Switch: {}".format(interaction, user, entity))

            old_context = data['old_state']['context']
            old_interaction, old_user = self.determine_interaction(old_context)
            self.log("OLD Interaction: {}, User: {}, Switch: {}".format(old_interaction, old_user, entity))

            new_state = data['new_state']['state']
            if interaction == "physical":
                if entity == switch1:
                    if new_state == "on":
                        self.turn_on(switch2)
                        self.log("OFF triggered by switch1")
                    else:
                        self.turn_off(switch2)
                        self.log("OFF triggered by switch1")
                elif entity == switch2:
                    if new_state == "on":
                        self.turn_on(switch1)
                        self.log("ON triggered by switch2")
                    else:
                        self.turn_off(switch1)
                        self.log("OFF triggered by switch2")

    def determine_interaction(self, context):
        id = context.get("id")
        parent_id = context.get("parent_id")
        user_id = context.get("user_id")

        if id is not None and parent_id is None and user_id is None:
            interaction = "physical"
            user = "unknown"
        elif id is not None and parent_id is not None and user_id is None:
            interaction = "automation"
            user = "unknown"
        elif id is not None and parent_id is None and user_id is not None:
            interaction = "ui"
            user = user_id
        else:
            interaction = "unknown"
            user = "unknown"

        return interaction, user

Why not use listen_state() and get_state() instead?

This is where I came from. Then I tried fishing out the event. Same problem.
I simplified it to:

import appdaemon.plugins.hass.hassapi as hass

class MyTestClass(hass.Hass):

    def initialize(self):
        self.log("Hello from AppDaemon")
        self.listen_state(self.my_callback,"switch.schalter_1_kuche")
        self.listen_state(self.my_callback,"switch.schalter_2_kuche")

    def my_callback(self, entity, attribute, old, new, kwargs):
        self.log("----------STATE CHANGED EVENT----------")
        context=self.get_state(entity, attribute="context")
        self.log(context.get("user_id"))

With just this, everything reports properly and snappy. Buth when I toggle a switch by script, I need to wait 1-2sec before it will report a physical interation without a user_id.

There’s ready made blueprints for this exact scenarios you can try if rolling your own is causing you issues.

In no particular order:

Thank you for your inpunts. I tested a few of these. Basically I run into the same problem as before. They work, but when toggle switch A and then within 1-2 sec switch B, only switch B acts.
I will take a closer look into it in the coming week.

While it does not really solve the underlying problem of reporting the wrong user, the blueprint solution is good enough. You have to sometimes “sync up” the lights.
Thank you @ShadowFist .

1 Like