-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
ENTSO-e_Energy_Rate_v1.7.2.fqa
1 lines (1 loc) · 95.2 KB
/
ENTSO-e_Energy_Rate_v1.7.2.fqa
1
{"name":"ENTSO-e Energy Rate","type":"com.fibaro.multilevelSensor","apiVersion":"1.2","initialProperties":{"viewLayout":{"$jason":{"body":{"header":{"style":{"height":"0"},"title":"quickApp_device_878"},"sections":{"items":[{"components":[{"name":"labelInfo","style":{"weight":"1.2"},"text":"labelInfo","type":"label","visible":true},{"style":{"weight":"0.5"},"type":"space"}],"style":{"weight":"1.2"},"type":"vertical"},{"components":[{"name":"refreshButton","style":{"weight":"1.2"},"text":"Refresh","type":"button","visible":true},{"style":{"weight":"0.5"},"type":"space"}],"style":{"weight":"1.2"},"type":"vertical"}]}},"head":{"title":"quickApp_device_878"}}},"uiCallbacks":[{"callback":"refresh_action","eventType":"onReleased","name":"refreshButton"}],"quickAppVariables":[{"name":"ENTSOE_Token","type":"string","value":"f442d0b3-450b-46d7-b752-d8d692fdb2c8"},{"name":"EnergyTax","type":"string","value":"0"},{"name":"TariffHistory","type":"string","value":"62"},{"name":"OperatorCost","type":"string","value":"0"},{"name":"GridLossesPct","type":"string","value":"0"},{"name":"AdjustmentPct","type":"string","value":"0"},{"name":"DealerFee","type":"string","value":"0"},{"name":"LocalGridCost","type":"string","value":"0"},{"name":"PriceLow","type":"string","value":"0.2"},{"name":"PriceHigh","type":"string","value":"1.0"},{"name":"PriceVeryHigh","type":"string","value":"2.0"},{"name":"PriceMedium","type":"string","value":"0.50"},{"name":"PriceDecimals","type":"string","value":"3"},{"name":"AddTariffDate","type":"string","value":""},{"name":"ConsoleDebug","type":"string","value":"OFF"},{"name":"NegativeRates","type":"string","value":"ON"},{"name":"ExchLastUpdate","type":"string","value":"2023-10-25"},{"name":"ExchAccessKey","type":"string","value":"b6766ee12f86279a1ec645806a2156ac"},{"name":"ExchangeRate","type":"string","value":11.796215}],"typeTemplateInitialized":true},"files":[{"name":"main","isMain":true,"isOpen":true,"content":"--[[\n ======================================================================================================= \n MIT License\n\n Copyright © 2023 JAM Data® AB by Jonny Mälman\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE. \n ======================================================================================================= \n\n ENTSO-e Energy Rate is an FIBARO QuickApp that get current Spot prices for almost every european countries and is independent from any energy power company for free.\n\n How to get your own ENTSO-e Token:\n I´ve provide an Token that works \"out of the box\", but can be changed in the future, so if you like you can create your own free token at ENTSO-e, but not required.\n Register an ENTSO-e account at: https://transparency.entsoe.eu/\n How to get an Token: https://transparency.entsoe.eu/content/static_content/download?path=/Static%20content/API-Token-Management.pdf\n\n The Exchange rate service (https://exchangerate.host) that is used in this QuickApp gets your local currency for free up to 1000 request/month if you register a account.\n If you use other currency than EUR, you need create and add the API Access Key from https://exchangerate.host in the local variables [ExchAccessKey].\n\n This is the first time I have developed in Lua language, so have some indulgence, but hey it works and I hope it works for U2 ;)\n I would appreciate if you have any feedback or suggestions to make this QuickApp better or more usefull, please send me an email at [email protected] and I´ll try to do my best :)\n\n And please give this QA your rating ⭐⭐⭐⭐⭐ in FIBARO Marketplace if you like it or not, I'll would be very happy!\n\n CHANGELOG:\n v1.0 First release 2023-01\n \n v1.1 New feature release 2023-03\n - Keeps Tariff rate history in FIBARO.\n - Show more usefull info in QA panel.\n - Add new general month average level variable \"EnergyMonthLevel\" for those that pay energy consumtion per month average.\n - Add new QA variable \"TariffHistory\" for how many days to store history in FIBARO tariff rates.\n - Localized panel text for language: EN, DK, NO, SV (if you want to help me with translation, please send me an email at [email protected])\n\n Braking changes that you need to change in your scenes if your using first release v1.0:\n General variable name change from \"EnergyRateArea\" to \"EnergyArea\".\n General variable name change from \"EnergyRateMedium\" to \"EnergyMediumPrice\".\n General variable name change from \"EnergyRateLevel\" to \"EnergyHourLevel\".\n General variable name change from \"EnergyRateNextLevel\" to \"EnergyNextHourLevel\".\n\n v1.2 Customer wishes release 2023-04\n - Option to add tax to the energy price.\n - Show if service error message in display panel.\n\n v1.3 Customer improvements 2023-05\n - Rewrite energy tariff table to store in general variable instead of FIBARO tariff table to solve negative energy prices.\n - Fix UTC time when request next day energy prices from ENTSO-e.\n - Improved energy value display formatting, with price decimal local QA variable, also show correct price if very low or negative price. (Except FIBARO tariff that can't show negative values)\n - All the rate levels are moved from general to local QA variables and are in real local energy price.\n - Move general variable \"EnergyTaxPercentage\" to local QA variable as \"EnergyTax\".\n - Add new general variable ON/OFF to store prices in FIBARO Tariff rate table.\n - Add translation in Portuguese (Thanks to Leandro C.).\n - Add local QA cost variables to calculate energy prices: {((ENTSO_price + operatorCost) x losses x adjustment) + dealer + localgrid} x tax (by Leandro C.).\n\n v1.4 Bug fix release 2023-05\n - Fix Update timer for display panel and variables. \n\n v1.5 Fix Exchange rate 2023-06\n - Fix historical exchange rates when show in FIBARO Tariff table.\n - Add QA varible [AddTariffDate] if you want to add historical rates to energy table. Input format: \"YYYY-MM-DD\".\n\n Braking changes from v1.4:\n All new tariff rates will now be stored in a new general variable [EnergyTariffTable] and all you old data will remain in [EnergyStateTable] until you delete it.\n\n v1.6 Fix QA Child value display 2023-08\n - Corrected QA Child to show negative values.\n - Add local QA variable [NegativeRates] to allow negative rates ON/OFF\n - Add new rate level if negative rate 🟣\n - Add general variable [EnergyCurrentRate] to easier read current energy rate price from other QA or scenes.\n\n v1.7 Fix Exchange rate API 2023-10\n - Update to support the new Exchangerate.Host API.\n This release Requires API Access Key from https://exchangerate.host only if you use other currency than Euro €.\n (NOTE! Historical exchange rates can't be fetch with this release because of changes at Exchangerate.Host)\n\n v1.7.2 Display panel & variable fix 2023-10\n - Minor display panel fix.\n - Local variable correction for Price Low, Medium, High and very High.\n \n]]\n\n-- QuickApp Initialize\nfunction QuickApp:onInit()\n self.debugLog = false -- Write to debug console true/false. Is set by local variable [ConsoleDebug]=ON/OFF.\n self.deviceName = \"ENTSO-e Energy Rate\"\n self.version = \"1.7.2\"\n self.Copyright = \"Copyright © 2023 JAM Data® AB\"\n\n self.httpClient = net.HTTPClient()\n \n -- Variables for exchangerate.host Api service if use of other currency than Euro €.\n -- https://exchangerate.host -- Free account now only support for non secure http and max 1000 requests/month. No history exchange rate can requests on a free account!\n self.exchangerate_baseURL = \"http://api.exchangerate.host/\"\n \n -- Variables for ENTSO-e Transparency Platform Api service\n -- https://transparency.entsoe.eu/content/static_content/Static%20content/web%20api/Guide.html\n self.entsoe_baseURL = \"https://web-api.tp.entsoe.eu/api\"\n self.child_rank_name = \"ENTSO-e Next Energy Rate\" -- Name of next energy price QA child\n self.next_rank_device_id = nil -- Id of next energy price QA child\n\n -- NOTE! Local QA variable names can only have maximun of 15 charachters.\n self.var_debug_log_name = \"ConsoleDebug\" -- Variable name of Debug log to console On/Off.\n self.var_token_name = \"ENTSOE_Token\" -- Variable name of ENTSO-e Token key.\n self.var_tariff_history_name = \"TariffHistory\" -- Variable name of tariff day(s) history.\n self.var_Low_name = \"PriceLow\" -- Variable name of low price.\n self.var_Medium_name = \"PriceMedium\" -- Variable name of medium price.\n self.var_High_name = \"PriceHigh\" -- Variable name of high price.\n self.var_VeryHigh_name = \"PriceVeryHigh\" -- Variable name of very high price.\n self.var_tax_percentage_name = \"EnergyTax\" -- Variable name of energy tax.\n self.var_price_decimals_name = \"PriceDecimals\" -- Variable name of number of decimals when display energy prices.\n self.var_add_date_tariff_name = \"AddTariffDate\" -- Variable name of date to add historical tariff rate.\n self.var_operator_cost_name = \"OperatorCost\" -- Variable name of Grid Operator deviation costs (€/kWh or €/MWh)\n self.var_grid_losses_name = \"GridLossesPct\" -- Variable name of Grid Losses percent cost (%).\n self.var_grid_adjustment_name = \"AdjustmentPct\" -- Variable name of Grid Adjustment percent cost added to the Grid Losses (%).\n self.var_dealer_cost_name = \"DealerFee\" -- Variable name of Dealer fee cost (€/kWh or €/MWh).\n self.var_grid_cost_name = \"LocalGridCost\" -- Variable name of Local Grid cost (€/kWh or €/MWh).\n self.var_allow_negative_name = \"NegativeRates\" -- Variable name of Allow negative rates ON/OFF.\n self.var_exchange_last_update_name = \"ExchLastUpdate\" -- Variable name of Laste date exchange rate update.\n self.var_exchange_rate_name = \"ExchangeRate\" -- Variable name of current exchange rate.\n self.var_exchange_rate_Key_name = \"ExchAccessKey\" -- Variable name of Exchangerate.host API Access Key (Required only if your currency is other than € Euro).\n\n -- Global variable names\n self.global_var_unit_name = \"EnergyUnit\" -- Global variable name of energy unit (KWh or MWh).\n self.global_var_state_table_name = \"EnergyTariffTable\" -- Global variable name of energy tariff table.\n self.global_var_area_name = \"EnergyArea\" -- Global variable name of energy area.\n self.global_var_level_name = \"EnergyHourLevel\" -- Global variable name of energy level.\n self.global_var_next_level_name = \"EnergyNextHourLevel\" -- Global variable name of energy level.\n self.global_var_month_level_name = \"EnergyMonthLevel\" -- Global variable name of energy level.\n self.global_var_fibaro_tariff_name = \"EnergyTariffInFibaro\" -- Global variable name of show Tariff rates i FIBARO On/Off.\n self.global_var_current_rate_name = \"EnergyCurrentRate\" -- Global variable name of current energy rate price.\n \n -- Default values to set if missing\n self.default_token = \"f442d0b3-450b-46d7-b752-d8d692fdb2c8\" -- See \"How to get your own Token:\" above.\n self.default_area_name = \"[SELECT AREA]\" -- Need to select an area before request ENTSO-e.\n self.default_unit = \"kWh\" -- Show kWh or MWh in display panel (FIBARO Tariff table is always in kWh)\n self.default_tax = \"0\" -- Default 0% energy tax.\n self.default_tariff_history = \"62\" -- Default 62 days ~2 month. (FIBARO Tariff panel will be slow to open if it's too many days)\n\n self.default_decimals = self:getDefaultPriceDecimals() -- Get default number of decimals based on currency for price display.\n self.default_Low_price = self:getDefaultRatePrice(10) -- Set 10% of medium price based on local currency.\n self.default_Medium_price = self:getDefaultRatePrice(100) -- Set 100% of medium price based on local currency.\n self.default_High_price = self:getDefaultRatePrice(160) -- Set 160% of medium price based on local currency.\n self.default_VeryHigh_price = self:getDefaultRatePrice(250) -- Set 250% of medium price based on local currency.\n \n self.default_operator_cost = \"0\" -- Default Grid Operator costs (0 €/kWh or 0 €/MWh).\n self.default_grid_losses = \"0\" -- Default Grid Losses in percent (0 %).\n self.default_grid_adjustment = \"0\" -- Default adjustment added to the Grid Losses in percent (0 %).\n self.default_dealer_cost = \"0\" -- Default Dealer cost (0 €/KWh or 0 €/MWh).\n self.default_grid_cost = \"0\" -- Default Local Grid cost (0 €/KWh or 0 €/MWh).\n self.default_allow_negative = \"ON\" -- Default Allow negative rates.\n\n -- Fixed QA variables\n self.nextday_releaseHourUtc = 12 -- The UTC hour of the day when ENTSO-e releses the next day prices.\n self.dateFormat = self:getDateFormat() -- The FIBARO Date format setting (\"YY-MM-DD\", \"DD.MM.YY\" or \"MM/DD/YY\").\n self.valueFormat = self:getValueFormat(self.default_decimals) -- The value formatting of decimals when display Energy prices ie. \"%.3f\"\n self.serviceRequestTime = \"--\" -- Last datetime when we request ENTSO-e webservice.\n self.serviceSuccess = true -- Request ENTSO-e service success or fault.\n self.exchangeRateUpdated = false -- Status if Exchange Rate is updated or not.\n self.exchangeLastDate = \"\" -- Last datetime when we request Exchange Rate Api service.\n self.exchangeRate = 0 -- Set default excahnge rate 0.\n self.entsoeServiceMessage = \"\" -- Request ENTSO-e service error message.\n self.exchServiceMessage = \"\" -- Request Exchange rate service error message.\n self.lastVariableUpdate = \"--\" -- Last general variable update.\n self.addTariffDate = \"\" -- Add historical tariff rates for a date (YYYY-MM-DD).\n self.dataChanged = true -- If data has change then display panel need to be updated.\n\n -- Let´s start the engine\n self:mainStart()\nend\n\n-- QA Main start execution\nfunction QuickApp:mainStart()\n self.debugLog = self:toBool(self:getLocalVariable(self.var_debug_log_name, \"OFF\"))\n self:debug(\">>>> Start QuickApp \" ..self.deviceName ..\" v\" ..self.version ..\" - \" ..self.Copyright ..\" <<<<\")\n \n -- Create global varaiables (See: defaults)\n self:createAreaVariables() \n self:createGlobalVariables()\n\n -- Init Child device to display next hour rate in FIBARO (See: QAChild_NextRank)\n self:initChildDevices({[\"com.fibaro.multilevelSensor\"] = ENTSOE_Next_Rank})\n self:validateChildren()\n \n -- set default values (See: defaults)\n self:setLocalVariables() \n\n -- Start main loop of requesting ExchangeRate and ENTSO-e services\n self:mainLoop(true)\nend\n\n-- Main loop that executes each minute\nfunction QuickApp:mainLoop(init)\n local loopTime = 2000\n \n -- Get current Exchange rate from Exchangerate.host Api Service if local currency is other than € Euro\n local dataChanged = self:getServiceExchangeRate(init)\n\n if (init == false) then\n -- Update Energy Rates table from ENSO-e Service if AreaCode is set\n if (self.areaCode ~= \"\") then self:refreshEnergyTariffTable() end\n \n -- Update variable and display panel if next hour\n if (not self:isDisplayPanelUpToDate(dataChanged)) then \n fibaro.setTimeout(2000, function()\n self:refreshDisplayVariables() -- Update Variables and Display panel\n self:updateFibaroTariffTable() -- Update FIBARO Tariff table if ON\n end)\n end\n\n -- Set to loop to each minute\n loopTime = 60000 - (os.date(\"%S\") * 1000)\n end\n\n -- Start this main loop each minute\n fibaro.setTimeout(loopTime, function() self:mainLoop(false) end)\nend\n\n-- Trigger if panel \"Refresh\" button pressed\nfunction QuickApp:refresh_action()\n self:d(\"Execute ENTSO-e service update on button event...\")\n self:updateView(\"refreshButton\", \"text\", \"⌛ \" ..self.i18n:get(\"Refreshing\") ..\"...\")\n \n -- Update Tariff table, variables and panel\n self.dataChanged = true\n self:getServiceExchangeRate(true)\n self:refreshEnergyTariffTable()\n fibaro.setTimeout(5000, function() self:refreshDisplayVariables() end) \nend"},{"name":"nextRank_Child","isMain":false,"isOpen":true,"content":"class 'ENTSOE_Next_Rank' (QuickAppChild)\r\nfunction ENTSOE_Next_Rank:__init(device)\r\n -- You should not insert code before QuickAppChild.__init.\r\n QuickAppChild.__init(self, device) -- We must call a constructor from the parent class\r\nend\r\n\r\n-- Set QA Child Value\r\nfunction ENTSOE_Next_Rank:setValue(value)\r\n self:updateProperty(\"value\", value)\r\nend\r\n\r\n-- Set QA Child Unit\r\nfunction ENTSOE_Next_Rank:setUnit(unit)\r\n self:updateProperty(\"unit\", unit)\r\nend\r\n\r\n-- Set QA Child Log\r\nfunction ENTSOE_Next_Rank:setLog(log)\r\n self:updateProperty(\"log\", log)\r\nend\r\n\r\n-- Validate if child panel exists, if not create it\r\nfunction QuickApp:validateChildren()\r\n self:d(\"Validate QuickApp Child\")\r\n for id, device in pairs(self.childDevices) do\r\n self:d(tostring(id) ..\" = \" ..device.name)\r\n\r\n if (device.name == self.child_rank_name) then\r\n self.next_rank_device_id = id\r\n self:d(tostring(id) ..\" - child_rank_name: \" ..device.name)\r\n end\r\n end\r\n\r\n if self.next_rank_device_id == nil then\r\n self:d(\"Create QuickApp Child\")\r\n self:createRankChild()\r\n end\r\nend\r\n\r\n-- Create child panel\r\nfunction QuickApp:createRankChild()\r\n local child = self:createChildDevice({\r\n name = self.child_rank_name,\r\n type = \"com.fibaro.multilevelSensor\",\r\n }, ENTSOE_Next_Rank)\r\n self.next_rank_device_id = child.id\r\nend"},{"name":"functions","isMain":false,"isOpen":true,"content":"-- Write to console if debug=true\r\nfunction QuickApp:d(msg)\r\n if self.debugLog then self:debug(msg) end\r\nend\r\n\r\n-- Get currency symbol\r\nfunction QuickApp:getCurrencySymbol()\r\n local currency = self.currency\r\n if (self.currency == nil) then currency = \"EUR\" end\r\n if (currency == \"EUR\") then return \"€\" end\r\n if (currency == \"USD\") then return \"$\" end\r\n if (currency == \"GBP\") then return \"£\" end\r\n if (currency == \"YEN\") then return \"¥\" end\r\n if (currency == \"SEK\") then return \"Kr\" end\r\n if (currency == \"NOK\") then return \"Kr\" end\r\n if (currency == \"DKK\") then return \"Kr\" end\r\n return currency\r\nend\r\n\r\n-- Get Lua date format from FIBARO date format\r\nfunction QuickApp:getDateFormat()\r\n if self.dateFormat == \"YY-MM-DD\" then return \"%Y-%m-%d\" end\r\n if self.dateFormat == \"DD.MM.YY\" then return \"%d.%m.%Y\" end\r\n if self.dateFormat == \"MM/DD/YY\" then return \"%m/%d/%Y\" end\r\n return \"%Y-%m-%d\"\r\nend\r\n\r\n-- Get value format\r\nfunction QuickApp:getValueFormat()\r\n if (self.decimals == nil) then self.decimals = self:getDefaultPriceDecimals() end\r\n return \"%.\" ..tostring(self.decimals) ..\"f\"\r\nend\r\n\r\nfunction QuickApp:toLocalDate(dateString, timezoneOffset, format)\r\n if dateString == \"\" then return \"\" end\r\n if timezoneOffset == nil then timezoneOffset = 0 end\r\n if format == nil then format = \"%Y%m%d\" end\r\n\r\n -- Convert input dateString = \"2022-12-25 23:00\" to table Id date \"20221225\"\r\n local iyear, imonth, iday, ihour, iminute = dateString:match(\"(%d+)-(%d+)-(%d+) (%d+):(%d+)\")\r\n local timestamp = os.time({year = iyear, month = imonth, day = iday, hour = ihour, min = iminute}) + timezoneOffset\r\n return os.date(format, timestamp)\r\nend\r\n\r\n-- Convert a dateid string to Lua date\r\nfunction QuickApp:toDate(dateId, hour, format, addHour)\r\n if dateId == nil or dateId == \"\" then return \"\" end\r\n if hour == nil or hour == \"\" then hour = 0 end\r\n if format == nil then format = \"%Y-%m-%d\" end\r\n if addHour == nil then addHour = 0 end\r\n\r\n -- Convert input dateId = \"20221225\" to Lua date format\r\n local iyear, imonth, iday = dateId:match(\"^(%d%d%d%d)(%d%d)(%d%d)$\")\r\n local timestamp = os.time({year = iyear, month = imonth, day = iday, hour = hour})\r\n return os.date(format, timestamp + (addHour * 60 * 60))\r\nend\r\n\r\n-- Convert a string to boolean\r\nfunction QuickApp:toBool(value)\r\n if (value == nil or value == \"\" or string.len(value) == 0) then return false end\r\n value = string.upper(value)\r\n local state = string.sub(value, 1, 1)\r\n if (state == \"T\" or state == \"Y\" or state == \"J\" or value == \"ON\") then return true end\r\n return false\r\nend\r\n\r\nfunction QuickApp:isEmpty(value)\r\n if (value == nil or string.gsub(value, \"%s+\", \"\") == \"\") then return true end\r\n return false\r\nend\r\n\r\nfunction QuickApp:isNotEmpty(value)\r\n if (value == nil or string.gsub(value, \"%s+\", \"\") == \"\") then return false end\r\n return true\r\nend\r\n\r\nfunction QuickApp:isDisplayPanelUpToDate(force)\r\n if force == nil or force or self:isEmpty(self.lastVariableUpdate) then return false end\r\n\r\n -- Convert input self.lastVariableUpdate = \"2022-12-25 23:00\" to hour\r\n local iyear, imonth, iday, ihour, iminute = self.lastVariableUpdate:match(\"(%d+)-(%d+)-(%d+) (%d+):(%d+)\")\r\n local timestamp = os.time({year = iyear, month = imonth, day = iday, hour = ihour, min = iminute, sec = 0})\r\n local lastHourUpdate = tonumber(os.date(\"%H\", timestamp))\r\n return lastHourUpdate == tonumber(os.date(\"%H\"))\r\nend\r\n\r\nfunction QuickApp:getNumbers(value)\r\n if (value == nil) then return nil end\r\n local str = \"\"\r\n string.gsub(value, \"%d+\", function(e) str = str ..e end)\r\n return str\r\nend\r\n\r\nfunction QuickApp:toDefault(value, default)\r\n if default == nil then default = \"\" end\r\n if value == nil or value == \"nan\" then return default end\r\n return value\r\nend\r\n\r\n-- Get ENTSO-e next day price release date in local time\r\nfunction QuickApp:getRateReleaseTime(timezoneOffset, format)\r\n if timezoneOffset == nil then timezoneOffset = 0 end\r\n if format == nil then format = \"!%H%M\" end\r\n return os.date(format, os.time({year=2000, month=1, day=1, hour=self.nextday_releaseHourUtc, min=10}) + timezoneOffset)\r\nend\r\n\r\nfunction QuickApp:isTimeForNextDayRates()\r\n if (self.serviceSuccess == nil or self.timezoneOffset == nil or self.tariffAreaRates == nil) then return false end\r\n\r\n if (self.serviceSuccess and tonumber(os.date(\"%H%M\", os.time())) >= tonumber(self:getRateReleaseTime(self.timezoneOffset, \"!%H%M\"))) then\r\n if (self:existsInEnergyTariffTable(self.tariffAreaRates, os.date(\"%Y%m%d\", os.time() + 86400))) then return false end\r\n return true\r\n end\r\n return false\r\nend\r\n\r\n-- Count items in a Lua table\r\nfunction QuickApp:tableCount(table)\r\n local count = 0\r\n if table == nil or table == \"\" then return count end\r\n for _ in pairs(table) do count = count + 1 end\r\n return count\r\nend\r\n\r\n-- Convert xml input dateString = \"2022-12-25T23:00Z\" to Lua date\r\nfunction QuickApp:getXmlDate(xmlString, name, format)\r\n local dateString = self:getXmlElement(xmlString, name)\r\n local iyear, imonth, iday, ihour, iminute = dateString:match(\"(%d+)-(%d+)-(%d+)T(%d+):(%d+)Z\")\r\n local timestamp = os.time({year = iyear, month = imonth, day = iday, hour = ihour, min = iminute})\r\n return os.date(format, timestamp)\r\nend\r\n\r\n-- Get xml element value\r\nfunction QuickApp:getXmlElement(xml, name)\r\n return xml:match(\"<\"..name..\">(.-)</\"..name..\">\")\r\nend\r\n\r\n-- Calculate tariff rate\r\nfunction QuickApp:calculateTariffRate(rawRate, exchRate, unit, tax, operator, losses, adjustment, dealer, grid, allowNegative)\r\n if (rawRate == nil) then rawRate = 0 end\r\n if (exchRate == nil or exchRate == 0) then exchRate = 1 end -- Set default excahnge rate 1:1 for €\r\n if (tax == nil or tax == 0) then tax = 1 end\r\n if (tax > 1) then tax = (tax / 100) + 1 end -- Convert input tax from % to decimal if > 1\r\n if (operator == nil) then operator = 0 end\r\n if (losses == nil or losses == 0) then losses = 1 end\r\n if (losses > 1) then losses = (losses / 100) + 1 end -- Convert input losses in % to decimal if > 1\r\n if (adjustment == nil or adjustment == 0) then adjustment = 1 end\r\n if (adjustment > 1) then adjustment = (adjustment / 100) + 1 end -- Convert input adjustment in % to decimal if > 1\r\n if (dealer == nil) then dealer = 0 end\r\n if (grid == nil) then grid = 0 end\r\n\r\n -- Get Unit scale. ENTSO-e always return prices in €/MWh\r\n local unitScale = 1000 -- kWh\r\n if (unit == \"MWh\") then unitScale = 1 end \r\n \r\n -- Recalculate main rate from EUR/mWh to {local currency}/{MWh or kWh} * tax\r\n local rate = tonumber(string.format(\"%f\", ((((((rawRate * exchRate) / unitScale) + operator) * losses * adjustment) + dealer + grid) * tax)))\r\n\r\n -- Don't return negative prices if not allow\r\n if (allowNegative ~= nil and allowNegative == false and rate < 0) then rate = 0 end\r\n\r\n return rate\r\nend\r\n\r\nfunction QuickApp:getRank(value)\r\n -- Set defaults if not valid input value\r\n if (value == nil or value == \"nan\" or value == \"--\") then return \"\" end\r\n value = tonumber(value)\r\n\r\n -- Return price rank from variable rank values\r\n local rank = \"VeryLOW\"\r\n if (value < 0) then rank = \"Negative\" end\r\n if (value >= self.low_price) then rank = \"LOW\" end\r\n if (value >= self.medium_price) then rank = \"MEDIUM\" end\r\n if (value >= self.high_price) then rank = \"HIGH\" end\r\n if (value >= self.veryhigh_price) then rank = \"VeryHIGH\" end\r\n\r\n self:d(\"Set the rank level value \" ..value ..\" = \" ..rank)\r\n\r\n return rank\r\nend\r\n\r\nfunction QuickApp:getRankIcon(value)\r\n if (value == \"VeryHIGH\") then return \"🔴\" end\r\n if (value == \"HIGH\") then return \"🟠\" end\r\n if (value == \"MEDIUM\") then return \"🟡\" end\r\n if (value == \"LOW\") then return \"🔵\" end\r\n if (value == \"VeryLOW\") then return \"🟢\" end\r\n if (value == \"Negative\") then return \"🟣\" end\r\n return \"⛔\" -- Wrong value\r\nend\r\n\r\nfunction QuickApp:getNextDirection(currentValue, nextValue)\r\n -- Examples ⬆️⬇️➡️ or ⇧⇨⇩\r\n if (currentValue == nil) then currentValue = 0 end\r\n if (nextValue == nil) then nextValue = 0 end\r\n if (tonumber(currentValue) > tonumber(nextValue)) then return \"⬇️\" end\r\n if (tonumber(currentValue) < tonumber(nextValue)) then return \"⬆️\" end\r\n return \"➡️\"\r\nend"},{"name":"defaults","isMain":false,"isOpen":true,"content":"function QuickApp:createGlobalTableVariable()\r\n -- Create Energy state table global variable\r\n local table_var = {\r\n name=self.global_var_state_table_name,\r\n isEnum=false,\r\n readOnly=true,\r\n value=\"\"\r\n }\r\n response, status = api.post('/globalVariables/', table_var)\r\n self:d(\"Create global variable: \" ..tostring(table_var.name) ..\" => \" ..tostring(status) ..\" - \" ..tostring(response.message))\r\nend\r\n\r\nfunction QuickApp:createGlobalVariables()\r\n -- Create global variable Current energy rate price\r\n local rate_var = {\r\n name=self.global_var_current_rate_name,\r\n isEnum=false,\r\n readOnly=true,\r\n value=\"\"\r\n }\r\n response, status = api.post('/globalVariables/', rate_var)\r\n self:d(\"Create global variable: \" ..tostring(rate_var.name) ..\" => \" ..tostring(status) ..\" - \" ..tostring(response.message))\r\n\r\n -- Create Energy store in FIBARO Tariff global variable\r\n local tariff_var = {\r\n name=self.global_var_fibaro_tariff_name,\r\n isEnum=true,\r\n readOnly=true,\r\n value=\"ON\",\r\n enumValues={\"ON\",\"OFF\"}\r\n }\r\n response, status = api.post('/globalVariables/', tariff_var)\r\n self:d(\"Create global variable: \" ..tostring(tariff_var.name) ..\" => \" ..tostring(status) ..\" - \" ..tostring(response.message))\r\n\r\n -- Create Energy Unit global variable\r\n local unit_var = {\r\n name=self.global_var_unit_name,\r\n isEnum=true,\r\n readOnly=true,\r\n value=self.default_unit,\r\n enumValues={\"kWh\",\"MWh\"}\r\n }\r\n response, status = api.post('/globalVariables/', unit_var)\r\n self:d(\"Create global variable: \" ..tostring(unit_var.name) ..\" => \" ..tostring(status) ..\" - \" ..tostring(response.message))\r\n\r\n -- Create Level rate global variable\r\n local level_var = {\r\n name=self.global_var_level_name,\r\n isEnum=true,\r\n readOnly=true,\r\n value=\"HIGH\",\r\n enumValues={\"Negative\",\"VeryLOW\",\"LOW\",\"MEDIUM\",\"HIGH\",\"VeryHIGH\"}\r\n }\r\n response, status = api.post('/globalVariables/', level_var)\r\n self:d(\"Create global variable: \" ..tostring(level_var.name) ..\" => \" ..tostring(status) ..\" - \" ..tostring(response.message))\r\n\r\n -- Create Next Level rate global variable\r\n level_var.name = self.global_var_next_level_name\r\n response, status = api.post('/globalVariables/', level_var)\r\n self:d(\"Create global variable: \" ..tostring(level_var.name) ..\" => \" ..tostring(status) ..\" - \" ..tostring(response.message))\r\n\r\n -- Create Month Level rate global variable\r\n level_var.name = self.global_var_month_level_name\r\n response, status = api.post('/globalVariables/', level_var)\r\n self:d(\"Create global variable: \" ..tostring(level_var.name) ..\" => \" ..tostring(status) ..\" - \" ..tostring(response.message))\r\nend\r\n\r\nfunction QuickApp:setLocalVariables()\r\n -- Not required, but you can register and create your own account and get a token at ENTSO-e.\r\n -- Register an free ENTSO-e account at: https://transparency.entsoe.eu/\r\n -- How to get an Token: https://transparency.entsoe.eu/content/static_content/download?path=/Static%20content/API-Token-Management.pdf \r\n self.token = self:getLocalVariable(self.var_token_name, self.default_token)\r\n\r\n -- Exchange rate access key is required if use of other currency than Euro.\r\n -- Register a free Exchangerate.host account at: https://exchangerate.host and get your Access Key.\r\n self.exchangerate_Key = self:getLocalVariable(self.var_exchange_rate_Key_name, \"\")\r\n self.exchangeLastUpdate = self:getLocalVariable(self.var_exchange_last_update_name, \"\")\r\n self.exchangeRate = tonumber(self:getLocalVariable(self.var_exchange_rate_name, 0))\r\n \r\n -- Get/Set local rate price variables\r\n self.low_price = tonumber(self:getLocalVariable(self.var_Low_name, self.default_Low_price))\r\n self.medium_price = tonumber(self:getLocalVariable(self.var_Medium_name, self.default_Medium_price))\r\n self:debug(\"Medium price: \" ..self.medium_price)\r\n self.high_price = tonumber(self:getLocalVariable(self.var_High_name, self.default_High_price))\r\n self.veryhigh_price = tonumber(self:getLocalVariable(self.var_VeryHigh_name, self.default_VeryHigh_price))\r\n\r\n self.addTariffDate = self:getLocalVariable(self.var_add_date_tariff_name, \"\")\r\n\r\n -- Get/Set local variable price decimals\r\n self.decimals = self:getLocalVariable(self.var_price_decimals_name, self.default_decimals)\r\n -- Get/Set local variable Days to keep FIBARO Tariff history\r\n self.tariffHistory = self:getLocalVariable(self.var_tariff_history_name, self.default_tariff_history)\r\n -- Get/Set local variable tax\r\n self.tax = self:getLocalVariable(self.var_tax_percentage_name, self.default_tax)\r\n \r\n -- Get/Set local variable operator cost\r\n self.operatorCost = self:getLocalVariable(self.var_operator_cost_name, self.default_operator_cost)\r\n -- Get/Set local variable grid losses in percent\r\n self.gridLosses = self:getLocalVariable(self.var_grid_losses_name, self.default_grid_losses)\r\n -- Get/Set local variable grid adjustment in percent\r\n self.gridAdjustment = self:getLocalVariable(self.var_grid_adjustment_name, self.default_grid_adjustment)\r\n -- Get/Set local variable dealer cost\r\n self.dealerCost = self:getLocalVariable(self.var_dealer_cost_name, self.default_dealer_cost)\r\n -- Get/Set local variable grid cost\r\n self.gridCost = self:getLocalVariable(self.var_grid_cost_name, self.default_grid_cost)\r\n\r\n -- Get/Set local variable allow negative rates\r\n self.allowNegative = self:toBool(self:getLocalVariable(self.var_allow_negative_name, self.default_allow_negative))\r\n\r\n -- Get/Set global FIBARO variables\r\n self.areaName = self:getGlobalFibaroVariable(self.global_var_area_name, self.default_area_name)\r\n self.areaCode = self:getAreaCode(self.areaName)\r\n self.unit = self:getGlobalFibaroVariable(self.global_var_unit_name, self.default_unit)\r\n\r\n self:refreshDisplayVariables()\r\nend\r\n\r\nfunction QuickApp:refreshDisplayVariables()\r\n -- Get FIBARO settings\r\n local fibaroSettings = api.get(\"/settings/info\")\r\n self.currency = fibaroSettings.currency\r\n self.timezoneOffset = tonumber(fibaroSettings.timezoneOffset)\r\n self.dateFormat = fibaroSettings.dateFormat\r\n self.i18n = i18n:new(fibaroSettings.defaultLanguage)\r\n \r\n -- Refrech global variables\r\n self.areaName = fibaro.getGlobalVariable(self.global_var_area_name)\r\n self.areaCode = self:getAreaCode(self.areaName)\r\n self.unit = fibaro.getGlobalVariable(self.global_var_unit_name)\r\n if (self.unit == nil) then self.unit = \"kWh\" end\r\n\r\n -- Load Tariff data table\r\n self:loadEnergyTariffTable()\r\n\r\n -- Refresh QA variable values\r\n self.token = self:getVariable(self.var_token_name)\r\n self.tariffHistory = tonumber(self:getVariable(self.var_tariff_history_name))\r\n self.low_price = tonumber(self:getVariable(self.var_Low_name))\r\n self.high_price = tonumber(self:getVariable(self.var_High_name))\r\n self.veryhigh_price = tonumber(self:getVariable(self.var_VeryHigh_name))\r\n self.tax = tonumber(self:getVariable(self.var_tax_percentage_name))\r\n self.decimals = self:getVariable(self.var_price_decimals_name)\r\n self.valueFormat = self:getValueFormat()\r\n\r\n self.operatorCost = tonumber(self:getVariable(self.var_operator_cost_name))\r\n self.gridLosses = tonumber(self:getVariable(self.var_grid_losses_name))\r\n self.gridAdjustment = tonumber(self:getVariable(self.var_grid_adjustment_name))\r\n self.dealerCost = tonumber(self:getVariable(self.var_dealer_cost_name))\r\n self.gridCost = tonumber(self:getVariable(self.var_grid_cost_name))\r\n\r\n self.allowNegative = self:toBool(self:getVariable(self.var_allow_negative_name))\r\n\r\n self.addTariffDate = self:getVariable(self.var_add_date_tariff_name)\r\n\r\n -- Update QA Unit property\r\n self:updateProperty(\"unit\", self:getCurrencySymbol() .. \"/\" ..tostring(self.unit))\r\n \r\n -- Set Energy rates data to display\r\n self:displayEnergyRate()\r\nend\r\n\r\n-- Get local QA variable, set to default value if variable is missing\r\nfunction QuickApp:getLocalVariable(name, defaultValue)\r\n if defaultValue == nil then defaultValue = \"\" end\r\n if (name == nil) then return defaultValue end\r\n local value = self:getVariable(name)\r\n\r\n if (value == nil or value == \"\" or value == \"nil\") then\r\n self:setVariable(name, tostring(defaultValue))\r\n return defaultValue\r\n else\r\n return value\r\n end\r\nend\r\n\r\n-- Get local QA variable, set to default value if variable is missing\r\nfunction QuickApp:getGlobalFibaroVariable(name, defaultValue)\r\n if defaultValue == nil then defaultValue = \"\" end\r\n if (name == nil) then return defaultValue end\r\n local value = fibaro.getGlobalVariable(name)\r\n\r\n if (value == nil or value == \"\" or value == \"nil\") then\r\n fibaro.setGlobalVariable(name, tostring(defaultValue))\r\n return defaultValue\r\n else\r\n return value\r\n end\r\nend\r\n\r\n-- Get default rate price based in local currency\r\nfunction QuickApp:getDefaultPriceDecimals()\r\n if (self.currency == nil or self.currency == \"\") then\r\n local fibaroSettings = api.get(\"/settings/info\")\r\n self.currency = fibaroSettings.currency\r\n end\r\n\r\n -- TODO: do an more accurat difference between currencies\r\n if (self.currency == \"EUR\" or self.currency == \"USD\" or self.currency == \"GBP\") then\r\n return \"5\"\r\n else\r\n return \"3\"\r\n end\r\nend\r\n\r\n-- Get default rate price based in local currency\r\nfunction QuickApp:getDefaultRatePrice(percent)\r\n if (self.currency == nil or self.currency == \"\") then\r\n local fibaroSettings = api.get(\"/settings/info\")\r\n self.currency = fibaroSettings.currency\r\n end\r\n\r\n -- TODO: do an more accurat difference between currencies\r\n if (self.currency == \"EUR\" or self.currency == \"USD\" or self.currency == \"GBP\") then\r\n return tostring(0.2 * (percent / 100))\r\n else\r\n return tostring(1 * (percent / 100))\r\n end\r\nend"},{"name":"display","isMain":false,"isOpen":true,"content":"function QuickApp:displayEnergyRate() \r\n -- ENTSO-e Energy Area not selected\r\n if self.areaCode == nil or self.areaCode == \"\" then\r\n self:updateView(\"labelInfo\", \"text\", \"\\n💡 \" ..self.i18n:get(\"NoSelectedArea\") ..\"\\n\\n\\n\")\r\n self:updateView(\"refreshButton\", \"text\", \"⚠️ \" ..self.i18n:get(\"Refresh\") ..\" ⚠️\")\r\n self:d(\"No EnergyArea selected!\")\r\n return\r\n end\r\n\r\n -- Get current Fibaro Tariff Data\r\n local tariffData = self:getEnergyRateData()\r\n \r\n -- Calculate values\r\n local rank = self:getRank(tariffData.currentRate)\r\n local nextRank = self:getRank(tariffData.nextRate)\r\n local nextDir = self:getNextDirection(tariffData.currentRate, tariffData.nextRate)\r\n local prevDir = self:getNextDirection(tariffData.previousRate, tariffData.currentRate)\r\n local rateDiff = string.format(self.valueFormat, tariffData.nextRate - tariffData.currentRate)\r\n local prevDiff = string.format(self.valueFormat, tariffData.currentRate - tariffData.previousRate)\r\n\r\n -- Set dafaults\r\n local labelInfo = \"\"\r\n local avgRate = 0\r\n local avgRank = \"\"\r\n local refresh = \"♻️\"\r\n local lastRqt = refresh ..self.i18n:get(\"LoadingEnergyRates\") ..\"️...\"\r\n local lastUpd = \"(\" ..tostring(self.lastVariableUpdate) ..\") \" ..refresh ..self.i18n:get(\"Refreshing\") ..\"️...\" \r\n local areaName = self.areaName\r\n local areaCode = self.areaCode\r\n\r\n -- Set Exchange rate\r\n local exchangeRate = \"--\"\r\n if (self.exchangeRate ~= nil) then exchangeRate = string.format(self.valueFormat, self.exchangeRate) end\r\n\r\n -- Show if any service error\r\n if (self:isNotEmpty(self.entsoeServiceMessage)) then labelInfo = labelInfo ..\"⛔ \" ..self.entsoeServiceMessage ..\"\\n\\n\" end\r\n if (self:isNotEmpty(self.exchServiceMessage)) then labelInfo = labelInfo ..\"⛔ \" ..self.exchServiceMessage ..\"\\n\\n\" end\r\n \r\n -- If Service request failed\r\n if self.serviceSuccess == false then\r\n refresh = \"⚠️\"\r\n lastUpd = tostring(self.lastVariableUpdate)\r\n areaName = self.areaName ..\"\\n\" ..refresh ..\" \" ..self.i18n:get(\"MissingEnergyRatesForSelectedArea\")\r\n end\r\n\r\n -- Only update variables and tariff if we got \"real\" rate data\r\n if tariffData.energyPricesUpdated == true then\r\n avgTotalRank = self:getRank(tariffData.avgTotalRate)\r\n avgDayRank = self:getRank(tariffData.avgDayRate)\r\n avgMonthRank = self:getRank(tariffData.avgMonthRate)\r\n \r\n -- Update FIBARO general variables \r\n fibaro.setGlobalVariable(self.global_var_level_name, rank) \r\n fibaro.setGlobalVariable(self.global_var_next_level_name, nextRank)\r\n fibaro.setGlobalVariable(self.global_var_month_level_name, avgMonthRank)\r\n\r\n -- Update QA with current hour price\r\n self:updateProperty('value', tonumber(tariffData.currentRate))\r\n self:updateProperty('log', self:getRankIcon(rank) ..rank)\r\n\r\n -- Update child QA with next hour price\r\n if (self.next_rank_device_id ~= nil) then\r\n fibaro.call(self.next_rank_device_id, 'setUnit', self:getCurrencySymbol() ..\" \" ..nextDir)\r\n fibaro.call(self.next_rank_device_id, 'setValue', tonumber(rateDiff))\r\n fibaro.call(self.next_rank_device_id, 'setLog', self:getRankIcon(nextRank) ..nextRank)\r\n end\r\n \r\n self.lastVariableUpdate = os.date(\"%Y-%m-%d %H:%M\")\r\n self.dataChanged = false\r\n lastUpd = self.lastVariableUpdate\r\n refresh = \"\"\r\n \r\n self:d(\"Display panels updated: \" ..os.date(\"%Y-%m-%d %H:%M:%S\"))\r\n end\r\n\r\n -- Update FIBARO Info panel\r\n if (tariffData.dayRatesExists == false) then\r\n labelInfo = labelInfo ..\"⚠️ \" ..self.i18n:get(\"TodayRatesMissing\") ..\"\\n\"\r\n labelInfo = labelInfo ..self.i18n:get(\"CurrentHour\") ..\" --\\n\"\r\n labelInfo = labelInfo ..self.i18n:get(\"NextHour\") ..\" --\\n\"\r\n labelInfo = labelInfo ..self.i18n:get(\"TodayAverage\") ..\" --\\n\\n\"\r\n else\r\n labelInfo = labelInfo ..self.i18n:get(\"TodayRates\") ..\" (\" ..self.i18n:get(\"Range\") ..\": \" ..tariffData.minDayRate ..\" ~ \" ..tariffData.maxDayRate ..\" \" ..self:getCurrencySymbol() ..\")\\n\"\r\n labelInfo = labelInfo ..self:getRankIcon(rank) ..\" \" ..self.i18n:get(\"CurrentHour\") ..\": \" ..tariffData.currentRate ..\" \" ..self:getCurrencySymbol() ..\" (\" ..prevDiff ..\" \" ..prevDir ..\") - \" ..self.i18n:get(rank) ..\"\\n\"\r\n labelInfo = labelInfo ..self:getRankIcon(nextRank) ..\" \" ..self.i18n:get(\"NextHour\") ..\": \" ..tariffData.nextRate ..\" \" ..self:getCurrencySymbol() ..\" (\" ..rateDiff ..\" \" ..nextDir ..\") - \" ..self.i18n:get(nextRank) ..\"\\n\"\r\n\r\n labelInfo = labelInfo ..self:getRankIcon(avgDayRank) ..\" \" ..self.i18n:get(\"TodayAverage\") ..\": \" .. tariffData.avgDayRate ..\" \" ..self:getCurrencySymbol() ..\" - \" ..self.i18n:get(avgDayRank) ..\"\\n\"\r\n end\r\n\r\n if (tariffData.nextDayRate == true) then\r\n local avgNextDayRank = self:getRank(tariffData.avgNextDayRate)\r\n labelInfo = labelInfo ..\"\\n\"\r\n labelInfo = labelInfo ..self.i18n:get(\"TomorrowRateRange\") ..\": \" ..tariffData.minNextDayRate ..\" ~ \" ..tariffData.maxNextDayRate ..\" \" ..self:getCurrencySymbol() ..\"\\n\"\r\n labelInfo = labelInfo ..self:getRankIcon(avgNextDayRank) ..\" \" ..self.i18n:get(\"TomorrowAverage\") ..\": \" ..tariffData.avgNextDayRate ..\" \" ..self:getCurrencySymbol() ..\" - \" ..self.i18n:get(avgNextDayRank) ..\"\\n\"\r\n end\r\n\r\n labelInfo = labelInfo ..\"\\n\"\r\n labelInfo = labelInfo ..self.i18n:get(\"TariffRatePeriod\") ..\": \" ..tariffData.firstDate ..\" -- \" ..tariffData.lastDate ..\"\\n\"\r\n labelInfo = labelInfo ..self:getRankIcon(avgMonthRank) ..\" \" ..self.i18n:get(\"ThisMonthAverage\") .. \": \" ..tariffData.avgMonthRate ..\" \" ..self:getCurrencySymbol() ..\" (\" ..string.format(\"%.0f\", tariffData.avgMonthCount/24) ..\" \" ..self.i18n:get(\"Days\") ..\")\\n\"\r\n labelInfo = labelInfo ..self:getRankIcon(avgTotalRank) ..\" \" ..self.i18n:get(\"TotalTariffAverage\") .. \": \" ..tariffData.avgTotalRate ..\" \" ..self:getCurrencySymbol() ..\" (\" ..string.format(\"%.0f\", tariffData.count/24) ..\" \" ..self.i18n:get(\"Days\") ..\")\" ..\"\\n\"\r\n\r\n labelInfo = labelInfo ..\"\\n\"\r\n labelInfo = labelInfo ..self.i18n:get(\"LowRatePrice\") ..\": \" ..self.low_price ..\" ~ \" ..self.medium_price ..\" \" ..self:getCurrencySymbol() ..\"/\" ..self.unit ..\"\\n\"\r\n labelInfo = labelInfo ..self.i18n:get(\"MediumRatePrice\") ..\": \" ..self.medium_price ..\" ~ \" ..self.high_price ..\" \" ..self:getCurrencySymbol() ..\"/\" ..self.unit ..\"\\n\"\r\n labelInfo = labelInfo ..self.i18n:get(\"HighRatePrice\") ..\": \" ..self.high_price ..\" ~ \" ..self.veryhigh_price ..\" \" ..self:getCurrencySymbol() ..\"/\" ..self.unit ..\"\\n\"\r\n\r\n labelInfo = labelInfo ..\"\\n\"\r\n labelInfo = labelInfo ..self.i18n:get(\"EnergyArea\") ..\": \" ..areaName ..\"\\n\"\r\n --labelInfo = labelInfo ..self.i18n:get(\"AreaCode\") ..\": \" ..areaCode ..\"\\n\" -- This is unnecessary information\r\n labelInfo = labelInfo ..self.i18n:get(\"TariffRateHistory\") ..\": \" ..self.tariffHistory ..\" \" ..self.i18n:get(\"days\") ..\"\\n\"\r\n labelInfo = labelInfo ..self.i18n:get(\"FibaroTariff\") ..\": \" ..fibaro.getGlobalVariable(self.global_var_fibaro_tariff_name) ..\"\\n\"\r\n \r\n labelInfo = labelInfo ..\"\\n\"\r\n labelInfo = labelInfo ..self.i18n:get(\"EnergyRateUpdate\") ..\": \" ..self.serviceRequestTime ..\"\\n\"\r\n labelInfo = labelInfo ..self.i18n:get(\"VariableUpdate\") ..\": \" ..lastUpd ..\"\\n\"\r\n\r\n -- Only show if Tax value is set\r\n if (self.tax ~= nil and self.tax ~= 0) then\r\n labelInfo = labelInfo ..\"\\n\"\r\n labelInfo = labelInfo ..refresh ..self.i18n:get(\"Tax\") ..\": \" ..string.format(\"%.2f\", self.tax) ..\"%\"\r\n end\r\n\r\n -- Only show if Operator value is set\r\n if (self.operatorCost ~= 0) then\r\n labelInfo = labelInfo ..\"\\n\"\r\n labelInfo = labelInfo ..refresh ..self.i18n:get(\"OperatorCost\") ..\": \" ..self.operatorCost ..\" \" ..self:getCurrencySymbol() ..\"/\"..self.unit\r\n end\r\n -- Only show if Losses value is set\r\n if (self.gridLosses ~= 0) then\r\n labelInfo = labelInfo ..\"\\n\"\r\n labelInfo = labelInfo ..refresh ..self.i18n:get(\"GridLosses\") ..\": \" ..string.format(\"%.2f\", self.gridLosses) ..\"%\"\r\n end\r\n -- Only show if Adjustment value is set\r\n if (self.gridAdjustment ~= 0) then\r\n labelInfo = labelInfo ..\"\\n\"\r\n labelInfo = labelInfo ..refresh ..self.i18n:get(\"GridAdjustment\") ..\": \" ..string.format(\"%.2f\", self.gridAdjustment) ..\"%\"\r\n end\r\n -- Only show if Dealer value is set\r\n if (self.dealerCost ~= 0) then\r\n labelInfo = labelInfo ..\"\\n\"\r\n labelInfo = labelInfo ..refresh ..self.i18n:get(\"DealerCost\") ..\": \" ..self.dealerCost ..\" \" ..self:getCurrencySymbol() ..\"/\"..self.unit\r\n end\r\n -- Only show if Grid value is set\r\n if (self.gridCost ~= 0) then\r\n labelInfo = labelInfo ..\"\\n\"\r\n labelInfo = labelInfo ..refresh ..self.i18n:get(\"GridCost\") ..\": \" ..self.gridCost ..\" \" ..self:getCurrencySymbol() ..\"/\"..self.unit\r\n end\r\n\r\n -- Only show if exchange currency is not in Euro\r\n if (self.currency ~= \"EUR\") then\r\n labelInfo = labelInfo ..\"\\n\"\r\n if (self:isNotEmpty(self.exchServiceMessage)) then labelInfo = labelInfo ..\"⚠️ \" end\r\n labelInfo = labelInfo ..self.i18n:get(\"ExchangeRate\") ..\" \" ..tostring(self.exchangeLastUpdate) ..\": 1 € = \" ..exchangeRate ..\" \" ..self.currency\r\n end\r\n \r\n -- If missing translation, just to trigger users to help me with translations ;)\r\n if not (self.i18n.isTranslated) then\r\n labelInfo = labelInfo ..\"\\n\"\r\n labelInfo = labelInfo ..\"💡 \"..self.i18n:get(\"MissingTranslation\") ..\": \" ..self.i18n.languageCode\r\n end\r\n\r\n self:updateView(\"labelInfo\", \"text\", labelInfo)\r\n self:updateView(\"refreshButton\", \"text\", self.i18n:get(\"Refresh\"))\r\n\r\n self:d(\"Current (\" ..self.i18n:get(rank) ..\") Rate: \" ..tariffData.currentRate ..\" \" ..self.currency ..\"/kWh, Next (\" ..nextRank ..\") Rate: \" ..tariffData.nextRate ..\" \" ..self.currency ..\"/kWh\" ..\" (\" ..rateDiff ..nextDir ..\")\")\r\nend"},{"name":"entsoe_GetEnergyRate","isMain":false,"isOpen":true,"content":"-- https://transparency.entsoe.eu/content/static_content/Static%20content/web%20api/Guide.html#_generation_domain\r\n-- 4.2.10. Day Ahead Rates [12.1.D]\r\n\r\nfunction QuickApp:getServiceRateData(callback, instance, date, exchRate, reportError)\r\n -- ENTSO-e service only returns 24 hour Rates on each request even if we define another \"to Date\" :(\r\n if (date == nil or date == \"\") then\r\n self:d(\"Missing date when request ENTSO-e rate service\")\r\n return nil \r\n end\r\n\r\n -- Don´t request ENTSO-e service if we already have the requested energy rates in table\r\n if (self:existsInEnergyTariffTable(self.tariffAreaRates, date)) then\r\n self:d(\"ENTSO-e rate already exists for date: \" ..date ..\" (Exch: \" ..tostring(exchRate) ..\")\")\r\n if (self.serviceRequestTime == \"--\") then\r\n self.serviceRequestTime = self:toDate(date)\r\n self.dataChanged = true \r\n end\r\n self.serviceSuccess = true\r\n return nil\r\n end\r\n \r\n -- Set current Exchange rate if missing or 0 if nextday\r\n if (exchRate == nil) then exchRate = self.exchangeRate end\r\n if (tonumber(date) > tonumber(os.date(\"%Y%m%d\"))) then exchRate = 0 end\r\n\r\n -- Request ENTO-e service\r\n self:d(\"Request ENTSO-e for period UTC: \" ..date ..\"0000 -- \" ..date ..\"2300 (AreaCode: \" ..self.areaCode ..\") Exch: \" ..tostring(exchRate))\r\n local ratePrices = {}\r\n local rateTable = {}\r\n local url = self.entsoe_baseURL ..\"?documentType=A44&in_Domain=\" ..self.areaCode ..\"&out_Domain=\" ..self.areaCode ..\"&periodStart=\" ..date ..\"0000&periodEnd=\" ..date ..\"2300&securityToken=\" ..self.token\r\n\r\n self.httpClient:request(url, {\r\n options = {\r\n method = \"GET\",\r\n headers = {\r\n [\"SECURITY_TOKEN\"] = self.token,\r\n [\"Accept\"] = \"text/xml\"\r\n }\r\n },\r\n success = function(response)\r\n -- Create Rate table from XML response\r\n local periodXml = self:getXmlElement(response.data, \"Period\")\r\n local success, data = pcall(function()\r\n return self:xml2PriceTable(periodXml) -- Extact rate prices from XML\r\n end) \r\n if success then\r\n if data == nil then\r\n if (reportError == true) then\r\n self.serviceSuccess = false\r\n self.entsoeServiceMessage = \"ERROR: Empty response from Url \" ..url\r\n QuickApp:error(self.entsoeServiceMessage)\r\n end\r\n return nil\r\n end\r\n\r\n -- Get UTC start and end date from response XML\r\n local startDate = self:getXmlDate(periodXml, \"start\", \"%Y-%m-%d %H:%M\")\r\n local endDate = self:getXmlDate(periodXml, \"end\", \"%Y-%m-%d %H:%M\")\r\n\r\n -- Create local date and rate table\r\n for index, rate in pairs(data) do\r\n local localDate = self:toLocalDate(startDate, self.timezoneOffset, \"%Y%m%d\")\r\n local ratePrice = {\r\n date = localDate,\r\n exch = exchRate,\r\n rate = tonumber(rate)\r\n }\r\n table.insert(ratePrices, ratePrice)\r\n end\r\n\r\n self.serviceRequestTime = os.date(self:getDateFormat()) ..\" \" ..os.date(\"%H:%M\")\r\n self.serviceSuccess = true\r\n self.entsoeServiceMessage = \"\"\r\n self.dataChanged = true\r\n self:d(\"=> ENTSO-e Success response: Start UTC = \" .. startDate .. \", End UTC = \" .. endDate .. \", \" .. self:tableCount(ratePrices) .. \" rates\")\r\n \r\n pcall(callback, instance, ratePrices)\r\n else\r\n if (reportError == true) then\r\n self.serviceSuccess = false\r\n self.dataChanged = true\r\n self.entsoeServiceMessage = \"Error: Can't get energy rates from ENTSO-e for area: \" ..self.areaName\r\n QuickApp:error(self.entsoeServiceMessage)\r\n end\r\n return nil\r\n end\r\n end,\r\n error = function(message)\r\n if (reportError == true) then\r\n self.serviceSuccess = false\r\n self.entsoeServiceMessage = \"ENTSO-e Error: \" ..message\r\n QuickApp:error(self.entsoeServiceMessage)\r\n self.httpClient = net.HTTPClient()\r\n end\r\n return nil\r\n end\r\n })\r\nend\r\n\r\n-- Extract ENTSO-e prices from response xml into Lua table\r\nfunction QuickApp:xml2PriceTable(xml)\r\n local priceTable = {}\r\n local ni, c, label, xarg, empty\r\n local i, j = 1, 1\r\n\r\n while true do\r\n ni, j, c, label, xarg, empty = string.find(xml, \"<(%/?)([%w:_]+)(.-)(%/?)>\", i)\r\n if not ni then break end\r\n local text = string.sub(xml, i, ni-1)\r\n \r\n if not string.find(text, \"^%s*$\") and label == \"price\" then\r\n table.insert(priceTable, text)\r\n end\r\n\r\n i = j+1\r\n end\r\n\r\n return priceTable\r\nend"},{"name":"entsoe_TariffTable","isMain":false,"isOpen":true,"content":"-- Load Energy Tariff table from FIBARO general variable \r\nfunction QuickApp:loadEnergyTariffTable()\r\n self.tariffData = {}\r\n self.tariffAreaRates = {}\r\n\r\n local jsonString = fibaro.getGlobalVariable(self.global_var_state_table_name)\r\n \r\n -- Create global variable if missing\r\n if (jsonString == nil) then self:createGlobalTableVariable() end\r\n\r\n -- Decode json string to table\r\n if (jsonString ~= nil and jsonString ~= \"\") then \r\n self.tariffData = json.decode(jsonString)\r\n end\r\n\r\n -- Set local area tariff table\r\n if (self.areaName == nil or self.areaName == \"\" or self.currency == nil or self.currency == \"\") then return end\r\n self.tariffAreaRates = self.tariffData[self.areaName ..\":\" ..self.currency]\r\n if (self.tariffAreaRates == nil) then self.tariffAreaRates = {} end\r\nend\r\n\r\n-- Refresh Energy Tariff table\r\nfunction QuickApp:refreshEnergyTariffTable()\r\n -- Get current day energy rates.\r\n self:getServiceRateData(QuickApp.updateEnergyTariffTable, self, os.date(\"%Y%m%d\"), self.exchangeRate, true)\r\n\r\n -- Get next 24 hour energy rates if they have been released\r\n fibaro.setTimeout(2000, function() self:getNextDayTariffRates() end)\r\n \r\n -- Add additional energy rate date from QA variable \"AddTariffDate\"\r\n fibaro.setTimeout(4000, function() self:updateHistoryTariffRates() end)\r\nend\r\n\r\n-- Update Energy Tariff table\r\nfunction QuickApp:updateEnergyTariffTable(energyRateTable)\r\n -- Exit if no data from ENTSO-e service\r\n if (energyRateTable == nil or self:tableCount(energyRateTable) == 0) then return end\r\n\r\n -- Set local variables\r\n if self.tariffHistory == nil then self.tariffHistory = 365 end\r\n local tariffHourHistory = self.tariffHistory\r\n local updateTable = false\r\n local areaTableId = self.areaName ..\":\" ..self.currency\r\n\r\n -- Load Energy Tariff Table \r\n self:loadEnergyTariffTable()\r\n \r\n local tblCount = 0\r\n local dayRates = {}\r\n local exchRate = self.exchangeRate\r\n local tariffDate = os.date(\"%Y%m%d\")\r\n\r\n -- Add ENTSO-e raw rates to Energy tariff table if not already exists\r\n local totalRate = 0;\r\n \r\n -- Loop ENTSO-e rates table and add to new Energy rates table\r\n for index, tariff in pairs(energyRateTable) do\r\n totalRate = totalRate + tariff.rate\r\n exchRate = tariff.exch\r\n tariffDate = tariff.date\r\n\r\n if not (self:existsInEnergyTariffTable(self.tariffAreaRates, tariff.date, index)) then\r\n table.insert(dayRates, {tariff.rate})\r\n updateTable = true\r\n end\r\n end\r\n\r\n -- If all rates in response table is 0 then not update Energy Tariff table, something is wrong!?\r\n if (updateTable and totalRate == 0) then\r\n self.serviceSuccess = false -- Something got wrong in ENTSO-e request\r\n QuickApp:error(\"Error in ENTSO-e Energy rate data!\")\r\n return\r\n end\r\n \r\n -- Add new rates to Area Tariff table\r\n if (updateTable) then\r\n table.insert(self.tariffAreaRates, {date = tariffDate, exch = exchRate, rates = dayRates})\r\n tblCount = self:tableCount(self.tariffAreaRates)\r\n\r\n -- Sort tariff table by Id (DateTime)\r\n table.sort(self.tariffAreaRates, function (t1, t2) return t1.date < t2.date end )\r\n\r\n -- Update Energy tariff rates\r\n local updTariffs = {}\r\n local startIndex = 0\r\n\r\n -- Clean old Energy tartiff rates\r\n if (tariffHourHistory > 0 and tariffHourHistory < tblCount) then\r\n startIndex = tblCount - tariffHourHistory\r\n end\r\n\r\n -- Update Exchange rate and clean old history\r\n for index, tariff in pairs(self.tariffAreaRates) do\r\n if index > startIndex then\r\n if (tariff.exch == 0 and tariff.date == os.date(\"%Y%m%d\")) then\r\n self:d(\"Exchange rate updated: \" ..tariff.exch ..\" => \" ..self.exchangeRate)\r\n tariff.exch = self.exchangeRate\r\n end\r\n table.insert(updTariffs, tariff)\r\n end\r\n end\r\n self.tariffAreaRates = updTariffs\r\n\r\n -- Save Energy tariff table to FIBARO general variable\r\n local areaTariffs = {}\r\n areaTariffs[areaTableId] = self.tariffAreaRates\r\n self.TariffData = areaTariffs\r\n fibaro.setGlobalVariable(self.global_var_state_table_name, json.encode(self.TariffData))\r\n\r\n self:d(\"Energy Tariff table updated: \" ..tariffDate ..\" (Exch:\" ..exchRate ..\"). Cleaned from \" ..startIndex ..\" old history day(s)\")\r\n end\r\nend\r\n\r\n-- Get Energy Tariff Data\r\nfunction QuickApp:getEnergyRateData()\r\n -- Load Energy Tariff data from general variables if empty\r\n self:loadEnergyTariffTable()\r\n\r\n if self.tariffHistory == nil then self.tariffHistory = 365 end\r\n \r\n local energyPricesUpdated = false\r\n local nowFormat = \"%H\" -- Local timezone \"HH\"\r\n local dayDate = os.date(\"%Y%m%d\") -- Local timezone \"YYYYMMDD\"\r\n local monthDate = os.date(\"%Y%m\") -- Local timezone \"YYYYMM\"\r\n local nextDayDate = os.date(\"%Y%m%d\", os.time() + 86400)\r\n local oneHour = 1 * 60 * 60 -- 1 hour\r\n local previousRate = self.high_price -- Set default to High price\r\n local currentRate = nil -- Set default to High price\r\n local nextRate = self.high_price -- Set default to High price\r\n local dayRatesExists = false\r\n local totalCount = 0\r\n local totalRate = 0\r\n local totalDayCount = 0\r\n local totalDayRate = 0\r\n local totalMonthCount = 0\r\n local totalMonthRate = 0\r\n local minDayRate = 9999\r\n local maxDayRate = 0\r\n local totalNextDayCount = 0\r\n local totalNextDayRate = 0 \r\n local minNextDayRate = 9999\r\n local maxNextDayRate = 0\r\n local avgNextDayRate = 0\r\n local nextDayRate = false\r\n local firstIdDate = \"\"\r\n local lastIdDate = \"\"\r\n local exchRate = 0\r\n\r\n -- Sum each FIBARO tariff rate (Rate is in €/MWh and id is in local format \"YYMMDDHH\")\r\n local tblCount = self:tableCount(self.tariffAreaRates)\r\n if (tblCount > 0) then\r\n for _, dateTariff in pairs(self.tariffAreaRates) do\r\n if (dateTariff == nil) then return {} end\r\n \r\n for index, tariff in pairs(dateTariff.rates) do\r\n local time = index - 1\r\n if (dateTariff.exch > 0) then exchRate = dateTariff.exch end\r\n\r\n -- Set first and last id\r\n if firstIdDate == \"\" then firstIdDate = dateTariff.date end\r\n lastIdDate = dateTariff.date\r\n\r\n -- Calculate to Local Tariff Rate price\r\n local locRate = self:calculateTariffRate(tariff[1], exchRate, self.unit, self.tax, self.operatorCost, self.gridLosses, self.gridAdjustment, self.dealerCost, self.gridCost, self.allowNegative)\r\n\r\n -- Set total values\r\n totalRate = totalRate + locRate\r\n totalCount = totalCount + 1\r\n energyPricesUpdated = true\r\n\r\n -- Sum today values \"YYYYMMDD\"\r\n if (dateTariff.date == dayDate) then\r\n totalDayRate = totalDayRate + locRate\r\n totalDayCount = totalDayCount + 1\r\n if locRate < minDayRate then minDayRate = locRate end\r\n if locRate > maxDayRate then maxDayRate = locRate end\r\n \r\n -- Set previous, next and current rate values\r\n if (time == tonumber(os.date(nowFormat, os.time() - oneHour))) then previousRate = locRate end\r\n if (time == tonumber(os.date(nowFormat, os.time() + oneHour))) then nextRate = locRate end\r\n if (time == tonumber(os.date(nowFormat, os.time()))) then currentRate = locRate end \r\n end\r\n \r\n -- Sum current month values \"YYYYMM\"\r\n if (string.sub(dateTariff.date, 1, 6) == monthDate) then\r\n totalMonthRate = totalMonthRate + locRate\r\n totalMonthCount = totalMonthCount + 1\r\n end\r\n\r\n -- Sum tomorrow values\r\n if (dateTariff.date == nextDayDate) then\r\n totalNextDayRate = totalNextDayRate + locRate\r\n totalNextDayCount = totalNextDayCount + 1\r\n if locRate < minNextDayRate then minNextDayRate = locRate end\r\n if locRate > maxNextDayRate then maxNextDayRate = locRate end\r\n end\r\n end\r\n end\r\n end\r\n\r\n -- Set minimum day rates to 0 if 9999\r\n if (minNextDayRate >= 9999) then minNextDayRate = 0 end\r\n if (minDayRate >= 9999) then minDayRate = 0 end\r\n \r\n -- Set if day rates exists\r\n if (totalDayCount > 0) then dayRatesExists = true end\r\n\r\n -- Calculate tomorrow average values\r\n if (totalNextDayCount > 0) then \r\n avgNextDayRate = totalNextDayRate / totalNextDayCount \r\n nextDayRate = true\r\n end\r\n\r\n -- Set current price if not exists\r\n local storeRate = tostring(currentRate)\r\n if (currentRate == nil) then\r\n currentRate = self.high_price\r\n storeRate = \"\"\r\n end\r\n\r\n -- Store current rate price to general variable\r\n fibaro.setGlobalVariable(self.global_var_current_rate_name, storeRate)\r\n\r\n -- Set return Tariff Data table\r\n local tariffData = {\r\n energyPricesUpdated = energyPricesUpdated,\r\n count = totalCount,\r\n previousRate = string.format(self.valueFormat, previousRate),\r\n currentRate = string.format(self.valueFormat, currentRate),\r\n nextRate = string.format(self.valueFormat, nextRate),\r\n dayRatesExists = dayRatesExists,\r\n avgTotalRate = self:toDefault(string.format(self.valueFormat, totalRate / totalCount), \"0\"),\r\n avgDayRate = self:toDefault(string.format(self.valueFormat, totalDayRate / totalDayCount), \"0\"),\r\n avgDayCount = totalDayCount,\r\n avgMonthRate = self:toDefault(string.format(self.valueFormat, totalMonthRate / totalMonthCount), \"0\"),\r\n avgMonthCount = totalMonthCount,\r\n minDayRate = string.format(self.valueFormat, minDayRate),\r\n maxDayRate = string.format(self.valueFormat, maxDayRate),\r\n nextDayRate = nextDayRate,\r\n avgNextDayRate = self:toDefault(string.format(self.valueFormat, avgNextDayRate), \"0\"),\r\n minNextDayRate = string.format(self.valueFormat, minNextDayRate),\r\n maxNextDayRate = string.format(self.valueFormat, maxNextDayRate),\r\n firstDate = self:toDate(firstIdDate, 0, \"%Y-%m-%d\"),\r\n lastDate = self:toDate(lastIdDate, 0, \"%Y-%m-%d\")\r\n }\r\n\r\n self:d(\"Energy tariff - Count: \" ..tariffData.count ..\" (History \" ..(self.tariffHistory) ..\" days), Previous Rate: \" ..tariffData.previousRate ..\", Current Rate: \" ..tariffData.currentRate ..\", next Rate: \" ..tariffData.nextRate ..\", Total avrage Rate: \" ..tariffData.avgTotalRate)\r\n\r\n return tariffData\r\nend\r\n\r\n-- Get next 24 hour energy rates if they have been released, normally the next day energy rates are released after 12:00 UTC.\r\n-- We also need the next day rates to solve the midnight shift between 23:00 and 00:00.\r\nfunction QuickApp:getNextDayTariffRates()\r\n if (self:isTimeForNextDayRates()) then\r\n self:getServiceRateData(QuickApp.updateEnergyTariffTable, self, os.date(\"!%Y%m%d\", os.time() + 86400), self.exchangeRate, false)\r\n\r\n self:debug(\"Get next 24 hour energy rates. (\" ..tostring(self.serviceSuccess) ..\") \" ..os.date(\"%H:%M\", os.time()) ..\" >= \" ..self:getRateReleaseTime(self.timezoneOffset, \"!%H:%M\") ..\" (UTC: \" ..os.date(\"!%Y-%m-%d\", os.time() + 86400) ..\") Exch: \" ..tostring(self.exchangeRate))\r\n end\r\nend\r\n\r\n-- Update Tariff table with history rates\r\nfunction QuickApp:updateHistoryTariffRates()\r\n -- Get date from local variable\r\n self.addTariffDate = self:getVariable(self.var_add_date_tariff_name)\r\n local addTariffDate = self:getNumbers(self.addTariffDate)\r\n\r\n if (self.serviceSuccess == false or addTariffDate == nil or addTariffDate == \"\" or addTariffDate == \"0\") then return end\r\n \r\n -- Get history Exchange rate from Exchangerate.host Api Service !! This not supported on Free account anymore !!\r\n --if (self.currency ~= \"EUR\") then -- If local currency already in Euro we don't need exchange rates.\r\n -- self:getServiceExchangeData(QuickApp.setExchangeRate, self, self:toDate(addTariffDate))\r\n --end\r\n\r\n fibaro.setTimeout(2000, function() \r\n local exchHistRate = self.exchangeRate\r\n self:getServiceRateData(QuickApp.updateEnergyTariffTable, self, addTariffDate, exchHistRate, false)\r\n self:debug(\"Add extra energy tariff rates for date: \" ..addTariffDate ..\" Exchange rate: 1 € = \" ..exchHistRate ..\" \" ..self.currency)\r\n\r\n self.addTariffDate = \"\"\r\n self:setVariable(self.var_add_date_tariff_name, dateString)\r\n end)\r\nend\r\n\r\n-- Check if rate already exists in Energy tariff table\r\nfunction QuickApp:existsInEnergyTariffTable(table, date, index)\r\n self:d(\"Check if Tariff rate exists: \" ..tostring(date) .. \", Index: \" ..tostring(index) ..\", \" ..tostring(table))\r\n if table == nil or date == nil or date == \"\" then return false end\r\n \r\n for idx, tariff in pairs(table) do\r\n -- self:d(\"--> Rate: \" ..tostring(date) ..\" = \" ..tariff.date ..\" [\" ..tostring(index) ..\"] = \" ..idx)\r\n if (tariff.date == date and (index == nil or tariff.rates[index][1] ~= nil)) then\r\n self:d(\"Tariff rate for \" ..date ..\" exists\")\r\n return true\r\n end\r\n end\r\n\r\n return false\r\nend"},{"name":"entsoe_AreaCodes","isMain":false,"isOpen":false,"content":"-- See \"BZN\" Area codes at: https://transparency.entsoe.eu/content/static_content/Static%20content/web%20api/Guide.html#_areas\r\n\r\nfunction QuickApp:createAreaVariables()\r\n local level_var = {\r\n name=self.global_var_area_name,\r\n isEnum=true,\r\n readOnly=true,\r\n value=self.default_area_name,\r\n enumValues={\"[SELECT AREA]\", \"Austria (AT)\",\"Belgium (BE)\",\"Bosnia and Herz. (BA)\",\"Bulgaria (BG)\",\"Croatia (HR)\",\"Czech Republic (CZ)\",\"Denmark (DK1)\",\"Denmark (DK2)\",\"Estonia (EE)\",\"Finland (FI)\",\"France (FR)\",\"Germany (DE-LU)\",\"Greece (GR)\",\"Hungary (HU)\",\"Ireland (SEM)\",\"Italy (Calabria)\",\"Italy (SACOAC)\",\"Italy (SACODC)\",\"Italy (Centre-North)\",\"Italy (Centre-South)\",\"Italy (North)\",\"Italy (Sardinia)\",\"Italy (Sicily)\",\"Italy (South)\",\"Latvia (LV)\",\"Lithuania (LT)\",\"Luxembourg (LU)\",\"Netherlands (NL)\",\"North Macedonia (MK)\",\"Norway (NO1)\",\"Norway (NO2)\",\"Norway (NO2NSL)\",\"Norway (NO3)\",\"Norway (NO4)\",\"Norway (NO5)\",\"Poland (PL)\",\"Portugal (PT)\",\"Romania (RO)\",\"Serbia (RS)\",\"Slovakia (SK)\",\"Slovenia (SI)\",\"Spain (ES)\",\"Sweden (SE1)\",\"Sweden (SE2)\",\"Sweden (SE3)\",\"Sweden (SE4)\",\"Switzerland (CH)\",\"Ukraine (UA-IPS)\",\"United Kingdom (GB)\"}\r\n }\r\n api.post('/globalVariables/',level_var) \r\nend\r\n\r\nfunction QuickApp:getAreaCode(areaName)\r\n -- Set default Area code if \"areaName\" is missing.\r\n if areaName == nil or areaName == \"\" then areaName = self.default_area_name end\r\n \r\n if (areaName == \"[SELECT AREA]\") then return \"\" end\r\n if (areaName == \"Austria (AT)\") then return \"10YAT-APG------L\" end\r\n if (areaName == \"Belgium (BE)\") then return \"10YBE----------2\" end\r\n if (areaName == \"Bosnia and Herz. (BA)\") then return \"10YBA-JPCC-----D\" end\r\n if (areaName == \"Bulgaria (BG)\") then return \"10YCA-BULGARIA-R\" end\r\n if (areaName == \"Croatia (HR)\") then return \"10YHR-HEP------M\" end\r\n if (areaName == \"Czech Republic (CZ)\") then return \"10YCZ-CEPS-----N\" end\r\n if (areaName == \"Denmark (DK1)\") then return \"10YDK-1--------W\" end\r\n if (areaName == \"Denmark (DK2)\") then return \"10YDK-2--------M\" end\r\n if (areaName == \"Estonia (EE)\") then return \"10Y1001A1001A39I\" end\r\n if (areaName == \"Finland (FI)\") then return \"10YFI-1--------U\" end\r\n if (areaName == \"France (FR)\") then return \"10YFR-RTE------C\" end\r\n if (areaName == \"Germany (DE-LU)\") then return \"10Y1001A1001A82H\" end\r\n if (areaName == \"Greece (GR)\") then return \"10YGR-HTSO-----Y\" end\r\n if (areaName == \"Hungary (HU)\") then return \"10YHU-MAVIR----U\" end\r\n if (areaName == \"Ireland (SEM)\") then return \"10Y1001A1001A59C\" end\r\n if (areaName == \"Italy (Calabria)\") then return \"10Y1001C--00096J\" end\r\n if (areaName == \"Italy (Centre-North)\") then return \"10Y1001A1001A70O\" end\r\n if (areaName == \"Italy (Centre-South)\") then return \"10Y1001A1001A71M\" end\r\n if (areaName == \"Italy (North)\") then return \"10Y1001A1001A73I\" end\r\n if (areaName == \"Italy (SACOAC)\") then return \"10Y1001A1001A885\" end\r\n if (areaName == \"Italy (SACODC)\") then return \"10Y1001A1001A893\" end\r\n if (areaName == \"Italy (Sardinia)\") then return \"10Y1001A1001A74G\" end\r\n if (areaName == \"Italy (Sicily)\") then return \"10Y1001A1001A75E\" end\r\n if (areaName == \"Italy (South)\") then return \"10Y1001A1001A788\" end\r\n if (areaName == \"Latvia (LV)\") then return \"10YLV-1001A00074\" end\r\n if (areaName == \"Lithuania (LT)\") then return \"10YLT-1001A0008Q\" end\r\n if (areaName == \"Luxembourg (LU)\") then return \"10Y1001A1001A82H\" end\r\n if (areaName == \"Netherlands (NL)\") then return \"10YNL----------L\" end\r\n if (areaName == \"North Macedonia (MK)\") then return \"10YMK-MEPSO----8\" end\r\n if (areaName == \"Norway (NO1)\") then return \"10YNO-1--------2\" end\r\n if (areaName == \"Norway (NO2)\") then return \"10YNO-2--------T\" end\r\n if (areaName == \"Norway (NO2NSL)\") then return \"50Y0JVU59B4JWQCU\" end\r\n if (areaName == \"Norway (NO3)\") then return \"10YNO-3--------J\" end\r\n if (areaName == \"Norway (NO4)\") then return \"10YNO-4--------9\" end\r\n if (areaName == \"Norway (NO5)\") then return \"10Y1001A1001A48H\" end\r\n if (areaName == \"Poland (PL)\") then return \"10YPL-AREA-----S\" end\r\n if (areaName == \"Portugal (PT)\") then return \"10YPT-REN------W\" end\r\n if (areaName == \"Romania (RO)\") then return \"10YRO-TEL------P\" end\r\n if (areaName == \"Serbia (RS)\") then return \"10YCS-SERBIATSOV\" end\r\n if (areaName == \"Slovakia (SK)\") then return \"10YSK-SEPS-----K\" end\r\n if (areaName == \"Slovenia (SI)\") then return \"10YSI-ELES-----O\" end\r\n if (areaName == \"Spain (ES)\") then return \"10YES-REE------0\" end\r\n if (areaName == \"Sweden (SE1)\") then return \"10Y1001A1001A44P\" end\r\n if (areaName == \"Sweden (SE2)\") then return \"10Y1001A1001A45N\" end\r\n if (areaName == \"Sweden (SE3)\") then return \"10Y1001A1001A46L\" end\r\n if (areaName == \"Sweden (SE4)\") then return \"10Y1001A1001A47J\" end\r\n if (areaName == \"Switzerland (CH)\") then return \"10YCH-SWISSGRIDZ\" end\r\n if (areaName == \"Ukraine (UA-IPS)\") then return \"10Y1001C--000182\" end\r\n if (areaName == \"United Kingdom (GB)\") then return \"10YGB----------A\" end\r\n\r\n return \"\" -- No match\r\nend"},{"name":"fibaroTariffRate","isMain":false,"isOpen":false,"content":"-- Update FIBARO Tariff rate table\r\nfunction QuickApp:updateFibaroTariffTable()\r\n -- Exit if no data in global table or we got Service error\r\n if (self:getGlobalFibaroVariable(self.global_var_fibaro_tariff_name, \"ON\") == \"OFF\" or self:tableCount(self.tariffAreaRates) == 0) then \r\n self:d(\"Update FIBARO Tariff table is turned OFF\")\r\n return\r\n end\r\n\r\n -- Get current FIBARO Energy Tariff rate data\r\n local tariffData = api.get(\"/energy/billing/tariff\")\r\n local currRate = tariffData.rate\r\n local exchRate = self.exchangeRate\r\n local addTariffs = {}\r\n local currentRateTime = os.date(\"%Y%m%d%H\")\r\n local count = 0\r\n\r\n -- Update FIBARO Additional Tariff table in local currency/kWh and local timezone\r\n for _, dateTariff in pairs(self.tariffAreaRates) do\r\n if (dateTariff == nil) then return end\r\n exchRate = dateTariff.exch\r\n\r\n for index, tariff in pairs(dateTariff.rates) do\r\n local time = index - 1\r\n local currTime = self:toDate(dateTariff.date, time, \"%Y%m%d%H\")\r\n local startTime = self:toDate(dateTariff.date, time, \"%H:%M\", 0)\r\n local endTime = self:toDate(dateTariff.date, time, \"%H:%M\", 1)\r\n local tariffName = self:toDate(dateTariff.date, time, self:getDateFormat(), 0) ..\" \" ..startTime ..\" (\" ..tariff[1] ..\" €/MWh)\"\r\n\r\n -- Calculate to Local Tariff Rate price, FIBARO can only display prices in kWh\r\n local locRate = self:calculateTariffRate(tariff[1], exchRate, \"kWh\", self.tax, self.operatorCost, self.gridLosses, self.gridAdjustment, self.dealerCost, self.gridCost)\r\n\r\n -- FIBARO Tariff table can't have zero or negative value :(\r\n if (locRate <= 0) then\r\n tariffName = tariffName ..\" \" ..string.format(self:getValueFormat(), locRate) ..\" ⛔\"\r\n locRate = 0.00001\r\n end\r\n\r\n -- Get current rate\r\n if (currTime == currentRateTime) then \r\n currRate = locRate\r\n tariffName = tariffName ..\" ⭐\"\r\n self:d(\"Current energy price: \"..tariff[1] ..\"=\" ..currRate ..\" at exchange: \" ..dateTariff.exch ..\", Unit: \" ..self.unit ..\", Name: \" ..tariffName)\r\n end\r\n \r\n -- Add additional tariff to local tariff table\r\n local newTariff = {\r\n name = tariffName,\r\n rate = locRate,\r\n startTime = startTime,\r\n endTime = endTime,\r\n days = {string.lower(self:toDate(dateTariff.date, time, \"%A\", 0))}\r\n }\r\n table.insert(addTariffs, newTariff)\r\n count = count + 1\r\n end\r\n end\r\n\r\n -- Save new tariff table to FIBARO Tariff table\r\n local response, code = api.put(\"/energy/billing/tariff\", {\r\n returnRate = tariffData.returnRate,\r\n additionalTariffs = addTariffs,\r\n name = tariffData.name,\r\n rate = currRate\r\n })\r\n\r\n self:d(\" => Update \" ..count ..\" FIBARO Tariffs with response code: \" .. tostring(code) .. \" - \\\"\" .. tariffData.name .. \"\\\" Rate: \" .. tariffData.rate .. \" => \" .. currRate)\r\nend"},{"name":"i18n","isMain":false,"isOpen":false,"content":"class \"i18n\"\r\n\r\nphrases = {\r\n en = {\r\n [\"Refreshing\"] = \"Refreshing\",\r\n [\"LoadingEnergyRates\"] = \"Loading energy rates\",\r\n [\"Refresh\"] = \"Refresh\",\r\n [\"MissingEnergyRatesForSelectedArea\"] = \"Missing Energy rates for selected energy area\",\r\n [\"ExchangeRate\"] = \"Exchange rate\",\r\n [\"ExchangeRateLastUpdate\"] = \"Exchange rate updated\",\r\n [\"TodayRates\"] = \"Todays rates\",\r\n [\"TodayRatesMissing\"] = \"Todays rates is missing from ENTSO-e!\", \r\n [\"Range\"] = \"Range\",\r\n [\"To\"] = \"to\",\r\n [\"CurrentHour\"] = \"Current hour\",\r\n [\"NextHour\"] = \"Next hour\",\r\n [\"TodayAverage\"] = \"Today average\",\r\n [\"TomorrowRatesReleases\"] = \"Tomorrow rates releases after\",\r\n [\"TomorrowAverage\"] = \"Tomorrow average\",\r\n [\"TomorrowRateRange\"] = \"Tomorrow rate range\",\r\n [\"TariffRatePeriod\"] = \"Tariff rate period\",\r\n [\"ThisMonthAverage\"] = \"This month average\",\r\n [\"TotalTariffAverage\"] = \"Total tariff average\",\r\n [\"Days\"] = \"Days\",\r\n [\"days\"] = \"days\",\r\n [\"EnergyArea\"] = \"Energy area\",\r\n [\"AreaCode\"] = \"ENTSO-e AreaCode\",\r\n [\"LanguageCode\"] = \"Language Code\",\r\n [\"TariffRateHistory\"] = \"Tariff rate history\",\r\n [\"LowRatePrice\"] = \"Low price range\",\r\n [\"MediumRatePrice\"] = \"Medium price range\",\r\n [\"HighRatePrice\"] = \"High price range\",\r\n [\"Tax\"] = \"Energy prices include tax of\",\r\n [\"OperatorCost\"] = \"Operator cost\",\r\n [\"GridLosses\"] = \"Grid Losses percent cost\",\r\n [\"GridAdjustment\"] = \"Adjustment percent cost\",\r\n [\"DealerCost\"] = \"Dealer cost\",\r\n [\"GridCost\"] = \"Local Grid cost\",\r\n [\"FibaroTariff\"] = \"FIBARO Tariff rates\",\r\n [\"EnergyRateUpdate\"] = \"ENTSO-e Service update\",\r\n [\"VariableUpdate\"] = \"Panel update\",\r\n [\"NoSelectedArea\"] = \"Please select [EnergyArea] in FIBARO general variables.\",\r\n [\"MissingTranslation\"] = \"Translation is missing for this language\",\r\n [\"VeryHIGH\"] = \"VeryHIGH\",\r\n [\"HIGH\"] = \"HIGH\",\r\n [\"MEDIUM\"] = \"MEDIUM\",\r\n [\"LOW\"] = \"LOW\",\r\n [\"VeryLOW\"] = \"VeryLOW\",\r\n },\r\n da = {\r\n [\"Refreshing\"] = \"Opdatering\",\r\n [\"LoadingEnergyRates\"] = \"Indlæsning af energisatser\",\r\n [\"Refresh\"] = \"Opdatering\",\r\n [\"MissingEnergyRatesForSelectedArea\"] = \"Manglende energisatser for udvalgt energiområde\",\r\n [\"ExchangeRate\"] = \"Valutakurs\",\r\n [\"ExchangeRateLastUpdate\"] = \"Exchange rate updated\",\r\n [\"TodayRates\"] = \"I dag kurser\",\r\n [\"TodayRatesMissing\"] = \"Todays rates is missing\",\r\n [\"Range\"] = \"Rækkevidde\",\r\n [\"To\"] = \"til\",\r\n [\"CurrentHour\"] = \"Aktuel time\",\r\n [\"NextHour\"] = \"Næste time\",\r\n [\"TodayAverage\"] = \"Dagens gennemsnit\",\r\n [\"TomorrowRatesReleases\"] = \"Priserne i morgen offentliggøres\",\r\n [\"TomorrowAverage\"] = \"Morgendagens gennemsnit\",\r\n [\"TomorrowRateRange\"] = \"Prisinterval i morgen\",\r\n [\"TariffRatePeriod\"] = \"Tarifperiode\",\r\n [\"ThisMonthAverage\"] = \"Gennemsnit i denne måned\",\r\n [\"TotalTariffAverage\"] = \"Samlet tarifgennemsnit\",\r\n [\"Days\"] = \"Dage\",\r\n [\"days\"] = \"dage\",\r\n [\"EnergyArea\"] = \"Energiområde\",\r\n [\"AreaCode\"] = \"ENTSO-e Områdenummer\",\r\n [\"LanguageCode\"] = \"Sprogkode\",\r\n [\"TariffRateHistory\"] = \"Tarifhistorie\",\r\n [\"LowRatePrice\"] = \"Lav pris\",\r\n [\"MediumRatePrice\"] = \"Mellempris pris\",\r\n [\"HighRatePrice\"] = \"Høj pris\",\r\n [\"Tax\"] = \"Priserne er inklusive moms\",\r\n [\"OperatorCost\"] = \"Operatøromkostninger\",\r\n [\"GridLosses\"] = \"Nettab i procent omkostninger\",\r\n [\"GridAdjustment\"] = \"Justeringsprocent omkostninger\",\r\n [\"DealerCost\"] = \"Forhandler omkostninger\",\r\n [\"GridCost\"] = \"Lokale netomkostninger\",\r\n [\"FibaroTariff\"] = \"FIBARO tarifsatser\",\r\n [\"NoSelectedArea\"] = \"Vælg venligst [EnergyArea] i FIBARO generelle variabler.\",\r\n [\"EnergyRateUpdate\"] = \"ENTSO-e Energirate opdatering\",\r\n [\"VariableUpdate\"] = \"Panel opdatering\",\r\n [\"MissingTranslation\"] = \"Translation is missing for this language\",\r\n [\"VeryHIGH\"] = \"VeryHIGH\",\r\n [\"HIGH\"] = \"HIGH\",\r\n [\"MEDIUM\"] = \"MEDIUM\",\r\n [\"LOW\"] = \"LOW\",\r\n [\"VeryLOW\"] = \"VeryLOW\",\r\n },\r\n no = {\r\n [\"Refreshing\"] = \"Oppdaterer\",\r\n [\"LoadingEnergyRates\"] = \"Laster spotpriser\",\r\n [\"Refresh\"] = \"Oppdater\",\r\n [\"MissingEnergyRatesForSelectedArea\"] = \"Spotpriser mangler for valgt energiområde\",\r\n [\"ExchangeRate\"] = \"Vekslingskurs\",\r\n [\"ExchangeRateLastUpdate\"] = \"Exchange rate updated\",\r\n [\"TodayRates\"] = \"Dagens priser\",\r\n [\"TodayRatesMissing\"] = \"Todays rates is missing\",\r\n [\"Range\"] = \"Område\",\r\n [\"To\"] = \"til\",\r\n [\"CurrentHour\"] = \"Aktuell time\",\r\n [\"NextHour\"] = \"Neste time\",\r\n [\"TodayAverage\"] = \"Dagens gjennomsnittsverdi\",\r\n [\"TomorrowRatesReleases\"] = \"Morgendagens priser er sluppet\",\r\n [\"TomorrowAverage\"] = \"Morgendagens gjennomsnitt\",\r\n [\"TomorrowRateRange\"] = \"Morgendagens omfang\",\r\n [\"TariffRatePeriod\"] = \"Tariffperiode\",\r\n [\"ThisMonthAverage\"] = \"Nåværende måneds gjennomsnitt\",\r\n [\"TotalTariffAverage\"] = \"Gjennomsnittlig verdi totalt sett\",\r\n [\"Days\"] = \"Dager\",\r\n [\"days\"] = \"dager\",\r\n [\"EnergyArea\"] = \"Energiområde\",\r\n [\"AreaCode\"] = \"Områdeskode (ENTSO-e)\",\r\n [\"LanguageCode\"] = \"Språkkode\",\r\n [\"TariffRateHistory\"] = \"Tariffhistorie\",\r\n [\"LowRatePrice\"] = \"Lav pris\",\r\n [\"MediumRatePrice\"] = \"Gjennomsnittspris\",\r\n [\"HighRatePrice\"] = \"Høy pris\",\r\n [\"Tax\"] = \"Prisene inkluderer skatt\",\r\n [\"OperatorCost\"] = \"Operatørkostnad\",\r\n [\"GridLosses\"] = \"Nettap prosent kostnad\",\r\n [\"GridAdjustment\"] = \"Justering prosent kostnad\",\r\n [\"DealerCost\"] = \"Forhandlerkostnad\",\r\n [\"GridCost\"] = \"Lokale nettkostnader\",\r\n [\"FibaroTariff\"] = \"FIBARO Tariffsatser\",\r\n [\"NoSelectedArea\"] = \"Vennligst velg [EnergyArea] i FIBARO generelle variabler.\",\r\n [\"EnergyRateUpdate\"] = \"ENTSO-e Spotpriser sist oppdatert\",\r\n [\"VariableUpdate\"] = \"Panel sist oppdatert\",\r\n [\"MissingTranslation\"] = \"Translation is missing for this language\",\r\n [\"VeryHIGH\"] = \"VeryHIGH\",\r\n [\"HIGH\"] = \"HIGH\",\r\n [\"MEDIUM\"] = \"MEDIUM\",\r\n [\"LOW\"] = \"LOW\",\r\n [\"VeryLOW\"] = \"VeryLOW\",\r\n },\r\n pt = {\r\n [\"Refreshing\"] = \"Em atualização\",\r\n [\"LoadingEnergyRates\"] = \"A carregar preços\",\r\n [\"Refresh\"] = \"Atualizar\",\r\n [\"MissingEnergyRatesForSelectedArea\"] = \"Preços inexistentes para o local selecionado\",\r\n [\"ExchangeRate\"] = \"Taxa de câmbio\",\r\n [\"ExchangeRateLastUpdate\"] = \"Exchange rate updated\",\r\n [\"TodayRates\"] = \"Preços de hoje\",\r\n [\"TodayRatesMissing\"] = \"Todays rates is missing\",\r\n [\"Range\"] = \"Intervalo\",\r\n [\"To\"] = \"a\",\r\n [\"CurrentHour\"] = \"Hora atual\",\r\n [\"NextHour\"] = \"Hora seguinte\",\r\n [\"TodayAverage\"] = \"Média de hoje\",\r\n [\"TomorrowRatesReleases\"] = \"Indicação dos preços para amanhã\",\r\n [\"TomorrowAverage\"] = \"Preço médio de amanhã\",\r\n [\"TomorrowRateRange\"] = \"Intervalo de preços para amanhã\",\r\n [\"TariffRatePeriod\"] = \"Período tarifário\",\r\n [\"ThisMonthAverage\"] = \"Média do mês atual\",\r\n [\"TotalTariffAverage\"] = \"Preço médio total\",\r\n [\"Days\"] = \"Dias\",\r\n [\"days\"] = \"dias\",\r\n [\"EnergyArea\"] = \"Local\",\r\n [\"AreaCode\"] = \"Código do local\",\r\n [\"LanguageCode\"] = \"Código de idioma\",\r\n [\"TariffRateHistory\"] = \"Histórico de preços\",\r\n [\"LowRatePrice\"] = \"Preço de taxa baixa\",\r\n [\"MediumRatePrice\"] = \"Preço médio\",\r\n [\"HighRatePrice\"] = \"Preço de taxa alta\",\r\n [\"Tax\"] = \"Preço inclui taxa de\", \r\n [\"OperatorCost\"] = \"Desvios e serviços do operador de sistema\",\r\n [\"GridLosses\"] = \"Perdas de rede\",\r\n [\"GridAdjustment\"] = \"Fator de adequação ORD\",\r\n [\"DealerCost\"] = \"Custo de gestão\",\r\n [\"GridCost\"] = \"Taxa de Acesso às Redes\",\r\n [\"FibaroTariff\"] = \"Tarifas FIBARO\",\r\n [\"NoSelectedArea\"] = \"Selecione [EnergyArea] nas variáveis gerais FIBARO.\",\r\n [\"EnergyRateUpdate\"] = \"ENTSO-e Atualização do preço\",\r\n [\"VariableUpdate\"] = \"Atualização de variações\",\r\n [\"MissingTranslation\"] = \"Tradução inexistente para este idioma\",\r\n [\"VeryHIGH\"] = \"Muito ALTO\",\r\n [\"HIGH\"] = \"ALTO\",\r\n [\"MEDIUM\"] = \"RAZOÁVEL\",\r\n [\"LOW\"] = \"BAIXO\",\r\n [\"VeryLOW\"] = \"Muito BAIXO\",\r\n },\r\n sv = {\r\n [\"Refreshing\"] = \"Uppdaterar\",\r\n [\"LoadingEnergyRates\"] = \"Laddar spotpriser\",\r\n [\"Refresh\"] = \"Uppdatera\",\r\n [\"MissingEnergyRatesForSelectedArea\"] = \"Spotpriser saknas för valt energiområde\",\r\n [\"ExchangeRate\"] = \"Växlingskurs\",\r\n [\"ExchangeRateLastUpdate\"] = \"Senast updaterad\",\r\n [\"TodayRates\"] = \"Dagens priser\",\r\n [\"TodayRatesMissing\"] = \"Todays rates is missing\",\r\n [\"Range\"] = \"Omfång\",\r\n [\"To\"] = \"till\",\r\n [\"CurrentHour\"] = \"Innevarande timme\",\r\n [\"NextHour\"] = \"Kommande timme\",\r\n [\"TodayAverage\"] = \"Dagens medelvärde\",\r\n [\"TomorrowRatesReleases\"] = \"Morgondagens priser släpps efter\",\r\n [\"TomorrowAverage\"] = \"Morgondagens medelvärde\",\r\n [\"TomorrowRateRange\"] = \"Morgondagens omfång\",\r\n [\"TariffRatePeriod\"] = \"Tariffperiod\",\r\n [\"ThisMonthAverage\"] = \"Innevarande månads medelvärde\",\r\n [\"TotalTariffAverage\"] = \"Medelvärde totalt\",\r\n [\"Days\"] = \"Dagar\",\r\n [\"days\"] = \"dagar\",\r\n [\"EnergyArea\"] = \"Energiområde\",\r\n [\"AreaCode\"] = \"Områdeskod (ENTSO-e)\",\r\n [\"LanguageCode\"] = \"Språkkod\",\r\n [\"TariffRateHistory\"] = \"Tariff historik\",\r\n [\"LowRatePrice\"] = \"Lågt pris\",\r\n [\"MediumRatePrice\"] = \"Medelpris\",\r\n [\"HighRatePrice\"] = \"Högt pris\",\r\n [\"Tax\"] = \"Priserna inkluderar skatt med\",\r\n [\"OperatorCost\"] = \"Operatörskostnad\",\r\n [\"GridLosses\"] = \"Nätförlustskostnad\",\r\n [\"GridAdjustment\"] = \"Nät justeringskostnad\",\r\n [\"DealerCost\"] = \"Leverantörskostnad\",\r\n [\"GridCost\"] = \"Lokal nätkostnad\",\r\n [\"FibaroTariff\"] = \"FIBARO Tariff priser\",\r\n [\"EnergyRateUpdate\"] = \"ENTSO-e Spotpriser uppdaterad\",\r\n [\"VariableUpdate\"] = \"Panel senast uppdaterad\",\r\n [\"NoSelectedArea\"] = \"Inget elområde valt i generella variabler [EnergyArea].\", \r\n [\"MissingTranslation\"] = \"Translation is missing for this language\",\r\n [\"VeryHIGH\"] = \"VeryHIGH\",\r\n [\"HIGH\"] = \"HIGH\",\r\n [\"MEDIUM\"] = \"MEDIUM\",\r\n [\"LOW\"] = \"LOW\",\r\n [\"VeryLOW\"] = \"VeryLOW\",\r\n }\r\n}\r\n\r\nfunction i18n:new(langCode)\r\n self.isTranslated = true\r\n if phrases[langCode] == nil then \r\n langCode = \"en\"\r\n self.isTranslated = false\r\n end\r\n self.languageCode = langCode\r\n self.phrases = phrases[langCode]\r\n return self\r\nend\r\n\r\nfunction i18n:get(key)\r\n if key == nil then return \"\" end\r\n if self.phrases[key] then return self.phrases[key] end\r\n return key\r\nend"},{"name":"exchangeRate_host","isMain":false,"isOpen":false,"content":"-- Exchange rates API (https://exchangerate.host) is a simple and lightweight free service for current and historical foreign exchange rates & crypto exchange rates.\r\n-- Create free account at https://exchangerate.host to get an access key that will give you 1000 requests each month.\r\n\r\nfunction QuickApp:getServiceExchangeRate(force)\r\n self.exchServiceMessage = \"\"\r\n\r\n if (self.currency == \"EUR\") then\r\n if (self.exchangeRate > 1) then \r\n self:setVariable(self.var_exchange_rate_name, 0)\r\n self:setVariable(self.var_exchange_last_update_name, \"\")\r\n end\r\n self.exchangeRate = 1 -- Set default excahnge rate 1:1 for €\r\n self.exchangeRateUpdated = true\r\n return force\r\n end\r\n\r\n -- Check if we fulfill to request Exchangerate.host API\r\n if (self:isEmpty(self.exchangerate_Key)) then\r\n self.exchangeRateUpdated = false\r\n self.exchServiceMessage = \"Local QA variable [\" ..self.var_exchange_rate_Key_name ..\"] is required to get exchange rates between EUR and \" ..self.currency ..\".\\nRegister a free account at https://exchangerate.host to get your own API access key.\"\r\n QuickApp:error(self.exchServiceMessage)\r\n return true\r\n end\r\n \r\n -- Check if we already have got the Exchange rate for today\r\n if (self:isNotEmpty(self.exchangeLastUpdate) and os.date(\"%Y-%m-%d\") == self.exchangeLastUpdate) then\r\n self:d(\"Exchange rate for date \" ..self.exchangeLastUpdate ..\" is already retrieved: 1 EUR = \" ..tostring(self.exchangeRate) ..\" \" ..self.currency)\r\n self.exchangeRateUpdated = true\r\n return force\r\n end\r\n\r\n -- Request exchangerate.host with source currency \"EUR\" that always is ENTSO-e base currency\r\n local url = self.exchangerate_baseURL ..\"live?access_key=\" ..self.exchangerate_Key ..\"&source=EUR¤cies=\" ..self.currency\r\n\r\n self.httpClient:request(url, {\r\n options = {\r\n method = \"GET\",\r\n headers = {[\"Accept\"] = \"application/json\"}\r\n },\r\n success = function(response)\r\n local success, data = pcall(function()\r\n return json.decode(response.data)\r\n end)\r\n\r\n if success then\r\n self:d(\"Exchange Rate response: \" ..json.encode(data))\r\n\r\n if (data.success == true) then\r\n self.exchangeRateUpdated = true\r\n self.exchangeLastUpdate = os.date(\"%Y-%m-%d\", data.timestamp)\r\n self:setVariable(self.var_exchange_last_update_name, self.exchangeLastUpdate)\r\n \r\n local cSymbol = \"EUR\" ..self.currency\r\n self.exchangeRate = data.quotes[cSymbol]\r\n self:setVariable(self.var_exchange_rate_name, self.exchangeRate)\r\n self:d(\"Exchange Rate \" ..os.date(\"%Y-%m-%d %H:%M\", data.timestamp) ..\": 1 EUR = \" ..self.exchangeRate ..\" \" ..self.currency)\r\n else\r\n self.exchangeRateUpdated = false\r\n self.exchServiceMessage = \"Exchange rate error:\\n\" ..data.error.info\r\n QuickApp:error(\"Error when request for exchange rate from Url: \" ..url ..\". \" ..self.exchServiceMessage)\r\n end\r\n else\r\n self.exchangeRateUpdated = false\r\n QuickApp:error(\"Broken json response from Url: \" ..url)\r\n end\r\n end,\r\n error = function(message)\r\n self.exchangeRateUpdated = false\r\n self.exchServiceMessage = \"Exchange rate error: \" ..message\r\n self.httpClient = net.HTTPClient()\r\n QuickApp:error(self.exchServiceMessage)\r\n end\r\n })\r\n return true\r\nend"}]}