diff --git a/hatasmota/button.py b/hatasmota/button.py index 5ab2608..9cc8d7a 100644 --- a/hatasmota/button.py +++ b/hatasmota/button.py @@ -10,9 +10,10 @@ OPTION_BUTTON_SINGLE, OPTION_BUTTON_SWAP, OPTION_MQTT_BUTTONS, + RSLT_ACTION, ) from .trigger import TasmotaTrigger -from .utils import get_state_button_trigger, get_topic_stat_button_trigger +from .utils import get_topic_stat_result, get_value_by_path _LOGGER = logging.getLogger(__name__) @@ -124,7 +125,7 @@ def from_discovery_message(cls, config, idx): idx=idx, source="button", subtype=f"button_{idx+1}", - trigger_topic=get_topic_stat_button_trigger(config, idx), + trigger_topic=get_topic_stat_result(config), type=trigger_type, ) ) @@ -146,6 +147,6 @@ class TasmotaButtonTrigger(TasmotaTrigger): def _trig_message_received(self, msg): """Handle new MQTT messages.""" - event = get_state_button_trigger(msg.payload) + event = get_value_by_path(msg.payload, [f"Button{self.cfg.idx+1}", RSLT_ACTION]) if event == self.cfg.event: self._on_trigger_callback() diff --git a/hatasmota/const.py b/hatasmota/const.py index 807163f..ab3431a 100644 --- a/hatasmota/const.py +++ b/hatasmota/const.py @@ -16,6 +16,7 @@ CONF_DEVICENAME = "dn" CONF_FRIENDLYNAME = "fn" CONF_FULLTOPIC = "ft" +CONF_IFAN = "if" CONF_IP = "ip" CONF_HOSTNAME = "hn" CONF_MAC = "mac" @@ -31,6 +32,7 @@ CONF_RELAY = "rl" CONF_SW_VERSION = "sw" CONF_SWITCH = "swc" +CONF_SWITCHNAME = "swn" CONF_TOPIC = "t" CONF_TUYA = "ty" CONF_VERSION = "ver" @@ -46,6 +48,7 @@ LST_RGBCW = 5 # fmt: off +OPTION_MQTT_RESPONSE = "4" # Return MQTT response as RESULT or %COMMAND% OPTION_BUTTON_SWAP = "11" # Swap button single and double press functionality OPTION_BUTTON_SINGLE = "13" # Allow immediate action on single button press OPTION_DECIMAL_TEXT = "17" # Show Color string as hex or comma-separated @@ -53,8 +56,8 @@ OPTION_HASS_LIGHT = "30" # Enforce Home Assistant auto-discovery as light OPTION_PWM_MULTI_CHANNELS = "68" # Multi-channel PWM instead of a single light OPTION_MQTT_BUTTONS = "73" # Enable Buttons decoupling and send multi-press and hold MQTT messages -OPTION_SHUTTER_MODE = "80" # Blinds and shutters support OPTION_REDUCED_CT_RANGE = "82" # Reduce the CT range from 153..500 to 200.380 +OPTION_MQTT_SWITCHES = "114" # Enable sending switch MQTT messages # fmt: on PREFIX_CMND = 0 @@ -66,7 +69,7 @@ RL_LIGHT = 2 RL_SHUTTER = 3 -RSLT_ACTION = "ACTION" +RSLT_ACTION = "Action" RSLT_POWER = "POWER" RSLT_STATE = "STATE" RSLT_TRIG = "TRIG" diff --git a/hatasmota/discovery.py b/hatasmota/discovery.py index 20221b2..b63a64a 100644 --- a/hatasmota/discovery.py +++ b/hatasmota/discovery.py @@ -12,6 +12,7 @@ CONF_FRIENDLYNAME, CONF_FULLTOPIC, CONF_HOSTNAME, + CONF_IFAN, CONF_IP, CONF_LIGHT_SUBTYPE, CONF_LINK_RGB_CT, @@ -28,6 +29,7 @@ CONF_STATE, CONF_SW_VERSION, CONF_SWITCH, + CONF_SWITCHNAME, CONF_TOPIC, CONF_TUYA, CONF_VERSION, @@ -36,10 +38,11 @@ OPTION_DECIMAL_TEXT, OPTION_HASS_LIGHT, OPTION_MQTT_BUTTONS, + OPTION_MQTT_RESPONSE, + OPTION_MQTT_SWITCHES, OPTION_NOT_POWER_LINKED, OPTION_PWM_MULTI_CHANNELS, OPTION_REDUCED_CT_RANGE, - OPTION_SHUTTER_MODE, RL_LIGHT, RL_RELAY, ) @@ -57,6 +60,7 @@ TASMOTA_OPTIONS_SCHEMA = vol.Schema( { + OPTION_MQTT_RESPONSE: cv.bit, OPTION_BUTTON_SWAP: cv.bit, OPTION_BUTTON_SINGLE: cv.bit, OPTION_DECIMAL_TEXT: cv.bit, @@ -64,8 +68,8 @@ OPTION_HASS_LIGHT: cv.bit, OPTION_PWM_MULTI_CHANNELS: cv.bit, OPTION_MQTT_BUTTONS: cv.bit, - OPTION_SHUTTER_MODE: cv.bit, OPTION_REDUCED_CT_RANGE: cv.bit, + OPTION_MQTT_SWITCHES: cv.bit, }, required=True, ) @@ -77,6 +81,7 @@ CONF_FRIENDLYNAME: vol.All(cv.ensure_list, [cv.optional_string]), CONF_FULLTOPIC: cv.string, CONF_HOSTNAME: cv.string, + CONF_IFAN: cv.bit, CONF_IP: cv.string, CONF_LIGHT_SUBTYPE: cv.positive_int, CONF_LINK_RGB_CT: cv.bit, @@ -89,6 +94,7 @@ CONF_STATE: vol.All(cv.ensure_list, [cv.string]), CONF_SW_VERSION: cv.string, CONF_SWITCH: vol.All(cv.ensure_list, [int]), + CONF_SWITCHNAME: vol.All(cv.ensure_list, [cv.optional_string]), CONF_RELAY: vol.All(cv.ensure_list, [cv.positive_int]), CONF_TOPIC: cv.string, CONF_TUYA: cv.bit, diff --git a/hatasmota/switch.py b/hatasmota/switch.py index ad62bc7..9c9a778 100644 --- a/hatasmota/switch.py +++ b/hatasmota/switch.py @@ -7,7 +7,7 @@ CONF_MAC, CONF_STATE, CONF_SWITCH, - SENSOR_SWITCH, + RSLT_ACTION, STATE_HOLD, STATE_TOGGLE, STATUS_SENSOR, @@ -41,12 +41,10 @@ config_get_state_online, config_get_state_power_off, config_get_state_power_on, - get_state_state, - get_state_switch_trigger, + config_get_switchname, get_topic_command_status, + get_topic_stat_result, get_topic_stat_status, - get_topic_stat_switch, - get_topic_stat_switch_trigger, get_topic_tele_sensor, get_topic_tele_will, get_value_by_path, @@ -175,6 +173,7 @@ class TasmotaSwitchTriggerConfig: mac: str = attr.ib() source: str = attr.ib() subtype: str = attr.ib() + switchname: str = attr.ib() trigger_topic: str = attr.ib() type: str = attr.ib() @@ -192,7 +191,8 @@ def from_discovery_message(cls, config, idx): idx=idx, source="switch", subtype=f"switch_{idx+1}", - trigger_topic=get_topic_stat_switch_trigger(config, idx), + switchname=config_get_switchname(config, idx), + trigger_topic=get_topic_stat_result(config), type=trigger_type, ) ) @@ -214,7 +214,7 @@ class TasmotaSwitchTrigger(TasmotaTrigger): def _trig_message_received(self, msg): """Handle new MQTT messages.""" - event = get_state_switch_trigger(msg.payload) + event = get_value_by_path(msg.payload, [self.cfg.switchname, RSLT_ACTION]) if event == self.cfg.event: self._on_trigger_callback() @@ -225,13 +225,12 @@ class TasmotaSwitchConfig(TasmotaAvailabilityConfig, TasmotaEntityConfig): off_delay: int = attr.ib() poll_topic: str = attr.ib() - sensor: str = attr.ib() state_power_off: str = attr.ib() state_power_on: str = attr.ib() state_topic1: str = attr.ib() state_topic2: str = attr.ib() state_topic3: str = attr.ib() - # unique_id: str = attr.ib() + switchname: str = attr.ib() @classmethod def from_discovery_message(cls, config, idx, platform): @@ -253,12 +252,12 @@ def from_discovery_message(cls, config, idx, platform): availability_offline=config_get_state_offline(config), availability_online=config_get_state_online(config), off_delay=off_delay, - sensor=SENSOR_SWITCH + f"{idx+1}", state_power_off=config_get_state_power_off(config), state_power_on=config_get_state_power_on(config), - state_topic1=get_topic_stat_switch(config, idx), + state_topic1=get_topic_stat_result(config), state_topic2=get_topic_tele_sensor(config), state_topic3=get_topic_stat_status(config, 8), + switchname=config_get_switchname(config, idx), ) @@ -276,16 +275,18 @@ async def subscribe_topics(self): def state_message_received(msg): """Handle new MQTT state messages.""" state = None - # tasmota_0848A2/stat/SWITCH1 / {"STATE":"OFF"} + # tasmota_0848A2/stat/RESULT / {"Switch1":{"Action":"ON"}} if msg.topic == self._cfg.state_topic1: - state = get_state_state(msg.payload) + state = get_value_by_path( + msg.payload, [self._cfg.switchname, RSLT_ACTION] + ) # tasmota_0848A2/tele/SENSOR / {"Time":"2020-09-20T09:41:28","Switch1":"ON"} if msg.topic == self._cfg.state_topic2: - state = get_value_by_path(msg.payload, [self._cfg.sensor]) + state = get_value_by_path(msg.payload, [self._cfg.switchname]) # tasmota_0848A2/stat/STATUS8 / {"StatusSNS":{"Time":"2020-09-20T09:41:00","Switch1":"ON"}} if msg.topic == self._cfg.state_topic3: state = get_value_by_path( - msg.payload, [STATUS_SENSOR, self._cfg.sensor] + msg.payload, [STATUS_SENSOR, self._cfg.switchname] ) if state == self._cfg.state_power_on: self._on_state_callback(True) @@ -293,7 +294,7 @@ def state_message_received(msg): self._on_state_callback(False) availability_topics = self.get_availability_topics() - # tasmota_0848A2/stat/SWITCH1 / {"STATE":"OFF"} + # tasmota_0848A2/stat/RESULT / {"Switch1":{"Action":"ON"}} # tasmota_0848A2/tele/SENSOR / {"Time":"2020-09-20T09:41:28","Switch1":"ON"} # tasmota_0848A2/stat/STATUS8 / {"StatusSNS":{"Time":"2020-09-20T09:41:00","Switch1":"ON"}} topics = { diff --git a/hatasmota/utils.py b/hatasmota/utils.py index 19579bb..b9dcd43 100644 --- a/hatasmota/utils.py +++ b/hatasmota/utils.py @@ -17,6 +17,7 @@ CONF_PREFIX, CONF_RELAY, CONF_STATE, + CONF_SWITCHNAME, CONF_TOPIC, PREFIX_CMND, PREFIX_STAT, @@ -24,7 +25,6 @@ RSLT_ACTION, RSLT_POWER, RSLT_STATE, - RSLT_TRIG, STATE_OFF, STATE_ON, ) @@ -85,7 +85,7 @@ def get_topic_command_status(config): def get_topic_stat_button_trigger(config, idx): """Get topic for tele state.""" - return _get_topic_stat(config) + f"BUTTON{idx+1}T" + return _get_topic_stat(config) + f"BUTTON{idx+1}" def get_topic_stat_result(config): @@ -107,7 +107,7 @@ def get_topic_stat_switch(config, idx): def get_topic_stat_switch_trigger(config, idx): """Get topic for tele state.""" - return _get_topic_stat(config) + f"SWITCH{idx+1}T" + return _get_topic_stat(config) + f"SWITCH{idx+1}" def get_topic_tele_sensor(config): @@ -185,11 +185,6 @@ def get_state_button_trigger(status): return get_value(status, RSLT_ACTION) -def get_state_switch_trigger(status): - """Get state of switch.""" - return get_value(status, RSLT_TRIG) - - def config_get_friendlyname(config, platform, idx): """Get config friendly name.""" friendly_names = config[CONF_FRIENDLYNAME] @@ -204,6 +199,15 @@ def config_get_friendlyname(config, platform, idx): return friendly_names[idx] +def config_get_switchname(config, idx): + """Get switch name.""" + switch_names = config[CONF_SWITCHNAME] + + if idx >= len(switch_names) or switch_names[idx] is None: + return f"Switch{idx+1}" + return switch_names[idx] + + TOPIC_MATCHER = re.compile(r"^(?P[A-Z0-9_-]+)\/(?:config|sensors)$")