diff --git a/decoders/connector/advantech/wise-2410/assets/logo.png b/decoders/connector/advantech/wise-2410/assets/logo.png new file mode 100644 index 00000000..83df2eb0 Binary files /dev/null and b/decoders/connector/advantech/wise-2410/assets/logo.png differ diff --git a/decoders/connector/advantech/wise-2410/connector.jsonc b/decoders/connector/advantech/wise-2410/connector.jsonc new file mode 100644 index 00000000..f03f4d33 --- /dev/null +++ b/decoders/connector/advantech/wise-2410/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Advantech WISE-2410", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/advantech/wise-2410/description.md b/decoders/connector/advantech/wise-2410/description.md new file mode 100644 index 00000000..6003bc46 --- /dev/null +++ b/decoders/connector/advantech/wise-2410/description.md @@ -0,0 +1 @@ +Industrial smart sensor with accelerometer and temperature sensors over LoRaWAN™ \ No newline at end of file diff --git a/decoders/connector/advantech/wise-2410/v1.0.0/payload-config.jsonc b/decoders/connector/advantech/wise-2410/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..e7d60618 --- /dev/null +++ b/decoders/connector/advantech/wise-2410/v1.0.0/payload-config.jsonc @@ -0,0 +1,25 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "WISE-2410 is a LoRaWAN wireless condition monitoring sensor integrated with an ARM Cortex-M4 processor, LoRa transceiver, 3-axis accelerometer and temperature sensor. It balances the wireless bandwidth between WISE-2410 and the gateway, so it also mitigates the data transmission fail rates between edge-devices and gateways.\n\nFeatures: \n* LoRaWAN wireless connectivity\n* Built-in 3-axis accelerometer and temperature sensor\n* On-board computing. Directly sends VRMS, ARMS, Peak, Displacement, Kurtosis, Crest factor, Skewness and Standard Deviation values to applications\n* Support battery power supply, no wiring installation needed\n* Easy settings with user friendly interfaces on WISE Studio utility\n* ISO 10816-1 compliant\n* Support wide temperature -20 ~ 80 °C (Battery-powered)\n* IP66 enclosure design\n\n \nProduct Website:\n* https://www.advantech.com/pt-br/products/b7e2306f-d561-4ca9-b0e3-33f7057e185f/wise-2410/mod_25018dc7-355c-40b4-bf9b-c93f6c73f1a0", + "install_end_text": "# Advantech WISE-2410 Dashboard Template\n\n\n\n![Template Image](https://api.tago.io/file/5bbcb03b667d7a002e56664b/advantech/smallwise2410dashimg.png)\n\n\n\nInstall the dashboard template by clicking [here](https://admin.tago.io/template/660beb6c2bbc8d00091dd697).", + "device_annotation": "Install the dashboard template by clicking [here](https://admin.tago.io/template/660beb6c2bbc8d00091dd697).", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/advantech/wise-2410/v1.0.0/payload.test.ts b/decoders/connector/advantech/wise-2410/v1.0.0/payload.test.ts new file mode 100644 index 00000000..615be206 --- /dev/null +++ b/decoders/connector/advantech/wise-2410/v1.0.0/payload.test.ts @@ -0,0 +1,153 @@ +import { describe, test, expect } from "vitest"; +import { decoderRun } from "../../../../../src/functions/decoder-run"; + +const file_path = "decoders/connector/advantech/wise-2410/v1.0.0/payload.ts" as const; + +function preparePayload(payloadHex) { + let payload = [{ variable: "payload", value: payloadHex }]; + payload = decoderRun(file_path, { payload }); + //y axis + const accelerometer_y_axis_senevent = payload.find((item) => item.variable === "accelerometer_y_axis_senevent"); + const accelerometer_y_axis_crestfactor = payload.find((item) => item.variable === "accelerometer_y_axis_crestfactor"); + const accelerometer_y_axis_kurtosis = payload.find((item) => item.variable === "accelerometer_y_axis_kurtosis"); + const accelerometer_y_axis_rms = payload.find((item) => item.variable === "accelerometer_y_axis_rms"); + const accelerometer_y_axis_peak = payload.find((item) => item.variable === "accelerometer_y_axis_peak"); + const accelerometer_y_axis_oavelocity = payload.find((item) => item.variable === "accelerometer_y_axis_oavelocity"); + const accelerometer_y_axis_skewness = payload.find((item) => item.variable === "accelerometer_y_axis_skewness"); + const accelerometer_y_axis_deviation = payload.find((item) => item.variable === "accelerometer_y_axis_deviation"); + const accelerometer_y_axis_peak_to_peak_displacement = payload.find((item) => item.variable === "accelerometer_y_axis_peak_to_peak_displacement"); + //x axis + const accelerometer_x_axis_senevent = payload.find((item) => item.variable === "accelerometer_x_axis_senevent"); + const accelerometer_x_axis_crestfactor = payload.find((item) => item.variable === "accelerometer_x_axis_crestfactor"); + const accelerometer_x_axis_kurtosis = payload.find((item) => item.variable === "accelerometer_x_axis_kurtosis"); + const accelerometer_x_axis_rms = payload.find((item) => item.variable === "accelerometer_x_axis_rms"); + const accelerometer_x_axis_peak = payload.find((item) => item.variable === "accelerometer_x_axis_peak"); + const accelerometer_x_axis_oavelocity = payload.find((item) => item.variable === "accelerometer_x_axis_oavelocity"); + const accelerometer_x_axis_skewness = payload.find((item) => item.variable === "accelerometer_x_axis_skewness"); + const accelerometer_x_axis_deviation = payload.find((item) => item.variable === "accelerometer_x_axis_deviation"); + const accelerometer_x_axis_peak_to_peak_displacement = payload.find((item) => item.variable === "accelerometer_x_axis_peak_to_peak_displacement"); + //z axis + const accelerometer_z_axis_senevent = payload.find((item) => item.variable === "accelerometer_z_axis_senevent"); + const accelerometer_z_axis_crestfactor = payload.find((item) => item.variable === "accelerometer_z_axis_crestfactor"); + const accelerometer_z_axis_kurtosis = payload.find((item) => item.variable === "accelerometer_z_axis_kurtosis"); + const accelerometer_z_axis_rms = payload.find((item) => item.variable === "accelerometer_z_axis_rms"); + const accelerometer_z_axis_peak = payload.find((item) => item.variable === "accelerometer_z_axis_peak"); + const accelerometer_z_axis_oavelocity = payload.find((item) => item.variable === "accelerometer_z_axis_oavelocity"); + const accelerometer_z_axis_skewness = payload.find((item) => item.variable === "accelerometer_z_axis_skewness"); + const accelerometer_z_axis_deviation = payload.find((item) => item.variable === "accelerometer_z_axis_deviation"); + const accelerometer_z_axis_peak_to_peak_displacement = payload.find((item) => item.variable === "accelerometer_z_axis_peak_to_peak_displacement"); + + const temphumi_senval = payload.find((item) => item.variable === "temphumi_senval"); + const temphumi_event = payload.find((item) => item.variable === "temphumi_event"); + const temphumi_status = payload.find((item) => item.variable === "temphumi_status"); + const temphumi_range = payload.find((item) => item.variable === "temphumi_range"); + + const accelerometer_time = payload.find((item) => item.variable === "accelerometer_time"); + const accelerometer_logindex = payload.find((item) => item.variable === "accelerometer_logindex"); + const device_batteryvolt = payload.find((item) => item.variable === "device_batteryvolt"); + const device_powersrc = payload.find((item) => item.variable === "device_powersrc"); + const device_events = payload.find((item) => item.variable === "device_events"); + + const parse_error = payload.find((item) => item.variable === "parse_error"); + return { + payload, + accelerometer_y_axis_senevent, + accelerometer_y_axis_crestfactor, + accelerometer_y_axis_kurtosis, + accelerometer_y_axis_rms, + accelerometer_y_axis_peak, + accelerometer_y_axis_oavelocity, + accelerometer_y_axis_skewness, + accelerometer_y_axis_deviation, + accelerometer_y_axis_peak_to_peak_displacement, + accelerometer_x_axis_senevent, + accelerometer_x_axis_crestfactor, + accelerometer_x_axis_kurtosis, + accelerometer_x_axis_rms, + accelerometer_x_axis_peak, + accelerometer_x_axis_oavelocity, + accelerometer_x_axis_peak_to_peak_displacement, + accelerometer_x_axis_skewness, + accelerometer_x_axis_deviation, + accelerometer_z_axis_senevent, + accelerometer_z_axis_crestfactor, + accelerometer_z_axis_kurtosis, + accelerometer_z_axis_rms, + accelerometer_z_axis_peak, + accelerometer_z_axis_oavelocity, + accelerometer_z_axis_peak_to_peak_displacement, + accelerometer_z_axis_skewness, + accelerometer_z_axis_deviation, + temphumi_senval, + temphumi_event, + temphumi_status, + temphumi_range, + accelerometer_time, + accelerometer_logindex, + device_batteryvolt, + device_powersrc, + device_events, + parse_error, + }; +} + +describe("Axis", () => { + test("should decode the payload correctly", () => { + const payloadHex = "817a585008070000001e7800005441e2ff0000100015000f000c0007021b000200040000001f001a001300feff9d011d000200090000001a001a001300f4ff8b01fbff0200070003000000000f6c016660091b00010000106c016696"; + const result = preparePayload(payloadHex); + + expect(result.accelerometer_y_axis_senevent?.value).toBe(0); + expect(result.accelerometer_y_axis_crestfactor?.value).toBe(4.13); + expect(result.accelerometer_y_axis_kurtosis?.value).toBe(-0.02); + expect(result.accelerometer_y_axis_rms?.value).toBe(0.019); + expect(result.accelerometer_y_axis_peak?.value).toBe(0.026); + expect(result.accelerometer_y_axis_oavelocity?.value).toBe(0.31); + expect(result.accelerometer_y_axis_skewness?.value).toBe(0.29); + expect(result.accelerometer_y_axis_deviation?.value).toBe(0.02); + expect(result.accelerometer_y_axis_peak_to_peak_displacement?.value).toBe(9); + + expect(result.accelerometer_x_axis_senevent?.value).toBe(0); + expect(result.accelerometer_x_axis_crestfactor?.value).toBe(5.19); + expect(result.accelerometer_x_axis_kurtosis?.value).toBe(0.12); + expect(result.accelerometer_x_axis_rms?.value).toBe(0.015); + expect(result.accelerometer_x_axis_peak?.value).toBe(0.021); + expect(result.accelerometer_x_axis_oavelocity?.value).toBe(0.16); + expect(result.accelerometer_x_axis_skewness?.value).toBe(0.27); + expect(result.accelerometer_x_axis_deviation?.value).toBe(0.02); + expect(result.accelerometer_x_axis_peak_to_peak_displacement?.value).toBe(4); + + expect(result.accelerometer_z_axis_senevent?.value).toBe(0); + expect(result.accelerometer_z_axis_crestfactor?.value).toBe(3.95); + expect(result.accelerometer_z_axis_kurtosis?.value).toBe(-0.12); + expect(result.accelerometer_z_axis_rms?.value).toBe(0.019); + expect(result.accelerometer_z_axis_peak?.value).toBe(0.026); + expect(result.accelerometer_z_axis_oavelocity?.value).toBe(0.26); + expect(result.accelerometer_z_axis_skewness?.value).toBe(-0.05); + expect(result.accelerometer_z_axis_deviation?.value).toBe(0.02); + expect(result.accelerometer_z_axis_peak_to_peak_displacement?.value).toBe(7); + + expect(result.temphumi_senval?.value).toBe(30.75); + expect(result.temphumi_event?.value).toBe(0); + expect(result.temphumi_status?.value).toBe(0); + expect(result.temphumi_range?.value).toBe(0); + + expect(result.accelerometer_time?.value).toBe(1711369231); + expect(result.accelerometer_logindex?.value).toBe(0); + expect(result.device_batteryvolt?.value).toBe(0); + expect(result.device_powersrc?.value).toBe(1); + expect(result.device_events?.value).toBe(0); + }); +}); + +describe("Shall not be parsed", () => { + let payload = [{ variable: "shallnotpass", value: "04096113950292" }]; + payload = decoderRun(file_path, { payload }); + + test("Output Result", () => { + expect(Array.isArray(payload)).toBe(true); + }); + + test("Not parsed Result", () => { + expect(payload).toEqual([{ variable: "shallnotpass", value: "04096113950292" }]); + }); +}); diff --git a/decoders/connector/advantech/wise-2410/v1.0.0/payload.ts b/decoders/connector/advantech/wise-2410/v1.0.0/payload.ts new file mode 100644 index 00000000..0fddc067 --- /dev/null +++ b/decoders/connector/advantech/wise-2410/v1.0.0/payload.ts @@ -0,0 +1,1277 @@ +/* eslint-disable unicorn/prefer-string-replace-all */ +/* eslint-disable unicorn/no-array-for-each */ +/* eslint-disable unicorn/prefer-string-slice */ +/* eslint-disable unicorn/prefer-date-now */ +/* eslint-disable unicorn/numeric-separators-style */ +/* eslint-disable unicorn/number-literal-case */ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable unicorn/no-lonely-if */ +/* eslint-disable unicorn/prefer-at */ +/* eslint-disable unicorn/prefer-negative-index */ +/* eslint-disable no-undef */ +/* eslint-disable no-redeclare */ +/* eslint-disable curly */ +/* eslint-disable @typescript-eslint/restrict-plus-operands */ +/* eslint-disable no-var */ +// @ts-nocheck + +// Code received from Advantech, therfor after verifying with Vitor, Typescript will not be needed. + +//////////////////////////////////////////////////////////////////////////////// +// Advantech, iSensing SW team +// +// Frame Data Parser for WISE Lora modules (execute in Node-RED) +// +// version: 1.7.1 <2023/09/26> +// +//////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////// +// User defined variables +/////////////////////////////////////// + +// If program is run in NodeRed. True: run in NodeRed, False: not run in NodeRed +var bIsRunNodeRed = false; + +// If MQTT publish message with Femto Gateway. True: publish with Femto Gateway, False: not publish with Femto Gateway +var bIsFemtoGateway = false; + +/////////////////////////////////////// +// User defined variables +/////////////////////////////////////// + +//Min Frame length +const MIN_FRAME_LENGTH = 4; + +//Header +const MASK_HEADER_FIRST_SEGMENT = 0x80; +const MASK_HEADER_ADDRESS_MODE = 0x0c; +const MASK_HEADER_ADDRESS_NONE = 0x00; +const MASK_HEADER_ADDRESS_2_OCTECT = 0x04; +const MASK_HEADER_ADDRESS_8_OCTECT = 0x08; +const MASK_HEADER_FRAME_VERSION = 0x03; + +//Payload Data +//AI +const PAYLOAD_DI_DATA = 0x00; +//DO +const PAYLOAD_DO_DATA = 0x10; +//DI +const PAYLOAD_AI_DATA = 0x30; +//Sensor +const PAYLOAD_SENSOR_DATA = 0x50; +//Device Status +const PAYLOAD_DEVICE_DATA = 0x60; +//Coil data +const PAYLOAD_COIL_DATA = 0x70; +//Register data +const PAYLOAD_REGISTER_DATA = 0x80; + +//DI +const MASK_PAYLOAD_DI_STATUS = 0x01; +const MASK_PAYLOAD_DI_VALUE = 0x02; +const MASK_PAYLOAD_DI_EVENT = 0x04; +const DI_MODE_FREQUENCY = 4; + +//DO +const MASK_PAYLOAD_DO_STATUS = 0x01; +const MASK_PAYLOAD_DO_ABSOLUTE_PULSE_OUTPUT = 0x02; +const MASK_PAYLOAD_DO_INCREMENTAL_PULSE_OUTPUT = 0x04; + +//AI +const MASK_PAYLOAD_AI_STATUS = 0x01; +const MASK_PAYLOAD_AI_RAW_VALUE = 0x02; +const MASK_PAYLOAD_AI_EVENT = 0x04; +const MASK_PAYLOAD_AI_MAX_VALUE = 0x08; +const MASK_PAYLOAD_AI_MIN_VALUE = 0x10; + +const MASK_PAYLOAD_AI_MASK2_RANGE = 0x01; + +//Sensor Range +const MASK_PAYLOAD_SENSOR_TEMP_C_TYPE = 0x00; +const MASK_PAYLOAD_SENSOR_TEMP_F_TYPE = 0x01; +const MASK_PAYLOAD_SENSOR_TEMP_K_TYPE = 0x02; +const MASK_PAYLOAD_SENSOR_HUMIDITY_TYPE = 0x03; +const MASK_PAYLOAD_SENSOR_ACCELERATOR_TYPE_G = 0x04; +const MASK_PAYLOAD_SENSOR_ACCELERATOR_TYPE_MS2 = 0x05; + +const MASK_PAYLOAD_SENSOR_MASK_SENSNSOR_STATUS = 0x01; +const MASK_PAYLOAD_SENSOR_MASK_SENSNSOR_EVENT = 0x02; +const MASK_PAYLOAD_SENSOR_MASK_SENSNSOR_VALUE = 0x04; +const MASK_PAYLOAD_SENSOR_MASK_SENSNSOR_MAX_VALUE = 0x08; +const MASK_PAYLOAD_SENSOR_MASK_SENSNSOR_MIN_VALUE = 0x10; + +const MASK_PAYLOAD_SENSOR_AXIS_X_MASK = 0x01; +const MASK_PAYLOAD_SENSOR_AXIS_Y_MASK = 0x02; +const MASK_PAYLOAD_SENSOR_AXIS_Z_MASK = 0x04; + +const MASK_PAYLOAD_SENSOR_MASK2_LOGINDEX = 0x01; +const MASK_PAYLOAD_SENSOR_MASK2_TIME = 0x02; + +//Sensor Extend Mask +const MASK_PAYLOAD_SENSOR_EXTMASK_VELOCITY = 0x01; +const MASK_PAYLOAD_SENSOR_EXTMASK_PEAK = 0x02; +const MASK_PAYLOAD_SENSOR_EXTMASK_RMS = 0x04; +const MASK_PAYLOAD_SENSOR_EXTMASK_KURTOSIS = 0x08; +const MASK_PAYLOAD_SENSOR_EXTMASK_CRESTFACTOR = 0x10; +const MASK_PAYLOAD_SENSOR_EXTMASK_SKEWNESS = 0x20; +const MASK_PAYLOAD_SENSOR_EXTMASK_STDDEVIATION = 0x40; +const MASK_PAYLOAD_SENSOR_EXTMASK_DISPLACEMENT = 0x80; + +//Massive data +const MASK_PAYLOAD_SENSOR_EXTMASK_B = 0x01; +const MASK_PAYLOAD_SENSOR_EXTMASK_MASSIVE_DATA_INFO = 0x01; +const MASK_PAYLOAD_SENSOR_EXTMASK_MASSIVE_DATA_SEC = 0x02; +const MASK_PAYLOAD_SENSOR_EXTMASK_MASSIVE_DATA_LOG = 0x04; + +//Massive Data Type +const MASK_PAYLOAD_SENSOR_MASSIVE_DATA_TYPE_MASSIVE_TYPE = 0x03; +const MASK_PAYLOAD_SENSOR_MASSIVE_DATA_TYPE_SAMPLE_PER_AXIS = 0x0c; +const MASK_PAYLOAD_SENSOR_MASSIVE_DATA_TYPE_BYTES_PER_SAMPLE = 0x10; +const MASK_PAYLOAD_SENSOR_MASSIVE_DATA_TYPE_MASSIVE_TYPE_FFT = 0x01; + +/////////////////////////////////////// +//Device Status +const MASK_DEVICE_EVENT = 0x01; +const MASK_DEVICE_POWER_SOURCE = 0x02; +const MASK_DEVICE_BATTERY_LEVEL = 0x04; +const MASK_DEVICE_BATTERY_VOLTAGE = 0x08; +const MASK_DEVICE_TIMESTAMP = 0x10; +const MASK_DEVICE_POSITION = 0x20; + +const MASK_DEVICE_POSITION_LATITUDE = 0x02; +const MASK_DEVICE_POSITION_LONGITUDE = 0x01; + +//Coil Data +const MASK_PAYLOAD_COIL_STATUS = 0x01; +const MASK_PAYLOAD_COIL_VALUE = 0x02; +const MASK_PAYLOAD_COIL_MULTI_CH = 0x04; + +//Register Data +const MASK_PAYLOAD_REGISTER_STATUS = 0x01; +const MASK_PAYLOAD_REGISTER_VALUE = 0x02; +const MASK_PAYLOAD_REGISTER_MULTI_CH = 0x04; + +/////////////////////////////////////// +// Variables +//////////////////////////////////////////// + +//input data is hex string + +var version; +var payload_mac; + +//If program executes in NodeRed, get input data from msg.payload.data +if (bIsRunNodeRed) { + if (bIsFemtoGateway) { + payloadHex = msg.payload[0].data; + payload_mac = msg.payload[0].macAddr; + } else { + payloadHex = msg.payload.data; + if (msg.payload.macAddr) { + payload_mac = msg.payload.macAddr; + } else { + payload_mac = ""; + } + } +} else { + var msg = {}; + msg.payload = ""; +} + +/* 8bit-CRC: 0x07 = x8 + x2 + x + 1 */ +var au8CRC8_Pol07_Table = [ + 0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d, 0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65, 0x48, 0x4f, 0x46, 0x41, 0x54, + 0x53, 0x5a, 0x5d, 0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd, 0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8, 0xaf, + 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd, 0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2, 0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea, 0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, + 0xa2, 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a, 0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a, 0x57, 0x50, 0x59, 0x5e, + 0x4b, 0x4c, 0x45, 0x42, 0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, 0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c, 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4, 0xf9, + 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, 0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4, 0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c, 0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, + 0x43, 0x44, 0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34, 0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 0x76, 0x71, 0x78, + 0x7f, 0x6a, 0x6d, 0x64, 0x63, 0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b, 0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, 0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb, + 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83, 0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb, 0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3, +]; + +//////////////////////////////////////////// +// Functions +//////////////////////////////////////////// + +function transformData(data, parentKey = "") { + let transformed = []; + + Object.keys(data).forEach((key) => { + // Process the key to be in lowercase and replace spaces with underscores + const processedKey = key.toLowerCase().replace(/\s+/g, "_"); + // Initially form the newKey, then replace "." and "-" with "_" + let newKey = parentKey ? `${parentKey}.${processedKey}` : processedKey; + // Replace "." and "-" with "_" in the newKey + newKey = newKey.replace(/[.-]/g, "_"); + + if (typeof data[key] === "object" && data[key] !== null && !Array.isArray(data[key])) { + // Recursively call transformData for nested objects + transformed = transformed.concat(transformData(data[key], newKey)); + } else { + // Push the processed key and value to the transformed array + transformed.push({ variable: newKey, value: data[key] }); + } + }); + + return transformed; +} + +if (!bIsRunNodeRed) { + var node = function () {}; + node.warn = function (arg) { + console.log(arg); + }; + node.error = function (arg) { + console.error(arg); + }; +} + +function addZero(i) { + i = i + ""; + if (i.length < 2) { + i = "0" + i; + } + return i; +} + +function translateInt32(a, b, c, d) { + return (d << 24) + (c << 16) + (b << 8) + a; +} + +function translateInt24(a, b, c) { + return (c << 16) + (b << 8) + a; +} + +function translateInt16(a, b) { + return a + (b << 8); +} + +function convertMaskToArray(number, channelCount) { + var biArray = []; + var temp; + for (var i = 0; i < channelCount; ++i) { + temp = number; + temp = temp >> i; + biArray.push(temp & 1); + } + return biArray; +} + +function convertToSignedInt16(number) { + if ((number & 0x8000) > 0) { + number = number - 0x10000; + } + return number; +} + +function convertToSignedInt32(number) { + if ((number & 0x80000000) > 0) { + number = number - 0x100000000; + } + return number; +} + +function parseAxisData(index, bIsSensorEventExist, extMask, jsonObj, range) { + if (bIsSensorEventExist) { + jsonObj.SenEvent = translateInt16(hexArr[index++], hexArr[index++]); + } + if (extMask & MASK_PAYLOAD_SENSOR_EXTMASK_VELOCITY) { + jsonObj.OAVelocity = translateInt16(hexArr[index++], hexArr[index++]) / 100; + } + if (extMask & MASK_PAYLOAD_SENSOR_EXTMASK_PEAK) { + if (range === MASK_PAYLOAD_SENSOR_ACCELERATOR_TYPE_G) { + jsonObj.Peak = translateInt16(hexArr[index++], hexArr[index++]) / 1000; + } else { + jsonObj.Peak = translateInt16(hexArr[index++], hexArr[index++]) / 100; + } + } + if (extMask & MASK_PAYLOAD_SENSOR_EXTMASK_RMS) { + if (range === MASK_PAYLOAD_SENSOR_ACCELERATOR_TYPE_G) { + jsonObj.RMS = translateInt16(hexArr[index++], hexArr[index++]) / 1000; + } else { + jsonObj.RMS = translateInt16(hexArr[index++], hexArr[index++]) / 100; + } + } + if (extMask & MASK_PAYLOAD_SENSOR_EXTMASK_KURTOSIS) { + jsonObj.Kurtosis = convertToSignedInt16(translateInt16(hexArr[index++], hexArr[index++])) / 100; + } + if (extMask & MASK_PAYLOAD_SENSOR_EXTMASK_CRESTFACTOR) { + jsonObj.CrestFactor = convertToSignedInt16(translateInt16(hexArr[index++], hexArr[index++])) / 100; + } + if (extMask & MASK_PAYLOAD_SENSOR_EXTMASK_SKEWNESS) { + jsonObj.Skewness = convertToSignedInt16(translateInt16(hexArr[index++], hexArr[index++])) / 100; + } + if (extMask & MASK_PAYLOAD_SENSOR_EXTMASK_STDDEVIATION) { + jsonObj.Deviation = convertToSignedInt16(translateInt16(hexArr[index++], hexArr[index++])) / 100; + } + if (extMask & MASK_PAYLOAD_SENSOR_EXTMASK_DISPLACEMENT) { + jsonObj["Peak-to-Peak Displacement"] = translateInt16(hexArr[index++], hexArr[index++]); + } + + return index; +} + +function DIParse(index) { + var length; + var mode = hexArr[index++] & 0x0f; + + if (version > 0) { + length = hexArr[index++]; + } + + var channel = hexArr[index++]; + if (version > 0) length -= 1; // channel index and mask + var channelIndex = (channel & 0xe0) >> 5; + var channelMask = channel & 0x07; + + message["DI" + channelIndex] = {}; + + if (channelMask & MASK_PAYLOAD_DI_STATUS) { + var arrBinary = convertMaskToArray(hexArr[index++], 8); + if (version > 0) length -= 1; + + message["DI" + channelIndex].status = {}; + message["DI" + channelIndex].status["Signal Logic"] = arrBinary[0]; + message["DI" + channelIndex].status["Start Counter"] = arrBinary[1]; + message["DI" + channelIndex].status["Get/Clean Counter Overflow"] = arrBinary[2]; + // message['DI'+channelIndex].status['Clean Counter Status'] = arrBinary[3]; + message["DI" + channelIndex].status["Get/Clean L2H Latch"] = arrBinary[4]; + message["DI" + channelIndex].status["Get/Clean H2L Latch"] = arrBinary[5]; + } + + message["DI" + channelIndex].mode = mode; + + if (channelMask & MASK_PAYLOAD_DI_VALUE) { + if (mode == DI_MODE_FREQUENCY) { + // frequency mode + message["DI" + channelIndex].Frequency_Value = translateInt32(hexArr[index++], hexArr[index++], hexArr[index++], hexArr[index++]); + } else { + message["DI" + channelIndex].Counter_Value = translateInt32(hexArr[index++], hexArr[index++], hexArr[index++], hexArr[index++]); + } + if (version > 0) length -= 4; + } + + if (channelMask & MASK_PAYLOAD_DI_EVENT) { + message["DI" + channelIndex].Event = hexArr[index++]; + if (version > 0) length -= 1; + } + + if (version > 0) { + if (length > 0) { + index += length; + } + } + + return index; +} + +function DOParse(index) { + var length; + var mode = hexArr[index++] & 0x0f; + + if (version > 0) { + length = hexArr[index++]; + } + + var channel = hexArr[index++]; + if (version > 0) length -= 1; // channel index and mask + var channelIndex = (channel & 0xe0) >> 5; + var channelMask = channel & 0x07; + + message["DO" + channelIndex] = {}; + + var modeText = ""; + switch (mode) { + case 0: + modeText = "DO"; + break; + case 1: + modeText = "Pulse output"; + break; + case 2: + modeText = "Low to High delay"; + break; + case 3: + modeText = "High to Low delay"; + break; + case 4: + modeText = "AI alarm drive"; + break; + } + message["DO" + channelIndex].Mode = modeText; + + if (channelMask & MASK_PAYLOAD_DO_STATUS) { + var status = convertMaskToArray(hexArr[index++], 8); + if (version > 0) length -= 1; + message["DO" + channelIndex].status = {}; + message["DO" + channelIndex].status["Signal Logic"] = status[0]; + message["DO" + channelIndex].status["Pulse Output Continue"] = status[1]; + } + if (mode == 1) { + message["DO" + channelIndex].PulsAbs = 0; + message["DO" + channelIndex].PulsInc = 0; + } + if (channelMask & MASK_PAYLOAD_DO_ABSOLUTE_PULSE_OUTPUT) { + message["DO" + channelIndex].PulsAbs = translateInt32(hexArr[index++], hexArr[index++], hexArr[index++], hexArr[index++]); + if (version > 0) length -= 4; + } + if (channelMask & MASK_PAYLOAD_DO_INCREMENTAL_PULSE_OUTPUT) { + message["DO" + channelIndex].PulsInc = translateInt32(hexArr[index++], hexArr[index++], hexArr[index++], hexArr[index++]); + if (version > 0) length -= 4; + } + + if (version > 0) { + if (length > 0) { + index += length; + } + } + + return index; +} + +function AIParse(index) { + var length; + var range = hexArr[index++] & 0x0f; + + if (version > 0) { + length = hexArr[index++]; + } + + var channel = hexArr[index++]; + if (version > 0) length -= 1; // channel index and mask + var channelIndex = (channel & 0xe0) >> 5; + var channelMask = channel & 0x1f; + + message["AI" + channelIndex] = {}; + message["AI" + channelIndex].Range = range; + + if (channelMask & MASK_PAYLOAD_AI_STATUS) { + var status = convertMaskToArray(hexArr[index++], 8); + if (version > 0) length -= 1; + message["AI" + channelIndex].status = {}; + message["AI" + channelIndex].status["Low Alarm"] = status[0]; + message["AI" + channelIndex].status["High Alarm"] = status[1]; + } + if (channelMask & MASK_PAYLOAD_AI_RAW_VALUE) { + message["AI" + channelIndex]["Raw Data"] = translateInt16(hexArr[index++], hexArr[index++]); + if (version > 0) length -= 2; + } + if (channelMask & MASK_PAYLOAD_AI_EVENT) { + message["AI" + channelIndex].Event = translateInt16(hexArr[index++], hexArr[index++]); + if (version > 0) length -= 2; + } + if (channelMask & MASK_PAYLOAD_AI_MAX_VALUE) { + message["AI" + channelIndex].MaxVal = translateInt16(hexArr[index++], hexArr[index++]); + if (version > 0) length -= 2; + } + if (channelMask & MASK_PAYLOAD_AI_MIN_VALUE) { + message["AI" + channelIndex].MinVal = translateInt16(hexArr[index++], hexArr[index++]); + if (version > 0) length -= 2; + } + + if (version > 0 && length > 0) { + var mask2 = hexArr[index++]; + length -= 1; + if (mask2 & MASK_PAYLOAD_AI_MASK2_RANGE) { + message["AI" + channelIndex].Range = hexArr[index++]; + length -= 1; + } + if (length > 0) { + index += length; + } + } + + return index; +} + +function sensorParse(index) { + var length; + var range = hexArr[index] & 0x0f; + //Temperature/Humidity + if ( + range === MASK_PAYLOAD_SENSOR_TEMP_C_TYPE || + range === MASK_PAYLOAD_SENSOR_TEMP_F_TYPE || + range === MASK_PAYLOAD_SENSOR_TEMP_K_TYPE || + range === MASK_PAYLOAD_SENSOR_HUMIDITY_TYPE + ) { + if (version > 0) { + index++; + length = hexArr[index]; + } + + message.TempHumi = {}; + message.TempHumi.Range = range; + index++; + //message.TempHumi.ChIdx = hexArr[index] & 0xE0; + mask = hexArr[index] & 0x1f; + if (version > 0) length -= 1; // channel index and mask + index++; + + //if sensor status exist + if (mask & MASK_PAYLOAD_SENSOR_MASK_SENSNSOR_STATUS) { + message.TempHumi.Status = hexArr[index++]; + if (version > 0) length -= 1; + } + //if sensor event exist + if (mask & MASK_PAYLOAD_SENSOR_MASK_SENSNSOR_EVENT) { + message.TempHumi.Event = translateInt16(hexArr[index++], hexArr[index++]); + if (version > 0) length -= 2; + } + //if sensor value exist + if (mask & MASK_PAYLOAD_SENSOR_MASK_SENSNSOR_VALUE) { + if (range === MASK_PAYLOAD_SENSOR_HUMIDITY_TYPE) { + message.TempHumi.SenVal = translateInt32(hexArr[index++], hexArr[index++], hexArr[index++], hexArr[index++]) / 1000; + } else { + message.TempHumi.SenVal = convertToSignedInt32(translateInt32(hexArr[index++], hexArr[index++], hexArr[index++], hexArr[index++])) / 1000; + } + if (version > 0) length -= 4; + } + //if sensor MAX value exist + if (mask & MASK_PAYLOAD_SENSOR_MASK_SENSNSOR_MAX_VALUE) { + message.TempHumi.SenMaxVal = translateInt32(hexArr[index++], hexArr[index++], hexArr[index++], hexArr[index++]) / 100; + if (version > 0) length -= 4; + } + //if sensor MIN value exist + if (mask & MASK_PAYLOAD_SENSOR_MASK_SENSNSOR_MIN_VALUE) { + message.TempHumi.SenMinVal = translateInt32(hexArr[index++], hexArr[index++], hexArr[index++], hexArr[index++]) / 100; + if (version > 0) length -= 4; + } + + if (version > 0) { + // reserved + // var mask2 = hexArr[index++]; + if (length > 0) { + index += length; + } + } + } + if (range === MASK_PAYLOAD_SENSOR_ACCELERATOR_TYPE_G || range === MASK_PAYLOAD_SENSOR_ACCELERATOR_TYPE_MS2) { + bIsSensorEventExist = false; + + if (version > 0) { + index++; + var length = hexArr[index]; + } + + index++; + axisMask = (hexArr[index] & 0xe0) >> 5; + + var arrAxisMask = convertMaskToArray(axisMask, 8); + var intAxisMaskEnable = 0; + arrAxisMask.forEach(function (item) { + if (item == 1) { + intAxisMaskEnable++; + } + }); + + mask = hexArr[index] & 0x1f; + index++; + extMask = hexArr[index]; //extend mask + + var arrExtMask = convertMaskToArray(extMask, 8); + var intExtMaskEnable = 0; + arrExtMask.forEach(function (item) { + if (item == 1) { + intExtMaskEnable++; + } + }); + + if (!(mask & MASK_PAYLOAD_SENSOR_EXTMASK_B)) { + message.Accelerometer = {}; + + //if sensor event exist + if (mask & MASK_PAYLOAD_SENSOR_MASK_SENSNSOR_EVENT) { + bIsSensorEventExist = true; + } + index++; + + if (axisMask & MASK_PAYLOAD_SENSOR_AXIS_X_MASK) { + message.Accelerometer["X-Axis"] = {}; + index = parseAxisData(index, bIsSensorEventExist, extMask, message.Accelerometer["X-Axis"], range); + } + if (axisMask & MASK_PAYLOAD_SENSOR_AXIS_Y_MASK) { + message.Accelerometer["Y-Axis"] = {}; + index = parseAxisData(index, bIsSensorEventExist, extMask, message.Accelerometer["Y-Axis"], range); + } + if (axisMask & MASK_PAYLOAD_SENSOR_AXIS_Z_MASK) { + message.Accelerometer["Z-Axis"] = {}; + index = parseAxisData(index, bIsSensorEventExist, extMask, message.Accelerometer["Z-Axis"], range); + } + + length = length - 2 - intAxisMaskEnable * (intExtMaskEnable * 2 + (bIsSensorEventExist ? 2 : 0)); // Length - (Axis Mask + Mask) - Extend Mask A - Axis Data + message.Accelerometer.LogIndex = 0; + if (version > 0 && length > 0) { + var mask2 = hexArr[index++]; + length -= 1; + if (mask2 & MASK_PAYLOAD_SENSOR_MASK2_LOGINDEX) { + message.Accelerometer.LogIndex = translateInt32(hexArr[index++], hexArr[index++], hexArr[index++], hexArr[index++]); + length -= 4; + } + if (mask2 & MASK_PAYLOAD_SENSOR_MASK2_TIME) { + message.Accelerometer.Time = translateInt32(hexArr[index++], hexArr[index++], hexArr[index++], hexArr[index++]); + length -= 4; + } + if (length > 0) { + index += length; + } + } + } else { + // extend mask B + index++; + if (extMask & MASK_PAYLOAD_SENSOR_EXTMASK_MASSIVE_DATA_INFO) { + var dataType = hexArr[index++]; + var sampleRate = translateInt24(hexArr[index++], hexArr[index++], hexArr[index++]); + var points = translateInt16(hexArr[index++], hexArr[index++]); + var logIndex = translateInt32(hexArr[index++], hexArr[index++], hexArr[index++], hexArr[index++]); + var timestamp = translateInt32(hexArr[index++], hexArr[index++], hexArr[index++], hexArr[index++]); + var totalLength = translateInt32(hexArr[index++], hexArr[index++], hexArr[index++], hexArr[index++]); + var massType = dataType & MASK_PAYLOAD_SENSOR_MASSIVE_DATA_TYPE_MASSIVE_TYPE; + var bytesPerSample = (dataType & MASK_PAYLOAD_SENSOR_MASSIVE_DATA_TYPE_BYTES_PER_SAMPLE) >> 4 > 0 ? 4 : 2; + var samplesPerAxis = + massType == MASK_PAYLOAD_SENSOR_MASSIVE_DATA_TYPE_MASSIVE_TYPE_FFT && (dataType & MASK_PAYLOAD_SENSOR_MASSIVE_DATA_TYPE_SAMPLE_PER_AXIS) >> 2 > 0 + ? points / 2.56 / 2 + : points / 2.56; + var bytesPerAxis = bytesPerSample * samplesPerAxis; + + // length = length - Massive Info + length = length - 18; + + var objData = {}; + objData.timestamp = timestamp; + objData.lastSeq = hexArr[1]; + objData.lastPayload = hexArr; + objData.logIndex = logIndex; + objData.sampleRate = sampleRate; + objData.points = points; + objData.bytesPerSample = bytesPerSample; + objData.samplesPerAxis = samplesPerAxis; + objData.bytesPerAxis = bytesPerAxis; + objData.totalLength = totalLength; + } + if (extMask & MASK_PAYLOAD_SENSOR_EXTMASK_MASSIVE_DATA_SEC) { + var FFTDataStorage = JSON.parse(); + if (typeof FFTDataStorage.timestamp == "undefined") { + throw "FFT Data lost first packet."; + } + + var axisType = ["X", "Y", "Z"]; + if (!(axisMask & MASK_PAYLOAD_SENSOR_AXIS_X_MASK)) { + var axisIndex = axisType.indexOf("X"); + if (axisIndex > -1) { + axisType.splice(axisIndex, 1); + } + } + if (!(axisMask & MASK_PAYLOAD_SENSOR_AXIS_Y_MASK)) { + var axisIndex = axisType.indexOf("Y"); + if (axisIndex > -1) { + axisType.splice(axisIndex, 1); + } + } + if (!(axisMask & MASK_PAYLOAD_SENSOR_AXIS_Z_MASK)) { + var axisIndex = axisType.indexOf("Z"); + if (axisIndex > -1) { + axisType.splice(axisIndex, 1); + } + } + + var logIndex = translateInt32(hexArr[index++], hexArr[index++], hexArr[index++], hexArr[index++]); + var initialOffset = translateInt32(hexArr[index++], hexArr[index++], hexArr[index++], hexArr[index++]); + var offset = initialOffset; + // length = length - (Axis Mask + Mask + Extend Mask + Log Index + Offset) + length = length - 10; + + message.FFT = {}; + if (!(extMask & MASK_PAYLOAD_SENSOR_EXTMASK_MASSIVE_DATA_INFO)) { + if (FFTDataStorage.lastSeq === hexArr[1]) { + throw "Packet of FFT Data duplicated."; + } + + if ((("0x" + (FFTDataStorage.lastSeq + 1).toString(16)) & 0xff) !== hexArr[1]) { + // lost packet + var lastPayload = FFTDataStorage.lastPayload; + var lastOffset; + if (lastPayload[6] & MASK_PAYLOAD_SENSOR_EXTMASK_MASSIVE_DATA_INFO) { + lastOffset = translateInt32(lastPayload[29], lastPayload[30], lastPayload[31], lastPayload[32]) + lastPayload[4] - 28; + } else { + lastOffset = translateInt32(lastPayload[11], lastPayload[12], lastPayload[13], lastPayload[14]) + lastPayload[4] - 10; + } + + if (logIndex != FFTDataStorage.logIndex) { + // previous FFT Data lost packet and next FFT Data lost first packet + var fillLength = FFTDataStorage.bytesPerAxis * intAxisMaskEnable - 1 - lastOffset; + var logIndex = FFTDataStorage.logIndex; + + var objData = {}; + objData.LOG_INDEX = logIndex; + objData.BYTE_OFFSET = lastOffset; + objData.LENGTH = fillLength; + throw "FFT Data lost first packet."; + } + + var fillLength = offset - lastOffset; + var logIndex = FFTDataStorage.logIndex; + + lostPacketInfo.LOG_INDEX = logIndex; + lostPacketInfo.BYTE_OFFSET = lastOffset; + lostPacketInfo.LENGTH = fillLength; + } + } + + var timestamp = FFTDataStorage.timestamp; + var logIndex = FFTDataStorage.logIndex; + var sampleRate = FFTDataStorage.sampleRate; + var points = FFTDataStorage.points; + var bytesPerSample = FFTDataStorage.bytesPerSample; + var samplesPerAxis = FFTDataStorage.samplesPerAxis; + var bytesPerAxis = FFTDataStorage.bytesPerAxis; + var totalLength = FFTDataStorage.totalLength; + message.FFT.LOG_INDEX = logIndex; + message.FFT.TIME = timestamp; + message.FFT.SAMPLING_RATE = sampleRate; + message.FFT.NUMBER_OF_SAMPLES = points; + message.FFT.START_BYTE_OFFSET = offset; + var axisData = {}; + csvMessage = '"TIME","AXIS_TYPE","DATA","LOG_INDEX","BYTE_OFFSET","SAMPLE_FREQ"\n'; + for (var i = 0; i < length / bytesPerSample; i++) { + var axis = offset < bytesPerAxis ? axisType[0] : offset < bytesPerAxis * 2 ? axisType[1] : axisType[2]; + var data = bytesPerSample == 2 ? translateInt16(hexArr[index++], hexArr[index++]) : translateInt32(hexArr[index++], hexArr[index++], hexArr[index++], hexArr[index++]); + var sampleIndex = (offset % bytesPerAxis) / bytesPerSample; // by axis + // sampleFreq = sampleIndex * sampling rate / number of samples + var sampleFreq = sampleIndex * (sampleRate / points); + + if (typeof axisData[axis] == "undefined") { + axisData[axis] = {}; + axisData[axis].AXIS_TYPE = axis; + axisData[axis].START_SAMPLE_INDEX = sampleIndex; + axisData[axis].END_SAMPLE_INDEX = + offset % bytesPerAxis >= (initialOffset + length) % bytesPerAxis ? samplesPerAxis - 1 : ((initialOffset + length) % bytesPerAxis) / bytesPerSample - 1; + axisData[axis].DATA = []; + } + axisData[axis].DATA.push(data); + csvMessage += timestamp + "," + axis + "," + data + "," + logIndex + "," + offset + "," + Math.floor(sampleFreq * 1000) / 1000 + "\n"; + + offset += bytesPerSample; + if (offset >= totalLength) { + index = index + (length / bytesPerSample - i - 1) * bytesPerSample; + break; + } + } + message.FFT.END_BYTE_OFFSET = offset - 1; + message.FFT.AXIS_DATA = []; + for (i in axisData) { + message.FFT.AXIS_DATA.push(axisData[i]); + } + axisData = {}; + + if (offset != bytesPerAxis * intAxisMaskEnable) { + FFTDataStorage.lastSeq = hexArr[1]; + FFTDataStorage.lastPayload = hexArr; + } else { + var objData = {}; + } + } + // TBD + // if (extMask & MASK_PAYLOAD_SENSOR_EXTMASK_MASSIVE_DATA_LOG) { + + // } + } + } + + return index; +} + +function deviceParse(index) { + var length; + message.Device = {}; + index++; + if (version > 0) { + length = hexArr[index++]; + } + mask = hexArr[index++]; + if (version > 0) length -= 1; // mask + + if (mask & MASK_DEVICE_EVENT) { + message.Device.Events = hexArr[index++]; + if (version > 0) length -= 1; + } + if (mask & MASK_DEVICE_POWER_SOURCE) { + message.Device.PowerSrc = hexArr[index++]; + if (version > 0) length -= 1; + } + if (mask & MASK_DEVICE_BATTERY_LEVEL) { + message.Device.BatteryLevel = hexArr[index++]; + if (version > 0) length -= 1; + } + if (mask & MASK_DEVICE_BATTERY_VOLTAGE) { + message.Device.BatteryVolt = translateInt16(hexArr[index++], hexArr[index++]); + if (version > 0) length -= 2; + } + if (mask & MASK_DEVICE_TIMESTAMP) { + message.Device.Time = translateInt32(hexArr[index++], hexArr[index++], hexArr[index++], hexArr[index++]); + if (version > 0) length -= 4; + } + if (mask & MASK_DEVICE_POSITION) { + message.Device.GNSS = {}; + var latitudeStr = ""; + var longitudeStr = ""; + if (hexArr[index] & MASK_DEVICE_POSITION_LATITUDE) { + latitudeStr = "S"; + } else { + latitudeStr = "N"; + } + if (hexArr[index] & MASK_DEVICE_POSITION_LONGITUDE) { + longitudeStr = "W"; + } else { + longitudeStr = "E"; + } + index++; + + message.Device.GNSS.Latitude = (translateInt24(hexArr[index++], hexArr[index++], hexArr[index++]) / 100000).toFixed(5) + " " + latitudeStr; + message.Device.GNSS.Longitude = (translateInt24(hexArr[index++], hexArr[index++], hexArr[index++]) / 100000).toFixed(5) + " " + longitudeStr; + if (version > 0) length -= 7; + } + + if (version > 0) { + if (length > 0) { + index += length; + } + } + + return index; +} + +function coilParse(index) { + var length; + var mask = hexArr[index++] & 0x07; + + if (version > 0) { + length = hexArr[index++]; + } + + var channel = hexArr[index++]; + if (version > 0) length -= 1; // port and channel index + var port = (channel & 0x80) >> 7; + + if (mask & MASK_PAYLOAD_COIL_MULTI_CH) { + var infoLen = channel & 0x7f; + var recordLen = hexArr[index++]; + var dataMask = hexArr[index++]; + var i, + j, + k, + maskGroup, + chMask, + ch = 0; + var isSupportStatus = (dataMask & MASK_PAYLOAD_COIL_STATUS) == MASK_PAYLOAD_COIL_STATUS; + var isSupportData = (dataMask & MASK_PAYLOAD_COIL_VALUE) == MASK_PAYLOAD_COIL_VALUE; + + for (i = 1; i <= infoLen - 2; i++) { + maskGroup = hexArr[index++]; + i++; + for (j = 0; j < 7; j++) { + if ((maskGroup & (1 << j)) == 0) { + ch += 8; + continue; + } + chMask = hexArr[index++]; + i++; + for (k = 0; k < 8; k++) { + if ((chMask & (1 << k)) == 0) { + ch += 1; + continue; + } + message["RtuCoil" + port + "-" + ch] = {}; + ch += 1; + } + } + } + if (version > 0) length -= infoLen; + + for (i = 0; i < ch; i++) { + if (typeof message["RtuCoil" + port + "-" + i] != "undefined") { + if (isSupportStatus) { + message["RtuCoil" + port + "-" + i].Status = hexArr[index++]; + if (version > 0) length -= 1; + } + if (isSupportData) { + message["RtuCoil" + port + "-" + i].Data = hexArr[index++]; + if (version > 0) length -= 1; + } + } + } + } else { + var channelIndex = channel & 0x7f; + + message["RtuCoil" + port + "-" + channelIndex] = {}; + + if (mask & MASK_PAYLOAD_COIL_STATUS) { + message["RtuCoil" + port + "-" + channelIndex].Status = hexArr[index++]; + if (version > 0) length -= 1; + } + if (mask & MASK_PAYLOAD_COIL_VALUE) { + message["RtuCoil" + port + "-" + channelIndex].Data = hexArr[index++]; + if (version > 0) length -= 1; + } + } + + if (version > 0) { + if (length > 0) { + index += length; + } + } + + return index; +} + +function registerParse(index) { + var length; + var mask = hexArr[index++] & 0x07; + + if (version > 0) { + length = hexArr[index++]; + } + + var channel = hexArr[index++]; + if (version > 0) length -= 1; // port and channel index + var port = (channel & 0x80) >> 7; + + if (mask & MASK_PAYLOAD_REGISTER_MULTI_CH) { + var infoLen = channel & 0x7f; + var recordLen = hexArr[index++]; + var dataMask = hexArr[index++]; + var i, + j, + k, + maskGroup, + chMask, + ch = 0; + var isSupportStatus = (dataMask & MASK_PAYLOAD_REGISTER_STATUS) == MASK_PAYLOAD_REGISTER_STATUS; + var isSupportData = (dataMask & MASK_PAYLOAD_REGISTER_VALUE) == MASK_PAYLOAD_REGISTER_VALUE; + + for (i = 1; i <= infoLen - 2; i++) { + maskGroup = hexArr[index++]; + i++; + for (j = 0; j < 7; j++) { + if ((maskGroup & (1 << j)) == 0) { + ch += 8; + continue; + } + chMask = hexArr[index++]; + i++; + for (k = 0; k < 8; k++) { + if ((chMask & (1 << k)) == 0) { + ch += 1; + continue; + } + message["RtuRegister" + port + "-" + ch] = {}; + ch += 1; + } + } + } + if (version > 0) length -= infoLen; + + for (i = 0; i < ch; i++) { + if (typeof message["RtuRegister" + port + "-" + i] != "undefined") { + if (isSupportStatus) { + message["RtuRegister" + port + "-" + i].Status = hexArr[index++]; + if (version > 0) length -= 1; + } + if (isSupportData) { + message["RtuRegister" + port + "-" + i].Data = translateInt16(hexArr[index++], hexArr[index++]); + if (version > 0) length -= 2; + } + } + } + } else { + var channelIndex = channel & 0x7f; + + message["RtuRegister" + port + "-" + channelIndex] = {}; + + if (mask & MASK_PAYLOAD_REGISTER_STATUS) { + message["RtuRegister" + port + "-" + channelIndex].Status = hexArr[index++]; + if (version > 0) length -= 1; + } + if (mask & MASK_PAYLOAD_REGISTER_VALUE) { + message["RtuRegister" + port + "-" + channelIndex].Data = translateInt16(hexArr[index++], hexArr[index++]); + if (version > 0) length -= 2; + } + } + + if (version > 0) { + if (length > 0) { + index += length; + } + } + + return index; +} + +function parsePayLoad(index) { + //var bIsDataParsed = false; + var axisMask = ""; + var mask = ""; + var extMask = ""; //extend mask + var bIsSensorEventExist = false; + + //DI + if ((hexArr[index] & 0xf0) === PAYLOAD_DI_DATA) { + index = DIParse(index); + + if (index < arrLength - 1) { + //1: ignore CRC + parsePayLoad(index); + } + } + + //DO + else if ((hexArr[index] & 0xf0) === PAYLOAD_DO_DATA) { + index = DOParse(index); + + if (index < arrLength - 1) { + //1: ignore CRC + parsePayLoad(index); + } + } + + //AI + else if ((hexArr[index] & 0xf0) === PAYLOAD_AI_DATA) { + index = AIParse(index); + + if (index < arrLength - 1) { + //1: ignore CRC + parsePayLoad(index); + } + } + + //Sensor Type + else if ((hexArr[index] & 0xf0) === PAYLOAD_SENSOR_DATA) { + index = sensorParse(index); + + if (index < arrLength - 1) { + //1: ignore CRC + parsePayLoad(index); + } + } + + //Device Status + else if ((hexArr[index] & 0xf0) === PAYLOAD_DEVICE_DATA) { + index = deviceParse(index); + + if (index < arrLength - 1) { + //1: ignore CRC + parsePayLoad(index); + } + } + + //Coil Data + else if ((hexArr[index] & 0xf0) === PAYLOAD_COIL_DATA) { + index = coilParse(index); + + if (index < arrLength - 1) { + //1: ignore CRC + parsePayLoad(index); + } + } + + //Register Data + else if ((hexArr[index] & 0xf0) === PAYLOAD_REGISTER_DATA) { + index = registerParse(index); + + if (index < arrLength - 1) { + //1: ignore CRC + parsePayLoad(index); + } + } +} + +function getSourceAddressLength(address) { + var addressLength = 0; + + if (address != "" && address != null) { + addressLength = address.length / 2; + } + + return addressLength; +} + +function checkFrameLength() { + var addressLength = getSourceAddressLength(message.SourceAddress); + + if (message.TotalLength + addressLength + 4 != arrLength) { + //4: Frame control + Sequence number + length + CRC + return false; + } else { + return true; + } +} + +function CrcCalc(u8Arr, u16Length) { + var u16i; + var u8CRC = 0xff; + + for (u16i = 0; u16i < u16Length; u16i++) { + u8CRC = au8CRC8_Pol07_Table[u8CRC ^ u8Arr[u16i]]; + } + return u8CRC; +} + +function checkPayloadLengthAndSetStorage(hexArr, sequence) { + var sourceAddressLen = 0; + if ((hexArr[0] & MASK_HEADER_ADDRESS_MODE) === MASK_HEADER_ADDRESS_2_OCTECT) { + sourceAddressLen = 2; + } else if ((hexArr[0] & MASK_HEADER_ADDRESS_MODE) === MASK_HEADER_ADDRESS_8_OCTECT) { + sourceAddressLen = 8; + } + // (Octet)packet length - Frame Control - Frame Sequence Number - Total Length - Source Address - CRC !== payload length + if (hexArr.length - 1 - 1 - 1 - sourceAddressLen - 1 !== hexArr[2]) { + var objData = {}; + objData.sequence = sequence !== null ? sequence : hexArr[1]; + objData.time = new Date().getTime(); + objData.payload = hexArr; + + return false; + } else { + return true; + } +} + +const WISE2410Payloadd = payload.find((x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data" || x.variable === "payload_hex"); + +//////////////////////////////////////////// +// Main +//////////////////////////////////////////// +if (WISE2410Payloadd) { + payloadHex = WISE2410Payloadd.value; + var message = {}; //output of this program + var csvMessage = ""; // csv data output for MQTT publish + var lostPacketInfo = {}; // data for packet re-transmission + + var i, arrLength; + var hexArr = []; //translated hex arry from input string + //var payload = {}; + var hexPayloadArr = []; + var arrayIndex = 0; //index of current processing position in hexArr + + arrLength = payloadHex.length; + + try { + if (arrLength < MIN_FRAME_LENGTH || arrLength % 2 !== 0) { + msg.payload = "received frame length error"; + } + + //parse hex string to array + arrLength = arrLength / 2; + + for (i = 0; i < arrLength; i++) { + hexArr.push(parseInt(payloadHex.substring(i * 2, i * 2 + 2), 16)); //parse hex + } + + // check frame structure version + version = hexArr[0] & MASK_HEADER_FRAME_VERSION; + + //check if this is first segment + if (!(hexArr[0] & MASK_HEADER_FIRST_SEGMENT)) { + // packet reassemble + + if (payloadStorage.sequence === hexArr[1]) { + msg.payload = "Sequence number repeat. Drop this packet."; + } + + if (typeof payloadStorage.sequence == "undefined" || (("0x" + (payloadStorage.sequence + 1).toString(16)) & 0xff) !== hexArr[1]) { + msg.payload = "Sequence number error. Packet may be lost."; + } + + if (typeof payloadStorage.time == "undefined" || new Date().getTime() - payloadStorage.time > 60000) { + msg.payload = "Timeout."; + } + + var currentSeq = hexArr[1]; + + hexArr = payloadStorage.payload.concat(hexArr.slice(2, hexArr.length)); + } + + arrLength = hexArr.length; + if (bIsFemtoGateway) { + message.mac = payload_mac; + } + //get sequence number + message.SequenceNumber = hexArr[++arrayIndex]; + //get payload length + message.TotalLength = hexArr[++arrayIndex]; + + var sourceAddress = ""; + + //check WHDR header: source address + if ((hexArr[0] & MASK_HEADER_ADDRESS_MODE) === MASK_HEADER_ADDRESS_NONE) { + arrayIndex++; + message.SourceAddress = null; + } else if ((hexArr[0] & MASK_HEADER_ADDRESS_MODE) === MASK_HEADER_ADDRESS_2_OCTECT) { + console.log("2 octects source address"); + arrayIndex++; + for (i = arrayIndex; i < arrayIndex + 2; i++) { + sourceAddress = sourceAddress + addZero(hexArr[i].toString(16)); + } + message.SourceAddress = sourceAddress; + arrayIndex += 2; + } else if ((hexArr[0] & MASK_HEADER_ADDRESS_MODE) === MASK_HEADER_ADDRESS_8_OCTECT) { + console.log("8 octects source address"); + arrayIndex++; + for (i = arrayIndex; i < arrayIndex + 8; i++) { + sourceAddress = sourceAddress + addZero(hexArr[i].toString(16)); + } + message.SourceAddress = sourceAddress; + arrayIndex += 8; + } + + //check CRC + hexPayloadArr = hexArr.slice(3 + getSourceAddressLength(message.SourceAddress), hexArr.length - 1); + var calculateCRC = CrcCalc(hexPayloadArr, hexPayloadArr.length); + if (version > 0) { + calculateCRC = ~calculateCRC & 0xff; // JavaScript bitwise operators are converted to signed 32-bit integers + } + + if (calculateCRC != hexArr[hexArr.length - 1]) { + console.log("Frame CRC check failed."); + msg.payload = "Frame CRC check failed."; + } + + //check if frame legnth is correct + if (message.SourceAddress != null && !checkFrameLength()) { + console.log("Frame length error"); + msg.payload = "Frame length error"; + } + + // Parse Payload + parsePayLoad(arrayIndex); + } catch (error) { + console.log("Error: Parser failed. " + error); + msg.payload = "Error: Parser failed. " + error; + var output = [msg, null]; + if (typeof lostPacketInfo.LOG_INDEX != "undefined") { + output.push({ payload: JSON.stringify(lostPacketInfo) }); + + lostPacketInfo = {}; + } + } + + //////////////////////////////////////////// + //return data + //////////////////////////////////////////// + + if (bIsRunNodeRed) { + msg.payload = message; + var output = [msg]; + if (csvMessage.length > 0) { + output.push({ payload: csvMessage }); + if (typeof lostPacketInfo.LOG_INDEX != "undefined") { + output.push({ payload: JSON.stringify(lostPacketInfo) }); + lostPacketInfo = {}; + } + } + } else { + payload = payload.concat(transformData(message)); + } +} diff --git a/decoders/connector/atim/dind160/assets/logo.png b/decoders/connector/atim/dind160/assets/logo.png new file mode 100644 index 00000000..e12de1b6 Binary files /dev/null and b/decoders/connector/atim/dind160/assets/logo.png differ diff --git a/decoders/connector/atim/dind160/connector.jsonc b/decoders/connector/atim/dind160/connector.jsonc new file mode 100644 index 00000000..9baa87f0 --- /dev/null +++ b/decoders/connector/atim/dind160/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Atim DIND160", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/atim/dind160/description.md b/decoders/connector/atim/dind160/description.md new file mode 100644 index 00000000..ac5952b7 --- /dev/null +++ b/decoders/connector/atim/dind160/description.md @@ -0,0 +1 @@ +Dry contacts and Metering over LoRaWAN or Sigfox \ No newline at end of file diff --git a/decoders/connector/atim/dind160/v1.0.0/payload-config.jsonc b/decoders/connector/atim/dind160/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..3684d15f --- /dev/null +++ b/decoders/connector/atim/dind160/v1.0.0/payload-config.jsonc @@ -0,0 +1,26 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "The ACW-DIND160 monitors the status of dry contacts status and meters’ index consumption and control digital outputs (open collector, push / pull and dry contact type) and send the data to LoRa or Sigfox. Remotely control equipment or PLCs using the dry contacts outputs. Possibility to add a digital temperature probe via jack plug (option). Compatible with Sigfox repeater (ACW-GW). Contains 16 inputs.\n\n\n**Technical Data**\n* Dimensions: 53 x 67 x 95 mm\n* Antenna: External (SMA connector)\n* Temperature: -20°C to +55°C (operation), -40°C to +70°C (storage)\n* Mounts to: DIN rail compliant\n* Power supply: 1x 10-30Vcc\n* Consumption: 100mA\n* Digital inputs TOR: 16 configurable inputs, change of state alerting, 8 meters configurable on whatever input (4 meters before version V1.2.0), available activation of 4 groups of inputs\n* Digital outputs TOR: 8 outputs driven via downlink\n* Alarm: Wrenching / Shock\n* Frequency: 865-870 MHz\n* Power: 25 mW (14 dBm)\n* Transfer rate: Sigfox: 100 bit/s, LoRaWAN: 300 bit/s to 10 kbit/s", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/sigfox/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/atim/dind160/v1.0.0/payload.js b/decoders/connector/atim/dind160/v1.0.0/payload.js new file mode 100644 index 00000000..58709750 --- /dev/null +++ b/decoders/connector/atim/dind160/v1.0.0/payload.js @@ -0,0 +1,1332 @@ +/* eslint-disable no-plusplus */ +/* eslint-disable no-bitwise */ +function decodeStream(payload, type, timestamp) { + // Init result + const result = { historics: [], events: [], realTimes: [] }; + + // Parse stream + // const jsonStream = JSON.parse(stream); + + // Get time and payload + // const time = new Date(jsonStream.data.timestamp * 1000) / 1000; + const time = new Date(timestamp * 1000) / 1000; + // const { payload } = jsonStream.data; + + // Save network informations + result.realTimes.push({ tagRef: "sys_data_payload", timestamp: time, tagValue: String(payload) }); + result.realTimes.push({ tagRef: "sys_data_timestamp", timestamp: time, tagValue: String(time) }); + /* if (jsonStream.data !== undefined) { + result.realTimes.push({ tagRef: "sys_data_type", timestamp: time, tagValue: String(jsonStream.data.type) }); + result.realTimes.push({ tagRef: "sys_data_raw", timestamp: time, tagValue: String(jsonStream.data.raw) }); + } + if (jsonStream.metadata !== undefined) { + result.realTimes.push({ tagRef: "sys_metadata_lastStreamTimestampUtc", timestamp: time, tagValue: String(jsonStream.metadata.lastStreamTimestampUtc) }); + if (jsonStream.metadata.device !== undefined) { + result.realTimes.push({ tagRef: "sys_device_sn", timestamp: time, tagValue: String(jsonStream.metadata.device.serialNumber) }); + result.realTimes.push({ tagRef: "sys_device_name", timestamp: time, tagValue: String(jsonStream.metadata.device.name) }); + } + if (jsonStream.metadata.endpoint !== undefined) { + result.realTimes.push({ tagRef: "sys_endpoint_name", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.name) }); + result.realTimes.push({ tagRef: "sys_endpoint_type", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.type) }); + } + if (jsonStream.metadata.network !== undefined) { + result.realTimes.push({ tagRef: "sys_network_port", timestamp: time, tagValue: String(jsonStream.metadata.network.port) }); + result.realTimes.push({ tagRef: "sys_network_linkQuality", timestamp: time, tagValue: String(jsonStream.metadata.network.linkQuality) }); + } + if (jsonStream.metadata.location !== undefined) { + result.realTimes.push({ tagRef: "sys_location_source", timestamp: time, tagValue: String(jsonStream.metadata.location.source) }); + result.realTimes.push({ tagRef: "sys_location_latitude", timestamp: time, tagValue: String(jsonStream.metadata.location.latitude) }); + result.realTimes.push({ tagRef: "sys_location_longitude", timestamp: time, tagValue: String(jsonStream.metadata.location.longitude) }); + result.realTimes.push({ tagRef: "sys_location_accuracy", timestamp: time, tagValue: String(jsonStream.metadata.location.accuracy) }); + } + } */ + if (type !== 3) { + // if KHEIRON + /* if (jsonStream.metadata.endpoint.type === 0) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).fcnt) }); + } + // if SIGFOX + if (jsonStream.metadata.endpoint.type === 5) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).seqNumber) }); + } + // if OBJENIOUS + if (jsonStream.metadata.endpoint.type === 6) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).count) }); + result.realTimes.push({ tagRef: "custom_data_raw_devEUI", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).device_properties.deveui) }); + } + */ + // Get message ID + const msgStr = parseInt(payload.substring(0, 2), 16); + + // Switch message ids + switch (msgStr) { + case 1: { + // Get meter + const battery = parseInt(payload.substring(2, 6), 16); + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery / 1000), + }); + + break; + } + case 3: { + // Get data + const battery = parseInt(payload.substring(2, 4), 16); + let temperature = parseInt(payload.substring(4, 8), 16); + const humidity = parseInt(payload.substring(8, 12), 16); + + // check if negative value + if (temperature >> 15 === 1) { + const reverseValue = temperature ^ 65535; + temperature = (reverseValue + 1) * -1; + } + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temperature / 100), + }); + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity / 100), + }); + break; + } + case 5: { + const testCounter = parseInt(payload.substring(2, 4), 16); + + result.realTimes.push({ + tagRef: "p_test", + timestamp: time, + tagValue: String(testCounter), + }); + + break; + } + case 9: { + // Get input + const di = parseInt(payload.substring(4, 6), 16); + const di1 = (di & 1) === 1; + const di2 = (di & 2) === 2; + + // get ouput + const dout = parseInt(payload.substring(2, 4), 16); + const do1 = (dout & 1) === 1; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DO1", + timestamp: time, + tagValue: String(do1), + }); + + break; + } + case 10: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + break; + } + case 11: { + break; + } + case 12: { + break; + } + case 13: { + break; + } + case 14: { + break; + } + case 21: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // Decode + temp = (temp - 33184) / 128; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + break; + } + case 20: { + // Get meter + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 48: { + // get realtime meter + const meter = parseInt(payload.substring(42, 50), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + + // get historic meter + const meter1 = parseInt(payload.substring(34, 42), 16); + const meter2 = parseInt(payload.substring(26, 34), 16); + const meter3 = parseInt(payload.substring(18, 26), 16); + const meter4 = parseInt(payload.substring(10, 18), 16); + const meter5 = parseInt(payload.substring(2, 10), 16); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 1) / 1000, + tagValue: String(meter1), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 2) / 1000, + tagValue: String(meter2), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 3) / 1000, + tagValue: String(meter3), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 4) / 1000, + tagValue: String(meter4), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 5) / 1000, + tagValue: String(meter5), + }); + + break; + } + case 49: { + // get realtime meter + const meter = parseInt(payload.substring(2, 10), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + break; + } + case 55: { + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + break; + } + case 57: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // const a = new Date(time * 1000); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (600000 + i * 600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 58: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (1800000 + i * 1800000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 59: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (3600000 + i * 3600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 22: { + // Get meter + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(4, 6), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 15: { + break; + } + case 16: { + break; + } + case 17: { + break; + } + case 18: { + break; + } + case 23: { + // Get temperature & humidity + let temp = parseInt(payload.substring(2, 6), 16); + let humidity = parseInt(payload.substring(6, 10), 16); + + // Decode + temp = (temp * 175.72) / 65536 - 46.85; + humidity = (humidity * 125) / 65536 - 6; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity), + }); + break; + } + case 24: { + break; + } + case 30: { + break; + } + case 31: { + break; + } + case 32: { + break; + } + case 33: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 10 / 64240; + + result.realTimes.push({ + tagRef: "p_voltage", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 25: { + break; + } + case 34: { + break; + } + case 35: { + break; + } + case 36: { + break; + } + case 37: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 16 / 47584; + result.realTimes.push({ + tagRef: "p_current", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 27: { + break; + } + case 42: { + break; + } + case 43: { + break; + } + case 44: { + break; + } + case 45: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + break; + } + case 26: { + break; + } + case 38: { + break; + } + case 39: { + break; + } + case 40: { + break; + } + case 41: { + break; + } + case 83: { + break; + } + case 84: { + break; + } + case 85: { + break; + } + case 86: { + break; + } + case 87: { + break; + } + case 88: { + break; + } + case 89: { + break; + } + case 90: { + break; + } + case 91: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + let temp2 = parseInt(payload.substring(6, 10), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + + // check if error + if (temp2 !== 32768) { + // check if negative value + if (temp2 >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp2 = (reverseValue + 1) * -1; + } + + // apply coef + temp2 *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature2", + timestamp: time, + tagValue: String(temp2), + }); + } + + break; + } + case 19: { + // Get water leak state + const waterleak = parseInt(payload.substring(4, 6), 16); + + // save + result.realTimes.push({ + tagRef: "p_waterLeak", + timestamp: time, + tagValue: String(waterleak), + }); + break; + } + case 47: { + // get temperature + const temp = parseInt(payload.substring(14, 18), 16); + const p_temperature = (10.888 - Math.sqrt((-10.888) ** 2 + 4 * 0.00347 * (1777.3 - temp))) / (2 * -0.00347) + 30; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + + if (parseInt(payload.substring(18, 34), 16) !== 0) { + // get data for gps decoding + const a = parseInt(payload.substring(18, 20), 16) >> 4; + const b = ((parseInt(payload.substring(18, 20), 16) << 4) & 255) >> 4; + const c = parseInt(payload.substring(20, 22), 16) >> 4; + const d = ((parseInt(payload.substring(20, 22), 16) << 4) & 255) >> 4; + const e = parseInt(payload.substring(22, 24), 16) >> 4; + const f = ((parseInt(payload.substring(22, 24), 16) << 4) & 255) >> 4; + const g = parseInt(payload.substring(24, 26), 16) >> 4; + const h = ((parseInt(payload.substring(24, 26), 16) << 4) & 255) >> 4; + const i = parseInt(payload.substring(26, 28), 16) >> 4; + const j = ((parseInt(payload.substring(26, 28), 16) << 4) & 255) >> 4; + const k = parseInt(payload.substring(28, 30), 16) >> 4; + const l = ((parseInt(payload.substring(28, 30), 16) << 4) & 255) >> 4; + const m = parseInt(payload.substring(30, 32), 16) >> 4; + const n = ((parseInt(payload.substring(30, 32), 16) << 4) & 255) >> 4; + const o = parseInt(payload.substring(32, 34), 16) >> 4; + // const p = ((parseInt(payload.substring(32, 34), 16) << 4) & 255) >> 6; + const q = ((parseInt(payload.substring(32, 34), 16) << 6) & 255) >> 7; + const r = ((parseInt(payload.substring(32, 34), 16) << 7) & 255) >> 7; + // get latitude and longitude + const latitude = (2 * q - 1) * (10 * a + b + c / 6 + d / 60 + e / 600 + f / 6000 + g / 60000); + const longitude = (2 * r - 1) * (100 * h + 10 * i + j + k / 6 + l / 60 + m / 600 + n / 6000 + o / 60000); + + // save + result.realTimes.push({ + tagRef: "p_latitude", + timestamp: time, + tagValue: String(latitude), + }); + result.realTimes.push({ + tagRef: "p_longitude", + timestamp: time, + tagValue: String(longitude), + }); + } + + break; + } + case 50: { + const p_vibration = parseInt(payload.substring(4, 6), 16); + const p_ils = parseInt(payload.substring(6, 8), 16); + const p_temperature = (2103 - parseInt(payload.substring(8, 12), 16)) / 10.9; + + // save + result.realTimes.push({ + tagRef: "p_vibration", + timestamp: time, + tagValue: String(p_vibration), + }); + result.realTimes.push({ + tagRef: "p_ils", + timestamp: time, + tagValue: String(p_ils), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + break; + } + case 51: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + break; + } + case 52: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get count + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(parseInt(payload.substring(4, 12), 16)), + }); + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(parseInt(payload.substring(12, 20), 16)), + }); + break; + } + case 53: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 54: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 62: { + // Get Absence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(0), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + const valueWorZ = parseInt(payload.substring(10, 14), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + + // Save WorZ + result.realTimes.push({ + tagRef: "p_WorZ", + timestamp: time, + tagValue: String(valueWorZ), + }); + break; + } + case 63: { + // Get Presence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(1), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + break; + } + case 65: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + break; + } + case 66: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + break; + } + case 67: { + // save + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time - 1, + tagValue: String(1), + context: [], + }); + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time, + tagValue: String(0), + context: [], + }); + break; + } + case 78: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + // get count 1 + const count1 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 79: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(14, 22), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 80: { + // get count 1 + const count1 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 81: { + // get count 3 + const count3 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count3", + timestamp: time, + tagValue: String(count3), + }); + + // get count 4 + const count4 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count4", + timestamp: time, + tagValue: String(count4), + }); + break; + } + case 82: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 93: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(2 + 8 * j, 10 + 8 * j), 16)), + }); + } + break; + } + case 94: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(-2 + 8 * j, 6 + 8 * j), 16)), + }); + } + break; + } + case 95: { + // get count 5 + const count5 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count5", + timestamp: time, + tagValue: String(count5), + }); + + // get count 6 + const count6 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count6", + timestamp: time, + tagValue: String(count6), + }); + break; + } + case 96: { + // get count 7 + const count7 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count7", + timestamp: time, + tagValue: String(count7), + }); + + // get count 8 + const count8 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count8", + timestamp: time, + tagValue: String(count8), + }); + break; + } + case 2: + case 4: + case 6: + case 7: + case 8: + case 46: + case 56: + case 60: + case 61: + case 64: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 92: + case 97: + case 98: + case 99: + default: + break; + } + } + // Return result + return result; +} + +function ToTagoFormat(object_item, serie) { + const historics = []; + const events = []; + const realTimes = []; + // eslint-disable-next-line guard-for-in + for (const key in object_item.realTimes) { + realTimes.push({ + variable: `realTimes_${object_item.realTimes[key].tagRef}`.toLowerCase(), + value: object_item.realTimes[key].tagValue, + time: object_item.realTimes[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.events) { + events.push({ + variable: `events_${object_item.events[key].tagRef}`.toLowerCase(), + value: object_item.events[key].tagValue, + time: object_item.events[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.historics) { + historics.push({ + variable: `historics_${object_item.historics[key].tagRef}`.toLowerCase(), + value: object_item.historics[key].tagValue, + time: object_item.historics[key].timestamp, + serie, + }); + } + const result = historics.concat(events, realTimes); + + return result; +} + +/* let payload = [ + { variable: "payload", value: "5e7f000000003f00000040000000410000004200000043000000440000004500000046" }, + { variable: "type", value: 0 }, + { variable: "timestamp", value: 1605614318410 }, +]; */ + +const data = payload.find((x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data"); +const type = payload.find((x) => x.variable === "type"); +const timestamp = payload.find((x) => x.variable === "timestamp"); + +if (data) { + // const buffer = Buffer.from(data.value, "hex"); + const serie = new Date().getTime(); + // payload = decodeStream(data.value, type.value, timestamp.value); + payload = ToTagoFormat(decodeStream(data.value, type.value, timestamp.value), serie); +} + +// eslint-disable-next-line no-console +// console.log(payload); diff --git a/decoders/connector/atim/dind21/assets/logo.png b/decoders/connector/atim/dind21/assets/logo.png new file mode 100644 index 00000000..314f1af2 Binary files /dev/null and b/decoders/connector/atim/dind21/assets/logo.png differ diff --git a/decoders/connector/atim/dind21/connector.jsonc b/decoders/connector/atim/dind21/connector.jsonc new file mode 100644 index 00000000..414050f3 --- /dev/null +++ b/decoders/connector/atim/dind21/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Atim DIND21", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/atim/dind21/description.md b/decoders/connector/atim/dind21/description.md new file mode 100644 index 00000000..3bf30fe7 --- /dev/null +++ b/decoders/connector/atim/dind21/description.md @@ -0,0 +1 @@ +Digital Input/Output and Metering over LoRaWAN or Sigfox \ No newline at end of file diff --git a/decoders/connector/atim/dind21/v1.0.0/payload-config.jsonc b/decoders/connector/atim/dind21/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..a704ed34 --- /dev/null +++ b/decoders/connector/atim/dind21/v1.0.0/payload-config.jsonc @@ -0,0 +1,26 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "The ACW-DIND21 is an Industrial Internet of Things (IIoT) device in DIN format ready to be installed in an electrical box for the purpose of monitoring it. The 2 inputs can be configured as dry contact input or as counting. The digital output allows controlling equipment or restarting remotely from the network. This product can be used in different use case:\n* Control and monitoring of equipment (electrical cabinets, generators, air compressors, etc.)\n* Intrusion detection (replacement of old alarms)\n* Control and restart of remote equipment (pumps, motors, valves, etc ...).\n\n**Technical Data**\n* Dimensions: 90 x 57 x 17,9 mm\n* Antenna: External (SMA connector)\n* Temperature: -20°C to +55°C (operation), -40°C to +70°C (storage)\n* Mounts to: DIN rail compliant\n* Power supply: External 10-30 Vcc\n* Weight: 50g\n* Frequency: 865-870 MHz\n* Power: 25 mW (14 dBm)", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/sigfox/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/atim/dind21/v1.0.0/payload.js b/decoders/connector/atim/dind21/v1.0.0/payload.js new file mode 100644 index 00000000..58709750 --- /dev/null +++ b/decoders/connector/atim/dind21/v1.0.0/payload.js @@ -0,0 +1,1332 @@ +/* eslint-disable no-plusplus */ +/* eslint-disable no-bitwise */ +function decodeStream(payload, type, timestamp) { + // Init result + const result = { historics: [], events: [], realTimes: [] }; + + // Parse stream + // const jsonStream = JSON.parse(stream); + + // Get time and payload + // const time = new Date(jsonStream.data.timestamp * 1000) / 1000; + const time = new Date(timestamp * 1000) / 1000; + // const { payload } = jsonStream.data; + + // Save network informations + result.realTimes.push({ tagRef: "sys_data_payload", timestamp: time, tagValue: String(payload) }); + result.realTimes.push({ tagRef: "sys_data_timestamp", timestamp: time, tagValue: String(time) }); + /* if (jsonStream.data !== undefined) { + result.realTimes.push({ tagRef: "sys_data_type", timestamp: time, tagValue: String(jsonStream.data.type) }); + result.realTimes.push({ tagRef: "sys_data_raw", timestamp: time, tagValue: String(jsonStream.data.raw) }); + } + if (jsonStream.metadata !== undefined) { + result.realTimes.push({ tagRef: "sys_metadata_lastStreamTimestampUtc", timestamp: time, tagValue: String(jsonStream.metadata.lastStreamTimestampUtc) }); + if (jsonStream.metadata.device !== undefined) { + result.realTimes.push({ tagRef: "sys_device_sn", timestamp: time, tagValue: String(jsonStream.metadata.device.serialNumber) }); + result.realTimes.push({ tagRef: "sys_device_name", timestamp: time, tagValue: String(jsonStream.metadata.device.name) }); + } + if (jsonStream.metadata.endpoint !== undefined) { + result.realTimes.push({ tagRef: "sys_endpoint_name", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.name) }); + result.realTimes.push({ tagRef: "sys_endpoint_type", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.type) }); + } + if (jsonStream.metadata.network !== undefined) { + result.realTimes.push({ tagRef: "sys_network_port", timestamp: time, tagValue: String(jsonStream.metadata.network.port) }); + result.realTimes.push({ tagRef: "sys_network_linkQuality", timestamp: time, tagValue: String(jsonStream.metadata.network.linkQuality) }); + } + if (jsonStream.metadata.location !== undefined) { + result.realTimes.push({ tagRef: "sys_location_source", timestamp: time, tagValue: String(jsonStream.metadata.location.source) }); + result.realTimes.push({ tagRef: "sys_location_latitude", timestamp: time, tagValue: String(jsonStream.metadata.location.latitude) }); + result.realTimes.push({ tagRef: "sys_location_longitude", timestamp: time, tagValue: String(jsonStream.metadata.location.longitude) }); + result.realTimes.push({ tagRef: "sys_location_accuracy", timestamp: time, tagValue: String(jsonStream.metadata.location.accuracy) }); + } + } */ + if (type !== 3) { + // if KHEIRON + /* if (jsonStream.metadata.endpoint.type === 0) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).fcnt) }); + } + // if SIGFOX + if (jsonStream.metadata.endpoint.type === 5) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).seqNumber) }); + } + // if OBJENIOUS + if (jsonStream.metadata.endpoint.type === 6) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).count) }); + result.realTimes.push({ tagRef: "custom_data_raw_devEUI", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).device_properties.deveui) }); + } + */ + // Get message ID + const msgStr = parseInt(payload.substring(0, 2), 16); + + // Switch message ids + switch (msgStr) { + case 1: { + // Get meter + const battery = parseInt(payload.substring(2, 6), 16); + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery / 1000), + }); + + break; + } + case 3: { + // Get data + const battery = parseInt(payload.substring(2, 4), 16); + let temperature = parseInt(payload.substring(4, 8), 16); + const humidity = parseInt(payload.substring(8, 12), 16); + + // check if negative value + if (temperature >> 15 === 1) { + const reverseValue = temperature ^ 65535; + temperature = (reverseValue + 1) * -1; + } + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temperature / 100), + }); + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity / 100), + }); + break; + } + case 5: { + const testCounter = parseInt(payload.substring(2, 4), 16); + + result.realTimes.push({ + tagRef: "p_test", + timestamp: time, + tagValue: String(testCounter), + }); + + break; + } + case 9: { + // Get input + const di = parseInt(payload.substring(4, 6), 16); + const di1 = (di & 1) === 1; + const di2 = (di & 2) === 2; + + // get ouput + const dout = parseInt(payload.substring(2, 4), 16); + const do1 = (dout & 1) === 1; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DO1", + timestamp: time, + tagValue: String(do1), + }); + + break; + } + case 10: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + break; + } + case 11: { + break; + } + case 12: { + break; + } + case 13: { + break; + } + case 14: { + break; + } + case 21: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // Decode + temp = (temp - 33184) / 128; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + break; + } + case 20: { + // Get meter + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 48: { + // get realtime meter + const meter = parseInt(payload.substring(42, 50), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + + // get historic meter + const meter1 = parseInt(payload.substring(34, 42), 16); + const meter2 = parseInt(payload.substring(26, 34), 16); + const meter3 = parseInt(payload.substring(18, 26), 16); + const meter4 = parseInt(payload.substring(10, 18), 16); + const meter5 = parseInt(payload.substring(2, 10), 16); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 1) / 1000, + tagValue: String(meter1), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 2) / 1000, + tagValue: String(meter2), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 3) / 1000, + tagValue: String(meter3), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 4) / 1000, + tagValue: String(meter4), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 5) / 1000, + tagValue: String(meter5), + }); + + break; + } + case 49: { + // get realtime meter + const meter = parseInt(payload.substring(2, 10), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + break; + } + case 55: { + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + break; + } + case 57: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // const a = new Date(time * 1000); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (600000 + i * 600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 58: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (1800000 + i * 1800000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 59: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (3600000 + i * 3600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 22: { + // Get meter + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(4, 6), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 15: { + break; + } + case 16: { + break; + } + case 17: { + break; + } + case 18: { + break; + } + case 23: { + // Get temperature & humidity + let temp = parseInt(payload.substring(2, 6), 16); + let humidity = parseInt(payload.substring(6, 10), 16); + + // Decode + temp = (temp * 175.72) / 65536 - 46.85; + humidity = (humidity * 125) / 65536 - 6; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity), + }); + break; + } + case 24: { + break; + } + case 30: { + break; + } + case 31: { + break; + } + case 32: { + break; + } + case 33: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 10 / 64240; + + result.realTimes.push({ + tagRef: "p_voltage", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 25: { + break; + } + case 34: { + break; + } + case 35: { + break; + } + case 36: { + break; + } + case 37: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 16 / 47584; + result.realTimes.push({ + tagRef: "p_current", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 27: { + break; + } + case 42: { + break; + } + case 43: { + break; + } + case 44: { + break; + } + case 45: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + break; + } + case 26: { + break; + } + case 38: { + break; + } + case 39: { + break; + } + case 40: { + break; + } + case 41: { + break; + } + case 83: { + break; + } + case 84: { + break; + } + case 85: { + break; + } + case 86: { + break; + } + case 87: { + break; + } + case 88: { + break; + } + case 89: { + break; + } + case 90: { + break; + } + case 91: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + let temp2 = parseInt(payload.substring(6, 10), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + + // check if error + if (temp2 !== 32768) { + // check if negative value + if (temp2 >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp2 = (reverseValue + 1) * -1; + } + + // apply coef + temp2 *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature2", + timestamp: time, + tagValue: String(temp2), + }); + } + + break; + } + case 19: { + // Get water leak state + const waterleak = parseInt(payload.substring(4, 6), 16); + + // save + result.realTimes.push({ + tagRef: "p_waterLeak", + timestamp: time, + tagValue: String(waterleak), + }); + break; + } + case 47: { + // get temperature + const temp = parseInt(payload.substring(14, 18), 16); + const p_temperature = (10.888 - Math.sqrt((-10.888) ** 2 + 4 * 0.00347 * (1777.3 - temp))) / (2 * -0.00347) + 30; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + + if (parseInt(payload.substring(18, 34), 16) !== 0) { + // get data for gps decoding + const a = parseInt(payload.substring(18, 20), 16) >> 4; + const b = ((parseInt(payload.substring(18, 20), 16) << 4) & 255) >> 4; + const c = parseInt(payload.substring(20, 22), 16) >> 4; + const d = ((parseInt(payload.substring(20, 22), 16) << 4) & 255) >> 4; + const e = parseInt(payload.substring(22, 24), 16) >> 4; + const f = ((parseInt(payload.substring(22, 24), 16) << 4) & 255) >> 4; + const g = parseInt(payload.substring(24, 26), 16) >> 4; + const h = ((parseInt(payload.substring(24, 26), 16) << 4) & 255) >> 4; + const i = parseInt(payload.substring(26, 28), 16) >> 4; + const j = ((parseInt(payload.substring(26, 28), 16) << 4) & 255) >> 4; + const k = parseInt(payload.substring(28, 30), 16) >> 4; + const l = ((parseInt(payload.substring(28, 30), 16) << 4) & 255) >> 4; + const m = parseInt(payload.substring(30, 32), 16) >> 4; + const n = ((parseInt(payload.substring(30, 32), 16) << 4) & 255) >> 4; + const o = parseInt(payload.substring(32, 34), 16) >> 4; + // const p = ((parseInt(payload.substring(32, 34), 16) << 4) & 255) >> 6; + const q = ((parseInt(payload.substring(32, 34), 16) << 6) & 255) >> 7; + const r = ((parseInt(payload.substring(32, 34), 16) << 7) & 255) >> 7; + // get latitude and longitude + const latitude = (2 * q - 1) * (10 * a + b + c / 6 + d / 60 + e / 600 + f / 6000 + g / 60000); + const longitude = (2 * r - 1) * (100 * h + 10 * i + j + k / 6 + l / 60 + m / 600 + n / 6000 + o / 60000); + + // save + result.realTimes.push({ + tagRef: "p_latitude", + timestamp: time, + tagValue: String(latitude), + }); + result.realTimes.push({ + tagRef: "p_longitude", + timestamp: time, + tagValue: String(longitude), + }); + } + + break; + } + case 50: { + const p_vibration = parseInt(payload.substring(4, 6), 16); + const p_ils = parseInt(payload.substring(6, 8), 16); + const p_temperature = (2103 - parseInt(payload.substring(8, 12), 16)) / 10.9; + + // save + result.realTimes.push({ + tagRef: "p_vibration", + timestamp: time, + tagValue: String(p_vibration), + }); + result.realTimes.push({ + tagRef: "p_ils", + timestamp: time, + tagValue: String(p_ils), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + break; + } + case 51: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + break; + } + case 52: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get count + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(parseInt(payload.substring(4, 12), 16)), + }); + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(parseInt(payload.substring(12, 20), 16)), + }); + break; + } + case 53: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 54: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 62: { + // Get Absence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(0), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + const valueWorZ = parseInt(payload.substring(10, 14), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + + // Save WorZ + result.realTimes.push({ + tagRef: "p_WorZ", + timestamp: time, + tagValue: String(valueWorZ), + }); + break; + } + case 63: { + // Get Presence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(1), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + break; + } + case 65: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + break; + } + case 66: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + break; + } + case 67: { + // save + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time - 1, + tagValue: String(1), + context: [], + }); + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time, + tagValue: String(0), + context: [], + }); + break; + } + case 78: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + // get count 1 + const count1 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 79: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(14, 22), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 80: { + // get count 1 + const count1 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 81: { + // get count 3 + const count3 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count3", + timestamp: time, + tagValue: String(count3), + }); + + // get count 4 + const count4 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count4", + timestamp: time, + tagValue: String(count4), + }); + break; + } + case 82: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 93: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(2 + 8 * j, 10 + 8 * j), 16)), + }); + } + break; + } + case 94: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(-2 + 8 * j, 6 + 8 * j), 16)), + }); + } + break; + } + case 95: { + // get count 5 + const count5 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count5", + timestamp: time, + tagValue: String(count5), + }); + + // get count 6 + const count6 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count6", + timestamp: time, + tagValue: String(count6), + }); + break; + } + case 96: { + // get count 7 + const count7 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count7", + timestamp: time, + tagValue: String(count7), + }); + + // get count 8 + const count8 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count8", + timestamp: time, + tagValue: String(count8), + }); + break; + } + case 2: + case 4: + case 6: + case 7: + case 8: + case 46: + case 56: + case 60: + case 61: + case 64: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 92: + case 97: + case 98: + case 99: + default: + break; + } + } + // Return result + return result; +} + +function ToTagoFormat(object_item, serie) { + const historics = []; + const events = []; + const realTimes = []; + // eslint-disable-next-line guard-for-in + for (const key in object_item.realTimes) { + realTimes.push({ + variable: `realTimes_${object_item.realTimes[key].tagRef}`.toLowerCase(), + value: object_item.realTimes[key].tagValue, + time: object_item.realTimes[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.events) { + events.push({ + variable: `events_${object_item.events[key].tagRef}`.toLowerCase(), + value: object_item.events[key].tagValue, + time: object_item.events[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.historics) { + historics.push({ + variable: `historics_${object_item.historics[key].tagRef}`.toLowerCase(), + value: object_item.historics[key].tagValue, + time: object_item.historics[key].timestamp, + serie, + }); + } + const result = historics.concat(events, realTimes); + + return result; +} + +/* let payload = [ + { variable: "payload", value: "5e7f000000003f00000040000000410000004200000043000000440000004500000046" }, + { variable: "type", value: 0 }, + { variable: "timestamp", value: 1605614318410 }, +]; */ + +const data = payload.find((x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data"); +const type = payload.find((x) => x.variable === "type"); +const timestamp = payload.find((x) => x.variable === "timestamp"); + +if (data) { + // const buffer = Buffer.from(data.value, "hex"); + const serie = new Date().getTime(); + // payload = decodeStream(data.value, type.value, timestamp.value); + payload = ToTagoFormat(decodeStream(data.value, type.value, timestamp.value), serie); +} + +// eslint-disable-next-line no-console +// console.log(payload); diff --git a/decoders/connector/atim/dind44/assets/logo.png b/decoders/connector/atim/dind44/assets/logo.png new file mode 100644 index 00000000..e12de1b6 Binary files /dev/null and b/decoders/connector/atim/dind44/assets/logo.png differ diff --git a/decoders/connector/atim/dind44/connector.jsonc b/decoders/connector/atim/dind44/connector.jsonc new file mode 100644 index 00000000..47d2f6bb --- /dev/null +++ b/decoders/connector/atim/dind44/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Atim DIND44", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/atim/dind44/description.md b/decoders/connector/atim/dind44/description.md new file mode 100644 index 00000000..ac5952b7 --- /dev/null +++ b/decoders/connector/atim/dind44/description.md @@ -0,0 +1 @@ +Dry contacts and Metering over LoRaWAN or Sigfox \ No newline at end of file diff --git a/decoders/connector/atim/dind44/v1.0.0/payload-config.jsonc b/decoders/connector/atim/dind44/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..17497b6a --- /dev/null +++ b/decoders/connector/atim/dind44/v1.0.0/payload-config.jsonc @@ -0,0 +1,26 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "The ACW-DIND44 monitors the status of dry contacts status and meters’ index consumption and control digital outputs (open collector, push / pull and dry contact type) and send the data to LoRa or Sigfox. Remotely control equipment or PLCs using the dry contacts outputs. Possibility to add a digital temperature probe via jack plug (option). Compatible with Sigfox repeater (ACW-GW). Contains 4 inputs and 4 outputs.\n\n\n**Technical Data**\n* Dimensions: 53 x 67 x 95 mm\n* Antenna: External (SMA connector)\n* Temperature: -20°C to +55°C (operation), -40°C to +70°C (storage)\n* Mounts to: DIN rail compliant\n* Power supply: 1x 10-30Vcc\n* Consumption: 100mA\n* Digital inputs TOR: 4 configurable inputs, change of state alerting, 4 meters configurable on whatever input, available activation of a group of inputs\n* Digital outputs TOR: 4 outputs driven via downlink\n* Alarm: Wrenching / Shock\n* Frequency: 865-870 MHz\n* Power: 25 mW (14 dBm)\n* Transfer rate: Sigfox: 100 bit/s, LoRaWAN: 300 bit/s to 10 kbit/s", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/sigfox/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/atim/dind44/v1.0.0/payload.js b/decoders/connector/atim/dind44/v1.0.0/payload.js new file mode 100644 index 00000000..58709750 --- /dev/null +++ b/decoders/connector/atim/dind44/v1.0.0/payload.js @@ -0,0 +1,1332 @@ +/* eslint-disable no-plusplus */ +/* eslint-disable no-bitwise */ +function decodeStream(payload, type, timestamp) { + // Init result + const result = { historics: [], events: [], realTimes: [] }; + + // Parse stream + // const jsonStream = JSON.parse(stream); + + // Get time and payload + // const time = new Date(jsonStream.data.timestamp * 1000) / 1000; + const time = new Date(timestamp * 1000) / 1000; + // const { payload } = jsonStream.data; + + // Save network informations + result.realTimes.push({ tagRef: "sys_data_payload", timestamp: time, tagValue: String(payload) }); + result.realTimes.push({ tagRef: "sys_data_timestamp", timestamp: time, tagValue: String(time) }); + /* if (jsonStream.data !== undefined) { + result.realTimes.push({ tagRef: "sys_data_type", timestamp: time, tagValue: String(jsonStream.data.type) }); + result.realTimes.push({ tagRef: "sys_data_raw", timestamp: time, tagValue: String(jsonStream.data.raw) }); + } + if (jsonStream.metadata !== undefined) { + result.realTimes.push({ tagRef: "sys_metadata_lastStreamTimestampUtc", timestamp: time, tagValue: String(jsonStream.metadata.lastStreamTimestampUtc) }); + if (jsonStream.metadata.device !== undefined) { + result.realTimes.push({ tagRef: "sys_device_sn", timestamp: time, tagValue: String(jsonStream.metadata.device.serialNumber) }); + result.realTimes.push({ tagRef: "sys_device_name", timestamp: time, tagValue: String(jsonStream.metadata.device.name) }); + } + if (jsonStream.metadata.endpoint !== undefined) { + result.realTimes.push({ tagRef: "sys_endpoint_name", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.name) }); + result.realTimes.push({ tagRef: "sys_endpoint_type", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.type) }); + } + if (jsonStream.metadata.network !== undefined) { + result.realTimes.push({ tagRef: "sys_network_port", timestamp: time, tagValue: String(jsonStream.metadata.network.port) }); + result.realTimes.push({ tagRef: "sys_network_linkQuality", timestamp: time, tagValue: String(jsonStream.metadata.network.linkQuality) }); + } + if (jsonStream.metadata.location !== undefined) { + result.realTimes.push({ tagRef: "sys_location_source", timestamp: time, tagValue: String(jsonStream.metadata.location.source) }); + result.realTimes.push({ tagRef: "sys_location_latitude", timestamp: time, tagValue: String(jsonStream.metadata.location.latitude) }); + result.realTimes.push({ tagRef: "sys_location_longitude", timestamp: time, tagValue: String(jsonStream.metadata.location.longitude) }); + result.realTimes.push({ tagRef: "sys_location_accuracy", timestamp: time, tagValue: String(jsonStream.metadata.location.accuracy) }); + } + } */ + if (type !== 3) { + // if KHEIRON + /* if (jsonStream.metadata.endpoint.type === 0) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).fcnt) }); + } + // if SIGFOX + if (jsonStream.metadata.endpoint.type === 5) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).seqNumber) }); + } + // if OBJENIOUS + if (jsonStream.metadata.endpoint.type === 6) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).count) }); + result.realTimes.push({ tagRef: "custom_data_raw_devEUI", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).device_properties.deveui) }); + } + */ + // Get message ID + const msgStr = parseInt(payload.substring(0, 2), 16); + + // Switch message ids + switch (msgStr) { + case 1: { + // Get meter + const battery = parseInt(payload.substring(2, 6), 16); + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery / 1000), + }); + + break; + } + case 3: { + // Get data + const battery = parseInt(payload.substring(2, 4), 16); + let temperature = parseInt(payload.substring(4, 8), 16); + const humidity = parseInt(payload.substring(8, 12), 16); + + // check if negative value + if (temperature >> 15 === 1) { + const reverseValue = temperature ^ 65535; + temperature = (reverseValue + 1) * -1; + } + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temperature / 100), + }); + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity / 100), + }); + break; + } + case 5: { + const testCounter = parseInt(payload.substring(2, 4), 16); + + result.realTimes.push({ + tagRef: "p_test", + timestamp: time, + tagValue: String(testCounter), + }); + + break; + } + case 9: { + // Get input + const di = parseInt(payload.substring(4, 6), 16); + const di1 = (di & 1) === 1; + const di2 = (di & 2) === 2; + + // get ouput + const dout = parseInt(payload.substring(2, 4), 16); + const do1 = (dout & 1) === 1; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DO1", + timestamp: time, + tagValue: String(do1), + }); + + break; + } + case 10: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + break; + } + case 11: { + break; + } + case 12: { + break; + } + case 13: { + break; + } + case 14: { + break; + } + case 21: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // Decode + temp = (temp - 33184) / 128; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + break; + } + case 20: { + // Get meter + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 48: { + // get realtime meter + const meter = parseInt(payload.substring(42, 50), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + + // get historic meter + const meter1 = parseInt(payload.substring(34, 42), 16); + const meter2 = parseInt(payload.substring(26, 34), 16); + const meter3 = parseInt(payload.substring(18, 26), 16); + const meter4 = parseInt(payload.substring(10, 18), 16); + const meter5 = parseInt(payload.substring(2, 10), 16); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 1) / 1000, + tagValue: String(meter1), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 2) / 1000, + tagValue: String(meter2), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 3) / 1000, + tagValue: String(meter3), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 4) / 1000, + tagValue: String(meter4), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 5) / 1000, + tagValue: String(meter5), + }); + + break; + } + case 49: { + // get realtime meter + const meter = parseInt(payload.substring(2, 10), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + break; + } + case 55: { + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + break; + } + case 57: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // const a = new Date(time * 1000); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (600000 + i * 600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 58: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (1800000 + i * 1800000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 59: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (3600000 + i * 3600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 22: { + // Get meter + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(4, 6), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 15: { + break; + } + case 16: { + break; + } + case 17: { + break; + } + case 18: { + break; + } + case 23: { + // Get temperature & humidity + let temp = parseInt(payload.substring(2, 6), 16); + let humidity = parseInt(payload.substring(6, 10), 16); + + // Decode + temp = (temp * 175.72) / 65536 - 46.85; + humidity = (humidity * 125) / 65536 - 6; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity), + }); + break; + } + case 24: { + break; + } + case 30: { + break; + } + case 31: { + break; + } + case 32: { + break; + } + case 33: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 10 / 64240; + + result.realTimes.push({ + tagRef: "p_voltage", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 25: { + break; + } + case 34: { + break; + } + case 35: { + break; + } + case 36: { + break; + } + case 37: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 16 / 47584; + result.realTimes.push({ + tagRef: "p_current", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 27: { + break; + } + case 42: { + break; + } + case 43: { + break; + } + case 44: { + break; + } + case 45: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + break; + } + case 26: { + break; + } + case 38: { + break; + } + case 39: { + break; + } + case 40: { + break; + } + case 41: { + break; + } + case 83: { + break; + } + case 84: { + break; + } + case 85: { + break; + } + case 86: { + break; + } + case 87: { + break; + } + case 88: { + break; + } + case 89: { + break; + } + case 90: { + break; + } + case 91: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + let temp2 = parseInt(payload.substring(6, 10), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + + // check if error + if (temp2 !== 32768) { + // check if negative value + if (temp2 >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp2 = (reverseValue + 1) * -1; + } + + // apply coef + temp2 *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature2", + timestamp: time, + tagValue: String(temp2), + }); + } + + break; + } + case 19: { + // Get water leak state + const waterleak = parseInt(payload.substring(4, 6), 16); + + // save + result.realTimes.push({ + tagRef: "p_waterLeak", + timestamp: time, + tagValue: String(waterleak), + }); + break; + } + case 47: { + // get temperature + const temp = parseInt(payload.substring(14, 18), 16); + const p_temperature = (10.888 - Math.sqrt((-10.888) ** 2 + 4 * 0.00347 * (1777.3 - temp))) / (2 * -0.00347) + 30; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + + if (parseInt(payload.substring(18, 34), 16) !== 0) { + // get data for gps decoding + const a = parseInt(payload.substring(18, 20), 16) >> 4; + const b = ((parseInt(payload.substring(18, 20), 16) << 4) & 255) >> 4; + const c = parseInt(payload.substring(20, 22), 16) >> 4; + const d = ((parseInt(payload.substring(20, 22), 16) << 4) & 255) >> 4; + const e = parseInt(payload.substring(22, 24), 16) >> 4; + const f = ((parseInt(payload.substring(22, 24), 16) << 4) & 255) >> 4; + const g = parseInt(payload.substring(24, 26), 16) >> 4; + const h = ((parseInt(payload.substring(24, 26), 16) << 4) & 255) >> 4; + const i = parseInt(payload.substring(26, 28), 16) >> 4; + const j = ((parseInt(payload.substring(26, 28), 16) << 4) & 255) >> 4; + const k = parseInt(payload.substring(28, 30), 16) >> 4; + const l = ((parseInt(payload.substring(28, 30), 16) << 4) & 255) >> 4; + const m = parseInt(payload.substring(30, 32), 16) >> 4; + const n = ((parseInt(payload.substring(30, 32), 16) << 4) & 255) >> 4; + const o = parseInt(payload.substring(32, 34), 16) >> 4; + // const p = ((parseInt(payload.substring(32, 34), 16) << 4) & 255) >> 6; + const q = ((parseInt(payload.substring(32, 34), 16) << 6) & 255) >> 7; + const r = ((parseInt(payload.substring(32, 34), 16) << 7) & 255) >> 7; + // get latitude and longitude + const latitude = (2 * q - 1) * (10 * a + b + c / 6 + d / 60 + e / 600 + f / 6000 + g / 60000); + const longitude = (2 * r - 1) * (100 * h + 10 * i + j + k / 6 + l / 60 + m / 600 + n / 6000 + o / 60000); + + // save + result.realTimes.push({ + tagRef: "p_latitude", + timestamp: time, + tagValue: String(latitude), + }); + result.realTimes.push({ + tagRef: "p_longitude", + timestamp: time, + tagValue: String(longitude), + }); + } + + break; + } + case 50: { + const p_vibration = parseInt(payload.substring(4, 6), 16); + const p_ils = parseInt(payload.substring(6, 8), 16); + const p_temperature = (2103 - parseInt(payload.substring(8, 12), 16)) / 10.9; + + // save + result.realTimes.push({ + tagRef: "p_vibration", + timestamp: time, + tagValue: String(p_vibration), + }); + result.realTimes.push({ + tagRef: "p_ils", + timestamp: time, + tagValue: String(p_ils), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + break; + } + case 51: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + break; + } + case 52: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get count + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(parseInt(payload.substring(4, 12), 16)), + }); + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(parseInt(payload.substring(12, 20), 16)), + }); + break; + } + case 53: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 54: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 62: { + // Get Absence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(0), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + const valueWorZ = parseInt(payload.substring(10, 14), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + + // Save WorZ + result.realTimes.push({ + tagRef: "p_WorZ", + timestamp: time, + tagValue: String(valueWorZ), + }); + break; + } + case 63: { + // Get Presence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(1), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + break; + } + case 65: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + break; + } + case 66: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + break; + } + case 67: { + // save + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time - 1, + tagValue: String(1), + context: [], + }); + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time, + tagValue: String(0), + context: [], + }); + break; + } + case 78: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + // get count 1 + const count1 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 79: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(14, 22), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 80: { + // get count 1 + const count1 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 81: { + // get count 3 + const count3 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count3", + timestamp: time, + tagValue: String(count3), + }); + + // get count 4 + const count4 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count4", + timestamp: time, + tagValue: String(count4), + }); + break; + } + case 82: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 93: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(2 + 8 * j, 10 + 8 * j), 16)), + }); + } + break; + } + case 94: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(-2 + 8 * j, 6 + 8 * j), 16)), + }); + } + break; + } + case 95: { + // get count 5 + const count5 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count5", + timestamp: time, + tagValue: String(count5), + }); + + // get count 6 + const count6 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count6", + timestamp: time, + tagValue: String(count6), + }); + break; + } + case 96: { + // get count 7 + const count7 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count7", + timestamp: time, + tagValue: String(count7), + }); + + // get count 8 + const count8 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count8", + timestamp: time, + tagValue: String(count8), + }); + break; + } + case 2: + case 4: + case 6: + case 7: + case 8: + case 46: + case 56: + case 60: + case 61: + case 64: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 92: + case 97: + case 98: + case 99: + default: + break; + } + } + // Return result + return result; +} + +function ToTagoFormat(object_item, serie) { + const historics = []; + const events = []; + const realTimes = []; + // eslint-disable-next-line guard-for-in + for (const key in object_item.realTimes) { + realTimes.push({ + variable: `realTimes_${object_item.realTimes[key].tagRef}`.toLowerCase(), + value: object_item.realTimes[key].tagValue, + time: object_item.realTimes[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.events) { + events.push({ + variable: `events_${object_item.events[key].tagRef}`.toLowerCase(), + value: object_item.events[key].tagValue, + time: object_item.events[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.historics) { + historics.push({ + variable: `historics_${object_item.historics[key].tagRef}`.toLowerCase(), + value: object_item.historics[key].tagValue, + time: object_item.historics[key].timestamp, + serie, + }); + } + const result = historics.concat(events, realTimes); + + return result; +} + +/* let payload = [ + { variable: "payload", value: "5e7f000000003f00000040000000410000004200000043000000440000004500000046" }, + { variable: "type", value: 0 }, + { variable: "timestamp", value: 1605614318410 }, +]; */ + +const data = payload.find((x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data"); +const type = payload.find((x) => x.variable === "type"); +const timestamp = payload.find((x) => x.variable === "timestamp"); + +if (data) { + // const buffer = Buffer.from(data.value, "hex"); + const serie = new Date().getTime(); + // payload = decodeStream(data.value, type.value, timestamp.value); + payload = ToTagoFormat(decodeStream(data.value, type.value, timestamp.value), serie); +} + +// eslint-disable-next-line no-console +// console.log(payload); diff --git a/decoders/connector/atim/dind80/assets/logo.png b/decoders/connector/atim/dind80/assets/logo.png new file mode 100644 index 00000000..e12de1b6 Binary files /dev/null and b/decoders/connector/atim/dind80/assets/logo.png differ diff --git a/decoders/connector/atim/dind80/connector.jsonc b/decoders/connector/atim/dind80/connector.jsonc new file mode 100644 index 00000000..60ef8b54 --- /dev/null +++ b/decoders/connector/atim/dind80/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Atim DIND80", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/atim/dind80/description.md b/decoders/connector/atim/dind80/description.md new file mode 100644 index 00000000..3bf30fe7 --- /dev/null +++ b/decoders/connector/atim/dind80/description.md @@ -0,0 +1 @@ +Digital Input/Output and Metering over LoRaWAN or Sigfox \ No newline at end of file diff --git a/decoders/connector/atim/dind80/v1.0.0/payload-config.jsonc b/decoders/connector/atim/dind80/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..ba3c8661 --- /dev/null +++ b/decoders/connector/atim/dind80/v1.0.0/payload-config.jsonc @@ -0,0 +1,26 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "The ACW-DIND80 monitors the status of dry contacts status and meters’ index consumption and control digital outputs (open collector, push / pull and dry contact type) and send the data to LoRa or Sigfox. Remotely control equipment or PLCs using the dry contacts outputs. Possibility to add a digital temperature probe via jack plug (option). Compatible with Sigfox repeater (ACW-GW). Contains 8 inputs.\n\n\n**Technical Data**\n* Dimensions: 53 x 67 x 95 mm\n* Antenna: External (SMA connector)\n* Temperature: -20°C to +55°C (operation), -40°C to +70°C (storage)\n* Mounts to: DIN rail compliant\n* Power supply: 1x 10-30Vcc\n* Consumption: 100mA\n* Digital inputs TOR: 8 configurable inputs, change of state alerting, 8 meters configurable on whatever input, available activation of a group of inputs\n* Alarm: Wrenching / Shock\n* Frequency: 865-870 MHz\n* Power: 25 mW (14 dBm)\n* Transfer rate: Sigfox: 100 bit/s, LoRaWAN: 300 bit/s to 10 kbit/s", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/sigfox/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/atim/dind80/v1.0.0/payload.js b/decoders/connector/atim/dind80/v1.0.0/payload.js new file mode 100644 index 00000000..58709750 --- /dev/null +++ b/decoders/connector/atim/dind80/v1.0.0/payload.js @@ -0,0 +1,1332 @@ +/* eslint-disable no-plusplus */ +/* eslint-disable no-bitwise */ +function decodeStream(payload, type, timestamp) { + // Init result + const result = { historics: [], events: [], realTimes: [] }; + + // Parse stream + // const jsonStream = JSON.parse(stream); + + // Get time and payload + // const time = new Date(jsonStream.data.timestamp * 1000) / 1000; + const time = new Date(timestamp * 1000) / 1000; + // const { payload } = jsonStream.data; + + // Save network informations + result.realTimes.push({ tagRef: "sys_data_payload", timestamp: time, tagValue: String(payload) }); + result.realTimes.push({ tagRef: "sys_data_timestamp", timestamp: time, tagValue: String(time) }); + /* if (jsonStream.data !== undefined) { + result.realTimes.push({ tagRef: "sys_data_type", timestamp: time, tagValue: String(jsonStream.data.type) }); + result.realTimes.push({ tagRef: "sys_data_raw", timestamp: time, tagValue: String(jsonStream.data.raw) }); + } + if (jsonStream.metadata !== undefined) { + result.realTimes.push({ tagRef: "sys_metadata_lastStreamTimestampUtc", timestamp: time, tagValue: String(jsonStream.metadata.lastStreamTimestampUtc) }); + if (jsonStream.metadata.device !== undefined) { + result.realTimes.push({ tagRef: "sys_device_sn", timestamp: time, tagValue: String(jsonStream.metadata.device.serialNumber) }); + result.realTimes.push({ tagRef: "sys_device_name", timestamp: time, tagValue: String(jsonStream.metadata.device.name) }); + } + if (jsonStream.metadata.endpoint !== undefined) { + result.realTimes.push({ tagRef: "sys_endpoint_name", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.name) }); + result.realTimes.push({ tagRef: "sys_endpoint_type", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.type) }); + } + if (jsonStream.metadata.network !== undefined) { + result.realTimes.push({ tagRef: "sys_network_port", timestamp: time, tagValue: String(jsonStream.metadata.network.port) }); + result.realTimes.push({ tagRef: "sys_network_linkQuality", timestamp: time, tagValue: String(jsonStream.metadata.network.linkQuality) }); + } + if (jsonStream.metadata.location !== undefined) { + result.realTimes.push({ tagRef: "sys_location_source", timestamp: time, tagValue: String(jsonStream.metadata.location.source) }); + result.realTimes.push({ tagRef: "sys_location_latitude", timestamp: time, tagValue: String(jsonStream.metadata.location.latitude) }); + result.realTimes.push({ tagRef: "sys_location_longitude", timestamp: time, tagValue: String(jsonStream.metadata.location.longitude) }); + result.realTimes.push({ tagRef: "sys_location_accuracy", timestamp: time, tagValue: String(jsonStream.metadata.location.accuracy) }); + } + } */ + if (type !== 3) { + // if KHEIRON + /* if (jsonStream.metadata.endpoint.type === 0) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).fcnt) }); + } + // if SIGFOX + if (jsonStream.metadata.endpoint.type === 5) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).seqNumber) }); + } + // if OBJENIOUS + if (jsonStream.metadata.endpoint.type === 6) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).count) }); + result.realTimes.push({ tagRef: "custom_data_raw_devEUI", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).device_properties.deveui) }); + } + */ + // Get message ID + const msgStr = parseInt(payload.substring(0, 2), 16); + + // Switch message ids + switch (msgStr) { + case 1: { + // Get meter + const battery = parseInt(payload.substring(2, 6), 16); + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery / 1000), + }); + + break; + } + case 3: { + // Get data + const battery = parseInt(payload.substring(2, 4), 16); + let temperature = parseInt(payload.substring(4, 8), 16); + const humidity = parseInt(payload.substring(8, 12), 16); + + // check if negative value + if (temperature >> 15 === 1) { + const reverseValue = temperature ^ 65535; + temperature = (reverseValue + 1) * -1; + } + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temperature / 100), + }); + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity / 100), + }); + break; + } + case 5: { + const testCounter = parseInt(payload.substring(2, 4), 16); + + result.realTimes.push({ + tagRef: "p_test", + timestamp: time, + tagValue: String(testCounter), + }); + + break; + } + case 9: { + // Get input + const di = parseInt(payload.substring(4, 6), 16); + const di1 = (di & 1) === 1; + const di2 = (di & 2) === 2; + + // get ouput + const dout = parseInt(payload.substring(2, 4), 16); + const do1 = (dout & 1) === 1; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DO1", + timestamp: time, + tagValue: String(do1), + }); + + break; + } + case 10: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + break; + } + case 11: { + break; + } + case 12: { + break; + } + case 13: { + break; + } + case 14: { + break; + } + case 21: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // Decode + temp = (temp - 33184) / 128; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + break; + } + case 20: { + // Get meter + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 48: { + // get realtime meter + const meter = parseInt(payload.substring(42, 50), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + + // get historic meter + const meter1 = parseInt(payload.substring(34, 42), 16); + const meter2 = parseInt(payload.substring(26, 34), 16); + const meter3 = parseInt(payload.substring(18, 26), 16); + const meter4 = parseInt(payload.substring(10, 18), 16); + const meter5 = parseInt(payload.substring(2, 10), 16); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 1) / 1000, + tagValue: String(meter1), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 2) / 1000, + tagValue: String(meter2), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 3) / 1000, + tagValue: String(meter3), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 4) / 1000, + tagValue: String(meter4), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 5) / 1000, + tagValue: String(meter5), + }); + + break; + } + case 49: { + // get realtime meter + const meter = parseInt(payload.substring(2, 10), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + break; + } + case 55: { + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + break; + } + case 57: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // const a = new Date(time * 1000); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (600000 + i * 600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 58: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (1800000 + i * 1800000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 59: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (3600000 + i * 3600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 22: { + // Get meter + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(4, 6), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 15: { + break; + } + case 16: { + break; + } + case 17: { + break; + } + case 18: { + break; + } + case 23: { + // Get temperature & humidity + let temp = parseInt(payload.substring(2, 6), 16); + let humidity = parseInt(payload.substring(6, 10), 16); + + // Decode + temp = (temp * 175.72) / 65536 - 46.85; + humidity = (humidity * 125) / 65536 - 6; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity), + }); + break; + } + case 24: { + break; + } + case 30: { + break; + } + case 31: { + break; + } + case 32: { + break; + } + case 33: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 10 / 64240; + + result.realTimes.push({ + tagRef: "p_voltage", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 25: { + break; + } + case 34: { + break; + } + case 35: { + break; + } + case 36: { + break; + } + case 37: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 16 / 47584; + result.realTimes.push({ + tagRef: "p_current", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 27: { + break; + } + case 42: { + break; + } + case 43: { + break; + } + case 44: { + break; + } + case 45: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + break; + } + case 26: { + break; + } + case 38: { + break; + } + case 39: { + break; + } + case 40: { + break; + } + case 41: { + break; + } + case 83: { + break; + } + case 84: { + break; + } + case 85: { + break; + } + case 86: { + break; + } + case 87: { + break; + } + case 88: { + break; + } + case 89: { + break; + } + case 90: { + break; + } + case 91: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + let temp2 = parseInt(payload.substring(6, 10), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + + // check if error + if (temp2 !== 32768) { + // check if negative value + if (temp2 >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp2 = (reverseValue + 1) * -1; + } + + // apply coef + temp2 *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature2", + timestamp: time, + tagValue: String(temp2), + }); + } + + break; + } + case 19: { + // Get water leak state + const waterleak = parseInt(payload.substring(4, 6), 16); + + // save + result.realTimes.push({ + tagRef: "p_waterLeak", + timestamp: time, + tagValue: String(waterleak), + }); + break; + } + case 47: { + // get temperature + const temp = parseInt(payload.substring(14, 18), 16); + const p_temperature = (10.888 - Math.sqrt((-10.888) ** 2 + 4 * 0.00347 * (1777.3 - temp))) / (2 * -0.00347) + 30; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + + if (parseInt(payload.substring(18, 34), 16) !== 0) { + // get data for gps decoding + const a = parseInt(payload.substring(18, 20), 16) >> 4; + const b = ((parseInt(payload.substring(18, 20), 16) << 4) & 255) >> 4; + const c = parseInt(payload.substring(20, 22), 16) >> 4; + const d = ((parseInt(payload.substring(20, 22), 16) << 4) & 255) >> 4; + const e = parseInt(payload.substring(22, 24), 16) >> 4; + const f = ((parseInt(payload.substring(22, 24), 16) << 4) & 255) >> 4; + const g = parseInt(payload.substring(24, 26), 16) >> 4; + const h = ((parseInt(payload.substring(24, 26), 16) << 4) & 255) >> 4; + const i = parseInt(payload.substring(26, 28), 16) >> 4; + const j = ((parseInt(payload.substring(26, 28), 16) << 4) & 255) >> 4; + const k = parseInt(payload.substring(28, 30), 16) >> 4; + const l = ((parseInt(payload.substring(28, 30), 16) << 4) & 255) >> 4; + const m = parseInt(payload.substring(30, 32), 16) >> 4; + const n = ((parseInt(payload.substring(30, 32), 16) << 4) & 255) >> 4; + const o = parseInt(payload.substring(32, 34), 16) >> 4; + // const p = ((parseInt(payload.substring(32, 34), 16) << 4) & 255) >> 6; + const q = ((parseInt(payload.substring(32, 34), 16) << 6) & 255) >> 7; + const r = ((parseInt(payload.substring(32, 34), 16) << 7) & 255) >> 7; + // get latitude and longitude + const latitude = (2 * q - 1) * (10 * a + b + c / 6 + d / 60 + e / 600 + f / 6000 + g / 60000); + const longitude = (2 * r - 1) * (100 * h + 10 * i + j + k / 6 + l / 60 + m / 600 + n / 6000 + o / 60000); + + // save + result.realTimes.push({ + tagRef: "p_latitude", + timestamp: time, + tagValue: String(latitude), + }); + result.realTimes.push({ + tagRef: "p_longitude", + timestamp: time, + tagValue: String(longitude), + }); + } + + break; + } + case 50: { + const p_vibration = parseInt(payload.substring(4, 6), 16); + const p_ils = parseInt(payload.substring(6, 8), 16); + const p_temperature = (2103 - parseInt(payload.substring(8, 12), 16)) / 10.9; + + // save + result.realTimes.push({ + tagRef: "p_vibration", + timestamp: time, + tagValue: String(p_vibration), + }); + result.realTimes.push({ + tagRef: "p_ils", + timestamp: time, + tagValue: String(p_ils), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + break; + } + case 51: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + break; + } + case 52: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get count + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(parseInt(payload.substring(4, 12), 16)), + }); + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(parseInt(payload.substring(12, 20), 16)), + }); + break; + } + case 53: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 54: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 62: { + // Get Absence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(0), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + const valueWorZ = parseInt(payload.substring(10, 14), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + + // Save WorZ + result.realTimes.push({ + tagRef: "p_WorZ", + timestamp: time, + tagValue: String(valueWorZ), + }); + break; + } + case 63: { + // Get Presence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(1), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + break; + } + case 65: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + break; + } + case 66: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + break; + } + case 67: { + // save + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time - 1, + tagValue: String(1), + context: [], + }); + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time, + tagValue: String(0), + context: [], + }); + break; + } + case 78: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + // get count 1 + const count1 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 79: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(14, 22), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 80: { + // get count 1 + const count1 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 81: { + // get count 3 + const count3 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count3", + timestamp: time, + tagValue: String(count3), + }); + + // get count 4 + const count4 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count4", + timestamp: time, + tagValue: String(count4), + }); + break; + } + case 82: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 93: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(2 + 8 * j, 10 + 8 * j), 16)), + }); + } + break; + } + case 94: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(-2 + 8 * j, 6 + 8 * j), 16)), + }); + } + break; + } + case 95: { + // get count 5 + const count5 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count5", + timestamp: time, + tagValue: String(count5), + }); + + // get count 6 + const count6 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count6", + timestamp: time, + tagValue: String(count6), + }); + break; + } + case 96: { + // get count 7 + const count7 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count7", + timestamp: time, + tagValue: String(count7), + }); + + // get count 8 + const count8 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count8", + timestamp: time, + tagValue: String(count8), + }); + break; + } + case 2: + case 4: + case 6: + case 7: + case 8: + case 46: + case 56: + case 60: + case 61: + case 64: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 92: + case 97: + case 98: + case 99: + default: + break; + } + } + // Return result + return result; +} + +function ToTagoFormat(object_item, serie) { + const historics = []; + const events = []; + const realTimes = []; + // eslint-disable-next-line guard-for-in + for (const key in object_item.realTimes) { + realTimes.push({ + variable: `realTimes_${object_item.realTimes[key].tagRef}`.toLowerCase(), + value: object_item.realTimes[key].tagValue, + time: object_item.realTimes[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.events) { + events.push({ + variable: `events_${object_item.events[key].tagRef}`.toLowerCase(), + value: object_item.events[key].tagValue, + time: object_item.events[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.historics) { + historics.push({ + variable: `historics_${object_item.historics[key].tagRef}`.toLowerCase(), + value: object_item.historics[key].tagValue, + time: object_item.historics[key].timestamp, + serie, + }); + } + const result = historics.concat(events, realTimes); + + return result; +} + +/* let payload = [ + { variable: "payload", value: "5e7f000000003f00000040000000410000004200000043000000440000004500000046" }, + { variable: "type", value: 0 }, + { variable: "timestamp", value: 1605614318410 }, +]; */ + +const data = payload.find((x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data"); +const type = payload.find((x) => x.variable === "type"); +const timestamp = payload.find((x) => x.variable === "timestamp"); + +if (data) { + // const buffer = Buffer.from(data.value, "hex"); + const serie = new Date().getTime(); + // payload = decodeStream(data.value, type.value, timestamp.value); + payload = ToTagoFormat(decodeStream(data.value, type.value, timestamp.value), serie); +} + +// eslint-disable-next-line no-console +// console.log(payload); diff --git a/decoders/connector/atim/dind88/assets/logo.png b/decoders/connector/atim/dind88/assets/logo.png new file mode 100644 index 00000000..e12de1b6 Binary files /dev/null and b/decoders/connector/atim/dind88/assets/logo.png differ diff --git a/decoders/connector/atim/dind88/connector.jsonc b/decoders/connector/atim/dind88/connector.jsonc new file mode 100644 index 00000000..3a5d699c --- /dev/null +++ b/decoders/connector/atim/dind88/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Atim DIND88", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/atim/dind88/description.md b/decoders/connector/atim/dind88/description.md new file mode 100644 index 00000000..ac5952b7 --- /dev/null +++ b/decoders/connector/atim/dind88/description.md @@ -0,0 +1 @@ +Dry contacts and Metering over LoRaWAN or Sigfox \ No newline at end of file diff --git a/decoders/connector/atim/dind88/v1.0.0/payload-config.jsonc b/decoders/connector/atim/dind88/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..b7790c21 --- /dev/null +++ b/decoders/connector/atim/dind88/v1.0.0/payload-config.jsonc @@ -0,0 +1,26 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "The ACW-DIND88 monitors the status of dry contacts status and meters’ index consumption and control digital outputs (open collector, push / pull and dry contact type) and send the data to LoRa or Sigfox. Remotely control equipment or PLCs using the dry contacts outputs. Possibility to add a digital temperature probe via jack plug (option). Compatible with Sigfox repeater (ACW-GW). Contains 8 inputs and 8 outputs.\n\n\n**Technical Data**\n* Dimensions: 53 x 67 x 95 mm\n* Antenna: External (SMA connector)\n* Temperature: -20°C to +55°C (operation), -40°C to +70°C (storage)\n* Mounts to: DIN rail compliant\n* Power supply: 1x 10-30Vcc\n* Consumption: 100mA\n* Digital inputs TOR: 8 configurable inputs, change of state alerting, 8 meters configurable on whatever input, available activation of a group of inputs\n* Digital outputs TOR: 8 outputs driven via downlink\n* Alarm: Wrenching / Shock\n* Frequency: 865-870 MHz\n* Power: 25 mW (14 dBm)\n* Transfer rate: Sigfox: 100 bit/s, LoRaWAN: 300 bit/s to 10 kbit/s", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/sigfox/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/atim/dind88/v1.0.0/payload.js b/decoders/connector/atim/dind88/v1.0.0/payload.js new file mode 100644 index 00000000..58709750 --- /dev/null +++ b/decoders/connector/atim/dind88/v1.0.0/payload.js @@ -0,0 +1,1332 @@ +/* eslint-disable no-plusplus */ +/* eslint-disable no-bitwise */ +function decodeStream(payload, type, timestamp) { + // Init result + const result = { historics: [], events: [], realTimes: [] }; + + // Parse stream + // const jsonStream = JSON.parse(stream); + + // Get time and payload + // const time = new Date(jsonStream.data.timestamp * 1000) / 1000; + const time = new Date(timestamp * 1000) / 1000; + // const { payload } = jsonStream.data; + + // Save network informations + result.realTimes.push({ tagRef: "sys_data_payload", timestamp: time, tagValue: String(payload) }); + result.realTimes.push({ tagRef: "sys_data_timestamp", timestamp: time, tagValue: String(time) }); + /* if (jsonStream.data !== undefined) { + result.realTimes.push({ tagRef: "sys_data_type", timestamp: time, tagValue: String(jsonStream.data.type) }); + result.realTimes.push({ tagRef: "sys_data_raw", timestamp: time, tagValue: String(jsonStream.data.raw) }); + } + if (jsonStream.metadata !== undefined) { + result.realTimes.push({ tagRef: "sys_metadata_lastStreamTimestampUtc", timestamp: time, tagValue: String(jsonStream.metadata.lastStreamTimestampUtc) }); + if (jsonStream.metadata.device !== undefined) { + result.realTimes.push({ tagRef: "sys_device_sn", timestamp: time, tagValue: String(jsonStream.metadata.device.serialNumber) }); + result.realTimes.push({ tagRef: "sys_device_name", timestamp: time, tagValue: String(jsonStream.metadata.device.name) }); + } + if (jsonStream.metadata.endpoint !== undefined) { + result.realTimes.push({ tagRef: "sys_endpoint_name", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.name) }); + result.realTimes.push({ tagRef: "sys_endpoint_type", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.type) }); + } + if (jsonStream.metadata.network !== undefined) { + result.realTimes.push({ tagRef: "sys_network_port", timestamp: time, tagValue: String(jsonStream.metadata.network.port) }); + result.realTimes.push({ tagRef: "sys_network_linkQuality", timestamp: time, tagValue: String(jsonStream.metadata.network.linkQuality) }); + } + if (jsonStream.metadata.location !== undefined) { + result.realTimes.push({ tagRef: "sys_location_source", timestamp: time, tagValue: String(jsonStream.metadata.location.source) }); + result.realTimes.push({ tagRef: "sys_location_latitude", timestamp: time, tagValue: String(jsonStream.metadata.location.latitude) }); + result.realTimes.push({ tagRef: "sys_location_longitude", timestamp: time, tagValue: String(jsonStream.metadata.location.longitude) }); + result.realTimes.push({ tagRef: "sys_location_accuracy", timestamp: time, tagValue: String(jsonStream.metadata.location.accuracy) }); + } + } */ + if (type !== 3) { + // if KHEIRON + /* if (jsonStream.metadata.endpoint.type === 0) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).fcnt) }); + } + // if SIGFOX + if (jsonStream.metadata.endpoint.type === 5) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).seqNumber) }); + } + // if OBJENIOUS + if (jsonStream.metadata.endpoint.type === 6) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).count) }); + result.realTimes.push({ tagRef: "custom_data_raw_devEUI", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).device_properties.deveui) }); + } + */ + // Get message ID + const msgStr = parseInt(payload.substring(0, 2), 16); + + // Switch message ids + switch (msgStr) { + case 1: { + // Get meter + const battery = parseInt(payload.substring(2, 6), 16); + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery / 1000), + }); + + break; + } + case 3: { + // Get data + const battery = parseInt(payload.substring(2, 4), 16); + let temperature = parseInt(payload.substring(4, 8), 16); + const humidity = parseInt(payload.substring(8, 12), 16); + + // check if negative value + if (temperature >> 15 === 1) { + const reverseValue = temperature ^ 65535; + temperature = (reverseValue + 1) * -1; + } + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temperature / 100), + }); + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity / 100), + }); + break; + } + case 5: { + const testCounter = parseInt(payload.substring(2, 4), 16); + + result.realTimes.push({ + tagRef: "p_test", + timestamp: time, + tagValue: String(testCounter), + }); + + break; + } + case 9: { + // Get input + const di = parseInt(payload.substring(4, 6), 16); + const di1 = (di & 1) === 1; + const di2 = (di & 2) === 2; + + // get ouput + const dout = parseInt(payload.substring(2, 4), 16); + const do1 = (dout & 1) === 1; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DO1", + timestamp: time, + tagValue: String(do1), + }); + + break; + } + case 10: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + break; + } + case 11: { + break; + } + case 12: { + break; + } + case 13: { + break; + } + case 14: { + break; + } + case 21: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // Decode + temp = (temp - 33184) / 128; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + break; + } + case 20: { + // Get meter + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 48: { + // get realtime meter + const meter = parseInt(payload.substring(42, 50), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + + // get historic meter + const meter1 = parseInt(payload.substring(34, 42), 16); + const meter2 = parseInt(payload.substring(26, 34), 16); + const meter3 = parseInt(payload.substring(18, 26), 16); + const meter4 = parseInt(payload.substring(10, 18), 16); + const meter5 = parseInt(payload.substring(2, 10), 16); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 1) / 1000, + tagValue: String(meter1), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 2) / 1000, + tagValue: String(meter2), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 3) / 1000, + tagValue: String(meter3), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 4) / 1000, + tagValue: String(meter4), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 5) / 1000, + tagValue: String(meter5), + }); + + break; + } + case 49: { + // get realtime meter + const meter = parseInt(payload.substring(2, 10), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + break; + } + case 55: { + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + break; + } + case 57: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // const a = new Date(time * 1000); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (600000 + i * 600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 58: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (1800000 + i * 1800000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 59: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (3600000 + i * 3600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 22: { + // Get meter + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(4, 6), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 15: { + break; + } + case 16: { + break; + } + case 17: { + break; + } + case 18: { + break; + } + case 23: { + // Get temperature & humidity + let temp = parseInt(payload.substring(2, 6), 16); + let humidity = parseInt(payload.substring(6, 10), 16); + + // Decode + temp = (temp * 175.72) / 65536 - 46.85; + humidity = (humidity * 125) / 65536 - 6; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity), + }); + break; + } + case 24: { + break; + } + case 30: { + break; + } + case 31: { + break; + } + case 32: { + break; + } + case 33: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 10 / 64240; + + result.realTimes.push({ + tagRef: "p_voltage", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 25: { + break; + } + case 34: { + break; + } + case 35: { + break; + } + case 36: { + break; + } + case 37: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 16 / 47584; + result.realTimes.push({ + tagRef: "p_current", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 27: { + break; + } + case 42: { + break; + } + case 43: { + break; + } + case 44: { + break; + } + case 45: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + break; + } + case 26: { + break; + } + case 38: { + break; + } + case 39: { + break; + } + case 40: { + break; + } + case 41: { + break; + } + case 83: { + break; + } + case 84: { + break; + } + case 85: { + break; + } + case 86: { + break; + } + case 87: { + break; + } + case 88: { + break; + } + case 89: { + break; + } + case 90: { + break; + } + case 91: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + let temp2 = parseInt(payload.substring(6, 10), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + + // check if error + if (temp2 !== 32768) { + // check if negative value + if (temp2 >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp2 = (reverseValue + 1) * -1; + } + + // apply coef + temp2 *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature2", + timestamp: time, + tagValue: String(temp2), + }); + } + + break; + } + case 19: { + // Get water leak state + const waterleak = parseInt(payload.substring(4, 6), 16); + + // save + result.realTimes.push({ + tagRef: "p_waterLeak", + timestamp: time, + tagValue: String(waterleak), + }); + break; + } + case 47: { + // get temperature + const temp = parseInt(payload.substring(14, 18), 16); + const p_temperature = (10.888 - Math.sqrt((-10.888) ** 2 + 4 * 0.00347 * (1777.3 - temp))) / (2 * -0.00347) + 30; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + + if (parseInt(payload.substring(18, 34), 16) !== 0) { + // get data for gps decoding + const a = parseInt(payload.substring(18, 20), 16) >> 4; + const b = ((parseInt(payload.substring(18, 20), 16) << 4) & 255) >> 4; + const c = parseInt(payload.substring(20, 22), 16) >> 4; + const d = ((parseInt(payload.substring(20, 22), 16) << 4) & 255) >> 4; + const e = parseInt(payload.substring(22, 24), 16) >> 4; + const f = ((parseInt(payload.substring(22, 24), 16) << 4) & 255) >> 4; + const g = parseInt(payload.substring(24, 26), 16) >> 4; + const h = ((parseInt(payload.substring(24, 26), 16) << 4) & 255) >> 4; + const i = parseInt(payload.substring(26, 28), 16) >> 4; + const j = ((parseInt(payload.substring(26, 28), 16) << 4) & 255) >> 4; + const k = parseInt(payload.substring(28, 30), 16) >> 4; + const l = ((parseInt(payload.substring(28, 30), 16) << 4) & 255) >> 4; + const m = parseInt(payload.substring(30, 32), 16) >> 4; + const n = ((parseInt(payload.substring(30, 32), 16) << 4) & 255) >> 4; + const o = parseInt(payload.substring(32, 34), 16) >> 4; + // const p = ((parseInt(payload.substring(32, 34), 16) << 4) & 255) >> 6; + const q = ((parseInt(payload.substring(32, 34), 16) << 6) & 255) >> 7; + const r = ((parseInt(payload.substring(32, 34), 16) << 7) & 255) >> 7; + // get latitude and longitude + const latitude = (2 * q - 1) * (10 * a + b + c / 6 + d / 60 + e / 600 + f / 6000 + g / 60000); + const longitude = (2 * r - 1) * (100 * h + 10 * i + j + k / 6 + l / 60 + m / 600 + n / 6000 + o / 60000); + + // save + result.realTimes.push({ + tagRef: "p_latitude", + timestamp: time, + tagValue: String(latitude), + }); + result.realTimes.push({ + tagRef: "p_longitude", + timestamp: time, + tagValue: String(longitude), + }); + } + + break; + } + case 50: { + const p_vibration = parseInt(payload.substring(4, 6), 16); + const p_ils = parseInt(payload.substring(6, 8), 16); + const p_temperature = (2103 - parseInt(payload.substring(8, 12), 16)) / 10.9; + + // save + result.realTimes.push({ + tagRef: "p_vibration", + timestamp: time, + tagValue: String(p_vibration), + }); + result.realTimes.push({ + tagRef: "p_ils", + timestamp: time, + tagValue: String(p_ils), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + break; + } + case 51: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + break; + } + case 52: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get count + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(parseInt(payload.substring(4, 12), 16)), + }); + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(parseInt(payload.substring(12, 20), 16)), + }); + break; + } + case 53: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 54: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 62: { + // Get Absence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(0), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + const valueWorZ = parseInt(payload.substring(10, 14), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + + // Save WorZ + result.realTimes.push({ + tagRef: "p_WorZ", + timestamp: time, + tagValue: String(valueWorZ), + }); + break; + } + case 63: { + // Get Presence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(1), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + break; + } + case 65: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + break; + } + case 66: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + break; + } + case 67: { + // save + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time - 1, + tagValue: String(1), + context: [], + }); + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time, + tagValue: String(0), + context: [], + }); + break; + } + case 78: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + // get count 1 + const count1 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 79: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(14, 22), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 80: { + // get count 1 + const count1 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 81: { + // get count 3 + const count3 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count3", + timestamp: time, + tagValue: String(count3), + }); + + // get count 4 + const count4 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count4", + timestamp: time, + tagValue: String(count4), + }); + break; + } + case 82: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 93: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(2 + 8 * j, 10 + 8 * j), 16)), + }); + } + break; + } + case 94: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(-2 + 8 * j, 6 + 8 * j), 16)), + }); + } + break; + } + case 95: { + // get count 5 + const count5 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count5", + timestamp: time, + tagValue: String(count5), + }); + + // get count 6 + const count6 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count6", + timestamp: time, + tagValue: String(count6), + }); + break; + } + case 96: { + // get count 7 + const count7 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count7", + timestamp: time, + tagValue: String(count7), + }); + + // get count 8 + const count8 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count8", + timestamp: time, + tagValue: String(count8), + }); + break; + } + case 2: + case 4: + case 6: + case 7: + case 8: + case 46: + case 56: + case 60: + case 61: + case 64: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 92: + case 97: + case 98: + case 99: + default: + break; + } + } + // Return result + return result; +} + +function ToTagoFormat(object_item, serie) { + const historics = []; + const events = []; + const realTimes = []; + // eslint-disable-next-line guard-for-in + for (const key in object_item.realTimes) { + realTimes.push({ + variable: `realTimes_${object_item.realTimes[key].tagRef}`.toLowerCase(), + value: object_item.realTimes[key].tagValue, + time: object_item.realTimes[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.events) { + events.push({ + variable: `events_${object_item.events[key].tagRef}`.toLowerCase(), + value: object_item.events[key].tagValue, + time: object_item.events[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.historics) { + historics.push({ + variable: `historics_${object_item.historics[key].tagRef}`.toLowerCase(), + value: object_item.historics[key].tagValue, + time: object_item.historics[key].timestamp, + serie, + }); + } + const result = historics.concat(events, realTimes); + + return result; +} + +/* let payload = [ + { variable: "payload", value: "5e7f000000003f00000040000000410000004200000043000000440000004500000046" }, + { variable: "type", value: 0 }, + { variable: "timestamp", value: 1605614318410 }, +]; */ + +const data = payload.find((x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data"); +const type = payload.find((x) => x.variable === "type"); +const timestamp = payload.find((x) => x.variable === "timestamp"); + +if (data) { + // const buffer = Buffer.from(data.value, "hex"); + const serie = new Date().getTime(); + // payload = decodeStream(data.value, type.value, timestamp.value); + payload = ToTagoFormat(decodeStream(data.value, type.value, timestamp.value), serie); +} + +// eslint-disable-next-line no-console +// console.log(payload); diff --git a/decoders/connector/atim/dinda/assets/logo.png b/decoders/connector/atim/dinda/assets/logo.png new file mode 100644 index 00000000..6d64ecd8 Binary files /dev/null and b/decoders/connector/atim/dinda/assets/logo.png differ diff --git a/decoders/connector/atim/dinda/connector.jsonc b/decoders/connector/atim/dinda/connector.jsonc new file mode 100644 index 00000000..1e74732f --- /dev/null +++ b/decoders/connector/atim/dinda/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Atim DINDA", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/atim/dinda/description.md b/decoders/connector/atim/dinda/description.md new file mode 100644 index 00000000..887055e8 --- /dev/null +++ b/decoders/connector/atim/dinda/description.md @@ -0,0 +1 @@ +Analog input reporting over LoRaWAN or Sigfox \ No newline at end of file diff --git a/decoders/connector/atim/dinda/v1.0.0/payload-config.jsonc b/decoders/connector/atim/dinda/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..01d41061 --- /dev/null +++ b/decoders/connector/atim/dinda/v1.0.0/payload-config.jsonc @@ -0,0 +1,26 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "The ACW-DINDA monitors an analog input and transmits it to the LoRa or Sigfox network. Up to 144 daily 0-10V or 4-20mA values can be monitored. Data transmission can be setup through ACW configurator. Compatible with Sigfox repeater (ACW-GW).\n\n**Technical Data**\n* Dimensions: 90 x 57 x 67 mm\n* Antenna: External (SMA connector)\n* Temperature: -20°C to +55°C (operation), -40°C to +70°C (storage)\n* Mounts to: DIN rail compliant\n* Power supply: 1x 10-30Vcc\n* Frequency: 865-870 MHz\n* Power: 25 mW (14 dBm)\n* Transfer rate: Sigfox: 100 bit/s, LoRaWAN: 300 bit/s to 10 kbit/s\n", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/sigfox/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/atim/dinda/v1.0.0/payload.js b/decoders/connector/atim/dinda/v1.0.0/payload.js new file mode 100644 index 00000000..58709750 --- /dev/null +++ b/decoders/connector/atim/dinda/v1.0.0/payload.js @@ -0,0 +1,1332 @@ +/* eslint-disable no-plusplus */ +/* eslint-disable no-bitwise */ +function decodeStream(payload, type, timestamp) { + // Init result + const result = { historics: [], events: [], realTimes: [] }; + + // Parse stream + // const jsonStream = JSON.parse(stream); + + // Get time and payload + // const time = new Date(jsonStream.data.timestamp * 1000) / 1000; + const time = new Date(timestamp * 1000) / 1000; + // const { payload } = jsonStream.data; + + // Save network informations + result.realTimes.push({ tagRef: "sys_data_payload", timestamp: time, tagValue: String(payload) }); + result.realTimes.push({ tagRef: "sys_data_timestamp", timestamp: time, tagValue: String(time) }); + /* if (jsonStream.data !== undefined) { + result.realTimes.push({ tagRef: "sys_data_type", timestamp: time, tagValue: String(jsonStream.data.type) }); + result.realTimes.push({ tagRef: "sys_data_raw", timestamp: time, tagValue: String(jsonStream.data.raw) }); + } + if (jsonStream.metadata !== undefined) { + result.realTimes.push({ tagRef: "sys_metadata_lastStreamTimestampUtc", timestamp: time, tagValue: String(jsonStream.metadata.lastStreamTimestampUtc) }); + if (jsonStream.metadata.device !== undefined) { + result.realTimes.push({ tagRef: "sys_device_sn", timestamp: time, tagValue: String(jsonStream.metadata.device.serialNumber) }); + result.realTimes.push({ tagRef: "sys_device_name", timestamp: time, tagValue: String(jsonStream.metadata.device.name) }); + } + if (jsonStream.metadata.endpoint !== undefined) { + result.realTimes.push({ tagRef: "sys_endpoint_name", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.name) }); + result.realTimes.push({ tagRef: "sys_endpoint_type", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.type) }); + } + if (jsonStream.metadata.network !== undefined) { + result.realTimes.push({ tagRef: "sys_network_port", timestamp: time, tagValue: String(jsonStream.metadata.network.port) }); + result.realTimes.push({ tagRef: "sys_network_linkQuality", timestamp: time, tagValue: String(jsonStream.metadata.network.linkQuality) }); + } + if (jsonStream.metadata.location !== undefined) { + result.realTimes.push({ tagRef: "sys_location_source", timestamp: time, tagValue: String(jsonStream.metadata.location.source) }); + result.realTimes.push({ tagRef: "sys_location_latitude", timestamp: time, tagValue: String(jsonStream.metadata.location.latitude) }); + result.realTimes.push({ tagRef: "sys_location_longitude", timestamp: time, tagValue: String(jsonStream.metadata.location.longitude) }); + result.realTimes.push({ tagRef: "sys_location_accuracy", timestamp: time, tagValue: String(jsonStream.metadata.location.accuracy) }); + } + } */ + if (type !== 3) { + // if KHEIRON + /* if (jsonStream.metadata.endpoint.type === 0) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).fcnt) }); + } + // if SIGFOX + if (jsonStream.metadata.endpoint.type === 5) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).seqNumber) }); + } + // if OBJENIOUS + if (jsonStream.metadata.endpoint.type === 6) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).count) }); + result.realTimes.push({ tagRef: "custom_data_raw_devEUI", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).device_properties.deveui) }); + } + */ + // Get message ID + const msgStr = parseInt(payload.substring(0, 2), 16); + + // Switch message ids + switch (msgStr) { + case 1: { + // Get meter + const battery = parseInt(payload.substring(2, 6), 16); + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery / 1000), + }); + + break; + } + case 3: { + // Get data + const battery = parseInt(payload.substring(2, 4), 16); + let temperature = parseInt(payload.substring(4, 8), 16); + const humidity = parseInt(payload.substring(8, 12), 16); + + // check if negative value + if (temperature >> 15 === 1) { + const reverseValue = temperature ^ 65535; + temperature = (reverseValue + 1) * -1; + } + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temperature / 100), + }); + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity / 100), + }); + break; + } + case 5: { + const testCounter = parseInt(payload.substring(2, 4), 16); + + result.realTimes.push({ + tagRef: "p_test", + timestamp: time, + tagValue: String(testCounter), + }); + + break; + } + case 9: { + // Get input + const di = parseInt(payload.substring(4, 6), 16); + const di1 = (di & 1) === 1; + const di2 = (di & 2) === 2; + + // get ouput + const dout = parseInt(payload.substring(2, 4), 16); + const do1 = (dout & 1) === 1; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DO1", + timestamp: time, + tagValue: String(do1), + }); + + break; + } + case 10: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + break; + } + case 11: { + break; + } + case 12: { + break; + } + case 13: { + break; + } + case 14: { + break; + } + case 21: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // Decode + temp = (temp - 33184) / 128; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + break; + } + case 20: { + // Get meter + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 48: { + // get realtime meter + const meter = parseInt(payload.substring(42, 50), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + + // get historic meter + const meter1 = parseInt(payload.substring(34, 42), 16); + const meter2 = parseInt(payload.substring(26, 34), 16); + const meter3 = parseInt(payload.substring(18, 26), 16); + const meter4 = parseInt(payload.substring(10, 18), 16); + const meter5 = parseInt(payload.substring(2, 10), 16); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 1) / 1000, + tagValue: String(meter1), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 2) / 1000, + tagValue: String(meter2), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 3) / 1000, + tagValue: String(meter3), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 4) / 1000, + tagValue: String(meter4), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 5) / 1000, + tagValue: String(meter5), + }); + + break; + } + case 49: { + // get realtime meter + const meter = parseInt(payload.substring(2, 10), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + break; + } + case 55: { + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + break; + } + case 57: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // const a = new Date(time * 1000); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (600000 + i * 600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 58: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (1800000 + i * 1800000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 59: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (3600000 + i * 3600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 22: { + // Get meter + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(4, 6), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 15: { + break; + } + case 16: { + break; + } + case 17: { + break; + } + case 18: { + break; + } + case 23: { + // Get temperature & humidity + let temp = parseInt(payload.substring(2, 6), 16); + let humidity = parseInt(payload.substring(6, 10), 16); + + // Decode + temp = (temp * 175.72) / 65536 - 46.85; + humidity = (humidity * 125) / 65536 - 6; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity), + }); + break; + } + case 24: { + break; + } + case 30: { + break; + } + case 31: { + break; + } + case 32: { + break; + } + case 33: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 10 / 64240; + + result.realTimes.push({ + tagRef: "p_voltage", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 25: { + break; + } + case 34: { + break; + } + case 35: { + break; + } + case 36: { + break; + } + case 37: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 16 / 47584; + result.realTimes.push({ + tagRef: "p_current", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 27: { + break; + } + case 42: { + break; + } + case 43: { + break; + } + case 44: { + break; + } + case 45: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + break; + } + case 26: { + break; + } + case 38: { + break; + } + case 39: { + break; + } + case 40: { + break; + } + case 41: { + break; + } + case 83: { + break; + } + case 84: { + break; + } + case 85: { + break; + } + case 86: { + break; + } + case 87: { + break; + } + case 88: { + break; + } + case 89: { + break; + } + case 90: { + break; + } + case 91: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + let temp2 = parseInt(payload.substring(6, 10), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + + // check if error + if (temp2 !== 32768) { + // check if negative value + if (temp2 >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp2 = (reverseValue + 1) * -1; + } + + // apply coef + temp2 *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature2", + timestamp: time, + tagValue: String(temp2), + }); + } + + break; + } + case 19: { + // Get water leak state + const waterleak = parseInt(payload.substring(4, 6), 16); + + // save + result.realTimes.push({ + tagRef: "p_waterLeak", + timestamp: time, + tagValue: String(waterleak), + }); + break; + } + case 47: { + // get temperature + const temp = parseInt(payload.substring(14, 18), 16); + const p_temperature = (10.888 - Math.sqrt((-10.888) ** 2 + 4 * 0.00347 * (1777.3 - temp))) / (2 * -0.00347) + 30; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + + if (parseInt(payload.substring(18, 34), 16) !== 0) { + // get data for gps decoding + const a = parseInt(payload.substring(18, 20), 16) >> 4; + const b = ((parseInt(payload.substring(18, 20), 16) << 4) & 255) >> 4; + const c = parseInt(payload.substring(20, 22), 16) >> 4; + const d = ((parseInt(payload.substring(20, 22), 16) << 4) & 255) >> 4; + const e = parseInt(payload.substring(22, 24), 16) >> 4; + const f = ((parseInt(payload.substring(22, 24), 16) << 4) & 255) >> 4; + const g = parseInt(payload.substring(24, 26), 16) >> 4; + const h = ((parseInt(payload.substring(24, 26), 16) << 4) & 255) >> 4; + const i = parseInt(payload.substring(26, 28), 16) >> 4; + const j = ((parseInt(payload.substring(26, 28), 16) << 4) & 255) >> 4; + const k = parseInt(payload.substring(28, 30), 16) >> 4; + const l = ((parseInt(payload.substring(28, 30), 16) << 4) & 255) >> 4; + const m = parseInt(payload.substring(30, 32), 16) >> 4; + const n = ((parseInt(payload.substring(30, 32), 16) << 4) & 255) >> 4; + const o = parseInt(payload.substring(32, 34), 16) >> 4; + // const p = ((parseInt(payload.substring(32, 34), 16) << 4) & 255) >> 6; + const q = ((parseInt(payload.substring(32, 34), 16) << 6) & 255) >> 7; + const r = ((parseInt(payload.substring(32, 34), 16) << 7) & 255) >> 7; + // get latitude and longitude + const latitude = (2 * q - 1) * (10 * a + b + c / 6 + d / 60 + e / 600 + f / 6000 + g / 60000); + const longitude = (2 * r - 1) * (100 * h + 10 * i + j + k / 6 + l / 60 + m / 600 + n / 6000 + o / 60000); + + // save + result.realTimes.push({ + tagRef: "p_latitude", + timestamp: time, + tagValue: String(latitude), + }); + result.realTimes.push({ + tagRef: "p_longitude", + timestamp: time, + tagValue: String(longitude), + }); + } + + break; + } + case 50: { + const p_vibration = parseInt(payload.substring(4, 6), 16); + const p_ils = parseInt(payload.substring(6, 8), 16); + const p_temperature = (2103 - parseInt(payload.substring(8, 12), 16)) / 10.9; + + // save + result.realTimes.push({ + tagRef: "p_vibration", + timestamp: time, + tagValue: String(p_vibration), + }); + result.realTimes.push({ + tagRef: "p_ils", + timestamp: time, + tagValue: String(p_ils), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + break; + } + case 51: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + break; + } + case 52: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get count + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(parseInt(payload.substring(4, 12), 16)), + }); + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(parseInt(payload.substring(12, 20), 16)), + }); + break; + } + case 53: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 54: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 62: { + // Get Absence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(0), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + const valueWorZ = parseInt(payload.substring(10, 14), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + + // Save WorZ + result.realTimes.push({ + tagRef: "p_WorZ", + timestamp: time, + tagValue: String(valueWorZ), + }); + break; + } + case 63: { + // Get Presence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(1), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + break; + } + case 65: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + break; + } + case 66: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + break; + } + case 67: { + // save + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time - 1, + tagValue: String(1), + context: [], + }); + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time, + tagValue: String(0), + context: [], + }); + break; + } + case 78: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + // get count 1 + const count1 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 79: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(14, 22), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 80: { + // get count 1 + const count1 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 81: { + // get count 3 + const count3 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count3", + timestamp: time, + tagValue: String(count3), + }); + + // get count 4 + const count4 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count4", + timestamp: time, + tagValue: String(count4), + }); + break; + } + case 82: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 93: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(2 + 8 * j, 10 + 8 * j), 16)), + }); + } + break; + } + case 94: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(-2 + 8 * j, 6 + 8 * j), 16)), + }); + } + break; + } + case 95: { + // get count 5 + const count5 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count5", + timestamp: time, + tagValue: String(count5), + }); + + // get count 6 + const count6 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count6", + timestamp: time, + tagValue: String(count6), + }); + break; + } + case 96: { + // get count 7 + const count7 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count7", + timestamp: time, + tagValue: String(count7), + }); + + // get count 8 + const count8 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count8", + timestamp: time, + tagValue: String(count8), + }); + break; + } + case 2: + case 4: + case 6: + case 7: + case 8: + case 46: + case 56: + case 60: + case 61: + case 64: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 92: + case 97: + case 98: + case 99: + default: + break; + } + } + // Return result + return result; +} + +function ToTagoFormat(object_item, serie) { + const historics = []; + const events = []; + const realTimes = []; + // eslint-disable-next-line guard-for-in + for (const key in object_item.realTimes) { + realTimes.push({ + variable: `realTimes_${object_item.realTimes[key].tagRef}`.toLowerCase(), + value: object_item.realTimes[key].tagValue, + time: object_item.realTimes[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.events) { + events.push({ + variable: `events_${object_item.events[key].tagRef}`.toLowerCase(), + value: object_item.events[key].tagValue, + time: object_item.events[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.historics) { + historics.push({ + variable: `historics_${object_item.historics[key].tagRef}`.toLowerCase(), + value: object_item.historics[key].tagValue, + time: object_item.historics[key].timestamp, + serie, + }); + } + const result = historics.concat(events, realTimes); + + return result; +} + +/* let payload = [ + { variable: "payload", value: "5e7f000000003f00000040000000410000004200000043000000440000004500000046" }, + { variable: "type", value: 0 }, + { variable: "timestamp", value: 1605614318410 }, +]; */ + +const data = payload.find((x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data"); +const type = payload.find((x) => x.variable === "type"); +const timestamp = payload.find((x) => x.variable === "timestamp"); + +if (data) { + // const buffer = Buffer.from(data.value, "hex"); + const serie = new Date().getTime(); + // payload = decodeStream(data.value, type.value, timestamp.value); + payload = ToTagoFormat(decodeStream(data.value, type.value, timestamp.value), serie); +} + +// eslint-disable-next-line no-console +// console.log(payload); diff --git a/decoders/connector/atim/dinrsm/assets/logo.png b/decoders/connector/atim/dinrsm/assets/logo.png new file mode 100644 index 00000000..ae72effe Binary files /dev/null and b/decoders/connector/atim/dinrsm/assets/logo.png differ diff --git a/decoders/connector/atim/dinrsm/connector.jsonc b/decoders/connector/atim/dinrsm/connector.jsonc new file mode 100644 index 00000000..b11d9e29 --- /dev/null +++ b/decoders/connector/atim/dinrsm/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Atim DINRSM", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/atim/dinrsm/description.md b/decoders/connector/atim/dinrsm/description.md new file mode 100644 index 00000000..530dde77 --- /dev/null +++ b/decoders/connector/atim/dinrsm/description.md @@ -0,0 +1 @@ +Modbus master Gateway over LoRaWAN or Sigfox \ No newline at end of file diff --git a/decoders/connector/atim/dinrsm/v1.0.0/payload-config.jsonc b/decoders/connector/atim/dinrsm/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..a009bbcd --- /dev/null +++ b/decoders/connector/atim/dinrsm/v1.0.0/payload-config.jsonc @@ -0,0 +1,26 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "The ACW-DINRSM interrogates up to 49 modbus registers from slaves PLCs and sends its answers to LoRa or Sigfox. The devices act as a Master reading and writing into the Slaves to monitor or request on demand specific registers. When connected in serial link, up to 49 PLCs can be controlled by 1x ACW-DINRSM. Data frequency of transmission, registers and slaves requests setup are to be made on the ACW configurator. Compatible with Sigfox repeater (ACW-GW).\n\n**Technical Data**\n* Dimensions: 90 x 57 x 17,9 mm\n* Antenna: External (SMA connector)\n* Temperature: -20°C to +55°C (operation), -40°C to +70°C (storage)\n* Mounts to: DIN rail compliant\n* Power supply: 1x 10-30Vdc\n* Frequency: 865-870 MHz\n* Power: 25 mW (14 dBm)\n* Transfer rate: Sigfox: 100 bit/s, LoRaWAN: 300 bit/s to 10 kbit/s", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/sigfox/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/atim/dinrsm/v1.0.0/payload.js b/decoders/connector/atim/dinrsm/v1.0.0/payload.js new file mode 100644 index 00000000..58709750 --- /dev/null +++ b/decoders/connector/atim/dinrsm/v1.0.0/payload.js @@ -0,0 +1,1332 @@ +/* eslint-disable no-plusplus */ +/* eslint-disable no-bitwise */ +function decodeStream(payload, type, timestamp) { + // Init result + const result = { historics: [], events: [], realTimes: [] }; + + // Parse stream + // const jsonStream = JSON.parse(stream); + + // Get time and payload + // const time = new Date(jsonStream.data.timestamp * 1000) / 1000; + const time = new Date(timestamp * 1000) / 1000; + // const { payload } = jsonStream.data; + + // Save network informations + result.realTimes.push({ tagRef: "sys_data_payload", timestamp: time, tagValue: String(payload) }); + result.realTimes.push({ tagRef: "sys_data_timestamp", timestamp: time, tagValue: String(time) }); + /* if (jsonStream.data !== undefined) { + result.realTimes.push({ tagRef: "sys_data_type", timestamp: time, tagValue: String(jsonStream.data.type) }); + result.realTimes.push({ tagRef: "sys_data_raw", timestamp: time, tagValue: String(jsonStream.data.raw) }); + } + if (jsonStream.metadata !== undefined) { + result.realTimes.push({ tagRef: "sys_metadata_lastStreamTimestampUtc", timestamp: time, tagValue: String(jsonStream.metadata.lastStreamTimestampUtc) }); + if (jsonStream.metadata.device !== undefined) { + result.realTimes.push({ tagRef: "sys_device_sn", timestamp: time, tagValue: String(jsonStream.metadata.device.serialNumber) }); + result.realTimes.push({ tagRef: "sys_device_name", timestamp: time, tagValue: String(jsonStream.metadata.device.name) }); + } + if (jsonStream.metadata.endpoint !== undefined) { + result.realTimes.push({ tagRef: "sys_endpoint_name", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.name) }); + result.realTimes.push({ tagRef: "sys_endpoint_type", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.type) }); + } + if (jsonStream.metadata.network !== undefined) { + result.realTimes.push({ tagRef: "sys_network_port", timestamp: time, tagValue: String(jsonStream.metadata.network.port) }); + result.realTimes.push({ tagRef: "sys_network_linkQuality", timestamp: time, tagValue: String(jsonStream.metadata.network.linkQuality) }); + } + if (jsonStream.metadata.location !== undefined) { + result.realTimes.push({ tagRef: "sys_location_source", timestamp: time, tagValue: String(jsonStream.metadata.location.source) }); + result.realTimes.push({ tagRef: "sys_location_latitude", timestamp: time, tagValue: String(jsonStream.metadata.location.latitude) }); + result.realTimes.push({ tagRef: "sys_location_longitude", timestamp: time, tagValue: String(jsonStream.metadata.location.longitude) }); + result.realTimes.push({ tagRef: "sys_location_accuracy", timestamp: time, tagValue: String(jsonStream.metadata.location.accuracy) }); + } + } */ + if (type !== 3) { + // if KHEIRON + /* if (jsonStream.metadata.endpoint.type === 0) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).fcnt) }); + } + // if SIGFOX + if (jsonStream.metadata.endpoint.type === 5) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).seqNumber) }); + } + // if OBJENIOUS + if (jsonStream.metadata.endpoint.type === 6) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).count) }); + result.realTimes.push({ tagRef: "custom_data_raw_devEUI", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).device_properties.deveui) }); + } + */ + // Get message ID + const msgStr = parseInt(payload.substring(0, 2), 16); + + // Switch message ids + switch (msgStr) { + case 1: { + // Get meter + const battery = parseInt(payload.substring(2, 6), 16); + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery / 1000), + }); + + break; + } + case 3: { + // Get data + const battery = parseInt(payload.substring(2, 4), 16); + let temperature = parseInt(payload.substring(4, 8), 16); + const humidity = parseInt(payload.substring(8, 12), 16); + + // check if negative value + if (temperature >> 15 === 1) { + const reverseValue = temperature ^ 65535; + temperature = (reverseValue + 1) * -1; + } + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temperature / 100), + }); + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity / 100), + }); + break; + } + case 5: { + const testCounter = parseInt(payload.substring(2, 4), 16); + + result.realTimes.push({ + tagRef: "p_test", + timestamp: time, + tagValue: String(testCounter), + }); + + break; + } + case 9: { + // Get input + const di = parseInt(payload.substring(4, 6), 16); + const di1 = (di & 1) === 1; + const di2 = (di & 2) === 2; + + // get ouput + const dout = parseInt(payload.substring(2, 4), 16); + const do1 = (dout & 1) === 1; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DO1", + timestamp: time, + tagValue: String(do1), + }); + + break; + } + case 10: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + break; + } + case 11: { + break; + } + case 12: { + break; + } + case 13: { + break; + } + case 14: { + break; + } + case 21: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // Decode + temp = (temp - 33184) / 128; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + break; + } + case 20: { + // Get meter + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 48: { + // get realtime meter + const meter = parseInt(payload.substring(42, 50), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + + // get historic meter + const meter1 = parseInt(payload.substring(34, 42), 16); + const meter2 = parseInt(payload.substring(26, 34), 16); + const meter3 = parseInt(payload.substring(18, 26), 16); + const meter4 = parseInt(payload.substring(10, 18), 16); + const meter5 = parseInt(payload.substring(2, 10), 16); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 1) / 1000, + tagValue: String(meter1), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 2) / 1000, + tagValue: String(meter2), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 3) / 1000, + tagValue: String(meter3), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 4) / 1000, + tagValue: String(meter4), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 5) / 1000, + tagValue: String(meter5), + }); + + break; + } + case 49: { + // get realtime meter + const meter = parseInt(payload.substring(2, 10), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + break; + } + case 55: { + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + break; + } + case 57: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // const a = new Date(time * 1000); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (600000 + i * 600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 58: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (1800000 + i * 1800000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 59: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (3600000 + i * 3600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 22: { + // Get meter + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(4, 6), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 15: { + break; + } + case 16: { + break; + } + case 17: { + break; + } + case 18: { + break; + } + case 23: { + // Get temperature & humidity + let temp = parseInt(payload.substring(2, 6), 16); + let humidity = parseInt(payload.substring(6, 10), 16); + + // Decode + temp = (temp * 175.72) / 65536 - 46.85; + humidity = (humidity * 125) / 65536 - 6; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity), + }); + break; + } + case 24: { + break; + } + case 30: { + break; + } + case 31: { + break; + } + case 32: { + break; + } + case 33: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 10 / 64240; + + result.realTimes.push({ + tagRef: "p_voltage", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 25: { + break; + } + case 34: { + break; + } + case 35: { + break; + } + case 36: { + break; + } + case 37: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 16 / 47584; + result.realTimes.push({ + tagRef: "p_current", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 27: { + break; + } + case 42: { + break; + } + case 43: { + break; + } + case 44: { + break; + } + case 45: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + break; + } + case 26: { + break; + } + case 38: { + break; + } + case 39: { + break; + } + case 40: { + break; + } + case 41: { + break; + } + case 83: { + break; + } + case 84: { + break; + } + case 85: { + break; + } + case 86: { + break; + } + case 87: { + break; + } + case 88: { + break; + } + case 89: { + break; + } + case 90: { + break; + } + case 91: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + let temp2 = parseInt(payload.substring(6, 10), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + + // check if error + if (temp2 !== 32768) { + // check if negative value + if (temp2 >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp2 = (reverseValue + 1) * -1; + } + + // apply coef + temp2 *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature2", + timestamp: time, + tagValue: String(temp2), + }); + } + + break; + } + case 19: { + // Get water leak state + const waterleak = parseInt(payload.substring(4, 6), 16); + + // save + result.realTimes.push({ + tagRef: "p_waterLeak", + timestamp: time, + tagValue: String(waterleak), + }); + break; + } + case 47: { + // get temperature + const temp = parseInt(payload.substring(14, 18), 16); + const p_temperature = (10.888 - Math.sqrt((-10.888) ** 2 + 4 * 0.00347 * (1777.3 - temp))) / (2 * -0.00347) + 30; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + + if (parseInt(payload.substring(18, 34), 16) !== 0) { + // get data for gps decoding + const a = parseInt(payload.substring(18, 20), 16) >> 4; + const b = ((parseInt(payload.substring(18, 20), 16) << 4) & 255) >> 4; + const c = parseInt(payload.substring(20, 22), 16) >> 4; + const d = ((parseInt(payload.substring(20, 22), 16) << 4) & 255) >> 4; + const e = parseInt(payload.substring(22, 24), 16) >> 4; + const f = ((parseInt(payload.substring(22, 24), 16) << 4) & 255) >> 4; + const g = parseInt(payload.substring(24, 26), 16) >> 4; + const h = ((parseInt(payload.substring(24, 26), 16) << 4) & 255) >> 4; + const i = parseInt(payload.substring(26, 28), 16) >> 4; + const j = ((parseInt(payload.substring(26, 28), 16) << 4) & 255) >> 4; + const k = parseInt(payload.substring(28, 30), 16) >> 4; + const l = ((parseInt(payload.substring(28, 30), 16) << 4) & 255) >> 4; + const m = parseInt(payload.substring(30, 32), 16) >> 4; + const n = ((parseInt(payload.substring(30, 32), 16) << 4) & 255) >> 4; + const o = parseInt(payload.substring(32, 34), 16) >> 4; + // const p = ((parseInt(payload.substring(32, 34), 16) << 4) & 255) >> 6; + const q = ((parseInt(payload.substring(32, 34), 16) << 6) & 255) >> 7; + const r = ((parseInt(payload.substring(32, 34), 16) << 7) & 255) >> 7; + // get latitude and longitude + const latitude = (2 * q - 1) * (10 * a + b + c / 6 + d / 60 + e / 600 + f / 6000 + g / 60000); + const longitude = (2 * r - 1) * (100 * h + 10 * i + j + k / 6 + l / 60 + m / 600 + n / 6000 + o / 60000); + + // save + result.realTimes.push({ + tagRef: "p_latitude", + timestamp: time, + tagValue: String(latitude), + }); + result.realTimes.push({ + tagRef: "p_longitude", + timestamp: time, + tagValue: String(longitude), + }); + } + + break; + } + case 50: { + const p_vibration = parseInt(payload.substring(4, 6), 16); + const p_ils = parseInt(payload.substring(6, 8), 16); + const p_temperature = (2103 - parseInt(payload.substring(8, 12), 16)) / 10.9; + + // save + result.realTimes.push({ + tagRef: "p_vibration", + timestamp: time, + tagValue: String(p_vibration), + }); + result.realTimes.push({ + tagRef: "p_ils", + timestamp: time, + tagValue: String(p_ils), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + break; + } + case 51: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + break; + } + case 52: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get count + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(parseInt(payload.substring(4, 12), 16)), + }); + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(parseInt(payload.substring(12, 20), 16)), + }); + break; + } + case 53: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 54: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 62: { + // Get Absence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(0), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + const valueWorZ = parseInt(payload.substring(10, 14), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + + // Save WorZ + result.realTimes.push({ + tagRef: "p_WorZ", + timestamp: time, + tagValue: String(valueWorZ), + }); + break; + } + case 63: { + // Get Presence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(1), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + break; + } + case 65: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + break; + } + case 66: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + break; + } + case 67: { + // save + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time - 1, + tagValue: String(1), + context: [], + }); + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time, + tagValue: String(0), + context: [], + }); + break; + } + case 78: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + // get count 1 + const count1 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 79: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(14, 22), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 80: { + // get count 1 + const count1 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 81: { + // get count 3 + const count3 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count3", + timestamp: time, + tagValue: String(count3), + }); + + // get count 4 + const count4 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count4", + timestamp: time, + tagValue: String(count4), + }); + break; + } + case 82: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 93: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(2 + 8 * j, 10 + 8 * j), 16)), + }); + } + break; + } + case 94: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(-2 + 8 * j, 6 + 8 * j), 16)), + }); + } + break; + } + case 95: { + // get count 5 + const count5 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count5", + timestamp: time, + tagValue: String(count5), + }); + + // get count 6 + const count6 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count6", + timestamp: time, + tagValue: String(count6), + }); + break; + } + case 96: { + // get count 7 + const count7 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count7", + timestamp: time, + tagValue: String(count7), + }); + + // get count 8 + const count8 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count8", + timestamp: time, + tagValue: String(count8), + }); + break; + } + case 2: + case 4: + case 6: + case 7: + case 8: + case 46: + case 56: + case 60: + case 61: + case 64: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 92: + case 97: + case 98: + case 99: + default: + break; + } + } + // Return result + return result; +} + +function ToTagoFormat(object_item, serie) { + const historics = []; + const events = []; + const realTimes = []; + // eslint-disable-next-line guard-for-in + for (const key in object_item.realTimes) { + realTimes.push({ + variable: `realTimes_${object_item.realTimes[key].tagRef}`.toLowerCase(), + value: object_item.realTimes[key].tagValue, + time: object_item.realTimes[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.events) { + events.push({ + variable: `events_${object_item.events[key].tagRef}`.toLowerCase(), + value: object_item.events[key].tagValue, + time: object_item.events[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.historics) { + historics.push({ + variable: `historics_${object_item.historics[key].tagRef}`.toLowerCase(), + value: object_item.historics[key].tagValue, + time: object_item.historics[key].timestamp, + serie, + }); + } + const result = historics.concat(events, realTimes); + + return result; +} + +/* let payload = [ + { variable: "payload", value: "5e7f000000003f00000040000000410000004200000043000000440000004500000046" }, + { variable: "type", value: 0 }, + { variable: "timestamp", value: 1605614318410 }, +]; */ + +const data = payload.find((x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data"); +const type = payload.find((x) => x.variable === "type"); +const timestamp = payload.find((x) => x.variable === "timestamp"); + +if (data) { + // const buffer = Buffer.from(data.value, "hex"); + const serie = new Date().getTime(); + // payload = decodeStream(data.value, type.value, timestamp.value); + payload = ToTagoFormat(decodeStream(data.value, type.value, timestamp.value), serie); +} + +// eslint-disable-next-line no-console +// console.log(payload); diff --git a/decoders/connector/atim/gw/assets/logo.png b/decoders/connector/atim/gw/assets/logo.png new file mode 100644 index 00000000..655c7e06 Binary files /dev/null and b/decoders/connector/atim/gw/assets/logo.png differ diff --git a/decoders/connector/atim/gw/connector.jsonc b/decoders/connector/atim/gw/connector.jsonc new file mode 100644 index 00000000..2ebef0c0 --- /dev/null +++ b/decoders/connector/atim/gw/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Atim GW", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/atim/gw/description.md b/decoders/connector/atim/gw/description.md new file mode 100644 index 00000000..f31556e5 --- /dev/null +++ b/decoders/connector/atim/gw/description.md @@ -0,0 +1 @@ +Repeater over Sigfox \ No newline at end of file diff --git a/decoders/connector/atim/gw/v1.0.0/payload-config.jsonc b/decoders/connector/atim/gw/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..020ed9f8 --- /dev/null +++ b/decoders/connector/atim/gw/v1.0.0/payload-config.jsonc @@ -0,0 +1,11 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "The ACW-GW radio modem is a repeater that allows radio message reporting from an 868MHz to the Sigfox network. Connect to Sigfox any type of ACW sensor through this repeater.\n\n**Technical Data**\n* Dimensions: 90 x 52 x 17,9 mm\n* Weight: 50g\n* Antenna: External antenna (SMA connector)\n* Temperature: -20°C to +55°C (operation), -40°C to +70°C (storage)\n* Mounts to: DIN rail\n* Power supply: 10-30Vcc\n* Frequency: 865-870 MHz\n* Power: 25 mW (14 dBm)\n* Output: Local: 1.2 to 115 kbits/s, Sigfox: 100 bit/s", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/sigfox/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/atim/gw/v1.0.0/payload.js b/decoders/connector/atim/gw/v1.0.0/payload.js new file mode 100644 index 00000000..58709750 --- /dev/null +++ b/decoders/connector/atim/gw/v1.0.0/payload.js @@ -0,0 +1,1332 @@ +/* eslint-disable no-plusplus */ +/* eslint-disable no-bitwise */ +function decodeStream(payload, type, timestamp) { + // Init result + const result = { historics: [], events: [], realTimes: [] }; + + // Parse stream + // const jsonStream = JSON.parse(stream); + + // Get time and payload + // const time = new Date(jsonStream.data.timestamp * 1000) / 1000; + const time = new Date(timestamp * 1000) / 1000; + // const { payload } = jsonStream.data; + + // Save network informations + result.realTimes.push({ tagRef: "sys_data_payload", timestamp: time, tagValue: String(payload) }); + result.realTimes.push({ tagRef: "sys_data_timestamp", timestamp: time, tagValue: String(time) }); + /* if (jsonStream.data !== undefined) { + result.realTimes.push({ tagRef: "sys_data_type", timestamp: time, tagValue: String(jsonStream.data.type) }); + result.realTimes.push({ tagRef: "sys_data_raw", timestamp: time, tagValue: String(jsonStream.data.raw) }); + } + if (jsonStream.metadata !== undefined) { + result.realTimes.push({ tagRef: "sys_metadata_lastStreamTimestampUtc", timestamp: time, tagValue: String(jsonStream.metadata.lastStreamTimestampUtc) }); + if (jsonStream.metadata.device !== undefined) { + result.realTimes.push({ tagRef: "sys_device_sn", timestamp: time, tagValue: String(jsonStream.metadata.device.serialNumber) }); + result.realTimes.push({ tagRef: "sys_device_name", timestamp: time, tagValue: String(jsonStream.metadata.device.name) }); + } + if (jsonStream.metadata.endpoint !== undefined) { + result.realTimes.push({ tagRef: "sys_endpoint_name", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.name) }); + result.realTimes.push({ tagRef: "sys_endpoint_type", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.type) }); + } + if (jsonStream.metadata.network !== undefined) { + result.realTimes.push({ tagRef: "sys_network_port", timestamp: time, tagValue: String(jsonStream.metadata.network.port) }); + result.realTimes.push({ tagRef: "sys_network_linkQuality", timestamp: time, tagValue: String(jsonStream.metadata.network.linkQuality) }); + } + if (jsonStream.metadata.location !== undefined) { + result.realTimes.push({ tagRef: "sys_location_source", timestamp: time, tagValue: String(jsonStream.metadata.location.source) }); + result.realTimes.push({ tagRef: "sys_location_latitude", timestamp: time, tagValue: String(jsonStream.metadata.location.latitude) }); + result.realTimes.push({ tagRef: "sys_location_longitude", timestamp: time, tagValue: String(jsonStream.metadata.location.longitude) }); + result.realTimes.push({ tagRef: "sys_location_accuracy", timestamp: time, tagValue: String(jsonStream.metadata.location.accuracy) }); + } + } */ + if (type !== 3) { + // if KHEIRON + /* if (jsonStream.metadata.endpoint.type === 0) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).fcnt) }); + } + // if SIGFOX + if (jsonStream.metadata.endpoint.type === 5) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).seqNumber) }); + } + // if OBJENIOUS + if (jsonStream.metadata.endpoint.type === 6) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).count) }); + result.realTimes.push({ tagRef: "custom_data_raw_devEUI", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).device_properties.deveui) }); + } + */ + // Get message ID + const msgStr = parseInt(payload.substring(0, 2), 16); + + // Switch message ids + switch (msgStr) { + case 1: { + // Get meter + const battery = parseInt(payload.substring(2, 6), 16); + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery / 1000), + }); + + break; + } + case 3: { + // Get data + const battery = parseInt(payload.substring(2, 4), 16); + let temperature = parseInt(payload.substring(4, 8), 16); + const humidity = parseInt(payload.substring(8, 12), 16); + + // check if negative value + if (temperature >> 15 === 1) { + const reverseValue = temperature ^ 65535; + temperature = (reverseValue + 1) * -1; + } + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temperature / 100), + }); + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity / 100), + }); + break; + } + case 5: { + const testCounter = parseInt(payload.substring(2, 4), 16); + + result.realTimes.push({ + tagRef: "p_test", + timestamp: time, + tagValue: String(testCounter), + }); + + break; + } + case 9: { + // Get input + const di = parseInt(payload.substring(4, 6), 16); + const di1 = (di & 1) === 1; + const di2 = (di & 2) === 2; + + // get ouput + const dout = parseInt(payload.substring(2, 4), 16); + const do1 = (dout & 1) === 1; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DO1", + timestamp: time, + tagValue: String(do1), + }); + + break; + } + case 10: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + break; + } + case 11: { + break; + } + case 12: { + break; + } + case 13: { + break; + } + case 14: { + break; + } + case 21: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // Decode + temp = (temp - 33184) / 128; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + break; + } + case 20: { + // Get meter + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 48: { + // get realtime meter + const meter = parseInt(payload.substring(42, 50), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + + // get historic meter + const meter1 = parseInt(payload.substring(34, 42), 16); + const meter2 = parseInt(payload.substring(26, 34), 16); + const meter3 = parseInt(payload.substring(18, 26), 16); + const meter4 = parseInt(payload.substring(10, 18), 16); + const meter5 = parseInt(payload.substring(2, 10), 16); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 1) / 1000, + tagValue: String(meter1), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 2) / 1000, + tagValue: String(meter2), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 3) / 1000, + tagValue: String(meter3), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 4) / 1000, + tagValue: String(meter4), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 5) / 1000, + tagValue: String(meter5), + }); + + break; + } + case 49: { + // get realtime meter + const meter = parseInt(payload.substring(2, 10), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + break; + } + case 55: { + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + break; + } + case 57: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // const a = new Date(time * 1000); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (600000 + i * 600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 58: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (1800000 + i * 1800000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 59: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (3600000 + i * 3600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 22: { + // Get meter + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(4, 6), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 15: { + break; + } + case 16: { + break; + } + case 17: { + break; + } + case 18: { + break; + } + case 23: { + // Get temperature & humidity + let temp = parseInt(payload.substring(2, 6), 16); + let humidity = parseInt(payload.substring(6, 10), 16); + + // Decode + temp = (temp * 175.72) / 65536 - 46.85; + humidity = (humidity * 125) / 65536 - 6; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity), + }); + break; + } + case 24: { + break; + } + case 30: { + break; + } + case 31: { + break; + } + case 32: { + break; + } + case 33: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 10 / 64240; + + result.realTimes.push({ + tagRef: "p_voltage", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 25: { + break; + } + case 34: { + break; + } + case 35: { + break; + } + case 36: { + break; + } + case 37: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 16 / 47584; + result.realTimes.push({ + tagRef: "p_current", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 27: { + break; + } + case 42: { + break; + } + case 43: { + break; + } + case 44: { + break; + } + case 45: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + break; + } + case 26: { + break; + } + case 38: { + break; + } + case 39: { + break; + } + case 40: { + break; + } + case 41: { + break; + } + case 83: { + break; + } + case 84: { + break; + } + case 85: { + break; + } + case 86: { + break; + } + case 87: { + break; + } + case 88: { + break; + } + case 89: { + break; + } + case 90: { + break; + } + case 91: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + let temp2 = parseInt(payload.substring(6, 10), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + + // check if error + if (temp2 !== 32768) { + // check if negative value + if (temp2 >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp2 = (reverseValue + 1) * -1; + } + + // apply coef + temp2 *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature2", + timestamp: time, + tagValue: String(temp2), + }); + } + + break; + } + case 19: { + // Get water leak state + const waterleak = parseInt(payload.substring(4, 6), 16); + + // save + result.realTimes.push({ + tagRef: "p_waterLeak", + timestamp: time, + tagValue: String(waterleak), + }); + break; + } + case 47: { + // get temperature + const temp = parseInt(payload.substring(14, 18), 16); + const p_temperature = (10.888 - Math.sqrt((-10.888) ** 2 + 4 * 0.00347 * (1777.3 - temp))) / (2 * -0.00347) + 30; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + + if (parseInt(payload.substring(18, 34), 16) !== 0) { + // get data for gps decoding + const a = parseInt(payload.substring(18, 20), 16) >> 4; + const b = ((parseInt(payload.substring(18, 20), 16) << 4) & 255) >> 4; + const c = parseInt(payload.substring(20, 22), 16) >> 4; + const d = ((parseInt(payload.substring(20, 22), 16) << 4) & 255) >> 4; + const e = parseInt(payload.substring(22, 24), 16) >> 4; + const f = ((parseInt(payload.substring(22, 24), 16) << 4) & 255) >> 4; + const g = parseInt(payload.substring(24, 26), 16) >> 4; + const h = ((parseInt(payload.substring(24, 26), 16) << 4) & 255) >> 4; + const i = parseInt(payload.substring(26, 28), 16) >> 4; + const j = ((parseInt(payload.substring(26, 28), 16) << 4) & 255) >> 4; + const k = parseInt(payload.substring(28, 30), 16) >> 4; + const l = ((parseInt(payload.substring(28, 30), 16) << 4) & 255) >> 4; + const m = parseInt(payload.substring(30, 32), 16) >> 4; + const n = ((parseInt(payload.substring(30, 32), 16) << 4) & 255) >> 4; + const o = parseInt(payload.substring(32, 34), 16) >> 4; + // const p = ((parseInt(payload.substring(32, 34), 16) << 4) & 255) >> 6; + const q = ((parseInt(payload.substring(32, 34), 16) << 6) & 255) >> 7; + const r = ((parseInt(payload.substring(32, 34), 16) << 7) & 255) >> 7; + // get latitude and longitude + const latitude = (2 * q - 1) * (10 * a + b + c / 6 + d / 60 + e / 600 + f / 6000 + g / 60000); + const longitude = (2 * r - 1) * (100 * h + 10 * i + j + k / 6 + l / 60 + m / 600 + n / 6000 + o / 60000); + + // save + result.realTimes.push({ + tagRef: "p_latitude", + timestamp: time, + tagValue: String(latitude), + }); + result.realTimes.push({ + tagRef: "p_longitude", + timestamp: time, + tagValue: String(longitude), + }); + } + + break; + } + case 50: { + const p_vibration = parseInt(payload.substring(4, 6), 16); + const p_ils = parseInt(payload.substring(6, 8), 16); + const p_temperature = (2103 - parseInt(payload.substring(8, 12), 16)) / 10.9; + + // save + result.realTimes.push({ + tagRef: "p_vibration", + timestamp: time, + tagValue: String(p_vibration), + }); + result.realTimes.push({ + tagRef: "p_ils", + timestamp: time, + tagValue: String(p_ils), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + break; + } + case 51: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + break; + } + case 52: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get count + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(parseInt(payload.substring(4, 12), 16)), + }); + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(parseInt(payload.substring(12, 20), 16)), + }); + break; + } + case 53: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 54: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 62: { + // Get Absence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(0), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + const valueWorZ = parseInt(payload.substring(10, 14), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + + // Save WorZ + result.realTimes.push({ + tagRef: "p_WorZ", + timestamp: time, + tagValue: String(valueWorZ), + }); + break; + } + case 63: { + // Get Presence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(1), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + break; + } + case 65: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + break; + } + case 66: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + break; + } + case 67: { + // save + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time - 1, + tagValue: String(1), + context: [], + }); + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time, + tagValue: String(0), + context: [], + }); + break; + } + case 78: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + // get count 1 + const count1 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 79: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(14, 22), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 80: { + // get count 1 + const count1 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 81: { + // get count 3 + const count3 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count3", + timestamp: time, + tagValue: String(count3), + }); + + // get count 4 + const count4 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count4", + timestamp: time, + tagValue: String(count4), + }); + break; + } + case 82: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 93: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(2 + 8 * j, 10 + 8 * j), 16)), + }); + } + break; + } + case 94: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(-2 + 8 * j, 6 + 8 * j), 16)), + }); + } + break; + } + case 95: { + // get count 5 + const count5 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count5", + timestamp: time, + tagValue: String(count5), + }); + + // get count 6 + const count6 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count6", + timestamp: time, + tagValue: String(count6), + }); + break; + } + case 96: { + // get count 7 + const count7 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count7", + timestamp: time, + tagValue: String(count7), + }); + + // get count 8 + const count8 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count8", + timestamp: time, + tagValue: String(count8), + }); + break; + } + case 2: + case 4: + case 6: + case 7: + case 8: + case 46: + case 56: + case 60: + case 61: + case 64: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 92: + case 97: + case 98: + case 99: + default: + break; + } + } + // Return result + return result; +} + +function ToTagoFormat(object_item, serie) { + const historics = []; + const events = []; + const realTimes = []; + // eslint-disable-next-line guard-for-in + for (const key in object_item.realTimes) { + realTimes.push({ + variable: `realTimes_${object_item.realTimes[key].tagRef}`.toLowerCase(), + value: object_item.realTimes[key].tagValue, + time: object_item.realTimes[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.events) { + events.push({ + variable: `events_${object_item.events[key].tagRef}`.toLowerCase(), + value: object_item.events[key].tagValue, + time: object_item.events[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.historics) { + historics.push({ + variable: `historics_${object_item.historics[key].tagRef}`.toLowerCase(), + value: object_item.historics[key].tagValue, + time: object_item.historics[key].timestamp, + serie, + }); + } + const result = historics.concat(events, realTimes); + + return result; +} + +/* let payload = [ + { variable: "payload", value: "5e7f000000003f00000040000000410000004200000043000000440000004500000046" }, + { variable: "type", value: 0 }, + { variable: "timestamp", value: 1605614318410 }, +]; */ + +const data = payload.find((x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data"); +const type = payload.find((x) => x.variable === "type"); +const timestamp = payload.find((x) => x.variable === "timestamp"); + +if (data) { + // const buffer = Buffer.from(data.value, "hex"); + const serie = new Date().getTime(); + // payload = decodeStream(data.value, type.value, timestamp.value); + payload = ToTagoFormat(decodeStream(data.value, type.value, timestamp.value), serie); +} + +// eslint-disable-next-line no-console +// console.log(payload); diff --git a/decoders/connector/atim/mr2-ex/assets/logo.png b/decoders/connector/atim/mr2-ex/assets/logo.png new file mode 100644 index 00000000..b63b4aca Binary files /dev/null and b/decoders/connector/atim/mr2-ex/assets/logo.png differ diff --git a/decoders/connector/atim/mr2-ex/connector.jsonc b/decoders/connector/atim/mr2-ex/connector.jsonc new file mode 100644 index 00000000..c6fa45b0 --- /dev/null +++ b/decoders/connector/atim/mr2-ex/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Atim MR2-EX", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/atim/mr2-ex/description.md b/decoders/connector/atim/mr2-ex/description.md new file mode 100644 index 00000000..5ef8ffcd --- /dev/null +++ b/decoders/connector/atim/mr2-ex/description.md @@ -0,0 +1 @@ +Metering index of water, gas or electricity over LoRaWAN or Sigfox \ No newline at end of file diff --git a/decoders/connector/atim/mr2-ex/v1.0.0/payload-config.jsonc b/decoders/connector/atim/mr2-ex/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..8a5e98c8 --- /dev/null +++ b/decoders/connector/atim/mr2-ex/v1.0.0/payload-config.jsonc @@ -0,0 +1,26 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "The ACW-MR2-EX monitors and transmits the status of 2 dry contacts or meters’ index consumption (open collector type, NPN / PNP and dry contact). Compatible with Sigfox repeater (ACW-GW).\n\n\n**Technical Data**\n* Dimesions: 177 x 55 x 55 mm\n* Antenna: Integrated (¼ wave)\n* Temperature: -20°C to +55°C (operation), -40°C to +70°C (storage)\n* Mounts to: Wall, Tube, DIN-rail\n* Casing: IP65\n* Power supply: 1x A lithium battery\n* Weight: 100g\n* Frequency: 865-870 MHz\n* Power: 25mW (14 dBm) ", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/sigfox/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/atim/mr2-ex/v1.0.0/payload.js b/decoders/connector/atim/mr2-ex/v1.0.0/payload.js new file mode 100644 index 00000000..58709750 --- /dev/null +++ b/decoders/connector/atim/mr2-ex/v1.0.0/payload.js @@ -0,0 +1,1332 @@ +/* eslint-disable no-plusplus */ +/* eslint-disable no-bitwise */ +function decodeStream(payload, type, timestamp) { + // Init result + const result = { historics: [], events: [], realTimes: [] }; + + // Parse stream + // const jsonStream = JSON.parse(stream); + + // Get time and payload + // const time = new Date(jsonStream.data.timestamp * 1000) / 1000; + const time = new Date(timestamp * 1000) / 1000; + // const { payload } = jsonStream.data; + + // Save network informations + result.realTimes.push({ tagRef: "sys_data_payload", timestamp: time, tagValue: String(payload) }); + result.realTimes.push({ tagRef: "sys_data_timestamp", timestamp: time, tagValue: String(time) }); + /* if (jsonStream.data !== undefined) { + result.realTimes.push({ tagRef: "sys_data_type", timestamp: time, tagValue: String(jsonStream.data.type) }); + result.realTimes.push({ tagRef: "sys_data_raw", timestamp: time, tagValue: String(jsonStream.data.raw) }); + } + if (jsonStream.metadata !== undefined) { + result.realTimes.push({ tagRef: "sys_metadata_lastStreamTimestampUtc", timestamp: time, tagValue: String(jsonStream.metadata.lastStreamTimestampUtc) }); + if (jsonStream.metadata.device !== undefined) { + result.realTimes.push({ tagRef: "sys_device_sn", timestamp: time, tagValue: String(jsonStream.metadata.device.serialNumber) }); + result.realTimes.push({ tagRef: "sys_device_name", timestamp: time, tagValue: String(jsonStream.metadata.device.name) }); + } + if (jsonStream.metadata.endpoint !== undefined) { + result.realTimes.push({ tagRef: "sys_endpoint_name", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.name) }); + result.realTimes.push({ tagRef: "sys_endpoint_type", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.type) }); + } + if (jsonStream.metadata.network !== undefined) { + result.realTimes.push({ tagRef: "sys_network_port", timestamp: time, tagValue: String(jsonStream.metadata.network.port) }); + result.realTimes.push({ tagRef: "sys_network_linkQuality", timestamp: time, tagValue: String(jsonStream.metadata.network.linkQuality) }); + } + if (jsonStream.metadata.location !== undefined) { + result.realTimes.push({ tagRef: "sys_location_source", timestamp: time, tagValue: String(jsonStream.metadata.location.source) }); + result.realTimes.push({ tagRef: "sys_location_latitude", timestamp: time, tagValue: String(jsonStream.metadata.location.latitude) }); + result.realTimes.push({ tagRef: "sys_location_longitude", timestamp: time, tagValue: String(jsonStream.metadata.location.longitude) }); + result.realTimes.push({ tagRef: "sys_location_accuracy", timestamp: time, tagValue: String(jsonStream.metadata.location.accuracy) }); + } + } */ + if (type !== 3) { + // if KHEIRON + /* if (jsonStream.metadata.endpoint.type === 0) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).fcnt) }); + } + // if SIGFOX + if (jsonStream.metadata.endpoint.type === 5) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).seqNumber) }); + } + // if OBJENIOUS + if (jsonStream.metadata.endpoint.type === 6) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).count) }); + result.realTimes.push({ tagRef: "custom_data_raw_devEUI", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).device_properties.deveui) }); + } + */ + // Get message ID + const msgStr = parseInt(payload.substring(0, 2), 16); + + // Switch message ids + switch (msgStr) { + case 1: { + // Get meter + const battery = parseInt(payload.substring(2, 6), 16); + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery / 1000), + }); + + break; + } + case 3: { + // Get data + const battery = parseInt(payload.substring(2, 4), 16); + let temperature = parseInt(payload.substring(4, 8), 16); + const humidity = parseInt(payload.substring(8, 12), 16); + + // check if negative value + if (temperature >> 15 === 1) { + const reverseValue = temperature ^ 65535; + temperature = (reverseValue + 1) * -1; + } + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temperature / 100), + }); + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity / 100), + }); + break; + } + case 5: { + const testCounter = parseInt(payload.substring(2, 4), 16); + + result.realTimes.push({ + tagRef: "p_test", + timestamp: time, + tagValue: String(testCounter), + }); + + break; + } + case 9: { + // Get input + const di = parseInt(payload.substring(4, 6), 16); + const di1 = (di & 1) === 1; + const di2 = (di & 2) === 2; + + // get ouput + const dout = parseInt(payload.substring(2, 4), 16); + const do1 = (dout & 1) === 1; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DO1", + timestamp: time, + tagValue: String(do1), + }); + + break; + } + case 10: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + break; + } + case 11: { + break; + } + case 12: { + break; + } + case 13: { + break; + } + case 14: { + break; + } + case 21: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // Decode + temp = (temp - 33184) / 128; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + break; + } + case 20: { + // Get meter + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 48: { + // get realtime meter + const meter = parseInt(payload.substring(42, 50), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + + // get historic meter + const meter1 = parseInt(payload.substring(34, 42), 16); + const meter2 = parseInt(payload.substring(26, 34), 16); + const meter3 = parseInt(payload.substring(18, 26), 16); + const meter4 = parseInt(payload.substring(10, 18), 16); + const meter5 = parseInt(payload.substring(2, 10), 16); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 1) / 1000, + tagValue: String(meter1), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 2) / 1000, + tagValue: String(meter2), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 3) / 1000, + tagValue: String(meter3), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 4) / 1000, + tagValue: String(meter4), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 5) / 1000, + tagValue: String(meter5), + }); + + break; + } + case 49: { + // get realtime meter + const meter = parseInt(payload.substring(2, 10), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + break; + } + case 55: { + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + break; + } + case 57: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // const a = new Date(time * 1000); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (600000 + i * 600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 58: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (1800000 + i * 1800000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 59: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (3600000 + i * 3600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 22: { + // Get meter + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(4, 6), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 15: { + break; + } + case 16: { + break; + } + case 17: { + break; + } + case 18: { + break; + } + case 23: { + // Get temperature & humidity + let temp = parseInt(payload.substring(2, 6), 16); + let humidity = parseInt(payload.substring(6, 10), 16); + + // Decode + temp = (temp * 175.72) / 65536 - 46.85; + humidity = (humidity * 125) / 65536 - 6; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity), + }); + break; + } + case 24: { + break; + } + case 30: { + break; + } + case 31: { + break; + } + case 32: { + break; + } + case 33: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 10 / 64240; + + result.realTimes.push({ + tagRef: "p_voltage", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 25: { + break; + } + case 34: { + break; + } + case 35: { + break; + } + case 36: { + break; + } + case 37: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 16 / 47584; + result.realTimes.push({ + tagRef: "p_current", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 27: { + break; + } + case 42: { + break; + } + case 43: { + break; + } + case 44: { + break; + } + case 45: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + break; + } + case 26: { + break; + } + case 38: { + break; + } + case 39: { + break; + } + case 40: { + break; + } + case 41: { + break; + } + case 83: { + break; + } + case 84: { + break; + } + case 85: { + break; + } + case 86: { + break; + } + case 87: { + break; + } + case 88: { + break; + } + case 89: { + break; + } + case 90: { + break; + } + case 91: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + let temp2 = parseInt(payload.substring(6, 10), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + + // check if error + if (temp2 !== 32768) { + // check if negative value + if (temp2 >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp2 = (reverseValue + 1) * -1; + } + + // apply coef + temp2 *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature2", + timestamp: time, + tagValue: String(temp2), + }); + } + + break; + } + case 19: { + // Get water leak state + const waterleak = parseInt(payload.substring(4, 6), 16); + + // save + result.realTimes.push({ + tagRef: "p_waterLeak", + timestamp: time, + tagValue: String(waterleak), + }); + break; + } + case 47: { + // get temperature + const temp = parseInt(payload.substring(14, 18), 16); + const p_temperature = (10.888 - Math.sqrt((-10.888) ** 2 + 4 * 0.00347 * (1777.3 - temp))) / (2 * -0.00347) + 30; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + + if (parseInt(payload.substring(18, 34), 16) !== 0) { + // get data for gps decoding + const a = parseInt(payload.substring(18, 20), 16) >> 4; + const b = ((parseInt(payload.substring(18, 20), 16) << 4) & 255) >> 4; + const c = parseInt(payload.substring(20, 22), 16) >> 4; + const d = ((parseInt(payload.substring(20, 22), 16) << 4) & 255) >> 4; + const e = parseInt(payload.substring(22, 24), 16) >> 4; + const f = ((parseInt(payload.substring(22, 24), 16) << 4) & 255) >> 4; + const g = parseInt(payload.substring(24, 26), 16) >> 4; + const h = ((parseInt(payload.substring(24, 26), 16) << 4) & 255) >> 4; + const i = parseInt(payload.substring(26, 28), 16) >> 4; + const j = ((parseInt(payload.substring(26, 28), 16) << 4) & 255) >> 4; + const k = parseInt(payload.substring(28, 30), 16) >> 4; + const l = ((parseInt(payload.substring(28, 30), 16) << 4) & 255) >> 4; + const m = parseInt(payload.substring(30, 32), 16) >> 4; + const n = ((parseInt(payload.substring(30, 32), 16) << 4) & 255) >> 4; + const o = parseInt(payload.substring(32, 34), 16) >> 4; + // const p = ((parseInt(payload.substring(32, 34), 16) << 4) & 255) >> 6; + const q = ((parseInt(payload.substring(32, 34), 16) << 6) & 255) >> 7; + const r = ((parseInt(payload.substring(32, 34), 16) << 7) & 255) >> 7; + // get latitude and longitude + const latitude = (2 * q - 1) * (10 * a + b + c / 6 + d / 60 + e / 600 + f / 6000 + g / 60000); + const longitude = (2 * r - 1) * (100 * h + 10 * i + j + k / 6 + l / 60 + m / 600 + n / 6000 + o / 60000); + + // save + result.realTimes.push({ + tagRef: "p_latitude", + timestamp: time, + tagValue: String(latitude), + }); + result.realTimes.push({ + tagRef: "p_longitude", + timestamp: time, + tagValue: String(longitude), + }); + } + + break; + } + case 50: { + const p_vibration = parseInt(payload.substring(4, 6), 16); + const p_ils = parseInt(payload.substring(6, 8), 16); + const p_temperature = (2103 - parseInt(payload.substring(8, 12), 16)) / 10.9; + + // save + result.realTimes.push({ + tagRef: "p_vibration", + timestamp: time, + tagValue: String(p_vibration), + }); + result.realTimes.push({ + tagRef: "p_ils", + timestamp: time, + tagValue: String(p_ils), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + break; + } + case 51: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + break; + } + case 52: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get count + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(parseInt(payload.substring(4, 12), 16)), + }); + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(parseInt(payload.substring(12, 20), 16)), + }); + break; + } + case 53: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 54: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 62: { + // Get Absence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(0), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + const valueWorZ = parseInt(payload.substring(10, 14), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + + // Save WorZ + result.realTimes.push({ + tagRef: "p_WorZ", + timestamp: time, + tagValue: String(valueWorZ), + }); + break; + } + case 63: { + // Get Presence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(1), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + break; + } + case 65: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + break; + } + case 66: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + break; + } + case 67: { + // save + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time - 1, + tagValue: String(1), + context: [], + }); + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time, + tagValue: String(0), + context: [], + }); + break; + } + case 78: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + // get count 1 + const count1 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 79: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(14, 22), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 80: { + // get count 1 + const count1 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 81: { + // get count 3 + const count3 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count3", + timestamp: time, + tagValue: String(count3), + }); + + // get count 4 + const count4 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count4", + timestamp: time, + tagValue: String(count4), + }); + break; + } + case 82: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 93: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(2 + 8 * j, 10 + 8 * j), 16)), + }); + } + break; + } + case 94: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(-2 + 8 * j, 6 + 8 * j), 16)), + }); + } + break; + } + case 95: { + // get count 5 + const count5 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count5", + timestamp: time, + tagValue: String(count5), + }); + + // get count 6 + const count6 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count6", + timestamp: time, + tagValue: String(count6), + }); + break; + } + case 96: { + // get count 7 + const count7 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count7", + timestamp: time, + tagValue: String(count7), + }); + + // get count 8 + const count8 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count8", + timestamp: time, + tagValue: String(count8), + }); + break; + } + case 2: + case 4: + case 6: + case 7: + case 8: + case 46: + case 56: + case 60: + case 61: + case 64: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 92: + case 97: + case 98: + case 99: + default: + break; + } + } + // Return result + return result; +} + +function ToTagoFormat(object_item, serie) { + const historics = []; + const events = []; + const realTimes = []; + // eslint-disable-next-line guard-for-in + for (const key in object_item.realTimes) { + realTimes.push({ + variable: `realTimes_${object_item.realTimes[key].tagRef}`.toLowerCase(), + value: object_item.realTimes[key].tagValue, + time: object_item.realTimes[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.events) { + events.push({ + variable: `events_${object_item.events[key].tagRef}`.toLowerCase(), + value: object_item.events[key].tagValue, + time: object_item.events[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.historics) { + historics.push({ + variable: `historics_${object_item.historics[key].tagRef}`.toLowerCase(), + value: object_item.historics[key].tagValue, + time: object_item.historics[key].timestamp, + serie, + }); + } + const result = historics.concat(events, realTimes); + + return result; +} + +/* let payload = [ + { variable: "payload", value: "5e7f000000003f00000040000000410000004200000043000000440000004500000046" }, + { variable: "type", value: 0 }, + { variable: "timestamp", value: 1605614318410 }, +]; */ + +const data = payload.find((x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data"); +const type = payload.find((x) => x.variable === "type"); +const timestamp = payload.find((x) => x.variable === "timestamp"); + +if (data) { + // const buffer = Buffer.from(data.value, "hex"); + const serie = new Date().getTime(); + // payload = decodeStream(data.value, type.value, timestamp.value); + payload = ToTagoFormat(decodeStream(data.value, type.value, timestamp.value), serie); +} + +// eslint-disable-next-line no-console +// console.log(payload); diff --git a/decoders/connector/atim/mr2/assets/logo.png b/decoders/connector/atim/mr2/assets/logo.png new file mode 100644 index 00000000..ea36b111 Binary files /dev/null and b/decoders/connector/atim/mr2/assets/logo.png differ diff --git a/decoders/connector/atim/mr2/connector.jsonc b/decoders/connector/atim/mr2/connector.jsonc new file mode 100644 index 00000000..2d05c994 --- /dev/null +++ b/decoders/connector/atim/mr2/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Atim MR2", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/atim/mr2/description.md b/decoders/connector/atim/mr2/description.md new file mode 100644 index 00000000..b0c62280 --- /dev/null +++ b/decoders/connector/atim/mr2/description.md @@ -0,0 +1 @@ +Metering index of water, gas or electricity, or for remote bang-bang equipment control over LoRaWAN or Sigfox \ No newline at end of file diff --git a/decoders/connector/atim/mr2/v1.0.0/payload-config.jsonc b/decoders/connector/atim/mr2/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..731797ee --- /dev/null +++ b/decoders/connector/atim/mr2/v1.0.0/payload-config.jsonc @@ -0,0 +1,26 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "The ACW-MR2 is a sensor used for remote metering monitoring (gas, water, electricity, pluviometer meters…) or for remote bang-bang equipment control (eg: starting up/shutting down, opening/closing...). This sensor can be connected up to 2 independent equipment when they are close enough, allowing devices and network plans savings.\n\n\n**Technical Data**\n* Dimesions: 177 x 55 x 55 mm\n* Antenna: Integrated (¼ wave)\n* Temperature: -20°C to +55°C (operation), -40°C to +70°C (storage)\n* Mounts to: Wall, Mast, DIN-rail\n* Casing: IP65\n* Power supply: 2 packs of 2 batteries equivalent to 14.4 Ah\n* Weight: 160g\n* Frequency: 865-870 MHz\n* Power: 25mW (14 dBm) \n* Transfer rate: Sigfox: 100 bit/s, LoRaWAN: 300 bit/s to 10 kbit/s", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/sigfox/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/atim/mr2/v1.0.0/payload.js b/decoders/connector/atim/mr2/v1.0.0/payload.js new file mode 100644 index 00000000..de52b1ad --- /dev/null +++ b/decoders/connector/atim/mr2/v1.0.0/payload.js @@ -0,0 +1,644 @@ +/* eslint-disable guard-for-in */ +/* eslint-disable no-use-before-define */ +/* eslint-disable no-plusplus */ +/* eslint-disable no-case-declarations */ +/* eslint-disable no-bitwise */ +/* eslint-disable no-nested-ternary */ +function decodeStream(payload, timestamp, latitude, longitude) { + // Init result + let result = { historics: [], events: [], realTimes: [] }; + + // Parse stream + // const jsonStream = JSON.parse(stream); + + // Get time and payload + // const time = new Date(data.timestamp * 1000) / 1000; + const time = new Date(timestamp * 1000) / 1000; + // const { payload } = data; + + // Get message ID + const header = parseInt(payload.substring(0, 2), 16); + if (((header << 2) & 255) >> 7 === 1) { + result = DecodeMesureFrame(payload, time); + } else { + result = DecodeCommonFrame(payload, time); + } + + if (header === 129) { + result.realTimes.push({ tagRef: "p_batteryVoltage_empty", timestamp: time, tagValue: String(parseInt(payload.substr(2, 4), 16) / 1000) }); + result.realTimes.push({ tagRef: "p_batteryVoltage_inCharge", timestamp: time, tagValue: String(parseInt(payload.substr(6, 4), 16) / 1000) }); + } + + // Save network informations + result.realTimes.push({ tagRef: "sys_data_payload", timestamp: time, tagValue: String(payload) }); + result.realTimes.push({ tagRef: "sys_data_timestamp", timestamp: time, tagValue: String(time) }); + /* + if (data !== undefined) { + result.realTimes.push({ tagRef: "sys_data_type", timestamp: time, tagValue: String(data.type) }); + result.realTimes.push({ tagRef: "sys_data_raw", timestamp: time, tagValue: String(data.raw) }); + } + if (metadata !== undefined) { + if (metadata.device !== undefined) { + result.realTimes.push({ tagRef: "sys_device_sn", timestamp: time, tagValue: String(metadata.device.serialNumber) }); + result.realTimes.push({ tagRef: "sys_device_name", timestamp: time, tagValue: String(metadata.device.name) }); + } + if (metadata.endpoint !== undefined) { + result.realTimes.push({ tagRef: "sys_endpoint_name", timestamp: time, tagValue: String(metadata.endpoint.name) }); + result.realTimes.push({ tagRef: "sys_endpoint_type", timestamp: time, tagValue: String(metadata.endpoint.type) }); + } + if (metadata.network !== undefined) { + result.realTimes.push({ tagRef: "sys_network_port", timestamp: time, tagValue: String(metadata.network.port) }); + result.realTimes.push({ tagRef: "sys_network_linkQuality", timestamp: time, tagValue: String(metadata.network.linkQuality) }); + } */ + if (latitude !== undefined && longitude !== undefined) { + // result.realTimes.push({ tagRef: "sys_location_source", timestamp: time, tagValue: String(metadata.location.source) }); + result.realTimes.push({ tagRef: "sys_location_latitude", timestamp: time, tagValue: latitude, unit: "°" }); + result.realTimes.push({ tagRef: "sys_location_longitude", timestamp: time, tagValue: longitude, unit: "°" }); + // result.realTimes.push({ tagRef: "sys_location_accuracy", timestamp: time, tagValue: String(metadata.location.accuracy) }); + } + /* } + + if (metadata.endpoint !== undefined) { + // if KHEIRON + if (metadata.endpoint.type === 0) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(data.raw.fcnt) }); + } + // if SIGFOX + if (metadata.endpoint.type === 5) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(data.raw.seqNumber) }); + } + // if OBJENIOUS + if (metadata.endpoint.type === 6) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(data.raw.count) }); + result.realTimes.push({ tagRef: "custom_data_raw_devEUI", timestamp: time, tagValue: String(data.raw.device_properties.deveui) }); + } + } + */ + + return result; +} + +function DecodeCommonFrame(payload, time) { + // Init result + const result = { + historics: [], + events: [], + realTimes: [], + }; + + // get bit values + const horodatage = ((parseInt(payload.substring(0, 2), 16) << 1) & 255) >> 7; + + // calcul of the starting index + let startingIndex = 2 + horodatage * 8; + + // get frame type + const frameType = ((parseInt(payload.substring(0, 2), 16) << 4) & 255) >> 4; + + // keep alive frame + if (frameType === 1) { + result.realTimes.push({ + tagRef: "p_keepAlive", + timestamp: time, + tagValue: String(1), + }); + } + + // threshold alarm + if (frameType === 13 || frameType === 5) { + while (startingIndex < payload.length) { + // get header informations of each voie + const header_voie = parseInt(payload.substring(startingIndex, startingIndex + 2), 16); + const alertType_voie = header_voie >> 6; + const number_voie = ((header_voie << 2) & 255) >> 6; + const mesureType_voie = ((header_voie << 4) & 255) >> 4; + const mesureSize_voie = getMesureSize(mesureType_voie); + + // increase starting index + startingIndex += 2; + + // check if the size is different than 0 + if (mesureSize_voie !== 0) { + // get mesure + const mesure = parseInt(payload.substring(startingIndex, startingIndex + mesureSize_voie), 16); + + // get calculated table of log + const calculatedMesureTab = getCalculatedMesure( + mesure, + mesureType_voie, + number_voie, + horodatage ? new Date(parseInt(payload.substring(2, 10), 16) * 1000).getTime() : time + ); + + // add table log into realtimes + result.realTimes = result.realTimes.concat(calculatedMesureTab); + + // get calculated table of events + const eventTab = getThresholdEvents(mesureType_voie, alertType_voie, number_voie, horodatage ? new Date(parseInt(payload.substring(2, 10), 16) * 1000).getTime() : time); + + // add table event into events + result.events = result.events.concat(eventTab); + + // increase index + startingIndex += mesureSize_voie; + } else { + return result; + } + } + } + + // general alarm + if (frameType === 14) { + // get header informations of each voie + const header_error = parseInt(payload.substring(startingIndex, startingIndex + 2), 16); + const length_error = ((header_error << 3) & 255) >> 3; + + // increase starting index + startingIndex += 2; + + for (let e = 0; e < length_error; e++) { + // get error and add log into realtimes table + result.realTimes.push( + getError(parseInt(payload.substring(startingIndex, startingIndex + 2), 16), horodatage ? new Date(parseInt(payload.substring(2, 10), 16) * 1000).getTime() : time) + ); + + // increase starting index + startingIndex += 2; + } + } + + if (frameType === 15) { + if (parseInt(payload.substring(startingIndex, startingIndex + 2), 16) === 28) { + // add wirecut into realtimes table + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: horodatage ? new Date(parseInt(payload.substring(2, 10), 16) * 1000).getTime() : time, + tagValue: String(parseInt(payload.substring(startingIndex + 2, startingIndex + 4), 16) === 1 ? 0 : 1), + }); + } + } + return result; +} + +function DecodeMesureFrame(payload, time) { + // Init result + const result = { + historics: [], + events: [], + realTimes: [], + }; + + // get bit values + const horodatage = ((parseInt(payload.substring(0, 2), 16) << 1) & 255) >> 7; + const profondeur = (((parseInt(payload.substring(0, 2), 16) << 3) & 255) >> 6) + 1; + const nb_echantillons = (((parseInt(payload.substring(0, 2), 16) << 5) & 255) >> 5) + 1; + const period = profondeur > 1 || nb_echantillons > 1 ? parseInt(payload.substring(2 + horodatage * 8, 6 + horodatage * 8), 16) : 0; + // calcul of the starting index + let startingIndex = 2 + horodatage * 8 + (profondeur > 1 || nb_echantillons > 1) * 4; + + while (startingIndex < payload.length) { + // get header informations of each voie + const header_voie = parseInt(payload.substring(startingIndex, startingIndex + 2), 16); + const number_voie = ((header_voie << 2) & 255) >> 6; + const mesureType_voie = ((header_voie << 4) & 255) >> 4; + const mesureSize_voie = getMesureSize(mesureType_voie); + + // increase starting index + startingIndex += 2; + + // check if the size is different than 0 + if (mesureSize_voie !== 0) { + // iterate on each mesure + + for (let i = 0; i < profondeur * nb_echantillons; i++) { + // get mesure + const mesure = parseInt(payload.substring(startingIndex, startingIndex + mesureSize_voie), 16); + + if (i === 0) { + // get calculated table of log + const calculatedMesureTab = getCalculatedMesure( + mesure, + mesureType_voie, + number_voie, + horodatage ? new Date(parseInt(payload.substring(2, 10), 16) * 1000).getTime() : time + ); + + // add table log into realtimes + result.realTimes = result.realTimes.concat(calculatedMesureTab); + } else { + // get calculated table of log + const calculatedMesureTab = getCalculatedMesure( + mesure, + mesureType_voie, + number_voie, + horodatage + ? new Date((parseInt(payload.substring(2, 10), 16) - (period / nb_echantillons) * 60 * i) * 1000).getTime() + : new Date((time - (period / nb_echantillons) * 60 * i) * 1000).getTime() + ); + + // add table log into historics + result.historics = result.historics.concat(calculatedMesureTab); + } + + // increase index + startingIndex += mesureSize_voie; + } + } else { + return result; + } + } + + return result; +} + +function getMesureSize(mesureType) { + switch (mesureType) { + case 1: + return 2; + case 2: + case 3: + case 7: + case 8: + case 9: + case 10: + case 11: + return 4; + case 4: + return 8; + default: + return 0; + } +} + +function getCalculatedMesure(mesure, mesureType, number_voie, date) { + switch (mesureType) { + case 1: + case 2: + const tab = []; + const mesureString = `0000000${mesure.toString(2)}`.slice(-8); + for (let i = 1; i < mesureString.length + 1; i++) { + tab.push({ + tagRef: `p_DI${i}_${number_voie}`, + timestamp: date, + tagValue: String(mesureString[mesureString.length - i]), + }); + } + return tab; + case 3: + case 4: + return [ + { + tagRef: `p_count_${number_voie}`, + timestamp: date, + tagValue: String(mesure), + }, + ]; + case 7: + if (mesure >> 15 === 1) { + return [ + { + tagRef: `p_mm_${number_voie}`, + timestamp: date, + tagValue: String(((mesure ^ 65535) + 1) * -1), + }, + ]; + } + return [ + { + tagRef: `p_mm_${number_voie}`, + timestamp: date, + tagValue: String(mesure), + }, + ]; + + case 10: + if (mesure >> 15 === 1) { + return [ + { + tagRef: `p_mV_${number_voie}`, + timestamp: date, + tagValue: String(((mesure ^ 65535) + 1) * -1), + }, + ]; + } + return [ + { + tagRef: `p_mV_${number_voie}`, + timestamp: date, + tagValue: String(mesure), + }, + ]; + + case 11: + if (mesure >> 15 === 1) { + return [ + { + tagRef: `p_uA_${number_voie}`, + timestamp: date, + tagValue: String(((mesure ^ 65535) + 1) * -1), + }, + ]; + } + return [ + { + tagRef: `p_uA_${number_voie}`, + timestamp: date, + tagValue: String(mesure), + }, + ]; + + case 8: + if (mesure >> 15 === 1) { + return [ + { + tagRef: `p_temperature_${number_voie}`, + timestamp: date, + tagValue: String((((mesure ^ 65535) + 1) / 100) * -1), + }, + ]; + } + return [ + { + tagRef: `p_temperature_${number_voie}`, + timestamp: date, + tagValue: String(mesure / 100), + }, + ]; + + case 9: + if (mesure >> 15 === 1) { + return [ + { + tagRef: `p_humidity_${number_voie}`, + timestamp: date, + tagValue: String((((mesure ^ 65535) + 1) / 100) * -1), + }, + ]; + } + return [ + { + tagRef: `p_humidity_${number_voie}`, + timestamp: date, + tagValue: String(mesure / 100), + }, + ]; + + default: + return []; + } +} + +function getThresholdEvents(mesureType, alertType, number_voie, date) { + switch (mesureType) { + case 3: + case 4: + return [ + { + tagRef: `p_count_${number_voie}_high_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 1 ? 1 : 0), + context: [], + }, + { + tagRef: `p_count_${number_voie}_low_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 2 ? 1 : 0), + context: [], + }, + ]; + case 7: + return [ + { + tagRef: `p_mm_${number_voie}_high_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 1 ? 1 : 0), + context: [], + }, + { + tagRef: `p_mm_${number_voie}_low_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 2 ? 1 : 0), + context: [], + }, + ]; + case 10: + return [ + { + tagRef: `p_mV_${number_voie}_high_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 1 ? 1 : 0), + context: [], + }, + { + tagRef: `p_mV_${number_voie}_low_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 2 ? 1 : 0), + context: [], + }, + ]; + case 11: + return [ + { + tagRef: `p_uA_${number_voie}_high_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 1 ? 1 : 0), + context: [], + }, + { + tagRef: `p_uA_${number_voie}_low_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 2 ? 1 : 0), + context: [], + }, + ]; + case 8: + return [ + { + tagRef: `p_temperature_${number_voie}_high_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 1 ? 1 : 0), + context: [], + }, + { + tagRef: `p_temperature_${number_voie}_low_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 2 ? 1 : 0), + context: [], + }, + ]; + case 9: + return [ + { + tagRef: `p_humidity_${number_voie}_high_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 1 ? 1 : 0), + context: [], + }, + { + tagRef: `p_humidity_${number_voie}_low_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 2 ? 1 : 0), + context: [], + }, + ]; + default: + return []; + } +} + +function getError(error_code, date) { + let ref; + switch (error_code) { + case 0: + ref = "p_ERR_BUF_SMALLER"; + break; + case 1: + ref = "p_ERR_DEPTH_HISTORIC_OUT_OF_RANGE"; + break; + case 2: + ref = "p_ERR_NB_SAMPLE_OUT_OF_RANGE"; + break; + case 3: + ref = "p_ERR_NWAY_OUT_OF_RANGE"; + break; + case 4: + ref = "p_ERR_TYPEWAY_OUT_OF_RANGE"; + break; + case 5: + ref = "p_ERR_SAMPLING_PERIOD"; + break; + case 6: + ref = "p_ERR_KEEP_ALIVE_PERIOD"; + break; + case 7: + ref = "p_ERR_SUBTASK_END"; + break; + case 8: + ref = "p_ERR_NULL_POINTER"; + break; + case 9: + ref = "p_ERR_BATTERY_LEVEL_LOW"; + break; + case 10: + ref = "p_ERR_BATTERY_LEVEL_DEAD"; + break; + case 11: + ref = "p_ERR_EEPROM"; + break; + case 12: + ref = "p_ERR_ROM"; + break; + case 13: + ref = "p_ERR_RAM"; + break; + case 14: + ref = "p_ERR_SENSORS_TIMEOUT"; + break; + case 15: + ref = "p_ERR_SENSOR_STOP"; + break; + case 16: + ref = "p_ERR_SENSORS_FAIL"; + break; + case 17: + ref = "p_ERR_ARM_INIT_FAIL"; + break; + case 18: + ref = "p_ERR_ARM_PAYLOAD_BIGGER"; + break; + case 19: + ref = "p_ERR_ARM_BUSY"; + break; + case 20: + ref = "p_ERR_ARM_BRIDGE_ENABLE"; + break; + case 21: + ref = "p_ERR_ARM_TRANSMISSION"; + break; + case 22: + ref = "p_ERR_RADIO_QUEUE_FULL"; + break; + case 23: + ref = "p_ERR_CFG_BOX_INIT_FAIL"; + break; + case 24: + ref = "p_ERR_BOX_OPENED"; + break; + default: + return undefined; + } + + return { + tagRef: ref, + timestamp: date, + tagValue: String(1), + }; +} + +// eslint-disable-next-line no-unused-vars +function ToTagoFormat(object_item, serie, prefix = "") { + const historics = []; + const events = []; + const realTimes = []; + for (const key in object_item.realTimes) { + realTimes.push({ + variable: `realTimes_${object_item.realTimes[key].tagRef}`.toLowerCase(), + value: object_item.realTimes[key].tagValue, + time: object_item.realTimes[key].timestamp, + serie, + }); + } + for (const key in object_item.events) { + events.push({ + variable: `events_${object_item.events[key].tagRef}`.toLowerCase(), + value: object_item.events[key].tagValue, + time: object_item.events[key].timestamp, + serie, + }); + } + for (const key in object_item.historics) { + historics.push({ + variable: `historics_${object_item.historics[key].tagRef}`.toLowerCase(), + value: object_item.historics[key].tagValue, + time: object_item.historics[key].timestamp, + serie, + }); + } + const result = historics.concat(events, realTimes); + + return result; +} + +/* let payload = [ + { variable: "data", value: { timestamp: 1605614318410, payload: "810d240c68", raw: { seqNumber: 123 } } }, + { + variable: "metadata", + value: { + endpoint: { name: "sigfox", type: 5 }, + network: { port: 95, linkQuality: 100 }, + location: { source: "my_head", latitude: "-38°", longitude: "-18°", accuracy: 95 }, + device: { name: "TM1P", serialNumber: "01101011" }, + }, + }, +]; */ +/* +let payload = [ + { variable: "data", value: "a1000211aabb220055006633000300044400000004000000055700050006682221222279777777788a000800099b0009000a" }, + { variable: "timestamp", value: 1605614318410 }, + { variable: "latitude", value: -38 }, + { variable: "longitude", value: -18 }, +]; +*/ +const data = payload.find((x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data"); +const timestamp = payload.find((x) => x.variable === "timestamp"); +const latitude = payload.find((x) => x.variable === "latitude"); +const longitude = payload.find((x) => x.variable === "longitude"); + +if (data) { + // const buffer = Buffer.from(data.value, "hex"); + const serie = new Date().getTime(); + payload = ToTagoFormat(decodeStream(data.value, timestamp.value, latitude.value, longitude.value), serie); +} + +// eslint-disable-next-line no-console +// console.log(payload); diff --git a/decoders/connector/atim/th-i/assets/logo.png b/decoders/connector/atim/th-i/assets/logo.png new file mode 100644 index 00000000..d00f9ec4 Binary files /dev/null and b/decoders/connector/atim/th-i/assets/logo.png differ diff --git a/decoders/connector/atim/th-i/connector.jsonc b/decoders/connector/atim/th-i/connector.jsonc new file mode 100644 index 00000000..53b989c9 --- /dev/null +++ b/decoders/connector/atim/th-i/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Atim TH-I", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/atim/th-i/description.md b/decoders/connector/atim/th-i/description.md new file mode 100644 index 00000000..eddbd134 --- /dev/null +++ b/decoders/connector/atim/th-i/description.md @@ -0,0 +1 @@ +Indoor Temperature and Humidity sensor over LoRaWAN or Sigfox \ No newline at end of file diff --git a/decoders/connector/atim/th-i/v1.0.0/payload-config.jsonc b/decoders/connector/atim/th-i/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..e04c9cbd --- /dev/null +++ b/decoders/connector/atim/th-i/v1.0.0/payload-config.jsonc @@ -0,0 +1,26 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "The ACW-TH-I monitors and transmits the indoor temperature and humidity of all types of buildings (industrial, tertiary, hospital, etc.).\n\n**Technical Data**\n* Dimensions: 80 x 80 x 35 mm\n* Antenna: Integrated (¼ wave)\n* Temperature: -20°C to +55°C (operation), -40°C to +70°C (storage)\n* Mounts to: Wall\n* Housing: Building automation\n* Power supply: 1 battery-pack 3,6V / 7,2 mAh\n* Weight: 100g\n* Frequency: 865-870 MHz\n* Power: 25 mW (14 dBm)\n* Transfer rate: Local 1.2 to 115 kbit/s, Sigfox 100 bit/s, LoRaWAN 300 bit/s to 10 kbit/s\n\n**Temperature Sensor**\n* Range: -40 to +125°C\n* Resolution: 0.01°C (14 bits)\n* Precision from -40°C to -10°C: -0.7°C/+0.9°C\n* Precision from -10°C to 85°C: +-0.3°C\n* Precision from 85°C to 125°C: -0.7°C/+0.9°C\n\n**Humidity Sensor**\n* Range: 0% RH to 100% RH\n* Resolution: 0.025% RH RMS (12 bits)\n* Precision between 0 and 80% RH: +-3% RH", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/sigfox/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/atim/th-i/v1.0.0/payload.js b/decoders/connector/atim/th-i/v1.0.0/payload.js new file mode 100644 index 00000000..58709750 --- /dev/null +++ b/decoders/connector/atim/th-i/v1.0.0/payload.js @@ -0,0 +1,1332 @@ +/* eslint-disable no-plusplus */ +/* eslint-disable no-bitwise */ +function decodeStream(payload, type, timestamp) { + // Init result + const result = { historics: [], events: [], realTimes: [] }; + + // Parse stream + // const jsonStream = JSON.parse(stream); + + // Get time and payload + // const time = new Date(jsonStream.data.timestamp * 1000) / 1000; + const time = new Date(timestamp * 1000) / 1000; + // const { payload } = jsonStream.data; + + // Save network informations + result.realTimes.push({ tagRef: "sys_data_payload", timestamp: time, tagValue: String(payload) }); + result.realTimes.push({ tagRef: "sys_data_timestamp", timestamp: time, tagValue: String(time) }); + /* if (jsonStream.data !== undefined) { + result.realTimes.push({ tagRef: "sys_data_type", timestamp: time, tagValue: String(jsonStream.data.type) }); + result.realTimes.push({ tagRef: "sys_data_raw", timestamp: time, tagValue: String(jsonStream.data.raw) }); + } + if (jsonStream.metadata !== undefined) { + result.realTimes.push({ tagRef: "sys_metadata_lastStreamTimestampUtc", timestamp: time, tagValue: String(jsonStream.metadata.lastStreamTimestampUtc) }); + if (jsonStream.metadata.device !== undefined) { + result.realTimes.push({ tagRef: "sys_device_sn", timestamp: time, tagValue: String(jsonStream.metadata.device.serialNumber) }); + result.realTimes.push({ tagRef: "sys_device_name", timestamp: time, tagValue: String(jsonStream.metadata.device.name) }); + } + if (jsonStream.metadata.endpoint !== undefined) { + result.realTimes.push({ tagRef: "sys_endpoint_name", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.name) }); + result.realTimes.push({ tagRef: "sys_endpoint_type", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.type) }); + } + if (jsonStream.metadata.network !== undefined) { + result.realTimes.push({ tagRef: "sys_network_port", timestamp: time, tagValue: String(jsonStream.metadata.network.port) }); + result.realTimes.push({ tagRef: "sys_network_linkQuality", timestamp: time, tagValue: String(jsonStream.metadata.network.linkQuality) }); + } + if (jsonStream.metadata.location !== undefined) { + result.realTimes.push({ tagRef: "sys_location_source", timestamp: time, tagValue: String(jsonStream.metadata.location.source) }); + result.realTimes.push({ tagRef: "sys_location_latitude", timestamp: time, tagValue: String(jsonStream.metadata.location.latitude) }); + result.realTimes.push({ tagRef: "sys_location_longitude", timestamp: time, tagValue: String(jsonStream.metadata.location.longitude) }); + result.realTimes.push({ tagRef: "sys_location_accuracy", timestamp: time, tagValue: String(jsonStream.metadata.location.accuracy) }); + } + } */ + if (type !== 3) { + // if KHEIRON + /* if (jsonStream.metadata.endpoint.type === 0) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).fcnt) }); + } + // if SIGFOX + if (jsonStream.metadata.endpoint.type === 5) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).seqNumber) }); + } + // if OBJENIOUS + if (jsonStream.metadata.endpoint.type === 6) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).count) }); + result.realTimes.push({ tagRef: "custom_data_raw_devEUI", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).device_properties.deveui) }); + } + */ + // Get message ID + const msgStr = parseInt(payload.substring(0, 2), 16); + + // Switch message ids + switch (msgStr) { + case 1: { + // Get meter + const battery = parseInt(payload.substring(2, 6), 16); + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery / 1000), + }); + + break; + } + case 3: { + // Get data + const battery = parseInt(payload.substring(2, 4), 16); + let temperature = parseInt(payload.substring(4, 8), 16); + const humidity = parseInt(payload.substring(8, 12), 16); + + // check if negative value + if (temperature >> 15 === 1) { + const reverseValue = temperature ^ 65535; + temperature = (reverseValue + 1) * -1; + } + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temperature / 100), + }); + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity / 100), + }); + break; + } + case 5: { + const testCounter = parseInt(payload.substring(2, 4), 16); + + result.realTimes.push({ + tagRef: "p_test", + timestamp: time, + tagValue: String(testCounter), + }); + + break; + } + case 9: { + // Get input + const di = parseInt(payload.substring(4, 6), 16); + const di1 = (di & 1) === 1; + const di2 = (di & 2) === 2; + + // get ouput + const dout = parseInt(payload.substring(2, 4), 16); + const do1 = (dout & 1) === 1; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DO1", + timestamp: time, + tagValue: String(do1), + }); + + break; + } + case 10: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + break; + } + case 11: { + break; + } + case 12: { + break; + } + case 13: { + break; + } + case 14: { + break; + } + case 21: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // Decode + temp = (temp - 33184) / 128; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + break; + } + case 20: { + // Get meter + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 48: { + // get realtime meter + const meter = parseInt(payload.substring(42, 50), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + + // get historic meter + const meter1 = parseInt(payload.substring(34, 42), 16); + const meter2 = parseInt(payload.substring(26, 34), 16); + const meter3 = parseInt(payload.substring(18, 26), 16); + const meter4 = parseInt(payload.substring(10, 18), 16); + const meter5 = parseInt(payload.substring(2, 10), 16); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 1) / 1000, + tagValue: String(meter1), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 2) / 1000, + tagValue: String(meter2), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 3) / 1000, + tagValue: String(meter3), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 4) / 1000, + tagValue: String(meter4), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 5) / 1000, + tagValue: String(meter5), + }); + + break; + } + case 49: { + // get realtime meter + const meter = parseInt(payload.substring(2, 10), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + break; + } + case 55: { + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + break; + } + case 57: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // const a = new Date(time * 1000); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (600000 + i * 600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 58: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (1800000 + i * 1800000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 59: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (3600000 + i * 3600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 22: { + // Get meter + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(4, 6), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 15: { + break; + } + case 16: { + break; + } + case 17: { + break; + } + case 18: { + break; + } + case 23: { + // Get temperature & humidity + let temp = parseInt(payload.substring(2, 6), 16); + let humidity = parseInt(payload.substring(6, 10), 16); + + // Decode + temp = (temp * 175.72) / 65536 - 46.85; + humidity = (humidity * 125) / 65536 - 6; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity), + }); + break; + } + case 24: { + break; + } + case 30: { + break; + } + case 31: { + break; + } + case 32: { + break; + } + case 33: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 10 / 64240; + + result.realTimes.push({ + tagRef: "p_voltage", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 25: { + break; + } + case 34: { + break; + } + case 35: { + break; + } + case 36: { + break; + } + case 37: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 16 / 47584; + result.realTimes.push({ + tagRef: "p_current", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 27: { + break; + } + case 42: { + break; + } + case 43: { + break; + } + case 44: { + break; + } + case 45: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + break; + } + case 26: { + break; + } + case 38: { + break; + } + case 39: { + break; + } + case 40: { + break; + } + case 41: { + break; + } + case 83: { + break; + } + case 84: { + break; + } + case 85: { + break; + } + case 86: { + break; + } + case 87: { + break; + } + case 88: { + break; + } + case 89: { + break; + } + case 90: { + break; + } + case 91: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + let temp2 = parseInt(payload.substring(6, 10), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + + // check if error + if (temp2 !== 32768) { + // check if negative value + if (temp2 >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp2 = (reverseValue + 1) * -1; + } + + // apply coef + temp2 *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature2", + timestamp: time, + tagValue: String(temp2), + }); + } + + break; + } + case 19: { + // Get water leak state + const waterleak = parseInt(payload.substring(4, 6), 16); + + // save + result.realTimes.push({ + tagRef: "p_waterLeak", + timestamp: time, + tagValue: String(waterleak), + }); + break; + } + case 47: { + // get temperature + const temp = parseInt(payload.substring(14, 18), 16); + const p_temperature = (10.888 - Math.sqrt((-10.888) ** 2 + 4 * 0.00347 * (1777.3 - temp))) / (2 * -0.00347) + 30; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + + if (parseInt(payload.substring(18, 34), 16) !== 0) { + // get data for gps decoding + const a = parseInt(payload.substring(18, 20), 16) >> 4; + const b = ((parseInt(payload.substring(18, 20), 16) << 4) & 255) >> 4; + const c = parseInt(payload.substring(20, 22), 16) >> 4; + const d = ((parseInt(payload.substring(20, 22), 16) << 4) & 255) >> 4; + const e = parseInt(payload.substring(22, 24), 16) >> 4; + const f = ((parseInt(payload.substring(22, 24), 16) << 4) & 255) >> 4; + const g = parseInt(payload.substring(24, 26), 16) >> 4; + const h = ((parseInt(payload.substring(24, 26), 16) << 4) & 255) >> 4; + const i = parseInt(payload.substring(26, 28), 16) >> 4; + const j = ((parseInt(payload.substring(26, 28), 16) << 4) & 255) >> 4; + const k = parseInt(payload.substring(28, 30), 16) >> 4; + const l = ((parseInt(payload.substring(28, 30), 16) << 4) & 255) >> 4; + const m = parseInt(payload.substring(30, 32), 16) >> 4; + const n = ((parseInt(payload.substring(30, 32), 16) << 4) & 255) >> 4; + const o = parseInt(payload.substring(32, 34), 16) >> 4; + // const p = ((parseInt(payload.substring(32, 34), 16) << 4) & 255) >> 6; + const q = ((parseInt(payload.substring(32, 34), 16) << 6) & 255) >> 7; + const r = ((parseInt(payload.substring(32, 34), 16) << 7) & 255) >> 7; + // get latitude and longitude + const latitude = (2 * q - 1) * (10 * a + b + c / 6 + d / 60 + e / 600 + f / 6000 + g / 60000); + const longitude = (2 * r - 1) * (100 * h + 10 * i + j + k / 6 + l / 60 + m / 600 + n / 6000 + o / 60000); + + // save + result.realTimes.push({ + tagRef: "p_latitude", + timestamp: time, + tagValue: String(latitude), + }); + result.realTimes.push({ + tagRef: "p_longitude", + timestamp: time, + tagValue: String(longitude), + }); + } + + break; + } + case 50: { + const p_vibration = parseInt(payload.substring(4, 6), 16); + const p_ils = parseInt(payload.substring(6, 8), 16); + const p_temperature = (2103 - parseInt(payload.substring(8, 12), 16)) / 10.9; + + // save + result.realTimes.push({ + tagRef: "p_vibration", + timestamp: time, + tagValue: String(p_vibration), + }); + result.realTimes.push({ + tagRef: "p_ils", + timestamp: time, + tagValue: String(p_ils), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + break; + } + case 51: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + break; + } + case 52: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get count + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(parseInt(payload.substring(4, 12), 16)), + }); + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(parseInt(payload.substring(12, 20), 16)), + }); + break; + } + case 53: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 54: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 62: { + // Get Absence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(0), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + const valueWorZ = parseInt(payload.substring(10, 14), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + + // Save WorZ + result.realTimes.push({ + tagRef: "p_WorZ", + timestamp: time, + tagValue: String(valueWorZ), + }); + break; + } + case 63: { + // Get Presence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(1), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + break; + } + case 65: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + break; + } + case 66: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + break; + } + case 67: { + // save + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time - 1, + tagValue: String(1), + context: [], + }); + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time, + tagValue: String(0), + context: [], + }); + break; + } + case 78: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + // get count 1 + const count1 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 79: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(14, 22), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 80: { + // get count 1 + const count1 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 81: { + // get count 3 + const count3 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count3", + timestamp: time, + tagValue: String(count3), + }); + + // get count 4 + const count4 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count4", + timestamp: time, + tagValue: String(count4), + }); + break; + } + case 82: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 93: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(2 + 8 * j, 10 + 8 * j), 16)), + }); + } + break; + } + case 94: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(-2 + 8 * j, 6 + 8 * j), 16)), + }); + } + break; + } + case 95: { + // get count 5 + const count5 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count5", + timestamp: time, + tagValue: String(count5), + }); + + // get count 6 + const count6 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count6", + timestamp: time, + tagValue: String(count6), + }); + break; + } + case 96: { + // get count 7 + const count7 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count7", + timestamp: time, + tagValue: String(count7), + }); + + // get count 8 + const count8 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count8", + timestamp: time, + tagValue: String(count8), + }); + break; + } + case 2: + case 4: + case 6: + case 7: + case 8: + case 46: + case 56: + case 60: + case 61: + case 64: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 92: + case 97: + case 98: + case 99: + default: + break; + } + } + // Return result + return result; +} + +function ToTagoFormat(object_item, serie) { + const historics = []; + const events = []; + const realTimes = []; + // eslint-disable-next-line guard-for-in + for (const key in object_item.realTimes) { + realTimes.push({ + variable: `realTimes_${object_item.realTimes[key].tagRef}`.toLowerCase(), + value: object_item.realTimes[key].tagValue, + time: object_item.realTimes[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.events) { + events.push({ + variable: `events_${object_item.events[key].tagRef}`.toLowerCase(), + value: object_item.events[key].tagValue, + time: object_item.events[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.historics) { + historics.push({ + variable: `historics_${object_item.historics[key].tagRef}`.toLowerCase(), + value: object_item.historics[key].tagValue, + time: object_item.historics[key].timestamp, + serie, + }); + } + const result = historics.concat(events, realTimes); + + return result; +} + +/* let payload = [ + { variable: "payload", value: "5e7f000000003f00000040000000410000004200000043000000440000004500000046" }, + { variable: "type", value: 0 }, + { variable: "timestamp", value: 1605614318410 }, +]; */ + +const data = payload.find((x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data"); +const type = payload.find((x) => x.variable === "type"); +const timestamp = payload.find((x) => x.variable === "timestamp"); + +if (data) { + // const buffer = Buffer.from(data.value, "hex"); + const serie = new Date().getTime(); + // payload = decodeStream(data.value, type.value, timestamp.value); + payload = ToTagoFormat(decodeStream(data.value, type.value, timestamp.value), serie); +} + +// eslint-disable-next-line no-console +// console.log(payload); diff --git a/decoders/connector/atim/th-o/assets/logo.png b/decoders/connector/atim/th-o/assets/logo.png new file mode 100644 index 00000000..7998f7c6 Binary files /dev/null and b/decoders/connector/atim/th-o/assets/logo.png differ diff --git a/decoders/connector/atim/th-o/connector.jsonc b/decoders/connector/atim/th-o/connector.jsonc new file mode 100644 index 00000000..1eb5ab8b --- /dev/null +++ b/decoders/connector/atim/th-o/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Atim TH-O", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/atim/th-o/description.md b/decoders/connector/atim/th-o/description.md new file mode 100644 index 00000000..de44166b --- /dev/null +++ b/decoders/connector/atim/th-o/description.md @@ -0,0 +1 @@ +Outdoor Temperature and Humidity sensor over LoRaWAN or Sigfox \ No newline at end of file diff --git a/decoders/connector/atim/th-o/v1.0.0/payload-config.jsonc b/decoders/connector/atim/th-o/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..cac01a93 --- /dev/null +++ b/decoders/connector/atim/th-o/v1.0.0/payload-config.jsonc @@ -0,0 +1,26 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "The ACW-TH-O monitors and transmits the outdoor temperature and humidity of all types of buildings (industrial, tertiary, hospital, etc.).\n\n**Technical Data**\n* Dimensions: 100 x 100 x 35 mm\n* Antenna: Integrated (¼ wave)\n* Temperature: -20°C to +55°C (operation), -40°C to +70°C (storage)\n* Mounts to: Wall\n* Power supply: 1 battery-pack 3,6V / 7,2 mAh\n* Weight: 200g\n* Frequency: 865-870 MHz\n* Power: 25 mW (14 dBm)\n* Transfer rate: Sigfox 100 bit/s, LoRaWAN 300 bit/s to 10 kbit/s", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/sigfox/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/atim/th-o/v1.0.0/payload.js b/decoders/connector/atim/th-o/v1.0.0/payload.js new file mode 100644 index 00000000..58709750 --- /dev/null +++ b/decoders/connector/atim/th-o/v1.0.0/payload.js @@ -0,0 +1,1332 @@ +/* eslint-disable no-plusplus */ +/* eslint-disable no-bitwise */ +function decodeStream(payload, type, timestamp) { + // Init result + const result = { historics: [], events: [], realTimes: [] }; + + // Parse stream + // const jsonStream = JSON.parse(stream); + + // Get time and payload + // const time = new Date(jsonStream.data.timestamp * 1000) / 1000; + const time = new Date(timestamp * 1000) / 1000; + // const { payload } = jsonStream.data; + + // Save network informations + result.realTimes.push({ tagRef: "sys_data_payload", timestamp: time, tagValue: String(payload) }); + result.realTimes.push({ tagRef: "sys_data_timestamp", timestamp: time, tagValue: String(time) }); + /* if (jsonStream.data !== undefined) { + result.realTimes.push({ tagRef: "sys_data_type", timestamp: time, tagValue: String(jsonStream.data.type) }); + result.realTimes.push({ tagRef: "sys_data_raw", timestamp: time, tagValue: String(jsonStream.data.raw) }); + } + if (jsonStream.metadata !== undefined) { + result.realTimes.push({ tagRef: "sys_metadata_lastStreamTimestampUtc", timestamp: time, tagValue: String(jsonStream.metadata.lastStreamTimestampUtc) }); + if (jsonStream.metadata.device !== undefined) { + result.realTimes.push({ tagRef: "sys_device_sn", timestamp: time, tagValue: String(jsonStream.metadata.device.serialNumber) }); + result.realTimes.push({ tagRef: "sys_device_name", timestamp: time, tagValue: String(jsonStream.metadata.device.name) }); + } + if (jsonStream.metadata.endpoint !== undefined) { + result.realTimes.push({ tagRef: "sys_endpoint_name", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.name) }); + result.realTimes.push({ tagRef: "sys_endpoint_type", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.type) }); + } + if (jsonStream.metadata.network !== undefined) { + result.realTimes.push({ tagRef: "sys_network_port", timestamp: time, tagValue: String(jsonStream.metadata.network.port) }); + result.realTimes.push({ tagRef: "sys_network_linkQuality", timestamp: time, tagValue: String(jsonStream.metadata.network.linkQuality) }); + } + if (jsonStream.metadata.location !== undefined) { + result.realTimes.push({ tagRef: "sys_location_source", timestamp: time, tagValue: String(jsonStream.metadata.location.source) }); + result.realTimes.push({ tagRef: "sys_location_latitude", timestamp: time, tagValue: String(jsonStream.metadata.location.latitude) }); + result.realTimes.push({ tagRef: "sys_location_longitude", timestamp: time, tagValue: String(jsonStream.metadata.location.longitude) }); + result.realTimes.push({ tagRef: "sys_location_accuracy", timestamp: time, tagValue: String(jsonStream.metadata.location.accuracy) }); + } + } */ + if (type !== 3) { + // if KHEIRON + /* if (jsonStream.metadata.endpoint.type === 0) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).fcnt) }); + } + // if SIGFOX + if (jsonStream.metadata.endpoint.type === 5) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).seqNumber) }); + } + // if OBJENIOUS + if (jsonStream.metadata.endpoint.type === 6) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).count) }); + result.realTimes.push({ tagRef: "custom_data_raw_devEUI", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).device_properties.deveui) }); + } + */ + // Get message ID + const msgStr = parseInt(payload.substring(0, 2), 16); + + // Switch message ids + switch (msgStr) { + case 1: { + // Get meter + const battery = parseInt(payload.substring(2, 6), 16); + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery / 1000), + }); + + break; + } + case 3: { + // Get data + const battery = parseInt(payload.substring(2, 4), 16); + let temperature = parseInt(payload.substring(4, 8), 16); + const humidity = parseInt(payload.substring(8, 12), 16); + + // check if negative value + if (temperature >> 15 === 1) { + const reverseValue = temperature ^ 65535; + temperature = (reverseValue + 1) * -1; + } + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temperature / 100), + }); + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity / 100), + }); + break; + } + case 5: { + const testCounter = parseInt(payload.substring(2, 4), 16); + + result.realTimes.push({ + tagRef: "p_test", + timestamp: time, + tagValue: String(testCounter), + }); + + break; + } + case 9: { + // Get input + const di = parseInt(payload.substring(4, 6), 16); + const di1 = (di & 1) === 1; + const di2 = (di & 2) === 2; + + // get ouput + const dout = parseInt(payload.substring(2, 4), 16); + const do1 = (dout & 1) === 1; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DO1", + timestamp: time, + tagValue: String(do1), + }); + + break; + } + case 10: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + break; + } + case 11: { + break; + } + case 12: { + break; + } + case 13: { + break; + } + case 14: { + break; + } + case 21: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // Decode + temp = (temp - 33184) / 128; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + break; + } + case 20: { + // Get meter + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 48: { + // get realtime meter + const meter = parseInt(payload.substring(42, 50), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + + // get historic meter + const meter1 = parseInt(payload.substring(34, 42), 16); + const meter2 = parseInt(payload.substring(26, 34), 16); + const meter3 = parseInt(payload.substring(18, 26), 16); + const meter4 = parseInt(payload.substring(10, 18), 16); + const meter5 = parseInt(payload.substring(2, 10), 16); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 1) / 1000, + tagValue: String(meter1), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 2) / 1000, + tagValue: String(meter2), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 3) / 1000, + tagValue: String(meter3), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 4) / 1000, + tagValue: String(meter4), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 5) / 1000, + tagValue: String(meter5), + }); + + break; + } + case 49: { + // get realtime meter + const meter = parseInt(payload.substring(2, 10), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + break; + } + case 55: { + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + break; + } + case 57: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // const a = new Date(time * 1000); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (600000 + i * 600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 58: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (1800000 + i * 1800000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 59: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (3600000 + i * 3600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 22: { + // Get meter + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(4, 6), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 15: { + break; + } + case 16: { + break; + } + case 17: { + break; + } + case 18: { + break; + } + case 23: { + // Get temperature & humidity + let temp = parseInt(payload.substring(2, 6), 16); + let humidity = parseInt(payload.substring(6, 10), 16); + + // Decode + temp = (temp * 175.72) / 65536 - 46.85; + humidity = (humidity * 125) / 65536 - 6; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity), + }); + break; + } + case 24: { + break; + } + case 30: { + break; + } + case 31: { + break; + } + case 32: { + break; + } + case 33: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 10 / 64240; + + result.realTimes.push({ + tagRef: "p_voltage", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 25: { + break; + } + case 34: { + break; + } + case 35: { + break; + } + case 36: { + break; + } + case 37: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 16 / 47584; + result.realTimes.push({ + tagRef: "p_current", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 27: { + break; + } + case 42: { + break; + } + case 43: { + break; + } + case 44: { + break; + } + case 45: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + break; + } + case 26: { + break; + } + case 38: { + break; + } + case 39: { + break; + } + case 40: { + break; + } + case 41: { + break; + } + case 83: { + break; + } + case 84: { + break; + } + case 85: { + break; + } + case 86: { + break; + } + case 87: { + break; + } + case 88: { + break; + } + case 89: { + break; + } + case 90: { + break; + } + case 91: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + let temp2 = parseInt(payload.substring(6, 10), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + + // check if error + if (temp2 !== 32768) { + // check if negative value + if (temp2 >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp2 = (reverseValue + 1) * -1; + } + + // apply coef + temp2 *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature2", + timestamp: time, + tagValue: String(temp2), + }); + } + + break; + } + case 19: { + // Get water leak state + const waterleak = parseInt(payload.substring(4, 6), 16); + + // save + result.realTimes.push({ + tagRef: "p_waterLeak", + timestamp: time, + tagValue: String(waterleak), + }); + break; + } + case 47: { + // get temperature + const temp = parseInt(payload.substring(14, 18), 16); + const p_temperature = (10.888 - Math.sqrt((-10.888) ** 2 + 4 * 0.00347 * (1777.3 - temp))) / (2 * -0.00347) + 30; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + + if (parseInt(payload.substring(18, 34), 16) !== 0) { + // get data for gps decoding + const a = parseInt(payload.substring(18, 20), 16) >> 4; + const b = ((parseInt(payload.substring(18, 20), 16) << 4) & 255) >> 4; + const c = parseInt(payload.substring(20, 22), 16) >> 4; + const d = ((parseInt(payload.substring(20, 22), 16) << 4) & 255) >> 4; + const e = parseInt(payload.substring(22, 24), 16) >> 4; + const f = ((parseInt(payload.substring(22, 24), 16) << 4) & 255) >> 4; + const g = parseInt(payload.substring(24, 26), 16) >> 4; + const h = ((parseInt(payload.substring(24, 26), 16) << 4) & 255) >> 4; + const i = parseInt(payload.substring(26, 28), 16) >> 4; + const j = ((parseInt(payload.substring(26, 28), 16) << 4) & 255) >> 4; + const k = parseInt(payload.substring(28, 30), 16) >> 4; + const l = ((parseInt(payload.substring(28, 30), 16) << 4) & 255) >> 4; + const m = parseInt(payload.substring(30, 32), 16) >> 4; + const n = ((parseInt(payload.substring(30, 32), 16) << 4) & 255) >> 4; + const o = parseInt(payload.substring(32, 34), 16) >> 4; + // const p = ((parseInt(payload.substring(32, 34), 16) << 4) & 255) >> 6; + const q = ((parseInt(payload.substring(32, 34), 16) << 6) & 255) >> 7; + const r = ((parseInt(payload.substring(32, 34), 16) << 7) & 255) >> 7; + // get latitude and longitude + const latitude = (2 * q - 1) * (10 * a + b + c / 6 + d / 60 + e / 600 + f / 6000 + g / 60000); + const longitude = (2 * r - 1) * (100 * h + 10 * i + j + k / 6 + l / 60 + m / 600 + n / 6000 + o / 60000); + + // save + result.realTimes.push({ + tagRef: "p_latitude", + timestamp: time, + tagValue: String(latitude), + }); + result.realTimes.push({ + tagRef: "p_longitude", + timestamp: time, + tagValue: String(longitude), + }); + } + + break; + } + case 50: { + const p_vibration = parseInt(payload.substring(4, 6), 16); + const p_ils = parseInt(payload.substring(6, 8), 16); + const p_temperature = (2103 - parseInt(payload.substring(8, 12), 16)) / 10.9; + + // save + result.realTimes.push({ + tagRef: "p_vibration", + timestamp: time, + tagValue: String(p_vibration), + }); + result.realTimes.push({ + tagRef: "p_ils", + timestamp: time, + tagValue: String(p_ils), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + break; + } + case 51: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + break; + } + case 52: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get count + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(parseInt(payload.substring(4, 12), 16)), + }); + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(parseInt(payload.substring(12, 20), 16)), + }); + break; + } + case 53: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 54: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 62: { + // Get Absence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(0), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + const valueWorZ = parseInt(payload.substring(10, 14), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + + // Save WorZ + result.realTimes.push({ + tagRef: "p_WorZ", + timestamp: time, + tagValue: String(valueWorZ), + }); + break; + } + case 63: { + // Get Presence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(1), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + break; + } + case 65: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + break; + } + case 66: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + break; + } + case 67: { + // save + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time - 1, + tagValue: String(1), + context: [], + }); + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time, + tagValue: String(0), + context: [], + }); + break; + } + case 78: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + // get count 1 + const count1 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 79: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(14, 22), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 80: { + // get count 1 + const count1 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 81: { + // get count 3 + const count3 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count3", + timestamp: time, + tagValue: String(count3), + }); + + // get count 4 + const count4 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count4", + timestamp: time, + tagValue: String(count4), + }); + break; + } + case 82: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 93: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(2 + 8 * j, 10 + 8 * j), 16)), + }); + } + break; + } + case 94: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(-2 + 8 * j, 6 + 8 * j), 16)), + }); + } + break; + } + case 95: { + // get count 5 + const count5 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count5", + timestamp: time, + tagValue: String(count5), + }); + + // get count 6 + const count6 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count6", + timestamp: time, + tagValue: String(count6), + }); + break; + } + case 96: { + // get count 7 + const count7 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count7", + timestamp: time, + tagValue: String(count7), + }); + + // get count 8 + const count8 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count8", + timestamp: time, + tagValue: String(count8), + }); + break; + } + case 2: + case 4: + case 6: + case 7: + case 8: + case 46: + case 56: + case 60: + case 61: + case 64: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 92: + case 97: + case 98: + case 99: + default: + break; + } + } + // Return result + return result; +} + +function ToTagoFormat(object_item, serie) { + const historics = []; + const events = []; + const realTimes = []; + // eslint-disable-next-line guard-for-in + for (const key in object_item.realTimes) { + realTimes.push({ + variable: `realTimes_${object_item.realTimes[key].tagRef}`.toLowerCase(), + value: object_item.realTimes[key].tagValue, + time: object_item.realTimes[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.events) { + events.push({ + variable: `events_${object_item.events[key].tagRef}`.toLowerCase(), + value: object_item.events[key].tagValue, + time: object_item.events[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.historics) { + historics.push({ + variable: `historics_${object_item.historics[key].tagRef}`.toLowerCase(), + value: object_item.historics[key].tagValue, + time: object_item.historics[key].timestamp, + serie, + }); + } + const result = historics.concat(events, realTimes); + + return result; +} + +/* let payload = [ + { variable: "payload", value: "5e7f000000003f00000040000000410000004200000043000000440000004500000046" }, + { variable: "type", value: 0 }, + { variable: "timestamp", value: 1605614318410 }, +]; */ + +const data = payload.find((x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data"); +const type = payload.find((x) => x.variable === "type"); +const timestamp = payload.find((x) => x.variable === "timestamp"); + +if (data) { + // const buffer = Buffer.from(data.value, "hex"); + const serie = new Date().getTime(); + // payload = decodeStream(data.value, type.value, timestamp.value); + payload = ToTagoFormat(decodeStream(data.value, type.value, timestamp.value), serie); +} + +// eslint-disable-next-line no-console +// console.log(payload); diff --git a/decoders/connector/atim/ti-g/assets/logo.png b/decoders/connector/atim/ti-g/assets/logo.png new file mode 100644 index 00000000..919818a6 Binary files /dev/null and b/decoders/connector/atim/ti-g/assets/logo.png differ diff --git a/decoders/connector/atim/ti-g/connector.jsonc b/decoders/connector/atim/ti-g/connector.jsonc new file mode 100644 index 00000000..76b78c27 --- /dev/null +++ b/decoders/connector/atim/ti-g/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Atim TI-G", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/atim/ti-g/description.md b/decoders/connector/atim/ti-g/description.md new file mode 100644 index 00000000..b6e5c113 --- /dev/null +++ b/decoders/connector/atim/ti-g/description.md @@ -0,0 +1 @@ +Indoor Temperature + GPS sensor over LoRaWAN \ No newline at end of file diff --git a/decoders/connector/atim/ti-g/v1.0.0/payload-config.jsonc b/decoders/connector/atim/ti-g/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..5a8362b1 --- /dev/null +++ b/decoders/connector/atim/ti-g/v1.0.0/payload-config.jsonc @@ -0,0 +1,25 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "The ACW-TI-G is a compact device measuring the temperature of fixed or mobile equipment and geo-locate precisely. The data is transmitted periodically in normal operation (for example 1 time / day). The GPS only wakes up if the equipment has moved since the last transmission, which guarantees autonomy for several years. It is ideally suited for monitoring moveable mobile electrical cabinets, or any equipment to be monitored. The primary purpose of this Industrial Internet of Things (IIoT) product is to prevent abnormal heating of equipment and to locate it when needed. \n\n**Technical Data**\n* Dimensions: 80 x 80 x 35 mm\n* Antenna: Integrated (¼ wave), Integrated Ant. GPS/GNSS/GALILEO\n* Temperature: -20°C to +55°C (operation), -40°C to +70°C (storage)\n* Precision: +/-0.5°C\n* Housing: IP20\n* Power supply: 2x AA lithium batteries\n* Weight: 100g\n* Frequency: 865-870 MHz\n* Power: 25 mW (14 dBm)\n* GPS sensitivity: -148dBm (cold start), -163dBm (hot start)", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/atim/ti-g/v1.0.0/payload.js b/decoders/connector/atim/ti-g/v1.0.0/payload.js new file mode 100644 index 00000000..58709750 --- /dev/null +++ b/decoders/connector/atim/ti-g/v1.0.0/payload.js @@ -0,0 +1,1332 @@ +/* eslint-disable no-plusplus */ +/* eslint-disable no-bitwise */ +function decodeStream(payload, type, timestamp) { + // Init result + const result = { historics: [], events: [], realTimes: [] }; + + // Parse stream + // const jsonStream = JSON.parse(stream); + + // Get time and payload + // const time = new Date(jsonStream.data.timestamp * 1000) / 1000; + const time = new Date(timestamp * 1000) / 1000; + // const { payload } = jsonStream.data; + + // Save network informations + result.realTimes.push({ tagRef: "sys_data_payload", timestamp: time, tagValue: String(payload) }); + result.realTimes.push({ tagRef: "sys_data_timestamp", timestamp: time, tagValue: String(time) }); + /* if (jsonStream.data !== undefined) { + result.realTimes.push({ tagRef: "sys_data_type", timestamp: time, tagValue: String(jsonStream.data.type) }); + result.realTimes.push({ tagRef: "sys_data_raw", timestamp: time, tagValue: String(jsonStream.data.raw) }); + } + if (jsonStream.metadata !== undefined) { + result.realTimes.push({ tagRef: "sys_metadata_lastStreamTimestampUtc", timestamp: time, tagValue: String(jsonStream.metadata.lastStreamTimestampUtc) }); + if (jsonStream.metadata.device !== undefined) { + result.realTimes.push({ tagRef: "sys_device_sn", timestamp: time, tagValue: String(jsonStream.metadata.device.serialNumber) }); + result.realTimes.push({ tagRef: "sys_device_name", timestamp: time, tagValue: String(jsonStream.metadata.device.name) }); + } + if (jsonStream.metadata.endpoint !== undefined) { + result.realTimes.push({ tagRef: "sys_endpoint_name", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.name) }); + result.realTimes.push({ tagRef: "sys_endpoint_type", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.type) }); + } + if (jsonStream.metadata.network !== undefined) { + result.realTimes.push({ tagRef: "sys_network_port", timestamp: time, tagValue: String(jsonStream.metadata.network.port) }); + result.realTimes.push({ tagRef: "sys_network_linkQuality", timestamp: time, tagValue: String(jsonStream.metadata.network.linkQuality) }); + } + if (jsonStream.metadata.location !== undefined) { + result.realTimes.push({ tagRef: "sys_location_source", timestamp: time, tagValue: String(jsonStream.metadata.location.source) }); + result.realTimes.push({ tagRef: "sys_location_latitude", timestamp: time, tagValue: String(jsonStream.metadata.location.latitude) }); + result.realTimes.push({ tagRef: "sys_location_longitude", timestamp: time, tagValue: String(jsonStream.metadata.location.longitude) }); + result.realTimes.push({ tagRef: "sys_location_accuracy", timestamp: time, tagValue: String(jsonStream.metadata.location.accuracy) }); + } + } */ + if (type !== 3) { + // if KHEIRON + /* if (jsonStream.metadata.endpoint.type === 0) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).fcnt) }); + } + // if SIGFOX + if (jsonStream.metadata.endpoint.type === 5) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).seqNumber) }); + } + // if OBJENIOUS + if (jsonStream.metadata.endpoint.type === 6) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).count) }); + result.realTimes.push({ tagRef: "custom_data_raw_devEUI", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).device_properties.deveui) }); + } + */ + // Get message ID + const msgStr = parseInt(payload.substring(0, 2), 16); + + // Switch message ids + switch (msgStr) { + case 1: { + // Get meter + const battery = parseInt(payload.substring(2, 6), 16); + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery / 1000), + }); + + break; + } + case 3: { + // Get data + const battery = parseInt(payload.substring(2, 4), 16); + let temperature = parseInt(payload.substring(4, 8), 16); + const humidity = parseInt(payload.substring(8, 12), 16); + + // check if negative value + if (temperature >> 15 === 1) { + const reverseValue = temperature ^ 65535; + temperature = (reverseValue + 1) * -1; + } + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temperature / 100), + }); + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity / 100), + }); + break; + } + case 5: { + const testCounter = parseInt(payload.substring(2, 4), 16); + + result.realTimes.push({ + tagRef: "p_test", + timestamp: time, + tagValue: String(testCounter), + }); + + break; + } + case 9: { + // Get input + const di = parseInt(payload.substring(4, 6), 16); + const di1 = (di & 1) === 1; + const di2 = (di & 2) === 2; + + // get ouput + const dout = parseInt(payload.substring(2, 4), 16); + const do1 = (dout & 1) === 1; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DO1", + timestamp: time, + tagValue: String(do1), + }); + + break; + } + case 10: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + break; + } + case 11: { + break; + } + case 12: { + break; + } + case 13: { + break; + } + case 14: { + break; + } + case 21: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // Decode + temp = (temp - 33184) / 128; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + break; + } + case 20: { + // Get meter + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 48: { + // get realtime meter + const meter = parseInt(payload.substring(42, 50), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + + // get historic meter + const meter1 = parseInt(payload.substring(34, 42), 16); + const meter2 = parseInt(payload.substring(26, 34), 16); + const meter3 = parseInt(payload.substring(18, 26), 16); + const meter4 = parseInt(payload.substring(10, 18), 16); + const meter5 = parseInt(payload.substring(2, 10), 16); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 1) / 1000, + tagValue: String(meter1), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 2) / 1000, + tagValue: String(meter2), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 3) / 1000, + tagValue: String(meter3), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 4) / 1000, + tagValue: String(meter4), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 5) / 1000, + tagValue: String(meter5), + }); + + break; + } + case 49: { + // get realtime meter + const meter = parseInt(payload.substring(2, 10), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + break; + } + case 55: { + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + break; + } + case 57: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // const a = new Date(time * 1000); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (600000 + i * 600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 58: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (1800000 + i * 1800000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 59: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (3600000 + i * 3600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 22: { + // Get meter + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(4, 6), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 15: { + break; + } + case 16: { + break; + } + case 17: { + break; + } + case 18: { + break; + } + case 23: { + // Get temperature & humidity + let temp = parseInt(payload.substring(2, 6), 16); + let humidity = parseInt(payload.substring(6, 10), 16); + + // Decode + temp = (temp * 175.72) / 65536 - 46.85; + humidity = (humidity * 125) / 65536 - 6; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity), + }); + break; + } + case 24: { + break; + } + case 30: { + break; + } + case 31: { + break; + } + case 32: { + break; + } + case 33: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 10 / 64240; + + result.realTimes.push({ + tagRef: "p_voltage", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 25: { + break; + } + case 34: { + break; + } + case 35: { + break; + } + case 36: { + break; + } + case 37: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 16 / 47584; + result.realTimes.push({ + tagRef: "p_current", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 27: { + break; + } + case 42: { + break; + } + case 43: { + break; + } + case 44: { + break; + } + case 45: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + break; + } + case 26: { + break; + } + case 38: { + break; + } + case 39: { + break; + } + case 40: { + break; + } + case 41: { + break; + } + case 83: { + break; + } + case 84: { + break; + } + case 85: { + break; + } + case 86: { + break; + } + case 87: { + break; + } + case 88: { + break; + } + case 89: { + break; + } + case 90: { + break; + } + case 91: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + let temp2 = parseInt(payload.substring(6, 10), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + + // check if error + if (temp2 !== 32768) { + // check if negative value + if (temp2 >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp2 = (reverseValue + 1) * -1; + } + + // apply coef + temp2 *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature2", + timestamp: time, + tagValue: String(temp2), + }); + } + + break; + } + case 19: { + // Get water leak state + const waterleak = parseInt(payload.substring(4, 6), 16); + + // save + result.realTimes.push({ + tagRef: "p_waterLeak", + timestamp: time, + tagValue: String(waterleak), + }); + break; + } + case 47: { + // get temperature + const temp = parseInt(payload.substring(14, 18), 16); + const p_temperature = (10.888 - Math.sqrt((-10.888) ** 2 + 4 * 0.00347 * (1777.3 - temp))) / (2 * -0.00347) + 30; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + + if (parseInt(payload.substring(18, 34), 16) !== 0) { + // get data for gps decoding + const a = parseInt(payload.substring(18, 20), 16) >> 4; + const b = ((parseInt(payload.substring(18, 20), 16) << 4) & 255) >> 4; + const c = parseInt(payload.substring(20, 22), 16) >> 4; + const d = ((parseInt(payload.substring(20, 22), 16) << 4) & 255) >> 4; + const e = parseInt(payload.substring(22, 24), 16) >> 4; + const f = ((parseInt(payload.substring(22, 24), 16) << 4) & 255) >> 4; + const g = parseInt(payload.substring(24, 26), 16) >> 4; + const h = ((parseInt(payload.substring(24, 26), 16) << 4) & 255) >> 4; + const i = parseInt(payload.substring(26, 28), 16) >> 4; + const j = ((parseInt(payload.substring(26, 28), 16) << 4) & 255) >> 4; + const k = parseInt(payload.substring(28, 30), 16) >> 4; + const l = ((parseInt(payload.substring(28, 30), 16) << 4) & 255) >> 4; + const m = parseInt(payload.substring(30, 32), 16) >> 4; + const n = ((parseInt(payload.substring(30, 32), 16) << 4) & 255) >> 4; + const o = parseInt(payload.substring(32, 34), 16) >> 4; + // const p = ((parseInt(payload.substring(32, 34), 16) << 4) & 255) >> 6; + const q = ((parseInt(payload.substring(32, 34), 16) << 6) & 255) >> 7; + const r = ((parseInt(payload.substring(32, 34), 16) << 7) & 255) >> 7; + // get latitude and longitude + const latitude = (2 * q - 1) * (10 * a + b + c / 6 + d / 60 + e / 600 + f / 6000 + g / 60000); + const longitude = (2 * r - 1) * (100 * h + 10 * i + j + k / 6 + l / 60 + m / 600 + n / 6000 + o / 60000); + + // save + result.realTimes.push({ + tagRef: "p_latitude", + timestamp: time, + tagValue: String(latitude), + }); + result.realTimes.push({ + tagRef: "p_longitude", + timestamp: time, + tagValue: String(longitude), + }); + } + + break; + } + case 50: { + const p_vibration = parseInt(payload.substring(4, 6), 16); + const p_ils = parseInt(payload.substring(6, 8), 16); + const p_temperature = (2103 - parseInt(payload.substring(8, 12), 16)) / 10.9; + + // save + result.realTimes.push({ + tagRef: "p_vibration", + timestamp: time, + tagValue: String(p_vibration), + }); + result.realTimes.push({ + tagRef: "p_ils", + timestamp: time, + tagValue: String(p_ils), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + break; + } + case 51: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + break; + } + case 52: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get count + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(parseInt(payload.substring(4, 12), 16)), + }); + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(parseInt(payload.substring(12, 20), 16)), + }); + break; + } + case 53: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 54: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 62: { + // Get Absence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(0), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + const valueWorZ = parseInt(payload.substring(10, 14), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + + // Save WorZ + result.realTimes.push({ + tagRef: "p_WorZ", + timestamp: time, + tagValue: String(valueWorZ), + }); + break; + } + case 63: { + // Get Presence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(1), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + break; + } + case 65: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + break; + } + case 66: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + break; + } + case 67: { + // save + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time - 1, + tagValue: String(1), + context: [], + }); + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time, + tagValue: String(0), + context: [], + }); + break; + } + case 78: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + // get count 1 + const count1 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 79: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(14, 22), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 80: { + // get count 1 + const count1 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 81: { + // get count 3 + const count3 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count3", + timestamp: time, + tagValue: String(count3), + }); + + // get count 4 + const count4 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count4", + timestamp: time, + tagValue: String(count4), + }); + break; + } + case 82: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 93: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(2 + 8 * j, 10 + 8 * j), 16)), + }); + } + break; + } + case 94: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(-2 + 8 * j, 6 + 8 * j), 16)), + }); + } + break; + } + case 95: { + // get count 5 + const count5 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count5", + timestamp: time, + tagValue: String(count5), + }); + + // get count 6 + const count6 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count6", + timestamp: time, + tagValue: String(count6), + }); + break; + } + case 96: { + // get count 7 + const count7 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count7", + timestamp: time, + tagValue: String(count7), + }); + + // get count 8 + const count8 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count8", + timestamp: time, + tagValue: String(count8), + }); + break; + } + case 2: + case 4: + case 6: + case 7: + case 8: + case 46: + case 56: + case 60: + case 61: + case 64: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 92: + case 97: + case 98: + case 99: + default: + break; + } + } + // Return result + return result; +} + +function ToTagoFormat(object_item, serie) { + const historics = []; + const events = []; + const realTimes = []; + // eslint-disable-next-line guard-for-in + for (const key in object_item.realTimes) { + realTimes.push({ + variable: `realTimes_${object_item.realTimes[key].tagRef}`.toLowerCase(), + value: object_item.realTimes[key].tagValue, + time: object_item.realTimes[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.events) { + events.push({ + variable: `events_${object_item.events[key].tagRef}`.toLowerCase(), + value: object_item.events[key].tagValue, + time: object_item.events[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.historics) { + historics.push({ + variable: `historics_${object_item.historics[key].tagRef}`.toLowerCase(), + value: object_item.historics[key].tagValue, + time: object_item.historics[key].timestamp, + serie, + }); + } + const result = historics.concat(events, realTimes); + + return result; +} + +/* let payload = [ + { variable: "payload", value: "5e7f000000003f00000040000000410000004200000043000000440000004500000046" }, + { variable: "type", value: 0 }, + { variable: "timestamp", value: 1605614318410 }, +]; */ + +const data = payload.find((x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data"); +const type = payload.find((x) => x.variable === "type"); +const timestamp = payload.find((x) => x.variable === "timestamp"); + +if (data) { + // const buffer = Buffer.from(data.value, "hex"); + const serie = new Date().getTime(); + // payload = decodeStream(data.value, type.value, timestamp.value); + payload = ToTagoFormat(decodeStream(data.value, type.value, timestamp.value), serie); +} + +// eslint-disable-next-line no-console +// console.log(payload); diff --git a/decoders/connector/atim/tm1d-hp/assets/logo.png b/decoders/connector/atim/tm1d-hp/assets/logo.png new file mode 100644 index 00000000..02e5783a Binary files /dev/null and b/decoders/connector/atim/tm1d-hp/assets/logo.png differ diff --git a/decoders/connector/atim/tm1d-hp/connector.jsonc b/decoders/connector/atim/tm1d-hp/connector.jsonc new file mode 100644 index 00000000..d917ae3a --- /dev/null +++ b/decoders/connector/atim/tm1d-hp/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Atim TM1D-HP", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/atim/tm1d-hp/description.md b/decoders/connector/atim/tm1d-hp/description.md new file mode 100644 index 00000000..b11dc5c2 --- /dev/null +++ b/decoders/connector/atim/tm1d-hp/description.md @@ -0,0 +1 @@ +Temperature sensor over LoRaWAN or Sigfox \ No newline at end of file diff --git a/decoders/connector/atim/tm1d-hp/v1.0.0/payload-config.jsonc b/decoders/connector/atim/tm1d-hp/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..22a328b6 --- /dev/null +++ b/decoders/connector/atim/tm1d-hp/v1.0.0/payload-config.jsonc @@ -0,0 +1,26 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "The ACW-TM1D-HP monitors and transmits one remote temperature. The sensor has 1 external digital probe. Data transmission at regular intervals configurable as required through the ACW configurator. Compatible with Sigfox repeater (ACW-GW).\n\n**Technical Data**\n* Dimensions: 160 x 53 x 53 mm\n* Antenna: Integrated (¼ wave)\n* Temperature probe: Digital - Cable length: 2 m\n* Temperature: -20°C to +55°C (operation), -40°C to +70°C (storage)\n* Mounts to: Wall, tube or pole, DIN rail\n* Housing: IP65\n* Power supply: 2x battery-packs (14,4 Ah)\n* Weight: 210g\n* Frequency: 865-870 MHz\n* Power: 25 mW (14 dBm)\n* Transfer rate: Sigfox: 100 bit/s, LoRaWAN: 300 bit/s to 10 kbit/s\n\n**Temperature Sensor**\n* Range: -55°C to +125°C\n* Resolution: 0.5°C (9 bits)\n* Precision from -55°C to -10°C: +/- 2°C\n* Precision from -10°C to +85°C: +/- 0.5°C\n* Precision from +85°C to +125°C: +/- 2°C", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/sigfox/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/atim/tm1d-hp/v1.0.0/payload.js b/decoders/connector/atim/tm1d-hp/v1.0.0/payload.js new file mode 100644 index 00000000..58709750 --- /dev/null +++ b/decoders/connector/atim/tm1d-hp/v1.0.0/payload.js @@ -0,0 +1,1332 @@ +/* eslint-disable no-plusplus */ +/* eslint-disable no-bitwise */ +function decodeStream(payload, type, timestamp) { + // Init result + const result = { historics: [], events: [], realTimes: [] }; + + // Parse stream + // const jsonStream = JSON.parse(stream); + + // Get time and payload + // const time = new Date(jsonStream.data.timestamp * 1000) / 1000; + const time = new Date(timestamp * 1000) / 1000; + // const { payload } = jsonStream.data; + + // Save network informations + result.realTimes.push({ tagRef: "sys_data_payload", timestamp: time, tagValue: String(payload) }); + result.realTimes.push({ tagRef: "sys_data_timestamp", timestamp: time, tagValue: String(time) }); + /* if (jsonStream.data !== undefined) { + result.realTimes.push({ tagRef: "sys_data_type", timestamp: time, tagValue: String(jsonStream.data.type) }); + result.realTimes.push({ tagRef: "sys_data_raw", timestamp: time, tagValue: String(jsonStream.data.raw) }); + } + if (jsonStream.metadata !== undefined) { + result.realTimes.push({ tagRef: "sys_metadata_lastStreamTimestampUtc", timestamp: time, tagValue: String(jsonStream.metadata.lastStreamTimestampUtc) }); + if (jsonStream.metadata.device !== undefined) { + result.realTimes.push({ tagRef: "sys_device_sn", timestamp: time, tagValue: String(jsonStream.metadata.device.serialNumber) }); + result.realTimes.push({ tagRef: "sys_device_name", timestamp: time, tagValue: String(jsonStream.metadata.device.name) }); + } + if (jsonStream.metadata.endpoint !== undefined) { + result.realTimes.push({ tagRef: "sys_endpoint_name", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.name) }); + result.realTimes.push({ tagRef: "sys_endpoint_type", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.type) }); + } + if (jsonStream.metadata.network !== undefined) { + result.realTimes.push({ tagRef: "sys_network_port", timestamp: time, tagValue: String(jsonStream.metadata.network.port) }); + result.realTimes.push({ tagRef: "sys_network_linkQuality", timestamp: time, tagValue: String(jsonStream.metadata.network.linkQuality) }); + } + if (jsonStream.metadata.location !== undefined) { + result.realTimes.push({ tagRef: "sys_location_source", timestamp: time, tagValue: String(jsonStream.metadata.location.source) }); + result.realTimes.push({ tagRef: "sys_location_latitude", timestamp: time, tagValue: String(jsonStream.metadata.location.latitude) }); + result.realTimes.push({ tagRef: "sys_location_longitude", timestamp: time, tagValue: String(jsonStream.metadata.location.longitude) }); + result.realTimes.push({ tagRef: "sys_location_accuracy", timestamp: time, tagValue: String(jsonStream.metadata.location.accuracy) }); + } + } */ + if (type !== 3) { + // if KHEIRON + /* if (jsonStream.metadata.endpoint.type === 0) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).fcnt) }); + } + // if SIGFOX + if (jsonStream.metadata.endpoint.type === 5) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).seqNumber) }); + } + // if OBJENIOUS + if (jsonStream.metadata.endpoint.type === 6) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).count) }); + result.realTimes.push({ tagRef: "custom_data_raw_devEUI", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).device_properties.deveui) }); + } + */ + // Get message ID + const msgStr = parseInt(payload.substring(0, 2), 16); + + // Switch message ids + switch (msgStr) { + case 1: { + // Get meter + const battery = parseInt(payload.substring(2, 6), 16); + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery / 1000), + }); + + break; + } + case 3: { + // Get data + const battery = parseInt(payload.substring(2, 4), 16); + let temperature = parseInt(payload.substring(4, 8), 16); + const humidity = parseInt(payload.substring(8, 12), 16); + + // check if negative value + if (temperature >> 15 === 1) { + const reverseValue = temperature ^ 65535; + temperature = (reverseValue + 1) * -1; + } + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temperature / 100), + }); + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity / 100), + }); + break; + } + case 5: { + const testCounter = parseInt(payload.substring(2, 4), 16); + + result.realTimes.push({ + tagRef: "p_test", + timestamp: time, + tagValue: String(testCounter), + }); + + break; + } + case 9: { + // Get input + const di = parseInt(payload.substring(4, 6), 16); + const di1 = (di & 1) === 1; + const di2 = (di & 2) === 2; + + // get ouput + const dout = parseInt(payload.substring(2, 4), 16); + const do1 = (dout & 1) === 1; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DO1", + timestamp: time, + tagValue: String(do1), + }); + + break; + } + case 10: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + break; + } + case 11: { + break; + } + case 12: { + break; + } + case 13: { + break; + } + case 14: { + break; + } + case 21: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // Decode + temp = (temp - 33184) / 128; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + break; + } + case 20: { + // Get meter + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 48: { + // get realtime meter + const meter = parseInt(payload.substring(42, 50), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + + // get historic meter + const meter1 = parseInt(payload.substring(34, 42), 16); + const meter2 = parseInt(payload.substring(26, 34), 16); + const meter3 = parseInt(payload.substring(18, 26), 16); + const meter4 = parseInt(payload.substring(10, 18), 16); + const meter5 = parseInt(payload.substring(2, 10), 16); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 1) / 1000, + tagValue: String(meter1), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 2) / 1000, + tagValue: String(meter2), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 3) / 1000, + tagValue: String(meter3), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 4) / 1000, + tagValue: String(meter4), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 5) / 1000, + tagValue: String(meter5), + }); + + break; + } + case 49: { + // get realtime meter + const meter = parseInt(payload.substring(2, 10), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + break; + } + case 55: { + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + break; + } + case 57: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // const a = new Date(time * 1000); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (600000 + i * 600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 58: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (1800000 + i * 1800000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 59: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (3600000 + i * 3600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 22: { + // Get meter + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(4, 6), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 15: { + break; + } + case 16: { + break; + } + case 17: { + break; + } + case 18: { + break; + } + case 23: { + // Get temperature & humidity + let temp = parseInt(payload.substring(2, 6), 16); + let humidity = parseInt(payload.substring(6, 10), 16); + + // Decode + temp = (temp * 175.72) / 65536 - 46.85; + humidity = (humidity * 125) / 65536 - 6; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity), + }); + break; + } + case 24: { + break; + } + case 30: { + break; + } + case 31: { + break; + } + case 32: { + break; + } + case 33: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 10 / 64240; + + result.realTimes.push({ + tagRef: "p_voltage", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 25: { + break; + } + case 34: { + break; + } + case 35: { + break; + } + case 36: { + break; + } + case 37: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 16 / 47584; + result.realTimes.push({ + tagRef: "p_current", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 27: { + break; + } + case 42: { + break; + } + case 43: { + break; + } + case 44: { + break; + } + case 45: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + break; + } + case 26: { + break; + } + case 38: { + break; + } + case 39: { + break; + } + case 40: { + break; + } + case 41: { + break; + } + case 83: { + break; + } + case 84: { + break; + } + case 85: { + break; + } + case 86: { + break; + } + case 87: { + break; + } + case 88: { + break; + } + case 89: { + break; + } + case 90: { + break; + } + case 91: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + let temp2 = parseInt(payload.substring(6, 10), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + + // check if error + if (temp2 !== 32768) { + // check if negative value + if (temp2 >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp2 = (reverseValue + 1) * -1; + } + + // apply coef + temp2 *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature2", + timestamp: time, + tagValue: String(temp2), + }); + } + + break; + } + case 19: { + // Get water leak state + const waterleak = parseInt(payload.substring(4, 6), 16); + + // save + result.realTimes.push({ + tagRef: "p_waterLeak", + timestamp: time, + tagValue: String(waterleak), + }); + break; + } + case 47: { + // get temperature + const temp = parseInt(payload.substring(14, 18), 16); + const p_temperature = (10.888 - Math.sqrt((-10.888) ** 2 + 4 * 0.00347 * (1777.3 - temp))) / (2 * -0.00347) + 30; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + + if (parseInt(payload.substring(18, 34), 16) !== 0) { + // get data for gps decoding + const a = parseInt(payload.substring(18, 20), 16) >> 4; + const b = ((parseInt(payload.substring(18, 20), 16) << 4) & 255) >> 4; + const c = parseInt(payload.substring(20, 22), 16) >> 4; + const d = ((parseInt(payload.substring(20, 22), 16) << 4) & 255) >> 4; + const e = parseInt(payload.substring(22, 24), 16) >> 4; + const f = ((parseInt(payload.substring(22, 24), 16) << 4) & 255) >> 4; + const g = parseInt(payload.substring(24, 26), 16) >> 4; + const h = ((parseInt(payload.substring(24, 26), 16) << 4) & 255) >> 4; + const i = parseInt(payload.substring(26, 28), 16) >> 4; + const j = ((parseInt(payload.substring(26, 28), 16) << 4) & 255) >> 4; + const k = parseInt(payload.substring(28, 30), 16) >> 4; + const l = ((parseInt(payload.substring(28, 30), 16) << 4) & 255) >> 4; + const m = parseInt(payload.substring(30, 32), 16) >> 4; + const n = ((parseInt(payload.substring(30, 32), 16) << 4) & 255) >> 4; + const o = parseInt(payload.substring(32, 34), 16) >> 4; + // const p = ((parseInt(payload.substring(32, 34), 16) << 4) & 255) >> 6; + const q = ((parseInt(payload.substring(32, 34), 16) << 6) & 255) >> 7; + const r = ((parseInt(payload.substring(32, 34), 16) << 7) & 255) >> 7; + // get latitude and longitude + const latitude = (2 * q - 1) * (10 * a + b + c / 6 + d / 60 + e / 600 + f / 6000 + g / 60000); + const longitude = (2 * r - 1) * (100 * h + 10 * i + j + k / 6 + l / 60 + m / 600 + n / 6000 + o / 60000); + + // save + result.realTimes.push({ + tagRef: "p_latitude", + timestamp: time, + tagValue: String(latitude), + }); + result.realTimes.push({ + tagRef: "p_longitude", + timestamp: time, + tagValue: String(longitude), + }); + } + + break; + } + case 50: { + const p_vibration = parseInt(payload.substring(4, 6), 16); + const p_ils = parseInt(payload.substring(6, 8), 16); + const p_temperature = (2103 - parseInt(payload.substring(8, 12), 16)) / 10.9; + + // save + result.realTimes.push({ + tagRef: "p_vibration", + timestamp: time, + tagValue: String(p_vibration), + }); + result.realTimes.push({ + tagRef: "p_ils", + timestamp: time, + tagValue: String(p_ils), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + break; + } + case 51: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + break; + } + case 52: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get count + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(parseInt(payload.substring(4, 12), 16)), + }); + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(parseInt(payload.substring(12, 20), 16)), + }); + break; + } + case 53: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 54: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 62: { + // Get Absence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(0), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + const valueWorZ = parseInt(payload.substring(10, 14), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + + // Save WorZ + result.realTimes.push({ + tagRef: "p_WorZ", + timestamp: time, + tagValue: String(valueWorZ), + }); + break; + } + case 63: { + // Get Presence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(1), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + break; + } + case 65: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + break; + } + case 66: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + break; + } + case 67: { + // save + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time - 1, + tagValue: String(1), + context: [], + }); + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time, + tagValue: String(0), + context: [], + }); + break; + } + case 78: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + // get count 1 + const count1 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 79: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(14, 22), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 80: { + // get count 1 + const count1 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 81: { + // get count 3 + const count3 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count3", + timestamp: time, + tagValue: String(count3), + }); + + // get count 4 + const count4 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count4", + timestamp: time, + tagValue: String(count4), + }); + break; + } + case 82: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 93: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(2 + 8 * j, 10 + 8 * j), 16)), + }); + } + break; + } + case 94: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(-2 + 8 * j, 6 + 8 * j), 16)), + }); + } + break; + } + case 95: { + // get count 5 + const count5 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count5", + timestamp: time, + tagValue: String(count5), + }); + + // get count 6 + const count6 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count6", + timestamp: time, + tagValue: String(count6), + }); + break; + } + case 96: { + // get count 7 + const count7 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count7", + timestamp: time, + tagValue: String(count7), + }); + + // get count 8 + const count8 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count8", + timestamp: time, + tagValue: String(count8), + }); + break; + } + case 2: + case 4: + case 6: + case 7: + case 8: + case 46: + case 56: + case 60: + case 61: + case 64: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 92: + case 97: + case 98: + case 99: + default: + break; + } + } + // Return result + return result; +} + +function ToTagoFormat(object_item, serie) { + const historics = []; + const events = []; + const realTimes = []; + // eslint-disable-next-line guard-for-in + for (const key in object_item.realTimes) { + realTimes.push({ + variable: `realTimes_${object_item.realTimes[key].tagRef}`.toLowerCase(), + value: object_item.realTimes[key].tagValue, + time: object_item.realTimes[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.events) { + events.push({ + variable: `events_${object_item.events[key].tagRef}`.toLowerCase(), + value: object_item.events[key].tagValue, + time: object_item.events[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.historics) { + historics.push({ + variable: `historics_${object_item.historics[key].tagRef}`.toLowerCase(), + value: object_item.historics[key].tagValue, + time: object_item.historics[key].timestamp, + serie, + }); + } + const result = historics.concat(events, realTimes); + + return result; +} + +/* let payload = [ + { variable: "payload", value: "5e7f000000003f00000040000000410000004200000043000000440000004500000046" }, + { variable: "type", value: 0 }, + { variable: "timestamp", value: 1605614318410 }, +]; */ + +const data = payload.find((x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data"); +const type = payload.find((x) => x.variable === "type"); +const timestamp = payload.find((x) => x.variable === "timestamp"); + +if (data) { + // const buffer = Buffer.from(data.value, "hex"); + const serie = new Date().getTime(); + // payload = decodeStream(data.value, type.value, timestamp.value); + payload = ToTagoFormat(decodeStream(data.value, type.value, timestamp.value), serie); +} + +// eslint-disable-next-line no-console +// console.log(payload); diff --git a/decoders/connector/atim/tm1p/assets/logo.png b/decoders/connector/atim/tm1p/assets/logo.png new file mode 100644 index 00000000..5d2a5327 Binary files /dev/null and b/decoders/connector/atim/tm1p/assets/logo.png differ diff --git a/decoders/connector/atim/tm1p/connector.jsonc b/decoders/connector/atim/tm1p/connector.jsonc new file mode 100644 index 00000000..965fd4ed --- /dev/null +++ b/decoders/connector/atim/tm1p/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Atim TM1P", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/atim/tm1p/description.md b/decoders/connector/atim/tm1p/description.md new file mode 100644 index 00000000..b11dc5c2 --- /dev/null +++ b/decoders/connector/atim/tm1p/description.md @@ -0,0 +1 @@ +Temperature sensor over LoRaWAN or Sigfox \ No newline at end of file diff --git a/decoders/connector/atim/tm1p/v1.0.0/payload-config.jsonc b/decoders/connector/atim/tm1p/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..8f692f0f --- /dev/null +++ b/decoders/connector/atim/tm1p/v1.0.0/payload-config.jsonc @@ -0,0 +1,26 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "The ACW-TM1P monitors and transmits one remote temperature. The sensor has 1 external PT100/PT1000 analog probe. Data transmission at regular intervals configurable as required through the ACW configurator. Compatible with Sigfox repeater (ACW-GW).\n\n**Technical Data**\n* Dimensions: 154 x 54 x 54 mm\n* Antenna: Integrated (¼ wave)\n* Temperature probe: 1 analog probe PT100 or PT1000\n* Temperature: -25°C to +70°C (operation), -40°C to +70°C (storage)\n* Mounts to: Wall, tube or mast, DIN rail\n* Housing: IP65\n* Power supply: 2x battery-packs (14,4 Ah)\n* Weight: 305g\n* Frequency: 863-870 MHz\n* Power: 25 mW (14 dBm)\n* Transfer rate: Sigfox: 100 bit/s, LoRaWAN: 300 bit/s to 10 kbit/s\n\n**Temperature Sensor**\n* Range: -327.68°C to +327.67°C\n* Resolution: 0.05°C\n* Precision from -25°C to +70°C (Operating range): 0.5 (Data applicable to 2-wire probes)", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/sigfox/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/atim/tm1p/v1.0.0/payload.js b/decoders/connector/atim/tm1p/v1.0.0/payload.js new file mode 100644 index 00000000..de52b1ad --- /dev/null +++ b/decoders/connector/atim/tm1p/v1.0.0/payload.js @@ -0,0 +1,644 @@ +/* eslint-disable guard-for-in */ +/* eslint-disable no-use-before-define */ +/* eslint-disable no-plusplus */ +/* eslint-disable no-case-declarations */ +/* eslint-disable no-bitwise */ +/* eslint-disable no-nested-ternary */ +function decodeStream(payload, timestamp, latitude, longitude) { + // Init result + let result = { historics: [], events: [], realTimes: [] }; + + // Parse stream + // const jsonStream = JSON.parse(stream); + + // Get time and payload + // const time = new Date(data.timestamp * 1000) / 1000; + const time = new Date(timestamp * 1000) / 1000; + // const { payload } = data; + + // Get message ID + const header = parseInt(payload.substring(0, 2), 16); + if (((header << 2) & 255) >> 7 === 1) { + result = DecodeMesureFrame(payload, time); + } else { + result = DecodeCommonFrame(payload, time); + } + + if (header === 129) { + result.realTimes.push({ tagRef: "p_batteryVoltage_empty", timestamp: time, tagValue: String(parseInt(payload.substr(2, 4), 16) / 1000) }); + result.realTimes.push({ tagRef: "p_batteryVoltage_inCharge", timestamp: time, tagValue: String(parseInt(payload.substr(6, 4), 16) / 1000) }); + } + + // Save network informations + result.realTimes.push({ tagRef: "sys_data_payload", timestamp: time, tagValue: String(payload) }); + result.realTimes.push({ tagRef: "sys_data_timestamp", timestamp: time, tagValue: String(time) }); + /* + if (data !== undefined) { + result.realTimes.push({ tagRef: "sys_data_type", timestamp: time, tagValue: String(data.type) }); + result.realTimes.push({ tagRef: "sys_data_raw", timestamp: time, tagValue: String(data.raw) }); + } + if (metadata !== undefined) { + if (metadata.device !== undefined) { + result.realTimes.push({ tagRef: "sys_device_sn", timestamp: time, tagValue: String(metadata.device.serialNumber) }); + result.realTimes.push({ tagRef: "sys_device_name", timestamp: time, tagValue: String(metadata.device.name) }); + } + if (metadata.endpoint !== undefined) { + result.realTimes.push({ tagRef: "sys_endpoint_name", timestamp: time, tagValue: String(metadata.endpoint.name) }); + result.realTimes.push({ tagRef: "sys_endpoint_type", timestamp: time, tagValue: String(metadata.endpoint.type) }); + } + if (metadata.network !== undefined) { + result.realTimes.push({ tagRef: "sys_network_port", timestamp: time, tagValue: String(metadata.network.port) }); + result.realTimes.push({ tagRef: "sys_network_linkQuality", timestamp: time, tagValue: String(metadata.network.linkQuality) }); + } */ + if (latitude !== undefined && longitude !== undefined) { + // result.realTimes.push({ tagRef: "sys_location_source", timestamp: time, tagValue: String(metadata.location.source) }); + result.realTimes.push({ tagRef: "sys_location_latitude", timestamp: time, tagValue: latitude, unit: "°" }); + result.realTimes.push({ tagRef: "sys_location_longitude", timestamp: time, tagValue: longitude, unit: "°" }); + // result.realTimes.push({ tagRef: "sys_location_accuracy", timestamp: time, tagValue: String(metadata.location.accuracy) }); + } + /* } + + if (metadata.endpoint !== undefined) { + // if KHEIRON + if (metadata.endpoint.type === 0) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(data.raw.fcnt) }); + } + // if SIGFOX + if (metadata.endpoint.type === 5) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(data.raw.seqNumber) }); + } + // if OBJENIOUS + if (metadata.endpoint.type === 6) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(data.raw.count) }); + result.realTimes.push({ tagRef: "custom_data_raw_devEUI", timestamp: time, tagValue: String(data.raw.device_properties.deveui) }); + } + } + */ + + return result; +} + +function DecodeCommonFrame(payload, time) { + // Init result + const result = { + historics: [], + events: [], + realTimes: [], + }; + + // get bit values + const horodatage = ((parseInt(payload.substring(0, 2), 16) << 1) & 255) >> 7; + + // calcul of the starting index + let startingIndex = 2 + horodatage * 8; + + // get frame type + const frameType = ((parseInt(payload.substring(0, 2), 16) << 4) & 255) >> 4; + + // keep alive frame + if (frameType === 1) { + result.realTimes.push({ + tagRef: "p_keepAlive", + timestamp: time, + tagValue: String(1), + }); + } + + // threshold alarm + if (frameType === 13 || frameType === 5) { + while (startingIndex < payload.length) { + // get header informations of each voie + const header_voie = parseInt(payload.substring(startingIndex, startingIndex + 2), 16); + const alertType_voie = header_voie >> 6; + const number_voie = ((header_voie << 2) & 255) >> 6; + const mesureType_voie = ((header_voie << 4) & 255) >> 4; + const mesureSize_voie = getMesureSize(mesureType_voie); + + // increase starting index + startingIndex += 2; + + // check if the size is different than 0 + if (mesureSize_voie !== 0) { + // get mesure + const mesure = parseInt(payload.substring(startingIndex, startingIndex + mesureSize_voie), 16); + + // get calculated table of log + const calculatedMesureTab = getCalculatedMesure( + mesure, + mesureType_voie, + number_voie, + horodatage ? new Date(parseInt(payload.substring(2, 10), 16) * 1000).getTime() : time + ); + + // add table log into realtimes + result.realTimes = result.realTimes.concat(calculatedMesureTab); + + // get calculated table of events + const eventTab = getThresholdEvents(mesureType_voie, alertType_voie, number_voie, horodatage ? new Date(parseInt(payload.substring(2, 10), 16) * 1000).getTime() : time); + + // add table event into events + result.events = result.events.concat(eventTab); + + // increase index + startingIndex += mesureSize_voie; + } else { + return result; + } + } + } + + // general alarm + if (frameType === 14) { + // get header informations of each voie + const header_error = parseInt(payload.substring(startingIndex, startingIndex + 2), 16); + const length_error = ((header_error << 3) & 255) >> 3; + + // increase starting index + startingIndex += 2; + + for (let e = 0; e < length_error; e++) { + // get error and add log into realtimes table + result.realTimes.push( + getError(parseInt(payload.substring(startingIndex, startingIndex + 2), 16), horodatage ? new Date(parseInt(payload.substring(2, 10), 16) * 1000).getTime() : time) + ); + + // increase starting index + startingIndex += 2; + } + } + + if (frameType === 15) { + if (parseInt(payload.substring(startingIndex, startingIndex + 2), 16) === 28) { + // add wirecut into realtimes table + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: horodatage ? new Date(parseInt(payload.substring(2, 10), 16) * 1000).getTime() : time, + tagValue: String(parseInt(payload.substring(startingIndex + 2, startingIndex + 4), 16) === 1 ? 0 : 1), + }); + } + } + return result; +} + +function DecodeMesureFrame(payload, time) { + // Init result + const result = { + historics: [], + events: [], + realTimes: [], + }; + + // get bit values + const horodatage = ((parseInt(payload.substring(0, 2), 16) << 1) & 255) >> 7; + const profondeur = (((parseInt(payload.substring(0, 2), 16) << 3) & 255) >> 6) + 1; + const nb_echantillons = (((parseInt(payload.substring(0, 2), 16) << 5) & 255) >> 5) + 1; + const period = profondeur > 1 || nb_echantillons > 1 ? parseInt(payload.substring(2 + horodatage * 8, 6 + horodatage * 8), 16) : 0; + // calcul of the starting index + let startingIndex = 2 + horodatage * 8 + (profondeur > 1 || nb_echantillons > 1) * 4; + + while (startingIndex < payload.length) { + // get header informations of each voie + const header_voie = parseInt(payload.substring(startingIndex, startingIndex + 2), 16); + const number_voie = ((header_voie << 2) & 255) >> 6; + const mesureType_voie = ((header_voie << 4) & 255) >> 4; + const mesureSize_voie = getMesureSize(mesureType_voie); + + // increase starting index + startingIndex += 2; + + // check if the size is different than 0 + if (mesureSize_voie !== 0) { + // iterate on each mesure + + for (let i = 0; i < profondeur * nb_echantillons; i++) { + // get mesure + const mesure = parseInt(payload.substring(startingIndex, startingIndex + mesureSize_voie), 16); + + if (i === 0) { + // get calculated table of log + const calculatedMesureTab = getCalculatedMesure( + mesure, + mesureType_voie, + number_voie, + horodatage ? new Date(parseInt(payload.substring(2, 10), 16) * 1000).getTime() : time + ); + + // add table log into realtimes + result.realTimes = result.realTimes.concat(calculatedMesureTab); + } else { + // get calculated table of log + const calculatedMesureTab = getCalculatedMesure( + mesure, + mesureType_voie, + number_voie, + horodatage + ? new Date((parseInt(payload.substring(2, 10), 16) - (period / nb_echantillons) * 60 * i) * 1000).getTime() + : new Date((time - (period / nb_echantillons) * 60 * i) * 1000).getTime() + ); + + // add table log into historics + result.historics = result.historics.concat(calculatedMesureTab); + } + + // increase index + startingIndex += mesureSize_voie; + } + } else { + return result; + } + } + + return result; +} + +function getMesureSize(mesureType) { + switch (mesureType) { + case 1: + return 2; + case 2: + case 3: + case 7: + case 8: + case 9: + case 10: + case 11: + return 4; + case 4: + return 8; + default: + return 0; + } +} + +function getCalculatedMesure(mesure, mesureType, number_voie, date) { + switch (mesureType) { + case 1: + case 2: + const tab = []; + const mesureString = `0000000${mesure.toString(2)}`.slice(-8); + for (let i = 1; i < mesureString.length + 1; i++) { + tab.push({ + tagRef: `p_DI${i}_${number_voie}`, + timestamp: date, + tagValue: String(mesureString[mesureString.length - i]), + }); + } + return tab; + case 3: + case 4: + return [ + { + tagRef: `p_count_${number_voie}`, + timestamp: date, + tagValue: String(mesure), + }, + ]; + case 7: + if (mesure >> 15 === 1) { + return [ + { + tagRef: `p_mm_${number_voie}`, + timestamp: date, + tagValue: String(((mesure ^ 65535) + 1) * -1), + }, + ]; + } + return [ + { + tagRef: `p_mm_${number_voie}`, + timestamp: date, + tagValue: String(mesure), + }, + ]; + + case 10: + if (mesure >> 15 === 1) { + return [ + { + tagRef: `p_mV_${number_voie}`, + timestamp: date, + tagValue: String(((mesure ^ 65535) + 1) * -1), + }, + ]; + } + return [ + { + tagRef: `p_mV_${number_voie}`, + timestamp: date, + tagValue: String(mesure), + }, + ]; + + case 11: + if (mesure >> 15 === 1) { + return [ + { + tagRef: `p_uA_${number_voie}`, + timestamp: date, + tagValue: String(((mesure ^ 65535) + 1) * -1), + }, + ]; + } + return [ + { + tagRef: `p_uA_${number_voie}`, + timestamp: date, + tagValue: String(mesure), + }, + ]; + + case 8: + if (mesure >> 15 === 1) { + return [ + { + tagRef: `p_temperature_${number_voie}`, + timestamp: date, + tagValue: String((((mesure ^ 65535) + 1) / 100) * -1), + }, + ]; + } + return [ + { + tagRef: `p_temperature_${number_voie}`, + timestamp: date, + tagValue: String(mesure / 100), + }, + ]; + + case 9: + if (mesure >> 15 === 1) { + return [ + { + tagRef: `p_humidity_${number_voie}`, + timestamp: date, + tagValue: String((((mesure ^ 65535) + 1) / 100) * -1), + }, + ]; + } + return [ + { + tagRef: `p_humidity_${number_voie}`, + timestamp: date, + tagValue: String(mesure / 100), + }, + ]; + + default: + return []; + } +} + +function getThresholdEvents(mesureType, alertType, number_voie, date) { + switch (mesureType) { + case 3: + case 4: + return [ + { + tagRef: `p_count_${number_voie}_high_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 1 ? 1 : 0), + context: [], + }, + { + tagRef: `p_count_${number_voie}_low_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 2 ? 1 : 0), + context: [], + }, + ]; + case 7: + return [ + { + tagRef: `p_mm_${number_voie}_high_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 1 ? 1 : 0), + context: [], + }, + { + tagRef: `p_mm_${number_voie}_low_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 2 ? 1 : 0), + context: [], + }, + ]; + case 10: + return [ + { + tagRef: `p_mV_${number_voie}_high_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 1 ? 1 : 0), + context: [], + }, + { + tagRef: `p_mV_${number_voie}_low_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 2 ? 1 : 0), + context: [], + }, + ]; + case 11: + return [ + { + tagRef: `p_uA_${number_voie}_high_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 1 ? 1 : 0), + context: [], + }, + { + tagRef: `p_uA_${number_voie}_low_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 2 ? 1 : 0), + context: [], + }, + ]; + case 8: + return [ + { + tagRef: `p_temperature_${number_voie}_high_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 1 ? 1 : 0), + context: [], + }, + { + tagRef: `p_temperature_${number_voie}_low_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 2 ? 1 : 0), + context: [], + }, + ]; + case 9: + return [ + { + tagRef: `p_humidity_${number_voie}_high_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 1 ? 1 : 0), + context: [], + }, + { + tagRef: `p_humidity_${number_voie}_low_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 2 ? 1 : 0), + context: [], + }, + ]; + default: + return []; + } +} + +function getError(error_code, date) { + let ref; + switch (error_code) { + case 0: + ref = "p_ERR_BUF_SMALLER"; + break; + case 1: + ref = "p_ERR_DEPTH_HISTORIC_OUT_OF_RANGE"; + break; + case 2: + ref = "p_ERR_NB_SAMPLE_OUT_OF_RANGE"; + break; + case 3: + ref = "p_ERR_NWAY_OUT_OF_RANGE"; + break; + case 4: + ref = "p_ERR_TYPEWAY_OUT_OF_RANGE"; + break; + case 5: + ref = "p_ERR_SAMPLING_PERIOD"; + break; + case 6: + ref = "p_ERR_KEEP_ALIVE_PERIOD"; + break; + case 7: + ref = "p_ERR_SUBTASK_END"; + break; + case 8: + ref = "p_ERR_NULL_POINTER"; + break; + case 9: + ref = "p_ERR_BATTERY_LEVEL_LOW"; + break; + case 10: + ref = "p_ERR_BATTERY_LEVEL_DEAD"; + break; + case 11: + ref = "p_ERR_EEPROM"; + break; + case 12: + ref = "p_ERR_ROM"; + break; + case 13: + ref = "p_ERR_RAM"; + break; + case 14: + ref = "p_ERR_SENSORS_TIMEOUT"; + break; + case 15: + ref = "p_ERR_SENSOR_STOP"; + break; + case 16: + ref = "p_ERR_SENSORS_FAIL"; + break; + case 17: + ref = "p_ERR_ARM_INIT_FAIL"; + break; + case 18: + ref = "p_ERR_ARM_PAYLOAD_BIGGER"; + break; + case 19: + ref = "p_ERR_ARM_BUSY"; + break; + case 20: + ref = "p_ERR_ARM_BRIDGE_ENABLE"; + break; + case 21: + ref = "p_ERR_ARM_TRANSMISSION"; + break; + case 22: + ref = "p_ERR_RADIO_QUEUE_FULL"; + break; + case 23: + ref = "p_ERR_CFG_BOX_INIT_FAIL"; + break; + case 24: + ref = "p_ERR_BOX_OPENED"; + break; + default: + return undefined; + } + + return { + tagRef: ref, + timestamp: date, + tagValue: String(1), + }; +} + +// eslint-disable-next-line no-unused-vars +function ToTagoFormat(object_item, serie, prefix = "") { + const historics = []; + const events = []; + const realTimes = []; + for (const key in object_item.realTimes) { + realTimes.push({ + variable: `realTimes_${object_item.realTimes[key].tagRef}`.toLowerCase(), + value: object_item.realTimes[key].tagValue, + time: object_item.realTimes[key].timestamp, + serie, + }); + } + for (const key in object_item.events) { + events.push({ + variable: `events_${object_item.events[key].tagRef}`.toLowerCase(), + value: object_item.events[key].tagValue, + time: object_item.events[key].timestamp, + serie, + }); + } + for (const key in object_item.historics) { + historics.push({ + variable: `historics_${object_item.historics[key].tagRef}`.toLowerCase(), + value: object_item.historics[key].tagValue, + time: object_item.historics[key].timestamp, + serie, + }); + } + const result = historics.concat(events, realTimes); + + return result; +} + +/* let payload = [ + { variable: "data", value: { timestamp: 1605614318410, payload: "810d240c68", raw: { seqNumber: 123 } } }, + { + variable: "metadata", + value: { + endpoint: { name: "sigfox", type: 5 }, + network: { port: 95, linkQuality: 100 }, + location: { source: "my_head", latitude: "-38°", longitude: "-18°", accuracy: 95 }, + device: { name: "TM1P", serialNumber: "01101011" }, + }, + }, +]; */ +/* +let payload = [ + { variable: "data", value: "a1000211aabb220055006633000300044400000004000000055700050006682221222279777777788a000800099b0009000a" }, + { variable: "timestamp", value: 1605614318410 }, + { variable: "latitude", value: -38 }, + { variable: "longitude", value: -18 }, +]; +*/ +const data = payload.find((x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data"); +const timestamp = payload.find((x) => x.variable === "timestamp"); +const latitude = payload.find((x) => x.variable === "latitude"); +const longitude = payload.find((x) => x.variable === "longitude"); + +if (data) { + // const buffer = Buffer.from(data.value, "hex"); + const serie = new Date().getTime(); + payload = ToTagoFormat(decodeStream(data.value, timestamp.value, latitude.value, longitude.value), serie); +} + +// eslint-disable-next-line no-console +// console.log(payload); diff --git a/decoders/connector/atim/tm2d-hp/assets/logo.png b/decoders/connector/atim/tm2d-hp/assets/logo.png new file mode 100644 index 00000000..83e95ea2 Binary files /dev/null and b/decoders/connector/atim/tm2d-hp/assets/logo.png differ diff --git a/decoders/connector/atim/tm2d-hp/connector.jsonc b/decoders/connector/atim/tm2d-hp/connector.jsonc new file mode 100644 index 00000000..3afd7fdc --- /dev/null +++ b/decoders/connector/atim/tm2d-hp/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Atim TM2D-HP", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/atim/tm2d-hp/description.md b/decoders/connector/atim/tm2d-hp/description.md new file mode 100644 index 00000000..b11dc5c2 --- /dev/null +++ b/decoders/connector/atim/tm2d-hp/description.md @@ -0,0 +1 @@ +Temperature sensor over LoRaWAN or Sigfox \ No newline at end of file diff --git a/decoders/connector/atim/tm2d-hp/v1.0.0/payload-config.jsonc b/decoders/connector/atim/tm2d-hp/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..993fd6f8 --- /dev/null +++ b/decoders/connector/atim/tm2d-hp/v1.0.0/payload-config.jsonc @@ -0,0 +1,26 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "The ACW-TM2D-HP monitors and transmits two remote temperatures. The sensor has 2 external digital probes. Data transmission at regular intervals configurable as required through the ACW configurator. Compatible with Sigfox repeater (ACW-GW).\n\n**Technical Data**\n* Dimensions: 154 x 54 x 54 mm\n* Antenna: Integrated (¼ wave)\n* Temperature probe: Numerical\n* Cable length: 2 or 5 meters\n* Temperature: -20°C to +55°C (operation), -40°C to +70°C (storage), -55°C to +125°C (probes)\n* Mounts to: Wall, tube or pole, DIN rail\n* Housing: IP65\n* Power supply: 2x battery-packs (14,4 Ah)\n* Weight: 280g\n* Frequency: 865-870 MHz\n* Power: 25 mW (14 dBm)\n* Transfer rate: Sigfox: 100 bit/s, LoRaWAN: 300 bit/s to 10 kbit/s\n\n**Temperature Sensor**\n* Range: -55°C to +125°C\n* Resolution: 0.5°C (9 bits)\n* Precision from -55°C to -10°C: +/- 2°C\n* Precision from -10°C to +85°C: +/- 0.5°C\n* Precision from +85°C to +125°C: +/- 2°C", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/sigfox/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/atim/tm2d-hp/v1.0.0/payload.js b/decoders/connector/atim/tm2d-hp/v1.0.0/payload.js new file mode 100644 index 00000000..58709750 --- /dev/null +++ b/decoders/connector/atim/tm2d-hp/v1.0.0/payload.js @@ -0,0 +1,1332 @@ +/* eslint-disable no-plusplus */ +/* eslint-disable no-bitwise */ +function decodeStream(payload, type, timestamp) { + // Init result + const result = { historics: [], events: [], realTimes: [] }; + + // Parse stream + // const jsonStream = JSON.parse(stream); + + // Get time and payload + // const time = new Date(jsonStream.data.timestamp * 1000) / 1000; + const time = new Date(timestamp * 1000) / 1000; + // const { payload } = jsonStream.data; + + // Save network informations + result.realTimes.push({ tagRef: "sys_data_payload", timestamp: time, tagValue: String(payload) }); + result.realTimes.push({ tagRef: "sys_data_timestamp", timestamp: time, tagValue: String(time) }); + /* if (jsonStream.data !== undefined) { + result.realTimes.push({ tagRef: "sys_data_type", timestamp: time, tagValue: String(jsonStream.data.type) }); + result.realTimes.push({ tagRef: "sys_data_raw", timestamp: time, tagValue: String(jsonStream.data.raw) }); + } + if (jsonStream.metadata !== undefined) { + result.realTimes.push({ tagRef: "sys_metadata_lastStreamTimestampUtc", timestamp: time, tagValue: String(jsonStream.metadata.lastStreamTimestampUtc) }); + if (jsonStream.metadata.device !== undefined) { + result.realTimes.push({ tagRef: "sys_device_sn", timestamp: time, tagValue: String(jsonStream.metadata.device.serialNumber) }); + result.realTimes.push({ tagRef: "sys_device_name", timestamp: time, tagValue: String(jsonStream.metadata.device.name) }); + } + if (jsonStream.metadata.endpoint !== undefined) { + result.realTimes.push({ tagRef: "sys_endpoint_name", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.name) }); + result.realTimes.push({ tagRef: "sys_endpoint_type", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.type) }); + } + if (jsonStream.metadata.network !== undefined) { + result.realTimes.push({ tagRef: "sys_network_port", timestamp: time, tagValue: String(jsonStream.metadata.network.port) }); + result.realTimes.push({ tagRef: "sys_network_linkQuality", timestamp: time, tagValue: String(jsonStream.metadata.network.linkQuality) }); + } + if (jsonStream.metadata.location !== undefined) { + result.realTimes.push({ tagRef: "sys_location_source", timestamp: time, tagValue: String(jsonStream.metadata.location.source) }); + result.realTimes.push({ tagRef: "sys_location_latitude", timestamp: time, tagValue: String(jsonStream.metadata.location.latitude) }); + result.realTimes.push({ tagRef: "sys_location_longitude", timestamp: time, tagValue: String(jsonStream.metadata.location.longitude) }); + result.realTimes.push({ tagRef: "sys_location_accuracy", timestamp: time, tagValue: String(jsonStream.metadata.location.accuracy) }); + } + } */ + if (type !== 3) { + // if KHEIRON + /* if (jsonStream.metadata.endpoint.type === 0) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).fcnt) }); + } + // if SIGFOX + if (jsonStream.metadata.endpoint.type === 5) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).seqNumber) }); + } + // if OBJENIOUS + if (jsonStream.metadata.endpoint.type === 6) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).count) }); + result.realTimes.push({ tagRef: "custom_data_raw_devEUI", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).device_properties.deveui) }); + } + */ + // Get message ID + const msgStr = parseInt(payload.substring(0, 2), 16); + + // Switch message ids + switch (msgStr) { + case 1: { + // Get meter + const battery = parseInt(payload.substring(2, 6), 16); + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery / 1000), + }); + + break; + } + case 3: { + // Get data + const battery = parseInt(payload.substring(2, 4), 16); + let temperature = parseInt(payload.substring(4, 8), 16); + const humidity = parseInt(payload.substring(8, 12), 16); + + // check if negative value + if (temperature >> 15 === 1) { + const reverseValue = temperature ^ 65535; + temperature = (reverseValue + 1) * -1; + } + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temperature / 100), + }); + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity / 100), + }); + break; + } + case 5: { + const testCounter = parseInt(payload.substring(2, 4), 16); + + result.realTimes.push({ + tagRef: "p_test", + timestamp: time, + tagValue: String(testCounter), + }); + + break; + } + case 9: { + // Get input + const di = parseInt(payload.substring(4, 6), 16); + const di1 = (di & 1) === 1; + const di2 = (di & 2) === 2; + + // get ouput + const dout = parseInt(payload.substring(2, 4), 16); + const do1 = (dout & 1) === 1; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DO1", + timestamp: time, + tagValue: String(do1), + }); + + break; + } + case 10: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + break; + } + case 11: { + break; + } + case 12: { + break; + } + case 13: { + break; + } + case 14: { + break; + } + case 21: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // Decode + temp = (temp - 33184) / 128; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + break; + } + case 20: { + // Get meter + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 48: { + // get realtime meter + const meter = parseInt(payload.substring(42, 50), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + + // get historic meter + const meter1 = parseInt(payload.substring(34, 42), 16); + const meter2 = parseInt(payload.substring(26, 34), 16); + const meter3 = parseInt(payload.substring(18, 26), 16); + const meter4 = parseInt(payload.substring(10, 18), 16); + const meter5 = parseInt(payload.substring(2, 10), 16); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 1) / 1000, + tagValue: String(meter1), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 2) / 1000, + tagValue: String(meter2), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 3) / 1000, + tagValue: String(meter3), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 4) / 1000, + tagValue: String(meter4), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 5) / 1000, + tagValue: String(meter5), + }); + + break; + } + case 49: { + // get realtime meter + const meter = parseInt(payload.substring(2, 10), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + break; + } + case 55: { + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + break; + } + case 57: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // const a = new Date(time * 1000); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (600000 + i * 600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 58: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (1800000 + i * 1800000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 59: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (3600000 + i * 3600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 22: { + // Get meter + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(4, 6), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 15: { + break; + } + case 16: { + break; + } + case 17: { + break; + } + case 18: { + break; + } + case 23: { + // Get temperature & humidity + let temp = parseInt(payload.substring(2, 6), 16); + let humidity = parseInt(payload.substring(6, 10), 16); + + // Decode + temp = (temp * 175.72) / 65536 - 46.85; + humidity = (humidity * 125) / 65536 - 6; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity), + }); + break; + } + case 24: { + break; + } + case 30: { + break; + } + case 31: { + break; + } + case 32: { + break; + } + case 33: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 10 / 64240; + + result.realTimes.push({ + tagRef: "p_voltage", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 25: { + break; + } + case 34: { + break; + } + case 35: { + break; + } + case 36: { + break; + } + case 37: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 16 / 47584; + result.realTimes.push({ + tagRef: "p_current", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 27: { + break; + } + case 42: { + break; + } + case 43: { + break; + } + case 44: { + break; + } + case 45: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + break; + } + case 26: { + break; + } + case 38: { + break; + } + case 39: { + break; + } + case 40: { + break; + } + case 41: { + break; + } + case 83: { + break; + } + case 84: { + break; + } + case 85: { + break; + } + case 86: { + break; + } + case 87: { + break; + } + case 88: { + break; + } + case 89: { + break; + } + case 90: { + break; + } + case 91: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + let temp2 = parseInt(payload.substring(6, 10), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + + // check if error + if (temp2 !== 32768) { + // check if negative value + if (temp2 >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp2 = (reverseValue + 1) * -1; + } + + // apply coef + temp2 *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature2", + timestamp: time, + tagValue: String(temp2), + }); + } + + break; + } + case 19: { + // Get water leak state + const waterleak = parseInt(payload.substring(4, 6), 16); + + // save + result.realTimes.push({ + tagRef: "p_waterLeak", + timestamp: time, + tagValue: String(waterleak), + }); + break; + } + case 47: { + // get temperature + const temp = parseInt(payload.substring(14, 18), 16); + const p_temperature = (10.888 - Math.sqrt((-10.888) ** 2 + 4 * 0.00347 * (1777.3 - temp))) / (2 * -0.00347) + 30; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + + if (parseInt(payload.substring(18, 34), 16) !== 0) { + // get data for gps decoding + const a = parseInt(payload.substring(18, 20), 16) >> 4; + const b = ((parseInt(payload.substring(18, 20), 16) << 4) & 255) >> 4; + const c = parseInt(payload.substring(20, 22), 16) >> 4; + const d = ((parseInt(payload.substring(20, 22), 16) << 4) & 255) >> 4; + const e = parseInt(payload.substring(22, 24), 16) >> 4; + const f = ((parseInt(payload.substring(22, 24), 16) << 4) & 255) >> 4; + const g = parseInt(payload.substring(24, 26), 16) >> 4; + const h = ((parseInt(payload.substring(24, 26), 16) << 4) & 255) >> 4; + const i = parseInt(payload.substring(26, 28), 16) >> 4; + const j = ((parseInt(payload.substring(26, 28), 16) << 4) & 255) >> 4; + const k = parseInt(payload.substring(28, 30), 16) >> 4; + const l = ((parseInt(payload.substring(28, 30), 16) << 4) & 255) >> 4; + const m = parseInt(payload.substring(30, 32), 16) >> 4; + const n = ((parseInt(payload.substring(30, 32), 16) << 4) & 255) >> 4; + const o = parseInt(payload.substring(32, 34), 16) >> 4; + // const p = ((parseInt(payload.substring(32, 34), 16) << 4) & 255) >> 6; + const q = ((parseInt(payload.substring(32, 34), 16) << 6) & 255) >> 7; + const r = ((parseInt(payload.substring(32, 34), 16) << 7) & 255) >> 7; + // get latitude and longitude + const latitude = (2 * q - 1) * (10 * a + b + c / 6 + d / 60 + e / 600 + f / 6000 + g / 60000); + const longitude = (2 * r - 1) * (100 * h + 10 * i + j + k / 6 + l / 60 + m / 600 + n / 6000 + o / 60000); + + // save + result.realTimes.push({ + tagRef: "p_latitude", + timestamp: time, + tagValue: String(latitude), + }); + result.realTimes.push({ + tagRef: "p_longitude", + timestamp: time, + tagValue: String(longitude), + }); + } + + break; + } + case 50: { + const p_vibration = parseInt(payload.substring(4, 6), 16); + const p_ils = parseInt(payload.substring(6, 8), 16); + const p_temperature = (2103 - parseInt(payload.substring(8, 12), 16)) / 10.9; + + // save + result.realTimes.push({ + tagRef: "p_vibration", + timestamp: time, + tagValue: String(p_vibration), + }); + result.realTimes.push({ + tagRef: "p_ils", + timestamp: time, + tagValue: String(p_ils), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + break; + } + case 51: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + break; + } + case 52: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get count + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(parseInt(payload.substring(4, 12), 16)), + }); + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(parseInt(payload.substring(12, 20), 16)), + }); + break; + } + case 53: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 54: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 62: { + // Get Absence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(0), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + const valueWorZ = parseInt(payload.substring(10, 14), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + + // Save WorZ + result.realTimes.push({ + tagRef: "p_WorZ", + timestamp: time, + tagValue: String(valueWorZ), + }); + break; + } + case 63: { + // Get Presence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(1), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + break; + } + case 65: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + break; + } + case 66: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + break; + } + case 67: { + // save + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time - 1, + tagValue: String(1), + context: [], + }); + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time, + tagValue: String(0), + context: [], + }); + break; + } + case 78: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + // get count 1 + const count1 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 79: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(14, 22), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 80: { + // get count 1 + const count1 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 81: { + // get count 3 + const count3 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count3", + timestamp: time, + tagValue: String(count3), + }); + + // get count 4 + const count4 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count4", + timestamp: time, + tagValue: String(count4), + }); + break; + } + case 82: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 93: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(2 + 8 * j, 10 + 8 * j), 16)), + }); + } + break; + } + case 94: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(-2 + 8 * j, 6 + 8 * j), 16)), + }); + } + break; + } + case 95: { + // get count 5 + const count5 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count5", + timestamp: time, + tagValue: String(count5), + }); + + // get count 6 + const count6 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count6", + timestamp: time, + tagValue: String(count6), + }); + break; + } + case 96: { + // get count 7 + const count7 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count7", + timestamp: time, + tagValue: String(count7), + }); + + // get count 8 + const count8 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count8", + timestamp: time, + tagValue: String(count8), + }); + break; + } + case 2: + case 4: + case 6: + case 7: + case 8: + case 46: + case 56: + case 60: + case 61: + case 64: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 92: + case 97: + case 98: + case 99: + default: + break; + } + } + // Return result + return result; +} + +function ToTagoFormat(object_item, serie) { + const historics = []; + const events = []; + const realTimes = []; + // eslint-disable-next-line guard-for-in + for (const key in object_item.realTimes) { + realTimes.push({ + variable: `realTimes_${object_item.realTimes[key].tagRef}`.toLowerCase(), + value: object_item.realTimes[key].tagValue, + time: object_item.realTimes[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.events) { + events.push({ + variable: `events_${object_item.events[key].tagRef}`.toLowerCase(), + value: object_item.events[key].tagValue, + time: object_item.events[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.historics) { + historics.push({ + variable: `historics_${object_item.historics[key].tagRef}`.toLowerCase(), + value: object_item.historics[key].tagValue, + time: object_item.historics[key].timestamp, + serie, + }); + } + const result = historics.concat(events, realTimes); + + return result; +} + +/* let payload = [ + { variable: "payload", value: "5e7f000000003f00000040000000410000004200000043000000440000004500000046" }, + { variable: "type", value: 0 }, + { variable: "timestamp", value: 1605614318410 }, +]; */ + +const data = payload.find((x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data"); +const type = payload.find((x) => x.variable === "type"); +const timestamp = payload.find((x) => x.variable === "timestamp"); + +if (data) { + // const buffer = Buffer.from(data.value, "hex"); + const serie = new Date().getTime(); + // payload = decodeStream(data.value, type.value, timestamp.value); + payload = ToTagoFormat(decodeStream(data.value, type.value, timestamp.value), serie); +} + +// eslint-disable-next-line no-console +// console.log(payload); diff --git a/decoders/connector/atim/tm2p/assets/logo.png b/decoders/connector/atim/tm2p/assets/logo.png new file mode 100644 index 00000000..62fe8bda Binary files /dev/null and b/decoders/connector/atim/tm2p/assets/logo.png differ diff --git a/decoders/connector/atim/tm2p/connector.jsonc b/decoders/connector/atim/tm2p/connector.jsonc new file mode 100644 index 00000000..b875fc4b --- /dev/null +++ b/decoders/connector/atim/tm2p/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Atim TM2P", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/atim/tm2p/description.md b/decoders/connector/atim/tm2p/description.md new file mode 100644 index 00000000..b11dc5c2 --- /dev/null +++ b/decoders/connector/atim/tm2p/description.md @@ -0,0 +1 @@ +Temperature sensor over LoRaWAN or Sigfox \ No newline at end of file diff --git a/decoders/connector/atim/tm2p/v1.0.0/payload-config.jsonc b/decoders/connector/atim/tm2p/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..fc8f0eb6 --- /dev/null +++ b/decoders/connector/atim/tm2p/v1.0.0/payload-config.jsonc @@ -0,0 +1,26 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "The ACW-TM2P monitors and transmits one remote temperature. The sensor has 2 external PT100/PT1000 analog probes. Data transmission at regular intervals configurable as required through the ACW configurator. Compatible with Sigfox repeater (ACW-GW).\n\n**Technical Data**\n* Dimensions: 154 x 54 x 54 mm\n* Antenna: Integrated (¼ wave)\n* Temperature probe: 2 analog probe PT100 or PT1000\n* Temperature: -25°C to +70°C (operation), -40°C to +70°C (storage)\n* Mounts to: Wall, tube or mast, DIN rail\n* Housing: IP65\n* Power supply: 2x battery-packs (14,4 Ah)\n* Weight: 305g\n* Frequency: 863-870 MHz\n* Power: 25 mW (14 dBm)\n* Transfer rate: Sigfox: 100 bit/s, LoRaWAN: 300 bit/s to 10 kbit/s\n\n**Temperature Sensor**\n* Range: -327.68°C to +327.67°C\n* Resolution: 0.05°C\n* Precision from -25°C to +70°C (Operating range): 0.5 (Data applicable to 2-wire probes)", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/sigfox/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/atim/tm2p/v1.0.0/payload.js b/decoders/connector/atim/tm2p/v1.0.0/payload.js new file mode 100644 index 00000000..de52b1ad --- /dev/null +++ b/decoders/connector/atim/tm2p/v1.0.0/payload.js @@ -0,0 +1,644 @@ +/* eslint-disable guard-for-in */ +/* eslint-disable no-use-before-define */ +/* eslint-disable no-plusplus */ +/* eslint-disable no-case-declarations */ +/* eslint-disable no-bitwise */ +/* eslint-disable no-nested-ternary */ +function decodeStream(payload, timestamp, latitude, longitude) { + // Init result + let result = { historics: [], events: [], realTimes: [] }; + + // Parse stream + // const jsonStream = JSON.parse(stream); + + // Get time and payload + // const time = new Date(data.timestamp * 1000) / 1000; + const time = new Date(timestamp * 1000) / 1000; + // const { payload } = data; + + // Get message ID + const header = parseInt(payload.substring(0, 2), 16); + if (((header << 2) & 255) >> 7 === 1) { + result = DecodeMesureFrame(payload, time); + } else { + result = DecodeCommonFrame(payload, time); + } + + if (header === 129) { + result.realTimes.push({ tagRef: "p_batteryVoltage_empty", timestamp: time, tagValue: String(parseInt(payload.substr(2, 4), 16) / 1000) }); + result.realTimes.push({ tagRef: "p_batteryVoltage_inCharge", timestamp: time, tagValue: String(parseInt(payload.substr(6, 4), 16) / 1000) }); + } + + // Save network informations + result.realTimes.push({ tagRef: "sys_data_payload", timestamp: time, tagValue: String(payload) }); + result.realTimes.push({ tagRef: "sys_data_timestamp", timestamp: time, tagValue: String(time) }); + /* + if (data !== undefined) { + result.realTimes.push({ tagRef: "sys_data_type", timestamp: time, tagValue: String(data.type) }); + result.realTimes.push({ tagRef: "sys_data_raw", timestamp: time, tagValue: String(data.raw) }); + } + if (metadata !== undefined) { + if (metadata.device !== undefined) { + result.realTimes.push({ tagRef: "sys_device_sn", timestamp: time, tagValue: String(metadata.device.serialNumber) }); + result.realTimes.push({ tagRef: "sys_device_name", timestamp: time, tagValue: String(metadata.device.name) }); + } + if (metadata.endpoint !== undefined) { + result.realTimes.push({ tagRef: "sys_endpoint_name", timestamp: time, tagValue: String(metadata.endpoint.name) }); + result.realTimes.push({ tagRef: "sys_endpoint_type", timestamp: time, tagValue: String(metadata.endpoint.type) }); + } + if (metadata.network !== undefined) { + result.realTimes.push({ tagRef: "sys_network_port", timestamp: time, tagValue: String(metadata.network.port) }); + result.realTimes.push({ tagRef: "sys_network_linkQuality", timestamp: time, tagValue: String(metadata.network.linkQuality) }); + } */ + if (latitude !== undefined && longitude !== undefined) { + // result.realTimes.push({ tagRef: "sys_location_source", timestamp: time, tagValue: String(metadata.location.source) }); + result.realTimes.push({ tagRef: "sys_location_latitude", timestamp: time, tagValue: latitude, unit: "°" }); + result.realTimes.push({ tagRef: "sys_location_longitude", timestamp: time, tagValue: longitude, unit: "°" }); + // result.realTimes.push({ tagRef: "sys_location_accuracy", timestamp: time, tagValue: String(metadata.location.accuracy) }); + } + /* } + + if (metadata.endpoint !== undefined) { + // if KHEIRON + if (metadata.endpoint.type === 0) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(data.raw.fcnt) }); + } + // if SIGFOX + if (metadata.endpoint.type === 5) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(data.raw.seqNumber) }); + } + // if OBJENIOUS + if (metadata.endpoint.type === 6) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(data.raw.count) }); + result.realTimes.push({ tagRef: "custom_data_raw_devEUI", timestamp: time, tagValue: String(data.raw.device_properties.deveui) }); + } + } + */ + + return result; +} + +function DecodeCommonFrame(payload, time) { + // Init result + const result = { + historics: [], + events: [], + realTimes: [], + }; + + // get bit values + const horodatage = ((parseInt(payload.substring(0, 2), 16) << 1) & 255) >> 7; + + // calcul of the starting index + let startingIndex = 2 + horodatage * 8; + + // get frame type + const frameType = ((parseInt(payload.substring(0, 2), 16) << 4) & 255) >> 4; + + // keep alive frame + if (frameType === 1) { + result.realTimes.push({ + tagRef: "p_keepAlive", + timestamp: time, + tagValue: String(1), + }); + } + + // threshold alarm + if (frameType === 13 || frameType === 5) { + while (startingIndex < payload.length) { + // get header informations of each voie + const header_voie = parseInt(payload.substring(startingIndex, startingIndex + 2), 16); + const alertType_voie = header_voie >> 6; + const number_voie = ((header_voie << 2) & 255) >> 6; + const mesureType_voie = ((header_voie << 4) & 255) >> 4; + const mesureSize_voie = getMesureSize(mesureType_voie); + + // increase starting index + startingIndex += 2; + + // check if the size is different than 0 + if (mesureSize_voie !== 0) { + // get mesure + const mesure = parseInt(payload.substring(startingIndex, startingIndex + mesureSize_voie), 16); + + // get calculated table of log + const calculatedMesureTab = getCalculatedMesure( + mesure, + mesureType_voie, + number_voie, + horodatage ? new Date(parseInt(payload.substring(2, 10), 16) * 1000).getTime() : time + ); + + // add table log into realtimes + result.realTimes = result.realTimes.concat(calculatedMesureTab); + + // get calculated table of events + const eventTab = getThresholdEvents(mesureType_voie, alertType_voie, number_voie, horodatage ? new Date(parseInt(payload.substring(2, 10), 16) * 1000).getTime() : time); + + // add table event into events + result.events = result.events.concat(eventTab); + + // increase index + startingIndex += mesureSize_voie; + } else { + return result; + } + } + } + + // general alarm + if (frameType === 14) { + // get header informations of each voie + const header_error = parseInt(payload.substring(startingIndex, startingIndex + 2), 16); + const length_error = ((header_error << 3) & 255) >> 3; + + // increase starting index + startingIndex += 2; + + for (let e = 0; e < length_error; e++) { + // get error and add log into realtimes table + result.realTimes.push( + getError(parseInt(payload.substring(startingIndex, startingIndex + 2), 16), horodatage ? new Date(parseInt(payload.substring(2, 10), 16) * 1000).getTime() : time) + ); + + // increase starting index + startingIndex += 2; + } + } + + if (frameType === 15) { + if (parseInt(payload.substring(startingIndex, startingIndex + 2), 16) === 28) { + // add wirecut into realtimes table + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: horodatage ? new Date(parseInt(payload.substring(2, 10), 16) * 1000).getTime() : time, + tagValue: String(parseInt(payload.substring(startingIndex + 2, startingIndex + 4), 16) === 1 ? 0 : 1), + }); + } + } + return result; +} + +function DecodeMesureFrame(payload, time) { + // Init result + const result = { + historics: [], + events: [], + realTimes: [], + }; + + // get bit values + const horodatage = ((parseInt(payload.substring(0, 2), 16) << 1) & 255) >> 7; + const profondeur = (((parseInt(payload.substring(0, 2), 16) << 3) & 255) >> 6) + 1; + const nb_echantillons = (((parseInt(payload.substring(0, 2), 16) << 5) & 255) >> 5) + 1; + const period = profondeur > 1 || nb_echantillons > 1 ? parseInt(payload.substring(2 + horodatage * 8, 6 + horodatage * 8), 16) : 0; + // calcul of the starting index + let startingIndex = 2 + horodatage * 8 + (profondeur > 1 || nb_echantillons > 1) * 4; + + while (startingIndex < payload.length) { + // get header informations of each voie + const header_voie = parseInt(payload.substring(startingIndex, startingIndex + 2), 16); + const number_voie = ((header_voie << 2) & 255) >> 6; + const mesureType_voie = ((header_voie << 4) & 255) >> 4; + const mesureSize_voie = getMesureSize(mesureType_voie); + + // increase starting index + startingIndex += 2; + + // check if the size is different than 0 + if (mesureSize_voie !== 0) { + // iterate on each mesure + + for (let i = 0; i < profondeur * nb_echantillons; i++) { + // get mesure + const mesure = parseInt(payload.substring(startingIndex, startingIndex + mesureSize_voie), 16); + + if (i === 0) { + // get calculated table of log + const calculatedMesureTab = getCalculatedMesure( + mesure, + mesureType_voie, + number_voie, + horodatage ? new Date(parseInt(payload.substring(2, 10), 16) * 1000).getTime() : time + ); + + // add table log into realtimes + result.realTimes = result.realTimes.concat(calculatedMesureTab); + } else { + // get calculated table of log + const calculatedMesureTab = getCalculatedMesure( + mesure, + mesureType_voie, + number_voie, + horodatage + ? new Date((parseInt(payload.substring(2, 10), 16) - (period / nb_echantillons) * 60 * i) * 1000).getTime() + : new Date((time - (period / nb_echantillons) * 60 * i) * 1000).getTime() + ); + + // add table log into historics + result.historics = result.historics.concat(calculatedMesureTab); + } + + // increase index + startingIndex += mesureSize_voie; + } + } else { + return result; + } + } + + return result; +} + +function getMesureSize(mesureType) { + switch (mesureType) { + case 1: + return 2; + case 2: + case 3: + case 7: + case 8: + case 9: + case 10: + case 11: + return 4; + case 4: + return 8; + default: + return 0; + } +} + +function getCalculatedMesure(mesure, mesureType, number_voie, date) { + switch (mesureType) { + case 1: + case 2: + const tab = []; + const mesureString = `0000000${mesure.toString(2)}`.slice(-8); + for (let i = 1; i < mesureString.length + 1; i++) { + tab.push({ + tagRef: `p_DI${i}_${number_voie}`, + timestamp: date, + tagValue: String(mesureString[mesureString.length - i]), + }); + } + return tab; + case 3: + case 4: + return [ + { + tagRef: `p_count_${number_voie}`, + timestamp: date, + tagValue: String(mesure), + }, + ]; + case 7: + if (mesure >> 15 === 1) { + return [ + { + tagRef: `p_mm_${number_voie}`, + timestamp: date, + tagValue: String(((mesure ^ 65535) + 1) * -1), + }, + ]; + } + return [ + { + tagRef: `p_mm_${number_voie}`, + timestamp: date, + tagValue: String(mesure), + }, + ]; + + case 10: + if (mesure >> 15 === 1) { + return [ + { + tagRef: `p_mV_${number_voie}`, + timestamp: date, + tagValue: String(((mesure ^ 65535) + 1) * -1), + }, + ]; + } + return [ + { + tagRef: `p_mV_${number_voie}`, + timestamp: date, + tagValue: String(mesure), + }, + ]; + + case 11: + if (mesure >> 15 === 1) { + return [ + { + tagRef: `p_uA_${number_voie}`, + timestamp: date, + tagValue: String(((mesure ^ 65535) + 1) * -1), + }, + ]; + } + return [ + { + tagRef: `p_uA_${number_voie}`, + timestamp: date, + tagValue: String(mesure), + }, + ]; + + case 8: + if (mesure >> 15 === 1) { + return [ + { + tagRef: `p_temperature_${number_voie}`, + timestamp: date, + tagValue: String((((mesure ^ 65535) + 1) / 100) * -1), + }, + ]; + } + return [ + { + tagRef: `p_temperature_${number_voie}`, + timestamp: date, + tagValue: String(mesure / 100), + }, + ]; + + case 9: + if (mesure >> 15 === 1) { + return [ + { + tagRef: `p_humidity_${number_voie}`, + timestamp: date, + tagValue: String((((mesure ^ 65535) + 1) / 100) * -1), + }, + ]; + } + return [ + { + tagRef: `p_humidity_${number_voie}`, + timestamp: date, + tagValue: String(mesure / 100), + }, + ]; + + default: + return []; + } +} + +function getThresholdEvents(mesureType, alertType, number_voie, date) { + switch (mesureType) { + case 3: + case 4: + return [ + { + tagRef: `p_count_${number_voie}_high_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 1 ? 1 : 0), + context: [], + }, + { + tagRef: `p_count_${number_voie}_low_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 2 ? 1 : 0), + context: [], + }, + ]; + case 7: + return [ + { + tagRef: `p_mm_${number_voie}_high_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 1 ? 1 : 0), + context: [], + }, + { + tagRef: `p_mm_${number_voie}_low_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 2 ? 1 : 0), + context: [], + }, + ]; + case 10: + return [ + { + tagRef: `p_mV_${number_voie}_high_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 1 ? 1 : 0), + context: [], + }, + { + tagRef: `p_mV_${number_voie}_low_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 2 ? 1 : 0), + context: [], + }, + ]; + case 11: + return [ + { + tagRef: `p_uA_${number_voie}_high_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 1 ? 1 : 0), + context: [], + }, + { + tagRef: `p_uA_${number_voie}_low_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 2 ? 1 : 0), + context: [], + }, + ]; + case 8: + return [ + { + tagRef: `p_temperature_${number_voie}_high_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 1 ? 1 : 0), + context: [], + }, + { + tagRef: `p_temperature_${number_voie}_low_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 2 ? 1 : 0), + context: [], + }, + ]; + case 9: + return [ + { + tagRef: `p_humidity_${number_voie}_high_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 1 ? 1 : 0), + context: [], + }, + { + tagRef: `p_humidity_${number_voie}_low_alm`, + timestamp: date, + tagValue: String(alertType === 0 ? 0 : alertType === 2 ? 1 : 0), + context: [], + }, + ]; + default: + return []; + } +} + +function getError(error_code, date) { + let ref; + switch (error_code) { + case 0: + ref = "p_ERR_BUF_SMALLER"; + break; + case 1: + ref = "p_ERR_DEPTH_HISTORIC_OUT_OF_RANGE"; + break; + case 2: + ref = "p_ERR_NB_SAMPLE_OUT_OF_RANGE"; + break; + case 3: + ref = "p_ERR_NWAY_OUT_OF_RANGE"; + break; + case 4: + ref = "p_ERR_TYPEWAY_OUT_OF_RANGE"; + break; + case 5: + ref = "p_ERR_SAMPLING_PERIOD"; + break; + case 6: + ref = "p_ERR_KEEP_ALIVE_PERIOD"; + break; + case 7: + ref = "p_ERR_SUBTASK_END"; + break; + case 8: + ref = "p_ERR_NULL_POINTER"; + break; + case 9: + ref = "p_ERR_BATTERY_LEVEL_LOW"; + break; + case 10: + ref = "p_ERR_BATTERY_LEVEL_DEAD"; + break; + case 11: + ref = "p_ERR_EEPROM"; + break; + case 12: + ref = "p_ERR_ROM"; + break; + case 13: + ref = "p_ERR_RAM"; + break; + case 14: + ref = "p_ERR_SENSORS_TIMEOUT"; + break; + case 15: + ref = "p_ERR_SENSOR_STOP"; + break; + case 16: + ref = "p_ERR_SENSORS_FAIL"; + break; + case 17: + ref = "p_ERR_ARM_INIT_FAIL"; + break; + case 18: + ref = "p_ERR_ARM_PAYLOAD_BIGGER"; + break; + case 19: + ref = "p_ERR_ARM_BUSY"; + break; + case 20: + ref = "p_ERR_ARM_BRIDGE_ENABLE"; + break; + case 21: + ref = "p_ERR_ARM_TRANSMISSION"; + break; + case 22: + ref = "p_ERR_RADIO_QUEUE_FULL"; + break; + case 23: + ref = "p_ERR_CFG_BOX_INIT_FAIL"; + break; + case 24: + ref = "p_ERR_BOX_OPENED"; + break; + default: + return undefined; + } + + return { + tagRef: ref, + timestamp: date, + tagValue: String(1), + }; +} + +// eslint-disable-next-line no-unused-vars +function ToTagoFormat(object_item, serie, prefix = "") { + const historics = []; + const events = []; + const realTimes = []; + for (const key in object_item.realTimes) { + realTimes.push({ + variable: `realTimes_${object_item.realTimes[key].tagRef}`.toLowerCase(), + value: object_item.realTimes[key].tagValue, + time: object_item.realTimes[key].timestamp, + serie, + }); + } + for (const key in object_item.events) { + events.push({ + variable: `events_${object_item.events[key].tagRef}`.toLowerCase(), + value: object_item.events[key].tagValue, + time: object_item.events[key].timestamp, + serie, + }); + } + for (const key in object_item.historics) { + historics.push({ + variable: `historics_${object_item.historics[key].tagRef}`.toLowerCase(), + value: object_item.historics[key].tagValue, + time: object_item.historics[key].timestamp, + serie, + }); + } + const result = historics.concat(events, realTimes); + + return result; +} + +/* let payload = [ + { variable: "data", value: { timestamp: 1605614318410, payload: "810d240c68", raw: { seqNumber: 123 } } }, + { + variable: "metadata", + value: { + endpoint: { name: "sigfox", type: 5 }, + network: { port: 95, linkQuality: 100 }, + location: { source: "my_head", latitude: "-38°", longitude: "-18°", accuracy: 95 }, + device: { name: "TM1P", serialNumber: "01101011" }, + }, + }, +]; */ +/* +let payload = [ + { variable: "data", value: "a1000211aabb220055006633000300044400000004000000055700050006682221222279777777788a000800099b0009000a" }, + { variable: "timestamp", value: 1605614318410 }, + { variable: "latitude", value: -38 }, + { variable: "longitude", value: -18 }, +]; +*/ +const data = payload.find((x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data"); +const timestamp = payload.find((x) => x.variable === "timestamp"); +const latitude = payload.find((x) => x.variable === "latitude"); +const longitude = payload.find((x) => x.variable === "longitude"); + +if (data) { + // const buffer = Buffer.from(data.value, "hex"); + const serie = new Date().getTime(); + payload = ToTagoFormat(decodeStream(data.value, timestamp.value, latitude.value, longitude.value), serie); +} + +// eslint-disable-next-line no-console +// console.log(payload); diff --git a/decoders/connector/atim/to-g/assets/logo.png b/decoders/connector/atim/to-g/assets/logo.png new file mode 100644 index 00000000..67b986e9 Binary files /dev/null and b/decoders/connector/atim/to-g/assets/logo.png differ diff --git a/decoders/connector/atim/to-g/connector.jsonc b/decoders/connector/atim/to-g/connector.jsonc new file mode 100644 index 00000000..7c7c828a --- /dev/null +++ b/decoders/connector/atim/to-g/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Atim TO-G", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/atim/to-g/description.md b/decoders/connector/atim/to-g/description.md new file mode 100644 index 00000000..e4eb7376 --- /dev/null +++ b/decoders/connector/atim/to-g/description.md @@ -0,0 +1 @@ +Outdoor Temperature + GPS sensor over LoRaWAN \ No newline at end of file diff --git a/decoders/connector/atim/to-g/v1.0.0/payload-config.jsonc b/decoders/connector/atim/to-g/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..17ca3294 --- /dev/null +++ b/decoders/connector/atim/to-g/v1.0.0/payload-config.jsonc @@ -0,0 +1,25 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "The ACW-TO-G is a device for measuring the ambient temperature while geolocating it. The case is waterproof, but it let air through. Data is transmitted periodically and in the event of an alert that threshold is exceeded. Ideal for monitoring your equipment on the Web, it is perfectly suited for monitoring buildings in the service sector or industry.\n\n**Technical Data**\n* Dimensions: 100 x 100 x 35 mm\n* Antenna: Integrated (¼ wave)\n* Temperature: -20°C to +55°C (operation), -40°C to +70°C (storage)\n* Mounts to: Wall\n* Housing: PVC IP66 Compact\n* Power supply: 2x AA lithium batteries\n* Weight: 100g\n* Frequency: 865-870 MHz\n* Power: 25 mW (14 dBm)\n* Transfer rate: Local 1.2 to 115 kbit/s, LoRaWAN 300 bit/s to 10 kbit/s", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/atim/to-g/v1.0.0/payload.js b/decoders/connector/atim/to-g/v1.0.0/payload.js new file mode 100644 index 00000000..58709750 --- /dev/null +++ b/decoders/connector/atim/to-g/v1.0.0/payload.js @@ -0,0 +1,1332 @@ +/* eslint-disable no-plusplus */ +/* eslint-disable no-bitwise */ +function decodeStream(payload, type, timestamp) { + // Init result + const result = { historics: [], events: [], realTimes: [] }; + + // Parse stream + // const jsonStream = JSON.parse(stream); + + // Get time and payload + // const time = new Date(jsonStream.data.timestamp * 1000) / 1000; + const time = new Date(timestamp * 1000) / 1000; + // const { payload } = jsonStream.data; + + // Save network informations + result.realTimes.push({ tagRef: "sys_data_payload", timestamp: time, tagValue: String(payload) }); + result.realTimes.push({ tagRef: "sys_data_timestamp", timestamp: time, tagValue: String(time) }); + /* if (jsonStream.data !== undefined) { + result.realTimes.push({ tagRef: "sys_data_type", timestamp: time, tagValue: String(jsonStream.data.type) }); + result.realTimes.push({ tagRef: "sys_data_raw", timestamp: time, tagValue: String(jsonStream.data.raw) }); + } + if (jsonStream.metadata !== undefined) { + result.realTimes.push({ tagRef: "sys_metadata_lastStreamTimestampUtc", timestamp: time, tagValue: String(jsonStream.metadata.lastStreamTimestampUtc) }); + if (jsonStream.metadata.device !== undefined) { + result.realTimes.push({ tagRef: "sys_device_sn", timestamp: time, tagValue: String(jsonStream.metadata.device.serialNumber) }); + result.realTimes.push({ tagRef: "sys_device_name", timestamp: time, tagValue: String(jsonStream.metadata.device.name) }); + } + if (jsonStream.metadata.endpoint !== undefined) { + result.realTimes.push({ tagRef: "sys_endpoint_name", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.name) }); + result.realTimes.push({ tagRef: "sys_endpoint_type", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.type) }); + } + if (jsonStream.metadata.network !== undefined) { + result.realTimes.push({ tagRef: "sys_network_port", timestamp: time, tagValue: String(jsonStream.metadata.network.port) }); + result.realTimes.push({ tagRef: "sys_network_linkQuality", timestamp: time, tagValue: String(jsonStream.metadata.network.linkQuality) }); + } + if (jsonStream.metadata.location !== undefined) { + result.realTimes.push({ tagRef: "sys_location_source", timestamp: time, tagValue: String(jsonStream.metadata.location.source) }); + result.realTimes.push({ tagRef: "sys_location_latitude", timestamp: time, tagValue: String(jsonStream.metadata.location.latitude) }); + result.realTimes.push({ tagRef: "sys_location_longitude", timestamp: time, tagValue: String(jsonStream.metadata.location.longitude) }); + result.realTimes.push({ tagRef: "sys_location_accuracy", timestamp: time, tagValue: String(jsonStream.metadata.location.accuracy) }); + } + } */ + if (type !== 3) { + // if KHEIRON + /* if (jsonStream.metadata.endpoint.type === 0) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).fcnt) }); + } + // if SIGFOX + if (jsonStream.metadata.endpoint.type === 5) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).seqNumber) }); + } + // if OBJENIOUS + if (jsonStream.metadata.endpoint.type === 6) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).count) }); + result.realTimes.push({ tagRef: "custom_data_raw_devEUI", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).device_properties.deveui) }); + } + */ + // Get message ID + const msgStr = parseInt(payload.substring(0, 2), 16); + + // Switch message ids + switch (msgStr) { + case 1: { + // Get meter + const battery = parseInt(payload.substring(2, 6), 16); + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery / 1000), + }); + + break; + } + case 3: { + // Get data + const battery = parseInt(payload.substring(2, 4), 16); + let temperature = parseInt(payload.substring(4, 8), 16); + const humidity = parseInt(payload.substring(8, 12), 16); + + // check if negative value + if (temperature >> 15 === 1) { + const reverseValue = temperature ^ 65535; + temperature = (reverseValue + 1) * -1; + } + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temperature / 100), + }); + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity / 100), + }); + break; + } + case 5: { + const testCounter = parseInt(payload.substring(2, 4), 16); + + result.realTimes.push({ + tagRef: "p_test", + timestamp: time, + tagValue: String(testCounter), + }); + + break; + } + case 9: { + // Get input + const di = parseInt(payload.substring(4, 6), 16); + const di1 = (di & 1) === 1; + const di2 = (di & 2) === 2; + + // get ouput + const dout = parseInt(payload.substring(2, 4), 16); + const do1 = (dout & 1) === 1; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DO1", + timestamp: time, + tagValue: String(do1), + }); + + break; + } + case 10: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + break; + } + case 11: { + break; + } + case 12: { + break; + } + case 13: { + break; + } + case 14: { + break; + } + case 21: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // Decode + temp = (temp - 33184) / 128; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + break; + } + case 20: { + // Get meter + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 48: { + // get realtime meter + const meter = parseInt(payload.substring(42, 50), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + + // get historic meter + const meter1 = parseInt(payload.substring(34, 42), 16); + const meter2 = parseInt(payload.substring(26, 34), 16); + const meter3 = parseInt(payload.substring(18, 26), 16); + const meter4 = parseInt(payload.substring(10, 18), 16); + const meter5 = parseInt(payload.substring(2, 10), 16); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 1) / 1000, + tagValue: String(meter1), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 2) / 1000, + tagValue: String(meter2), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 3) / 1000, + tagValue: String(meter3), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 4) / 1000, + tagValue: String(meter4), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 5) / 1000, + tagValue: String(meter5), + }); + + break; + } + case 49: { + // get realtime meter + const meter = parseInt(payload.substring(2, 10), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + break; + } + case 55: { + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + break; + } + case 57: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // const a = new Date(time * 1000); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (600000 + i * 600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 58: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (1800000 + i * 1800000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 59: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (3600000 + i * 3600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 22: { + // Get meter + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(4, 6), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 15: { + break; + } + case 16: { + break; + } + case 17: { + break; + } + case 18: { + break; + } + case 23: { + // Get temperature & humidity + let temp = parseInt(payload.substring(2, 6), 16); + let humidity = parseInt(payload.substring(6, 10), 16); + + // Decode + temp = (temp * 175.72) / 65536 - 46.85; + humidity = (humidity * 125) / 65536 - 6; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity), + }); + break; + } + case 24: { + break; + } + case 30: { + break; + } + case 31: { + break; + } + case 32: { + break; + } + case 33: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 10 / 64240; + + result.realTimes.push({ + tagRef: "p_voltage", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 25: { + break; + } + case 34: { + break; + } + case 35: { + break; + } + case 36: { + break; + } + case 37: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 16 / 47584; + result.realTimes.push({ + tagRef: "p_current", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 27: { + break; + } + case 42: { + break; + } + case 43: { + break; + } + case 44: { + break; + } + case 45: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + break; + } + case 26: { + break; + } + case 38: { + break; + } + case 39: { + break; + } + case 40: { + break; + } + case 41: { + break; + } + case 83: { + break; + } + case 84: { + break; + } + case 85: { + break; + } + case 86: { + break; + } + case 87: { + break; + } + case 88: { + break; + } + case 89: { + break; + } + case 90: { + break; + } + case 91: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + let temp2 = parseInt(payload.substring(6, 10), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + + // check if error + if (temp2 !== 32768) { + // check if negative value + if (temp2 >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp2 = (reverseValue + 1) * -1; + } + + // apply coef + temp2 *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature2", + timestamp: time, + tagValue: String(temp2), + }); + } + + break; + } + case 19: { + // Get water leak state + const waterleak = parseInt(payload.substring(4, 6), 16); + + // save + result.realTimes.push({ + tagRef: "p_waterLeak", + timestamp: time, + tagValue: String(waterleak), + }); + break; + } + case 47: { + // get temperature + const temp = parseInt(payload.substring(14, 18), 16); + const p_temperature = (10.888 - Math.sqrt((-10.888) ** 2 + 4 * 0.00347 * (1777.3 - temp))) / (2 * -0.00347) + 30; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + + if (parseInt(payload.substring(18, 34), 16) !== 0) { + // get data for gps decoding + const a = parseInt(payload.substring(18, 20), 16) >> 4; + const b = ((parseInt(payload.substring(18, 20), 16) << 4) & 255) >> 4; + const c = parseInt(payload.substring(20, 22), 16) >> 4; + const d = ((parseInt(payload.substring(20, 22), 16) << 4) & 255) >> 4; + const e = parseInt(payload.substring(22, 24), 16) >> 4; + const f = ((parseInt(payload.substring(22, 24), 16) << 4) & 255) >> 4; + const g = parseInt(payload.substring(24, 26), 16) >> 4; + const h = ((parseInt(payload.substring(24, 26), 16) << 4) & 255) >> 4; + const i = parseInt(payload.substring(26, 28), 16) >> 4; + const j = ((parseInt(payload.substring(26, 28), 16) << 4) & 255) >> 4; + const k = parseInt(payload.substring(28, 30), 16) >> 4; + const l = ((parseInt(payload.substring(28, 30), 16) << 4) & 255) >> 4; + const m = parseInt(payload.substring(30, 32), 16) >> 4; + const n = ((parseInt(payload.substring(30, 32), 16) << 4) & 255) >> 4; + const o = parseInt(payload.substring(32, 34), 16) >> 4; + // const p = ((parseInt(payload.substring(32, 34), 16) << 4) & 255) >> 6; + const q = ((parseInt(payload.substring(32, 34), 16) << 6) & 255) >> 7; + const r = ((parseInt(payload.substring(32, 34), 16) << 7) & 255) >> 7; + // get latitude and longitude + const latitude = (2 * q - 1) * (10 * a + b + c / 6 + d / 60 + e / 600 + f / 6000 + g / 60000); + const longitude = (2 * r - 1) * (100 * h + 10 * i + j + k / 6 + l / 60 + m / 600 + n / 6000 + o / 60000); + + // save + result.realTimes.push({ + tagRef: "p_latitude", + timestamp: time, + tagValue: String(latitude), + }); + result.realTimes.push({ + tagRef: "p_longitude", + timestamp: time, + tagValue: String(longitude), + }); + } + + break; + } + case 50: { + const p_vibration = parseInt(payload.substring(4, 6), 16); + const p_ils = parseInt(payload.substring(6, 8), 16); + const p_temperature = (2103 - parseInt(payload.substring(8, 12), 16)) / 10.9; + + // save + result.realTimes.push({ + tagRef: "p_vibration", + timestamp: time, + tagValue: String(p_vibration), + }); + result.realTimes.push({ + tagRef: "p_ils", + timestamp: time, + tagValue: String(p_ils), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + break; + } + case 51: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + break; + } + case 52: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get count + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(parseInt(payload.substring(4, 12), 16)), + }); + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(parseInt(payload.substring(12, 20), 16)), + }); + break; + } + case 53: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 54: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 62: { + // Get Absence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(0), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + const valueWorZ = parseInt(payload.substring(10, 14), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + + // Save WorZ + result.realTimes.push({ + tagRef: "p_WorZ", + timestamp: time, + tagValue: String(valueWorZ), + }); + break; + } + case 63: { + // Get Presence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(1), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + break; + } + case 65: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + break; + } + case 66: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + break; + } + case 67: { + // save + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time - 1, + tagValue: String(1), + context: [], + }); + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time, + tagValue: String(0), + context: [], + }); + break; + } + case 78: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + // get count 1 + const count1 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 79: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(14, 22), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 80: { + // get count 1 + const count1 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 81: { + // get count 3 + const count3 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count3", + timestamp: time, + tagValue: String(count3), + }); + + // get count 4 + const count4 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count4", + timestamp: time, + tagValue: String(count4), + }); + break; + } + case 82: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 93: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(2 + 8 * j, 10 + 8 * j), 16)), + }); + } + break; + } + case 94: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(-2 + 8 * j, 6 + 8 * j), 16)), + }); + } + break; + } + case 95: { + // get count 5 + const count5 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count5", + timestamp: time, + tagValue: String(count5), + }); + + // get count 6 + const count6 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count6", + timestamp: time, + tagValue: String(count6), + }); + break; + } + case 96: { + // get count 7 + const count7 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count7", + timestamp: time, + tagValue: String(count7), + }); + + // get count 8 + const count8 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count8", + timestamp: time, + tagValue: String(count8), + }); + break; + } + case 2: + case 4: + case 6: + case 7: + case 8: + case 46: + case 56: + case 60: + case 61: + case 64: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 92: + case 97: + case 98: + case 99: + default: + break; + } + } + // Return result + return result; +} + +function ToTagoFormat(object_item, serie) { + const historics = []; + const events = []; + const realTimes = []; + // eslint-disable-next-line guard-for-in + for (const key in object_item.realTimes) { + realTimes.push({ + variable: `realTimes_${object_item.realTimes[key].tagRef}`.toLowerCase(), + value: object_item.realTimes[key].tagValue, + time: object_item.realTimes[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.events) { + events.push({ + variable: `events_${object_item.events[key].tagRef}`.toLowerCase(), + value: object_item.events[key].tagValue, + time: object_item.events[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.historics) { + historics.push({ + variable: `historics_${object_item.historics[key].tagRef}`.toLowerCase(), + value: object_item.historics[key].tagValue, + time: object_item.historics[key].timestamp, + serie, + }); + } + const result = historics.concat(events, realTimes); + + return result; +} + +/* let payload = [ + { variable: "payload", value: "5e7f000000003f00000040000000410000004200000043000000440000004500000046" }, + { variable: "type", value: 0 }, + { variable: "timestamp", value: 1605614318410 }, +]; */ + +const data = payload.find((x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data"); +const type = payload.find((x) => x.variable === "type"); +const timestamp = payload.find((x) => x.variable === "timestamp"); + +if (data) { + // const buffer = Buffer.from(data.value, "hex"); + const serie = new Date().getTime(); + // payload = decodeStream(data.value, type.value, timestamp.value); + payload = ToTagoFormat(decodeStream(data.value, type.value, timestamp.value), serie); +} + +// eslint-disable-next-line no-console +// console.log(payload); diff --git a/decoders/connector/atim/tst/assets/logo.png b/decoders/connector/atim/tst/assets/logo.png new file mode 100644 index 00000000..5c815144 Binary files /dev/null and b/decoders/connector/atim/tst/assets/logo.png differ diff --git a/decoders/connector/atim/tst/connector.jsonc b/decoders/connector/atim/tst/connector.jsonc new file mode 100644 index 00000000..b620309f --- /dev/null +++ b/decoders/connector/atim/tst/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Atim TST", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/atim/tst/description.md b/decoders/connector/atim/tst/description.md new file mode 100644 index 00000000..ae8a05f6 --- /dev/null +++ b/decoders/connector/atim/tst/description.md @@ -0,0 +1 @@ +Network coverage quality tester over LoRaWAN or Sigfox \ No newline at end of file diff --git a/decoders/connector/atim/tst/v1.0.0/payload-config.jsonc b/decoders/connector/atim/tst/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..4d2461cd --- /dev/null +++ b/decoders/connector/atim/tst/v1.0.0/payload-config.jsonc @@ -0,0 +1,26 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "This tester enables you to accurately evaluate the LoRaWAN/Sigfox network coverage quality. A multicolored LED displays a color according to the coverage quality at your location. Test frames emitted by the push of the central button are visible on our Platform, and on your national LoRaWAN/Sigfox operator’s backend. Very useful for discovering and mastering LPWAN technologies and the realization of \"Proof of Concept\".\n\n**Technical Data**\n* Dimensions: 90 x 45 x 15 mm\n* Housing: Plastic\n* Power supply: Internal LiPo Battery 325mAh, rechargeable via micro USB port (micro USB wire not included)\n* Weight: 30g\n* Frequency: 868 MHz\n* Power: 25 mW (14 dBm)\n", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/sigfox/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/atim/tst/v1.0.0/payload.js b/decoders/connector/atim/tst/v1.0.0/payload.js new file mode 100644 index 00000000..58709750 --- /dev/null +++ b/decoders/connector/atim/tst/v1.0.0/payload.js @@ -0,0 +1,1332 @@ +/* eslint-disable no-plusplus */ +/* eslint-disable no-bitwise */ +function decodeStream(payload, type, timestamp) { + // Init result + const result = { historics: [], events: [], realTimes: [] }; + + // Parse stream + // const jsonStream = JSON.parse(stream); + + // Get time and payload + // const time = new Date(jsonStream.data.timestamp * 1000) / 1000; + const time = new Date(timestamp * 1000) / 1000; + // const { payload } = jsonStream.data; + + // Save network informations + result.realTimes.push({ tagRef: "sys_data_payload", timestamp: time, tagValue: String(payload) }); + result.realTimes.push({ tagRef: "sys_data_timestamp", timestamp: time, tagValue: String(time) }); + /* if (jsonStream.data !== undefined) { + result.realTimes.push({ tagRef: "sys_data_type", timestamp: time, tagValue: String(jsonStream.data.type) }); + result.realTimes.push({ tagRef: "sys_data_raw", timestamp: time, tagValue: String(jsonStream.data.raw) }); + } + if (jsonStream.metadata !== undefined) { + result.realTimes.push({ tagRef: "sys_metadata_lastStreamTimestampUtc", timestamp: time, tagValue: String(jsonStream.metadata.lastStreamTimestampUtc) }); + if (jsonStream.metadata.device !== undefined) { + result.realTimes.push({ tagRef: "sys_device_sn", timestamp: time, tagValue: String(jsonStream.metadata.device.serialNumber) }); + result.realTimes.push({ tagRef: "sys_device_name", timestamp: time, tagValue: String(jsonStream.metadata.device.name) }); + } + if (jsonStream.metadata.endpoint !== undefined) { + result.realTimes.push({ tagRef: "sys_endpoint_name", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.name) }); + result.realTimes.push({ tagRef: "sys_endpoint_type", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.type) }); + } + if (jsonStream.metadata.network !== undefined) { + result.realTimes.push({ tagRef: "sys_network_port", timestamp: time, tagValue: String(jsonStream.metadata.network.port) }); + result.realTimes.push({ tagRef: "sys_network_linkQuality", timestamp: time, tagValue: String(jsonStream.metadata.network.linkQuality) }); + } + if (jsonStream.metadata.location !== undefined) { + result.realTimes.push({ tagRef: "sys_location_source", timestamp: time, tagValue: String(jsonStream.metadata.location.source) }); + result.realTimes.push({ tagRef: "sys_location_latitude", timestamp: time, tagValue: String(jsonStream.metadata.location.latitude) }); + result.realTimes.push({ tagRef: "sys_location_longitude", timestamp: time, tagValue: String(jsonStream.metadata.location.longitude) }); + result.realTimes.push({ tagRef: "sys_location_accuracy", timestamp: time, tagValue: String(jsonStream.metadata.location.accuracy) }); + } + } */ + if (type !== 3) { + // if KHEIRON + /* if (jsonStream.metadata.endpoint.type === 0) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).fcnt) }); + } + // if SIGFOX + if (jsonStream.metadata.endpoint.type === 5) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).seqNumber) }); + } + // if OBJENIOUS + if (jsonStream.metadata.endpoint.type === 6) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).count) }); + result.realTimes.push({ tagRef: "custom_data_raw_devEUI", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).device_properties.deveui) }); + } + */ + // Get message ID + const msgStr = parseInt(payload.substring(0, 2), 16); + + // Switch message ids + switch (msgStr) { + case 1: { + // Get meter + const battery = parseInt(payload.substring(2, 6), 16); + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery / 1000), + }); + + break; + } + case 3: { + // Get data + const battery = parseInt(payload.substring(2, 4), 16); + let temperature = parseInt(payload.substring(4, 8), 16); + const humidity = parseInt(payload.substring(8, 12), 16); + + // check if negative value + if (temperature >> 15 === 1) { + const reverseValue = temperature ^ 65535; + temperature = (reverseValue + 1) * -1; + } + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temperature / 100), + }); + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity / 100), + }); + break; + } + case 5: { + const testCounter = parseInt(payload.substring(2, 4), 16); + + result.realTimes.push({ + tagRef: "p_test", + timestamp: time, + tagValue: String(testCounter), + }); + + break; + } + case 9: { + // Get input + const di = parseInt(payload.substring(4, 6), 16); + const di1 = (di & 1) === 1; + const di2 = (di & 2) === 2; + + // get ouput + const dout = parseInt(payload.substring(2, 4), 16); + const do1 = (dout & 1) === 1; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DO1", + timestamp: time, + tagValue: String(do1), + }); + + break; + } + case 10: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + break; + } + case 11: { + break; + } + case 12: { + break; + } + case 13: { + break; + } + case 14: { + break; + } + case 21: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // Decode + temp = (temp - 33184) / 128; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + break; + } + case 20: { + // Get meter + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 48: { + // get realtime meter + const meter = parseInt(payload.substring(42, 50), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + + // get historic meter + const meter1 = parseInt(payload.substring(34, 42), 16); + const meter2 = parseInt(payload.substring(26, 34), 16); + const meter3 = parseInt(payload.substring(18, 26), 16); + const meter4 = parseInt(payload.substring(10, 18), 16); + const meter5 = parseInt(payload.substring(2, 10), 16); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 1) / 1000, + tagValue: String(meter1), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 2) / 1000, + tagValue: String(meter2), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 3) / 1000, + tagValue: String(meter3), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 4) / 1000, + tagValue: String(meter4), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 5) / 1000, + tagValue: String(meter5), + }); + + break; + } + case 49: { + // get realtime meter + const meter = parseInt(payload.substring(2, 10), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + break; + } + case 55: { + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + break; + } + case 57: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // const a = new Date(time * 1000); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (600000 + i * 600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 58: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (1800000 + i * 1800000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 59: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (3600000 + i * 3600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 22: { + // Get meter + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(4, 6), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 15: { + break; + } + case 16: { + break; + } + case 17: { + break; + } + case 18: { + break; + } + case 23: { + // Get temperature & humidity + let temp = parseInt(payload.substring(2, 6), 16); + let humidity = parseInt(payload.substring(6, 10), 16); + + // Decode + temp = (temp * 175.72) / 65536 - 46.85; + humidity = (humidity * 125) / 65536 - 6; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity), + }); + break; + } + case 24: { + break; + } + case 30: { + break; + } + case 31: { + break; + } + case 32: { + break; + } + case 33: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 10 / 64240; + + result.realTimes.push({ + tagRef: "p_voltage", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 25: { + break; + } + case 34: { + break; + } + case 35: { + break; + } + case 36: { + break; + } + case 37: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 16 / 47584; + result.realTimes.push({ + tagRef: "p_current", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 27: { + break; + } + case 42: { + break; + } + case 43: { + break; + } + case 44: { + break; + } + case 45: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + break; + } + case 26: { + break; + } + case 38: { + break; + } + case 39: { + break; + } + case 40: { + break; + } + case 41: { + break; + } + case 83: { + break; + } + case 84: { + break; + } + case 85: { + break; + } + case 86: { + break; + } + case 87: { + break; + } + case 88: { + break; + } + case 89: { + break; + } + case 90: { + break; + } + case 91: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + let temp2 = parseInt(payload.substring(6, 10), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + + // check if error + if (temp2 !== 32768) { + // check if negative value + if (temp2 >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp2 = (reverseValue + 1) * -1; + } + + // apply coef + temp2 *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature2", + timestamp: time, + tagValue: String(temp2), + }); + } + + break; + } + case 19: { + // Get water leak state + const waterleak = parseInt(payload.substring(4, 6), 16); + + // save + result.realTimes.push({ + tagRef: "p_waterLeak", + timestamp: time, + tagValue: String(waterleak), + }); + break; + } + case 47: { + // get temperature + const temp = parseInt(payload.substring(14, 18), 16); + const p_temperature = (10.888 - Math.sqrt((-10.888) ** 2 + 4 * 0.00347 * (1777.3 - temp))) / (2 * -0.00347) + 30; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + + if (parseInt(payload.substring(18, 34), 16) !== 0) { + // get data for gps decoding + const a = parseInt(payload.substring(18, 20), 16) >> 4; + const b = ((parseInt(payload.substring(18, 20), 16) << 4) & 255) >> 4; + const c = parseInt(payload.substring(20, 22), 16) >> 4; + const d = ((parseInt(payload.substring(20, 22), 16) << 4) & 255) >> 4; + const e = parseInt(payload.substring(22, 24), 16) >> 4; + const f = ((parseInt(payload.substring(22, 24), 16) << 4) & 255) >> 4; + const g = parseInt(payload.substring(24, 26), 16) >> 4; + const h = ((parseInt(payload.substring(24, 26), 16) << 4) & 255) >> 4; + const i = parseInt(payload.substring(26, 28), 16) >> 4; + const j = ((parseInt(payload.substring(26, 28), 16) << 4) & 255) >> 4; + const k = parseInt(payload.substring(28, 30), 16) >> 4; + const l = ((parseInt(payload.substring(28, 30), 16) << 4) & 255) >> 4; + const m = parseInt(payload.substring(30, 32), 16) >> 4; + const n = ((parseInt(payload.substring(30, 32), 16) << 4) & 255) >> 4; + const o = parseInt(payload.substring(32, 34), 16) >> 4; + // const p = ((parseInt(payload.substring(32, 34), 16) << 4) & 255) >> 6; + const q = ((parseInt(payload.substring(32, 34), 16) << 6) & 255) >> 7; + const r = ((parseInt(payload.substring(32, 34), 16) << 7) & 255) >> 7; + // get latitude and longitude + const latitude = (2 * q - 1) * (10 * a + b + c / 6 + d / 60 + e / 600 + f / 6000 + g / 60000); + const longitude = (2 * r - 1) * (100 * h + 10 * i + j + k / 6 + l / 60 + m / 600 + n / 6000 + o / 60000); + + // save + result.realTimes.push({ + tagRef: "p_latitude", + timestamp: time, + tagValue: String(latitude), + }); + result.realTimes.push({ + tagRef: "p_longitude", + timestamp: time, + tagValue: String(longitude), + }); + } + + break; + } + case 50: { + const p_vibration = parseInt(payload.substring(4, 6), 16); + const p_ils = parseInt(payload.substring(6, 8), 16); + const p_temperature = (2103 - parseInt(payload.substring(8, 12), 16)) / 10.9; + + // save + result.realTimes.push({ + tagRef: "p_vibration", + timestamp: time, + tagValue: String(p_vibration), + }); + result.realTimes.push({ + tagRef: "p_ils", + timestamp: time, + tagValue: String(p_ils), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + break; + } + case 51: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + break; + } + case 52: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get count + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(parseInt(payload.substring(4, 12), 16)), + }); + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(parseInt(payload.substring(12, 20), 16)), + }); + break; + } + case 53: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 54: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 62: { + // Get Absence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(0), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + const valueWorZ = parseInt(payload.substring(10, 14), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + + // Save WorZ + result.realTimes.push({ + tagRef: "p_WorZ", + timestamp: time, + tagValue: String(valueWorZ), + }); + break; + } + case 63: { + // Get Presence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(1), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + break; + } + case 65: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + break; + } + case 66: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + break; + } + case 67: { + // save + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time - 1, + tagValue: String(1), + context: [], + }); + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time, + tagValue: String(0), + context: [], + }); + break; + } + case 78: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + // get count 1 + const count1 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 79: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(14, 22), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 80: { + // get count 1 + const count1 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 81: { + // get count 3 + const count3 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count3", + timestamp: time, + tagValue: String(count3), + }); + + // get count 4 + const count4 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count4", + timestamp: time, + tagValue: String(count4), + }); + break; + } + case 82: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 93: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(2 + 8 * j, 10 + 8 * j), 16)), + }); + } + break; + } + case 94: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(-2 + 8 * j, 6 + 8 * j), 16)), + }); + } + break; + } + case 95: { + // get count 5 + const count5 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count5", + timestamp: time, + tagValue: String(count5), + }); + + // get count 6 + const count6 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count6", + timestamp: time, + tagValue: String(count6), + }); + break; + } + case 96: { + // get count 7 + const count7 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count7", + timestamp: time, + tagValue: String(count7), + }); + + // get count 8 + const count8 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count8", + timestamp: time, + tagValue: String(count8), + }); + break; + } + case 2: + case 4: + case 6: + case 7: + case 8: + case 46: + case 56: + case 60: + case 61: + case 64: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 92: + case 97: + case 98: + case 99: + default: + break; + } + } + // Return result + return result; +} + +function ToTagoFormat(object_item, serie) { + const historics = []; + const events = []; + const realTimes = []; + // eslint-disable-next-line guard-for-in + for (const key in object_item.realTimes) { + realTimes.push({ + variable: `realTimes_${object_item.realTimes[key].tagRef}`.toLowerCase(), + value: object_item.realTimes[key].tagValue, + time: object_item.realTimes[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.events) { + events.push({ + variable: `events_${object_item.events[key].tagRef}`.toLowerCase(), + value: object_item.events[key].tagValue, + time: object_item.events[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.historics) { + historics.push({ + variable: `historics_${object_item.historics[key].tagRef}`.toLowerCase(), + value: object_item.historics[key].tagValue, + time: object_item.historics[key].timestamp, + serie, + }); + } + const result = historics.concat(events, realTimes); + + return result; +} + +/* let payload = [ + { variable: "payload", value: "5e7f000000003f00000040000000410000004200000043000000440000004500000046" }, + { variable: "type", value: 0 }, + { variable: "timestamp", value: 1605614318410 }, +]; */ + +const data = payload.find((x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data"); +const type = payload.find((x) => x.variable === "type"); +const timestamp = payload.find((x) => x.variable === "timestamp"); + +if (data) { + // const buffer = Buffer.from(data.value, "hex"); + const serie = new Date().getTime(); + // payload = decodeStream(data.value, type.value, timestamp.value); + payload = ToTagoFormat(decodeStream(data.value, type.value, timestamp.value), serie); +} + +// eslint-disable-next-line no-console +// console.log(payload); diff --git a/decoders/connector/atim/wl/assets/logo.png b/decoders/connector/atim/wl/assets/logo.png new file mode 100644 index 00000000..318570b8 Binary files /dev/null and b/decoders/connector/atim/wl/assets/logo.png differ diff --git a/decoders/connector/atim/wl/connector.jsonc b/decoders/connector/atim/wl/connector.jsonc new file mode 100644 index 00000000..d4101e39 --- /dev/null +++ b/decoders/connector/atim/wl/connector.jsonc @@ -0,0 +1,13 @@ +{ + "$schema": "../../../../schema/connector.json", + "name": "Atim WL", + "images": { + "logo": "./assets/logo.png" + }, + "versions": { + "v1.0.0": { + "src": "./v1.0.0/payload.js", + "manifest": "./v1.0.0/payload-config.jsonc" + } + } +} diff --git a/decoders/connector/atim/wl/description.md b/decoders/connector/atim/wl/description.md new file mode 100644 index 00000000..3ae22aea --- /dev/null +++ b/decoders/connector/atim/wl/description.md @@ -0,0 +1 @@ +Leakage detection sensor over LoRaWAN or Sigfox \ No newline at end of file diff --git a/decoders/connector/atim/wl/v1.0.0/payload-config.jsonc b/decoders/connector/atim/wl/v1.0.0/payload-config.jsonc new file mode 100644 index 00000000..57411067 --- /dev/null +++ b/decoders/connector/atim/wl/v1.0.0/payload-config.jsonc @@ -0,0 +1,26 @@ +{ + "$schema": "../../../../../schema/connector_details.json", + "description": "../description.md", + "install_text": "The ACW-WL allows to remotely prevent floods harsh consequences. The sensor detects when the level is critical and sends to the alert to the LPWAN networks.\nSupplied with a ½ wave antenna with 4m cable, a keep alive frame is sent once a week. LED display when radio transmission takes place. By default, the device is set on 3 flood detection a day. Compatible with Sigfox repeater (ACW-GW).\n\n**Technical Data**\n* Dimensions: 160 x 53 x 53 mm\n* Antenna: Integrated (¼ wave)\n* Temperature probe: Digital - Cable length: 2 m\n* Temperature: -20°C to +55°C (operation), -40°C to +70°C (storage)\n* Mounts to: Wall, tube or pole, DIN rail\n* Housing: IP65\n* Power supply: 2x battery-packs (14,4 Ah)\n* Weight: 210g\n* Frequency: 865-870 MHz\n* Power: 25 mW (14 dBm)\n* Transfer rate: Sigfox: 100 bit/s, LoRaWAN: 300 bit/s to 10 kbit/s", + "install_end_text": "", + "device_annotation": "", + "device_parameters": [], + "networks": [ + "../../../../network/lorawan-actility/v1.0.0/payload.js", + "../../../../network/lorawan-chirpstack/v1.0.0/payload.js", + "../../../../network/lorawan-citykinect/v1.0.0/payload.js", + "../../../../network/lorawan-everynet/v1.0.0/payload.js", + "../../../../network/lorawan-kerlink/v1.0.0/payload.js", + "../../../../network/lorawan-loriot-/v1.0.0/payload.js", + "../../../../network/lorawan-machineq/v1.0.0/payload.js", + "../../../../network/lorawan-orbiwise/v1.0.0/payload.js", + "../../../../network/lorawan-senet/v1.0.0/payload.js", + "../../../../network/lorawan-senra/v1.0.0/payload.js", + "../../../../network/lorawan-swisscom/v1.0.0/payload.js", + "../../../../network/lorawan-tektelic/v1.0.0/payload.js", + "../../../../network/lorawan-ttittn-v3/v1.0.0/payload.js", + "../../../../network/sigfox/v1.0.0/payload.js", + "../../../../network/lorawan-helium/v1.0.0/payload.js", + "../../../../network/lorawan-brdot-/v1.0.0/payload.js" + ] +} \ No newline at end of file diff --git a/decoders/connector/atim/wl/v1.0.0/payload.js b/decoders/connector/atim/wl/v1.0.0/payload.js new file mode 100644 index 00000000..58709750 --- /dev/null +++ b/decoders/connector/atim/wl/v1.0.0/payload.js @@ -0,0 +1,1332 @@ +/* eslint-disable no-plusplus */ +/* eslint-disable no-bitwise */ +function decodeStream(payload, type, timestamp) { + // Init result + const result = { historics: [], events: [], realTimes: [] }; + + // Parse stream + // const jsonStream = JSON.parse(stream); + + // Get time and payload + // const time = new Date(jsonStream.data.timestamp * 1000) / 1000; + const time = new Date(timestamp * 1000) / 1000; + // const { payload } = jsonStream.data; + + // Save network informations + result.realTimes.push({ tagRef: "sys_data_payload", timestamp: time, tagValue: String(payload) }); + result.realTimes.push({ tagRef: "sys_data_timestamp", timestamp: time, tagValue: String(time) }); + /* if (jsonStream.data !== undefined) { + result.realTimes.push({ tagRef: "sys_data_type", timestamp: time, tagValue: String(jsonStream.data.type) }); + result.realTimes.push({ tagRef: "sys_data_raw", timestamp: time, tagValue: String(jsonStream.data.raw) }); + } + if (jsonStream.metadata !== undefined) { + result.realTimes.push({ tagRef: "sys_metadata_lastStreamTimestampUtc", timestamp: time, tagValue: String(jsonStream.metadata.lastStreamTimestampUtc) }); + if (jsonStream.metadata.device !== undefined) { + result.realTimes.push({ tagRef: "sys_device_sn", timestamp: time, tagValue: String(jsonStream.metadata.device.serialNumber) }); + result.realTimes.push({ tagRef: "sys_device_name", timestamp: time, tagValue: String(jsonStream.metadata.device.name) }); + } + if (jsonStream.metadata.endpoint !== undefined) { + result.realTimes.push({ tagRef: "sys_endpoint_name", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.name) }); + result.realTimes.push({ tagRef: "sys_endpoint_type", timestamp: time, tagValue: String(jsonStream.metadata.endpoint.type) }); + } + if (jsonStream.metadata.network !== undefined) { + result.realTimes.push({ tagRef: "sys_network_port", timestamp: time, tagValue: String(jsonStream.metadata.network.port) }); + result.realTimes.push({ tagRef: "sys_network_linkQuality", timestamp: time, tagValue: String(jsonStream.metadata.network.linkQuality) }); + } + if (jsonStream.metadata.location !== undefined) { + result.realTimes.push({ tagRef: "sys_location_source", timestamp: time, tagValue: String(jsonStream.metadata.location.source) }); + result.realTimes.push({ tagRef: "sys_location_latitude", timestamp: time, tagValue: String(jsonStream.metadata.location.latitude) }); + result.realTimes.push({ tagRef: "sys_location_longitude", timestamp: time, tagValue: String(jsonStream.metadata.location.longitude) }); + result.realTimes.push({ tagRef: "sys_location_accuracy", timestamp: time, tagValue: String(jsonStream.metadata.location.accuracy) }); + } + } */ + if (type !== 3) { + // if KHEIRON + /* if (jsonStream.metadata.endpoint.type === 0) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).fcnt) }); + } + // if SIGFOX + if (jsonStream.metadata.endpoint.type === 5) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).seqNumber) }); + } + // if OBJENIOUS + if (jsonStream.metadata.endpoint.type === 6) { + result.realTimes.push({ tagRef: "custom_data_raw_count", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).count) }); + result.realTimes.push({ tagRef: "custom_data_raw_devEUI", timestamp: time, tagValue: String(JSON.parse(jsonStream.data.raw).device_properties.deveui) }); + } + */ + // Get message ID + const msgStr = parseInt(payload.substring(0, 2), 16); + + // Switch message ids + switch (msgStr) { + case 1: { + // Get meter + const battery = parseInt(payload.substring(2, 6), 16); + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery / 1000), + }); + + break; + } + case 3: { + // Get data + const battery = parseInt(payload.substring(2, 4), 16); + let temperature = parseInt(payload.substring(4, 8), 16); + const humidity = parseInt(payload.substring(8, 12), 16); + + // check if negative value + if (temperature >> 15 === 1) { + const reverseValue = temperature ^ 65535; + temperature = (reverseValue + 1) * -1; + } + + result.realTimes.push({ + tagRef: "p_battery", + timestamp: time, + tagValue: String(battery), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temperature / 100), + }); + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity / 100), + }); + break; + } + case 5: { + const testCounter = parseInt(payload.substring(2, 4), 16); + + result.realTimes.push({ + tagRef: "p_test", + timestamp: time, + tagValue: String(testCounter), + }); + + break; + } + case 9: { + // Get input + const di = parseInt(payload.substring(4, 6), 16); + const di1 = (di & 1) === 1; + const di2 = (di & 2) === 2; + + // get ouput + const dout = parseInt(payload.substring(2, 4), 16); + const do1 = (dout & 1) === 1; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DO1", + timestamp: time, + tagValue: String(do1), + }); + + break; + } + case 10: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + break; + } + case 11: { + break; + } + case 12: { + break; + } + case 13: { + break; + } + case 14: { + break; + } + case 21: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // Decode + temp = (temp - 33184) / 128; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + break; + } + case 20: { + // Get meter + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 48: { + // get realtime meter + const meter = parseInt(payload.substring(42, 50), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + + // get historic meter + const meter1 = parseInt(payload.substring(34, 42), 16); + const meter2 = parseInt(payload.substring(26, 34), 16); + const meter3 = parseInt(payload.substring(18, 26), 16); + const meter4 = parseInt(payload.substring(10, 18), 16); + const meter5 = parseInt(payload.substring(2, 10), 16); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 1) / 1000, + tagValue: String(meter1), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 2) / 1000, + tagValue: String(meter2), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 3) / 1000, + tagValue: String(meter3), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 4) / 1000, + tagValue: String(meter4), + }); + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - 600000 * 5) / 1000, + tagValue: String(meter5), + }); + + break; + } + case 49: { + // get realtime meter + const meter = parseInt(payload.substring(2, 10), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter), + }); + break; + } + case 55: { + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + break; + } + case 57: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // const a = new Date(time * 1000); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (600000 + i * 600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 58: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (1800000 + i * 1800000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 59: { + // get realtime meter + let meter1 = parseInt(payload.substring(2, 7), 16); + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + // get historic meter + for (let i = 0; i < 5; i++) { + // remove delta from reference meter + meter1 -= parseInt(payload.substring(7 + i * 3, 10 + i * 3), 16); + + // add to historics (remove 10min to the date of the reference meter) + result.historics.push({ + tagRef: "p_count1", + timestamp: new Date(time * 1000 - (3600000 + i * 3600000)) / 1000, + tagValue: String(meter1), + }); + } + break; + } + case 22: { + // Get meter + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 4) === 4; + const di2 = (di & 8) === 8; + const di3 = (di & 32) === 32; + const di4 = (di & 128) === 128; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + result.realTimes.push({ + tagRef: "p_DI3", + timestamp: time, + tagValue: String(di3), + }); + + result.realTimes.push({ + tagRef: "p_DI4", + timestamp: time, + tagValue: String(di4), + }); + + const meter1 = parseInt(payload.substring(4, 12), 16); + const meter2 = parseInt(payload.substring(12, 20), 16); + + result.realTimes.push({ + tagRef: "p_wirecut", + timestamp: time, + tagValue: String((parseInt(payload.substring(4, 6), 16) & 1) === 1), + }); + + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(meter1), + }); + + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(meter2), + }); + break; + } + case 15: { + break; + } + case 16: { + break; + } + case 17: { + break; + } + case 18: { + break; + } + case 23: { + // Get temperature & humidity + let temp = parseInt(payload.substring(2, 6), 16); + let humidity = parseInt(payload.substring(6, 10), 16); + + // Decode + temp = (temp * 175.72) / 65536 - 46.85; + humidity = (humidity * 125) / 65536 - 6; + + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + + result.realTimes.push({ + tagRef: "p_humidity", + timestamp: time, + tagValue: String(humidity), + }); + break; + } + case 24: { + break; + } + case 30: { + break; + } + case 31: { + break; + } + case 32: { + break; + } + case 33: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 10 / 64240; + + result.realTimes.push({ + tagRef: "p_voltage", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 25: { + break; + } + case 34: { + break; + } + case 35: { + break; + } + case 36: { + break; + } + case 37: { + // Get input + const di = parseInt(payload.substring(2, 4), 16); + const di1 = (di & 32) === 32; + const di2 = (di & 16) === 16; + + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String(di1), + }); + + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String(di2), + }); + + // Get ana + let ana = parseInt(payload.substring(4, 8), 16); + + // Decode + ana *= 16 / 47584; + result.realTimes.push({ + tagRef: "p_current", + timestamp: time, + tagValue: String(ana), + }); + break; + } + case 27: { + break; + } + case 42: { + break; + } + case 43: { + break; + } + case 44: { + break; + } + case 45: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + break; + } + case 26: { + break; + } + case 38: { + break; + } + case 39: { + break; + } + case 40: { + break; + } + case 41: { + break; + } + case 83: { + break; + } + case 84: { + break; + } + case 85: { + break; + } + case 86: { + break; + } + case 87: { + break; + } + case 88: { + break; + } + case 89: { + break; + } + case 90: { + break; + } + case 91: { + // Get temperature + let temp = parseInt(payload.substring(2, 6), 16); + let temp2 = parseInt(payload.substring(6, 10), 16); + + // check if error + if (temp !== 32768) { + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // apply coef + temp *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp), + }); + } + + // check if error + if (temp2 !== 32768) { + // check if negative value + if (temp2 >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp2 = (reverseValue + 1) * -1; + } + + // apply coef + temp2 *= 0.0625; + + // save + result.realTimes.push({ + tagRef: "p_temperature2", + timestamp: time, + tagValue: String(temp2), + }); + } + + break; + } + case 19: { + // Get water leak state + const waterleak = parseInt(payload.substring(4, 6), 16); + + // save + result.realTimes.push({ + tagRef: "p_waterLeak", + timestamp: time, + tagValue: String(waterleak), + }); + break; + } + case 47: { + // get temperature + const temp = parseInt(payload.substring(14, 18), 16); + const p_temperature = (10.888 - Math.sqrt((-10.888) ** 2 + 4 * 0.00347 * (1777.3 - temp))) / (2 * -0.00347) + 30; + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + + if (parseInt(payload.substring(18, 34), 16) !== 0) { + // get data for gps decoding + const a = parseInt(payload.substring(18, 20), 16) >> 4; + const b = ((parseInt(payload.substring(18, 20), 16) << 4) & 255) >> 4; + const c = parseInt(payload.substring(20, 22), 16) >> 4; + const d = ((parseInt(payload.substring(20, 22), 16) << 4) & 255) >> 4; + const e = parseInt(payload.substring(22, 24), 16) >> 4; + const f = ((parseInt(payload.substring(22, 24), 16) << 4) & 255) >> 4; + const g = parseInt(payload.substring(24, 26), 16) >> 4; + const h = ((parseInt(payload.substring(24, 26), 16) << 4) & 255) >> 4; + const i = parseInt(payload.substring(26, 28), 16) >> 4; + const j = ((parseInt(payload.substring(26, 28), 16) << 4) & 255) >> 4; + const k = parseInt(payload.substring(28, 30), 16) >> 4; + const l = ((parseInt(payload.substring(28, 30), 16) << 4) & 255) >> 4; + const m = parseInt(payload.substring(30, 32), 16) >> 4; + const n = ((parseInt(payload.substring(30, 32), 16) << 4) & 255) >> 4; + const o = parseInt(payload.substring(32, 34), 16) >> 4; + // const p = ((parseInt(payload.substring(32, 34), 16) << 4) & 255) >> 6; + const q = ((parseInt(payload.substring(32, 34), 16) << 6) & 255) >> 7; + const r = ((parseInt(payload.substring(32, 34), 16) << 7) & 255) >> 7; + // get latitude and longitude + const latitude = (2 * q - 1) * (10 * a + b + c / 6 + d / 60 + e / 600 + f / 6000 + g / 60000); + const longitude = (2 * r - 1) * (100 * h + 10 * i + j + k / 6 + l / 60 + m / 600 + n / 6000 + o / 60000); + + // save + result.realTimes.push({ + tagRef: "p_latitude", + timestamp: time, + tagValue: String(latitude), + }); + result.realTimes.push({ + tagRef: "p_longitude", + timestamp: time, + tagValue: String(longitude), + }); + } + + break; + } + case 50: { + const p_vibration = parseInt(payload.substring(4, 6), 16); + const p_ils = parseInt(payload.substring(6, 8), 16); + const p_temperature = (2103 - parseInt(payload.substring(8, 12), 16)) / 10.9; + + // save + result.realTimes.push({ + tagRef: "p_vibration", + timestamp: time, + tagValue: String(p_vibration), + }); + result.realTimes.push({ + tagRef: "p_ils", + timestamp: time, + tagValue: String(p_ils), + }); + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(p_temperature), + }); + break; + } + case 51: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + break; + } + case 52: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get count + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(parseInt(payload.substring(4, 12), 16)), + }); + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(parseInt(payload.substring(12, 20), 16)), + }); + break; + } + case 53: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 54: { + // get DI + result.realTimes.push({ + tagRef: "p_DI1", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 1) === 1), + }); + result.realTimes.push({ + tagRef: "p_DI2", + timestamp: time, + tagValue: String((parseInt(payload.substring(2, 4), 16) & 2) === 2), + }); + + // get ana + result.realTimes.push({ + tagRef: "p_analogic1", + timestamp: time, + tagValue: String((parseInt(payload.substring(6, 10), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic2", + timestamp: time, + tagValue: String((parseInt(payload.substring(10, 14), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic3", + timestamp: time, + tagValue: String((parseInt(payload.substring(14, 18), 16) * 20) / 4095), + }); + result.realTimes.push({ + tagRef: "p_analogic4", + timestamp: time, + tagValue: String((parseInt(payload.substring(18, 22), 16) * 20) / 4095), + }); + break; + } + case 62: { + // Get Absence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(0), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + const valueWorZ = parseInt(payload.substring(10, 14), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + + // Save WorZ + result.realTimes.push({ + tagRef: "p_WorZ", + timestamp: time, + tagValue: String(valueWorZ), + }); + break; + } + case 63: { + // Get Presence + result.realTimes.push({ + tagRef: "p_presence", + timestamp: time, + tagValue: String(1), + }); + + // get values + const valueNorV = parseInt(payload.substring(2, 6), 16); + const valueMorY = parseInt(payload.substring(6, 10), 16); + + // Save NorV + result.realTimes.push({ + tagRef: "p_NorV", + timestamp: time, + tagValue: String(valueNorV), + }); + + // Save MorY + result.realTimes.push({ + tagRef: "p_MorY", + timestamp: time, + tagValue: String(valueMorY), + }); + break; + } + case 65: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + break; + } + case 66: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + coef *= 2; + } + + break; + } + case 67: { + // save + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time - 1, + tagValue: String(1), + context: [], + }); + result.events.push({ + tagRef: "p_choc_alm", + timestamp: time, + tagValue: String(0), + context: [], + }); + break; + } + case 78: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + // get count 1 + const count1 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 79: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(14, 22), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 80: { + // get count 1 + const count1 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + + // get count 2 + const count2 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count2", + timestamp: time, + tagValue: String(count2), + }); + break; + } + case 81: { + // get count 3 + const count3 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count3", + timestamp: time, + tagValue: String(count3), + }); + + // get count 4 + const count4 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count4", + timestamp: time, + tagValue: String(count4), + }); + break; + } + case 82: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get count 1 + const count1 = parseInt(payload.substring(6, 14), 16); + // save + result.realTimes.push({ + tagRef: "p_count1", + timestamp: time, + tagValue: String(count1), + }); + break; + } + case 93: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + // get temperature + let temp = parseInt(payload.substring(6, 10), 16); + + // check if negative value + if (temp >> 15 === 1) { + const reverseValue = temp ^ 65535; + temp = (reverseValue + 1) * -1; + } + + // save + result.realTimes.push({ + tagRef: "p_temperature", + timestamp: time, + tagValue: String(temp / 10), + }); + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(2 + 8 * j, 10 + 8 * j), 16)), + }); + } + break; + } + case 94: { + // get digital input + const di = parseInt(payload.substring(4, 6) + payload.substring(2, 4), 16); + // init coef + let coef = 1; + + for (let i = 1; i < 17; i++) { + // save + result.realTimes.push({ + tagRef: `p_DI${i}`, + timestamp: time, + tagValue: String((di & coef) === coef), + }); + + // multiply coef by 2 + coef *= 2; + } + + for (let j = 1; j < 9; j++) { + // save + result.realTimes.push({ + tagRef: `p_count${j}`, + timestamp: time, + tagValue: String(parseInt(payload.substring(-2 + 8 * j, 6 + 8 * j), 16)), + }); + } + break; + } + case 95: { + // get count 5 + const count5 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count5", + timestamp: time, + tagValue: String(count5), + }); + + // get count 6 + const count6 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count6", + timestamp: time, + tagValue: String(count6), + }); + break; + } + case 96: { + // get count 7 + const count7 = parseInt(payload.substring(2, 10), 16); + // save + result.realTimes.push({ + tagRef: "p_count7", + timestamp: time, + tagValue: String(count7), + }); + + // get count 8 + const count8 = parseInt(payload.substring(10, 18), 16); + // save + result.realTimes.push({ + tagRef: "p_count8", + timestamp: time, + tagValue: String(count8), + }); + break; + } + case 2: + case 4: + case 6: + case 7: + case 8: + case 46: + case 56: + case 60: + case 61: + case 64: + case 68: + case 69: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 76: + case 77: + case 92: + case 97: + case 98: + case 99: + default: + break; + } + } + // Return result + return result; +} + +function ToTagoFormat(object_item, serie) { + const historics = []; + const events = []; + const realTimes = []; + // eslint-disable-next-line guard-for-in + for (const key in object_item.realTimes) { + realTimes.push({ + variable: `realTimes_${object_item.realTimes[key].tagRef}`.toLowerCase(), + value: object_item.realTimes[key].tagValue, + time: object_item.realTimes[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.events) { + events.push({ + variable: `events_${object_item.events[key].tagRef}`.toLowerCase(), + value: object_item.events[key].tagValue, + time: object_item.events[key].timestamp, + serie, + }); + } + // eslint-disable-next-line guard-for-in + for (const key in object_item.historics) { + historics.push({ + variable: `historics_${object_item.historics[key].tagRef}`.toLowerCase(), + value: object_item.historics[key].tagValue, + time: object_item.historics[key].timestamp, + serie, + }); + } + const result = historics.concat(events, realTimes); + + return result; +} + +/* let payload = [ + { variable: "payload", value: "5e7f000000003f00000040000000410000004200000043000000440000004500000046" }, + { variable: "type", value: 0 }, + { variable: "timestamp", value: 1605614318410 }, +]; */ + +const data = payload.find((x) => x.variable === "payload_raw" || x.variable === "payload" || x.variable === "data"); +const type = payload.find((x) => x.variable === "type"); +const timestamp = payload.find((x) => x.variable === "timestamp"); + +if (data) { + // const buffer = Buffer.from(data.value, "hex"); + const serie = new Date().getTime(); + // payload = decodeStream(data.value, type.value, timestamp.value); + payload = ToTagoFormat(decodeStream(data.value, type.value, timestamp.value), serie); +} + +// eslint-disable-next-line no-console +// console.log(payload);