mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-12 21:36:30 +00:00
LoRaWan Decode Files (#23412)
* Add LoraWanDecoders Documentation to follow in separate PR. See https://github.com/arendst/Tasmota/discussions/23394 * Create README.md Quick Guide * Update README.md Typo --------- Co-authored-by: UBWH <clark@ubwh.com.au>
This commit is contained in:
parent
ca9df09d6a
commit
08e8f0b64d
42
tasmota/berry/lorawan/decoders/LwDecode.be
Normal file
42
tasmota/berry/lorawan/decoders/LwDecode.be
Normal file
@ -0,0 +1,42 @@
|
||||
# Decoder files are modeled on the *.js files found here:
|
||||
# https://github.com/TheThingsNetwork/lorawan-devices/tree/master/vendor
|
||||
|
||||
var LwRegions = ["EU868", "US915", "IN865","AU915","KZ865","RU864","AS923", "AS923-1","AS923-2","AS923-3"]
|
||||
|
||||
import mqtt
|
||||
tasmota.cmd('SetOption100 off')
|
||||
tasmota.cmd('SetOption118 off')
|
||||
tasmota.cmd('SetOption119 off')
|
||||
tasmota.cmd('LoRaWanBridge on')
|
||||
var thisDevice = tasmota.cmd('Status',true)['Status']['Topic']
|
||||
var LwDecoders = {}
|
||||
var LwDeco
|
||||
|
||||
def LwDecode(topic, idx, data, databytes)
|
||||
import json
|
||||
|
||||
var LwData = json.load(data)
|
||||
if !LwData.contains('LwReceived') return true end # Processed
|
||||
var deviceData = LwData['LwReceived']
|
||||
var deviceName = deviceData.keys()()
|
||||
var Payload = deviceData[deviceName]['Payload']
|
||||
var FPort = deviceData[deviceName]['FPort']
|
||||
var decoder = deviceData[deviceName].find('Decoder')
|
||||
if !decoder return true end
|
||||
|
||||
if !LwDecoders.find(decoder)
|
||||
LwDeco = nil
|
||||
load(decoder) #sets LwDeco if found
|
||||
if LwDeco LwDecoders.insert(decoder, LwDeco) end
|
||||
end
|
||||
|
||||
if Payload.size() && LwDecoders.find(decoder)
|
||||
var decoded = LwDecoders[decoder].decodeUplink(FPort, Payload)
|
||||
var mqttData = {"LwDecoded":{deviceName:decoded}}
|
||||
mqtt.publish (topic, json.dump(mqttData))
|
||||
end
|
||||
|
||||
return true #processed
|
||||
end
|
||||
|
||||
mqtt.subscribe("tele/" + thisDevice + "/SENSOR",LwDecode)
|
41
tasmota/berry/lorawan/decoders/LwDecoderSample.be
Normal file
41
tasmota/berry/lorawan/decoders/LwDecoderSample.be
Normal file
@ -0,0 +1,41 @@
|
||||
# LoRaWAN Decoder file for an example DEVICE
|
||||
#
|
||||
# References
|
||||
# Manufacturer Reference: https://xxxxxx <== URL TO Manufacturer's documentation explaining how to decode raw data
|
||||
# TTN Device Repository: https://github.com/TheThingsNetwork/lorawan-devices/blob/master/vendor/xxxxxx/DEVICE
|
||||
|
||||
# import Berry modules, if needed.
|
||||
# the string module is not needed in this decoder; shown as an example.
|
||||
import string
|
||||
|
||||
# Declare a new Class
|
||||
# The Class name should follow this format: LwDecoXXXX where XXXX is the DEVICE
|
||||
class LwDecoDEVICE
|
||||
|
||||
# Define this function exactly as below.
|
||||
# Name: decodeUplink Must use this name, and arguments
|
||||
# Arguments: FPort The Fport number used by the End Device for this packet of data
|
||||
# Bytes The Raw Data Payload
|
||||
static def decodeUplink(FPort, Bytes)
|
||||
|
||||
# Create the data structure (a Berry 'map'), and populate with the VENDOR & DEVICE names
|
||||
var data = {"Device":"VENDOR DEVICE"}
|
||||
|
||||
# For each Fport used by the DEVICE:
|
||||
# write a decoder that continues to populate the data structure by parsing the Raw Data Payload
|
||||
|
||||
if 2 == FPort && 11 == Bytes.size() #Example: For this device, Data from Fport 2 should have 11 bytes
|
||||
data.insert("LABEL1", Bytes[0] | Bytes[1] <<8 ) #Example Numerical value = Bytes[1]*256 + Bytes[0]
|
||||
data.insert("LABEL2", "TEXT VALUE") #Example Text value
|
||||
|
||||
else
|
||||
# Ignore other Fports
|
||||
end #Fport
|
||||
|
||||
return data
|
||||
end #decodeUplink()
|
||||
end #class
|
||||
|
||||
# Set LwDeco variable to the new Class
|
||||
LwDeco = LwDecoDEVICE # LwDeco: Do not Change
|
||||
# Value: MUST match the class name defined above.
|
12
tasmota/berry/lorawan/decoders/README.md
Normal file
12
tasmota/berry/lorawan/decoders/README.md
Normal file
@ -0,0 +1,12 @@
|
||||
## How to use the LoRaWan Device Decoder feature ##
|
||||
1. Download to local computer, then upload to your Tasmota File System:
|
||||
- [LwDecode.be](https://github.com/arendst/Tasmota/tree/master/tasmota/berry/lorawan/decoders/LwDecode.be)
|
||||
- the _Device Decoder File(s)_ for your _End Device(s)_
|
||||
2. Add this line to `autoexec.be` in the Tasmota File System (create if necessary)
|
||||
`load("LwDecode.be")`
|
||||
3. Join the End Devices to the Tasmota LoRaWan Bridge.
|
||||
- e.g. `LoRaWanAppKey<x> yyyyyyyy` where `<x>` is the Tasmota LoRaWan node number.
|
||||
4. Inform Tasmota of the name of the _Decoder File_ for each end device with this Tasmota console command: `LoRaWanDecoder<x> <decoderfile>` where `<x>` is the Tasmota LoRaWan node number.
|
||||
e.g. `LoRaWanDecoder1 LHT52` associates node 1 with the `LHT52.be` decoder file
|
||||
|
||||
5. Restart Berry with this Tasmota console command: `BrRestart`
|
29
tasmota/berry/lorawan/decoders/vendors/dragino/LDS02.be
vendored
Normal file
29
tasmota/berry/lorawan/decoders/vendors/dragino/LDS02.be
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
# LoRaWAN Decoder file for Dragino LDS02
|
||||
#
|
||||
# References
|
||||
# LHT52 User Manual: https://wiki.dragino.com/xwiki/bin/view/Main/User%20Manual%20for%20LoRaWAN%20End%20Nodes/LDS02%20-%20LoRaWAN%20Door%20Sensor%20User%20Manual/
|
||||
# TTN Device Repository: https://github.com/TheThingsNetwork/lorawan-devices/blob/master/vendor/dragino/lds02.js
|
||||
|
||||
class LwDecoLDS02
|
||||
|
||||
static def decodeUplink(FPort, Bytes)
|
||||
var data = {"Device":"Dragino LDS02"}
|
||||
|
||||
## SENSOR DATA ##
|
||||
|
||||
if 10 == FPort && Bytes.size() == 10
|
||||
data.insert("DoorOpen", ( Bytes[0] & 0x80 ) ? true : false )
|
||||
data.insert("Battery_mV", ( Bytes[1] | (Bytes[0] << 8) & 0x3FFF ))
|
||||
data.insert("DoorOpenEvents", Bytes[5] | (Bytes[4] << 8) | (Bytes[3] << 16 ))
|
||||
data.insert("DoorOpenLastDuration_mins", Bytes[8] | (Bytes[7] << 8) | (Bytes[6] << 16))
|
||||
data.insert("Alarm", (Bytes[9] & 0x01 ) ? true : false)
|
||||
|
||||
else
|
||||
# Ignore other Fports
|
||||
end #Fport
|
||||
|
||||
return data
|
||||
end #decodeUplink()
|
||||
end #class
|
||||
|
||||
LwDeco = LwDecoLDS02
|
53
tasmota/berry/lorawan/decoders/vendors/dragino/LHT52.be
vendored
Normal file
53
tasmota/berry/lorawan/decoders/vendors/dragino/LHT52.be
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
# LoRaWAN Decoder file for Dragino LHT52
|
||||
#
|
||||
# References
|
||||
# User Manual: https://wiki.dragino.com/xwiki/bin/view/Main/User%20Manual%20for%20LoRaWAN%20End%20Nodes/LHT52%20-%20LoRaWAN%20Temperature%20%26%20Humidity%20Sensor%20User%20Manual/
|
||||
# TTN Device Repository: https://github.com/TheThingsNetwork/lorawan-devices/blob/master/vendor/dragino/lht52.js
|
||||
|
||||
import string
|
||||
|
||||
class LwDecoLHT52
|
||||
|
||||
static def decodeUplink(FPort, Bytes)
|
||||
var data = {"Device":"Dragino LHT52"}
|
||||
|
||||
## SENSOR DATA ##
|
||||
if 2 == FPort && Bytes.size() == 11
|
||||
var TempC
|
||||
|
||||
TempC = Bytes[0]<<8 | Bytes[1]
|
||||
if Bytes[0]>0x7F TempC -= 0x10000 end
|
||||
TempC /= 100.0
|
||||
data.insert("TempC_Internal",TempC)
|
||||
|
||||
TempC = Bytes[4]<<8 | Bytes[5]
|
||||
if 0x7FFF == TempC
|
||||
data.insert("Ext_SensorConnected", false)
|
||||
else
|
||||
data.insert("Ext_SensorConnected", true)
|
||||
if Bytes[4]>0x7F TempC -= 0x10000 end
|
||||
TempC /= 100.0
|
||||
data.insert("TempC_External",TempC)
|
||||
end
|
||||
|
||||
data.insert("Hum_Internal", ((Bytes[2]<<8 ) | Bytes[3])/10.0)
|
||||
data.insert("Ext_SensorType", Bytes[6])
|
||||
data.insert("Systimestamp",(Bytes[7] << 24) | (Bytes[8] << 16) | (Bytes[9] << 8) | Bytes[10])
|
||||
|
||||
## STATUS DATA ##
|
||||
elif 5 == FPort && Bytes.size() == 7
|
||||
data.insert("Sensor_Model",Bytes[0])
|
||||
data.insert("Firmware_Version", f'v{Bytes[1]:%u}.{Bytes[2]>>4:%u}.{Bytes[2]&0xF:%u}')
|
||||
data.insert("Freq_Band",LwRegions[Bytes[3]-1])
|
||||
data.insert("Sub_Band",Bytes[4])
|
||||
data.insert("Bat_mV",(Bytes[5] << 8) | Bytes[6])
|
||||
|
||||
else
|
||||
# Ignore other Fports
|
||||
end #Fport
|
||||
|
||||
return data
|
||||
end #decodeUplink()
|
||||
end #class
|
||||
|
||||
LwDeco = LwDecoLHT52
|
99
tasmota/berry/lorawan/decoders/vendors/dragino/LHT65.be
vendored
Normal file
99
tasmota/berry/lorawan/decoders/vendors/dragino/LHT65.be
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
# LoRaWAN Decoder file for Dragino LHT65
|
||||
#
|
||||
# References
|
||||
# User Manual: https://www.dragino.com/downloads/downloads/LHT65/UserManual/LHT65_Temperature_Humidity_Sensor_UserManual_v1.8.5.pdf
|
||||
# TTN Device Repository: https://github.com/TheThingsNetwork/lorawan-devices/blob/master/vendor/dragino/lht65.js
|
||||
|
||||
import string
|
||||
var LHT65_BatteryStatus = ["Very low <= 2.5V","Low <=2.55V","OK","Good >= 2.65V"]
|
||||
|
||||
class LwDecoLHT65
|
||||
static def decodeUplink(FPort, Bytes)
|
||||
var data = {"Device":"Dragino LHT65"}
|
||||
data.insert("poll_message_status",(Bytes[6] & 0x40) >> 6)
|
||||
var Ext = Bytes[6] & 0x0F #External sensor type
|
||||
var NoConnect = (Bytes[6] & 0x80) >> 7
|
||||
|
||||
## SENSOR DATA ##
|
||||
if 2 == FPort && Bytes.size() == 11
|
||||
var TempC
|
||||
if Ext == 9 #Sensor E3, Temperature Sensor, Datalog Mod
|
||||
TempC = ((Bytes[0] << 8) | Bytes[1])
|
||||
if 0x7FFF == TempC
|
||||
data.insert("Ext_SensorConnected", false)
|
||||
else
|
||||
data.insert("Ext_SensorConnected", true)
|
||||
if Bytes[0]>0x7F TempC -= 0x10000 end
|
||||
data.insert("TempC_External", TempC / 100.0)
|
||||
end
|
||||
data.insert("Bat_status", LHT65_BatteryStatus[Bytes[4] >> 6])
|
||||
else
|
||||
data.insert("BatV",(((Bytes[0] << 8) | Bytes[1]) & 0x3fff) / 1000.0)
|
||||
data.insert("Bat_status", LHT65_BatteryStatus[Bytes[0] >> 6])
|
||||
end
|
||||
|
||||
if Ext != 0x0F
|
||||
TempC = ((Bytes[2] << 8) | Bytes[3])
|
||||
if Bytes[2]>0x7F TempC -= 0x10000 end
|
||||
data.insert("TempC_Internal", ( TempC / 100.0))
|
||||
data.insert("Hum_Internal" , (((Bytes[4] << 8) | Bytes[5]) / 10.0))
|
||||
end
|
||||
|
||||
if NoConnect
|
||||
data.insert('No_connect','No connection to external sensor')
|
||||
end
|
||||
|
||||
if 0==Ext
|
||||
data.insert("Ext_sensor", 'No external sensor')
|
||||
elif 1==Ext
|
||||
data.insert("Ext_sensor",'Temperature Sensor')
|
||||
TempC = ((Bytes[7] << 8) | Bytes[8])
|
||||
if 0x7FFF == TempC
|
||||
data.insert("Ext_SensorConnected", false)
|
||||
else
|
||||
data.insert("Ext_SensorConnected", true)
|
||||
if Bytes[7]>0x7F TempC -= 0x10000 end
|
||||
data.insert("TempC_External", TempC / 100.0)
|
||||
end
|
||||
elif 4==Ext
|
||||
data.insert("Work_mode", 'Interrupt Sensor send')
|
||||
data.insert("Exti_pin_level", Bytes[7] ? 'High' : 'Low')
|
||||
data.insert("Exti_status", Bytes[8] ? 'True' : 'False')
|
||||
elif 5==Ext
|
||||
data.insert("Work_mode", 'Illumination Sensor')
|
||||
data.insert("ILL_lx", (Bytes[7] << 8) | Bytes[8])
|
||||
elif 6==Ext
|
||||
data.insert("Work_mode", 'ADC Sensor')
|
||||
data.insert("ADC_V", ((Bytes[7] << 8) | Bytes[8]) / 1000.0)
|
||||
elif 7==Ext
|
||||
data.insert("Work_mode ", 'Interrupt Sensor count')
|
||||
data.insert("Exit_count", (Bytes[7] << 8) | Bytes[8])
|
||||
elif 8==Ext
|
||||
data.insert("Work_mode", 'Interrupt Sensor count')
|
||||
data.insert("Exit_count", (Bytes[7] << 24) | (Bytes[8] << 16) | (Bytes[9] << 8) | Bytes[10])
|
||||
elif 9==Ext
|
||||
data.insert("Work_mode", 'DS18B20 & timestamp')
|
||||
data.insert("Systimestamp", (Bytes[7] << 24) | (Bytes[8] << 16) | (Bytes[9] << 8) | Bytes[10])
|
||||
elif 15==Ext
|
||||
data.insert("Work_mode",'DS18B20ID')
|
||||
data.insert("ID",f"{Bytes[2]:%02X}" + f"{Bytes[3]:%02X}" + f"{Bytes[4]:%02X}" + f"{Bytes[5]:%02X}" + f"{Bytes[6]:%02X}" + f"{Bytes[8]:%02X}" + f"{Bytes[9]:%02X}" + f"{Bytes[10]:%02X}" )
|
||||
else
|
||||
data.insert("Ext_sensor", 'Unknown')
|
||||
end
|
||||
|
||||
elif 5 == FPort && Bytes.size() == 7
|
||||
data.insert("Sensor_Model",Bytes[0])
|
||||
data.insert("Firmware_Version", f'v{Bytes[1]:%u}.{Bytes[2]>>4:%u}.{Bytes[2]&0xF:%u}')
|
||||
data.insert("Freq_Band",LwRegions[Bytes[3]-1])
|
||||
data.insert("Sub_Band",Bytes[4])
|
||||
data.insert("Bat_mV",(Bytes[5] << 8) | Bytes[6])
|
||||
|
||||
else
|
||||
# Ignore other Fports
|
||||
end #Fport
|
||||
|
||||
return data
|
||||
end # decodeUplink()
|
||||
end # class
|
||||
|
||||
LwDeco = LwDecoLHT65
|
Loading…
x
Reference in New Issue
Block a user