Commit f2a8c860 authored by Giannis Kepas's avatar Giannis Kepas
Browse files

initial support for Device type

parent 1ad01de6
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -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." });
        }

@@ -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`, {
+49 −0
Original line number Diff line number Diff line
@@ -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:
@@ -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) {
+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;
+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,
};