Skip to content

[Profile Request] Thermokon - NOVOS 3 Occ Lum CO2 Temp_rH weiß LRW #25

Description

@Lucaneedshelp

Device Vendor

Thermokon Sensortechnik GmbH

Device Model

NOVOS 3 Occ Lum CO2 Temp_rH

Product Manual/Datasheet Link

https://www.thermokon.de/direct/alterra-base/mimes/get/f3f365ccac822a0b6 / https://www.thermokon.de/direct/alterra-base/mimes/get/3d63622604fadb043

LoRaWAN Class

Class A

LoRaWAN Protocol Version

LORAWAN_1_0_4

Uplink Data Examples

fPort 2: 10 00 D7 11 2D 12 03 20 40 01 5E 41 00 54 4B
fPort 2: 10 00 EC 11 37 12 04 B0 40 02 EE 41 07 54 4B
fPort 2: 10 00 C8 11 32 12 07 D0 40 13 88 41 19 54 4B

Decode Function (Optional)

// Thermokon Sensortechnik GmbH 
// Payload Decoder for Thermokon LoRaWAN devices
// Template: Adaption to corresponding NS/AS necessary
// -Revision B-
// -Subject to change without further notice!
//*********************************************************
// Changelog: 	- Added Identifiers for addtitional features (eg. NOVOS 3 INC EPD,...) 
//				- Removed Identifiers which were not used
//				- Changed objectnames for quick access (1-Byte) identifier
//*********************************************************
// Questions or remarks:support@thermokon.de

//Decoder Mode
var DecoderLegacyMode = false;
//Only set to "true" if former object identifier must be used


var LPP_S_TEMP_0 = 0x0010;
var LPP_S_RHUM_0 = 0x0011;
var LPP_S_CO2_0 = 0x0012;
var LPP_S_VOC_0 = 0x0013;
var LPP_S_MOISTURE_0 = 0x0014;
var LPP_S_ATM_P_0 = 0x0030;
var LPP_S_DIF_P_0 = 0x0031;
var LPP_S_FLOW_0 = 0x0032;
var LPP_S_LIGHT_0 = 0x0040;
var LPP_S_OCCU_0 = 0x0041;
var LPP_S_REED_0 = 0x0050;
var LPP_S_CONDENSATION_0 = 0x0051;
var LPP_S_VBAT_0 = 0x0054;
var LPP_S_BUTTON_0 = 0x0055;
var LPP_S_DI_0 = 0x0058;
var LPP_S_SETPOINT_0 = 0x0063;
var LPP_TEMP_1 = 0x9100;
var LPP_MOISTURE_1 = 0x9140;
var LPP_DIF_P_1 = 0x9310;
var LPP_FLOW_1 = 0x9320;
var LPP_OCCU_1 = 0x9410;
var LPP_REED_1 = 0x9500;
var LPP_BUTTON_1 = 0x9550;
var LPP_DI_1 = 0x9580;
var LPP_SETPOINT_1 = 0x9630;
var LPP_TEMP_2 = 0xA100;
var LPP_DEV_KEY = 0xC000;
var LPP_EPD_VALUE_EXT_OVERLOAD_0 = 0xC194;
var LPP_EPD_VALUE_EXT_OVERLOAD_1 = 0xC19C;
var LPP_EPD_VALUE_EXT_OVERLOAD_2 = 0xC1A4;
var LPP_EPD_VALUE_EXT_OVERLOAD_3 = 0xC1AC;
var LPP_FORCED_UPLINK = 0xC230;

function u16_to_s16(u16) {
	var s16 = u16&0xFFFF;
	if (0x8000 & s16){s16 = - (0x010000 - s16);}
	return s16;
}
function u8_to_s8(u8) {
	var s8=u8&0xFF;
	if (0x80 & s8) {s8 = - (0x0100 - s8);}
	return s8;
}

