[{"id":"2034170d90991aa9","type":"tab","label":"Fluxos globals","disabled":false,"info":"","env":[]},{"id":"b8890e41d85cb2cd","type":"function","z":"2034170d90991aa9","name":"getFlowConfig","func":"// Configuració global del flux\n// Es recomana mantenir aquesta configuració en msg sempre que es pugui, o recargar-la amb el flux global getFlowConfig\n// Emplenar segons les nostres necessitats, amb la recomanació de mantenir sempre la jerarquia de tipus en primer nivell\n\n// Dades de configuració base:\n// - : adreça de l'API de Sentilo\n// - : token genèric que es farà servir si no es detalla a nivell d'ens\n\nmsg.flowConfig = {\n sentilo: {\n api: {\n host: \"\",\n token: \"\"\n },\n catalog: {\n ens: [\n {\n ens: \"\",\n provider: \"\",\n token: \"\"\n },\n {\n ens: \"\",\n provider: \"\",\n token: \"\"\n }\n ],\n component: {\n \"component\": \"test_global\",\n \"componentType\": \"generic\",\n \"componentDesc\": \"Component de test pels fluxes globals\",\n \"componentPublicAccess\": \"false\",\n },\n sensors: {\n \"global001\": {\n \"sensor\": \"global001\",\n \"description\": \"Test sensor for global flows\",\n \"type\": \"status\",\n \"dataType\": \"boolean\",\n \"unit\": \"\",\n \"publicAccess\": \"false\",\n \"location\":\"41.4123488 2.2076235\",\n },\n \"global002\": {\n \"sensor\": \"global002\",\n \"description\": \"Test sensor for global flows\",\n \"type\": \"temperature\",\n \"dataType\": \"number\",\n \"unit\": \"ºC\",\n \"publicAccess\": \"false\",\n \"location\":\"41.4123488 2.2076235\",\n },\n \"global003\": {\n \"sensor\": \"global003\",\n \"description\": \"Test sensor for global flows\",\n \"type\": \"battery\",\n \"dataType\": \"number\",\n \"unit\": \"%\",\n \"publicAccess\": \"false\",\n \"location\":\"41.4123488 2.2076235\",\n }\n }\n }\n }\n}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":200,"y":100,"wires":[["0e593579fcc2bf49","6051edd1d62d5ceb"]]},{"id":"9fb2e55499967a41","type":"link in","z":"2034170d90991aa9","name":"globals.getFlowConfig","links":[],"x":75,"y":100,"wires":[["b8890e41d85cb2cd"]]},{"id":"0e593579fcc2bf49","type":"link out","z":"2034170d90991aa9","name":"getFlowConfig","mode":"return","links":[],"x":325,"y":100,"wires":[]},{"id":"6051edd1d62d5ceb","type":"debug","z":"2034170d90991aa9","name":"flowConfig","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"flowConfig","targetType":"msg","statusVal":"","statusType":"auto","x":430,"y":100,"wires":[]},{"id":"55d01ca815e59b73","type":"comment","z":"2034170d90991aa9","name":"globals.getFlowConfig: obtenció de la configuració del fluxe","info":"","x":270,"y":60,"wires":[]},{"id":"1e72e4a87251369d","type":"inject","z":"2034170d90991aa9","name":"globals.getFlowConfig","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":180,"y":140,"wires":[["9658c1cd80678d89"]]},{"id":"9658c1cd80678d89","type":"link call","z":"2034170d90991aa9","name":"","links":["9fb2e55499967a41"],"timeout":"30","x":400,"y":140,"wires":[["6ef6010df6e7352e"]]},{"id":"6ef6010df6e7352e","type":"debug","z":"2034170d90991aa9","name":"getFlowConfig","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"flowConfig","targetType":"msg","statusVal":"","statusType":"auto","x":600,"y":140,"wires":[]},{"id":"294b16dff52c4329","type":"link call","z":"2034170d90991aa9","name":"","links":["9fb2e55499967a41"],"timeout":"30","x":220,"y":240,"wires":[["b12f88c1d049c40e","bc440b33973301cd"]]},{"id":"b12f88c1d049c40e","type":"function","z":"2034170d90991aa9","name":"getCatalog","func":"// Node funcional de preparació per a l'obtenció de les dades del catàleg\n\nconst flowConfig = msg.flowConfig;\nif (!flowConfig?.sentilo?.catalog) {\n node.error(\"No hem pogut carregar la configuració necessària (flowConfig, sentiloConfig o catalogConfig)\");\n return null;\n}\n\nconst sentiloConfig = flowConfig.sentilo;\nconst catalogConfig = sentiloConfig.catalog;\n\nconst reqParams = msg.reqParams;\nif (!reqParams || typeof reqParams !== 'object') {\n node.error(\"No hem pogut carregar els paràmetres de cerca (reqParams)\");\n return null;\n}\n\nif (!reqParams.ens || !reqParams.provider) {\n node.error(\"Falten dades de filtrat o configuració de la crida (msg.reqParams.ens / msg.reqParams.provider)\");\n return null;\n}\n\nconst token = reqParams.token || sentiloConfig.api.token;\nif (!token) {\n node.error(\"No s'ha definit un token d'identificació\");\n return null;\n}\n\n// Prepaparació de l'http request\nconst host = sentiloConfig.api.host;\nconst request = `${host}/catalog`;\nconst filters = catalogConfig.component?.componentType \n ? `?componentType=${encodeURIComponent(catalogConfig.component.componentType)}`\n : \"\";\n\nmsg.url = request + filters;\nmsg.method = 'GET';\nmsg.headers = { 'identity_key': token };\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":450,"y":240,"wires":[["42ebbdebd8f5ee84","d959a26bcbc2094e"]]},{"id":"42ebbdebd8f5ee84","type":"http request","z":"2034170d90991aa9","name":"getCatalog","method":"use","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"x":630,"y":240,"wires":[["de4a31d62035b3b9","a08fc836113646af"]]},{"id":"85dcfa84054f5ff9","type":"comment","z":"2034170d90991aa9","name":"globals.getCatalog: Obtenir dades del catàleg","info":"","x":230,"y":200,"wires":[]},{"id":"335fe66302da6171","type":"link in","z":"2034170d90991aa9","name":"globals.getCatalog","links":[],"x":75,"y":240,"wires":[["294b16dff52c4329"]]},{"id":"de4a31d62035b3b9","type":"link out","z":"2034170d90991aa9","name":"getCatalog","mode":"return","links":[],"x":745,"y":240,"wires":[]},{"id":"d959a26bcbc2094e","type":"debug","z":"2034170d90991aa9","name":"getCatalog request","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":650,"y":200,"wires":[]},{"id":"a08fc836113646af","type":"debug","z":"2034170d90991aa9","name":"getCatalog response","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":880,"y":240,"wires":[]},{"id":"bc440b33973301cd","type":"debug","z":"2034170d90991aa9","name":"getCatalog reqParams","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"reqParams","targetType":"msg","statusVal":"","statusType":"auto","x":220,"y":280,"wires":[]},{"id":"4460e7a3db11a51e","type":"inject","z":"2034170d90991aa9","name":"Inici","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":130,"y":800,"wires":[["4768e13882bef64a"]]},{"id":"2033b7f58caed370","type":"link call","z":"2034170d90991aa9","name":"","links":["335fe66302da6171"],"timeout":"30","x":310,"y":980,"wires":[["4cdf585441629059"]]},{"id":"885e857c1eb0228f","type":"function","z":"2034170d90991aa9","name":"ensList","func":"// Node funcional que extreu el llistat d'ens de la configuració i els exposa per iterar-los\n\nconst flowConfig = msg.flowConfig || undefined;\nif (!flowConfig) {\n node.error(\"No hem pogut carregar el flowConfig\");\n return null;\n}\n\nconst sentiloConfig = flowConfig.sentilo || undefined;\nif (!sentiloConfig) {\n node.error(\"No hem pogut carregar la configuració necessària (sentiloConfig)\");\n return null;\n}\n\nconst catalogConfig = sentiloConfig.catalog || undefined;\nif (!catalogConfig) {\n node.error(\"No hem pogut carregar la configuració necessària (catalogConfig)\");\n return null;\n}\n\n// Carreguem el llistat d'ens per a iterar-lo\nmsg.ensList = catalogConfig.ens || [];\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":280,"y":860,"wires":[["b206d2956af4c55e"]]},{"id":"4768e13882bef64a","type":"link call","z":"2034170d90991aa9","name":"","links":["9fb2e55499967a41"],"timeout":"30","x":320,"y":800,"wires":[["885e857c1eb0228f"]]},{"id":"b206d2956af4c55e","type":"array-loop","z":"2034170d90991aa9","name":"ensList","key":"alb206d2956af4c55e","keyType":"msg","reset":false,"resetValue":"value-null","array":"ensList","arrayType":"msg","x":580,"y":860,"wires":[["5fb0c1c08f9e1168"],["dd795ed970d18e56","699eacc9eb266691","47009305cef761bd"]]},{"id":"dd795ed970d18e56","type":"delay","z":"2034170d90991aa9","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":580,"y":800,"wires":[["b206d2956af4c55e"]]},{"id":"699eacc9eb266691","type":"function","z":"2034170d90991aa9","name":"prepareReqParams","func":"// Node funciona que crea el reqParams per a la obtenció de les dades del cataleg\n\nconst ens = msg.payload || undefined;\nif (!ens) {\n node.error(\"Sense dades de l'ens\");\n return null;\n}\n\n// Passem com a reqParams les dades de l'ens\nmsg.reqParams = ens;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":310,"y":920,"wires":[["2033b7f58caed370","66264f99a9229fbd"]]},{"id":"47009305cef761bd","type":"debug","z":"2034170d90991aa9","name":"ens","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":750,"y":880,"wires":[]},{"id":"887e0e8ebfa8a843","type":"link call","z":"2034170d90991aa9","name":"","links":["9fb2e55499967a41"],"timeout":"30","x":220,"y":380,"wires":[["4c23bf7bc8dd7914","aa397dc1a4408d26"]]},{"id":"4c23bf7bc8dd7914","type":"function","z":"2034170d90991aa9","name":"addCatalog","func":"// Node funcional per afegir sensors al catàleg\n\nconst flowConfig = msg.flowConfig || undefined;\nif (!flowConfig) {\n node.error(\"No hem pogut carregar el flowConfig\");\n return null;\n}\n\nconst sentiloConfig = flowConfig.sentilo || undefined;\nif (!sentiloConfig) {\n node.error(\"No hem pogut carregar la configuració necessària (sentiloConfig)\");\n return null;\n}\n\nconst catalogConfig = sentiloConfig.catalog || undefined;\nif (!catalogConfig) {\n node.error(\"No hem pogut carregar la configuració necessària (catalogConfig)\");\n return null;\n}\n\nconst reqParams = msg.reqParams || undefined;\nif (!reqParams) {\n node.error(\"No hem pogut carregar els paràmetres de cerca (reqParams)\");\n return null;\n}\n\nif (!reqParams.ens || !reqParams.provider) {\n node.error(\"Falten dades de filtrat o configuració de la crida (msg.reqParams.ens / msg.reqParams.provider)\");\n return null;\n}\n\nconst sensorsToCreate = reqParams.sensorsToCreate || [];\n\nconst token = reqParams.token || sentiloConfig.api.token;\nif (!token) {\n node.error(\"No s'ha definit un token d'identificació\");\n return null;\n}\n\n// Preparació del node http-request\nconst provider = reqParams.provider;\nconst host = sentiloConfig.api.host;\nconst request = host + \"/catalog/\" + provider;\n\nmsg.url = request;\nmsg.method = 'POST';\nmsg.headers = {\n 'identity_key': token\n}\n\nmsg.payload = {\n sensors: sensorsToCreate\n};\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":450,"y":380,"wires":[["90c3e4d7460d9c0e","87d0bc1df5263ff0"]]},{"id":"90c3e4d7460d9c0e","type":"http request","z":"2034170d90991aa9","name":"addCatalog","method":"use","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"x":630,"y":380,"wires":[["59f5a90adfd3b9dc","40b32ac074ed6256"]]},{"id":"99b4baebae12c20e","type":"comment","z":"2034170d90991aa9","name":"globals.addCatalog: Afegir sensors al catàleg","info":"","x":230,"y":340,"wires":[]},{"id":"e3ffc67fb0e48b9c","type":"link in","z":"2034170d90991aa9","name":"globals.addCatalog","links":[],"x":75,"y":380,"wires":[["887e0e8ebfa8a843"]]},{"id":"59f5a90adfd3b9dc","type":"link out","z":"2034170d90991aa9","name":"addCatalog","mode":"return","links":[],"x":735,"y":380,"wires":[]},{"id":"87d0bc1df5263ff0","type":"debug","z":"2034170d90991aa9","name":"addCatalog request","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":650,"y":340,"wires":[]},{"id":"40b32ac074ed6256","type":"debug","z":"2034170d90991aa9","name":"addCatalog response","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":870,"y":380,"wires":[]},{"id":"aa397dc1a4408d26","type":"debug","z":"2034170d90991aa9","name":"addCatalog reqParams","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"reqParams","targetType":"msg","statusVal":"","statusType":"auto","x":230,"y":420,"wires":[]},{"id":"bad0dc548829ab9b","type":"link call","z":"2034170d90991aa9","name":"","links":["952d5523eb74378b"],"timeout":"30","x":200,"y":520,"wires":[["af2c415c070f71dd","0abbf345d2eab2fd"]]},{"id":"0abbf345d2eab2fd","type":"function","z":"2034170d90991aa9","name":"publishObservations","func":"// Node funcional de publicació de dades a Sentilo\n\nconst flowConfig = msg.flowConfig || undefined;\nif (!flowConfig) {\n node.error(\"No hem pogut carregar el flowConfig\");\n return null;\n}\n\nconst sentiloConfig = flowConfig.sentilo || undefined;\nif (!sentiloConfig) {\n node.error(\"No hem pogut carregar la configuració necessària (sentiloConfig)\");\n return null;\n}\n\nconst catalogConfig = sentiloConfig.catalog || undefined;\nif (!catalogConfig) {\n node.error(\"No hem pogut carregar la configuració necessària (catalogConfig)\");\n return null;\n}\n\nconst reqParams = msg.reqParams || undefined;\nif (!reqParams) {\n node.error(\"No hem pogut carregar els paràmetres de cerca (reqParams)\");\n return null;\n}\n\nif (!reqParams.ens || !reqParams.provider) {\n node.error(\"Falten dades de filtrat o configuració de la crida (msg.reqParams.ens / msg.reqParams.provider)\");\n return null;\n}\n\nconst observations = msg.payload || undefined;\nif (!observations) { \n node.error(\"Sense observacions a publicar\");\n return null;\n}\n\nconst token = reqParams.token || sentiloConfig.api.token;\nif (!token) {\n node.error(\"No s'ha definit un token d'identificació\");\n return null;\n}\n\n// Preparació del node http-request\nconst provider = reqParams.provider;\nconst host = sentiloConfig.api.host;\nconst request = host + \"/data/\" + provider;\n\nmsg.url = request;\nmsg.method = 'PUT';\nmsg.httpRequestTimeout = 300000; // 5 minuts\nmsg.headers = {\n 'identity_key': token\n}\n\nmsg.payload = observations;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":420,"y":520,"wires":[["062e6d53e2b9e146","ec1c65b8866eba20"]]},{"id":"ec1c65b8866eba20","type":"http request","z":"2034170d90991aa9","name":"publishObservations","method":"use","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"x":660,"y":520,"wires":[["af91f47375bc44fb","4fd476e7b2e95b4a"]]},{"id":"d5e70ef211e6dfb6","type":"comment","z":"2034170d90991aa9","name":"globals.publishObservations: Publicar observacions","info":"","x":250,"y":480,"wires":[]},{"id":"132d1417ab5647e1","type":"link in","z":"2034170d90991aa9","name":"globals.publishObservations","links":[],"x":75,"y":520,"wires":[["bad0dc548829ab9b"]]},{"id":"4fd476e7b2e95b4a","type":"link out","z":"2034170d90991aa9","name":"publishObservations","mode":"return","links":[],"x":795,"y":520,"wires":[]},{"id":"062e6d53e2b9e146","type":"debug","z":"2034170d90991aa9","name":"publishObservations request","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":680,"y":480,"wires":[]},{"id":"af91f47375bc44fb","type":"debug","z":"2034170d90991aa9","name":"publishObservations response","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":970,"y":520,"wires":[]},{"id":"af2c415c070f71dd","type":"debug","z":"2034170d90991aa9","name":"publishObservations reqParams","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"reqParams","targetType":"msg","statusVal":"","statusType":"auto","x":250,"y":560,"wires":[]},{"id":"c632e29834309a66","type":"link call","z":"2034170d90991aa9","name":"","links":["952d5523eb74378b"],"timeout":"30","x":200,"y":660,"wires":[["7cdd6f4cb7bb14e3","3c923a1211df39ba"]]},{"id":"7cdd6f4cb7bb14e3","type":"function","z":"2034170d90991aa9","name":"getLastObservations","func":"// Node funcional de recuperació de dades de Sentilo\n\nconst flowConfig = msg.flowConfig || undefined;\nif (!flowConfig) {\n node.error(\"No hem pogut carregar el flowConfig\");\n return null;\n}\n\nconst sentiloConfig = flowConfig.sentilo || undefined;\nif (!sentiloConfig) {\n node.error(\"No hem pogut carregar la configuració necessària (sentiloConfig)\");\n return null;\n}\n\nconst catalogConfig = sentiloConfig.catalog || undefined;\nif (!catalogConfig) {\n node.error(\"No hem pogut carregar la configuració necessària (catalogConfig)\");\n return null;\n}\n\nconst reqParams = msg.reqParams || undefined;\nif (!reqParams) {\n node.error(\"No hem pogut carregar els paràmetres de cerca (reqParams)\");\n return null;\n}\n\nif (!reqParams.ens || !reqParams.provider) {\n node.error(\"Falten dades de filtrat o configuració de la crida (msg.reqParams.ens / msg.reqParams.provider)\");\n return null;\n}\n\nconst observations = msg.payload || undefined;\nif (!observations) { \n node.error(\"Sense observacions a publicar\");\n return null;\n}\n\nconst token = reqParams.token || sentiloConfig.api.token;\nconst provider = reqParams.provider;\n\nlet queryParams = \"?\";\nconst filters = reqParams.filters || undefined;\nif (filters) {\n const fromDate = filters.from || undefined;\n const toDate = filters.to || undefined;\n const limit = filters.limit || undefined;\n if (fromDate) queryParams = queryParams + \"from=\"+fromDate + \"&\"\n if (toDate) queryParams = queryParams + \"to=\"+toDate + \"&\"\n if (limit) queryParams = queryParams + \"limit=\"+limit + \"&\"\n}\n\n\n// Preparació del node http-request\nconst host = sentiloConfig.api.host;\nconst request = host + \"/data/\" + provider + queryParams;\n\nmsg.url = request;\nmsg.method = 'GET';\nmsg.headers = {\n 'identity_key': token\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":420,"y":660,"wires":[["8a4df996e5135bfc","700afd2b3401c224"]]},{"id":"8a4df996e5135bfc","type":"http request","z":"2034170d90991aa9","name":"getLastObservations","method":"use","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"x":660,"y":660,"wires":[["43910ffa6d5b2c10","817503fdb8c1e587"]]},{"id":"23811721d042c3a4","type":"comment","z":"2034170d90991aa9","name":"globals.getLastObservations: Obtenir les darreres observacions","info":"","x":290,"y":620,"wires":[]},{"id":"76f120cfc1f02390","type":"link in","z":"2034170d90991aa9","name":"globals.getLastObservations","links":[],"x":75,"y":660,"wires":[["c632e29834309a66"]]},{"id":"43910ffa6d5b2c10","type":"link out","z":"2034170d90991aa9","name":"getLastObservations","mode":"return","links":[],"x":795,"y":660,"wires":[]},{"id":"700afd2b3401c224","type":"debug","z":"2034170d90991aa9","name":"getLastObservations request","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":680,"y":620,"wires":[]},{"id":"817503fdb8c1e587","type":"debug","z":"2034170d90991aa9","name":"getLastObservations response","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":970,"y":660,"wires":[]},{"id":"3c923a1211df39ba","type":"debug","z":"2034170d90991aa9","name":"getObservations reqParams","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"reqParams","targetType":"msg","statusVal":"","statusType":"auto","x":240,"y":700,"wires":[]},{"id":"4cdf585441629059","type":"function","z":"2034170d90991aa9","name":"sensorToCreate","func":"// Node funcional que prepara una llista de sensors que cal crear a Sentilo\n\nconst flowConfig = msg.flowConfig || undefined;\nif (!flowConfig) {\n node.error(\"No hem pogut carregar el flowConfig\");\n return null;\n}\n\nconst sentiloConfig = flowConfig.sentilo || undefined;\nif (!sentiloConfig) {\n node.error(\"No hem pogut carregar la configuració necessària (sentiloConfig)\");\n return null;\n}\n\nconst catalogConfig = sentiloConfig.catalog || undefined;\nif (!catalogConfig) {\n node.error(\"No hem pogut carregar la configuració necessària (catalogConfig)\");\n return null;\n}\n\nconst reqParams = msg.reqParams || undefined;\nif (!reqParams) {\n node.error(\"No hem pogut carregar els paràmetres de cerca (reqParams)\");\n return null;\n}\n\n// Configuració addicional del component\n// També es pot indicar dintre de la definició del sensor (si és el cas, no hem de fer l'actualització de la línia 49)\nconst component = catalogConfig.component || undefined;\nif (!component) {\n node.error(\"No hem pogut carregar la configuració del component (msg.flowConfig.catalog.component)\");\n return null;\n}\n\n// Dades del catàleg\nconst providers = msg.payload?.providers || undefined;\n\n// Sensors que han d'existir\nconst sensorsToExist = catalogConfig.sensors || {};\n\n// Si no hi ha proveïdors, tots els sensors s'han de crear\nlet sensorsToCreate = [];\nif (!providers) {\n sensorsToCreate = Object.values(sensorsToExist);\n} else {\n // Obtenir la llista de sensors existents als proveïdors\n const allSensorsFromProviders = providers.flatMap(provider => Array.isArray(provider.sensors) ? provider.sensors.map(sensor => sensor.sensor) : []);\n\n // Filtrar els sensors que no existeixen als proveïdors\n sensorsToCreate = Object.values(sensorsToExist).filter(sensor => !allSensorsFromProviders.includes(sensor.sensor));\n}\n\nmsg.reqParams.sensorsToExist = sensorsToExist;\nmsg.reqParams.sensorsToCreate = sensorsToCreate.map(sensor => ({\n ...sensor,\n component: component.component,\n componentType: component.componentType,\n componentDesc: component.componentDesc,\n componentPublicAccess: component.componentPublicAccess\n}));\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":980,"wires":[["6a1b3ad6060deec2","89d9b875ea57c50d","cf5ae969297ec92b"]]},{"id":"6a1b3ad6060deec2","type":"debug","z":"2034170d90991aa9","name":"sensorsToCreate","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"reqParams.sensorsToCreate","targetType":"msg","statusVal":"","statusType":"auto","x":750,"y":980,"wires":[]},{"id":"89d9b875ea57c50d","type":"switch","z":"2034170d90991aa9","name":"Crear sensors?","property":"reqParams.sensorsToCreate","propertyType":"msg","rules":[{"t":"nempty"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":300,"y":1060,"wires":[["54a6572eb0c2170b"],["e117e3eaef30eb60"]]},{"id":"54a6572eb0c2170b","type":"link call","z":"2034170d90991aa9","name":"","links":["e3ffc67fb0e48b9c"],"timeout":"30","x":530,"y":1040,"wires":[["507245d18cc30bf1"]]},{"id":"507245d18cc30bf1","type":"delay","z":"2034170d90991aa9","name":"","pauseType":"delay","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":720,"y":1040,"wires":[["e117e3eaef30eb60"]]},{"id":"5e3a8a64dc6b684e","type":"function","z":"2034170d90991aa9","name":"preparePublishObservations","func":"// Node funcional que prepara les dades per ser publicades\n\n// Mètode que genera dades aleatòries\nfunction getRandomValue(type, min = 0, max = 1) {\n switch (type) {\n case 'boolean':\n return Math.random() < 0.5;\n case 'integer':\n return Math.floor(Math.random() * (max - min + 1)) + min;\n case 'float':\n return Math.random() * (max - min) + min;\n default:\n throw new Error(\"Tipu no vàlid. Fes servir: 'boolean', 'integer' o 'float'.\");\n }\n}\n\n// Simulem l'obtenció de les dades i les publiquem\n// Preparem dades de test (ramdom)\nconst obs001 = getRandomValue('boolean'); // tipus 'status'\nconst obs002 = getRandomValue('float', 18.0, 35.0); // tipus 'temperature', valor random entre 18 i 35\nconst obs003 = getRandomValue('integer', 75, 100); // tipus 'battery', valor random entre 75 i 100\n\n// Creem el missatge a publicar amb les dades\nconst sensors = {\n \"sensors\":[\n {\n \"sensor\":\"global001\",\n \"observations\":[\n {\"value\": obs001}\n ]\n }, {\n \"sensor\":\"global002\",\n \"observations\":[\n {\"value\": obs002}\n ]\n }, {\n \"sensor\":\"global003\",\n \"observations\":[\n {\"value\": obs003}\n ]\n }, \n ]\n};\n\nmsg.payload = sensors;\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":560,"y":1220,"wires":[["58e29dcba58475dd","3c465962e206606b"]]},{"id":"58e29dcba58475dd","type":"debug","z":"2034170d90991aa9","name":"preparePublishObservations","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":860,"y":1220,"wires":[]},{"id":"cf5ae969297ec92b","type":"debug","z":"2034170d90991aa9","name":"sensorsToExist","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"reqParams.sensorsToExist","targetType":"msg","statusVal":"","statusType":"auto","x":740,"y":940,"wires":[]},{"id":"3c465962e206606b","type":"link call","z":"2034170d90991aa9","name":"","links":["132d1417ab5647e1"],"timeout":"30","x":560,"y":1280,"wires":[["c7de8eb557c47f1e"]]},{"id":"c7de8eb557c47f1e","type":"debug","z":"2034170d90991aa9","name":"publishObservations","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":840,"y":1280,"wires":[]},{"id":"e117e3eaef30eb60","type":"function","z":"2034170d90991aa9","name":"prepareGetLastObservations","func":"// Node funcional que recupera les darreres dades dels sensors\n\nconst reqParams = msg.reqParams || undefined;\nif (!reqParams) {\n node.error(\"No hem pogut carregar els paràmetres de cerca (reqParams)\");\n return null;\n}\n\n// Preparem possibles filtres de la query a Sentilo\n// Doc: https://sentilo.readthedocs.io/en/latest/api_docs/services/data/retrieve_provider_sensor_data.html#parameteres\nreqParams.filters = reqParams.filters || {};\n\n// Afgim un límit (opcional)\n// reqParams.filters.limit = 1000; \n\n// Afgim una data inici per a la cerca (opcional)\n// reqParams.filters.from = '27/03/2025T00:00:00';\n\n// Afgim una data fi per a la cerca (opcional)\n// reqParams.filters.to = '27/03/2025T23:59:59';\n\nmsg.reqParams = reqParams;\n\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":560,"y":1100,"wires":[["1180d79b90af2161"]]},{"id":"1180d79b90af2161","type":"link call","z":"2034170d90991aa9","name":"","links":["76f120cfc1f02390"],"timeout":"30","x":820,"y":1100,"wires":[["32db90f57562181f","750778890861bc8d"]]},{"id":"32db90f57562181f","type":"debug","z":"2034170d90991aa9","name":"getLastObservations","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1100,"y":1100,"wires":[]},{"id":"750778890861bc8d","type":"function","z":"2034170d90991aa9","name":"processLastObservations","func":"// Node funcional que processa les dades obtingudes\n// Si volem vasar la nostra publicació d'observacions en les dades prèvies\n// podem consultar-les en aquest punt, per tal de fer alguna crida addicional a un sistema extern\n\nconst observations = msg.payload.sensors || [];\nobservations.forEach(observation => {\n const sensor = observation.sensor;\n const date = observation.date;\n const value = observation.value;\n const time = observation.time;\n \n // Tractar les dades... \n \n // Preparar una possible crida a una api intermitja per obtenir les dades...\n \n});\n\n// En aquest punt prepararíem una possible crida a un servidor extern\n// Recuperarem les dades en el node httpRequest\n// Passarem el resultat al següent node de funció \"preparePublishObservations\"\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":1160,"wires":[["5e3a8a64dc6b684e"]]},{"id":"5fb0c1c08f9e1168","type":"debug","z":"2034170d90991aa9","name":"end","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":750,"y":840,"wires":[]},{"id":"66264f99a9229fbd","type":"debug","z":"2034170d90991aa9","name":"reqParams","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"reqParams","targetType":"msg","statusVal":"","statusType":"auto","x":510,"y":920,"wires":[]},{"id":"e8da3cf98152125a","type":"comment","z":"2034170d90991aa9","name":"Fux de test","info":"","x":120,"y":760,"wires":[]}]