[{"data":1,"prerenderedAt":491},["ShallowReactive",2],{"doc-\u002Fhardware\u002Fgeneric-lorawan":3},{"id":4,"title":5,"body":6,"description":481,"edit":482,"extension":483,"meta":484,"navigation":485,"path":486,"seo":487,"stem":488,"vertical":482,"weight":489,"__hash__":490},"content\u002Fhardware\u002Fgeneric-lorawan.md","Generic LoRaWAN",{"type":7,"value":8,"toc":474},"minimark",[9,13,26,31,34,37,78,81,171,213,217,226,247,259,263,278,282,293,385,392,396,467,470],[10,11,12],"p",{},"If your LoRaWAN device is not Efento and not on the OpenSense supported\nlist, you can still route it through TTN. You need three things:",[14,15,16,20,23],"ol",{},[17,18,19],"li",{},"The device has a payload decoder (or you write one) that produces JSON.",[17,21,22],{},"TTN has an HTTP webhook integration pointed at OpenSense.",[17,24,25],{},"OpenSense knows how to map the JSON to your sensor channels.",[27,28,30],"h2",{"id":29},"step-1-write-the-payload-decoder","Step 1 — write the payload decoder",[10,32,33],{},"TTN runs a JavaScript function per uplink. The function gets the raw bytes,\nreturns an object that OpenSense ingests.",[10,35,36],{},"Example for a Dragino LHT65 (T + ext probe T):",[38,39,41],"cmd",{"label":40},"$ ttn payload decoder",[10,42,43,44,48,49,52,53,48,56,59,60,48,63,66,67,48,70,73,74,77],{},"function decodeUplink(input) {\nconst b = input.bytes;\n\u002F\u002F Dragino LHT65 v1.x payload format\nconst battery = ((b",[45,46,47],"span",{},"0"," \u003C\u003C 8) | b",[45,50,51],{},"1",") \u002F 1000.0;            \u002F\u002F V\nconst t_int   = ((b",[45,54,55],{},"2",[45,57,58],{},"3",") \u002F 100.0;             \u002F\u002F °C\nconst rh_int  = ((b",[45,61,62],{},"4",[45,64,65],{},"5",") \u002F 100.0;             \u002F\u002F %\nconst t_ext   = ((b",[45,68,69],{},"7",[45,71,72],{},"8",") \u002F 100.0;             \u002F\u002F °C (DS18B20)\nreturn {\ndata: {\nmeasurements: ",[45,75,76],{},"\n{ type: 'temperature',  value: t_int, label: 'internal' },\n{ type: 'humidity',     value: rh_int },\n{ type: 'temperature',  value: t_ext, label: 'probe'    },\n{ type: 'battery',      value: battery, unit: 'V'       }\n","\n}\n};\n}",[10,79,80],{},"The shape OpenSense expects:",[82,83,88],"pre",{"className":84,"code":85,"language":86,"meta":87,"style":87},"language-json shiki shiki-themes github-dark github-dark","{\n  \"measurements\": [\n    { \"type\": \"\u003Ckind>\", \"value\": \u003Cnumber>, \"label\": \"\u003Coptional>\", \"unit\": \"\u003Coptional>\" }\n  ]\n}\n","json","",[89,90,91,99,109,159,165],"code",{"__ignoreMap":87},[45,92,95],{"class":93,"line":94},"line",1,[45,96,98],{"class":97},"suv1-","{\n",[45,100,102,106],{"class":93,"line":101},2,[45,103,105],{"class":104},"s8ozJ","  \"measurements\"",[45,107,108],{"class":97},": [\n",[45,110,112,115,118,121,125,128,131,133,137,139,142,144,147,149,152,154,156],{"class":93,"line":111},3,[45,113,114],{"class":97},"    { ",[45,116,117],{"class":104},"\"type\"",[45,119,120],{"class":97},": ",[45,122,124],{"class":123},"s4wv1","\"\u003Ckind>\"",[45,126,127],{"class":97},", ",[45,129,130],{"class":104},"\"value\"",[45,132,120],{"class":97},[45,134,136],{"class":135},"sX7ps","\u003Cnumber>",[45,138,127],{"class":97},[45,140,141],{"class":104},"\"label\"",[45,143,120],{"class":97},[45,145,146],{"class":123},"\"\u003Coptional>\"",[45,148,127],{"class":97},[45,150,151],{"class":104},"\"unit\"",[45,153,120],{"class":97},[45,155,146],{"class":123},[45,157,158],{"class":97}," }\n",[45,160,162],{"class":93,"line":161},4,[45,163,164],{"class":97},"  ]\n",[45,166,168],{"class":93,"line":167},5,[45,169,170],{"class":97},"}\n",[10,172,173,176,177,127,180,127,183,127,186,189,190,127,193,127,196,127,199,127,202,127,205,208,209,212],{},[89,174,175],{},"type"," must be one of: ",[89,178,179],{},"temperature",[89,181,182],{},"humidity",[89,184,185],{},"pressure",[89,187,188],{},"co2",",\n",[89,191,192],{},"voltage",[89,194,195],{},"current",[89,197,198],{},"power",[89,200,201],{},"leak",[89,203,204],{},"battery",[89,206,207],{},"signal",". Unknown types\nare ignored. ",[89,210,211],{},"label"," distinguishes multiple sensors of the same type on\none device (e.g. two probes).",[27,214,216],{"id":215},"step-2-webhook-to-opensense","Step 2 — webhook to OpenSense",[10,218,219,220,225],{},"Same as for ",[221,222,224],"a",{"href":223},"\u002Fhardware\u002Fefento-ns-t","Efento",":",[227,228,229,235,241],"ul",{},[17,230,231,234],{},[89,232,233],{},"Application → Integrations → Webhooks → + Add → JSON",".",[17,236,237,238,234],{},"Paste the OpenSense ingest URL from ",[89,239,240],{},"+ ADD DEVICE → LORAWAN → Generic",[17,242,243,244,234],{},"Enable ",[89,245,246],{},"Uplink message",[10,248,249,250,254,255,258],{},"OpenSense uses the ",[251,252,253],"strong",{},"DevEUI"," from the TTN payload metadata to identify\nthe device. If you have not registered the DevEUI in OpenSense yet, the\nfirst uplink lands in the ",[89,256,257],{},"unassigned"," tray on the site page; click to\nassociate.",[27,260,262],{"id":261},"step-3-map-channels","Step 3 — map channels",[10,264,265,266,269,270,273,274,277],{},"OpenSense auto-creates a channel per ",[89,267,268],{},"type + label"," combination on first\nuplink. Initial thresholds are pulled from the ",[251,271,272],{},"vertical"," preset of the\nsite. For example, a HACCP site auto-creates a temperature channel with\n",[89,275,276],{},"[−2 °C, +8 °C]"," operating range. Edit per-channel afterwards.",[27,279,281],{"id":280},"a-note-on-fport","A note on FPort",[10,283,284,285,288,289,292],{},"Some LoRaWAN devices send different payload shapes on different ",[89,286,287],{},"FPort","\nvalues. The payload decoder can switch on ",[89,290,291],{},"input.fPort"," to handle them:",[82,294,298],{"className":295,"code":296,"language":297,"meta":87,"style":87},"language-js shiki shiki-themes github-dark github-dark","function decodeUplink(input) {\n  if (input.fPort === 1)  return decodeReadings(input.bytes);\n  if (input.fPort === 99) return decodeConfig(input.bytes);\n  return { errors: ['unknown fport'] };\n}\n","js",[89,299,300,320,346,367,381],{"__ignoreMap":87},[45,301,302,306,310,313,317],{"class":93,"line":94},[45,303,305],{"class":304},"sOPea","function",[45,307,309],{"class":308},"sFR8T"," decodeUplink",[45,311,312],{"class":97},"(",[45,314,316],{"class":315},"s-3mD","input",[45,318,319],{"class":97},") {\n",[45,321,322,325,328,331,334,337,340,343],{"class":93,"line":101},[45,323,324],{"class":304},"  if",[45,326,327],{"class":97}," (input.fPort ",[45,329,330],{"class":304},"===",[45,332,333],{"class":104}," 1",[45,335,336],{"class":97},")  ",[45,338,339],{"class":304},"return",[45,341,342],{"class":308}," decodeReadings",[45,344,345],{"class":97},"(input.bytes);\n",[45,347,348,350,352,354,357,360,362,365],{"class":93,"line":111},[45,349,324],{"class":304},[45,351,327],{"class":97},[45,353,330],{"class":304},[45,355,356],{"class":104}," 99",[45,358,359],{"class":97},") ",[45,361,339],{"class":304},[45,363,364],{"class":308}," decodeConfig",[45,366,345],{"class":97},[45,368,369,372,375,378],{"class":93,"line":161},[45,370,371],{"class":304},"  return",[45,373,374],{"class":97}," { errors: [",[45,376,377],{"class":123},"'unknown fport'",[45,379,380],{"class":97},"] };\n",[45,382,383],{"class":93,"line":167},[45,384,170],{"class":97},[10,386,387,388,391],{},"OpenSense ignores anything that is not ",[89,389,390],{},"measurements",". Config uplinks are\nfine, they are simply dropped at the ingest.",[27,393,395],{"id":394},"devices-we-have-tested","Devices we have tested",[397,398,399,412],"table",{},[400,401,402],"thead",{},[403,404,405,409],"tr",{},[406,407,408],"th",{},"Vendor \u002F model",[406,410,411],{},"Status",[413,414,415,424,432,440,451,459],"tbody",{},[403,416,417,421],{},[418,419,420],"td",{},"Efento NS-T-3, NX-T",[418,422,423],{},"First-class, see dedicated page",[403,425,426,429],{},[418,427,428],{},"Dragino LHT65 \u002F LHT65N",[418,430,431],{},"Works with decoder above",[403,433,434,437],{},[418,435,436],{},"Milesight EM300-TH",[418,438,439],{},"Works with built-in TTN decoder",[403,441,442,445],{},[418,443,444],{},"Milesight EM500-CO2",[418,446,447,448],{},"Works, vertical = ",[89,449,450],{},"climate",[403,452,453,456],{},[418,454,455],{},"Adeunis Field Test Device",[418,457,458],{},"Works, useful for survey only",[403,460,461,464],{},[418,462,463],{},"Generic Decentlab DL-IAM",[418,465,466],{},"Works with vendor decoder",[10,468,469],{},"If you get a device working that is not on the list, send the decoder via\nGitHub and we will add it.",[471,472,473],"style",{},"html pre.shiki code .suv1-, html code.shiki .suv1-{--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .s8ozJ, html code.shiki .s8ozJ{--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .s4wv1, html code.shiki .s4wv1{--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .sX7ps, html code.shiki .sX7ps{--shiki-default:#FDAEB7;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sOPea, html code.shiki .sOPea{--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sFR8T, html code.shiki .sFR8T{--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .s-3mD, html code.shiki .s-3mD{--shiki-default:#FFAB70;--shiki-dark:#FFAB70}",{"title":87,"searchDepth":111,"depth":111,"links":475},[476,477,478,479,480],{"id":29,"depth":101,"text":30},{"id":215,"depth":101,"text":216},{"id":261,"depth":101,"text":262},{"id":280,"depth":101,"text":281},{"id":394,"depth":101,"text":395},"Any TTN device with a JSON payload",null,"md",{},true,"\u002Fhardware\u002Fgeneric-lorawan",{"title":5,"description":481},"hardware\u002Fgeneric-lorawan",140,"cIWRt_v3EfhQJQ8TzCMQ81bu8P-ViQdxhFNgcxnXRqY",1779022953800]