How to use MQTT discovery to expose lights

Hi,

In my home, I have several smart switches (Fibaro FGS and FGD) linked to smart lights (Philipps Hue). I created a light template to mix the switch and the lights. Obviously, there is a lot of copy/paste where only the name of the entity change.

So I’m thinking to create an app within AD taking a switch and a list of lights. The purpose of this app would be to create the lights within HA using MQTT discovery.

I find here and on Discord some guys that already did this, but there is no linked code.

I’m not sure how to start this project.

Sorry, maybe I am missing something, but why do you want to link smart switches with smart lights? Normally, you would want a smart light with a dumb switch (not a great solution, since you cannot touch the switch) or a smart switch with a dumb light (good solution).

Unless, you are using the smart switches as detached from the electrical network and do the connections through HA. Is this what you are trying to solve? If this is the case how are the smart switches communicating with HA?

So I use smart switch with smart light because I still want to use the switch manually (WAF), but I also want the capability to change the color or temperature of the light.

Here an example of the template I use:

table:
      friendly_name: Table
      entity_id:
        - switch.fibaro_system_fgs222_double_relay_switch_2x1_5kw_switch_4
        - light.lumieres_de_la_salle_a_manger
      value_template: "{{states('switch.fibaro_system_fgs222_double_relay_switch_2x1_5kw_switch_4')}}"
      level_template: "{{state_attr('light.lumieres_de_la_salle_a_manger', 'brightness')}}"
      turn_on:
        service: switch.turn_on
        entity_id: switch.fibaro_system_fgs222_double_relay_switch_2x1_5kw_switch_4
      turn_off:
        service: switch.turn_off
        entity_id: switch.fibaro_system_fgs222_double_relay_switch_2x1_5kw_switch_4
      set_level:
        service: light.turn_on
        data_template:
          entity_id: light.lumieres_de_la_salle_a_manger
          brightness: "{{ brightness }}"

I have to repeat this template for each switch/bulb tuple. Instead of this I want to use the MQTT discovery. AD will publish the light config and also listen for the MQTT messages.

So finally, I was able to do an application mix-in a switch and a light to expose them as a single light to HASS using the MQTT discovery:

Light table:
  class: MqttLight
  module: mqtt_light
  light: light.hue_light_1
  switch: switch.fibaro_system_fgs222_double_relay_switch_2x1_5kw_switch_4
  name: Table
import json
from appdaemon.adbase import ADBase


class MqttLight(ADBase):
    def initialize(self):
        self.hass = self.get_plugin_api("HASS")
        self.mqtt = self.get_plugin_api("MQTT")

        entity = str.lower(str.replace(self.args["name"], " ", "_"))
        self.topic = f"homeassistant/light/{entity}"

        self.hass.listen_state(self.update_switch_state, self.args["switch"])
        self.hass.listen_state(
            self.update_light_state, self.args["light"], attribute="all"
        )
        self.mqtt.listen_event(self.update_state, topic=f"{self.topic}/set")
        self.publish_light_configuration()

    def publish_light_configuration(self):
        supported_features = self.hass.get_state(
            self.args["light"], attribute="supported_features"
        )
        binary_supported_features = format(supported_features, "08b")
        config = {
            "~": self.topic,
            "name": self.args["name"],
            "cmd_t": "~/set",
            "stat_t": "~/state",
            "schema": "json",
            "brightness": binary_supported_features[7] == "1",
            "color_temp": binary_supported_features[6] == "1",
            "hs": binary_supported_features[3] == "1",
            "white_value": binary_supported_features[0] == "1",
            "effect": binary_supported_features[5] == "1",
            "effect_list": ["colorloop", "random"],
        }
        self.mqtt.mqtt_publish(f"{self.topic}/config", payload=json.dumps(config))

    def update_switch_state(self, entity, attribute, old, new, kwargs):
        payload = {"state": str.upper(new)}
        self.publish_state(payload)

    def update_light_state(self, entity, attribute, old, new, kwargs):
        self.hass.log(f"Update light state: {new}")
        attrs = new["attributes"]
        payload = {"state": "ON"}
        if "hs_color" in attrs:
            payload["color"] = {"h": attrs["hs_color"][0], "s": attrs["hs_color"][1]}
        if "brightness" in attrs:
            payload["brightness"] = attrs["brightness"]
        if "color_temp" in attrs:
            payload["color_temp"] = attrs["color_temp"]
        if "white_value" in attrs:
            payload["white_value"] = attrs["white_value"]
        self.publish_state(payload)

    def update_state(self, event_name, data, kwargs):
        payload = json.loads(data["payload"])
        if payload.get("state") == "ON":
            attributes = {}
            if "color" in payload:
                attributes["hs_color"] = [payload["color"]["h"], payload["color"]["s"]]
            if "brightness" in payload:
                attributes["brightness"] = payload["brightness"]
            if "color_temp" in payload:
                attributes["color_temp"] = payload["color_temp"]
            if "white_value" in payload:
                attributes["white_value"] = payload["white_value"]
            if "effect" in payload:
                attributes["effect"] = payload["effect"]

            self.hass.turn_on(self.args["switch"])
            self.hass.turn_on(self.args["light"], **attributes)

        elif payload.get("state") == "OFF":
            self.hass.turn_off(self.args["switch"])

        self.publish_state(payload)

    def publish_state(self, payload):
        self.mqtt.mqtt_publish(f"{self.topic}/state", payload=json.dumps(payload))

