Skip to content

Commit

Permalink
Add --quiet and --format flags (#39)
Browse files Browse the repository at this point in the history
* Uses axios.get to adds ts response definition

* Removes promise types since already infered

* Adds --quiet and --format flags

--quiet allow to hide all the progression message and spinner animation

--format allow to specify a different output format. The default format
(pretty) remain unchanged and is rendered as a pretty table. The (json)
format output a json string that can be redirected to a file or piped to
  • Loading branch information
yadomi authored Oct 11, 2022
1 parent 35f2d50 commit 29a40e8
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 51 deletions.
8 changes: 7 additions & 1 deletion bin/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import meow from 'meow';
import { auth } from './auth';
import { daily, dailyProduction, loadCurve, loadCurveProduction, maxPower, MeteringFlags } from './metering';
import { daily, dailyProduction, loadCurve, loadCurveProduction, maxPower, MeteringFlags, Format } from './metering';
import chalk from 'chalk';
import updateNotifier from 'update-notifier';
import dayjs from 'dayjs';
Expand Down Expand Up @@ -37,6 +37,8 @@ const mainHelp = `
--start -s Date de début (AAAA-MM-JJ). Par défaut: hier
--end -e Date de début (AAAA-MM-JJ). Par défaut: aujourd'hui
--usage-point-id -u Usage Point ID (PRM). Par défaut: le dernier utilisé
--format -f Determine le format d'affichage de sortie du script. Options: pretty, json. Par défaut: pretty
--quiet -q N'affiche pas les messages et animations de progression. Optionnel
--output -o Fichier .json de sortie. Optionnel
Exemples:
Expand Down Expand Up @@ -69,6 +71,8 @@ try {
start: { type: 'string', alias: 's', default: yesterday },
end: { type: 'string', alias: 'e', default: today },
output: { type: 'string', alias: 'o' },
format: { type: 'string', alias: 'f', default: 'pretty' },
quiet: { type: 'boolean', alias: 'q', default: false },
sandbox: { type: 'boolean' }, // For test purposes
},
});
Expand All @@ -81,6 +85,8 @@ const meteringFlags: MeteringFlags = {
start: cli.flags.start,
end: cli.flags.end,
output: cli.flags.output || null,
quiet: cli.flags.quiet,
format: cli.flags.format as Format,
usagePointId: cli.flags.usagePointId,
};

Expand Down
93 changes: 63 additions & 30 deletions bin/metering.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ import mkdirp from 'mkdirp';
import fs from 'fs';
import path from 'path';

export type Format = 'json' | 'pretty';

export type MeteringFlags = {
start: string;
end: string;
output: string | null;
quiet: boolean;
format: Format;
usagePointId?: string;
};

Expand All @@ -21,6 +25,8 @@ export function daily(flags: MeteringFlags) {
'Récupération de la consommation quotidienne',
false,
flags.output,
flags.quiet,
flags.format,
flags.usagePointId
);
}
Expand All @@ -32,6 +38,8 @@ export function loadCurve(flags: MeteringFlags) {
'Récupération de la courbe de charge',
true,
flags.output,
flags.quiet,
flags.format,
flags.usagePointId
);
}
Expand All @@ -42,7 +50,9 @@ export function dailyProduction(flags: MeteringFlags) {
session.getDailyProduction(flags.start, flags.end),
'Récupération de la production quotidienne',
false,
flags.output
flags.output,
flags.quiet,
flags.format
);
}

Expand All @@ -52,7 +62,9 @@ export function loadCurveProduction(flags: MeteringFlags) {
session.getProductionLoadCurve(flags.start, flags.end),
'Récupération de la courbe de charge de production',
true,
flags.output
flags.output,
flags.quiet,
flags.format
);
}

Expand All @@ -63,6 +75,8 @@ export function maxPower(flags: MeteringFlags) {
'Récupération de la puissance maximale quotidienne',
true,
flags.output,
flags.quiet,
flags.format,
flags.usagePointId
);
}
Expand All @@ -72,13 +86,16 @@ function handle(
spinnerText: string,
displayTime: boolean,
output: string | null,
quiet: boolean,
format: Format,
usagePointId?: string
) {
const session = store.getStoredSession(usagePointId);

const spinner = ora().start(spinnerText);
const previousAccessToken = session?.accessToken;

const spinner = ora();
if (!quiet) spinner.start(spinnerText);

return promise
.then(async (consumption) => {
if (output) {
Expand All @@ -89,43 +106,59 @@ function handle(
throw new Error(`Impossible d'écrire dans ${output}:\n${e.message}`);
}
}
spinner.succeed();

if (store.getStoredSession(session?.usagePointId)?.accessToken !== previousAccessToken) {
ora('Vos tokens ont été automatiquement renouvelés').succeed();
}
if (!quiet) {
spinner.succeed();

if (output) {
ora(`Résultats sauvegardés dans ${output}`).succeed();
if (store.getStoredSession(session?.usagePointId)?.accessToken !== previousAccessToken) {
ora('Vos tokens ont été automatiquement renouvelés').succeed();
}

if (output) {
ora(`Résultats sauvegardés dans ${output}`).succeed();
}
}

display(consumption, displayTime);
if (!output) {
render(format, consumption, displayTime);
}
})
.catch((e) => {
spinner.fail(e.message);
throw new Error();
});
}

