├── LICENSE ├── README.md ├── combined.js ├── door.js ├── gps.js ├── healthyhome.js └── pir.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Sensational Systems 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Things Network decoder functions for Smart Building Sensors 2 | 3 | We sell a range of low-cost LoRaWAN sensors designed for homes and offices. The range includes Door & Window detectors, Temperature & Relative Humidity sensors and PIR motion detectors. This repo has individual decoders for the sensors, and a combined decoder that can handle all in one application. 4 | 5 | You can find the sensors here: 6 | * Temperature & humidity [EU868 version](https://connectedthings.store/gb/home-and-office-sensors/smart-building-sensors-temperature-humidity-eu868.html), [US915 version](https://connectedthings.store/gb/home-and-office-sensors/smart-building-sensors-temperature-humidity-us915.html) 7 | * Door & Window [EU868 version](https://connectedthings.store/gb/home-and-office-sensors/smart-building-sensors-door-window-eu868.html), [US915 version](https://connectedthings.store/gb/home-and-office-sensors/smart-building-sensors-door-window-us915.html) 8 | * PIR Motion [EU868 version](https://connectedthings.store/gb/home-and-office-sensors/smart-building-sensors-motion-sensor-eu868.html), [US915 version](https://connectedthings.store/gb/home-and-office-sensors/smart-building-sensors-motion-sensor-us915.html) 9 | 10 | To use this: 11 | * create a TTN application and register your devices using the TTN console 12 | * in your application, choose "Payload Formats" from the navigation 13 | * paste the decoder function into the textarea 14 | 15 | The unit send well documented payloads that these decoders should handle. The devices also all use different LoRaWAN ports, so a combined decoder is included to handle them all in one function. 16 | 17 | This code is MIT licenced, and it works fine in our testing. We don't claim it to be excellent, pull requests are encouraged! 18 | -------------------------------------------------------------------------------- /combined.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Decoder function for The Things Network to unpack the payload of the Smart Building Sensors 3 | * 4 | * This function was created by Cameron Sharp at Sensational Systems - cameron@sensational.systems 5 | */ 6 | 7 | function Decoder(bytes, port) { 8 | 9 | var params = { 10 | "bytes": bytes 11 | }; 12 | 13 | if (port === 103) { 14 | // Healthy Home Sensor 15 | // VOC Measurement 16 | voc = (bytes[7] << 8) | bytes[6]; 17 | if (voc === 65535) { 18 | voc_error = true; 19 | } else { 20 | voc_error = false; 21 | } 22 | 23 | // CO2 Measurement 24 | co2 = (bytes[5] << 8) | bytes[4]; 25 | if (co2 === 65535) { 26 | co2_error = true; 27 | } else { 28 | co2_error = false; 29 | } 30 | 31 | // IAQ Measurement 32 | iaq = (bytes[9] << 9) | bytes[8]; 33 | 34 | // Humidity Measurement 35 | rh = bytes[3] &= 0x7f; 36 | if (rh === 127) { 37 | rh_error = true; 38 | } else { 39 | rh_error = false; 40 | } 41 | 42 | // Board temp measurement 43 | temp_board = bytes[2] & 0x7f; 44 | temp_board = temp_board - 32; 45 | 46 | // Ambient temp measurement 47 | temp_ambient = bytes[10] & 0x7f; 48 | temp_ambient = temp_ambient - 32; 49 | 50 | // Battery measurements 51 | batt = bytes[1] & 0x0f; 52 | batt = (25 + batt) / 10; 53 | 54 | params.voc = voc; 55 | params.voc_error = voc_error; 56 | params.co2 = co2; 57 | params.co2_error = co2_error; 58 | params.iaq = iaq; 59 | params.rh = rh; 60 | params.rh_error = rh_error; 61 | params.temp_board = temp_board; 62 | params.temp_ambient = temp_ambient; 63 | params.batt = batt; 64 | 65 | } else if (port === 100) { 66 | // Door Sensor 67 | // Temp measurement 68 | temp = bytes[2] & 0x7f; 69 | temp = temp - 32; 70 | 71 | // Battery measurements 72 | batt = bytes[1] & 0x0f; 73 | cap = bytes[1] >> 4; 74 | 75 | batt = (25 + batt) / 10; 76 | cap = (cap / 15) * 100; 77 | 78 | // Time measurement 79 | time = (bytes[4] << 8) | bytes[3]; 80 | 81 | // Count measurement 82 | count = ((bytes[7] << 16) | (bytes[6] << 8)) | bytes[5]; 83 | 84 | // Status measurement 85 | status = bytes[0] & 0x1; 86 | if (status === 1) { 87 | open = true; 88 | } else { 89 | open = false; 90 | } 91 | 92 | params.temp = temp; 93 | params.batt = batt; 94 | params.cap = cap; 95 | params.time = time; 96 | params.count = count; 97 | params.open = open; 98 | params.port = port; 99 | } else if (port === 102) { 100 | // PIR Sensor 101 | // Temp measurement 102 | temp = bytes[2] & 0x7f; 103 | temp = temp - 32; 104 | 105 | // Battery measurements 106 | batt = bytes[1] & 0x0f; 107 | cap = bytes[1] >> 4; 108 | 109 | batt = (25 + batt) / 10; 110 | cap = (cap / 15) * 100; 111 | 112 | // Time measurement 113 | time = (bytes[4] << 8) | bytes[3]; 114 | 115 | // Count measurement 116 | count = ((bytes[7] << 16) | (bytes[6] << 8)) | bytes[5]; 117 | 118 | // Status measurement 119 | status = bytes[0] & 0x1; 120 | if (status === 1) { 121 | occupied = true; 122 | } else { 123 | occupied = false; 124 | } 125 | 126 | params.temp = temp; 127 | params.batt = batt; 128 | params.cap = cap; 129 | params.time = time; 130 | params.count = count; 131 | params.occupied = occupied; 132 | params.port = port; 133 | } 134 | 135 | return params; 136 | } 137 | -------------------------------------------------------------------------------- /door.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Decoder function for The Things Network to unpack the payload of the Smart Building Sensors 3 | * 4 | * This function was created by Cameron Sharp at Sensational Systems - cameron@sensational.systems 5 | */ 6 | 7 | function Decoder(bytes, port) { 8 | 9 | var params = { 10 | "bytes": bytes 11 | }; 12 | 13 | // Temp measurement 14 | temp = bytes[2] & 0x7f; 15 | temp = temp - 32; 16 | 17 | // Battery measurements 18 | batt = bytes[1] & 0x0f; 19 | cap = bytes[1] >> 4; 20 | 21 | batt = (25 + batt) / 10; 22 | cap = (cap / 15) * 100; 23 | 24 | // Time measurement 25 | time = (bytes[4] << 8) | bytes[3]; 26 | 27 | // Count measurement 28 | count = ((bytes[7] << 16) | (bytes[6] << 8)) | bytes[5]; 29 | 30 | // Status measurement 31 | status = bytes[0] & 0x1; 32 | if (status === 1) { 33 | open = true; 34 | } else { 35 | open = false; 36 | } 37 | 38 | params.temp = temp; 39 | params.batt = batt; 40 | params.cap = cap; 41 | params.time = time; 42 | params.count = count; 43 | params.open = open; 44 | params.port = port; 45 | 46 | return params; 47 | } 48 | -------------------------------------------------------------------------------- /gps.js: -------------------------------------------------------------------------------- 1 | function Decoder(bytes, port) { 2 | 3 | var params = { 4 | "bytes": bytes 5 | }; 6 | 7 | if (port == 136) { 8 | if ((bytes[0] & 0x8) === 0) { 9 | params.gnss_fix = true; 10 | } else { 11 | params.gnss_fix = false; 12 | } 13 | 14 | // Mask off enf of temp byte, RFU 15 | temp = bytes[2] & 0x7f; 16 | 17 | acc = bytes[10] >> 5; 18 | acc = Math.pow(2, parseInt(acc) + 2); 19 | 20 | // Mask off end of accuracy byte, so lon doesn't get affected 21 | bytes[10] &= 0x1f; 22 | 23 | if ((bytes[10] & (1 << 4)) !== 0) { 24 | bytes[10] |= 0xe0; 25 | } 26 | 27 | // Mask off end of lat byte, RFU 28 | bytes[6] &= 0x0f; 29 | 30 | lat = ((bytes[6] << 24 | bytes[5] << 16) | bytes[4] << 8 ) | bytes[3]; 31 | lon = ((bytes[10] << 24 | bytes[9] << 16) | bytes[8] << 8 ) | bytes[7]; 32 | 33 | battery = bytes[1]; 34 | capacity = battery >> 4; 35 | voltage = battery & 0x0f; 36 | 37 | params.latitude = lat/1000000; 38 | params.longitude = lon/1000000; 39 | params.accuracy = acc; 40 | params.temperature = temp - 32; 41 | params.capacity = (capacity / 15) * 100; 42 | params.voltage = (25 + voltage)/10; 43 | params.port=port; 44 | 45 | return params; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /healthyhome.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Decoder function for The Things Network to unpack the payload of the Smart Building Sensors 3 | * 4 | * This function was created by Cameron Sharp at Sensational Systems - cameron@sensational.systems 5 | */ 6 | 7 | function Decoder(bytes, port) { 8 | 9 | var params = { 10 | "bytes": bytes 11 | }; 12 | 13 | // VOC Measurement 14 | voc = (bytes[7] << 8) | bytes[6]; 15 | if (voc === 65535) { 16 | voc_error = true; 17 | } else { 18 | voc_error = false; 19 | } 20 | 21 | // CO2 Measurement 22 | co2 = (bytes[5] << 8) | bytes[4]; 23 | if (co2 === 65535) { 24 | co2_error = true; 25 | } else { 26 | co2_error = false; 27 | } 28 | 29 | // IAQ Measurement 30 | iaq = (bytes[9] << 8) | bytes[8]; 31 | 32 | // Humidity Measurement 33 | rh = bytes[3] &= 0x7f; 34 | if (rh === 127) { 35 | rh_error = true; 36 | } else { 37 | rh_error = false; 38 | } 39 | 40 | // Board temp measurement 41 | temp_board = bytes[2] & 0x7f; 42 | temp_board = temp_board - 32; 43 | 44 | // Ambient temp measurement 45 | temp_ambient = bytes[10] & 0x7f; 46 | temp_ambient = temp_ambient - 32; 47 | 48 | // Battery measurements 49 | batt = bytes[1] & 0x0f; 50 | batt = (25 + batt) / 10; 51 | 52 | params.voc = voc; 53 | params.voc_error = voc_error; 54 | params.co2 = co2; 55 | params.co2_error = co2_error; 56 | params.iaq = iaq; 57 | params.rh = rh; 58 | params.rh_error = rh_error; 59 | params.temp_board = temp_board; 60 | params.temp_ambient = temp_ambient; 61 | params.batt = batt; 62 | 63 | return params; 64 | } 65 | -------------------------------------------------------------------------------- /pir.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Decoder function for The Things Network to unpack the payload of the Smart Building Sensors 3 | * 4 | * This function was created by Cameron Sharp at Sensational Systems - cameron@sensational.systems 5 | */ 6 | 7 | function Decoder(bytes, port) { 8 | 9 | var params = { 10 | "bytes": bytes 11 | }; 12 | 13 | // Temp measurement 14 | temp = bytes[2] & 0x7f; 15 | temp = temp - 32; 16 | 17 | // Battery measurements 18 | batt = bytes[1] & 0x0f; 19 | cap = bytes[1] >> 4; 20 | 21 | batt = (25 + batt) / 10; 22 | cap = (cap / 15) * 100; 23 | 24 | // Time measurement 25 | time = (bytes[4] << 8) | bytes[3]; 26 | 27 | // Count measurement 28 | count = ((bytes[7] << 16) | (bytes[6] << 8)) | bytes[5]; 29 | 30 | // Status measurement 31 | status = bytes[0] & 0x1; 32 | if (status === 1) { 33 | occupied = true; 34 | } else { 35 | occupied = false; 36 | } 37 | 38 | params.temp = temp; 39 | params.batt = batt; 40 | params.cap = cap; 41 | params.time = time; 42 | params.count = count; 43 | params.occupied = occupied; 44 | params.port = port; 45 | 46 | return params; 47 | } 48 | --------------------------------------------------------------------------------