I’ve updated and fixed some issue with my previous code. Using the Hue app, when I update the color of a light, the associated smart switch will be turn on automatically :slight_smile:

import json
from appdaemon.adbase import ADBase
import voluptuous as vol
from constants import APP_MIN_SCHEMA

APP_SCHEMA = APP_MIN_SCHEMA.extend(
    {
        vol.Required("name"): str,
        vol.Required("light"): str,
        vol.Required("switch"): str,
        vol.Required("unique_id"): str,
    }
)


class MqttLight(ADBase):
    def initialize(self):
        self.args = APP_SCHEMA(self.args)
        self.hass = self.get_plugin_api("HASS")
        self.mqtt = self.get_plugin_api("MQTT")
        self.light = self.args["light"]
        self.switch = self.args["switch"]

        entity = str.lower(str.replace(self.args["name"], " ", "_"))
        self.topic = f"homeassistant/light/{entity}"

        self.hass.listen_state(self.switch2mqtt, self.switch)
        self.hass.listen_state(self.light2mqtt, self.light, attribute="all")
        self.mqtt.listen_event(self.set_state, topic=f"{self.topic}/set")
        self.publish_light_configuration()

    def publish_light_configuration(self):
        supported_features = self.hass.get_state(
            self.light, attribute="supported_features"
        )
        effect_list = self.hass.get_state(self.light, attribute="effect_list")
        binary_supported_features = format(supported_features, "08b")
        config = {
            "~": self.topic,
            "name": self.args["name"],
            "uniq_id": self.args["unique_id"],
            "cmd_t": "~/set",
            "stat_t": "~/state",
            "schema": "json",
            "brightness": binary_supported_features[7] == "1",
            "color_temp": binary_supported_features[6] == "1",
            "effect": binary_supported_features[5] == "1",
            "hs": binary_supported_features[3] == "1",
            "white_value": binary_supported_features[0] == "1",
            "effect_list": effect_list,
            "ret": True,
        }
        self.mqtt.mqtt_publish(f"{self.topic}/config", payload=json.dumps(config))

    def switch2mqtt(self, entity, attribute, old, new, kwargs):
        payload = {"state": str.upper(new)}
        self.publish_state(payload)

    def light2mqtt(self, entity, attribute, old, new, kwargs):
        new_attrs = new["attributes"]
        attrs = ["brightness", "color_temp", "white_value"]
        payload = {attr: new_attrs[attr] for attr in attrs if attr in new_attrs}
        payload["state"] = new["state"].upper()
        if "hs_color" in new_attrs:
            payload["color"] = {
                "h": new_attrs["hs_color"][0],
                "s": new_attrs["hs_color"][1],
            }
        self.publish_state(payload)

    def set_state(self, event_name, data, kwargs):
        if "payload" not in data:
            return
        payload = json.loads(data["payload"])
        if payload.get("state") == "ON":
            attrs = ["brightness", "color_temp", "white_value", "effect"]
            attributes = {attr: payload[attr] for attr in attrs if attr in payload}
            if "color" in payload:
                attributes["hs_color"] = [payload["color"]["h"], payload["color"]["s"]]

            self.hass.turn_on(self.switch)
            self.hass.turn_on(self.light, **attributes)

        elif payload.get("state") == "OFF":
            self.hass.turn_off(self.switch)

        self.publish_state(payload)

    def publish_state(self, payload):
        self.mqtt.mqtt_publish(f"{self.topic}/state", payload=json.dumps(payload))