Skip to content

Commit

Permalink
Add support for discovery device trigger (1technophile#1090)
Browse files Browse the repository at this point in the history
* Add announce of DeviceTrigger +  Update doc
  • Loading branch information
Odyno authored and Kern, Thomas committed Oct 27, 2021
1 parent ad63903 commit e9d0871
Show file tree
Hide file tree
Showing 5 changed files with 262 additions and 19 deletions.
25 changes: 23 additions & 2 deletions docs/integrate/home_assistant.md
Original file line number Diff line number Diff line change
@@ -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)

Expand All @@ -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

Expand Down
93 changes: 78 additions & 15 deletions main/ZgatewayRF.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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<JSON_MSG_CALC_BUFFER> jsonBuffer;
JsonObject RFdata = jsonBuffer.to<JsonObject>();
Expand All @@ -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
Expand Down Expand Up @@ -269,4 +332,4 @@ void enableRFReceive() {
receiveInterupt = RF_RECEIVER_GPIO;
mySwitch.enableReceive(RF_RECEIVER_GPIO);
}
#endif
#endif
112 changes: 111 additions & 1 deletion main/ZmqttDiscovery.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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<JSON_MSG_CALC_BUFFER> jsonBuffer;
JsonObject sensor = jsonBuffer.to<JsonObject>();

// 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<JSON_MSG_BUFFER> jsonDeviceBuffer;
JsonObject device = jsonDeviceBuffer.to<JsonObject>();
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,
Expand Down Expand Up @@ -634,6 +743,7 @@ void pubMqttDiscovery() {
"", "", "", "", false, // device name, device manufacturer, device model, device mac, retain
stateClassNone //State Class
);

# endif

# ifdef ZgatewayRF2
Expand Down
2 changes: 1 addition & 1 deletion main/config_RF.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
49 changes: 49 additions & 0 deletions main/config_mqttDiscovery.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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"
Expand Down

0 comments on commit e9d0871

Please sign in to comment.