Module plugins.hass.HassEntityAutomation

Expand source code
from enum import Enum
from modules.base.Configuration import *
from modules.base.Instances import *
from plugins.hass.Configurations import *

class HassType(Enum):
    TRIGGER = "trigger"
    '''HomeAssistant's triggers are only to invoke HA Automations, not visible in Lovelace.'''

    SENSOR = "binary_sensor"
    '''Use this type to use the state of a piTomation entity in HA.'''

    SWITCH = "switch"
    '''Allows to toggle a piTomation state from HA.'''


class HassEntityAutomation(Stackable, Disposeable, Logging):
    '''Connection between our actions and scripts and HomeAssistants different entity types.'''

    def __init__(self, parent: Stackable, config: HassEntityConfiguration) -> None:
        from plugins.hass.Platform import Platform
        super().__init__(parent)
        
        self.platform: Platform 
        if type(parent) is Platform:
            self.platform = parent 

        self.configuration = config
        self.app = parent.get_app()
        
        if config.name is None:
            config.name = config.id

        self.base_topic = str(self.platform.base_topic)
        
        self.hass_type = HassType.SENSOR
        auto_discovery_temp = str(self.hass_type.value)

        if type(config) is HassBinarySensorEntityConfiguration:
            self.hass_type = HassType.SENSOR
            auto_discovery_temp = str(self.hass_type.value)
            if config.state_topic is None:
                config.state_topic = self.base_topic + "/" + config.id + "/state"
            
        elif type(config) is HassActionEntityConfiguration:
            self.hass_type = HassType.SWITCH
            if config.state_topic is None:
                config.state_topic = self.base_topic + "/" + config.id + "/state"
            if config.command_topic is None:
                config.command_topic = self.base_topic + "/" + config.id + "/command"

            self.on_command_automations = Automation.create_automations(self, config.on_command)
            self.off_command_automations = Automation.create_automations(self, config.off_command)
            auto_discovery_temp = str(self.hass_type.value)

        elif type(config) is HassTriggerEntityConfiguration:
            self.hass_type = HassType.TRIGGER
            if config.command_topic is None: #type: ignore
                config.command_topic = self.base_topic + "/" + config.id + "/command"
            auto_discovery_temp = "device_automation"
            

        self.auto_discovery_topic = self.platform.configuration.auto_discovery_topic \
            + "/" + auto_discovery_temp \
            + "/" + self.platform.hass_device["name"] \
            + "/" + config.id \
            + "/config"
            

    def start(self, call_stack: CallStack):

        from plugins.mqtt.Platform import Platform as mqtt_platform
        def get_mqtt_platform() -> mqtt_platform:
            return self.platform.communication #type: ignore
        self.mqtt = get_mqtt_platform()

        state_topic = self.base_topic + "/" + str(self.configuration.id) + "/state"
        wrapped_id = self.app.get_id(self.configuration.id)

        entity = {}
        
        entity["device"] = self.platform.hass_device

        if self.configuration.icon:
            entity["icon"] = self.configuration.icon

        def get_unique_id():
            '''Creates a unique id for HA.'''
            return "" \
                + str(call_stack.get(self.app.device.configuration.name)) \
                + "_" \
                + str(call_stack.get(str(self.hass_type.value))) \
                + "_" \
                + str(call_stack.get(self.configuration.id))


        def get_state():
            '''Returns the internal state of the exposed entity.'''

            exp_state = str(self.configuration.expose_state) #type: ignore
            if exp_state == "OFF":
                return False
            else:
                path = exp_state.split('.') #type: ignore
                act = wrapped_id
                for path_element in path:
                    act = getattr(act, path_element)    

                act_state = str(act).lower()

                if act_state == "true" or act_state == "on":
                    return entity["payload_on"]
                    
                if act_state == "false" or act_state == "off":
                    return entity["payload_off"]

                return act

        class UpdateHassState(Automation):
            '''Updates the state in HA.'''

            def invoke(_, call_stack: CallStack): #type: ignore
                act_state = get_state()
                self.mqtt.publish(state_topic, act_state)


        wrapped_id.on_state_changed_automations.append(UpdateHassState(self, AutomationConfiguration())) #type: ignore    


        if self.hass_type == HassType.TRIGGER and type(self.configuration) is HassTriggerEntityConfiguration:
            entity["topic"] = self.configuration.command_topic #type: ignore
            entity["automation_type"] = "trigger"
            entity["type"] = "button_short_press"
            entity["subtype"] = self.configuration.name
            entity["topic"] = self.configuration.command_topic  
            
            def OnCommand(call_stack: CallStack):
                payload = call_stack.get("{{{payload}}}")

                if isinstance(wrapped_id, BaseAction):
                    '''Update local state and call the automations of the entity.'''
                    wrapped_id.invoke(call_stack)

                if payload == entity["payload_on"]:
                    if self.on_command_automations:
                        for automation in self.on_command_automations:
                            automation.invoke(call_stack)

                if payload == entity["payload_off"]:
                    if self.off_command_automations:
                        for automation in self.off_command_automations:
                            automation.invoke(call_stack)                            

            self.mqtt.subscribe(self.configuration.command_topic, callback=OnCommand) #type: ignore
        

        elif self.hass_type == HassType.SWITCH and type(self.configuration) is HassActionEntityConfiguration:
            entity["name"] = self.configuration.name
            entity["unique_id"] = get_unique_id()
            if self.mqtt.configuration.availability:
                entity["availability_topic"] = call_stack.get(self.mqtt.configuration.availability.topic)
            entity["payload_off"] = "off"
            entity["payload_on"] = "on"
            #entity["optimistic"] = False
            entity["state_topic"] = state_topic
            entity["command_topic"] = self.configuration.command_topic     
            if self.mqtt.configuration.availability:
                entity["availability_topic"] = call_stack.get(self.mqtt.configuration.availability.topic)
                entity["payload_available"] = call_stack.get(self.mqtt.configuration.availability.payload_on)
                entity["payload_not_available"] = call_stack.get(self.mqtt.configuration.availability.payload_off)                 

            def OnCommand(call_stack: CallStack):
                payload = call_stack.get("{{{payload}}}")
                boolean_payload = False
                if payload == entity["payload_on"]: 
                    boolean_payload = True
                elif payload == entity["payload_off"]: 
                    boolean_payload = False

                call_stack = call_stack.with_key("payload", boolean_payload)

                if isinstance(wrapped_id, BaseAction):
                    '''Update local state and call the automations of the entity.'''
                    wrapped_id.invoke(call_stack)

                if payload == entity["payload_on"]:
                    if self.on_command_automations:
                        for automation in self.on_command_automations:
                            automation.invoke(call_stack)

                if payload == entity["payload_off"]:
                    if self.off_command_automations:
                        for automation in self.off_command_automations:
                            automation.invoke(call_stack)                            

            self.mqtt.subscribe(self.configuration.command_topic, callback=OnCommand) #type: ignore
        
        elif self.hass_type == HassType.SENSOR and type(self.configuration) is HassBinarySensorEntityConfiguration:
            entity["name"] = self.configuration.name
            entity["unique_id"] = get_unique_id()
            entity["state_topic"] = call_stack.get(state_topic)
            entity["payload_on"] = "on"
            entity["payload_off"] = "off"
            if self.mqtt.configuration.availability:
                entity["availability_topic"] = call_stack.get(self.mqtt.configuration.availability.topic)
                entity["payload_available"] = call_stack.get(self.mqtt.configuration.availability.payload_on)
                entity["payload_not_available"] = call_stack.get(self.mqtt.configuration.availability.payload_off)                 


        if self.hass_type == HassType.SENSOR or self.hass_type == HassType.SWITCH:
            '''Report actual state back to HA'''
            UpdateHassState(self, AutomationConfiguration()).invoke(call_stack) 

        self.mqtt.publish(self.auto_discovery_topic, entity, retain = True)

