import fs from 'fs'; import axios, { Axios, AxiosResponse } from "axios"; import { wrapper } from 'axios-cookiejar-support'; import { CookieJar } from 'tough-cookie'; import { FileCookieStore } from 'tough-cookie-file-store'; import { EnergyDTO, EnergyRequestDTO, Payload, PowerDTO, PowerRequestDTO, ReadingDTO, ReadingRequestDTO } from "./types"; import { TauronConfig } from '../config'; export * from './types'; const LOGIN_API = `https://logowanie.tauron-dystrybucja.pl/login`; const BASE_URL = 'https://elicznik.tauron-dystrybucja.pl'; const ENERGY_API = '/energia/api'; const POWER_API = '/moc/api'; const READING_API = '/odczyty/api'; /** * Utility class for Tauron API. * @param config - configuration of service */ export const Tauron = class { #http: Axios; #config: TauronConfig; constructor(config: TauronConfig) { // TODO: It should be paths instead of raw credentials this.#config = config; this.#http = wrapper(axios.create({ baseURL: BASE_URL, jar: new CookieJar(new FileCookieStore(config.cookiesJarPath || './cookies.json')), headers: { 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/jxl,image/webp,image/png,image/svg+xml,*/*;q=0.8', 'Accept-Language': 'en-US,en;q=0.5', 'Accept-Encoding': 'gzip, deflate, br, zstd', 'Sec-GPC': '1', 'Upgrade-Insecure-Requests': '1', 'Sec-Fetch-Dest': 'document', 'Sec-Fetch-Mode': 'navigate', 'Sec-Fetch-Site': 'none', 'Sec-Fetch-User': '?1', 'Priority': 'u=0, i', }, })); } /** * Performs the whole login process flow including: * - directing to the main page * - doing toe actual login * - setting the measurement base point */ async #login() { await this.#http.get(LOGIN_API); await this.#http.postForm(LOGIN_API, { username: fs.readFileSync(this.#config.usernamePath, 'utf8'), password: fs.readFileSync(this.#config.passwordPath, 'utf8'), service: BASE_URL }); await this.#http.postForm('/ustaw_punkt', { "site[client]": this.#config.point, "page": "energy" }); } /** * Generic function which performs the HTTP request. * If the request fails, the login attempt will be performed and the function will be invoked again * with new session cookies. * @param callback - function which performs the request on Axios instance being passed as argument * @returns response from callback function */ async query(callback: (http: Axios) => Promise>): Promise> { try { return await callback(this.#http); } catch(e) { console.log("Session expired, login requested"); await this.#login(); return await callback(this.#http); } } /** * Utility function to handle Tauron API shape (success field) automatically. * @param path - HTTP API path * @param data - the HTTP payload * @returns original response */ async #queryForm(path: string, data: any): Promise { const response = await this.query(h => h.postForm>(path, data)); if (!response.data.success) { throw new Error(`Invalid data request (success == false): ${JSON.stringify(response.data)}`); } return response.data.data; } /** * Returns the energy data for specific request payload. * @param data - the payload supported by Tauron API * @returns the energy data from Tauron API */ async getEnergy(data: EnergyRequestDTO): Promise { return this.#queryForm(ENERGY_API, data); } /** * Returns the energy data per specific day. * @param day - measurement day * @param type - type of measurement, can be 'consum' (regular one) or 'average' * @returns the energy data from Tauron API */ async getEnergyForDay(day: string): Promise { return await this.getEnergy({ type: 'consum', profile: "full time", from: day, to: day, }); } /** * Returns the energy data per specific dates range. * @param from - the measurement starting date * @param to - the measurement ending date * @param type - type of measurement, can be 'consum' (regular one) or 'average' * @returns the energy data from Tauron API */ async getEnergyForRange(from: string, to: string): Promise { return await this.getEnergy({ type: 'consum', from, to, profile: "range" }); } /** * Returns the energy data per specific year. * @param year measurement year * @param type - type of measurement, can be 'consum' (regular one) or 'average' * @returns the energy data from Tauron API */ async getEnergyForYear(year: string): Promise { return await this.getEnergy({ type: 'consum', profile: "year", from: `01.01.${year}`, to: `31.12.${year}`, }); } /** * Returns the power data for specific request payload. * @param data - the payload supported by Tauron API * @returns the power data from Tauron API */ async getPower(data: PowerRequestDTO): Promise { return this.#queryForm(POWER_API, data); } /** * Returns the power data per specific dates range. * @param from - the measurement starting date * @param to - the measurement ending date * @returns the power data from Tauron API */ async getPowerForRange(from: string, to: string): Promise { return await this.getPower({ from, to }); } /** * Returns the reading data for specific request payload. * @param data - the payload supported by Tauron API * @returns the reading data from Tauron API */ async getReading(data: ReadingRequestDTO): Promise { return this.#queryForm(READING_API, data); } /** * Returns the readings per specific dates range. * @param from - the measurement starting date * @param to - the measurement ending date * @returns the readings from Tauron API */ async getReadingForRange(from: string, to: string): Promise { return await this.getReading({ from, to, type: 'energia-pobrana', }); } };