Add GUI support to LoRaWan decoders

This commit is contained in:
Theo Arends 2025-05-22 17:32:16 +02:00
parent 92f14dd0c0
commit 4f4bf7c61b
6 changed files with 410 additions and 200 deletions

View File

@ -2,41 +2,77 @@
# https://github.com/TheThingsNetwork/lorawan-devices/tree/master/vendor
var LwRegions = ["EU868", "US915", "IN865","AU915","KZ865","RU864","AS923", "AS923-1","AS923-2","AS923-3"]
var LwDeco
import mqtt
class lwdecode_cls
var thisDevice
var LwDecoders
def init()
self.thisDevice = tasmota.cmd('Status',true)['Status']['Topic']
self.LwDecoders = {}
if global.lwdecode_driver
global.lwdecode_driver.stop() # Let previous instance bail out cleanly
end
tasmota.add_driver(global.lwdecode_driver := self)
tasmota.add_rule("LwReceived", /value, trigger, payload -> self.LwDecode(payload))
end
def LwDecode(data)
import json
var deviceData = data['LwReceived']
var deviceName = deviceData.keys()()
var Node = deviceData[deviceName]['Node']
var Payload = deviceData[deviceName]['Payload']
var FPort = deviceData[deviceName]['FPort']
var decoder = deviceData[deviceName].find('Decoder')
if !decoder
return true
end
if !self.LwDecoders.find(decoder)
LwDeco = nil
load(decoder) #sets LwDeco if found
if LwDeco
self.LwDecoders.insert(decoder, LwDeco)
end
end
if Payload.size() && self.LwDecoders.find(decoder)
var topic = "tele/" + self.thisDevice + "/SENSOR"
var decoded = self.LwDecoders[decoder].decodeUplink(Node, FPort, Payload)
var mqttData = {"LwDecoded":{deviceName:decoded}}
mqtt.publish(topic, json.dump(mqttData))
end
return true #processed
end
#------------------------------------------------------------
Display sensor value in the web UI and react to button
Called every WebRefresh time
------------------------------------------------------------#
def web_sensor()
import string
var msg = ""
for decoder: self.LwDecoders
msg = msg + decoder.add_web_sensor()
end
if msg
tasmota.web_send_decimal(msg)
end
end
end
lwdecode = lwdecode_cls()
tasmota.cmd('SetOption100 off') # Keep LwReceived in JSON message
tasmota.cmd('SetOption118 off') # Keep SENSOR as subtopic name
tasmota.cmd('SetOption119 off') # Keep device address in JSON message
tasmota.cmd('SetOption147 on') # Hide LwReceived MQTT message but keep rule processing
tasmota.cmd('LoRaWanBridge on')
var thisDevice = tasmota.cmd('Status',true)['Status']['Topic']
var LwDecoders = {}
var LwDeco
def LwDecode(data)
import json
var deviceData = data['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 topic = "tele/" + thisDevice + "/SENSOR"
var decoded = LwDecoders[decoder].decodeUplink(FPort, Payload)
var mqttData = {"LwDecoded":{deviceName:decoded}}
mqtt.publish (topic, json.dump(mqttData))
end
return true #processed
end
tasmota.add_rule("LwReceived", /value, trigger, payload -> LwDecode(payload))

View File

@ -8,6 +8,9 @@
# the string module is not needed in this decoder; shown as an example.
import string
# define a global map for storage of GUI parameters
global.DEVICENodes = {}
# Declare a new Class
# The Class name should follow this format: LwDecoXXXX where XXXX is the DEVICE
class LwDecoDEVICE
@ -16,24 +19,49 @@ class LwDecoDEVICE
# 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)
static def decodeUplink(Node, FPort, Bytes)
# Create the data structure (a Berry 'map'), and populate with the VENDOR & DEVICE names
var data = {"Device":"VENDOR DEVICE"}
var data = {"Device":"VENDOR DEVICE"}
data.insert("Node", Node)
var valid_values = false
var gui_value
# 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
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]
gui_value = Bytes[0] | Bytes[1] <<8
data.insert("LABEL2", "TEXT VALUE") #Example Text value
else
# Ignore other Fports
end #Fport
return data
end #decodeUplink()
var valid_values = true
else
# Ignore other Fports
end #Fport
if valid_values
if global.DEVICENodes.find(Node)
global.DEVICENodes.remove(Node)
end
global.DEVICENodes.insert(Node, [Node, gui_value])
end
return data
end #decodeUplink()
static def add_web_sensor()
var msg = ""
for sensor: global.DEVICENodes
msg += string.format("{s}DEVICE_%i Gui value{m}%i{e}",
sensor[0], sensor[1])
end
return msg
end #add_web_sensor()
end #class
# Set LwDeco variable to the new Class

View File

