Source: scraper/helpers.js

/**
 * @fileOverview Helper functions for scraping and navigating
 * @author Paul Orlob
 */

/**
 * Sunnyportal data
 * @global
 * @typedef {Object} SunnyData
 * @property {number} pv PV consumption
 * @property {number} home home consumption
 * @property {number} grid grid feed is negative, grid draw is positive
 * @property {number} battery_percentage percentage of pv battery
 * @property {number} battery_watts battery charging is negative, battery usage positive
 */

/**
 * @class
 */
class Helpers {

    /**
     * Performs login action and navigates to next site.
     * @param page
     * @param pass {string} password
     * @param user {string} username
     * @returns {!Promise<void>}
     */
    static async authenticate(page, pass, user) {
        let user_field = await page.$("#txtUserName");
        let password_field = await page.$("#txtPassword");
        let button = await page.$("#ctl00_ContentPlaceHolder1_Logincontrol1_LoginBtn");

        if (user == null || pass == null) {
            throw new TypeError("No username or password specified")
        }

        await user_field.type(user);
        await password_field.type(pass);

        const navPromise = page.waitForNavigation({waitUntil: "domcontentloaded"});
        await button.click();
        await navPromise;
    }

    /**
     * navigates from logged in start page to current status
     * @returns {!Promise<void>}
     */
    static async navigateToStatusPage(page) {
        let button_chevron = await page.$("#collapseNavi");
        await button_chevron.click();

        let nav_promise = page.waitForNavigation({waitUntil: "domcontentloaded"});
        let button_status = await page.$("#ctl00_NavigationLeftMenuControl_0_2");
        await button_status.click();
        await nav_promise;
    }

    /**
     * Retrieves data from status page
     * @param page
     * @returns {!Promise<SunnyData>} data object
     */
    static async getStatusData(page) {
        let pv_text = await this._getInnerHTML(page, "#ctl00_ContentPlaceHolder1_LiveSSEnabled > div:nth-child(4) > div.batteryStatus-container > div.batteryStatus-pv > div > span.batteryStatus-value.h3.header");
        let home_text = await this._getInnerHTML(page, "#ctl00_ContentPlaceHolder1_LiveSSEnabled > div:nth-child(4) > div.batteryStatus-container > div.batteryStatus-consumption > div.batteryStatus-text > span.batteryStatus-value.h3.header");
        let grid_text = await this._getInnerHTML(page, "#ctl00_ContentPlaceHolder1_LiveSSEnabled > div:nth-child(4) > div.batteryStatus-container > div.batteryStatus-grid > div > span.batteryStatus-value.h3.header");
        let battery_percentage_text = await this._getInnerHTML(page, "#ctl00_ContentPlaceHolder1_LiveSSEnabled > div:nth-child(4) > div.batteryStatus-container > div.batteryStatus-battery > div.batteryStatus-text.battery-percentage > span.batteryStatus-value.h3.header");
        let battery_watts_text = await this._getInnerHTML(page, "#ctl00_ContentPlaceHolder1_LiveSSEnabled > div:nth-child(4) > div.batteryStatus-container > div.batteryStatus-battery > div.batteryStatus-text.battery-power > span.batteryStatus-value.h3.header");

        let grid_attribute_value = await page.$eval("#ctl00_ContentPlaceHolder1_LiveSSEnabled > div:nth-child(4) > div.batteryStatus-container > div.batteryStatus-grid", (element) => {
                return element.getAttribute("data-grid")
            });

        let battery_attribute_value = await page.$eval("#ctl00_ContentPlaceHolder1_LiveSSEnabled > div:nth-child(4) > div.batteryStatus-container > div.batteryStatus-battery", (element) => {
                return element.getAttribute("data-status")
            });

        let grid = this._convertAndRound(grid_text);
        if (grid_attribute_value === "feedIn") {
            grid *= -1
        }

        let battery_watts = this._convertAndRound(battery_watts_text);
        if (battery_attribute_value === "charge") {
            battery_watts *= -1
        }

        return {
            pv: this._convertAndRound(pv_text),
            home: this._convertAndRound(home_text),
            grid: grid,
            battery_percentage: this._convertAndRound(battery_percentage_text, 0),
            battery_watts: battery_watts
        };

    }

    /**
     * Converts a string to float and rounds it.
     * @param string
     * @param decimalPlaces
     * @returns {number}
     * @private
     */
    static _convertAndRound(string, decimalPlaces = 2) {
        let number = parseFloat(string).toFixed(decimalPlaces);
        return parseFloat(number);
    }

    /**
     * retrieves inner html of element
     * @param page Root element
     * @param {string} selector Selector for element
     * @returns {Promise<string>} returns innerHTML of element selected
     * @private
     */
    static async _getInnerHTML(page, selector) {
        return await page.$eval(selector, (element) => {
            return element.innerHTML
        })
    }

}

module.exports = Helpers;