Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion vendor/arwin-technology/index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ endDevices:
- lrs20310
- lrs20600
- lrs10701
- lrs2m001-4xxx
- lrs2m001-4p3p
- lrs20ld0
- lrs20u00
- lrs20ud0
29 changes: 20 additions & 9 deletions vendor/arwin-technology/lrs10701-codec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ uplinkDecoder:
bytes: [1, 17, 8, 170, 15, 125, 58, 152, 9, 41, 90]
output:
data:
event: heartbeat/button
event: 'heartbeat/button'
aqi: 34
co2: 554
temperature: 22.7
Expand All @@ -24,8 +24,8 @@ uplinkDecoder:
output:
data:
tvoc: 270
pm1.0: 6.3
pm2.5: 7.2
'pm1.0': 6.3
'pm2.5': 7.2
pm10: 8.1
- description: Firmware version 1.07.000
input:
Expand All @@ -34,18 +34,28 @@ uplinkDecoder:
output:
data:
firmwareVersion: '1.07.000'
- description: data upload interval 15 min, AQI led on, active sensor T/H, CO2, PMx, Gas 1, Gas 1 type NH3
- description: firmware version 1.07.000, battery voltage 3.55 v, battery percentage 100 %, uplink count 66051
input:
fPort: 9
bytes: [1, 7, 0, 7, 13, 222, 100, 0, 1, 2, 3]
output:
data:
firmwareVersion: '1.07.007'
batteryLevel: 3550
batteryPercentage: 100
uplinkCount: 66051
- description: data upload interval 15 min, AQI led on, active sensor T/H, CO2, PMx, Gas 1, Gas 1 type NH3, Gas 2, Gas 2 type H2S, Gas sensor resulution 0
input:
fPort: 12
bytes: [0, 15, 1, 29, 29, 1, 0, 1]
bytes: [0, 15, 1, 63, 63, 1, 2, 1]
output:
data:
dataUploadInterval: 15
statusLED: on
sensorType: 'T/H,CO2,PMx,Gas1'
sensorStatus: 'T/H,CO2,PMx,Gas1'
statusLED: 'on'
sensorType: 'T/H,TVOC,CO2,PMx,Gas1,Gas2'
sensorStatus: 'T/H,TVOC,CO2,PMx,Gas1,Gas2,gasResolution=0'
gas1Type: 'NH3'
gas2Type: 'None'
gas2Type: 'H2S'
- description: High temperature threshold 40C, Low temperature threshold 10C, High humidity threshold 95%, Low humidity threshold 35%
input:
fPort: 13
Expand Down Expand Up @@ -125,6 +135,7 @@ downlinkEncoder:
input:
data:
cmd: setGasesThresholds
ec_res: 0
co2Threshold: 800
tvocThreshold: 1200
gas1Threshold: 10
Expand Down
154 changes: 109 additions & 45 deletions vendor/arwin-technology/lrs10701.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

var lrs10701_events = ['heartbeat/button', 'rsvd', 'T/H', 'CO2', 'EC1', 'EC2', 'TVOC', 'PMx'];
var sensor_list = ['T/H', 'TVOC', 'CO2', 'PMx', 'Gas1', 'Gas2'];
var sensor_list = ['T/H', 'TVOC', 'CO2', 'PMx', 'Gas1', 'Gas2', 'rsvd', 'gasResolution=0', 'gasResolution=1'];
var gas_sensor_type = ['None', 'NH3', 'H2S', 'NO2', 'CO', 'HCHO', 'Custom'];