function display(consumption: Consumption, displayTime: boolean) {
const maxValue = Math.max(...consumption.data.map((x) => x.value));
const chartLength = 30;
// Headers
console.info(
'\n' +
chalk.yellow.underline(`Date${' '.repeat(displayTime ? 16 : 7)}`) +
' ' +
chalk.green.underline(`Valeur (${consumption.unit})`) +
' ' +
chalk.cyan.underline(`Graphique${' '.repeat(chartLength - 9)}`)
);
function render(format: Format, ...args: [Consumption, boolean]) {
Rendered[format](...args);
}

for (const line of consumption.data) {
const Rendered = {
json(consumption: Consumption, displayTime: boolean) {
console.log(JSON.stringify(consumption, null, 2));
},
pretty(consumption: Consumption, displayTime: boolean) {
const maxValue = Math.max(...consumption.data.map((x) => x.value));
const chartLength = 30;
// Headers
console.info(
chalk.yellow(`${line.date} `) +
chalk.green(`${line.value}`) +
' '.repeat(10 + consumption.unit.length - line.value.toString().length) +
chalk.cyan('■'.repeat(maxValue && line.value ? Math.ceil((chartLength * line.value) / maxValue) : 0))
'\n' +
chalk.yellow.underline(`Date${' '.repeat(displayTime ? 16 : 7)}`) +
' ' +
chalk.green.underline(`Valeur (${consumption.unit})`) +
' ' +
chalk.cyan.underline(`Graphique${' '.repeat(chartLength - 9)}`)
);
}
}

for (const line of consumption.data) {
console.info(
chalk.yellow(`${line.date} `) +
chalk.green(`${line.value}`) +
' '.repeat(10 + consumption.unit.length - line.value.toString().length) +
chalk.cyan(
'■'.repeat(maxValue && line.value ? Math.ceil((chartLength * line.value) / maxValue) : 0)
)
);
}
},
};
82 changes: 62 additions & 20 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,48 @@ export type ConsumptionData = {
value: number;
};

type ReadingTypeLoadCurve = {
measurement_kind: 'power';
unit: 'W';
aggregate: 'average';
};

type ReadingTypeMaxPower = {
measurement_kind: 'power';
unit: 'VA';
aggregate: 'maximum';
};

type ReadingTypeConsumption = {
measurement_kind: 'energy';
unit: 'Wh';
aggregate: 'sum';
};

type ReadingType = ReadingTypeLoadCurve | ReadingTypeMaxPower | ReadingTypeConsumption;
type IntervalReading = {
value: string;
date: string;
};

type MeteringResponse = {
meter_reading: {
usage_point_id: string;
start: string;
end: string;
quality: string;
reading_type: ReadingType;
interval_reading: IntervalReading[];
};
};

type RefreshTokenResponse = {
response: {
access_token?: string;
refresh_token?: string;
};
};

export type TokenRefreshCallback = (accessToken: string, refreshToken: string) => void;

export class Session {
Expand All @@ -34,11 +76,11 @@ export class Session {
}
}

getDailyConsumption(start: string, end: string): Promise<Consumption> {
getDailyConsumption(start: string, end: string) {
return this.request('daily_consumption', start, end);
}

getLoadCurve(start: string, end: string): Promise<Consumption> {
getLoadCurve(start: string, end: string) {
return this.request('consumption_load_curve', start, end);
}

Expand All @@ -55,23 +97,24 @@ export class Session {
}

private request(endpoint: string, start: string, end: string, retrying = false): Promise<Consumption> {
return axios({
method: 'get',
url: `${this.baseURL}/v4/metering_data/${endpoint}?${qs.stringify({
start: start,
end: end,
usage_point_id: this.config.usagePointId,
})}`,
headers: {
Authorization: `Bearer ${this.config.accessToken}`,
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
},
})
const url = `${this.baseURL}/v4/metering_data/${endpoint}?${qs.stringify({
start: start,
end: end,
usage_point_id: this.config.usagePointId,
})}`;

return axios
.get<MeteringResponse>(url, {
headers: {
Authorization: `Bearer ${this.config.accessToken}`,
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
},
})
.then((res) => {
return {
unit: res.data.meter_reading.reading_type.unit,
data: res.data.meter_reading.interval_reading.map((d: any) => ({
data: res.data.meter_reading.interval_reading.map((d) => ({
date: d.date,
value: parseFloat(d.value),
})),
Expand Down Expand Up @@ -102,10 +145,9 @@ export class Session {

private refreshToken() {
const host = this.config.sandbox ? 'linky.bokub.vercel.app' : 'conso.vercel.app';
return axios({
method: 'get',
url: `https://${host}/api/refresh?token=${this.config.refreshToken}`,
})
const url = `https://${host}/api/refresh?token=${this.config.refreshToken}`;
return axios
.get<RefreshTokenResponse>(url)
.then((res) => {
const { access_token, refresh_token } = res.data.response;
if (!access_token) {
Expand Down

0 comments on commit 29a40e8

Please sign in to comment.