⬇️T-Valve Downlink encoder
Universal Decoder:
Supports: The Thinks Network, Milesight, DataCake, Chirpstack
// --- Milesight ---
function Encode(port, obj) {
return encodeDownlink({ fPort: port, data: obj }).bytes;
}
// --- DataCake / generic ---
function Encoder(port, obj) {
return encodeDownlink({ fPort: port, data: obj }).bytes;
}
// --- The Things Stack v3 / TTN V3 / TTI / ChirpStack v4 / ThingPark / Main ---
function encodeDownlink(input) {
var bytes = [];
for (var key in input.data) {
switch (key) {
// ---------- valve control ----------
case "setValveState":
// 0: open, 1: close
bytes.push(0x0c);
bytes.push(input.data.setValveState);
break;
case "setSingleTimeValveState":
// { state: 0|1, time: 1..65535 }
bytes.push(0x14);
bytes.push(input.data.setSingleTimeValveState.state);
bytes.push((input.data.setSingleTimeValveState.time >> 8) & 0xff);
bytes.push(input.data.setSingleTimeValveState.time & 0xff);
break;
case "setOpenCloseTime":
// { openingTime: 1..255, closingTime: 1..255 } (seconds)
bytes.push(0x01);
bytes.push(input.data.setOpenCloseTime.openingTime);
bytes.push(input.data.setOpenCloseTime.closingTime);
break;
case "setOpenCloseTimeExtended":
// { openingTime: 1..65535, closingTime: 1..65535 } (ms)
bytes.push(0x0d);
bytes.push((input.data.setOpenCloseTimeExtended.openingTime >> 8) & 0xff);
bytes.push(input.data.setOpenCloseTimeExtended.openingTime & 0xff);
bytes.push((input.data.setOpenCloseTimeExtended.closingTime >> 8) & 0xff);
bytes.push(input.data.setOpenCloseTimeExtended.closingTime & 0xff);
break;
case "getOpenCloseTimeExtended":
bytes.push(0x0e);
break;
// ---------- manual control / emergency ----------
case "setManualControl":
// { enableOpen: bool, enableClose: bool } -> bit1 close, bit0 open
bytes.push(0x05);
bytes.push((input.data.setManualControl.enableClose ? 2 : 0) | (input.data.setManualControl.enableOpen ? 1 : 0));
break;
case "setEmergencyOpenings":
// { maxOpenings: 1..15 }
bytes.push(0x04);
bytes.push(input.data.setEmergencyOpenings.maxOpenings);
break;
case "getEmergencyOpenings":
bytes.push(0x0f);
break;
// ---------- flood ----------
case "setFloodAlarmTime":
// { time: 1..255 }
bytes.push(0x06);
bytes.push(input.data.setFloodAlarmTime.time);
break;
case "getFloodAlarmTime":
bytes.push(0x10);
break;
case "setDeviceFloodSensor":
// { enabled: bool }
bytes.push(0x0a);
bytes.push(input.data.setDeviceFloodSensor.enabled ? 1 : 0);
break;
case "getDeviceFloodSensor":
bytes.push(0x13);
break;
// ---------- working voltage ----------
case "setWorkingVoltage":
// { voltage: 1840..2500 } mV -> (voltage - 1600) / 8
bytes.push(0x09);
bytes.push(Math.round((input.data.setWorkingVoltage.voltage - 1600) / 8));
break;
case "getWorkingVoltage":
bytes.push(0x11);
break;
// ---------- LED / buzzer ----------
case "setLED":
// { ledId: 1..4, behavior: 0..4, seconds: 1..255 }
bytes.push(0x02);
bytes.push(input.data.setLED.ledId);
bytes.push(input.data.setLED.behavior);
bytes.push(input.data.setLED.seconds);
break;
case "setBuzzer":
// { volume, frequency, activeTime, onTime, offTime }; volume/freq packed into one byte
bytes.push(0x03);
bytes.push(((input.data.setBuzzer.volume << 4) | input.data.setBuzzer.frequency) & 0xff);
bytes.push(input.data.setBuzzer.activeTime);
bytes.push(Math.round(input.data.setBuzzer.onTime / 10));
bytes.push(Math.round(input.data.setBuzzer.offTime / 10));
break;
// ---------- data requests / lifecycle ----------
case "requestFullData":
bytes.push(0x08);
break;
case "deactivateDevice":
bytes.push(0x0b);
break;
// ---------- shared general commands (T-Valve-specific IDs) ----------
case "setKeepAlive":
// { time: 1..255 } minutes
bytes.push(0x07);
bytes.push(input.data.setKeepAlive);
break;
case "getKeepAliveTime":
bytes.push(0x12);
break;
case "getDeviceVersions":
bytes.push(0x04);
break;
case "setJoinRetryPeriod":
// value in minutes (1..21) -> minutes*60/5
bytes.push(0x15);
bytes.push(Math.round((input.data.setJoinRetryPeriod * 60) / 5));
break;
case "getJoinRetryPeriod":
bytes.push(0x16);
break;
case "setUplinkType":
// "00": unconfirmed, "01": confirmed (or numeric 0/1)
bytes.push(0x17);
bytes.push(parseInt(input.data.setUplinkType, 16) || 0);
break;
case "getUplinkType":
bytes.push(0x18);
break;
case "setWatchDogParams":
// { confirmedUplinks: 0..255, unconfirmedUplinks: 0..255 }
bytes.push(0x19);
bytes.push(input.data.setWatchDogParams.confirmedUplinks);
bytes.push(input.data.setWatchDogParams.unconfirmedUplinks);
break;
case "getWatchDogParams":
bytes.push(0x1a);
break;
// ---------- always keep this passthrough ----------
case "sendCustomHexCommand": {
var hex = input.data.sendCustomHexCommand;
for (var i = 0; i < hex.length; i += 2) {
bytes.push(parseInt(hex.substr(i, 2), 16));
}
break;
}
default:
break;
}
}
return { bytes: bytes, fPort: 1, warnings: [], errors: [] };
}
function decodeDownlink(input) {
return { data: { bytes: input.bytes }, warnings: [], errors: [] };
}
// example downlink commands
// {"setValveState":1} --> 0x0C01 (close)
// {"setValveState":0} --> 0x0C00 (open)
// {"setSingleTimeValveState":{"state":1,"time":3600}} --> 0x14010E10
// {"setOpenCloseTime":{"openingTime":30,"closingTime":30}} --> 0x011E1E
// {"setOpenCloseTimeExtended":{"openingTime":5000,"closingTime":5000}}--> 0x0D138813 88
// {"getOpenCloseTimeExtended":""} --> 0x0E
// {"setManualControl":{"enableOpen":true,"enableClose":true}} --> 0x0503
// {"setEmergencyOpenings":{"maxOpenings":5}} --> 0x0405
// {"getEmergencyOpenings":""} --> 0x0F
// {"setFloodAlarmTime":{"time":10}} --> 0x060A
// {"getFloodAlarmTime":""} --> 0x10
// {"setDeviceFloodSensor":{"enabled":true}} --> 0x0A01
// {"getDeviceFloodSensor":""} --> 0x13
// {"setWorkingVoltage":{"voltage":2000}} --> 0x0932 ((2000-1600)/8 = 50)
// {"getWorkingVoltage":""} --> 0x11
// {"setLED":{"ledId":1,"behavior":2,"seconds":5}} --> 0x02010205
// {"setBuzzer":{"volume":5,"frequency":3,"activeTime":10,"onTime":100,"offTime":100}} --> 0x03530A0A0A
// {"requestFullData":""} --> 0x08
// {"deactivateDevice":""} --> 0x0B
// {"setKeepAlive":5} --> 0x0705
// {"getKeepAliveTime":""} --> 0x12
// {"getDeviceVersions":""} --> 0x04
// {"setJoinRetryPeriod":10} --> 0x1578 (minutes*60/5)
// {"getJoinRetryPeriod":""} --> 0x16
// {"setUplinkType":"01"} --> 0x1701 (confirmed)
// {"getUplinkType":""} --> 0x18
// {"setWatchDogParams":{"confirmedUplinks":20,"unconfirmedUplinks":10}} --> 0x19140A
// {"getWatchDogParams":""} --> 0x1A
// {"sendCustomHexCommand":"080F15"} --> 0x080F15
//Last updated
Was this helpful?