UNPKG

iobroker.tibberlink

Version:

links tibber API data to be used in ioBroker

413 lines 25.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TibberAPICaller = void 0; const tibber_api_1 = require("tibber-api"); const EnergyResolution_js_1 = require("tibber-api/lib/src/models/enums/EnergyResolution.js"); const projectUtils_js_1 = require("./projectUtils.js"); class TibberAPICaller extends projectUtils_js_1.ProjectUtils { tibberConfig; tibberQuery; constructor(tibberConfig, adapter) { super(adapter); this.tibberConfig = tibberConfig; this.tibberQuery = new tibber_api_1.TibberQuery(this.tibberConfig, 60000); } async updateHomesFromAPI() { try { const Homes = await this.tibberQuery.getHomes(); this.adapter.log.debug(`Got homes from tibber api: ${JSON.stringify(Homes)}`); const homeInfoList = []; for (const currentHome of Homes) { homeInfoList.push({ ID: currentHome.id, NameInApp: currentHome.appNickname, RealTime: currentHome.features.realTimeConsumptionEnabled, FeedActive: false, PriceDataPollActive: true, }); this.tibberConfig.homeId = currentHome.id; const basePath = `Homes.${currentHome.id}`; void this.checkAndSetValue(`${basePath}.General.Id`, currentHome.id, "ID of your home"); void this.checkAndSetValue(`${basePath}.General.Timezone`, currentHome.timeZone, "The time zone the home resides in"); void this.checkAndSetValue(`${basePath}.General.NameInApp`, currentHome.appNickname, "The nickname given to the home"); void this.checkAndSetValue(`${basePath}.General.AvatarInApp`, currentHome.appAvatar, "The chosen app avatar for the home"); void this.checkAndSetValue(`${basePath}.General.Type`, currentHome.type, "The type of home."); void this.checkAndSetValue(`${basePath}.General.PrimaryHeatingSource`, currentHome.primaryHeatingSource, "The primary form of heating in the home"); void this.checkAndSetValueNumber(`${basePath}.General.Size`, currentHome.size, "The size of the home in square meters"); void this.checkAndSetValueNumber(`${basePath}.General.NumberOfResidents`, currentHome.numberOfResidents, "The number of people living in the home"); void this.checkAndSetValueNumber(`${basePath}.General.MainFuseSize`, currentHome.mainFuseSize, "The main fuse size"); void this.checkAndSetValueBoolean(`${basePath}.General.HasVentilationSystem`, currentHome.hasVentilationSystem, "Whether the home has a ventilation system"); this.fetchAddress(currentHome.id, "Address", currentHome.address); this.fetchLegalEntity(currentHome.id, "Owner", currentHome.owner); void this.checkAndSetValueBoolean(`${basePath}.Features.RealTimeConsumptionEnabled`, currentHome.features.realTimeConsumptionEnabled, "Whether Tibber server will send consumption data by API"); } return homeInfoList; } catch (error) { this.adapter.log.error(this.generateErrorMessage(error, "fetching homes from Tibber API")); return []; } } async updateCurrentPriceAllHomes(homeInfoList, forceUpdate = false) { let okprice = true; for (const curHomeInfo of homeInfoList) { if (!curHomeInfo.PriceDataPollActive) { continue; } if (!(await this.updateCurrentPrice(curHomeInfo.ID, forceUpdate))) { okprice = false; } } return okprice; } async updateCurrentPrice(homeId, forceUpdate = false) { try { if (homeId) { let exDateCurrent = null; let pricesToday = []; const now = new Date(); if (!forceUpdate) { exDateCurrent = new Date(await this.getStateValue(`Homes.${homeId}.CurrentPrice.startsAt`)); pricesToday = JSON.parse(await this.getStateValue(`Homes.${homeId}.PricesToday.json`)); } if (Array.isArray(pricesToday) && pricesToday[2] && pricesToday[2].startsAt) { const exDateToday = new Date(pricesToday[2].startsAt); if (now.getDate == exDateToday.getDate) { this.fetchPriceRemainingAverage(homeId, `PricesToday.averageRemaining`, pricesToday); } } if (!exDateCurrent || now.getHours() !== exDateCurrent.getHours() || forceUpdate) { const currentPrice = await this.tibberQuery.getCurrentEnergyPrice(homeId); await this.fetchPrice(homeId, "CurrentPrice", currentPrice); this.adapter.log.debug(`Got current price from tibber api: ${JSON.stringify(currentPrice)} Force: ${forceUpdate}`); exDateCurrent = new Date(currentPrice.startsAt); if (exDateCurrent && now.getHours() === exDateCurrent.getHours()) { return true; } } else if (now.getHours() == exDateCurrent.getHours()) { this.adapter.log.debug(`Hour (${exDateCurrent.getHours()}) of known current price is already the current hour, polling of current price from Tibber skipped`); return true; } else { return false; } } else { return false; } } catch (error) { if (forceUpdate) { this.adapter.log.error(this.generateErrorMessage(error, `pull of current price`)); } else { this.adapter.log.warn(this.generateErrorMessage(error, `pull of current price`)); } return false; } return false; } async updatePricesTodayAllHomes(homeInfoList, forceUpdate = false) { let okprice = true; for (const curHomeInfo of homeInfoList) { if (!curHomeInfo.PriceDataPollActive) { continue; } if (!(await this.updatePricesToday(curHomeInfo.ID, forceUpdate))) { okprice = false; } else { const now = new Date(); void this.checkAndSetValue(`Homes.${curHomeInfo.ID}.PricesToday.lastUpdate`, now.toString(), `last update of prices today`); } } return okprice; } async updatePricesToday(homeId, forceUpdate = false) { try { let exDate = null; let exPricesToday = []; if (!forceUpdate) { exPricesToday = JSON.parse(await this.getStateValue(`Homes.${homeId}.PricesToday.json`)); } if (Array.isArray(exPricesToday) && exPricesToday[2] && exPricesToday[2].startsAt) { exDate = new Date(exPricesToday[2].startsAt); } const today = new Date(); today.setHours(0, 0, 0, 0); if (!exDate || exDate <= today || forceUpdate) { const pricesToday = await this.tibberQuery.getTodaysEnergyPrices(homeId); if (!(Array.isArray(pricesToday) && pricesToday.length > 0 && pricesToday[2] && pricesToday[2].total)) { throw new Error(`Got invalid data structure from Tibber [you might not have a valid (or fully confirmed) contract]`); } this.adapter.log.debug(`Got prices today from tibber api: ${JSON.stringify(pricesToday)} Force: ${forceUpdate}`); void this.checkAndSetValue(`Homes.${homeId}.PricesToday.json`, JSON.stringify(pricesToday), "The prices today as json"); void this.checkAndSetValue(`Homes.${homeId}.PricesYesterday.json`, JSON.stringify(exPricesToday), "The prices yesterday as json"); this.fetchPriceAverage(homeId, `PricesToday.average`, pricesToday); this.fetchPriceRemainingAverage(homeId, `PricesToday.averageRemaining`, pricesToday); this.fetchPriceMaximum(homeId, `PricesToday.maximum`, pricesToday.sort((a, b) => a.total - b.total)); this.fetchPriceMinimum(homeId, `PricesToday.minimum`, pricesToday.sort((a, b) => a.total - b.total)); for (const price of pricesToday) { const hour = new Date(price.startsAt.substr(0, 19)).getHours(); await this.fetchPrice(homeId, `PricesToday.${hour}`, price); } if (Array.isArray(pricesToday) && pricesToday[2] && pricesToday[2].startsAt) { void this.checkAndSetValue(`Homes.${homeId}.PricesToday.jsonBYpriceASC`, JSON.stringify(pricesToday.sort((a, b) => a.total - b.total)), "prices sorted by cost ascending as json"); exDate = new Date(pricesToday[2].startsAt); if (exDate && exDate >= today) { return true; } } else { void this.checkAndSetValue(`Homes.${homeId}.PricesToday.jsonBYpriceASC`, JSON.stringify(pricesToday), "prices sorted by cost ascending as json"); return false; } } else { this.adapter.log.debug(`Existing date of price info is already the today date, polling of prices today from Tibber skipped`); return true; } } catch (error) { if (forceUpdate) { this.adapter.log.error(this.generateErrorMessage(error, `force pull of prices today`)); } else { this.adapter.log.warn(this.generateErrorMessage(error, `pull of prices today`)); } return false; } return false; } async updatePricesTomorrowAllHomes(homeInfoList, forceUpdate = false) { let okprice = true; for (const curHomeInfo of homeInfoList) { if (!curHomeInfo.PriceDataPollActive) { continue; } if (!(await this.updatePricesTomorrow(curHomeInfo.ID, forceUpdate))) { okprice = false; } else { const now = new Date(); void this.checkAndSetValue(`Homes.${curHomeInfo.ID}.PricesTomorrow.lastUpdate`, now.toString(), `last update of prices tomorrow`); } } return okprice; } async updatePricesTomorrow(homeId, forceUpdate = false) { try { let exDate = null; let exPricesTomorrow = []; if (!forceUpdate) { exPricesTomorrow = JSON.parse(await this.getStateValue(`Homes.${homeId}.PricesTomorrow.json`)); } if (Array.isArray(exPricesTomorrow) && exPricesTomorrow[2] && exPricesTomorrow[2].startsAt) { exDate = new Date(exPricesTomorrow[2].startsAt); } const morgen = new Date(); morgen.setDate(morgen.getDate() + 1); morgen.setHours(0, 0, 0, 0); if (!exDate || exDate < morgen || forceUpdate) { const pricesTomorrow = await this.tibberQuery.getTomorrowsEnergyPrices(homeId); this.adapter.log.debug(`Got prices tomorrow from tibber api: ${JSON.stringify(pricesTomorrow)} Force: ${forceUpdate}`); void this.checkAndSetValue(`Homes.${homeId}.PricesTomorrow.json`, JSON.stringify(pricesTomorrow), "The prices tomorrow as json"); if (pricesTomorrow.length === 0) { this.adapter.log.debug(`Emptying prices tomorrow and average cause existing ones are obsolete...`); for (let hour = 0; hour < 24; hour++) { this.emptyingPrice(homeId, `PricesTomorrow.${hour}`); } this.emptyingPriceAverage(homeId, `PricesTomorrow.average`); this.emptyingPriceMaximum(homeId, `PricesTomorrow.maximum`); this.emptyingPriceMinimum(homeId, `PricesTomorrow.minimum`); void this.checkAndSetValue(`Homes.${homeId}.PricesTomorrow.jsonBYpriceASC`, JSON.stringify(pricesTomorrow), "prices sorted by cost ascending as json"); return false; } else if (Array.isArray(pricesTomorrow)) { for (const price of pricesTomorrow) { const hour = new Date(price.startsAt.substr(0, 19)).getHours(); await this.fetchPrice(homeId, `PricesTomorrow.${hour}`, price); } this.fetchPriceAverage(homeId, `PricesTomorrow.average`, pricesTomorrow); this.fetchPriceMaximum(homeId, `PricesTomorrow.maximum`, pricesTomorrow.sort((a, b) => a.total - b.total)); this.fetchPriceMinimum(homeId, `PricesTomorrow.minimum`, pricesTomorrow.sort((a, b) => a.total - b.total)); void this.checkAndSetValue(`Homes.${homeId}.PricesTomorrow.jsonBYpriceASC`, JSON.stringify(pricesTomorrow.sort((a, b) => a.total - b.total)), "prices sorted by cost ascending as json"); exDate = new Date(pricesTomorrow[2].startsAt); if (exDate && exDate >= morgen) { return true; } return false; } } else if (exDate && exDate >= morgen) { this.adapter.log.debug(`Existing date of price info is already the tomorrow date, polling of prices tomorrow from Tibber skipped`); return true; } return false; } catch (error) { if (forceUpdate) { this.adapter.log.error(this.generateErrorMessage(error, `force pull of prices tomorrow`)); } else { this.adapter.log.warn(this.generateErrorMessage(error, `pull of prices tomorrow`)); } return false; } } async updateConsumptionAllHomes() { try { for (const home of this.adapter.config.HomesList) { if (!home.statsActive || !home.homeID) { continue; } const homeID = home.homeID; const resolutions = [ { type: EnergyResolution_js_1.EnergyResolution.HOURLY, state: `jsonHourly`, numCons: home.numberConsHourly, description: `hour` }, { type: EnergyResolution_js_1.EnergyResolution.DAILY, state: `jsonDaily`, numCons: home.numberConsDaily, description: `day` }, { type: EnergyResolution_js_1.EnergyResolution.WEEKLY, state: `jsonWeekly`, numCons: home.numberConsWeekly, description: `week` }, { type: EnergyResolution_js_1.EnergyResolution.MONTHLY, state: `jsonMonthly`, numCons: home.numberConsMonthly, description: `month` }, { type: EnergyResolution_js_1.EnergyResolution.ANNUAL, state: `jsonAnnual`, numCons: home.numberConsAnnual, description: `year` }, ]; for (const { type, state, numCons, description } of resolutions) { if (numCons && numCons > 0) { const consumption = await this.tibberQuery.getConsumption(type, numCons, homeID); void this.checkAndSetValue(`Homes.${homeID}.Consumption.${state}`, JSON.stringify(consumption), `Historical consumption last ${description}s as json)`, `json`); } else { void this.checkAndSetValue(`Homes.${homeID}.Consumption.${state}`, `[]`); } } this.adapter.log.debug(`Got all consumption data from Tibber Server for home: ${homeID}`); } } catch (error) { this.adapter.log.error(this.generateErrorMessage(error, `pull of consumption data`)); } } async fetchPrice(homeId, objectDestination, price) { const basePath = `Homes.${homeId}.${objectDestination}`; await this.checkAndSetValueNumber(`${basePath}.total`, price.total, "Total price (energy + taxes)"); void this.checkAndSetValueNumber(`${basePath}.energy`, price.energy, "Spotmarket energy price"); void this.checkAndSetValueNumber(`${basePath}.tax`, price.tax, "Tax part of the price (energy, tax, VAT...)"); void this.checkAndSetValue(`${basePath}.startsAt`, price.startsAt, "Start time of the price"); void this.checkAndSetValue(`${basePath}.level`, price.level, "Price level compared to recent price values"); } fetchPriceAverage(homeId, objectDestination, price) { const totalSum = price.reduce((sum, item) => { if (item && typeof item.total === "number") { return sum + item.total; } return sum; }, 0); const basePath = `Homes.${homeId}.${objectDestination}`; void this.checkAndSetValueNumber(`${basePath}.total`, Math.round(1000 * (totalSum / price.length)) / 1000, "Todays total price average"); const energySum = price.reduce((sum, item) => sum + item.energy, 0); void this.checkAndSetValueNumber(`${basePath}.energy`, Math.round(1000 * (energySum / price.length)) / 1000, "Todays average spotmarket price"); const taxSum = price.reduce((sum, item) => sum + item.tax, 0); void this.checkAndSetValueNumber(`${basePath}.tax`, Math.round(1000 * (taxSum / price.length)) / 1000, "Todays average tax price"); } fetchPriceRemainingAverage(homeId, objectDestination, price) { const now = new Date(); const currentHour = now.getHours(); const filteredPrices = price.filter(item => { const itemHour = new Date(item.startsAt).getHours(); return itemHour >= currentHour; }); const remainingTotalSum = filteredPrices.reduce((sum, item) => { if (item && typeof item.total === "number") { return sum + item.total; } return sum; }, 0); const basePath = `Homes.${homeId}.${objectDestination}`; void this.checkAndSetValueNumber(`${basePath}.total`, Math.round(1000 * (remainingTotalSum / filteredPrices.length)) / 1000, "Todays total price remaining average"); const remainingEnergySum = filteredPrices.reduce((sum, item) => sum + item.energy, 0); void this.checkAndSetValueNumber(`${basePath}.energy`, Math.round(1000 * (remainingEnergySum / filteredPrices.length)) / 1000, "Todays remaining average spot market price"); const remainingTaxSum = filteredPrices.reduce((sum, item) => sum + item.tax, 0); void this.checkAndSetValueNumber(`${basePath}.tax`, Math.round(1000 * (remainingTaxSum / filteredPrices.length)) / 1000, "Todays remaining average tax price"); } fetchPriceMaximum(homeId, objectDestination, price) { if (!price || typeof price[23].total !== "number") { } const basePath = `Homes.${homeId}.${objectDestination}`; void this.checkAndSetValueNumber(`${basePath}.total`, Math.round(1000 * price[23].total) / 1000, "Todays total price maximum"); void this.checkAndSetValueNumber(`${basePath}.energy`, Math.round(1000 * price[23].energy) / 1000, "Todays spotmarket price at total price maximum"); void this.checkAndSetValueNumber(`${basePath}.tax`, Math.round(1000 * price[23].tax) / 1000, "Todays tax price at total price maximum"); void this.checkAndSetValue(`${basePath}.level`, price[23].level, "Price level compared to recent price values"); void this.checkAndSetValue(`${basePath}.startsAt`, price[23].startsAt, "Start time of the price maximum"); } fetchPriceMinimum(homeId, objectDestination, price) { const basePath = `Homes.${homeId}.${objectDestination}`; void this.checkAndSetValueNumber(`${basePath}.total`, Math.round(1000 * price[0].total) / 1000, "Todays total price minimum"); void this.checkAndSetValueNumber(`${basePath}.energy`, Math.round(1000 * price[0].energy) / 1000, "Todays spotmarket price at total price minimum"); void this.checkAndSetValueNumber(`${basePath}.tax`, Math.round(1000 * price[0].tax) / 1000, "Todays tax price at total price minimum"); void this.checkAndSetValue(`${basePath}.level`, price[0].level, "Price level compared to recent price values"); void this.checkAndSetValue(`${basePath}.startsAt`, price[0].startsAt, "Start time of the price minimum"); } emptyingPrice(homeId, objectDestination) { const basePath = `Homes.${homeId}.${objectDestination}`; void this.checkAndSetValueNumber(`${basePath}.total`, 0, "The total price (energy + taxes)"); void this.checkAndSetValueNumber(`${basePath}.energy`, 0, "Spotmarket price"); void this.checkAndSetValueNumber(`${basePath}.tax`, 0, "Tax part of the price (energy tax, VAT, etc.)"); void this.checkAndSetValue(`${basePath}.level`, "Not known now", "Price level compared to recent price values"); } emptyingPriceAverage(homeId, objectDestination) { const basePath = `Homes.${homeId}.${objectDestination}`; void this.checkAndSetValueNumber(`${basePath}.total`, 0, "The todays total price average"); void this.checkAndSetValueNumber(`${basePath}.energy`, 0, "The todays avarage spotmarket price"); void this.checkAndSetValueNumber(`${basePath}.tax`, 0, "The todays avarage tax price"); } emptyingPriceMaximum(homeId, objectDestination) { const basePath = `Homes.${homeId}.${objectDestination}`; void this.checkAndSetValueNumber(`${basePath}.total`, 0, "Todays total price maximum"); void this.checkAndSetValueNumber(`${basePath}.energy`, 0, "Todays spotmarket price at total price maximum"); void this.checkAndSetValueNumber(`${basePath}.tax`, 0, "Todays tax price at total price maximum"); void this.checkAndSetValue(`${basePath}.level`, "Not known now", "Price level compared to recent price values"); void this.checkAndSetValue(`${basePath}.startsAt`, "Not known now", "Start time of the price maximum"); } emptyingPriceMinimum(homeId, objectDestination) { const basePath = `Homes.${homeId}.${objectDestination}`; void this.checkAndSetValueNumber(`${basePath}.total`, 0, "Todays total price minimum"); void this.checkAndSetValueNumber(`${basePath}.energy`, 0, "Todays spotmarket price at total price minimum"); void this.checkAndSetValueNumber(`${basePath}.tax`, 0, "Todays tax price at total price minimum"); void this.checkAndSetValue(`${basePath}.level`, "Not known now", "Price level compared to recent price values"); void this.checkAndSetValue(`${basePath}.startsAt`, "Not known now", "Start time of the price minimum"); } fetchLegalEntity(homeId, objectDestination, legalEntity) { const basePath = `Homes.${homeId}.${objectDestination}`; void this.checkAndSetValue(`${basePath}.Id`, legalEntity.id); void this.checkAndSetValue(`${basePath}.FirstName`, legalEntity.firstName); void this.checkAndSetValueBoolean(`${basePath}.IsCompany`, legalEntity.isCompany); void this.checkAndSetValue(`${basePath}.Name`, legalEntity.name); void this.checkAndSetValue(`${basePath}.MiddleName`, legalEntity.middleName); void this.checkAndSetValue(`${basePath}.LastName`, legalEntity.lastName); void this.checkAndSetValue(`${basePath}.OrganizationNo`, legalEntity.organizationNo); void this.checkAndSetValue(`${basePath}.Language`, legalEntity.language); if (legalEntity.contactInfo) { this.fetchContactInfo(homeId, `${objectDestination}.ContactInfo`, legalEntity.contactInfo); } if (legalEntity.address) { this.fetchAddress(homeId, `${objectDestination}.Address`, legalEntity.address); } } fetchContactInfo(homeId, objectDestination, contactInfo) { const basePath = `Homes.${homeId}.${objectDestination}`; void this.checkAndSetValue(`${basePath}.Email`, contactInfo.email); void this.checkAndSetValue(`${basePath}.Mobile`, contactInfo.mobile); } fetchAddress(homeId, objectDestination, address) { const basePath = `Homes.${homeId}.${objectDestination}`; void this.checkAndSetValue(`${basePath}.address1`, address.address1); void this.checkAndSetValue(`${basePath}.address2`, address.address2); void this.checkAndSetValue(`${basePath}.address3`, address.address3); void this.checkAndSetValue(`${basePath}.City`, address.city); void this.checkAndSetValue(`${basePath}.PostalCode`, address.postalCode); void this.checkAndSetValue(`${basePath}.Country`, address.country); void this.checkAndSetValue(`${basePath}.Latitude`, address.latitude); void this.checkAndSetValue(`${basePath}.Longitude`, address.longitude); } } exports.TibberAPICaller = TibberAPICaller; //# sourceMappingURL=tibberAPICaller.js.map