@ -4,26 +4,50 @@
# 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
import string
global.lds02Nodes = {}
class LwDecoLDS02
static def decodeUplink(Node, FPort, Bytes)
var data = {"Device":"Dragino LDS02"}
data.insert("Node", Node)
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)
var valid_values = false
var door_open
## SENSOR DATA ##
if 10 == FPort && Bytes.size() == 10
door_open = ( Bytes[0] & 0x80 ) ? 1 : 0
data.insert("DoorOpen", ( door_open ) ? 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)
valid_values = true
else
# Ignore other Fports
end #Fport
else
# Ignore other Fports
end #Fport
return data
end #decodeUplink()
if valid_values
if global.lds02Nodes.find(Node)
global.lds02Nodes.remove(Node)
end
global.lds02Nodes.insert(Node, [Node, door_open])
end
return data
end #decodeUplink()
static def add_web_sensor()
var msg = ""
for sensor: global.lds02Nodes
msg += string.format("{s}LDS02_%i Door{m}%s{e}",
sensor[0], (sensor[1]) ? "Open" : "Closed")
end
return msg
end
end #class
LwDeco = LwDecoLDS02

View File

@ -6,50 +6,86 @@
import string
global.lht52Nodes = {}
class LwDecoLHT52
static def decodeUplink(Node, FPort, Bytes)
var data = {"Device":"Dragino LHT52"}
data.insert("Node", Node)
static def decodeUplink(FPort, Bytes)
var data = {"Device":"Dragino LHT52"}
## SENSOR DATA ##
if 2 == FPort && Bytes.size() == 11
var TempC
var valid_values = false
var temp_int
var humidity
var temp_ext = 1000
## 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[0] << 8 | Bytes[1]
if Bytes[0] > 0x7F
TempC -= 0x10000
end
TempC /= 100.0
data.insert("TempC_Internal", TempC)
temp_int = 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
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)
temp_ext = 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])
var epoch = (Bytes[7] << 24) | (Bytes[8] << 16) | (Bytes[9] << 8) | Bytes[10]
data.insert("Systimestamp",tasmota.time_str(epoch))
data.insert("Hum_Internal", ((Bytes[2] << 8) | Bytes[3]) / 10.0)
humidity = ((Bytes[2] << 8) | Bytes[3]) / 10.0
data.insert("Ext_SensorType", Bytes[6])
var epoch = (Bytes[7] << 24) | (Bytes[8] << 16) | (Bytes[9] << 8) | Bytes[10]
data.insert("Systimestamp",tasmota.time_str(epoch))
valid_values = true
## 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])
## 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
else
# Ignore other Fports
end #Fport
return data
end #decodeUplink()
if valid_values
if global.lht52Nodes.find(Node)
global.lht52Nodes.remove(Node)
end
global.lht52Nodes.insert(Node, [Node, temp_int, humidity, temp_ext])
end
return data
end #decodeUplink()
static def add_web_sensor()
var msg = ""
for sensor: global.lht52Nodes
msg += string.format("{s}LHT52_%i Temperature{m}%.1f °C{e}"..
"{s}LHT52_%i Humidity{m}%.1f %%{e}",
sensor[0], sensor[1],
sensor[0], sensor[2])
if sensor[3] < 1000
msg += string.format("{s}LHT52_%i Temperature ext.{m}%.1f °C{e}",
sensor[0], sensor[3])
end
end
return msg
end
end #class
LwDeco = LwDecoLHT52

View File

@ -6,96 +6,147 @@
import string
var LHT65_BatteryStatus = ["Very low <= 2.5V","Low <=2.55V","OK","Good >= 2.65V"]
global.lht65Nodes = {}
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
static def decodeUplink(FPort, Bytes)
var data = {"Device":"Dragino LHT65"}
data.insert("Node", Node)
data.insert("poll_message_status",(Bytes[6] & 0x40) >> 6)
var Ext = Bytes[6] & 0x0F #External sensor type
var NoConnect = (Bytes[6] & 0x80) >> 7
var valid_values = false
var temp_int = 1000
var humidity
var temp_ext = 1000
if global.lht65Nodes.find(Node)
temp_int = global.lht65Nodes.item(Node)[1]
humidity = global.lht65Nodes.item(Node)[2]
temp_ext = global.lht65Nodes.item(Node)[3]
end
## 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
temp_ext = TempC / 100.0
data.insert("TempC_External", temp_ext)
valid_values = true
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
temp_int = TempC / 100.0
data.insert("TempC_Internal", temp_int)
humidity = (((Bytes[4] << 8) | Bytes[5]) / 10.0)
data.insert("Hum_Internal" , humidity)
valid_values = true
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
temp_ext = TempC / 100.0
data.insert("TempC_External", temp_ext)
valid_values = true
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')
var epoch = (Bytes[7] << 24) | (Bytes[8] << 16) | (Bytes[9] << 8) | Bytes[10]
data.insert("Systimestamp",tasmota.time_str(epoch))
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])
## 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])
var epoch = (Bytes[7] << 24) | (Bytes[8] << 16) | (Bytes[9] << 8) | Bytes[10]
data.insert("Systimestamp",tasmota.time_str(epoch))
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()
# Ignore other Fports
end #Fport
if valid_values
if global.lht65Nodes.find(Node)
global.lht65Nodes.remove(Node)
end
global.lht65Nodes.insert(Node, [Node, temp_int, humidity, temp_ext])
end
return data
end # decodeUplink()
static def add_web_sensor()
var msg = ""
for sensor: global.lht65Nodes
if sensor[1] < 1000
msg += string.format("{s}LHT65_%i Temperature{m}%.1f °C{e}"..
"{s}LHT65_%i Humidity{m}%.1f %%{e}",
sensor[0], sensor[1],
sensor[0], sensor[2])
end
if sensor[3] < 1000
msg += string.format("{s}LHT65_%i Temperature ext.{m}%.1f °C{e}",
sensor[0], sensor[3])
end
end
return msg
end
end # class
LwDeco = LwDecoLHT65

