diff --git a/src/index.js b/src/index.js index 4bb82b0d60b18fdb96746c2a024e6247fb70d237..80f2fc3c6a1057c737da7a4ac2afdfaf276ce086 100644 --- a/src/index.js +++ b/src/index.js @@ -5,25 +5,25 @@ const { hydrateAndFilter, errors, cozyClient -} = require("cozy-konnector-libs"); +} = require('cozy-konnector-libs') -const getAccountId = require("./helpers/getAccountId"); +const getAccountId = require('./helpers/getAccountId') -const moment = require("moment"); -const rp = require("request-promise"); -require("moment-timezone"); +const moment = require('moment') +const rp = require('request-promise') +require('moment-timezone') -moment.locale("fr"); // set the language -moment.tz.setDefault("Europe/Paris"); // set the timezone +moment.locale('fr') // set the language +moment.tz.setDefault('Europe/Paris') // set the timezone /*** Connector Constants ***/ -const startDailyDate = moment().subtract(32, "month"); -const startDailyDateString = startDailyDate.format("YYYY-MM-DD"); -const startLoadDate = moment().subtract(7, "day"); -const startLoadDateString = startLoadDate.format("YYYY-MM-DD"); -const endDate = moment(); -const endDateString = endDate.format("YYYY-MM-DD"); -const baseUrl = "https://gw.prd.api.enedis.fr"; +const startDailyDate = moment().subtract(32, 'month') +const startDailyDateString = startDailyDate.format('YYYY-MM-DD') +const startLoadDate = moment().subtract(7, 'day') +const startLoadDateString = startLoadDate.format('YYYY-MM-DD') +const endDate = moment() +const endDateString = endDate.format('YYYY-MM-DD') +const baseUrl = 'https://gw.prd.api.enedis.fr' /** * The start function is run by the BaseKonnector instance only when it got all the account @@ -38,67 +38,67 @@ const baseUrl = "https://gw.prd.api.enedis.fr"; * @param {boolean} doRetry - whether we should use the refresh token or not */ async function start(fields, cozyParameters, doRetry = true) { - log("info", "Starting the enedis konnector"); - const accountId = getAccountId(); + log('info', 'Starting the enedis konnector') + const accountId = getAccountId() try { - const { access_token } = fields; - let usage_point_id = ""; + const { access_token } = fields + let usage_point_id = '' if ( this._account && this._account.oauth_callback_results && this._account.oauth_callback_results.usage_points_id ) { const usage_points_id = this._account.oauth_callback_results.usage_points_id.split( - "," - ); - usage_point_id = usage_points_id[0]; + ',' + ) + usage_point_id = usage_points_id[0] } else { - log("error", "no usage_point_id found"); - throw errors.USER_ACTION_NEEDED_OAUTH_OUTDATED; + log('error', 'no usage_point_id found') + throw errors.USER_ACTION_NEEDED_OAUTH_OUTDATED } - log("info", "Fetching enedis daily data"); - const fetchedDailyData = await getDailyData(access_token, usage_point_id); - log("info", "Process enedis daily data"); + log('info', 'Fetching enedis daily data') + const fetchedDailyData = await getDailyData(access_token, usage_point_id) + log('info', 'Process enedis daily data') const processedDailyData = await processData( fetchedDailyData, - "com.grandlyon.enedis.day", - ["year", "month", "day"] - ); - log("info", "Agregate enedis daily data for month and year"); - await agregateMonthAndYearData(processedDailyData); + 'com.grandlyon.enedis.day', + ['year', 'month', 'day'] + ) + log('info', 'Agregate enedis daily data for month and year') + await agregateMonthAndYearData(processedDailyData) - log("info", "Process enedis load data"); - await startLoadDataProcess(access_token, usage_point_id); + log('info', 'Process enedis load data') + await startLoadDataProcess(access_token, usage_point_id) } catch (err) { if (err.statusCode === 403 || err.code === 403) { if (!fields.refresh_token) { - log("info", "no refresh token found"); - throw errors.USER_ACTION_NEEDED_OAUTH_OUTDATED; + log('info', 'no refresh token found') + throw errors.USER_ACTION_NEEDED_OAUTH_OUTDATED } else if (doRetry) { - log("info", "asking refresh from the stack"); - let body; + log('info', 'asking refresh from the stack') + let body try { body = await cozyClient.fetchJSON( - "POST", + 'POST', `/accounts/enedisgrandlyon/${accountId}/refresh` - ); + ) } catch (err) { - log("info", `Error during refresh ${err.message}`); - throw errors.USER_ACTION_NEEDED_OAUTH_OUTDATED; + log('info', `Error during refresh ${err.message}`) + throw errors.USER_ACTION_NEEDED_OAUTH_OUTDATED } - log("info", "refresh response"); - log("info", JSON.stringify(body)); - fields.access_token = body.attributes.oauth.access_token; - return start(fields, cozyParameters, false); + log('info', 'refresh response') + log('info', JSON.stringify(body)) + fields.access_token = body.attributes.oauth.access_token + return start(fields, cozyParameters, false) } - log("error", `Error during authentication: ${err.message}`); - throw errors.VENDOR_DOWN; + log('error', `Error during authentication: ${err.message}`) + throw errors.VENDOR_DOWN } else { - log("error", "caught an unexpected error"); - log("error", err.message); - throw errors.VENDOR_DOWN; + log('error', 'caught an unexpected error') + log('error', err.message) + throw errors.VENDOR_DOWN } } } @@ -109,22 +109,22 @@ async function start(fields, cozyParameters, doRetry = true) { */ async function getDailyData(token, usagePointID) { const dataRequest = { - method: "GET", + method: 'GET', uri: baseUrl + - "/v4/metering_data/daily_consumption?start=" + + '/v4/metering_data/daily_consumption?start=' + startDailyDateString + - "&end=" + + '&end=' + endDateString + - "&usage_point_id=" + + '&usage_point_id=' + usagePointID, headers: { - Accept: "application/json", - Authorization: "Bearer " + token + Accept: 'application/json', + Authorization: 'Bearer ' + token } - }; - const response = await rp(dataRequest); - return response; + } + const response = await rp(dataRequest) + return response } /** @@ -133,27 +133,29 @@ async function getDailyData(token, usagePointID) { * If yes only call once the api */ async function startLoadDataProcess(token, usagePointID) { - log("info", "Check history"); - const isHistory = await isHistoryLoaded("com.grandlyon.enedis.minute"); + log('info', 'Check history') + const isHistory = await isHistoryLoaded('com.grandlyon.enedis.minute') if (isHistory) { - log("info", "launch process without history"); + log('info', 'launch process without history') await launchLoadDataProcess( token, usagePointID, startLoadDateString, endDateString - ); + ) } else { - log("info", "launch process with history"); - const increamentedStartDate = startLoadDate; - const incrementedEndDate = endDate; + log('info', 'launch process with history') for (var i = 0; i < 4; i++) { + const increamentedStartDate = moment(startLoadDate) + const incrementedEndDate = moment(endDate) + const increamentedStartDateString = increamentedStartDate.subtract(7 * i, 'day').format('YYYY-MM-DD') + const incrementedEndDateString = incrementedEndDate.subtract(7 * i, 'day').format('YYYY-MM-DD') await launchLoadDataProcess( token, usagePointID, - increamentedStartDate.subtract(7 * i, "day").format("YYYY-MM-DD"), - incrementedEndDate.subtract(7 * i, "day").format("YYYY-MM-DD") - ); + increamentedStartDateString, + incrementedEndDateString + ) } } } @@ -167,24 +169,24 @@ async function launchLoadDataProcess( _startLoadDate, _endDate ) { - log("info", "Fetching enedis load data"); + log('info', 'Fetching enedis load data') const fetchedLoadData = await getLoadData( token, usagePointID, _startLoadDate, _endDate - ); + ) if (fetchedLoadData && fetchedLoadData.length > 0) { - log("info", "Process enedis load data"); + log('info', 'Process enedis load data') const processedLoadData = await processData( fetchedLoadData, - "com.grandlyon.enedis.minute", - ["year", "month", "day", "hour", "minute"] - ); - log("info", "Agregate enedis load data for hour"); - await agregateHourlyData(processedLoadData); + 'com.grandlyon.enedis.minute', + ['year', 'month', 'day', 'hour', 'minute'] + ) + log('info', 'Agregate enedis load data for hour') + await agregateHourlyData(processedLoadData) } else { - log("info", "No consent or data for load curve"); + log('info', 'No consent or data for load curve') } } @@ -194,31 +196,31 @@ async function launchLoadDataProcess( */ async function getLoadData(token, usagePointID, _startDate, _endDate) { const dataRequest = { - method: "GET", + method: 'GET', uri: baseUrl + - "/v4/metering_data/consumption_load_curve?start=" + + '/v4/metering_data/consumption_load_curve?start=' + _startDate + - "&end=" + + '&end=' + _endDate + - "&usage_point_id=" + + '&usage_point_id=' + usagePointID, headers: { - Accept: "application/json", - Authorization: "Bearer " + token + Accept: 'application/json', + Authorization: 'Bearer ' + token } - }; + } try { - const response = await rp(dataRequest); - return response; + const response = await rp(dataRequest) + return response } catch (err) { if (err.statusCode === 404 || err.code === 404) { - log("warning", "caught an 404 error"); - log("warning", err.message); - log("warning", err); - return null; + log('warning', 'caught an 404 error') + log('warning', err.message) + log('warning', err) + return null } else { - throw err; + throw err } } } @@ -230,16 +232,16 @@ async function getLoadData(token, usagePointID, _startDate, _endDate) { * Return the list of filtered data */ async function processData(data, doctype, filterKeys) { - const parsedData = JSON.parse(data); - const intervalData = parsedData.meter_reading.interval_reading; - const formatedData = await formateData(intervalData, doctype); + const parsedData = JSON.parse(data) + const intervalData = parsedData.meter_reading.interval_reading + const formatedData = await formateData(intervalData, doctype) // Remove data for existing days into the DB const filteredData = await hydrateAndFilter(formatedData, doctype, { keys: filterKeys - }); + }) // Store new day data - await storeData(filteredData, doctype, filterKeys); - return filteredData; + await storeData(filteredData, doctype, filterKeys) + return filteredData } /** @@ -248,31 +250,31 @@ async function processData(data, doctype, filterKeys) { async function agregateMonthAndYearData(data) { // Sum year and month values into object with year or year-month as keys if (data && data.length > 0) { - let monthData = {}; - let yearData = {}; + let monthData = {} + let yearData = {} data.forEach(element => { - element.year + "-" + element.month in monthData - ? (monthData[element.year + "-" + element.month] += element.load) - : (monthData[element.year + "-" + element.month] = element.load); + element.year + '-' + element.month in monthData + ? (monthData[element.year + '-' + element.month] += element.load) + : (monthData[element.year + '-' + element.month] = element.load) element.year in yearData ? (yearData[element.year] += element.load) - : (yearData[element.year] = element.load); - }); + : (yearData[element.year] = element.load) + }) // Agregation for Month data const agregatedMonthData = await buildAgregatedData( monthData, - "com.grandlyon.enedis.month" - ); - await storeData(agregatedMonthData, "com.grandlyon.enedis.month", [ - "year", - "month" - ]); + 'com.grandlyon.enedis.month' + ) + await storeData(agregatedMonthData, 'com.grandlyon.enedis.month', [ + 'year', + 'month' + ]) // Agregation for Year data const agregatedYearData = await buildAgregatedData( yearData, - "com.grandlyon.enedis.year" - ); - await storeData(agregatedYearData, "com.grandlyon.enedis.year", ["year"]); + 'com.grandlyon.enedis.year' + ) + await storeData(agregatedYearData, 'com.grandlyon.enedis.year', ['year']) } } @@ -282,31 +284,31 @@ async function agregateMonthAndYearData(data) { async function agregateHourlyData(data) { // Sum year and month values into object with year or year-month as keys if (data && data.length > 0) { - let hourData = {}; + let hourData = {} data.forEach(element => { let key = element.year + - "-" + + '-' + element.month + - "-" + + '-' + element.day + - "-" + - element.hour; + '-' + + element.hour key in hourData ? (hourData[key] += element.load) - : (hourData[key] = element.load); - }); + : (hourData[key] = element.load) + }) // Agregation for Month data const agregatedMonthData = await buildAgregatedData( hourData, - "com.grandlyon.enedis.hour" - ); - await storeData(agregatedMonthData, "com.grandlyon.enedis.hour", [ - "year", - "month", - "day", - "hour" - ]); + 'com.grandlyon.enedis.hour' + ) + await storeData(agregatedMonthData, 'com.grandlyon.enedis.hour', [ + 'year', + 'month', + 'day', + 'hour' + ]) } } @@ -314,11 +316,11 @@ async function agregateHourlyData(data) { * Save data in the right doctype db and prevent duplicated keys */ async function storeData(data, doctype, filterKeys) { - log("debug", doctype, "Store into"); + log('debug', doctype, 'Store into') const filteredDocuments = await hydrateAndFilter(data, doctype, { keys: filterKeys - }); - return await addData(filteredDocuments, doctype); + }) + return await addData(filteredDocuments, doctype) } /** @@ -326,27 +328,27 @@ async function storeData(data, doctype, filterKeys) { * Remove bad data */ async function formateData(data, doctype) { - log("info", "Formating data"); + log('info', 'Formating data') return data.map(record => { - let date = moment(record.date, "YYYY/MM/DD h:mm:ss"); + let date = moment(record.date, 'YYYY/MM/DD h:mm:ss') if (record.value != -2) { const load = - doctype === "com.grandlyon.enedis.minute" + doctype === 'com.grandlyon.enedis.minute' ? record.value / 2 - : record.value; - if (doctype === "com.grandlyon.enedis.minute") { - date = date.subtract(30, "minute"); + : record.value + if (doctype === 'com.grandlyon.enedis.minute') { + date = date.subtract(30, 'minute') } return { load: parseFloat(load / 1000), - year: parseInt(date.format("YYYY")), - month: parseInt(date.format("M")), - day: parseInt(date.format("D")), - hour: parseInt(date.format("H")), - minute: parseInt(date.format("m")) - }; + year: parseInt(date.format('YYYY')), + month: parseInt(date.format('M')), + day: parseInt(date.format('D')), + hour: parseInt(date.format('H')), + minute: parseInt(date.format('m')) + } } - }); + }) } /** @@ -354,14 +356,14 @@ async function formateData(data, doctype) { * Return an Array of agregated data */ async function buildAgregatedData(data, doctype) { - let agregatedData = []; + let agregatedData = [] for (let [key, value] of Object.entries(data)) { - const data = await buildDataFromKey(doctype, key, value); - const oldValue = await resetInProgressAggregatedData(data, doctype); - data.load += oldValue; - agregatedData.push(data); + const data = await buildDataFromKey(doctype, key, value) + const oldValue = await resetInProgressAggregatedData(data, doctype) + data.load += oldValue + agregatedData.push(data) } - return agregatedData; + return agregatedData } /** @@ -371,24 +373,24 @@ async function buildAgregatedData(data, doctype) { * For month doctype: key = "YYYY-MM" */ async function buildDataFromKey(doctype, key, value) { - let year, month, day, hour; - if (doctype === "com.grandlyon.enedis.year") { - year = key; - month = 1; - day = 0; - hour = 0; - } else if (doctype === "com.grandlyon.enedis.month") { - const split = key.split("-"); - year = split[0]; - month = split[1]; - day = 0; - hour = 0; + let year, month, day, hour + if (doctype === 'com.grandlyon.enedis.year') { + year = key + month = 1 + day = 0 + hour = 0 + } else if (doctype === 'com.grandlyon.enedis.month') { + const split = key.split('-') + year = split[0] + month = split[1] + day = 0 + hour = 0 } else { - const split = key.split("-"); - year = split[0]; - month = split[1]; - day = split[2]; - hour = split[3]; + const split = key.split('-') + year = split[0] + month = split[1] + day = split[2] + hour = split[3] } return { load: Math.round(value * 10000) / 10000, @@ -397,30 +399,30 @@ async function buildDataFromKey(doctype, key, value) { day: parseInt(day), hour: parseInt(hour), minute: 0 - }; + } } /** * Function checking if the history is loaded */ async function isHistoryLoaded(doctype) { - log("debug", doctype, "Retrieve data"); - const result = await cozyClient.data.findAll(doctype); + log('debug', doctype, 'Retrieve data') + const result = await cozyClient.data.findAll(doctype) if (result && result.length > 0) { const filtered = result.filter(function(el) { return ( el.year <= startLoadDate.year && el.month <= startLoadDate.month && el.day <= startLoadDate.day - ); - }); + ) + }) if (filtered.length > 0) { - return false; + return false } else { - return true; + return true } } - return false; + return false } /** @@ -432,21 +434,21 @@ async function isHistoryLoaded(doctype) { */ async function resetInProgressAggregatedData(data, doctype) { // /!\ Warning: cannot use mongo queries because not supported for dev by cozy-konnectors-libs - log("debug", doctype, "Remove aggregated data for"); - const result = await cozyClient.data.findAll(doctype); + log('debug', doctype, 'Remove aggregated data for') + const result = await cozyClient.data.findAll(doctype) if (result && result.length > 0) { // Filter data to remove - var filtered = []; - if (doctype === "com.grandlyon.enedis.year") { + var filtered = [] + if (doctype === 'com.grandlyon.enedis.year') { // Yearly case filtered = result.filter(function(el) { - return el.year == data.year; - }); - } else if (doctype === "com.grandlyon.enedis.month") { + return el.year == data.year + }) + } else if (doctype === 'com.grandlyon.enedis.month') { // Monthly case filtered = result.filter(function(el) { - return el.year == data.year && el.month == data.month; - }); + return el.year == data.year && el.month == data.month + }) } else { // Hourly case filtered = result.filter(function(el) { @@ -455,19 +457,19 @@ async function resetInProgressAggregatedData(data, doctype) { el.month == data.month && el.day == data.day && el.hour == data.hour - ); - }); + ) + }) } // Remove data - let sum = 0.0; + let sum = 0.0 for (const doc of filtered) { - sum += doc.load; - log("debug", doc, "Removing this entry for " + doctype); - await cozyClient.data.delete(doctype, doc); + sum += doc.load + log('debug', doc, 'Removing this entry for ' + doctype) + await cozyClient.data.delete(doctype, doc) } - return sum; + return sum } - return 0.0; + return 0.0 } -module.exports = new BaseKonnector(start); +module.exports = new BaseKonnector(start)