@bokub/linky
Version:
Easily retrieve your Linky power consumption
187 lines (156 loc) • 5.53 kB
JavaScript
const qs = require('querystring');
const axios = require('axios');
const dayjs = require('dayjs');
const enedisNotice = ' - Check the Enedis website if the error persists';
const get = (p, o) => p.reduce((xs, x) => (xs && xs[x]) ? xs[x] : null, o);
const parseDate = dt => dayjs(`${dt.substr(6, 4)}-${dt.substr(3, 2)}-${dt.substr(0, 2)}`);
async function login(email, password) {
const body = qs.stringify({
IDToken1: email,
IDToken2: password,
SunQueryParamsString: 'cmVhbG09cGFydGljdWxpZXJz', // Base64 of 'realm=particuliers'
encoded: true,
gx_charset: 'UTF-8'
});
const uri = 'https://espace-client-connexion.enedis.fr/auth/UI/Login';
try {
await axios.post(uri, body, {
maxRedirects: 0
});
} catch (err) {
if (!err.response || err.response.status !== 302) {
throw new Error('Unexpected login response (2): ' + err.message + enedisNotice);
}
if (!err.response.headers || !err.response.headers['set-cookie']) {
console.log(err.response);
console.log(err.response.headers);
throw new Error('Unexpected login response (3)' + enedisNotice);
}
const cookies = err.response.headers['set-cookie']
.filter(el => el.indexOf('Domain=.enedis.fr') > -1)
.filter(el => el.indexOf('Expires=Thu, 01-Jan-1970 00:00:10 GMT') === -1);
if (!cookies || cookies.length === 0) {
throw new Error('Unexpected login response (4)' + enedisNotice);
}
const authCookies = cookies.filter(h => h.indexOf('iPlanetDirectoryPro=') === 0);
if (authCookies.length === 0) {
throw new Error('The email or the password is incorrect');
}
return new LinkySession(cookies);
}
throw new Error('Unexpected login response (1)' + enedisNotice);
}
class LinkySession {
constructor(cookies) {
this.cookies = cookies;
this.calledOnce = false;
}
async getHourlyData(opts) {
opts = LinkySession.parseOpts(opts, dayjs().add(-1, 'day'), dayjs());
const data = await this.getData('urlCdcHeure', opts);
return LinkySession.parseData(data, 0.5, 'hour');
}
async getDailyData(opts) {
opts = LinkySession.parseOpts(opts, dayjs().add(-1, 'day').add(-1, 'month'), dayjs().add(-1, 'day'));
const data = await this.getData('urlCdcJour', opts);
return LinkySession.parseData(data, 1, 'days');
}
async getMonthlyData(opts) {
opts = LinkySession.parseOpts(opts, dayjs().add(-11, 'month'), dayjs());
const data = await this.getData('urlCdcMois', opts);
return LinkySession.parseData(data, 1, 'month');
}
async getYearlyData(_) {
const data = await this.getData('urlCdcAn', {});
return LinkySession.parseData(data, 1, 'year');
}
async getData(resource, opts) {
const reqPart = 'lincspartdisplaycdc_WAR_lincspartcdcportlet';
const body = qs.stringify({
['_' + reqPart + '_dateDebut']: opts.start ? opts.start.format('DD/MM/YYYY') : null,
['_' + reqPart + '_dateFin']: opts.end ? opts.end.format('DD/MM/YYYY') : null
});
const query = qs.stringify({
p_p_id: reqPart,
p_p_lifecycle: 2,
p_p_state: 'normal',
p_p_mode: 'view',
p_p_resource_id: resource,
p_p_cacheability: 'cacheLevelPage',
p_p_col_id: 'column-1',
p_p_col_pos: 1,
p_p_col_count: 3
});
const url = 'https://espace-client-particuliers.enedis.fr/group/espace-particuliers/suivi-de-consommation?' + query;
let resp;
try {
// Call once
resp = await axios.post(url, body, {
maxRedirects: 0,
headers: {Cookie: this.getCookie(), 'content-type': 'application/x-www-form-urlencoded'},
withCredentials: true
});
} catch (err) {
if (!err.response || err.response.status !== 302 || this.calledOnce) {
throw new Error('Unexpected export response (2): ' + err.message + enedisNotice);
}
const newCookies = err.response.headers['set-cookie'];
this.cookies = this.cookies.concat(newCookies);
this.calledOnce = true;
try {
// Call a second time with the new cookies
resp = await axios.post(url, body, {
maxRedirects: 0,
withCredentials: true,
headers: {
Cookie: this.getCookie()
}
});
return resp.data;
} catch (err) {
throw new Error('Unexpected export response (3): ' + err.message + enedisNotice);
}
}
if (this.calledOnce) {
return resp.data;
}
if (JSON.stringify(resp.data).indexOf('Conditions') > -1) {
throw new Error('Please log in manually and accept the new terms of service');
}
throw new Error('Unexpected export response (1): ' + resp.data + enedisNotice);
}
getCookie() {
return this.cookies.map(c => c.substr(0, c.indexOf(';'))).reduce((a, b) => a + '; ' + b);
}
static parseOpts(opts, start, end) {
opts = opts || {};
return {
start: opts.start ? parseDate(opts.start) : start,
end: opts.end ? parseDate(opts.end) : end
};
}
static parseData(input, step, unit) {
if (get(['etat', 'valeur'], input) === 'nonActive') {
throw new Error('No available data for the selected period');
}
const values = get(['graphe', 'data'], input);
if (!Array.isArray(values)) {
throw new Error(`Unexpected data: ${JSON.stringify(input)}` + enedisNotice);
}
const start = parseDate(get(['graphe', 'periode', 'dateDebut'], input));
const offset = get(['graphe', 'decalage'], input) || 0;
values.splice(0, offset);
values.splice(-offset, offset);
const res = [];
let date = start;
for (const val of values) {
res.push({
date: date.format('YYYY-MM-DD HH:mm:ss'),
value: val.valeur < 0 ? null : val.valeur
});
date = date.add(step, unit);
}
return res;
}
}
module.exports = {login};