View File

@ -4,30 +4,65 @@
# DW10 Product information: https://www.browan.com/products-detail/OpenClose-Sensor-EBL-LoRaWAN/
# Browan JS Decoder (TTN): https://www.browan.com/member/login/?refererUrl=https%3A%2F%2Fwww.browan.com%2Fproducts-detail%2FOpenClose-Sensor-EBL-LoRaWAN%2F
import string
global.dw10Nodes = {}
class LwDecoDW10
static def decodeUplink(Node, FPort, Bytes)
var data = {"Device":"MerryIoT DW10"}
data.insert("Node", Node)
static def decodeUplink(FPort, Bytes)
var data = {"Device":"MerryIoT DW10"}
## SENSOR DATA ##
if 120 == FPort && Bytes.size() == 9
data.insert("DoorOpen", ( Bytes[0] & 0x01 ) ? true : false )
data.insert("ButtonPress", ( Bytes[0] & 0x02 ) ? true : false )
data.insert("TamperDetect", ( Bytes[0] & 0x04 ) ? true : false )
data.insert("TiltDetect", ( Bytes[0] & 0x08 ) ? true : false )
data.insert("Battery_mV", ( 21 + Bytes[1] ) * 100 )
data.insert("Temperature_C", Bytes[2])
data.insert("Humidity", Bytes[3])
data.insert("DoorOpenLastDuration_mins", Bytes[4] | (Bytes[5] << 8))
data.insert("DoorOpenEvents", Bytes[6] | (Bytes[7] << 8) | (Bytes[8] << 16 ))
var valid_values = false
var door_open
var button_pressed
var temperature
var humidity
## SENSOR DATA ##
if 120 == FPort && Bytes.size() == 9
door_open = ( Bytes[0] & 0x01 ) ? 1 : 0
data.insert("DoorOpen", ( door_open ) ? true : false )
button_pressed = ( Bytes[0] & 0x02 ) ? 1 : 0
data.insert("ButtonPress", ( button_pressed ) ? true : false )
data.insert("TamperDetect", ( Bytes[0] & 0x04 ) ? true : false )
data.insert("TiltDetect", ( Bytes[0] & 0x08 ) ? true : false )
data.insert("Battery_mV", ( 21 + Bytes[1] ) * 100 )
data.insert("Temperature_C", Bytes[2])
temperature = Bytes[2]
data.insert("Humidity", Bytes[3])
humidity = Bytes[3]
data.insert("DoorOpenLastDuration_mins", Bytes[4] | (Bytes[5] << 8))
data.insert("DoorOpenEvents", Bytes[6] | (Bytes[7] << 8) | (Bytes[8] << 16 ))
valid_values = true
else
# Ignore other Fports
end #Fport
else
# Ignore other Fports
end #Fport
return data
end #decodeUplink()
if valid_values
if global.dw10Nodes.find(Node)
global.dw10Nodes.remove(Node)
end
global.dw10Nodes.insert(Node, [Node, door_open, button_pressed, temperature, humidity])
end
return data
end #decodeUplink()
static def add_web_sensor()
var msg = ""
for sensor: global.dw10Nodes
msg += string.format("{s}DW10_%i Door{m}%s{e}"..
"{s}DW10_%i Button{m}%s{e}"..
"{s}DW10_%i Temperature{m}%.1f °C{e}"..
"{s}DW10_%i Humidity{m}%.1f %%{e}",
sensor[0], (sensor[1]) ? "Open" : "Closed",
sensor[0], (sensor[2]) ? "Pressed" : "Released",
sensor[0], sensor[3],
sensor[0], sensor[4])
end
return msg
end
end #class
LwDeco = LwDecoDW10