i am new to this subject and after trying for 2 days now, i think asking for help is the only way for me to get this running.
I have a device sending with lorawan to ttn. TTN send mqtt/ i am trying to pic them up on their server.
In Domoticz i installed new hardware: MQTT Client Gateway with Lan Interface- Remote adress is eu1.cloud.thethings.network, Port ist 1883, Username is x@ttn, password ist API key. Topic in prefix is: v3/x@ttn/devices
with this datamqtt explorer shows my decoded payload.
End device in TTN uses "Use Device Repository formatter"
Code: Select all
function decodeUplink(input) {
switch (input.fPort) {
case 10:
bytes = input.bytes;
var data = {};
for (i=0 ; i<bytes.length; i++) {
switch (bytes[i]) {
case 0x03:
data.hw_version = bytes[++i];
data.capabilities = (bytes[++i] << 8)+ bytes[++i];
break;
case 0x04:
p = bytes[++i];
v = (bytes[++i] << 8)+ bytes[++i];
switch (p) {
case 0x01:
data.conf_system=v;
break;
case 0x02:
data.conf_heartbeat=v;
break;
case 0x03:
data.conf_heavyrain=v;
break;
case 0x04:
data.conf_interval=v;
break;
default:
if (p > 16) data.error = "config parameter? "+bytes[i];
else {
data.conf_parameter = p;
data.conf_value = v;
}
}
break;
case 0x06:
sensor = bytes[++i];
sensorvalue = (bytes[++i] << 8)+ bytes[++i];
switch(sensor) {
case 0x01:
if (sensorvalue < 0x4000)
data.temperature_C = sensorvalue/10.0;
else
data.temperature_C = (sensorvalue - 0xffff)/10.0;
break;
case 0x02:
data.humidity = sensorvalue;
break;
case 0x03:
data.uptime = sensorvalue;
break;
case 0x08:
if (sensorvalue < 0x4000)
data.airtemperature_C = sensorvalue/10.0;
else
data.airtemperature_C = (sensorvalue - 0xffff)/10.0;
break;
case 0x11:
data.consumption = sensorvalue;
break;
case 0x12:
data.flow = sensorvalue;
break;
case 0x81:
data.rainlevel = sensorvalue * 0.5 ; // 1 == 500 ml
break;
default:
data.error = "sensor type? "+bytes[i];
}
break;
case 0x07:
data.motorposition = bytes[++i];
break;
case 0x0a:
data.fw_version = (bytes[++i] << 24) +(bytes[++i] << 16) + (bytes[++i] << 8) + bytes[++i];
break;
case 0x0b:
data.a_status = bytes[++i];
data.a_type = bytes[++i];
data.a_value = (bytes[++i] << 8)+ bytes[++i];
var val;
switch (data.a_type) {
case 0x01:
data.alarm = "Flood";
val = " ";
break;
case 0x02:
data.alarm = "Temperature Low";
if (data.a_value > 0x4000)
data.a_value = (data.a_value - 0xffff)/10.0;
val = " with " + data.a_value + " C";
break;
case 0x03:
data.alarm = "Heavyrain";
val = " with " + data.a_value + " l/m3";
break;
case 0x04:
data.alarm = "Humidity";
val = " with " + data.a_value + " %";
break;
case 0x05:
data.alarm = "Vibration";
val = " device";
break;
case 0x06:
data.alarm = "Temperature High";
if (data.a_value > 0x4000)
data.a_value = (data.a_value - 0xffff)/10.0;
val = " with " + data.a_value + " C";
break;
case 0x07:
data.alarm = "Air Temperature Low";
if (data.a_value > 0x4000)
data.a_value = (data.a_value - 0xffff)/10.0;
val = " with " + data.a_value + " C";
break;
case 0x0c:
data.alarm = "Battery";
val = " with " + data.a_value + " mAh";
break;
default:
data.error = "Alarm? "+data.a_type;
break;
}
if (data.a_status) data.alarm += " on" + val; else data.alarm += " off" + val;
break;
case 0x12:
data.bat_volt = (bytes[++i])/10.0;
data.bat_mAh = (bytes[++i] << 8) + bytes[++i];
break;
case 0x33:
data.duration = (bytes[++i] << 8)+ bytes[++i];
data.diff = (bytes[++i] << 8)+ bytes[++i];
break;
default:
data.error = "command? "+bytes[i];
}
}
return { data: data};
default:
return {errors: ['invalid FPort']};
}
}
function encodeDownlink(input) {
cmd = input.data.cmd;
switch (String(cmd)) {
case "config_set":
return {
fPort: 10,
bytes: [0x04, input.data.parameter, (input.data.value>>8) & 0xff, input.data.value & 0xff],
};
case "get_hw":
return {
fPort: 10,
bytes: [0x03],
};
case "get_fw":
return {
fPort: 10,
bytes: [0x1a],
};
case "config_get":
return {
fPort: 10,
bytes: [0x14, input.data.parameter],
};
}
}
function decodeDownlink(input) {
bytes = input.bytes;
data = {};
if (input.fPort != 10)
return {
errors: ['invalid FPort'],
};
switch (bytes[0]) {
case 0x04:
data.cmd = "config_set";
data.parameter = bytes[1];
data.value = (bytes[2] << 8)+ bytes[3];
break;
case 0x03:
data.cmd = "get_hw";
break;
case 0x0a:
data.cmd = "get_fw";
break;
case 0x04:
data.cmd = "config_get";
data.parameter = bytes[1];
break;
}
return {data:data}
}Code: Select all
{
"end_device_ids": {
"device_id":"y","application_ids" {
"application_id":"x"
},
"dev_eui":"060000F6xxx","join_eui":"49434854xxx1","dev_addr":"260B8xx"},
"correlation_ids":["gs:uplink:01KATNGAEXDXZKQ5Q9QJ5HTV3C"],
"received_at":"2025-11-24T10:10:45.547949795Z",
"uplink_message":{
"session_key_id":"AZqYkxmTl646rq5ll3XJlQ==",
"f_port":10,"f_cnt":95,"frm_payload":"Eh0AAwYBAPoGgQAA",
"decoded_payload":{
"bat_mAh":3,
"bat_volt":2.9,
"rainlevel":0,
"temperature_C":25},
"rx_metadata":[{
"gateway_ids":{
"gateway_id":"eui-7076ff005607xx","eui":"7076FF005xx"},
"time":"2025-11-24T10:10:45.263Z",
"timestamp":3943593060,"rssi":-122,
"channel_rssi":-122,
"snr":-1.2,
"uplink_token":"CiIKIAoUZXVpLTcwNzZmZjAwNTYwNzBiODxx/wBWBwuDEOxxxgwIpd6QyQYQjNDPogEgoM3ag+OFTioLCKXekMkGEMCftH0=",
"channel_index":5,
"gps_time":"2025-11-24T10:10:45.263Z",
"received_at":"2025-11-24T10:10:45.341043212Z"}],
"settings":{
"data_rate":{
"lora":{
"bandwidth":125000,
"spreading_factor":10, "coding_rate":"4/5"}},
"frequency":"868100000", "timestamp":3943593060, "time":"2025-11-24T10:10:45.263Z"},"received_at":"2025-11-24T10:10:45.342156701Z","consumed_airtime":"0.411648s",
"packet_error_rate":0.071428575,
"locations":{"user":{"latitude":53.xxx,"longitude":9.xxx,"altitude":22,"source":"SOURCE_REGISTRY"}},
"version_ids":{"brand_id":"aquascope", "model_id":
"ran",
"hardware_version":"1.0",
"firmware_version":"241115",
"band_id":"EU_863_870"},"network_ids":{"net_id":"000013",
"ns_id":"EC656E0000000181",
"tenant_id":"ttn","cluster_id":"eu1",
"cluster_address":"eu1.cloud.thethings.network"}}}Code: Select all
TtnMqttFuerLuaScripte: Connected to: eu1.cloud.thethings.network:1883Code: Select all
-- Lua script to parse TTN MQTT messages
commandArray = {}
-- Das VOLLSTÄNDIGE erwartete MQTT Topic
-- Dies ist der exakte Topic-String, den TTN verwendet
local expectedFullTopic = "v3/x@ttn/devices/y/up" --DAS MUSS FÜR JEDES DEVICE UND GGF AUCH FÜR DIE APPLICANTION NEU
local rainSensorIDX = 45
local batSensorIDX = 46
-- PRÜFUNG HINZUFÜGEN: Stellen Sie sicher, dass 'devicechanged' existiert und 'MQTTTopic' enthält
if (devicechanged and devicechanged['MQTTTopic']) then
-- Prüfen, ob es der RICHTIGE, vollständige Topic ist
if (devicechanged['MQTTTopic'] == expectedFullTopic) then
-- Die JSON-Payload auslesen
local jsonPayload = devicechanged['MQTTPayload']
-- JSON dekodieren
local decodedData = jsonlite.decode(jsonPayload)
-- Überprüfen, ob die Dekodierung erfolgreich war und die Datenstruktur passt
if (decodedData and decodedData.uplink_message and decodedData.uplink_message.decoded_payload) then
-- Hier greifen Sie auf die spezifischen Werte zu, die TTN dekodiert hat
local rainlevel = decodedData.uplink_message.decoded_payload.rainlevel
local bat_volt = decodedData.uplink_message.decoded_payload.bat_volt
-- Werte in die virtuellen Domoticz-Geräte schreiben
if (rainlevel) then
commandArray = {['UpdateDevice'] = rainSensorIDX .. "|0|" .. rainlevel}
end
if (bat_volt) then
commandArray = {['UpdateDevice'] = batSensorIDX .. "|0|" .. bat_volt .. ";0"}
end
end
end
end
return commandArraySo why is ther no Payload coming in to Domoticz? If it did come in, does the script run properly?