Classes

class HassEntityAutomation (parent: Stackable, config: HassEntityConfiguration)

Connection between our actions and scripts and HomeAssistants different entity types.

Expand source code
class HassEntityAutomation(Stackable, Disposeable, Logging):
    '''Connection between our actions and scripts and HomeAssistants different entity types.'''

    def __init__(self, parent: Stackable, config: HassEntityConfiguration) -> None:
        from plugins.hass.Platform import Platform
        super().__init__(parent)
        
        self.platform: Platform 
        if type(parent) is Platform:
            self.platform = parent 

        self.configuration = config
        self.app = parent.get_app()
        
        if config.name is None:
            config.name = config.id

        self.base_topic = str(self.platform.base_topic)
        
        self.hass_type = HassType.SENSOR
        auto_discovery_temp = str(self.hass_type.value)

        if type(config) is HassBinarySensorEntityConfiguration:
            self.hass_type = HassType.SENSOR
            auto_discovery_temp = str(self.hass_type.value)
            if config.state_topic is None:
                config.state_topic = self.base_topic + "/" + config.id + "/state"
            
        elif type(config) is HassActionEntityConfiguration:
            self.hass_type = HassType.SWITCH
            if config.state_topic is None:
                config.state_topic = self.base_topic + "/" + config.id + "/state"
            if config.command_topic is None:
                config.command_topic = self.base_topic + "/" + config.id + "/command"

            self.on_command_automations = Automation.create_automations(self, config.on_command)
            self.off_command_automations = Automation.create_automations(self, config.off_command)
            auto_discovery_temp = str(self.hass_type.value)

        elif type(config) is HassTriggerEntityConfiguration:
            self.hass_type = HassType.TRIGGER
            if config.command_topic is None: #type: ignore
                config.command_topic = self.base_topic + "/" + config.id + "/command"
            auto_discovery_temp = "device_automation"
            

        self.auto_discovery_topic = self.platform.configuration.auto_discovery_topic \
            + "/" + auto_discovery_temp \
            + "/" + self.platform.hass_device["name"] \
            + "/" + config.id \
            + "/config"
            

    def start(self, call_stack: CallStack):

        from plugins.mqtt.Platform import Platform as mqtt_platform
        def get_mqtt_platform() -> mqtt_platform:
            return self.platform.communication #type: ignore
        self.mqtt = get_mqtt_platform()

        state_topic = self.base_topic + "/" + str(self.configuration.id) + "/state"
        wrapped_id = self.app.get_id(self.configuration.id)

        entity = {}
        
        entity["device"] = self.platform.hass_device

        if self.configuration.icon:
            entity["icon"] = self.configuration.icon

        def get_unique_id():
            '''Creates a unique id for HA.'''
            return "" \
                + str(call_stack.get(self.app.device.configuration.name)) \
                + "_" \
                + str(call_stack.get(str(self.hass_type.value))) \
                + "_" \
                + str(call_stack.get(self.configuration.id))


        def get_state():
            '''Returns the internal state of the exposed entity.'''

            exp_state = str(self.configuration.expose_state) #type: ignore
            if exp_state == "OFF":
                return False
            else:
                path = exp_state.split('.') #type: ignore
                act = wrapped_id
                for path_element in path:
                    act = getattr(act, path_element)    

                act_state = str(act).lower()

                if act_state == "true" or act_state == "on":
                    return entity["payload_on"]
                    
                if act_state == "false" or act_state == "off":
                    return entity["payload_off"]

                return act

        class UpdateHassState(Automation):
            '''Updates the state in HA.'''

            def invoke(_, call_stack: CallStack): #type: ignore
                act_state = get_state()
                self.mqtt.publish(state_topic, act_state)


        wrapped_id.on_state_changed_automations.append(UpdateHassState(self, AutomationConfiguration())) #type: ignore    


        if self.hass_type == HassType.TRIGGER and type(self.configuration) is HassTriggerEntityConfiguration:
            entity["topic"] = self.configuration.command_topic #type: ignore
            entity["automation_type"] = "trigger"
            entity["type"] = "button_short_press"
            entity["subtype"] = self.configuration.name
            entity["topic"] = self.configuration.command_topic  
            
            def OnCommand(call_stack: CallStack):
                payload = call_stack.get("{{{payload}}}")

                if isinstance(wrapped_id, BaseAction):
                    '''Update local state and call the automations of the entity.'''
                    wrapped_id.invoke(call_stack)

                if payload == entity["payload_on"]:
                    if self.on_command_automations:
                        for automation in self.on_command_automations:
                            automation.invoke(call_stack)

                if payload == entity["payload_off"]:
                    if self.off_command_automations:
                        for automation in self.off_command_automations:
                            automation.invoke(call_stack)                            

            self.mqtt.subscribe(self.configuration.command_topic, callback=OnCommand) #type: ignore
        

        elif self.hass_type == HassType.SWITCH and type(self.configuration) is HassActionEntityConfiguration:
            entity["name"] = self.configuration.name
            entity["unique_id"] = get_unique_id()
            if self.mqtt.configuration.availability:
                entity["availability_topic"] = call_stack.get(self.mqtt.configuration.availability.topic)
            entity["payload_off"] = "off"
            entity["payload_on"] = "on"
            #entity["optimistic"] = False
            entity["state_topic"] = state_topic
            entity["command_topic"] = self.configuration.command_topic     
            if self.mqtt.configuration.availability:
                entity["availability_topic"] = call_stack.get(self.mqtt.configuration.availability.topic)
                entity["payload_available"] = call_stack.get(self.mqtt.configuration.availability.payload_on)
                entity["payload_not_available"] = call_stack.get(self.mqtt.configuration.availability.payload_off)                 

            def OnCommand(call_stack: CallStack):
                payload = call_stack.get("{{{payload}}}")
                boolean_payload = False
                if payload == entity["payload_on"]: 
                    boolean_payload = True
                elif payload == entity["payload_off"]: 
                    boolean_payload = False

                call_stack = call_stack.with_key("payload", boolean_payload)

                if isinstance(wrapped_id, BaseAction):
                    '''Update local state and call the automations of the entity.'''
                    wrapped_id.invoke(call_stack)

                if payload == entity["payload_on"]:
                    if self.on_command_automations:
                        for automation in self.on_command_automations:
                            automation.invoke(call_stack)

                if payload == entity["payload_off"]:
                    if self.off_command_automations:
                        for automation in self.off_command_automations:
                            automation.invoke(call_stack)                            

            self.mqtt.subscribe(self.configuration.command_topic, callback=OnCommand) #type: ignore
        
        elif self.hass_type == HassType.SENSOR and type(self.configuration) is HassBinarySensorEntityConfiguration:
            entity["name"] = self.configuration.name
            entity["unique_id"] = get_unique_id()
            entity["state_topic"] = call_stack.get(state_topic)
            entity["payload_on"] = "on"
            entity["payload_off"] = "off"
            if self.mqtt.configuration.availability:
                entity["availability_topic"] = call_stack.get(self.mqtt.configuration.availability.topic)
                entity["payload_available"] = call_stack.get(self.mqtt.configuration.availability.payload_on)
                entity["payload_not_available"] = call_stack.get(self.mqtt.configuration.availability.payload_off)                 


        if self.hass_type == HassType.SENSOR or self.hass_type == HassType.SWITCH:
            '''Report actual state back to HA'''
            UpdateHassState(self, AutomationConfiguration()).invoke(call_stack) 

        self.mqtt.publish(self.auto_discovery_topic, entity, retain = True)

