mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-27 20:56:35 +00:00
Change LoRaWan GUI sensor representation
This commit is contained in:
parent
2914554f5e
commit
2eb56b77a2
@ -5,6 +5,7 @@ var LwRegions = ["EU868", "US915", "IN865","AU915","KZ865","RU864","AS923", "AS9
|
|||||||
var LwDeco
|
var LwDeco
|
||||||
|
|
||||||
import mqtt
|
import mqtt
|
||||||
|
import string
|
||||||
|
|
||||||
class lwdecode_cls
|
class lwdecode_cls
|
||||||
var thisDevice
|
var thisDevice
|
||||||
@ -27,6 +28,7 @@ class lwdecode_cls
|
|||||||
var deviceData = data['LwReceived']
|
var deviceData = data['LwReceived']
|
||||||
var deviceName = deviceData.keys()()
|
var deviceName = deviceData.keys()()
|
||||||
var Node = deviceData[deviceName]['Node']
|
var Node = deviceData[deviceName]['Node']
|
||||||
|
var RSSI = deviceData[deviceName]['RSSI']
|
||||||
var Payload = deviceData[deviceName]['Payload']
|
var Payload = deviceData[deviceName]['Payload']
|
||||||
var FPort = deviceData[deviceName]['FPort']
|
var FPort = deviceData[deviceName]['FPort']
|
||||||
var decoder = deviceData[deviceName].find('Decoder')
|
var decoder = deviceData[deviceName].find('Decoder')
|
||||||
@ -44,7 +46,7 @@ class lwdecode_cls
|
|||||||
|
|
||||||
if Payload.size() && self.LwDecoders.find(decoder)
|
if Payload.size() && self.LwDecoders.find(decoder)
|
||||||
var topic = "tele/" + self.thisDevice + "/SENSOR"
|
var topic = "tele/" + self.thisDevice + "/SENSOR"
|
||||||
var decoded = self.LwDecoders[decoder].decodeUplink(Node, FPort, Payload)
|
var decoded = self.LwDecoders[decoder].decodeUplink(Node, RSSI, FPort, Payload)
|
||||||
var mqttData = {"LwDecoded":{deviceName:decoded}}
|
var mqttData = {"LwDecoded":{deviceName:decoded}}
|
||||||
mqtt.publish(topic, json.dump(mqttData))
|
mqtt.publish(topic, json.dump(mqttData))
|
||||||
end
|
end
|
||||||
@ -52,6 +54,61 @@ class lwdecode_cls
|
|||||||
return true #processed
|
return true #processed
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def dhm(last_time)
|
||||||
|
var since = tasmota.rtc('local') - last_time
|
||||||
|
var unit = "d"
|
||||||
|
if since > (24 * 3600)
|
||||||
|
since /= (24 * 3600)
|
||||||
|
if since > 99
|
||||||
|
since = 99
|
||||||
|
end
|
||||||
|
elif since > 3600
|
||||||
|
since /= 3600
|
||||||
|
unit = "h"
|
||||||
|
else
|
||||||
|
since /= 60
|
||||||
|
unit = "m"
|
||||||
|
end
|
||||||
|
return string.format("%02d%s", since, unit)
|
||||||
|
end
|
||||||
|
|
||||||
|
def header(name, name_tooltip, battery, battery_last_seen, rssi, last_seen)
|
||||||
|
var color_text = f'{tasmota.webcolor(0 #-COL_TEXT-#)}' # '#eaeaea'
|
||||||
|
var msg = string.format("<tr class='ltd htr'>") # ==== Start first table row
|
||||||
|
msg += string.format("<td><b title='%s'>%s</b></td>", name_tooltip, name)
|
||||||
|
if (battery < 1000)
|
||||||
|
# Battery low <= 2.5V (0%), high >= 3.1V (100%)
|
||||||
|
var batt_percent = (battery * 1000) - 2500
|
||||||
|
batt_percent /= 6 # 3.1V - 2.5V = 0.6V = 100%
|
||||||
|
if batt_percent < 0
|
||||||
|
batt_percent = 0
|
||||||
|
end
|
||||||
|
if batt_percent > 100
|
||||||
|
batt_percent = 100
|
||||||
|
end
|
||||||
|
batt_percent /= 7.14 # 1..14px showing battery load
|
||||||
|
msg += string.format("<td><i class=\"bt\" title=\"%.3fV (%s)\" style=\"--bl:%dpx;color:%s\"></i></td>",
|
||||||
|
battery, self.dhm(battery_last_seen), batt_percent, color_text)
|
||||||
|
else
|
||||||
|
msg += "<td> </td>"
|
||||||
|
end
|
||||||
|
if rssi < 1000
|
||||||
|
var num_bars = 4 - ((rssi * -1) / 33)
|
||||||
|
msg += string.format("<td><div title='RSSI %i' class='si'>",rssi)
|
||||||
|
for j:0..3
|
||||||
|
msg += string.format("<i class='b%d%s'></i>",
|
||||||
|
j, (num_bars < j) ? " o30" : "") # Bars
|
||||||
|
end
|
||||||
|
msg += string.format("</div></td>") # Close RSSI
|
||||||
|
else
|
||||||
|
msg += "<td> </td>"
|
||||||
|
end
|
||||||
|
msg += string.format("<td style='color:%s'>🕗%s</td>", # Clock
|
||||||
|
color_text, self.dhm(last_seen))
|
||||||
|
msg += "</tr>" # ==== End first table row
|
||||||
|
return msg
|
||||||
|
end #sensor()
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
Display sensor value in the web UI and react to button
|
Display sensor value in the web UI and react to button
|
||||||
Called every WebRefresh time
|
Called every WebRefresh time
|
||||||
@ -61,16 +118,35 @@ class lwdecode_cls
|
|||||||
|
|
||||||
var msg = ""
|
var msg = ""
|
||||||
for decoder: self.LwDecoders
|
for decoder: self.LwDecoders
|
||||||
msg = msg + decoder.add_web_sensor()
|
msg += decoder.add_web_sensor()
|
||||||
end
|
end
|
||||||
if msg
|
if msg
|
||||||
tasmota.web_send_decimal(msg)
|
var color_text = f'{tasmota.webcolor(0 #-COL_TEXT-#)}' # '#eaeaea'
|
||||||
|
var full_msg = string.format("</table>".. # Terminate current two column table and open new table
|
||||||
|
"<style>"..
|
||||||
|
# Table CSS
|
||||||
|
".ltd td:not(:first-child){width:20px;font-size:70%%}"..
|
||||||
|
".ltd td:last-child{width:45px}"..
|
||||||
|
".ltd .bt{margin-right:10px;}".. # Margin right should be half of the not-first width
|
||||||
|
".htr{line-height:20px}"..
|
||||||
|
# Signal Strength Indicator
|
||||||
|
".si{display:inline-flex;align-items:flex-end;height:15px;padding:0}"..
|
||||||
|
".si i{width:3px;margin-right:1px;border-radius:3px;background-color:%s}".. # WebColor(COL_TEXT)
|
||||||
|
".si .b0{height:25%%}.si .b1{height:50%%}.si .b2{height:75%%}.si .b3{height:100%%}.o30{opacity:.3}"..
|
||||||
|
"</style>"..
|
||||||
|
"{t}", # Open new table
|
||||||
|
color_text)
|
||||||
|
full_msg += msg
|
||||||
|
full_msg += "</table>{t}" # Close table and open new table
|
||||||
|
|
||||||
|
tasmota.web_send_decimal(full_msg)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
lwdecode = lwdecode_cls()
|
lwdecode = lwdecode_cls()
|
||||||
|
|
||||||
|
tasmota.cmd('LoraOption3 off') # Disable embedded decoding
|
||||||
tasmota.cmd('SetOption100 off') # Keep LwReceived in JSON message
|
tasmota.cmd('SetOption100 off') # Keep LwReceived in JSON message
|
||||||
tasmota.cmd('SetOption118 off') # Keep SENSOR as subtopic name
|
tasmota.cmd('SetOption118 off') # Keep SENSOR as subtopic name
|
||||||
tasmota.cmd('SetOption119 off') # Keep device address in JSON message
|
tasmota.cmd('SetOption119 off') # Keep device address in JSON message
|
||||||
|
@ -9,22 +9,28 @@ import string
|
|||||||
global.lds02Nodes = {}
|
global.lds02Nodes = {}
|
||||||
|
|
||||||
class LwDecoLDS02
|
class LwDecoLDS02
|
||||||
static def decodeUplink(Node, FPort, Bytes)
|
static def decodeUplink(Node, RSSI, FPort, Bytes)
|
||||||
var data = {"Device":"Dragino LDS02"}
|
var data = {"Device":"Dragino LDS02"}
|
||||||
data.insert("Node", Node)
|
data.insert("Node", Node)
|
||||||
|
|
||||||
var valid_values = false
|
var valid_values = false
|
||||||
|
var last_seen
|
||||||
|
var battery_last_seen
|
||||||
|
var battery
|
||||||
|
var rssi = RSSI
|
||||||
var door_open
|
var door_open
|
||||||
## SENSOR DATA ##
|
## SENSOR DATA ##
|
||||||
if 10 == FPort && Bytes.size() == 10
|
if 10 == FPort && Bytes.size() == 10
|
||||||
|
last_seen = tasmota.rtc('local')
|
||||||
door_open = ( Bytes[0] & 0x80 ) ? 1 : 0
|
door_open = ( Bytes[0] & 0x80 ) ? 1 : 0
|
||||||
data.insert("DoorOpen", ( door_open ) ? true : false)
|
data.insert("DoorOpen", ( door_open ) ? true : false)
|
||||||
data.insert("Battery_mV", ( Bytes[1] | (Bytes[0] << 8) & 0x3FFF ))
|
data.insert("BattV", ( Bytes[1] | (Bytes[0] << 8) & 0x3FFF ) / 1000.0)
|
||||||
|
battery_last_seen = tasmota.rtc('local')
|
||||||
|
battery = ( Bytes[1] | (Bytes[0] << 8) & 0x3FFF ) / 1000.0
|
||||||
data.insert("DoorOpenEvents", Bytes[5] | (Bytes[4] << 8) | (Bytes[3] << 16 ))
|
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("DoorOpenLastDuration_mins", Bytes[8] | (Bytes[7] << 8) | (Bytes[6] << 16))
|
||||||
data.insert("Alarm", (Bytes[9] & 0x01 ) ? true : false)
|
data.insert("Alarm", (Bytes[9] & 0x01 ) ? true : false)
|
||||||
valid_values = true
|
valid_values = true
|
||||||
|
|
||||||
else
|
else
|
||||||
# Ignore other Fports
|
# Ignore other Fports
|
||||||
end #Fport
|
end #Fport
|
||||||
@ -33,7 +39,7 @@ class LwDecoLDS02
|
|||||||
if global.lds02Nodes.find(Node)
|
if global.lds02Nodes.find(Node)
|
||||||
global.lds02Nodes.remove(Node)
|
global.lds02Nodes.remove(Node)
|
||||||
end
|
end
|
||||||
global.lds02Nodes.insert(Node, [Node, door_open])
|
global.lds02Nodes.insert(Node, [Node, last_seen, battery_last_seen, battery, RSSI, door_open])
|
||||||
end
|
end
|
||||||
|
|
||||||
return data
|
return data
|
||||||
@ -42,12 +48,24 @@ class LwDecoLDS02
|
|||||||
static def add_web_sensor()
|
static def add_web_sensor()
|
||||||
var msg = ""
|
var msg = ""
|
||||||
for sensor: global.lds02Nodes
|
for sensor: global.lds02Nodes
|
||||||
msg += string.format("{s}LDS02_%i Door{m}%s{e}",
|
# Sensor[0] [1] [2] [3] [4] [5]
|
||||||
sensor[0], (sensor[1]) ? "Open" : "Closed")
|
# [Node, last_seen, battery_last_seen, battery, RSSI, door_open]
|
||||||
end
|
|
||||||
|
|
||||||
return msg
|
var name = string.format("LDS02-%i", sensor[0])
|
||||||
|
var name_tooltip = "Dragino LDS02"
|
||||||
|
var battery = sensor[3]
|
||||||
|
var battery_last_seen = sensor[2]
|
||||||
|
var rssi = sensor[4]
|
||||||
|
var last_seen = sensor[1]
|
||||||
|
msg += lwdecode.header(name, name_tooltip, battery, battery_last_seen, rssi, last_seen)
|
||||||
|
|
||||||
|
# Sensors
|
||||||
|
msg += "<tr class='htr'><td colspan='4'>┆" # |
|
||||||
|
msg += string.format(" %s", (sensor[5]) ? "🔓" : "🔒") # Open or Closed lock - Door
|
||||||
|
msg += "{e}" # = </td></tr>
|
||||||
end
|
end
|
||||||
|
return msg
|
||||||
|
end #add_web_sensor()
|
||||||
end #class
|
end #class
|
||||||
|
|
||||||
LwDeco = LwDecoLDS02
|
LwDeco = LwDecoLDS02
|
||||||
|
@ -9,18 +9,32 @@ import string
|
|||||||
global.lht52Nodes = {}
|
global.lht52Nodes = {}
|
||||||
|
|
||||||
class LwDecoLHT52
|
class LwDecoLHT52
|
||||||
static def decodeUplink(Node, FPort, Bytes)
|
static def decodeUplink(Node, RSSI, FPort, Bytes)
|
||||||
var data = {"Device":"Dragino LHT52"}
|
var data = {"Device":"Dragino LHT52"}
|
||||||
data.insert("Node", Node)
|
data.insert("Node", Node)
|
||||||
|
|
||||||
var valid_values = false
|
var valid_values = false
|
||||||
|
var last_seen
|
||||||
|
var battery_last_seen
|
||||||
|
var battery = 1000
|
||||||
|
var rssi = RSSI
|
||||||
var temp_int
|
var temp_int
|
||||||
var humidity
|
var humidity
|
||||||
var temp_ext = 1000
|
var temp_ext = 1000
|
||||||
|
if global.lht52Nodes.find(Node)
|
||||||
|
last_seen = global.lht52Nodes.item(Node)[1]
|
||||||
|
battery_last_seen = global.lht52Nodes.item(Node)[2]
|
||||||
|
battery = global.lht52Nodes.item(Node)[3]
|
||||||
|
rssi = global.lht52Nodes.item(Node)[4]
|
||||||
|
temp_int = global.lht52Nodes.item(Node)[5]
|
||||||
|
humidity = global.lht52Nodes.item(Node)[6]
|
||||||
|
temp_ext = global.lht52Nodes.item(Node)[7]
|
||||||
|
end
|
||||||
## SENSOR DATA ##
|
## SENSOR DATA ##
|
||||||
if 2 == FPort && Bytes.size() == 11
|
if 2 == FPort && Bytes.size() == 11
|
||||||
var TempC
|
last_seen = tasmota.rtc('local')
|
||||||
|
|
||||||
|
var TempC
|
||||||
TempC = Bytes[0] << 8 | Bytes[1]
|
TempC = Bytes[0] << 8 | Bytes[1]
|
||||||
if Bytes[0] > 0x7F
|
if Bytes[0] > 0x7F
|
||||||
TempC -= 0x10000
|
TempC -= 0x10000
|
||||||
@ -55,8 +69,10 @@ class LwDecoLHT52
|
|||||||
data.insert("Firmware_Version", f'v{Bytes[1]:%u}.{Bytes[2]>>4:%u}.{Bytes[2]&0xF:%u}')
|
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("Freq_Band",LwRegions[Bytes[3]-1])
|
||||||
data.insert("Sub_Band",Bytes[4])
|
data.insert("Sub_Band",Bytes[4])
|
||||||
data.insert("Bat_mV",(Bytes[5] << 8) | Bytes[6])
|
data.insert("BattV",((Bytes[5] << 8) | Bytes[6]) / 1000)
|
||||||
|
battery_last_seen = tasmota.rtc('local')
|
||||||
|
battery = ((Bytes[5] << 8) | Bytes[6]) / 1000
|
||||||
|
valid_values = true
|
||||||
else
|
else
|
||||||
# Ignore other Fports
|
# Ignore other Fports
|
||||||
end #Fport
|
end #Fport
|
||||||
@ -65,7 +81,7 @@ class LwDecoLHT52
|
|||||||
if global.lht52Nodes.find(Node)
|
if global.lht52Nodes.find(Node)
|
||||||
global.lht52Nodes.remove(Node)
|
global.lht52Nodes.remove(Node)
|
||||||
end
|
end
|
||||||
global.lht52Nodes.insert(Node, [Node, temp_int, humidity, temp_ext])
|
global.lht52Nodes.insert(Node, [Node, last_seen, battery_last_seen, battery, RSSI, temp_int, humidity, temp_ext])
|
||||||
end
|
end
|
||||||
|
|
||||||
return data
|
return data
|
||||||
@ -74,18 +90,28 @@ class LwDecoLHT52
|
|||||||
static def add_web_sensor()
|
static def add_web_sensor()
|
||||||
var msg = ""
|
var msg = ""
|
||||||
for sensor: global.lht52Nodes
|
for sensor: global.lht52Nodes
|
||||||
msg += string.format("{s}LHT52_%i Temperature{m}%.1f °C{e}"..
|
# Sensor[0] [1] [2] [3] [4] [5] [6] [7]
|
||||||
"{s}LHT52_%i Humidity{m}%.1f %%{e}",
|
# [Node, last_seen, battery_last_seen, battery, RSSI, temp_int, humidity, temp_ext]
|
||||||
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
|
var name = string.format("LHT52-%i", sensor[0])
|
||||||
|
var name_tooltip = "Dragino LHT52"
|
||||||
|
var battery = sensor[3]
|
||||||
|
var battery_last_seen = sensor[2]
|
||||||
|
var rssi = sensor[4]
|
||||||
|
var last_seen = sensor[1]
|
||||||
|
msg += lwdecode.header(name, name_tooltip, battery, battery_last_seen, rssi, last_seen)
|
||||||
|
|
||||||
|
# Sensors
|
||||||
|
msg += "<tr class='htr'><td colspan='4'>┆" # |
|
||||||
|
msg += string.format(" ☀️ %.1f°C", sensor[5]) # Sunshine - Temperature internal
|
||||||
|
msg += string.format(" 💧 %.1f%%", sensor[6]) # Raindrop - Humidity
|
||||||
|
if sensor[7] < 1000
|
||||||
|
msg += string.format(" ☀️ ext %.1f°C", sensor[7]) # Sunshine - Temperature external
|
||||||
end
|
end
|
||||||
|
msg += "{e}" # = </td></tr>
|
||||||
|
end
|
||||||
|
return msg
|
||||||
|
end #add_web_sensor()
|
||||||
end #class
|
end #class
|
||||||
|
|
||||||
LwDeco = LwDecoLHT52
|
LwDeco = LwDecoLHT52
|
||||||
|
@ -9,7 +9,7 @@ var LHT65_BatteryStatus = ["Very low <= 2.5V","Low <=2.55V","OK","Good >= 2.65V"
|
|||||||
global.lht65Nodes = {}
|
global.lht65Nodes = {}
|
||||||
|
|
||||||
class LwDecoLHT65
|
class LwDecoLHT65
|
||||||
static def decodeUplink(Node, FPort, Bytes)
|
static def decodeUplink(Node, RSSI, FPort, Bytes)
|
||||||
var data = {"Device":"Dragino LHT65"}
|
var data = {"Device":"Dragino LHT65"}
|
||||||
data.insert("Node", Node)
|
data.insert("Node", Node)
|
||||||
data.insert("poll_message_status",(Bytes[6] & 0x40) >> 6)
|
data.insert("poll_message_status",(Bytes[6] & 0x40) >> 6)
|
||||||
@ -18,21 +18,30 @@ class LwDecoLHT65
|
|||||||
var NoConnect = (Bytes[6] & 0x80) >> 7
|
var NoConnect = (Bytes[6] & 0x80) >> 7
|
||||||
|
|
||||||
var valid_values = false
|
var valid_values = false
|
||||||
|
var last_seen
|
||||||
|
var battery_last_seen
|
||||||
|
var battery = 1000
|
||||||
|
var rssi = RSSI
|
||||||
var temp_int = 1000
|
var temp_int = 1000
|
||||||
var humidity
|
var humidity
|
||||||
var temp_ext = 1000
|
var temp_ext = 1000
|
||||||
var door_open = 1000
|
var door_open = 1000
|
||||||
if global.lht65Nodes.find(Node)
|
if global.lht65Nodes.find(Node)
|
||||||
temp_int = global.lht65Nodes.item(Node)[1]
|
last_seen = global.lht65Nodes.item(Node)[1]
|
||||||
humidity = global.lht65Nodes.item(Node)[2]
|
battery_last_seen = global.lht65Nodes.item(Node)[2]
|
||||||
temp_ext = global.lht65Nodes.item(Node)[3]
|
battery = global.lht65Nodes.item(Node)[3]
|
||||||
door_open = global.lht65Nodes.item(Node)[4]
|
RSSI = global.lht65Nodes.item(Node)[4]
|
||||||
|
temp_int = global.lht65Nodes.item(Node)[5]
|
||||||
|
humidity = global.lht65Nodes.item(Node)[6]
|
||||||
|
temp_ext = global.lht65Nodes.item(Node)[7]
|
||||||
|
door_open = global.lht65Nodes.item(Node)[8]
|
||||||
end
|
end
|
||||||
## SENSOR DATA ##
|
## SENSOR DATA ##
|
||||||
if 2 == FPort && Bytes.size() == 11
|
if 2 == FPort && Bytes.size() == 11
|
||||||
var TempC
|
var TempC
|
||||||
|
|
||||||
if Ext == 9 #Sensor E3, Temperature Sensor, Datalog Mod
|
if Ext == 9 #Sensor E3, Temperature Sensor, Datalog Mod
|
||||||
|
last_seen = tasmota.rtc('local')
|
||||||
TempC = ((Bytes[0] << 8) | Bytes[1])
|
TempC = ((Bytes[0] << 8) | Bytes[1])
|
||||||
if 0x7FFF == TempC
|
if 0x7FFF == TempC
|
||||||
data.insert("Ext_SensorConnected", false)
|
data.insert("Ext_SensorConnected", false)
|
||||||
@ -47,11 +56,15 @@ class LwDecoLHT65
|
|||||||
end
|
end
|
||||||
data.insert("Bat_status", LHT65_BatteryStatus[Bytes[4] >> 6])
|
data.insert("Bat_status", LHT65_BatteryStatus[Bytes[4] >> 6])
|
||||||
else
|
else
|
||||||
data.insert("BatV",(((Bytes[0] << 8) | Bytes[1]) & 0x3fff) / 1000.0)
|
data.insert("BattV",(((Bytes[0] << 8) | Bytes[1]) & 0x3fff) / 1000.0)
|
||||||
|
battery_last_seen = tasmota.rtc('local')
|
||||||
|
battery = (((Bytes[0] << 8) | Bytes[1]) & 0x3fff) / 1000.0
|
||||||
data.insert("Bat_status", LHT65_BatteryStatus[Bytes[0] >> 6])
|
data.insert("Bat_status", LHT65_BatteryStatus[Bytes[0] >> 6])
|
||||||
|
valid_values = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if Ext != 0x0F
|
if Ext != 0x0F
|
||||||
|
last_seen = tasmota.rtc('local')
|
||||||
TempC = ((Bytes[2] << 8) | Bytes[3])
|
TempC = ((Bytes[2] << 8) | Bytes[3])
|
||||||
if Bytes[2]>0x7F
|
if Bytes[2]>0x7F
|
||||||
TempC -= 0x10000
|
TempC -= 0x10000
|
||||||
@ -70,6 +83,7 @@ class LwDecoLHT65
|
|||||||
if 0 == Ext
|
if 0 == Ext
|
||||||
data.insert("Ext_sensor", 'No external sensor')
|
data.insert("Ext_sensor", 'No external sensor')
|
||||||
elif 1==Ext
|
elif 1==Ext
|
||||||
|
last_seen = tasmota.rtc('local')
|
||||||
data.insert("Ext_sensor",'Temperature Sensor')
|
data.insert("Ext_sensor",'Temperature Sensor')
|
||||||
TempC = ((Bytes[7] << 8) | Bytes[8])
|
TempC = ((Bytes[7] << 8) | Bytes[8])
|
||||||
if 0x7FFF == TempC
|
if 0x7FFF == TempC
|
||||||
@ -84,6 +98,7 @@ class LwDecoLHT65
|
|||||||
valid_values = true
|
valid_values = true
|
||||||
end
|
end
|
||||||
elif 4 == Ext
|
elif 4 == Ext
|
||||||
|
last_seen = tasmota.rtc('local')
|
||||||
data.insert("Work_mode", 'Interrupt Sensor send')
|
data.insert("Work_mode", 'Interrupt Sensor send')
|
||||||
door_open = ( Bytes[7] ) ? 0 : 1 # DS sensor
|
door_open = ( Bytes[7] ) ? 0 : 1 # DS sensor
|
||||||
data.insert("Exti_pin_level", Bytes[7] ? 'High' : 'Low')
|
data.insert("Exti_pin_level", Bytes[7] ? 'High' : 'Low')
|
||||||
@ -117,8 +132,10 @@ class LwDecoLHT65
|
|||||||
data.insert("Firmware_Version", f'v{Bytes[1]:%u}.{Bytes[2]>>4:%u}.{Bytes[2]&0xF:%u}')
|
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("Freq_Band",LwRegions[Bytes[3]-1])
|
||||||
data.insert("Sub_Band",Bytes[4])
|
data.insert("Sub_Band",Bytes[4])
|
||||||
data.insert("Bat_mV",(Bytes[5] << 8) | Bytes[6])
|
data.insert("BattV",((Bytes[5] << 8) | Bytes[6]) / 1000.0)
|
||||||
|
battery_last_seen = tasmota.rtc('local')
|
||||||
|
battery = ((Bytes[5] << 8) | Bytes[6]) / 1000.0
|
||||||
|
valid_values = true
|
||||||
else
|
else
|
||||||
# Ignore other Fports
|
# Ignore other Fports
|
||||||
end #Fport
|
end #Fport
|
||||||
@ -127,34 +144,42 @@ class LwDecoLHT65
|
|||||||
if global.lht65Nodes.find(Node)
|
if global.lht65Nodes.find(Node)
|
||||||
global.lht65Nodes.remove(Node)
|
global.lht65Nodes.remove(Node)
|
||||||
end
|
end
|
||||||
global.lht65Nodes.insert(Node, [Node, temp_int, humidity, temp_ext, door_open])
|
global.lht65Nodes.insert(Node, [Node, last_seen, battery_last_seen, battery, rssi, temp_int, humidity, temp_ext, door_open])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
return data
|
return data
|
||||||
end # decodeUplink()
|
end # decodeUplink()
|
||||||
|
|
||||||
static def add_web_sensor()
|
static def add_web_sensor()
|
||||||
var msg = ""
|
var msg = ""
|
||||||
for sensor: global.lht65Nodes
|
for sensor: global.lht65Nodes
|
||||||
if sensor[1] < 1000
|
# Sensor[0] [1] [2] [3] [4] [5] [6] [7] [8]
|
||||||
msg += string.format("{s}LHT65_%i Temperature{m}%.1f °C{e}"..
|
# [Node, last_seen, battery_last_seen, battery, RSSI, temp_int, humidity, temp_ext, door_open]
|
||||||
"{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
|
|
||||||
if sensor[4] < 1000
|
|
||||||
msg += string.format("{s}LHT65_%i Door{m}%s{e}",
|
|
||||||
sensor[0], (sensor[4]) ? "Open" : "Closed")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return msg
|
var name = string.format("LHT65-%i", sensor[0])
|
||||||
|
var name_tooltip = "Dragino LHT65"
|
||||||
|
var battery = sensor[3]
|
||||||
|
var battery_last_seen = sensor[2]
|
||||||
|
var rssi = sensor[4]
|
||||||
|
var last_seen = sensor[1]
|
||||||
|
msg += lwdecode.header(name, name_tooltip, battery, battery_last_seen, rssi, last_seen)
|
||||||
|
|
||||||
|
# Sensors
|
||||||
|
msg += "<tr class=\"htr\"><td colspan=\"4\">┆" # |
|
||||||
|
if sensor[5] < 1000
|
||||||
|
msg += string.format(" ☀️ %.1f°C", sensor[5]) # Sunshine - Temperature
|
||||||
|
msg += string.format(" 💧 %.1f%%", sensor[6]) # Raindrop - Humidity
|
||||||
end
|
end
|
||||||
|
if sensor[7] < 1000
|
||||||
|
msg += string.format(" ☀️ ext %.1f°C", sensor[7]) # Sunshine - Temperature external
|
||||||
|
end
|
||||||
|
if sensor[8] < 1000
|
||||||
|
msg += string.format(" %s", (sensor[8]) ? "🔓" : "🔒") # Open or Closed lock - Door
|
||||||
|
end
|
||||||
|
msg += "{e}" # = </td></tr>
|
||||||
|
end
|
||||||
|
return msg
|
||||||
|
end #add_web_sensor()
|
||||||
end # class
|
end # class
|
||||||
|
|
||||||
LwDeco = LwDecoLHT65
|
LwDeco = LwDecoLHT65
|
@ -9,25 +9,32 @@ import string
|
|||||||
global.dw10Nodes = {}
|
global.dw10Nodes = {}
|
||||||
|
|
||||||
class LwDecoDW10
|
class LwDecoDW10
|
||||||
static def decodeUplink(Node, FPort, Bytes)
|
static def decodeUplink(Node, RSSI, FPort, Bytes)
|
||||||
var data = {"Device":"MerryIoT DW10"}
|
var data = {"Device":"MerryIoT DW10"}
|
||||||
data.insert("Node", Node)
|
data.insert("Node", Node)
|
||||||
|
|
||||||
var valid_values = false
|
var valid_values = false
|
||||||
|
var last_seen
|
||||||
|
var battery_last_seen
|
||||||
|
var battery
|
||||||
|
var rssi = RSSI
|
||||||
var door_open
|
var door_open
|
||||||
var button_pressed
|
var button_pressed
|
||||||
var temperature
|
var temperature
|
||||||
var humidity
|
var humidity
|
||||||
## SENSOR DATA ##
|
## SENSOR DATA ##
|
||||||
if 120 == FPort && Bytes.size() == 9
|
if 120 == FPort && Bytes.size() == 9
|
||||||
|
last_seen = tasmota.rtc('local')
|
||||||
door_open = ( Bytes[0] & 0x01 ) ? 1 : 0
|
door_open = ( Bytes[0] & 0x01 ) ? 1 : 0
|
||||||
data.insert("DoorOpen", ( door_open ) ? true : false )
|
data.insert("DoorOpen", ( door_open ) ? true : false )
|
||||||
button_pressed = ( Bytes[0] & 0x02 ) ? 1 : 0
|
button_pressed = ( Bytes[0] & 0x02 ) ? 1 : 0
|
||||||
data.insert("ButtonPress", ( button_pressed ) ? true : false )
|
data.insert("ButtonPress", ( button_pressed ) ? true : false )
|
||||||
data.insert("TamperDetect", ( Bytes[0] & 0x04 ) ? true : false )
|
data.insert("TamperDetect", ( Bytes[0] & 0x04 ) ? true : false )
|
||||||
data.insert("TiltDetect", ( Bytes[0] & 0x08 ) ? true : false )
|
data.insert("TiltDetect", ( Bytes[0] & 0x08 ) ? true : false )
|
||||||
data.insert("Battery_mV", ( 21 + Bytes[1] ) * 100 )
|
data.insert("BattV", (( 21 + Bytes[1] ) * 100) / 1000.0 )
|
||||||
data.insert("Temperature_C", Bytes[2])
|
battery_last_seen = tasmota.rtc('local')
|
||||||
|
battery = (( 21 + Bytes[1] ) * 100) / 1000.0
|
||||||
|
data.insert("TemperatureC", Bytes[2])
|
||||||
temperature = Bytes[2]
|
temperature = Bytes[2]
|
||||||
data.insert("Humidity", Bytes[3])
|
data.insert("Humidity", Bytes[3])
|
||||||
humidity = Bytes[3]
|
humidity = Bytes[3]
|
||||||
@ -43,7 +50,7 @@ class LwDecoDW10
|
|||||||
if global.dw10Nodes.find(Node)
|
if global.dw10Nodes.find(Node)
|
||||||
global.dw10Nodes.remove(Node)
|
global.dw10Nodes.remove(Node)
|
||||||
end
|
end
|
||||||
global.dw10Nodes.insert(Node, [Node, door_open, button_pressed, temperature, humidity])
|
global.dw10Nodes.insert(Node, [Node, last_seen, battery_last_seen, battery, RSSI, door_open, button_pressed, temperature, humidity])
|
||||||
end
|
end
|
||||||
|
|
||||||
return data
|
return data
|
||||||
@ -52,17 +59,26 @@ class LwDecoDW10
|
|||||||
static def add_web_sensor()
|
static def add_web_sensor()
|
||||||
var msg = ""
|
var msg = ""
|
||||||
for sensor: global.dw10Nodes
|
for sensor: global.dw10Nodes
|
||||||
msg += string.format("{s}DW10_%i Door{m}%s{e}"..
|
# Sensor[0] [1] [2] [3] [4] [5] [6] [7] [8]
|
||||||
"{s}DW10_%i Button{m}%s{e}"..
|
# [Node, last_seen, battery_last_seen, battery, RSSI, door_open, button_pressed, temperature, humidity]
|
||||||
"{s}DW10_%i Temperature{m}%.1f °C{e}"..
|
|
||||||
"{s}DW10_%i Humidity{m}%.1f %%{e}",
|
var name = string.format("DW10-%i", sensor[0])
|
||||||
sensor[0], (sensor[1]) ? "Open" : "Closed",
|
var name_tooltip = "MerryIoT DW10"
|
||||||
sensor[0], (sensor[2]) ? "Pressed" : "Released",
|
var battery = sensor[3]
|
||||||
sensor[0], sensor[3],
|
var battery_last_seen = sensor[2]
|
||||||
sensor[0], sensor[4])
|
var rssi = sensor[4]
|
||||||
|
var last_seen = sensor[1]
|
||||||
|
msg += lwdecode.header(name, name_tooltip, battery, battery_last_seen, rssi, last_seen)
|
||||||
|
|
||||||
|
# Sensors
|
||||||
|
msg += "<tr class='htr'><td colspan='4'>┆" # |
|
||||||
|
msg += string.format(" ☀️ %.1f°C", sensor[7]) # Sunshine - Temperature
|
||||||
|
msg += string.format(" 💧 %.1f%%", sensor[8]) # Raindrop - Humidity
|
||||||
|
msg += string.format(" %s", (sensor[5]) ? "🔓" : "🔒") # Open or Closed lock - Door
|
||||||
|
msg += "{e}" # = </td></tr>
|
||||||
end
|
end
|
||||||
return msg
|
return msg
|
||||||
end
|
end #add_web_sensor()
|
||||||
end #class
|
end #class
|
||||||
|
|
||||||
LwDeco = LwDecoDW10
|
LwDeco = LwDecoDW10
|
||||||
|
@ -231,8 +231,8 @@ const char HTTP_SCRIPT_INFO_END[] PROGMEM =
|
|||||||
#include "./html_uncompressed/HTTP_HEAD_STYLE_WIFI.h"
|
#include "./html_uncompressed/HTTP_HEAD_STYLE_WIFI.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_ZIGBEE
|
#if defined(USE_ZIGBEE) || defined(USE_LORAWAN_BRIDGE)
|
||||||
// Styles used for Zigbee Web UI
|
// Styles used for Zigbee and LoRaWan Web UI
|
||||||
// Battery icon from https://css.gg/battery
|
// Battery icon from https://css.gg/battery
|
||||||
//
|
//
|
||||||
#ifdef USE_UNISHOX_COMPRESSION
|
#ifdef USE_UNISHOX_COMPRESSION
|
||||||
@ -997,7 +997,10 @@ void WSContentSendStyle_P(const char* formatP, ...) {
|
|||||||
#ifdef USE_WEB_STATUS_LINE_WIFI
|
#ifdef USE_WEB_STATUS_LINE_WIFI
|
||||||
WSContentSend_P(HTTP_HEAD_STYLE_WIFI, WebColor(COL_FORM), WebColor(COL_TITLE));
|
WSContentSend_P(HTTP_HEAD_STYLE_WIFI, WebColor(COL_FORM), WebColor(COL_TITLE));
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ZIGBEE
|
|
||||||
|
USE_LORAWAN_BRIDGE
|
||||||
|
|
||||||
|
#if defined(USE_ZIGBEE) || defined(USE_LORAWAN_BRIDGE)
|
||||||
WSContentSend_P(HTTP_HEAD_STYLE_ZIGBEE);
|
WSContentSend_P(HTTP_HEAD_STYLE_ZIGBEE);
|
||||||
#endif // USE_ZIGBEE
|
#endif // USE_ZIGBEE
|
||||||
if (formatP != nullptr) {
|
if (formatP != nullptr) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user