diff --git a/packages/api/lib/controllers/boxesController.js b/packages/api/lib/controllers/boxesController.js index 029546b2..25d3534e 100644 --- a/packages/api/lib/controllers/boxesController.js +++ b/packages/api/lib/controllers/boxesController.js @@ -56,9 +56,14 @@ * @apiSuccess {String} [image] image showing the senseBox */ -const - { Box, User, Claim } = require('@sensebox/opensensemap-api-models'), - { addCache, clearCache, checkContentType, redactEmail, postToMattermost } = require('../helpers/apiUtils'), +const { Box, User, Claim } = require('@sensebox/opensensemap-api-models'), + { + addCache, + clearCache, + checkContentType, + redactEmail, + postToMattermost, + } = require('../helpers/apiUtils'), { point } = require('@turf/helpers'), classifyTransformer = require('../transformers/classifyTransformer'), { @@ -66,7 +71,7 @@ const parseAndValidateTimeParamsForFindAllBoxes, validateFromToTimeParams, checkPrivilege, - validateDateNotPast + validateDateNotPast, } = require('../helpers/userParamHelpers'), handleError = require('../helpers/errorHandler'), jsonstringify = require('stringify-stream'); @@ -154,7 +159,10 @@ const */ const updateBox = async function updateBox (req, res) { try { - let box = await Box.findBoxById(req._userParams.boxId, { lean: false, populate: false }); + let box = await Box.findBoxById(req._userParams.boxId, { + lean: false, + populate: false, + }); box = await box.updateBox(req._userParams); if (box._sensorsChanged === true) { req.user.mail('newSketch', box); @@ -189,7 +197,10 @@ const updateBox = async function updateBox (req, res) { */ const getBoxLocations = async function getBoxLocations (req, res) { try { - const box = await Box.findBoxById(req._userParams.boxId, { onlyLocations: true, lean: false }); + const box = await Box.findBoxById(req._userParams.boxId, { + onlyLocations: true, + lean: false, + }); res.send(await box.getLocations(req._userParams)); } catch (err) { return handleError(err); @@ -305,7 +316,10 @@ const getBoxes = async function getBoxes (req, res) { let stringifier = jsonstringify({ open: '[', close: ']' }); // format if (req._userParams.format === 'geojson') { - stringifier = jsonstringify({ open: '{"type":"FeatureCollection","features":[', close: ']}' }, geoJsonStringifyReplacer); + stringifier = jsonstringify( + { open: '{"type":"FeatureCollection","features":[', close: ']}' }, + geoJsonStringifyReplacer + ); } try { @@ -484,7 +498,7 @@ const getBox = async function getBox (req, res) { * @apiParam (RequestBody) {Location} location the coordinates of this senseBox. * @apiParam (RequestBody) {String="homeV2Lora","homeV2Ethernet","homeV2Wifi","homeEthernet","homeWifi","homeEthernetFeinstaub","homeWifiFeinstaub","luftdaten_sds011","luftdaten_sds011_dht11","luftdaten_sds011_dht22","luftdaten_sds011_bmp180","luftdaten_sds011_bme280","hackair_home_v2"} [model] specify the model if you want to use a predefined senseBox model, autocreating sensor definitions. * @apiParam (RequestBody) {Sensor[]} [sensors] an array containing the sensors of this senseBox. Only use if `model` is unspecified. - * @apiParam (RequestBody) {String[]="hdc1080","bmp280","tsl45315","veml6070","sds011","bme680","smt50","soundlevelmeter","windspeed","scd30","dps310","sps30"} [sensorTemplates] Specify which sensors should be included. + * @apiParam (RequestBody) {String[]="hdc1080","bmp280","tsl45315","veml6070","sds011","bme680","smt50","soundlevelmeter","windspeed","scd30","dps310","sps30","rg15"} [sensorTemplates] Specify which sensors should be included. * @apiParam (RequestBody) {Object} [mqtt] specify parameters of the MQTT integration for external measurement upload. Please see below for the accepted parameters * @apiParam (RequestBody) {Object} [ttn] specify parameters for the TTN integration for measurement from TheThingsNetwork.org upload. Please see below for the accepted parameters * @apiParam (RequestBody) {Boolean="true","false"} [useAuth] whether to use access_token or not for authentication @@ -537,10 +551,15 @@ const postNewBox = async function postNewBox (req, res) { const getSketch = async function getSketch (req, res) { res.header('Content-Type', 'text/plain; charset=utf-8'); try { - const box = await Box.findBoxById(req._userParams.boxId, { populate: false, lean: false }); + const box = await Box.findBoxById(req._userParams.boxId, { + populate: false, + lean: false, + }); + const params = { - serialPort: req._userParams.serialPort, + sdsSerialPort: req._userParams.sdsSerialPort, + rg15SerialPort: req._userParams.rg15SerialPort, soilDigitalPort: req._userParams.soilDigitalPort, soundMeterPort: req._userParams.soundMeterPort, windSpeedPort: req._userParams.windSpeedPort, @@ -549,9 +568,11 @@ const getSketch = async function getSketch (req, res) { devEUI: req._userParams.devEUI, appEUI: req._userParams.appEUI, appKey: req._userParams.appKey, - display_enabled: req._userParams.display_enabled + display_enabled: req._userParams.display_enabled, }; + + // pass access token only if useAuth is true and access_token is available if (box.access_token) { params.access_token = box.access_token; @@ -579,10 +600,16 @@ const deleteBox = async function deleteBox (req, res) { try { await req.user.checkPassword(password); const box = await req.user.removeBox(boxId); - res.send({ code: 'Ok', message: 'box and all associated measurements marked for deletion' }); + res.send({ + code: 'Ok', + message: 'box and all associated measurements marked for deletion', + }); clearCache(['getBoxes', 'getStats']); - postToMattermost(`Box deleted: ${req.user.name} (${redactEmail(req.user.email)}) just deleted "${box.name}" (${boxId})`); - + postToMattermost( + `Box deleted: ${req.user.name} (${redactEmail( + req.user.email + )}) just deleted "${box.name}" (${boxId})` + ); } catch (err) { return handleError(err); } @@ -707,87 +734,91 @@ const getAllTags = async function getAllTags (req, res) { } }; - module.exports = { // auth required deleteBox: [ checkContentType, retrieveParameters([ { predef: 'boxId', required: true }, - { predef: 'password' } + { predef: 'password' }, ]), checkPrivilege, - deleteBox + deleteBox, ], getTransfer: [ retrieveParameters([{ predef: 'boxId', required: true }]), checkPrivilege, - getTransfer + getTransfer, ], createTransfer: [ retrieveParameters([ { predef: 'boxId', required: true }, - { predef: 'dateNoDefault' } + { predef: 'dateNoDefault' }, ]), validateDateNotPast, checkPrivilege, - createTransfer + createTransfer, ], updateTransfer: [ retrieveParameters([ { predef: 'boxId', required: true }, { name: 'token', dataType: 'String' }, - { predef: 'dateNoDefault', required: true } + { predef: 'dateNoDefault', required: true }, ]), validateDateNotPast, checkPrivilege, - updateTransfer + updateTransfer, ], removeTransfer: [ retrieveParameters([ { predef: 'boxId', required: true }, - { name: 'token', dataType: 'String' } + { name: 'token', dataType: 'String' }, ]), checkPrivilege, - removeTransfer + removeTransfer, ], claimBox: [ checkContentType, retrieveParameters([{ name: 'token', dataType: 'String' }]), - claimBox + claimBox, ], getSketch: [ retrieveParameters([ { predef: 'boxId', required: true }, { - name: 'serialPort', + name: 'sdsSerialPort', + dataType: 'String', + allowedValues: ['Serial1', 'Serial2'], + }, + { + name: 'rg15SerialPort', dataType: 'String', - allowedValues: ['Serial1', 'Serial2'] + allowedValues: ['Serial1', 'Serial2'], }, { name: 'soilDigitalPort', dataType: 'String', - allowedValues: ['A', 'B', 'C'] + allowedValues: ['A', 'B', 'C'], }, { name: 'soundMeterPort', dataType: 'String', - allowedValues: ['A', 'B', 'C'] + allowedValues: ['A', 'B', 'C'], }, { name: 'windSpeedPort', dataType: 'String', - allowedValues: ['A', 'B', 'C'] + allowedValues: ['A', 'B', 'C'], }, { name: 'ssid', dataType: 'StringWithEmpty' }, { name: 'password', dataType: 'StringWithEmpty' }, { name: 'devEUI', dataType: 'StringWithEmpty' }, { name: 'appEUI', dataType: 'StringWithEmpty' }, { name: 'appKey', dataType: 'StringWithEmpty' }, - { name: 'display_enabled', allowedValues: ['true', 'false'] } + { name: 'display_enabled', allowedValues: ['true', 'false'] }, ]), checkPrivilege, - getSketch + getSketch, ], updateBox: [ checkContentType, @@ -805,10 +836,10 @@ module.exports = { { name: 'addons', dataType: 'object' }, { predef: 'location' }, { name: 'useAuth', allowedValues: ['true', 'false'] }, - { name: 'generate_access_token', allowedValues: ['true', 'false'] } + { name: 'generate_access_token', allowedValues: ['true', 'false'] }, ]), checkPrivilege, - updateBox + updateBox, ], // no auth required getBoxLocations: [ @@ -817,13 +848,13 @@ module.exports = { { name: 'format', defaultValue: 'json', - allowedValues: ['json', 'geojson'] + allowedValues: ['json', 'geojson'], }, { predef: 'toDate' }, { predef: 'fromDate' }, - validateFromToTimeParams + validateFromToTimeParams, ]), - getBoxLocations + getBoxLocations, ], postNewBox: [ checkContentType, @@ -849,40 +880,41 @@ module.exports = { 'windspeed', 'scd30', 'dps310', - 'sps30' - ] + 'sps30', + 'rg15', + ], }, { name: 'serialPort', dataType: 'String', defaultValue: 'Serial1', - allowedValues: ['Serial1', 'Serial2'] + allowedValues: ['Serial1', 'Serial2'], }, { name: 'soilDigitalPort', dataType: 'String', defaultValue: 'A', - allowedValues: ['A', 'B', 'C'] + allowedValues: ['A', 'B', 'C'], }, { name: 'soundMeterPort', dataType: 'String', defaultValue: 'B', - allowedValues: ['A', 'B', 'C'] + allowedValues: ['A', 'B', 'C'], }, { name: 'windSpeedPort', dataType: 'String', defaultValue: 'C', - allowedValues: ['A', 'B', 'C'] + allowedValues: ['A', 'B', 'C'], }, { name: 'mqtt', dataType: 'object' }, { name: 'ttn', dataType: 'object' }, { name: 'useAuth', allowedValues: ['true', 'false'] }, { predef: 'location', required: true }, - { name: 'sharedBox', allowedValues: ['true', 'false'] } + { name: 'sharedBox', allowedValues: ['true', 'false'] }, ]), - postNewBox + postNewBox, ], getBox: [ retrieveParameters([ @@ -890,10 +922,10 @@ module.exports = { { name: 'format', defaultValue: 'json', - allowedValues: ['json', 'geojson'] - } + allowedValues: ['json', 'geojson'], + }, ]), - getBox + getBox, ], getBoxes: [ retrieveParameters([ @@ -902,7 +934,7 @@ module.exports = { { name: 'exposure', allowedValues: Box.BOX_VALID_EXPOSURES, - dataType: ['String'] + dataType: ['String'], }, { name: 'model', dataType: ['StringWithEmpty'] }, { name: 'grouptag', dataType: ['StringWithEmpty'] }, @@ -911,26 +943,26 @@ module.exports = { { name: 'format', defaultValue: 'json', - allowedValues: ['json', 'geojson'] + allowedValues: ['json', 'geojson'], }, { name: 'classify', defaultValue: 'false', - allowedValues: ['true', 'false'] + allowedValues: ['true', 'false'], }, { name: 'minimal', defaultValue: 'false', - allowedValues: ['true', 'false'] + allowedValues: ['true', 'false'], }, { name: 'full', defaultValue: 'false', allowedValues: ['true', 'false'] }, { predef: 'near' }, { name: 'maxDistance' }, - { predef: 'bbox' } + { predef: 'bbox' }, ]), parseAndValidateTimeParamsForFindAllBoxes, addCache('5 minutes', 'getBoxes'), - getBoxes + getBoxes, ], - getAllTags: [addCache('5 minutes', 'getAllTags'), getAllTags] + getAllTags: [addCache('5 minutes', 'getAllTags'), getAllTags], }; diff --git a/packages/api/package.json b/packages/api/package.json index e8f6d287..5ff82ac4 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -9,10 +9,11 @@ "Gerald Pape", "Norwin Roosen", "Umut Tas", - "Felix Erdmann" + "Felix Erdmann", + "Eric Thieme-Garmann" ], "dependencies": { - "@sensebox/opensensemap-api-models": "3.3.0", + "@sensebox/opensensemap-api-models": "3.3.2", "@turf/area": "^6.5.0", "@turf/bbox": "^6.5.0", "@turf/centroid": "^6.5.0", diff --git a/packages/models/CHANGELOG.md b/packages/models/CHANGELOG.md index a8a64a5a..35d0bbef 100644 --- a/packages/models/CHANGELOG.md +++ b/packages/models/CHANGELOG.md @@ -2,6 +2,14 @@ ## Unreleased +## v3.3.2 + +## v3.3.2 + - Have option to supply templater with serial ports for SDS and RG-15 + +## v3.3.1 +- Add Hydreon RG-15 Optical Rain Gauge + ## v3.3.0 - Add DNMS sensor template (#881) diff --git a/packages/models/package.json b/packages/models/package.json index 461cbb8b..57ad0dcd 100644 --- a/packages/models/package.json +++ b/packages/models/package.json @@ -1,14 +1,14 @@ { "name": "@sensebox/opensensemap-api-models", "description": "openSenseMap data models and database connection", - "version": "3.3.0", + "version": "3.3.2", "main": "index.js", "license": "MIT", "dependencies": { "@grpc/grpc-js": "^1.9.4", "@grpc/proto-loader": "^0.7.10", "@sensebox/osem-protos": "^1.1.0", - "@sensebox/sketch-templater": "1.13.1", + "@sensebox/sketch-templater": "1.13.6", "bcrypt": "^5.1.1", "bullmq": "^4.12.3", "config": "^3.3.6", diff --git a/packages/models/src/box/box.js b/packages/models/src/box/box.js index a29100bd..c5dd0c68 100644 --- a/packages/models/src/box/box.js +++ b/packages/models/src/box/box.js @@ -881,9 +881,12 @@ boxSchema.methods.updateSensors = function updateSensors (sensors) { } }; -boxSchema.methods.getSketch = function getSketch ({ encoding, serialPort, soilDigitalPort, soundMeterPort, windSpeedPort, ssid, password, devEUI, appEUI, appKey, access_token, display_enabled } = {}) { - if (serialPort) { - this.serialPort = serialPort; +boxSchema.methods.getSketch = function getSketch ({ encoding, sdsSerialPort, rg15SerialPort, soilDigitalPort, soundMeterPort, windSpeedPort, ssid, password, devEUI, appEUI, appKey, access_token, display_enabled } = {}) { + if (sdsSerialPort) { + this.sdsSerialPort = sdsSerialPort; + } + if (rg15SerialPort) { + this.rg15SerialPort = rg15SerialPort; } if (soilDigitalPort) { this.soilDigitalPort = soilDigitalPort; diff --git a/packages/models/src/box/sensorLayouts/sensebox.home.mcu.js b/packages/models/src/box/sensorLayouts/sensebox.home.mcu.js index e2740e5a..c28a2e0c 100644 --- a/packages/models/src/box/sensorLayouts/sensebox.home.mcu.js +++ b/packages/models/src/box/sensorLayouts/sensebox.home.mcu.js @@ -23,7 +23,9 @@ const { sps30_pm1, sps30_pm25, sps30_pm4, - sps30_pm10 + sps30_pm10, + rg15_intensity, + rg15_totalAcc } = sensorDefinitions; module.exports = [ @@ -47,5 +49,7 @@ module.exports = [ sps30_pm1, sps30_pm25, sps30_pm4, - sps30_pm10 + sps30_pm10, + rg15_intensity, + rg15_totalAcc, ]; diff --git a/packages/models/src/box/sensorLayouts/sensorDefinitions/index.js b/packages/models/src/box/sensorLayouts/sensorDefinitions/index.js index 94be21c0..d79bbc45 100644 --- a/packages/models/src/box/sensorLayouts/sensorDefinitions/index.js +++ b/packages/models/src/box/sensorLayouts/sensorDefinitions/index.js @@ -47,6 +47,8 @@ const veml6070_uvintensity = require('./veml6070_uvintensity'), sps30_pm25 = require('./sps30_pm25'), sps30_pm4 = require('./sps30_pm4'), sps30_pm10 = require('./sps30_pm10'), + rg15_intensity = require('./rg15_intensity'), + rg15_totalAcc = require('./rg15_totalacc'), sht3x_temperature = require('./sht3x_temperature'), sht3x_humidity = require('./sht3x_humidity'), dnms_la_eq = require('./dnms_la_eq'), @@ -100,6 +102,8 @@ module.exports = { sps30_pm25, sps30_pm4, sps30_pm10, + rg15_intensity, + rg15_totalAcc, sht3x_temperature, sht3x_humidity, dnms_la_eq, diff --git a/packages/models/src/box/sensorLayouts/sensorDefinitions/rg15_intensity.js b/packages/models/src/box/sensorLayouts/sensorDefinitions/rg15_intensity.js new file mode 100644 index 00000000..c7eeb1dd --- /dev/null +++ b/packages/models/src/box/sensorLayouts/sensorDefinitions/rg15_intensity.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = { + title: 'Niederschlagsintensität', + unit: 'mm/h', + sensorType: 'RG15', + icon: 'osem-umbrella' +}; diff --git a/packages/models/src/box/sensorLayouts/sensorDefinitions/rg15_totalacc.js b/packages/models/src/box/sensorLayouts/sensorDefinitions/rg15_totalacc.js new file mode 100644 index 00000000..61a14266 --- /dev/null +++ b/packages/models/src/box/sensorLayouts/sensorDefinitions/rg15_totalacc.js @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = { + title: 'Gesamtniederschlag', + unit: 'mm/m²', + sensorType: 'RG15', + icon: 'osem-umbrella', +}; + diff --git a/tests/tests/ZZZ-mail-test.js b/tests/tests/ZZZ-mail-test.js index 1cd096b8..5dfd6774 100644 --- a/tests/tests/ZZZ-mail-test.js +++ b/tests/tests/ZZZ-mail-test.js @@ -35,23 +35,6 @@ const getMail = async function getMail ( return mail; }; -const getMails = async function getMails (address, subject) { - const mailsResponse = await chakram.get( - 'http://mailhog:8025/api/v2/messages?limit=9999' - ); - const mails = mailsResponse.body.items; - - return mails - .filter(function (item) { - return ( - item.Raw.To[0] === address && - item.Content.Headers.Subject.includes(subject) - ); - }) - .map(function (mail) { - return $.load(mimelib.decodeQuotedPrintable(mail.Content.Body)); - }); -}; describe('mails', function () { it('should have sent mails', function () { @@ -549,29 +532,6 @@ describe('mails', function () { }); }); - it('should have sent special luftdaten info welcome mail', async function () { - const foundMails = await getMails( - 'luftdaten@email', - 'Your device on openSenseMap' - ); - expect(foundMails).not.to.be.empty; - expect( - foundMails.every(function (mail) { - expect(mail).to.exist; - const links = mail('a'); - let hasLink = false; - links.each(function (_, link) { - const href = $(link).attr('href'); - if (href.includes('opensensemap-luftdaten')) { - hasLink = true; - } - }); - expect(hasLink).to.be.true; - - return hasLink; - }) - ).true; - }); it('should have sent a mail upon deletion of an user', async function () { const mail = await getMail( diff --git a/yarn.lock b/yarn.lock index 1dd7003a..f0339487 100644 --- a/yarn.lock +++ b/yarn.lock @@ -178,10 +178,10 @@ resolved "https://registry.yarnpkg.com/@sensebox/osem-protos/-/osem-protos-1.1.0.tgz#a7de8bc6be867953f1309181a012063c23299e79" integrity sha512-H+nUVcWlT0dvIqfJnYHuX9JBcCkP1ZKGE5YTRNWPbAEdZ11h+srpQsmeI58wK5hJcdukaZAjc4Dy96IeGM77aA== -"@sensebox/sketch-templater@1.13.1": - version "1.13.1" - resolved "https://registry.yarnpkg.com/@sensebox/sketch-templater/-/sketch-templater-1.13.1.tgz#ecfe6201e7d584d7cd5f6e9a3bc4ea62d3889afe" - integrity sha512-zlwKd/usPPMyE5uyD7ngAN/BIpMTjVrQ9SKdWrdANjdW19vsr3IXFv7liHvSh2od1ipPt+JgIENMJAMCJJG4sA== +"@sensebox/sketch-templater@1.13.6": + version "1.13.6" + resolved "https://registry.yarnpkg.com/@sensebox/sketch-templater/-/sketch-templater-1.13.6.tgz#65c5974d3a282ba74f135343ef11d0615d50ba6f" + integrity sha512-VwcqTA0mg7Mus80TDMaaa3eV5z7W+dpHphIIicB+DsY8r1J7QgwvGxiJYTfNJGbirJlR+Q9SqnEfbmILPKlk8w== dependencies: config "^3.3.7" dedent "^0.7.0"