From e9d0871d4d13cfa92a86dff6da5351cdf7f92e05 Mon Sep 17 00:00:00 2001 From: Alessandro Staniscia Date: Sun, 24 Oct 2021 15:30:36 +0200 Subject: [PATCH] Add support for discovery device trigger (#1090) * Add announce of DeviceTrigger + Update doc --- docs/integrate/home_assistant.md | 25 ++++++- main/ZgatewayRF.ino | 93 ++++++++++++++++++++----- main/ZmqttDiscovery.ino | 112 ++++++++++++++++++++++++++++++- main/config_RF.h | 2 +- main/config_mqttDiscovery.h | 49 ++++++++++++++ 5 files changed, 262 insertions(+), 19 deletions(-) diff --git a/docs/integrate/home_assistant.md b/docs/integrate/home_assistant.md index 375408f29b..02c0df5aba 100644 --- a/docs/integrate/home_assistant.md +++ b/docs/integrate/home_assistant.md @@ -1,8 +1,19 @@ # Integrate Home Assistant + +Home Assistant provide the [MQTT integration](https://www.home-assistant.io/integrations/mqtt/) and through this integration it is possible to exploit and manage the messages published by OpenMqttGateway. + +Once this integration on home assistant is configured with the same mqtt broker, it is possible to create devices manually or through the autodiscovery function. + + ## Auto discovery -Home Assistant discovery is enabled by default on all binaries and platformio configurations except for UNO. With Arduino IDE please read the [advanced configuration section](../upload/advanced-configuration#auto-discovery) of the documentation. -First enable discovery on your MQTT integration in HASS. +From Home Assistant site + +> The discovery of MQTT devices will enable one to use MQTT devices with only minimal configuration effort on the side of Home Assistant. The configuration is done on the device itself and the topic used by the device. + +On OpenMqttGateway the Home Assistant discovery is enabled by default on all binaries and platformio configurations except for UNO. With Arduino IDE please read the [advanced configuration section](../upload/advanced-configuration#auto-discovery) of the documentation. Here are a few tips for activating discovery on Home Assistant, but for detailed configuration please refer to the Home Assistant website. + +Enable discovery on your MQTT integration in HASS. ![](../img/OpenMQTTGateway-Configuration-Home-Assistant-Discovery-Integration.png) @@ -22,6 +33,16 @@ OMG will use the auto discovery functionality of home assistant to create gatewa ![](../img/OpenMQTTGateway_Home_Assistant_MQTT_discovery.png) + +## MQTT Device Trigger and RF + +With OpenMqttGateway [configured to receive RF signals](./setitup/rf.html) the messages are transmitted as indicated by [RCSwitch based gateway](./use/rf.html#rcswitch-based-gateway), so it is possible to receive a pulse every time the sensor discover a signal. + +With autodiscovery enabled, HomeAssistant will discover a [MQTT Device Trigger](https://www.home-assistant.io/integrations/device_trigger.mqtt/) identified by the value field given in the mqtt argument. + + + + ## Manual integration examples From @123, @finity, @denniz03, @jrockstad, @anarchking, @dkluivingh diff --git a/main/ZgatewayRF.ino b/main/ZgatewayRF.ino index f6387d661a..a4e8faef05 100644 --- a/main/ZgatewayRF.ino +++ b/main/ZgatewayRF.ino @@ -37,22 +37,72 @@ RCSwitch mySwitch = RCSwitch(); +//SOME CONVERSION function from https://github.com/sui77/rc-switch/tree/master/examples/ReceiveDemo_Advanced +static const char* bin2tristate(const char* bin) { + static char returnValue[50]; + int pos = 0; + int pos2 = 0; + while (bin[pos] != '\0' && bin[pos + 1] != '\0') { + if (bin[pos] == '0' && bin[pos + 1] == '0') { + returnValue[pos2] = '0'; + } else if (bin[pos] == '1' && bin[pos + 1] == '1') { + returnValue[pos2] = '1'; + } else if (bin[pos] == '0' && bin[pos + 1] == '1') { + returnValue[pos2] = 'F'; + } else { + return "-"; + } + pos = pos + 2; + pos2++; + } + returnValue[pos2] = '\0'; + return returnValue; +} + +static char* dec2binWzerofill(unsigned long Dec, unsigned int bitLength) { + static char bin[64]; + unsigned int i = 0; + + while (Dec > 0) { + bin[32 + i++] = ((Dec & 1) > 0) ? '1' : '0'; + Dec = Dec >> 1; + } + + for (unsigned int j = 0; j < bitLength; j++) { + if (j >= bitLength - i) { + bin[j] = bin[31 + i - (j - (bitLength - i))]; + } else { + bin[j] = '0'; + } + } + bin[bitLength] = '\0'; + + return bin; +} + # if defined(ZmqttDiscovery) && !defined(RF_DISABLE_TRANSMIT) && defined(RFmqttDiscovery) -void RFtoMQTTdiscovery(SIGNAL_SIZE_UL_ULL MQTTvalue) { //on the fly switch creation from received RF values + +void RFtoMQTTdiscovery(SIGNAL_SIZE_UL_ULL MQTTvalue) { + //on the fly switch creation from received RF values char val[11]; sprintf(val, "%lu", MQTTvalue); - Log.trace(F("switchRFDiscovery" CR)); - char* switchRF[8] = {"switch", val, "", "", "", val, "", ""}; - //component type,name,availability topic,device class,value template,payload on, payload off, unit of measurement - + Log.trace(F("RF Entity Discovered, create HA Discovery CFG" CR)); + char* switchRF[2] = {val, "RF"}; Log.trace(F("CreateDiscoverySwitch: %s" CR), switchRF[1]); - createDiscovery(switchRF[0], - subjectRFtoMQTT, switchRF[1], (char*)getUniqueId(switchRF[1], switchRF[2]).c_str(), - will_Topic, switchRF[3], switchRF[4], - switchRF[5], switchRF[6], switchRF[7], - 0, "", "", true, subjectMQTTtoRF, - "", "", "", "", false, - stateClassNone); +# ifdef valueAsASubject + String discovery_topic = String(subjectRFtoMQTT) + "/" + String(switchRF[0]); +# else + String discovery_topic = String(subjectRFtoMQTT); +# endif + + String theUniqueId = getUniqueId("-" + String(switchRF[0]), "-" + String(switchRF[1])); + + announceDeviceTrigger( + false, + (char*)discovery_topic.c_str(), + "", "", + (char*)theUniqueId.c_str(), + "", "", "", ""); } # endif @@ -78,9 +128,9 @@ void setupRF() { void RFtoMQTT() { if (mySwitch.available()) { # ifdef ZradioCC1101 //receiving with CC1101 - const int JSON_MSG_CALC_BUFFER = JSON_OBJECT_SIZE(5); + const int JSON_MSG_CALC_BUFFER = JSON_OBJECT_SIZE(8); # else - const int JSON_MSG_CALC_BUFFER = JSON_OBJECT_SIZE(4); + const int JSON_MSG_CALC_BUFFER = JSON_OBJECT_SIZE(7); # endif StaticJsonDocument jsonBuffer; JsonObject RFdata = jsonBuffer.to(); @@ -89,10 +139,23 @@ void RFtoMQTT() { Log.trace(F("RF Task running on core :%d" CR), xPortGetCoreID()); # endif SIGNAL_SIZE_UL_ULL MQTTvalue = mySwitch.getReceivedValue(); + int length = mySwitch.getReceivedBitlength(); + const char* binary = dec2binWzerofill(MQTTvalue, length); + RFdata["value"] = (SIGNAL_SIZE_UL_ULL)MQTTvalue; RFdata["protocol"] = (int)mySwitch.getReceivedProtocol(); RFdata["length"] = (int)mySwitch.getReceivedBitlength(); RFdata["delay"] = (int)mySwitch.getReceivedDelay(); + RFdata["tre_state"] = bin2tristate(binary); + RFdata["binary"] = binary; + + unsigned int* raw = mySwitch.getReceivedRawdata(); + String rawDump = ""; + for (unsigned int i = 0; i <= length * 2; i++) { + rawDump = rawDump + String(raw[i]) + ","; + } + RFdata["raw"] = rawDump.c_str(); + # ifdef ZradioCC1101 // set Receive off and Transmitt on RFdata["mhz"] = receiveMhz; # endif @@ -269,4 +332,4 @@ void enableRFReceive() { receiveInterupt = RF_RECEIVER_GPIO; mySwitch.enableReceive(RF_RECEIVER_GPIO); } -#endif \ No newline at end of file +#endif diff --git a/main/ZmqttDiscovery.ino b/main/ZmqttDiscovery.ino index 1b3dc9714f..c02d8e2424 100644 --- a/main/ZmqttDiscovery.ino +++ b/main/ZmqttDiscovery.ino @@ -90,7 +90,116 @@ void createDiscoveryFromList(const char* mac, # endif /** - * Generate message and publish it on an mqtt discovery exploiter @see https://www.home-assistant.io/docs/mqtt/discovery/ + * @brief Create a message for Discovery Device Trigger. For HA @see https://www.home-assistant.io/integrations/device_trigger.mqtt/ + * @param use_gateway_info Boolean where true mean use the OMG information as Device Information + * @param topic The Topic where the trigger will publish the content + * @param type The type of the trigger, e.g. button_short_press. Entries supported by the HA Frontend: button_short_press, button_short_release, button_long_press, button_long_release, button_double_press, button_triple_press, button_quadruple_press, button_quintuple_press. If set to an unsupported value, will render as subtype type, e.g. button_1 spammed with type set to spammed and subtype set to button_1 + * @param subtype The subtype of the trigger, e.g. button_1. Entries supported by the HA frontend: turn_on, turn_off, button_1, button_2, button_3, button_4, button_5, button_6. If set to an unsupported value, will render as subtype type, e.g. left_button pressed with type set to button_short_press and subtype set to left_button + * @param unique_id Valid only if gateway entry is false, The IDs that uniquely identify the device. For example a serial number. + * @param device_name Valid only if gateway entry is false, The name of the device. + * @param device_manufacturer Valid only if gateway entry is false, The manufacturer of the device. + * @param device_model Valid only if gateway entry is false, The model of the device. + * @param device_mac Valid only if gateway entry is false, The connection of the device to the outside world + */ +void announceDeviceTrigger(bool use_gateway_info, + char* topic, + char* type, + char* subtype, + char* unique_id, + char* device_name, + char* device_manufacturer, + char* device_model, + char* device_mac) { + //Create The Json + const int JSON_MSG_CALC_BUFFER = JSON_OBJECT_SIZE(14) + JSON_OBJECT_SIZE(5) + JSON_ARRAY_SIZE(1); + StaticJsonDocument jsonBuffer; + JsonObject sensor = jsonBuffer.to(); + + // SET Default Configuration + sensor["automation_type"] = "trigger"; // The type of automation, must be ‘trigger’. + + //SET TYPE + if (type[0] != 0) { + sensor["type"] = type; + } else { + sensor["type"] = "button_short_press"; + } + + //SET SUBTYPE + if (subtype[0] != 0) { + sensor["subtype"] = subtype; + } else { + sensor["subtype"] = "turn_on"; + } + + /* Set The topic */ + if (topic[0]) { + char state_topic[mqtt_topic_max_size]; + + strcpy(state_topic, mqtt_topic); + strcat(state_topic, gateway_name); + + strcat(state_topic, topic); + sensor["topic"] = state_topic; + } + + /* Set The Devices */ + StaticJsonDocument jsonDeviceBuffer; + JsonObject device = jsonDeviceBuffer.to(); + JsonArray identifiers = device.createNestedArray("identifiers"); + + if (use_gateway_info) { + char JSONmessageBuffer[JSON_MSG_BUFFER]; + serializeJson(modules, JSONmessageBuffer, sizeof(JSONmessageBuffer)); + Log.notice(F("Received json : %s" CR), JSONmessageBuffer); + + device["name"] = gateway_name; + device["model"] = JSONmessageBuffer; + device["manufacturer"] = DEVICEMANUFACTURER; + device["sw_version"] = OMG_VERSION; + identifiers.add(getMacAddress()); + + } else { + char deviceid[13]; + memcpy(deviceid, &unique_id[0], 12); + deviceid[12] = '\0'; + + identifiers.add(deviceid); + + /*Set Connection */ + if (device_mac[0] != 0) { + JsonArray connections = device.createNestedArray("connections"); + JsonArray connection_mac = connections.createNestedArray(); + connection_mac.add("mac"); + connection_mac.add(device_mac); + } + + //Set manufacturer + if (device_manufacturer[0]) { + device["manufacturer"] = device_manufacturer; + } + + //Set name + if (device_name[0]) { + device["name"] = device_name; + } + + // set The Model + if (device_model[0]) { + device["model"] = device_model; + } + + device["via_device"] = gateway_name; //device name of the board + } + sensor["device"] = device; //device representing the board + + /* Publish on the topic */ + String topic_to_publish = String(discovery_Topic) + "/device_automation/" + String(unique_id) + "/config"; + pub_custom_topic((char*)topic_to_publish.c_str(), sensor, true); +} + +/** + * @brief Generate message and publish it on an mqtt discovery exploiter. For HA @see https://www.home-assistant.io/docs/mqtt/discovery/ * * @param sensor_type the Type * @param st_topic set state topic, @@ -634,6 +743,7 @@ void pubMqttDiscovery() { "", "", "", "", false, // device name, device manufacturer, device model, device mac, retain stateClassNone //State Class ); + # endif # ifdef ZgatewayRF2 diff --git a/main/config_RF.h b/main/config_RF.h index 7e86ed0a36..e088a5d331 100644 --- a/main/config_RF.h +++ b/main/config_RF.h @@ -86,7 +86,7 @@ int minimumRssi = 0; #define RF_EMITTER_REPEAT 20 #define RF2_EMITTER_REPEAT 2 // Actual repeats is 2^R, where R is the here configured amount //#define RF_DISABLE_TRANSMIT //Uncomment this line to disable RF transmissions. (RF Receive will work as normal.) -//#define RFmqttDiscovery true //uncomment this line so as to create a discovery switch for each RF signal received +#define RFmqttDiscovery true //uncomment this line so as to create a discovery switch for each RF signal received /*-------------------RF2 topics & parameters----------------------*/ //433Mhz newremoteswitch MQTT Subjects and keys diff --git a/main/config_mqttDiscovery.h b/main/config_mqttDiscovery.h index ad4e9e0617..890b5bae1e 100644 --- a/main/config_mqttDiscovery.h +++ b/main/config_mqttDiscovery.h @@ -31,6 +31,33 @@ extern String getUniqueId(String name, String sufix); extern void pubMqttDiscovery(); extern void createDiscoveryFromList(const char* mac, const char* sensorList[][8], int sensorCount, const char* device_name, const char* device_manufacturer, const char* device_model); + +/** + * @brief Generate message and publish it on an mqtt discovery exploiter. For HA @see https://www.home-assistant.io/docs/mqtt/discovery/ + * + * @param sensor_type the Type + * @param st_topic set state topic, + * @param s_name set name, + * @param unique_id set niqueId + * @param availability_topic set availability_topic, + * @param device_class set device_class, + * @param value_template set value_template, + * @param payload_on set payload_on, + * @param payload_off set payload_off, + * @param unit_of_meas set unit_of_meas, + * @param off_delay set off_delay + * @param payload_available set payload_avalaible, + * @param payload_not_avalaible set payload_not_avalaible + * @param gateway_entity set is a gateway entity, + * @param cmd_topic set command topic + * @param device_name set device name, + * @param device_manufacturer set device manufacturer, + * @param device_model set device model, + * @param device_mac set device mac, + * @param retainCmd set retain + * @param state_class set state class + * + * */ extern void createDiscovery(const char* sensor_type, const char* state_topic, const char* s_name, const char* unique_id, const char* availability_topic, const char* device_class, const char* value_template, @@ -40,6 +67,28 @@ extern void createDiscovery(const char* sensor_type, const char* device_name, const char* device_manufacturer, const char* device_model, const char* device_mac, bool retainCmd, const char* state_class); +/** + * @brief Create a message for Discovery Device Trigger. For HA @see https://www.home-assistant.io/integrations/device_trigger.mqtt/ + * @param use_gateway_info Boolean where true mean use the OMG information as Device Information + * @param topic The Topic where the trigger will publish the content + * @param type The type of the trigger, e.g. button_short_press. Entries supported by the HA Frontend: button_short_press, button_short_release, button_long_press, button_long_release, button_double_press, button_triple_press, button_quadruple_press, button_quintuple_press. If set to an unsupported value, will render as subtype type, e.g. button_1 spammed with type set to spammed and subtype set to button_1 + * @param subtype The subtype of the trigger, e.g. button_1. Entries supported by the HA frontend: turn_on, turn_off, button_1, button_2, button_3, button_4, button_5, button_6. If set to an unsupported value, will render as subtype type, e.g. left_button pressed with type set to button_short_press and subtype set to left_button + * @param unique_id Valid only if gateway entry is false, The IDs that uniquely identify the device. For example a serial number. + * @param device_name Valid only if gateway entry is false, The name of the device. + * @param device_manufacturer Valid only if gateway entry is false, The manufacturer of the device. + * @param device_model Valid only if gateway entry is false, The model of the device. + * @param device_mac Valid only if gateway entry is false, The connection of the device to the outside world + */ +void announceDeviceTrigger(bool use_gateway_info, + char* topic, + char* type, + char* subtype, + char* unique_id, + char* device_name, + char* device_manufacturer, + char* device_model, + char* device_mac); + #define discovery_Topic "homeassistant" #define DEVICEMANUFACTURER "OMG_community"