Ancestors

Methods

def start(self, call_stack: CallStack)
Expand source code
def start(self, call_stack: CallStack):

    from plugins.mqtt.Platform import Platform as mqtt_platform
    def get_mqtt_platform() -> mqtt_platform:
        return self.platform.communication #type: ignore
    self.mqtt = get_mqtt_platform()

    state_topic = self.base_topic + "/" + str(self.configuration.id) + "/state"
    wrapped_id = self.app.get_id(self.configuration.id)

    entity = {}
    
    entity["device"] = self.platform.hass_device

    if self.configuration.icon:
        entity["icon"] = self.configuration.icon

    def get_unique_id():
        '''Creates a unique id for HA.'''
        return "" \
            + str(call_stack.get(self.app.device.configuration.name)) \
            + "_" \
            + str(call_stack.get(str(self.hass_type.value))) \
            + "_" \
            + str(call_stack.get(self.configuration.id))


    def get_state():
        '''Returns the internal state of the exposed entity.'''

        exp_state = str(self.configuration.expose_state) #type: ignore
        if exp_state == "OFF":
            return False
        else:
            path = exp_state.split('.') #type: ignore
            act = wrapped_id
            for path_element in path:
                act = getattr(act, path_element)    

            act_state = str(act).lower()

            if act_state == "true" or act_state == "on":
                return entity["payload_on"]
                
            if act_state == "false" or act_state == "off":
                return entity["payload_off"]

            return act

    class UpdateHassState(Automation):
        '''Updates the state in HA.'''

        def invoke(_, call_stack: CallStack): #type: ignore
            act_state = get_state()
            self.mqtt.publish(state_topic, act_state)


    wrapped_id.on_state_changed_automations.append(UpdateHassState(self, AutomationConfiguration())) #type: ignore    


    if self.hass_type == HassType.TRIGGER and type(self.configuration) is HassTriggerEntityConfiguration:
        entity["topic"] = self.configuration.command_topic #type: ignore
        entity["automation_type"] = "trigger"
        entity["type"] = "button_short_press"
        entity["subtype"] = self.configuration.name
        entity["topic"] = self.configuration.command_topic  
        
        def OnCommand(call_stack: CallStack):
            payload = call_stack.get("{{{payload}}}")

            if isinstance(wrapped_id, BaseAction):
                '''Update local state and call the automations of the entity.'''
                wrapped_id.invoke(call_stack)

            if payload == entity["payload_on"]:
                if self.on_command_automations:
                    for automation in self.on_command_automations:
                        automation.invoke(call_stack)

            if payload == entity["payload_off"]:
                if self.off_command_automations:
                    for automation in self.off_command_automations:
                        automation.invoke(call_stack)                            

        self.mqtt.subscribe(self.configuration.command_topic, callback=OnCommand) #type: ignore
    

    elif self.hass_type == HassType.SWITCH and type(self.configuration) is HassActionEntityConfiguration:
        entity["name"] = self.configuration.name
        entity["unique_id"] = get_unique_id()
        if self.mqtt.configuration.availability:
            entity["availability_topic"] = call_stack.get(self.mqtt.configuration.availability.topic)
        entity["payload_off"] = "off"
        entity["payload_on"] = "on"
        #entity["optimistic"] = False
        entity["state_topic"] = state_topic
        entity["command_topic"] = self.configuration.command_topic     
        if self.mqtt.configuration.availability:
            entity["availability_topic"] = call_stack.get(self.mqtt.configuration.availability.topic)
            entity["payload_available"] = call_stack.get(self.mqtt.configuration.availability.payload_on)
            entity["payload_not_available"] = call_stack.get(self.mqtt.configuration.availability.payload_off)                 

        def OnCommand(call_stack: CallStack):
            payload = call_stack.get("{{{payload}}}")
            boolean_payload = False
            if payload == entity["payload_on"]: 
                boolean_payload = True
            elif payload == entity["payload_off"]: 
                boolean_payload = False

            call_stack = call_stack.with_key("payload", boolean_payload)

            if isinstance(wrapped_id, BaseAction):
                '''Update local state and call the automations of the entity.'''
                wrapped_id.invoke(call_stack)

            if payload == entity["payload_on"]:
                if self.on_command_automations:
                    for automation in self.on_command_automations:
                        automation.invoke(call_stack)

            if payload == entity["payload_off"]:
                if self.off_command_automations:
                    for automation in self.off_command_automations:
                        automation.invoke(call_stack)                            

        self.mqtt.subscribe(self.configuration.command_topic, callback=OnCommand) #type: ignore
    
    elif self.hass_type == HassType.SENSOR and type(self.configuration) is HassBinarySensorEntityConfiguration:
        entity["name"] = self.configuration.name
        entity["unique_id"] = get_unique_id()
        entity["state_topic"] = call_stack.get(state_topic)
        entity["payload_on"] = "on"
        entity["payload_off"] = "off"
        if self.mqtt.configuration.availability:
            entity["availability_topic"] = call_stack.get(self.mqtt.configuration.availability.topic)
            entity["payload_available"] = call_stack.get(self.mqtt.configuration.availability.payload_on)
            entity["payload_not_available"] = call_stack.get(self.mqtt.configuration.availability.payload_off)                 


    if self.hass_type == HassType.SENSOR or self.hass_type == HassType.SWITCH:
        '''Report actual state back to HA'''
        UpdateHassState(self, AutomationConfiguration()).invoke(call_stack) 

    self.mqtt.publish(self.auto_discovery_topic, entity, retain = True)

Inherited members

class HassType (value, names=None, *, module=None, qualname=None, type=None, start=1)

An enumeration.

Expand source code
class HassType(Enum):
    TRIGGER = "trigger"
    '''HomeAssistant's triggers are only to invoke HA Automations, not visible in Lovelace.'''

    SENSOR = "binary_sensor"
    '''Use this type to use the state of a piTomation entity in HA.'''

    SWITCH = "switch"
    '''Allows to toggle a piTomation state from HA.'''

Ancestors

  • enum.Enum

Class variables

var SENSOR

Use this type to use the state of a piTomation entity in HA.

var SWITCH

Allows to toggle a piTomation state from HA.

var TRIGGER

HomeAssistant's triggers are only to invoke HA Automations, not visible in Lovelace.