mirror of https://github.com/arendst/Tasmota.git
165 lines
8.4 KiB
C++
165 lines
8.4 KiB
C++
/*
|
|
xdrv_73_6_lorawan_decode.ino - LoRaWan node decode support for Tasmota
|
|
|
|
SPDX-FileCopyrightText: 2024 Theo Arends
|
|
|
|
SPDX-License-Identifier: GPL-3.0-only
|
|
*/
|
|
|
|
#ifdef USE_SPI_LORA
|
|
#ifdef USE_LORAWAN_BRIDGE
|
|
/*********************************************************************************************\
|
|
* LoRaWan node decode and presentation
|
|
\*********************************************************************************************/
|
|
|
|
void LoraWanPublishHeader(uint32_t node) {
|
|
ResponseClear(); // clear string
|
|
|
|
// Do we prefix with `LwReceived`?
|
|
if (!Settings->flag4.remove_zbreceived && // SetOption100 - (Zigbee) Remove LwReceived form JSON message (1)
|
|
!Settings->flag5.zb_received_as_subtopic) { // SetOption118 - (Zigbee) Move LwReceived from JSON message and into the subtopic replacing "SENSOR" default
|
|
if (Settings->flag5.zigbee_include_time && // SetOption144 - (Zigbee) Include time in `LwReceived` messages like other sensors
|
|
(Rtc.utc_time >= START_VALID_TIME)) {
|
|
// Add time if needed (and if time is valid)
|
|
ResponseAppendTimeFormat(Settings->flag2.time_format); // CMND_TIME
|
|
ResponseAppend_P(PSTR(",\"LwReceived\":"));
|
|
} else {
|
|
ResponseAppend_P(PSTR("{\"LwReceived\":"));
|
|
}
|
|
}
|
|
|
|
if (!Settings->flag5.zb_omit_json_addr) { // SetOption119 - (Zigbee) Remove the device addr from json payload, can be used with zb_topic_fname where the addr is already known from the topic
|
|
ResponseAppend_P(PSTR("{\"%s\":"), EscapeJSONString(Lora->settings.end_node[node].name.c_str()).c_str());
|
|
}
|
|
ResponseAppend_P(PSTR("{\"Node\":%d,\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""), node +1, Lora->settings.end_node[node].DevEUIl & 0x0000FFFF);
|
|
if (!Lora->settings.end_node[node].name.startsWith(F("0x"))) {
|
|
ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""), EscapeJSONString(Lora->settings.end_node[node].name.c_str()).c_str());
|
|
}
|
|
ResponseAppend_P(PSTR(",\"RSSI\":%1_f,\"SNR\":%1_f"), &Lora->rssi, &Lora->snr);
|
|
}
|
|
|
|
/*********************************************************************************************/
|
|
|
|
void LoraWanPublishFooter(uint32_t node) {
|
|
if (!Settings->flag5.zb_omit_json_addr) { // SetOption119 - (Zigbee) Remove the device addr from json payload, can be used with zb_topic_fname where the addr is already known from the topic
|
|
ResponseAppend_P(PSTR("}"));
|
|
}
|
|
if (!Settings->flag4.remove_zbreceived && // SetOption100 - (Zigbee) Remove LwReceived form JSON message (1)
|
|
!Settings->flag5.zb_received_as_subtopic) { // SetOption118 - (Zigbee) Move LwReceived from JSON message and into the subtopic replacing "SENSOR" default
|
|
ResponseAppend_P(PSTR("}"));
|
|
}
|
|
|
|
#ifdef USE_INFLUXDB
|
|
InfluxDbProcess(1); // Use a copy of ResponseData
|
|
#endif
|
|
|
|
if (Settings->flag4.zigbee_distinct_topics) { // SetOption89 - (MQTT, Zigbee) Distinct MQTT topics per device for Zigbee (1) (#7835)
|
|
char subtopic[TOPSZ];
|
|
// Clean special characters
|
|
char stemp[TOPSZ];
|
|
strlcpy(stemp, Lora->settings.end_node[node].name.c_str(), sizeof(stemp));
|
|
MakeValidMqtt(0, stemp);
|
|
if (Settings->flag5.zigbee_hide_bridge_topic) { // SetOption125 - (Zigbee) Hide bridge topic from zigbee topic (use with SetOption89) (1)
|
|
snprintf_P(subtopic, sizeof(subtopic), PSTR("%s"), stemp);
|
|
} else {
|
|
snprintf_P(subtopic, sizeof(subtopic), PSTR("%s/%s"), TasmotaGlobal.mqtt_topic, stemp);
|
|
}
|
|
char stopic[TOPSZ];
|
|
if (Settings->flag5.zb_received_as_subtopic) // SetOption118 - (Zigbee) Move LwReceived from JSON message and into the subtopic replacing "SENSOR" default
|
|
GetTopic_P(stopic, TELE, subtopic, PSTR("LwReceived"));
|
|
else
|
|
GetTopic_P(stopic, TELE, subtopic, PSTR(D_RSLT_SENSOR));
|
|
MqttPublish(stopic, Settings->flag.mqtt_sensor_retain);
|
|
} else {
|
|
MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_SENSOR), Settings->flag.mqtt_sensor_retain);
|
|
}
|
|
XdrvRulesProcess(0); // Apply rules
|
|
}
|
|
|
|
/*********************************************************************************************/
|
|
|
|
void LoraWanDecode(struct LoraNodeData_t* node_data) {
|
|
/* Enter with LoraNodeData_t
|
|
float rssi;
|
|
float snr;
|
|
uint8_t* payload;
|
|
uint8_t payload_len;
|
|
uint8_t node;
|
|
uint8_t FPort;
|
|
*/
|
|
if (bitRead(Lora->settings.flags, TAS_LORA_FLAG_DECODE_ENABLED)) { // LoraOption3 1
|
|
if (0x00161600 == Lora->settings.end_node[node_data->node].DevEUIh) { // MerryIoT
|
|
if (120 == node_data->FPort) { // MerryIoT door/window Sensor (DW10)
|
|
if (9 == node_data->payload_len) { // MerryIoT Sensor state
|
|
// 1 2 3 4 5 6 7 8 9
|
|
// 03 0F 19 2C 8A00 040000 - button
|
|
// 00 0F 19 2C 0000 050000 - door
|
|
uint8_t status = node_data->payload[0];
|
|
float battery_volt = (float)(21 + node_data->payload[1]) / 10.0;
|
|
int temperature = node_data->payload[2];
|
|
int humidity = node_data->payload[3];
|
|
uint32_t elapsed_time = node_data->payload[4] | (node_data->payload[5] << 8);
|
|
uint32_t events = node_data->payload[6] | (node_data->payload[7] << 8) | (node_data->payload[8] << 16);
|
|
#ifdef USE_LORA_DEBUG
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("LOR: Node %d, DevEUI %08X%08X, Events %d, LastEvent %d min, DoorOpen %d, Button %d, Tamper %d, Tilt %d, Battery %1_fV, Temp %d, Hum %d"),
|
|
node_data->node +1, Lora->settings.end_node[node_data->node].DevEUIh, Lora->settings.end_node[node_data->node].DevEUIl,
|
|
events, elapsed_time,
|
|
bitRead(status, 0), bitRead(status, 1), bitRead(status, 2), bitRead(status, 3),
|
|
&battery_volt,
|
|
temperature, humidity);
|
|
#endif // USE_LORA_DEBUG
|
|
LoraWanPublishHeader(node_data->node);
|
|
ResponseAppend_P(PSTR(",\"Events\":%d,\"LastEvent\":%d,\"DoorOpen\":%d,\"Button\":%d,\"Tamper\":%d,\"Tilt\":%d"
|
|
",\"Battery\":%1_f,"),
|
|
events, elapsed_time,
|
|
bitRead(status, 0), bitRead(status, 1), bitRead(status, 2), bitRead(status, 3),
|
|
&battery_volt);
|
|
ResponseAppendTHD(temperature, humidity);
|
|
ResponseAppend_P(PSTR("}"));
|
|
LoraWanPublishFooter(node_data->node);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (0xA840410E == Lora->settings.end_node[node_data->node].DevEUIh) { // Dragino
|
|
if (10 == node_data->FPort) { // Dragino LDS02
|
|
// 8CD2 01 000010 000000 00 - Door Open, 3.282V
|
|
// 0CD2 01 000011 000000 00 - Door Closed
|
|
uint8_t status = node_data->payload[0];
|
|
float battery_volt = (float)((node_data->payload[1] | (node_data->payload[0] << 8)) &0x3FFF) / 1000;
|
|
uint8_t MOD = node_data->payload[2]; // Always 0x01
|
|
uint32_t events = node_data->payload[5] | (node_data->payload[4] << 8) | (node_data->payload[3] << 16);
|
|
uint32_t open_duration = node_data->payload[8] | (node_data->payload[7] << 8) | (node_data->payload[6] << 16);
|
|
uint8_t alarm = node_data->payload[9];
|
|
#ifdef USE_LORA_DEBUG
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("LOR: Node %d, DevEUI %08X%08X, Events %d, LastEvent %d min, DoorOpen %d, Battery %3_fV, Alarm %d"),
|
|
node_data->node +1, Lora->settings.end_node[node_data->node].DevEUIh, Lora->settings.end_node[node_data->node].DevEUIl,
|
|
events, open_duration,
|
|
bitRead(status, 7),
|
|
&battery_volt,
|
|
bitRead(alarm, 0));
|
|
#endif // USE_LORA_DEBUG
|
|
LoraWanPublishHeader(node_data->node);
|
|
ResponseAppend_P(PSTR(",\"Events\":%d,\"LastEvent\":%d,\"DoorOpen\":%d,\"Alarm\":%d,\"Battery\":%3_f}"),
|
|
events, open_duration, bitRead(status, 7), bitRead(alarm, 0), &battery_volt);
|
|
LoraWanPublishFooter(node_data->node);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Joined device without decoding
|
|
LoraWanPublishHeader(node_data->node);
|
|
ResponseAppend_P(PSTR(",\"DevEUIh\":\"%08X\",\"DevEUIl\":\"%08X\",\"FPort\":%d,\"Payload\":["),
|
|
Lora->settings.end_node[node_data->node].DevEUIh, Lora->settings.end_node[node_data->node].DevEUIl, node_data->FPort);
|
|
for (uint32_t i = 0; i < node_data->payload_len; i++) {
|
|
ResponseAppend_P(PSTR("%s%d"), (0==i)?"":",", node_data->payload[i]);
|
|
}
|
|
ResponseAppend_P(PSTR("]}"));
|
|
LoraWanPublishFooter(node_data->node);
|
|
}
|
|
|
|
#endif // USE_LORAWAN_BRIDGE
|
|
#endif // USE_SPI_LORA
|