function DecodeLPPPayload(data) {
	var obj = {};	

	for(i=0;i<data.length;i++){
		var lpp = 0;
		if(data[i] <= 0x7F) {lpp = data[i]; i++;}
		else {lpp = (data[i] << 8) + data[i + 1]; i+=2;}

		switch(lpp){
			case LPP_S_TEMP_0:
				if(DecoderLegacyMode==false)
				{obj.TEMP_0 = u16_to_s16(data[i] << 8 | data[i+1]) / 10;}
				else if(DecoderLegacyMode==true)
				{obj.TEMP = u16_to_s16(data[i] << 8 | data[i+1]) / 10;}
				i++;
			break;
			case LPP_S_RHUM_0:
				if(DecoderLegacyMode==false)
				{obj.RHUM_0 = u8_to_s8(data[i]) / 1;}
				else if(DecoderLegacyMode==true)
				{obj.RHUM = u8_to_s8(data[i]) / 1;}
			break;
			case LPP_S_CO2_0:
				if(DecoderLegacyMode==false)
				{obj.CO2_0 = (data[i]<<8 | data[i+1]) / 1;}
				else if(DecoderLegacyMode==true)
				{obj.CO2 = (data[i]<<8 | data[i+1]) / 1;}
				i++;
			break;
			case LPP_S_VOC_0:
				if(DecoderLegacyMode==false)
				{obj.VOC_0 = (data[i]<<8 | data[i+1]) / 1;}
				else if(DecoderLegacyMode==true)
				{obj.VOC = (data[i]<<8 | data[i+1]) / 1;}
				i++;
			break;
			case LPP_S_MOISTURE_0:
				obj.MOISTURE_0 = u16_to_s16(data[i] << 8 | data[i+1]) / 1;
				i++;
			break;
			case LPP_S_ATM_P_0:
				if(DecoderLegacyMode==false)
				{obj.ATM_P_0 = (data[i]<<8 | data[i+1]) / 1}
				else if(DecoderLegacyMode==true)
				{obj.ATM_P = (data[i]<<8 | data[i+1]) / 1;}
				i++;
			break;
			case LPP_S_DIF_P_0:
				if(DecoderLegacyMode==false)
				{obj.DIF_P_0 = u16_to_s16((data[i]<<8 | data[i+1]) / 1)}
				else if(DecoderLegacyMode==true)
				{obj.DP = u16_to_s16((data[i]<<8 | data[i+1]) / 1)}
				i++;
			break;
			case LPP_S_FLOW_0:
				if(DecoderLegacyMode==false)
				{obj.FLOW_0 = (data[i]<<8 | data[i+1]) / 1;}
				else if(DecoderLegacyMode==true)
				{obj.FLOW = (data[i]<<8 | data[i+1]) / 1;}
				i++;
			break;			
			case LPP_S_LIGHT_0:
				if(DecoderLegacyMode==false)
				{obj.LIGHT_0 = (data[i]<<8 | data[i+1]) / 1;}
				else if(DecoderLegacyMode==true)
				{obj.VISIBLE_LIGHT = (data[i]<<8 | data[i+1]) / 1;}
				i++;
			break;			
			case LPP_S_OCCU_0:
				if(DecoderLegacyMode==false)
				{obj.OCCU_0_STATE = data[i] & 0x01;
				obj.OCCU_0_CNT = data[i] >> 1;}						
				else if(DecoderLegacyMode==true)
				{obj.OCCU0_STATE = data[i] & 0x01;
				 obj.OCCU0_CNT = data[i] >> 1;}
			break;			
			case LPP_S_REED_0:
				if(DecoderLegacyMode==false)
				{obj.REED_0_STATE = data[i] & 0x01;
				obj.REED_0_CNT = data[i] >> 1;}						
				else if(DecoderLegacyMode==true)
				{obj.REED0_STATE = data[i] & 0x01;
				 obj.REED0_CNT = data[i] >> 1;}
			break;				
			case LPP_S_CONDENSATION_0:
				if(DecoderLegacyMode==false)
				{obj.CONDENSATION_0_STATE = (data[i] >>> 7);
				obj.CONDENSATION_0_RAW = (u16_to_s16(data[i] << 8 | data[i+1]) / 1)&0x0FFF;
				i++;}
				else if(DecoderLegacyMode==true)
				{obj.CONDENSATION_STATE = (data[i] >>> 7);
				obj.CONDENSATION_RAW = (u16_to_s16(data[i] << 8 | data[i+1]) / 1)&0x0FFF;
				i++;}
			break;						
			case LPP_S_VBAT_0:
				if(DecoderLegacyMode==false)
				{obj.VBAT_0 = data[i] / (1.0/20);}
				else if(DecoderLegacyMode==true)
				{obj.VBAT = data[i] / (1.0/20);}
			break;				
			case LPP_S_BUTTON_0:
				if(data[i]>=2)
                    	{obj.BUTTON_0_PRESSED = 1}	
				else
				    {obj.BUTTON_0_PRESSED = 0}	

				if (data[i] & 0x01 >= 1)
                	{obj.BUTTON_0_LAST_TYPE = "Long Press"}
				else
				    {obj.BUTTON_0_LAST_TYPE = "Short Press"}

				obj.BUTTON_0_CNT = data[i] >> 1;
            		break;
			case LPP_S_DI_0:
				obj.DI_0_STATE = data[i] & 0x01;
				obj.DI_0_CNT = data[i] >> 1;
			break;
			case LPP_S_SETPOINT_0:
				if(DecoderLegacyMode==false)
				{obj.SETPOINT_0 = data[i] / 1;}
				else if(DecoderLegacyMode==true)
				{obj.SETPOINT = data[i] / 1;}
			break;    	                     
			case LPP_TEMP_1:
				obj.TEMP_1 = u16_to_s16(data[i] << 8 | data[i+1]) / 10;
				i++;
			break;
			case LPP_MOISTURE_1:
				obj.MOISTURE_1 = (data[i] << 8 | data[i+1]) / 1;
				i++;
			break;			
			case LPP_DIF_P_1 :				
				obj.DIF_P_1 = u16_to_s16((data[i]<<8 | data[i+1]) / 1)
				i++;
			break;
			case LPP_FLOW_1:				
				obj.FLOW_1 = (data[i]<<8 | data[i+1]) / 1;
				i++;
			break;
			case LPP_REED_1:
				if(DecoderLegacyMode==false)
				{obj.REED_1_STATE = data[i] & 0x01;
				obj.REED_1_CNT = data[i] >> 1;}						
				else if(DecoderLegacyMode==true)
				{obj.REED1_STATE = data[i] & 0x01;
				 obj.REED1_CNT = data[i] >> 1;}
			break;	           
			case LPP_BUTTON_1:			
				if(data[i]>=2)
				    {obj.BUTTON_1_PRESSED = 1}	
				else
				    {obj.BUTTON_1_PRESSED = 0}

				if (data[i] & 0x01 >= 1)
				    {obj.BUTTON_1_LAST_TYPE = "Long Press"}
				else
				    {obj.BUTTON_1_LAST_TYPE = "Short Press"}

				obj.BUTTON_1_CNT = data[i] >> 1;
			break;
			case LPP_DI_1:
				obj.DI_1_STATE = data[i] & 0x01;
				obj.DI_1_CNT = data[i] >> 1;
			break;            
			case LPP_SETPOINT_1:
				obj.SETPOINT_1 = (data[i] << 8 | data[i+1]) / 10;
				i++;
			break;            
			case LPP_TEMP_2:
				obj.TEMP_2 = u16_to_s16(data[i] << 8 | data[i+1]) / 10;
				i++;
			break;			
			case LPP_DEV_KEY:
				obj.DEV_KEY = (data[i] << 8 | data[i+1]) / 1;
				i++;
			break;            
			case LPP_EPD_VALUE_EXT_OVERLOAD_0:
				obj.EPD_VALUE_EXT_OVERLOAD_0 = u16_to_s16(data[i] << 8 | data[i+1]) / 1;
				i++;
			break;
			case LPP_EPD_VALUE_EXT_OVERLOAD_1:
				obj.EPD_VALUE_EXT_OVERLOAD_1 = u16_to_s16(data[i] << 8 | data[i+1]) / 1;
				i++;
			break;
			case LPP_EPD_VALUE_EXT_OVERLOAD_2:
				obj.EPD_VALUE_EXT_OVERLOAD_2 = u16_to_s16(data[i] << 8 | data[i+1]) / 1;
				i++;
			break;
			case LPP_EPD_VALUE_EXT_OVERLOAD_3:
				obj.EPD_VALUE_EXT_OVERLOAD_3 = u16_to_s16(data[i] << 8 | data[i+1]) / 1;
				i++;
			break;            
			case LPP_FORCED_UPLINK:
				obj.FORCED_UPLINK = (data[i] << 8 | data[i+1]) / 1;
				i++;
			break;            
			default: //somthing is wrong with data
				i = data.length;
			break;
		}
	}
	return obj;
}

function decodeUplink(input) {
	var warnings = [];
	warnings.push();
	var data = DecodeLPPPayload(input.bytes);
	return {data,warnings};
}

BACnet Object Mapping Requirements

  • Temperature → AnalogInputObject (degreesCelsius)
  • Relative Humidity → AnalogInputObject (percent)
  • CO2 → AnalogInputObject (partsPerMillion)
  • Brightness → AnalogInputObject (lux)
  • Motion Detection → BinaryInputObject (motion detected / no motion detected)
  • Occupancy → BinaryInputObject (occupied / unoccupied)
  • Battery Voltage → AnalogInputObject (millivolts)

Priority

⚡ Medium - Planned for near-term use

Email

luca.georg@thermokon.de

Company or organization

Thermokon Sensortechnik GmbH

Pre-submission Checklist

  • I have provided complete device model information
  • I have provided real uplink data examples
  • I have explained the meaning of data fields
  • I have checked for duplicate profile requests

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions