const { BaseKonnector, log, addData, hydrateAndFilter, cozyClient } = require('cozy-konnector-libs') 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 /*** Connector Constants ***/ const startDate = moment() .subtract(32, 'month') .format('YYYY-MM-DD') const endDate = moment().format('YYYY-MM-DD') const baseUrl = 'https://gw.hml.api.enedis.fr' // The start function is run by the BaseKonnector instance only when it got all the account // information (fields). When you run this connector yourself in "standalone" mode or "dev" mode, // the account information come from ./konnector-dev-config.json file // cozyParameters are static parameters, independents from the account. Most often, it can be a // secret api key. async function start(fields) { try { const { access_token } = fields const usage_point_id = 22516914714270 log('info', 'Fetching enedis data') const fetchedData = await getDailyData(access_token, usage_point_id) log('info', 'Process data') await processData(fetchedData) log('info', 'Saving data to Cozy') } catch (err) { log('error', err.message) } } // Retrieve data from the API // Format: { value: "Wh", "date": "YYYY-MM-DD" } async function getDailyData(token, usagePointID) { const dataRequest = { method: 'GET', uri: baseUrl + '/v4/metering_data/daily_consumption?start=' + startDate + '&end=' + endDate + '&usage_point_id=' + usagePointID, headers: { Accept: 'application/json', Authorization: 'Bearer ' + token } } try { const response = await rp(dataRequest) return response } catch (error) { throw error } } async function processData(data) { const parsedData = JSON.parse(data) const intervalData = parsedData.meter_reading.interval_reading const formatedData = await formateData(intervalData) // Remove data for existing days into the DB const filteredData = await hydrateAndFilter(formatedData, 'io.enedis.day', { keys: ['year', 'month', 'day'] }) // Store new day data await storeData(filteredData, 'io.enedis.day', ['year', 'month', 'day']) // Sum year and month values into object with year or year-month as keys if (filteredData && filteredData.length > 0) { let yearData = {} let monthData = {} filteredData.forEach(element => { element.year in yearData ? (yearData[element.year] += element.load) : (yearData[element.year] = element.load) element.year + '-' + element.month in monthData ? (monthData[element.year + '-' + element.month] += element.load) : (monthData[element.year + '-' + element.month] = element.load) }) const agregatedYearData = await agregateData('io.enedis.year', yearData) await storeData(agregatedYearData, 'io.enedis.year', ['year']) const agregatedMonthData = await agregateData('io.enedis.month', monthData) await storeData(agregatedMonthData, 'io.enedis.month', ['year', 'month']) } } /** * Save data in the right doctype db and prevent duplicated keys */ async function storeData(data, doctype, filterKeys) { log('debug', doctype, 'Store into') const filteredDocuments = await hydrateAndFilter(data, doctype, { keys: filterKeys }) return await addData(filteredDocuments, doctype) } /** * Format data for DB storage * Remove bad data */ async function formateData(data) { log('info', 'Formating data') log('debug', start, 'Start date') return data.map(record => { const date = moment(record.date, 'YYYY/MM/DD') if (record.value != -2) { return { load: parseFloat(record.value / 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')) } } }) } /** * Retrieve and remove old data for a specific doctype * Return an Array of agregated data */ async function agregateData(doctype, data) { let agregatedData = [] for (let [key, value] of Object.entries(data)) { const data = await buildDataFromKey(doctype, key, value) const oldValue = await resetInProgressAggregatedData(doctype, data) data.load += oldValue agregatedData.push(data) } return agregatedData } /** * Format an entry for DB storage * using key and value * For year doctype: key = "YYYY" * For month doctype: key = "YYYY-MM" */ async function buildDataFromKey(doctype, key, value) { let year, month if (doctype === 'io.enedis.year') { year = key month = 1 } else { const split = key.split('-') year = split[0] month = split[1] } return { load: Math.round(value * 1000) / 1000, year: parseInt(year), month: parseInt(month), day: 0, hour: 0, minute: 0 } } /** * Function handling special case. * The temporary aggregated data need to be remove in order for the most recent one te be saved. * ex for io.enedis.year : * { load: 76.712, year: 2020, ... } need to be replace by * { load: 82.212, year: 2020, ... } after enedis data reprocess */ async function resetInProgressAggregatedData(doctype, data) { // /!\ 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) if (result && result.length > 0) { // Filter data to remove var filtered = [] if (doctype === 'io.enedis.year') { // Yearly case filtered = result.filter(function(el) { return el.year == data.year }) } else { // Monthly case filtered = result.filter(function(el) { return el.year == data.year && el.month == data.month }) } // Remove data 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) } return sum } return 0.0 } module.exports = new BaseKonnector(start)