function hex2dec(hex) {
Expand All @@ -10,27 +11,29 @@ function hex2dec(hex) {
}

function decodeUplink(input) {
var i;
switch (input.fPort) {
case 10: // sensor data
var evt="";
for (let i=0; i<8; i++) {
if ((0x01<<i)&input.bytes[0])
for (i=0; i<8; i++) {
if ((0x01<<i)&input.bytes[0])
if (evt==="")
evt=lrs10701_events[i];
else
evt=evt+","+lrs10701_events[i];
}
var aqi_co2_t = input.bytes[1]<<24|input.bytes[2]<<16|input.bytes[3]<<8|input.bytes[4];
var ec_res = input.bytes[10]>>7;
return {
data: {
event: evt,
aqi: (aqi_co2_t>>23)&0x1ff,
co2: (aqi_co2_t>>10&0x1fff),
temperature: (hex2dec(aqi_co2_t&0x3ff)-300)/10,
humidity: input.bytes[5]*0.5,
gas1: (input.bytes[6]<<8|input.bytes[7])/1000,
gas2: (input.bytes[8]<<8|input.bytes[9])/1000,
battery: input.bytes[10],
gas1: ec_res ? (input.bytes[6]<<8|input.bytes[7])/10 : (input.bytes[6]<<8|input.bytes[7])/1000,
gas2: ec_res ? (input.bytes[8]<<8|input.bytes[9])/10 : (input.bytes[8]<<8|input.bytes[9])/1000,
battery: input.bytes[10]
},
};
case 11: // sensor data
Expand All @@ -39,38 +42,53 @@ function decodeUplink(input) {
tvoc: (input.bytes[0]<<8|input.bytes[1]),
"pm1.0": (input.bytes[2]<<16|input.bytes[3]<<8|input.bytes[4])/1000,
"pm2.5": (input.bytes[5]<<16|input.bytes[6]<<8|input.bytes[7])/1000,
"pm10": (input.bytes[8]<<16|input.bytes[9]<<8|input.bytes[10])/1000,
"pm10": (input.bytes[8]<<16|input.bytes[9]<<8|input.bytes[10])/1000
}
};
case 8: // version
var ver = input.bytes[0]+"."+("00"+input.bytes[1]).slice(-2)+"."+("000"+(input.bytes[2]<<8|input.bytes[3])).slice(-3);
var ver = input.bytes[0]+"."+("00"+input.bytes[1]).slice(-2)+"."+("000"+(input.bytes[2]<<8|input.bytes[3])).slice(-3);
return {
data: {
firmwareVersion: ver
}
};
case 9: // version, battery & uplink count
var ver = input.bytes[0]+"."+("00"+input.bytes[1]).slice(-2)+"."+("000"+(input.bytes[2]<<8|input.bytes[3])).slice(-3);
return {
data: {
firmwareVersion: ver,
batteryLevel: input.bytes[4]<<8|input.bytes[5],
batteryPercentage: input.bytes[6],
uplinkCount: input.bytes[7]<<24|input.bytes[8]<<16|input.bytes[9]<<8|input.bytes[10]
}
};
case 12: // device settings
var sensor_type="";
var sensor_ok="";
for (let i=0; i<8; i++) {
if ((0x01<<i)&input.bytes[3])
if (sensor_ok==="")
for (i=0; i<8; i++) {
if (((0x01<<i)&input.bytes[3]) && (i<6))
if (sensor_type==="")
sensor_type=sensor_list[i];
else
sensor_type=sensor_type+","+sensor_list[i];
if ((0x01<<i)&input.bytes[4])
if (((0x01<<i)&input.bytes[4]) || (i===7)) {
var inc=0;
if ((i===7) && ((0x01<<7)&input.bytes[4]))
inc=1;
if (sensor_ok==="")
sensor_ok=sensor_list[i];
sensor_ok=sensor_list[i+inc];
else
sensor_ok=sensor_ok+","+sensor_list[i];
sensor_ok=sensor_ok+","+sensor_list[i+inc];
}
}
return {
data: {
dataUploadInterval: hex2dec(input.bytes[0]<<8|input.bytes[1]),
statusLED: input.bytes[2] === 1 ? "on" : "off",
sensorType: sensor_type,
sensorStatus: sensor_ok,
gas1Type: gas_sensor_type[input.bytes[5]],
gas2Type: gas_sensor_type[input.bytes[6]],
gas1Type: input.bytes[5] < 6 ? gas_sensor_type[input.bytes[5]] : "unknown",
gas2Type: input.bytes[6] < 6 ? gas_sensor_type[input.bytes[6]] : "unknown"
}
};
case 13: // threshold settings
Expand All @@ -81,7 +99,7 @@ function decodeUplink(input) {
highTemperatureThreshold: hex2dec(input.bytes[1]<<8|input.bytes[2]),
lowTemperatureThreshold: hex2dec(input.bytes[3]<<8|input.bytes[4]),
highHumidityThreshold: (input.bytes[5]),
lowHumidityThreshold: (input.bytes[6]),
lowHumidityThreshold: (input.bytes[6])
}
};
case 1:
Expand All @@ -90,15 +108,24 @@ function decodeUplink(input) {
co2Threshold: (input.bytes[1]<<8|input.bytes[2]),
tvocThreshold: (input.bytes[3]<<8|input.bytes[4]),
gas1Threshold: (input.bytes[5]<<8|input.bytes[6])/1000,
gas2Threshold: (input.bytes[7]<<8|input.bytes[8])/1000,
gas2Threshold: (input.bytes[7]<<8|input.bytes[8])/1000
}
};
case 2:
return {
data: {
'pm1.0 Threshold': (input.bytes[1]<<16|input.bytes[2]<<8|input.bytes[3]),
'pm2.5 Threshold': (input.bytes[4]<<16|input.bytes[5]<<8|input.bytes[6]),
'pm10 Threshold': (input.bytes[7]<<16|input.bytes[8]<<8|input.bytes[9]),
'pm10 Threshold': (input.bytes[7]<<16|input.bytes[8]<<8|input.bytes[9])
}
};
case 3:
return {
data: {
co2Threshold: (input.bytes[1]<<8|input.bytes[2]),
tvocThreshold: (input.bytes[3]<<8|input.bytes[4]),
gas1Threshold: (input.bytes[5]<<8|input.bytes[6])/10,
gas2Threshold: (input.bytes[7]<<8|input.bytes[8])/10
}
};
default:
Expand All @@ -109,68 +136,105 @@ function decodeUplink(input) {
break;
default:
return {
errors: ['unknown FPort'],
errors: ['unknown FPort']
};
}
}

function isNumber(value) {
return typeof value === 'number';
}

function encodeDownlink(input) {
var payload = [];
var opt;

if (input.data.cmd === 'getFirmwareVersion') {
return {
fPort: 20,
bytes: [0]
bytes: [0x00]
};
}
else if (input.data.cmd === 'getDeviceSettings') {
return {
fPort: 21,
bytes: [0]
bytes: [0x00]
};
}
else if (input.data.cmd === 'getThresholdSettings') {
return {
fPort: 21,
bytes: [1]
} ;
bytes: [0x01]
};
}
else if (input.data.cmd === 'setDeviceSettings') {
var ult = input.data.dataUploadInterval;
var led = input.data.statusLED === 'on'?1:0;
var led = input.data.statusLED;
var dack = 1;
return {
fPort: 22,
bytes: payload.concat(ult>>8,ult&0xff,led,dack)
};
if (isNumber(ult) && ((led === 'on') || (led === 'off'))) {
return {
fPort: 22,
bytes: payload.concat((ult>>8)&0xff,ult&0xff,
(led === 'on')*1,
dack)
};
}
}
else if (input.data.cmd === 'setTHThresholds') {
var htth = (input.data.highTemperatureThreshold&0xffff);
var ltth = (input.data.lowTemperatureThreshold&0xffff);
var htth = input.data.highTemperatureThreshold;
var ltth = input.data.lowTemperatureThreshold;
var hhth = input.data.highHumidityThreshold;
var lhth = input.data.lowHumidityThreshold;
return {
fPort: 23,
bytes: payload.concat(0,htth>>8,htth&0xff,ltth>>8,ltth&0xff,hhth,lhth)
};
if (isNumber(htth) && isNumber(ltth) && isNumber(hhth) && isNumber(lhth)) {
return {
fPort: 23,
bytes: payload.concat(0x00,
(htth>>8)&0xff,htth&0xff,
(ltth>>8)&0xff,ltth&0xff,
hhth&0xff,
lhth&0xff)
};
}
}
else if (input.data.cmd === 'setGasesThresholds') {
var res = input.data.ec_res;
var co2th = input.data.co2Threshold;
var tvocth = input.data.tvocThreshold;
var g1th = input.data.gas1Threshold*1000;
var g2th = input.data.gas2Threshold*1000;
return {
fPort: 23,
bytes: payload.concat(1,co2th>>8,co2th&0xff,tvocth>>8,tvocth&0xff,g1th>>8,g1th&0xff,g2th>>8,g2th&0xff)
};
var g1th = input.data.gas1Threshold;
var g2th = input.data.gas2Threshold;
if (((res === 0) || (res === 1)) && isNumber(co2th) && isNumber(tvocth) && isNumber(g1th) && isNumber(g2th)) {
if (res) {
opt = 0x03;
g1th = g1th * 10;
g2th = g2th * 10;
}
else {
opt = 0x01;
g1th = g1th * 1000;
g2th = g2th * 1000;
}
return {
fPort: 23,
bytes: payload.concat(opt,
(co2th >>8)&0xff,co2th &0xff,
(tvocth>>8)&0xff,tvocth&0xff,
(g1th >>8)&0xff,g1th &0xff,
(g2th >>8)&0xff,g2th &0xff)
};
}
}
else if (input.data.cmd === 'setPMThresholds') {
var pm1p0th = input.data['pm1.0 Threshold'];
var pm2p5th = input.data['pm2.5 Threshold'];
var pm10th = input.data['pm10 Threshold'];
return {
fPort: 23,
bytes: payload.concat(2,pm1p0th>>16,(pm1p0th>>8)&0xff,pm1p0th&0xff,pm2p5th>>16,(pm2p5th>>8)&0xff,pm2p5th&0xff,pm10th>>16,(pm10th>>8)&0xff,pm10th&0xff)
};
if (isNumber(pm1p0th) && isNumber(pm2p5th) && isNumber(pm10th)) {
return {
fPort: 23,
bytes: payload.concat(0x02,
(pm1p0th>>16)&0xff,(pm1p0th>>8)&0xff,pm1p0th&0xff,
(pm2p5th>>16)&0xff,(pm2p5th>>8)&0xff,pm2p5th&0xff,
(pm10th >>16)&0xff,(pm10th >>8)&0xff,pm10th &0xff)
};
}
}
}
32 changes: 29 additions & 3 deletions vendor/arwin-technology/lrs10701.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: LRS10701 - Indoor Air Quality (IAQ) Sensor
description: Indoor Air Quality (IAQ) Sensor
name: SENSO8 LRS10701 - Indoor Air Quality (IAQ) Sensor
description: Indoor Air Quality (IAQ) Sensor by Arwin Technology Ltd.

# Hardware versions (optional, use when you have revisions)
hardwareVersions:
Expand Down Expand Up @@ -104,8 +104,34 @@ keySecurity: none

# Product and data sheet URLs (optional)
productURL: https://www.senso8.com/senso8-indoor-air-quality-sensors
dataSheetURL: https://www.senso8.com/_files/ugd/8cebc6_6f32d5db2af845149af9750b6d100de3.pdf
dataSheetURL: https://s3.ap-northeast-1.amazonaws.com/www.senso8.com/senso8_datasheet/IAQ/DS_LRS10701.pdf

# Photos
photos:
main: lrs10701.png

compliances:
safety:
- body: IEC
norm: EN
standard: 62368-1
radioEquipment:
- body: ETSI
norm: EN
standard: 300 220-1
version: 3.1.1
- body: ETSI
norm: EN
standard: 300 220-2
version: 3.2.1
- body: ETSI
norm: EN
standard: 301 489-1
version: 2.2.3
- body: ETSI
norm: EN
standard: 301 489-3
version: 2.3.2
- body: ETSI
norm: EN
standard: 62479:2010
Loading