Loading http-bridge/controllers/generalControllers.js +19 −0 Original line number Diff line number Diff line Loading @@ -43,20 +43,24 @@ function generateMeasurementTypeParam(type, attributes) { const fetchEntities = async (type, attrs, fetcher, additionalParams, res) => { try { if (!type) { console.error("[400] Missing required parameter: type") return res.status(400).json({ error: "Missing required parameter: type" }); } if (!typeMaps[type]) { console.error(`[400] Unsupported entity type: ${type}`) return res.status(400).json({ error: `Unsupported entity type: ${type}` }); } const measurementTypes = generateMeasurementTypeParam(type, attrs); if (!measurementTypes) { console.error(`[400] Missing measurement_type parameter.`) return res.status(400).json({ error: "Missing measurement_type parameter." }); } const apiResponse = await fetcher(type, measurementTypes, additionalParams); if (!apiResponse || !apiResponse.data || apiResponse.data.length === 0) { console.error("[500] Failed to retrieve data from API. See http-bridge logs for info.") return res.status(500).json({ error: "Failed to retrieve data from API. See http-bridge logs for info." }); } Loading Loading @@ -107,6 +111,21 @@ const fetchTemporalData = async (type, measurementTypes, params) => { */ const fetchRealTimeData = async (type, measurementTypes, params) => { switch (type) { case "Device": try { const response = await axios.get(`${process.env.API_URL}/device`, { params: { measurement_type: `in.(${measurementTypes.join(",")})`, order: "id.desc", } }); return response; } catch (error) { console.error(`error fetching real-time data\n${error.stack}`); return { data: [] }; } case "DeviceMeasurement": try { const response = await axios.get(`${process.env.API_URL}/device_measurement`, { Loading http-bridge/translator/NGSITranslator.js +49 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,8 @@ const typeMaps = require("../typeMaps/typeMaps"); class NGSITranslator { static toNGSI(apiData, entityType) { switch (entityType) { case "Device": return NGSITranslator.processDevice(apiData, entityType); case "DeviceMeasurement": return NGSITranslator.processDeviceMeasurement(apiData, entityType); default: Loading @@ -10,6 +12,53 @@ class NGSITranslator { } } // https://smartdatamodels.org/dataModel.Device/Device static processDevice(apiData, entityType) { const typeMap = typeMaps[entityType]; if (!typeMap) { throw new Error(`No mapping found for entity type: ${entityType}`); } const groupedByDevice = {}; apiData.forEach(item => { const { item: device_id, measurement_type, location1, location2, geo_coordinates } = item; // Ensure the device entry exists if (!groupedByDevice[device_id]) { let splitCoords = geo_coordinates.split(", ") groupedByDevice[device_id] = { id: `urn:ngsi-ld:Device:${device_id}`, controlledProperty: typeMap[measurement_type].key.split(" "), location: { type: "GeoProperty", value: { type: "Point", coordinates: [parseFloat(splitCoords[0]), parseFloat(splitCoords[1])] } }, address: { type: "Property", value: { addressCountry: "Greece", addressRegion: location1, streetAddress: location2 } }, type: entityType, "@context": [ "https://raw.githubusercontent.com/smart-data-models/dataModel.Device/master/context.jsonld", "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld" ] }; } }); return Object.values(groupedByDevice); } // https://smartdatamodels.org/dataModel.Device/DeviceMeasurement static processDeviceMeasurement(apiData, entityType) { const typeMap = typeMaps[entityType]; if (!typeMap) { Loading http-bridge/typeMaps/deviceTypeMap.js 0 → 100644 +42 −0 Original line number Diff line number Diff line const deviceTypeMap = { /* Each entry in the map is a key-value pair where the key is the measurement_type column and the value is an object with the following properties: - key: A string representing the name of the property in NGSI format. - unit: A character string representing the measurement unit for the type. - min: Min is the minimum possible value for the measurement type. - max: Max is the maximum possible value for the measurement type. - slug: The slug is used as "attrs" in the controller, allowing for the selection of specific attributes to be returned in the response. */ // --- MISSING // battery level // hub online state // mode (from fan coil) // signal strength // temperature apparent // temperate setpoint "temperature": { key: "temperature", unit: "CEL", min: 0, max: 60, slug: "temperature" }, "humidity": { key: "humidity", unit: "P1", min: 0, max: 100, slug: "humidity" }, "energy": { key: "energy", unit: "KWH", min: 0, max: 10000, slug: "energy_consumption" }, "power": { key: "power", unit: "WTT", min: 0, max: 10000, slug: "power_usage" }, "luminance": { key: "luminance", unit: "A24", min: 0, max: 1000, slug: "luminance" }, "barometric pressure": { key: "hectopascal", unit: "A97", min: 0, max: 1200, slug: "pressure" }, "wind speed": { key: "windSpeed", unit: "KMH", min: 0, max: 50, slug: "wind_speed" }, "wind angle": { key: "windDirection", unit: "DD", min: 0, max: 359.9, slug: "wind_direction" }, "CO2": { key: "carbonDioxide", unit: "P1", min: 0, max: 100, slug: "co2" }, "noise level": { key: "decibel", unit: "2N", min: 0, max: 100, slug: "noise_level" }, "motion detection switch": { key: "motion", unit: "A99", min: 0, max: 1, slug: "motion_detection" }, "magnetic switch": { key: "door", unit: "A99", min: 0, max: 1, slug: "magnetic_switch" }, "UV": { key: "UVIndex", unit: "C62", min: 0, max: 10, slug: "uv" }, }; /* NOTES * - Percentages are represented as "P1" in the unit field. * - Luminance is represented as "A24" in the unit field not candela per square meter. * - Decibels are represented as "2N" in the unit field. * - Switches are represented as "A99" in the unit field meaning '0' or '1' (bit). * - UV Index is represented as "C62" in the unit field meaning 'one' (synonym: unit) since UV Index is unitless. */ module.exports = deviceTypeMap; http-bridge/typeMaps/typeMaps.js +2 −0 Original line number Diff line number Diff line const deviceMeasurementTypeMap = require('./deviceMeasurementTypeMap'); const deviceTypeMap = require('./deviceTypeMap'); // List of all available NGSI-LD model types const typeMaps = { "Device": deviceTypeMap, "DeviceMeasurement": deviceMeasurementTypeMap, }; Loading Loading
http-bridge/controllers/generalControllers.js +19 −0 Original line number Diff line number Diff line Loading @@ -43,20 +43,24 @@ function generateMeasurementTypeParam(type, attributes) { const fetchEntities = async (type, attrs, fetcher, additionalParams, res) => { try { if (!type) { console.error("[400] Missing required parameter: type") return res.status(400).json({ error: "Missing required parameter: type" }); } if (!typeMaps[type]) { console.error(`[400] Unsupported entity type: ${type}`) return res.status(400).json({ error: `Unsupported entity type: ${type}` }); } const measurementTypes = generateMeasurementTypeParam(type, attrs); if (!measurementTypes) { console.error(`[400] Missing measurement_type parameter.`) return res.status(400).json({ error: "Missing measurement_type parameter." }); } const apiResponse = await fetcher(type, measurementTypes, additionalParams); if (!apiResponse || !apiResponse.data || apiResponse.data.length === 0) { console.error("[500] Failed to retrieve data from API. See http-bridge logs for info.") return res.status(500).json({ error: "Failed to retrieve data from API. See http-bridge logs for info." }); } Loading Loading @@ -107,6 +111,21 @@ const fetchTemporalData = async (type, measurementTypes, params) => { */ const fetchRealTimeData = async (type, measurementTypes, params) => { switch (type) { case "Device": try { const response = await axios.get(`${process.env.API_URL}/device`, { params: { measurement_type: `in.(${measurementTypes.join(",")})`, order: "id.desc", } }); return response; } catch (error) { console.error(`error fetching real-time data\n${error.stack}`); return { data: [] }; } case "DeviceMeasurement": try { const response = await axios.get(`${process.env.API_URL}/device_measurement`, { Loading
http-bridge/translator/NGSITranslator.js +49 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,8 @@ const typeMaps = require("../typeMaps/typeMaps"); class NGSITranslator { static toNGSI(apiData, entityType) { switch (entityType) { case "Device": return NGSITranslator.processDevice(apiData, entityType); case "DeviceMeasurement": return NGSITranslator.processDeviceMeasurement(apiData, entityType); default: Loading @@ -10,6 +12,53 @@ class NGSITranslator { } } // https://smartdatamodels.org/dataModel.Device/Device static processDevice(apiData, entityType) { const typeMap = typeMaps[entityType]; if (!typeMap) { throw new Error(`No mapping found for entity type: ${entityType}`); } const groupedByDevice = {}; apiData.forEach(item => { const { item: device_id, measurement_type, location1, location2, geo_coordinates } = item; // Ensure the device entry exists if (!groupedByDevice[device_id]) { let splitCoords = geo_coordinates.split(", ") groupedByDevice[device_id] = { id: `urn:ngsi-ld:Device:${device_id}`, controlledProperty: typeMap[measurement_type].key.split(" "), location: { type: "GeoProperty", value: { type: "Point", coordinates: [parseFloat(splitCoords[0]), parseFloat(splitCoords[1])] } }, address: { type: "Property", value: { addressCountry: "Greece", addressRegion: location1, streetAddress: location2 } }, type: entityType, "@context": [ "https://raw.githubusercontent.com/smart-data-models/dataModel.Device/master/context.jsonld", "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context.jsonld" ] }; } }); return Object.values(groupedByDevice); } // https://smartdatamodels.org/dataModel.Device/DeviceMeasurement static processDeviceMeasurement(apiData, entityType) { const typeMap = typeMaps[entityType]; if (!typeMap) { Loading
http-bridge/typeMaps/deviceTypeMap.js 0 → 100644 +42 −0 Original line number Diff line number Diff line const deviceTypeMap = { /* Each entry in the map is a key-value pair where the key is the measurement_type column and the value is an object with the following properties: - key: A string representing the name of the property in NGSI format. - unit: A character string representing the measurement unit for the type. - min: Min is the minimum possible value for the measurement type. - max: Max is the maximum possible value for the measurement type. - slug: The slug is used as "attrs" in the controller, allowing for the selection of specific attributes to be returned in the response. */ // --- MISSING // battery level // hub online state // mode (from fan coil) // signal strength // temperature apparent // temperate setpoint "temperature": { key: "temperature", unit: "CEL", min: 0, max: 60, slug: "temperature" }, "humidity": { key: "humidity", unit: "P1", min: 0, max: 100, slug: "humidity" }, "energy": { key: "energy", unit: "KWH", min: 0, max: 10000, slug: "energy_consumption" }, "power": { key: "power", unit: "WTT", min: 0, max: 10000, slug: "power_usage" }, "luminance": { key: "luminance", unit: "A24", min: 0, max: 1000, slug: "luminance" }, "barometric pressure": { key: "hectopascal", unit: "A97", min: 0, max: 1200, slug: "pressure" }, "wind speed": { key: "windSpeed", unit: "KMH", min: 0, max: 50, slug: "wind_speed" }, "wind angle": { key: "windDirection", unit: "DD", min: 0, max: 359.9, slug: "wind_direction" }, "CO2": { key: "carbonDioxide", unit: "P1", min: 0, max: 100, slug: "co2" }, "noise level": { key: "decibel", unit: "2N", min: 0, max: 100, slug: "noise_level" }, "motion detection switch": { key: "motion", unit: "A99", min: 0, max: 1, slug: "motion_detection" }, "magnetic switch": { key: "door", unit: "A99", min: 0, max: 1, slug: "magnetic_switch" }, "UV": { key: "UVIndex", unit: "C62", min: 0, max: 10, slug: "uv" }, }; /* NOTES * - Percentages are represented as "P1" in the unit field. * - Luminance is represented as "A24" in the unit field not candela per square meter. * - Decibels are represented as "2N" in the unit field. * - Switches are represented as "A99" in the unit field meaning '0' or '1' (bit). * - UV Index is represented as "C62" in the unit field meaning 'one' (synonym: unit) since UV Index is unitless. */ module.exports = deviceTypeMap;
http-bridge/typeMaps/typeMaps.js +2 −0 Original line number Diff line number Diff line const deviceMeasurementTypeMap = require('./deviceMeasurementTypeMap'); const deviceTypeMap = require('./deviceTypeMap'); // List of all available NGSI-LD model types const typeMaps = { "Device": deviceTypeMap, "DeviceMeasurement": deviceMeasurementTypeMap, }; Loading