diff --git a/lib/payload.json b/lib/payload.json index e13714211e..7971c43742 100644 --- a/lib/payload.json +++ b/lib/payload.json @@ -152,7 +152,7 @@ "type": "object", "properties": { "leak": { - "type": "boolean", + "type": ["boolean", "string"], "description": "Leak detected" } }, diff --git a/vendor/quandify/cubicmeter-1-1-copper-codec.yaml b/vendor/quandify/cubicmeter-1-1-copper-codec.yaml new file mode 100644 index 0000000000..9980bdacb6 --- /dev/null +++ b/vendor/quandify/cubicmeter-1-1-copper-codec.yaml @@ -0,0 +1,4 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://thethingsstack.io/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: cubicmeter-1-1-uplink.js diff --git a/vendor/quandify/cubicmeter-1-1-copper.png b/vendor/quandify/cubicmeter-1-1-copper.png new file mode 100644 index 0000000000..3d034667a7 Binary files /dev/null and b/vendor/quandify/cubicmeter-1-1-copper.png differ diff --git a/vendor/quandify/cubicmeter-1-1-copper.yaml b/vendor/quandify/cubicmeter-1-1-copper.yaml new file mode 100644 index 0000000000..24b9516490 --- /dev/null +++ b/vendor/quandify/cubicmeter-1-1-copper.yaml @@ -0,0 +1,122 @@ +name: CubicMeter 1.1 Copper +description: Non-invasive water meter and leakage sensor + +# Hardware versions (optional) +hardwareVersions: + - version: '1.0' + numeric: 1 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: '1.0' + numeric: 1 + # Supported hardware versions (optional) + hardwareVersions: + - '1.0' # Must refer to hardwareVersions declared above + # LoRaWAN Device Profiles per region + # Supported regions: EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, RU864-870 + profiles: + EU863-870: + id: cubicmeter-1-1-profile-eu868 + lorawanCertified: true + codec: cubicmeter-1-1-copper-codec +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2, +# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, +# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - battery + - temperature + - water + +# Additional radios that this device has (optional) +# Valid values are: ble, nfc, wifi, cellular. +# additionalRadios: +# - + +# Bridge interfaces (optional) +# Valid values are: modbus, m-bus, can bus, rs-485, sdi-12, analog. +# bridgeInterfaces: +# - + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + width: 39 + length: 86 + height: 82 + +# Weight in grams (optional) +weight: 260 + +# Battery information (optional) +battery: + replaceable: false + type: 3.6V Li-SOCI2 + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: 5 + max: 30 + # Relative humidity (fraction of 1) + # relativeHumidity: + # min: + # max: + +# IP rating (optional) +# ipCode: + +# Key provisioning (optional) +# Valid values are: custom (user can configure keys), join server and manifest. +# keyProvisioning: +# - + +# Key programming (optional) +# Valid values are: bluetooth, nfc, wifi, serial (when the user has a serial interface to set the keys) +# and firmware (when the user should change the firmware to set the keys). +# keyProgramming: +# - + +# Key security (optional) +# Valid values are: none, read protected and secure element. +# keySecurity: + +# Firmware programming (optional) +# Valid values are: serial (when the user has a serial interface to update the firmware), fuota lorawan (when the device +# supports LoRaWAN FUOTA via standard interfaces) and fuota other (other wireless update mechanism). +# firmwareProgramming: +# - + +# Product and data sheet URLs (optional) +productURL: https://quandify.com/cubicmeter +# dataSheetURL: + +# Photos (MAKE SURE THE IMAGE HAS A TRANSPARENT BACKGROUND) +photos: + main: cubicmeter-1-1-copper.png +# Youtube or Vimeo Video (optional) +# videos: +# main: + +# Regulatory compliances (optional) +# compliances: +# safety: +# - body: +# norm: +# standard: +# radioEquipment: +# - body: +# norm: +# standard: +# version: +# - body: +# norm: +# standard: +# version: diff --git a/vendor/quandify/cubicmeter-1-1-plastic-codec.yaml b/vendor/quandify/cubicmeter-1-1-plastic-codec.yaml new file mode 100644 index 0000000000..9980bdacb6 --- /dev/null +++ b/vendor/quandify/cubicmeter-1-1-plastic-codec.yaml @@ -0,0 +1,4 @@ +# Uplink decoder decodes binary data uplink into a JSON object (optional) +# For documentation on writing encoders and decoders, see: https://thethingsstack.io/integrations/payload-formatters/javascript/ +uplinkDecoder: + fileName: cubicmeter-1-1-uplink.js diff --git a/vendor/quandify/cubicmeter-1-1-plastic.png b/vendor/quandify/cubicmeter-1-1-plastic.png new file mode 100644 index 0000000000..f5d9c19bba Binary files /dev/null and b/vendor/quandify/cubicmeter-1-1-plastic.png differ diff --git a/vendor/quandify/cubicmeter-1-1-plastic.yaml b/vendor/quandify/cubicmeter-1-1-plastic.yaml new file mode 100644 index 0000000000..a0df8a9642 --- /dev/null +++ b/vendor/quandify/cubicmeter-1-1-plastic.yaml @@ -0,0 +1,122 @@ +name: CubicMeter 1.1 Plastic +description: Non-invasive water meter and leakage sensor + +# Hardware versions (optional) +hardwareVersions: + - version: '1.0' + numeric: 1 + +# Firmware versions (at least one is mandatory) +firmwareVersions: + - # Firmware version + version: '1.0' + numeric: 1 + # Supported hardware versions (optional) + hardwareVersions: + - '1.0' # Must refer to hardwareVersions declared above + # LoRaWAN Device Profiles per region + # Supported regions: EU863-870, US902-928, AU915-928, AS923, CN779-787, EU433, CN470-510, KR920-923, IN865-867, RU864-870 + profiles: + EU863-870: + id: cubicmeter-1-1-profile-eu868 + lorawanCertified: true + codec: cubicmeter-1-1-plastic-codec +# Sensors that this device features (optional) +# Valid values are: +# 4-20 ma, accelerometer, altitude, analog input, auxiliary, barometer, battery, button, bvoc, co, co2, conductivity, +# current, digital input, dissolved oxygen, distance, dust, energy, gps, gyroscope, h2s, humidity, iaq, level, light, +# lightning, link, magnetometer, moisture, motion, no, no2, o3, particulate matter, ph, pir, pm2.5, pm10, potentiometer, +# power, precipitation, pressure, proximity, pulse count, pulse frequency, radar, rainfall, rssi, smart valve, snr, so2, +# solar radiation, sound, strain, surface temperature, temperature, tilt, time, tvoc, uv, vapor pressure, velocity, +# vibration, voltage, water potential, water, weight, wifi ssid, wind direction, wind speed. +sensors: + - battery + - temperature + - water + +# Additional radios that this device has (optional) +# Valid values are: ble, nfc, wifi, cellular. +# additionalRadios: +# - + +# Bridge interfaces (optional) +# Valid values are: modbus, m-bus, can bus, rs-485, sdi-12, analog. +# bridgeInterfaces: +# - + +# Dimensions in mm (optional) +# Use width, height, length and/or diameter +dimensions: + width: 39 + length: 86 + height: 82 + +# Weight in grams (optional) +weight: 260 + +# Battery information (optional) +battery: + replaceable: false + type: 3.6V Li-SOCI2 + +# Operating conditions (optional) +operatingConditions: + # Temperature (Celsius) + temperature: + min: 5 + max: 30 + # Relative humidity (fraction of 1) + # relativeHumidity: + # min: + # max: + +# IP rating (optional) +# ipCode: + +# Key provisioning (optional) +# Valid values are: custom (user can configure keys), join server and manifest. +# keyProvisioning: +# - + +# Key programming (optional) +# Valid values are: bluetooth, nfc, wifi, serial (when the user has a serial interface to set the keys) +# and firmware (when the user should change the firmware to set the keys). +# keyProgramming: +# - + +# Key security (optional) +# Valid values are: none, read protected and secure element. +# keySecurity: + +# Firmware programming (optional) +# Valid values are: serial (when the user has a serial interface to update the firmware), fuota lorawan (when the device +# supports LoRaWAN FUOTA via standard interfaces) and fuota other (other wireless update mechanism). +# firmwareProgramming: +# - + +# Product and data sheet URLs (optional) +productURL: https://quandify.com/cubicmeter +# dataSheetURL: + +# Photos (MAKE SURE THE IMAGE HAS A TRANSPARENT BACKGROUND) +photos: + main: cubicmeter-1-1-plastic.png +# Youtube or Vimeo Video (optional) +# videos: +# main: + +# Regulatory compliances (optional) +# compliances: +# safety: +# - body: +# norm: +# standard: +# radioEquipment: +# - body: +# norm: +# standard: +# version: +# - body: +# norm: +# standard: +# version: diff --git a/vendor/quandify/cubicmeter-1-1-profile-eu868.yaml b/vendor/quandify/cubicmeter-1-1-profile-eu868.yaml new file mode 100644 index 0000000000..4d76e827dc --- /dev/null +++ b/vendor/quandify/cubicmeter-1-1-profile-eu868.yaml @@ -0,0 +1,47 @@ +# LoRaWAN MAC version: 1.0, 1.0.1, 1.0.2, 1.0.3, 1.0.4 or 1.1 +macVersion: 1.0.2 +# LoRaWAN Regional Parameters version. Values depend on the LoRaWAN version: +# 1.0: TS001-1.0 +# 1.0.1: TS001-1.0.1 +# 1.0.2: RP001-1.0.2 or RP001-1.0.2-RevB +# 1.0.3: RP001-1.0.3-RevA +# 1.0.4: RP002-1.0.0, RP002-1.0.1, RP002-1.0.2, RP002-1.0.3 or RP002-1.0.4 +# 1.1: RP001-1.1-RevA or RP001-1.1-RevB +regionalParametersVersion: RP001-1.0.2-RevB + +# Whether the end device supports join (OTAA) or not (ABP) +supportsJoin: true +# If your device is an ABP device (supportsJoin is false), uncomment the following fields: +# RX1 delay +#rx1Delay: 5 +# RX1 data rate offset +#rx1DataRateOffset: 0 +# RX2 data rate index +#rx2DataRateIndex: 0 +# RX2 frequency (MHz) +#rx2Frequency: 869.525 +# Factory preset frequencies (MHz) +#factoryPresetFrequencies: [868.1, 868.3, 868.5, 867.1, 867.3, 867.5, 867.7, 867.9] + +# Maximum EIRP +maxEIRP: 16 +# Whether the end device supports 32-bit frame counters +supports32bitFCnt: true + +# Whether the end device supports class B +supportsClassB: false +# If your device supports class B, uncomment the following fields: +# Maximum delay for the end device to answer a MAC request or confirmed downlink frame (seconds) +#classBTimeout: 60 +# Ping slot period (seconds) +#pingSlotPeriod: 128 +# Ping slot data rate index +#pingSlotDataRateIndex: 0 +# Ping slot frequency (MHz). Set to 0 if the band supports ping slot frequency hopping. +#pingSlotFrequency: 869.525 + +# Whether the end device supports class C +supportsClassC: false +# If your device supports class C, uncomment the following fields: +# Maximum delay for the end device to answer a MAC request or confirmed downlink frame (seconds) +#classCTimeout: 60 diff --git a/vendor/quandify/cubicmeter-1-1-uplink.js b/vendor/quandify/cubicmeter-1-1-uplink.js new file mode 100644 index 0000000000..e933ccb4c2 --- /dev/null +++ b/vendor/quandify/cubicmeter-1-1-uplink.js @@ -0,0 +1,132 @@ +// Please read here on how to implement the proper codec: https://www.thethingsindustries.com/docs/integrations/payload-formatters/javascript/ + +// Cubicmeter 1.1 uplink decoder + +function decodeUplink(input) { + var decoded = {}; + + switch (input.fPort) { + case 1: // Status report + decoded = statusReportDecoder(input.bytes); + break; + } + + return { + data: { + fPort: input.fPort, + length: input.bytes.length, + hexBytes: toHexString(input.bytes), + type: getPacketType(input.fPort), + decoded: decoded, + }, + }; +} +var LSB = true; + +var statusReportDecoder = function (bytes) { + const buffer = new ArrayBuffer(bytes.length); + const data = new DataView(buffer); + if (bytes.length < 28) { + throw new Error('payload too short'); + } + for (const index in bytes) { + data.setUint8(index, bytes[index]); + } + + return { + errorCode: data.getUint16(4, LSB), // current error code + totalVolume: data.getUint32(6, LSB), // All-time aggregated water usage in litres + leakStatus: data.getUint8(22), // current water leakage state + batteryActive: decodeBatteryLevel(data.getUint8(23)), // battery mV active + batteryRecovered: decodeBatteryLevel(data.getUint8(24)), // battery mV recovered + waterTemperatureMin: decodeTemperature(data.getUint8(25)), // min water temperature since last statusReport + waterTemperatureMax: decodeTemperature(data.getUint8(26)), // max water temperature since last statusReport + ambientTemperature: decodeTemperature(data.getUint8(27)), // current ambient temperature + }; +}; + +function decodeBatteryLevel(input) { + return 1800 + (input << 3); // convert to milliVolt +} + +function parseBatteryStatus(input) { + if (input <= 3100) { + return 'Low battery'; + } + + return ''; +} + +function decodeTemperature(input) { + return input * 0.5 - 20.0; // to °C +} + +// More packet types only available when using Quandify platform API +var getPacketType = function (type) { + switch (type) { + case 0: + return 'ping'; // empty ping message + case 1: + return 'statusReport'; // status message + } + + return 'Unknown'; +}; + +/* Smaller water leakages only availble when using Quandify platform API +as it requires cloud analytics */ +var parseLeakState = function (input) { + switch (input) { + case 3: + return 'Medium'; + case 4: + return 'Large'; + default: + return ''; + } +}; + +function toHexString(byteArray) { + return Array.from(byteArray, function (byte) { + return ('0' + (byte & 0xff).toString(16)).slice(-2).toUpperCase(); + }).join(''); +} + +function parseErrorCode(errorCode) { + switch (errorCode) { + case 0: + return ''; + case 384: + return 'Reverse flow'; + case 419: + case 421: + case 32768: + return 'No sensing'; + default: + return 'Contact support'; + } +} + +function normalizeUplink(input) { + return { + data: { + air: { + temperature: input.data.decoded.ambientTemperature, // °C + }, + water: { + temperature: { + min: input.data.decoded.waterTemperatureMin, // °C + max: input.data.decoded.waterTemperatureMax, // °C + }, + leak: parseLeakState(input.data.decoded.leak_state), // String + }, + metering: { + water: { + total: input.data.decoded.totalVolume, // L + }, + }, + battery: input.data.decoded.batteryRecovered / 1000, // V + }, + warnings: [parseErrorCode(input.data.decoded.errorCode), parseBatteryStatus(input.data.decoded.batteryRecovered)].filter((item) => item), + }; +} diff --git a/vendor/quandify/index.yaml b/vendor/quandify/index.yaml index 41a13527c0..f6804b3052 100644 --- a/vendor/quandify/index.yaml +++ b/vendor/quandify/index.yaml @@ -1,4 +1,6 @@ endDevices: + - cubicmeter-1-1-copper + - cubicmeter-1-1-plastic - cubicmeter profileIDs: '1':