Widget with today's and tomorrow's prices
This commit is contained in:
		
							parent
							
								
									9dc3378337
								
							
						
					
					
						commit
						aeb17d6969
					
				
					 3 changed files with 181 additions and 3 deletions
				
			
		
							
								
								
									
										153
									
								
								calculator/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								calculator/index.html
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,153 @@
 | 
			
		|||
<!doctype html>
 | 
			
		||||
<html>
 | 
			
		||||
<head>
 | 
			
		||||
  <meta charset="UTF-8">
 | 
			
		||||
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
  <script src="https://cdn.tailwindcss.com"></script>
 | 
			
		||||
 | 
			
		||||
  <script>
 | 
			
		||||
    function formatDate(date) {
 | 
			
		||||
      const year = date.getFullYear();
 | 
			
		||||
      const month = String(date.getMonth() + 1).padStart(2, '0');
 | 
			
		||||
      const day = String(date.getDate()).padStart(2, '0');
 | 
			
		||||
      return `${year}-${month}-${day}`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function loadData(date, today) {
 | 
			
		||||
      let prefix = today ? 'today' : 'tomorrow';
 | 
			
		||||
 | 
			
		||||
      if(prefix == 'today') {
 | 
			
		||||
        document.getElementById(prefix).innerText = "Dnes ("+date+")";
 | 
			
		||||
      } else {
 | 
			
		||||
        document.getElementById(prefix).innerText = "Zítra ("+date+")";
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      fetch('/price/day/'+date+'?num_cheapest_hours=8')
 | 
			
		||||
        .then(response => response.json())
 | 
			
		||||
        .then(data => {
 | 
			
		||||
          if(data.detail == "prices not found") {
 | 
			
		||||
            for (let i = 0; i < 24; i++) {
 | 
			
		||||
              document.getElementById(prefix + i).innerText = "-";
 | 
			
		||||
            }
 | 
			
		||||
            return;
 | 
			
		||||
          }
 | 
			
		||||
            
 | 
			
		||||
          for (let i = 0; i < 24; i++) {
 | 
			
		||||
            if(data.cheapest_hours.hours.includes(i)) {
 | 
			
		||||
                document.getElementById(prefix + i).className = "px-1 bg-green-500";
 | 
			
		||||
            } else if (data.most_expensive_hours.hours.includes(i)) {
 | 
			
		||||
                document.getElementById(prefix + i).className = "px-1 bg-rose-400";
 | 
			
		||||
            } else {
 | 
			
		||||
                document.getElementById(prefix + i).className = "px-1 bg-amber-100";
 | 
			
		||||
            }
 | 
			
		||||
            document.getElementById(prefix + i).innerText = Math.round(data.total.hours[i]*100)/100;
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function loadAllData() {
 | 
			
		||||
      const currentDate = new Date();
 | 
			
		||||
      const tomorrowDate = new Date(currentDate);
 | 
			
		||||
      tomorrowDate.setDate(currentDate.getDate() + 1);
 | 
			
		||||
 | 
			
		||||
      const currentDateString = formatDate(currentDate);
 | 
			
		||||
      const tomorrowDateString = formatDate(tomorrowDate);
 | 
			
		||||
 | 
			
		||||
      loadData(currentDateString, true);
 | 
			
		||||
      loadData(tomorrowDateString, false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    window.onload = function() {
 | 
			
		||||
      loadAllData();
 | 
			
		||||
      // Reload data every hour (3600000 milliseconds)
 | 
			
		||||
      setInterval(loadAllData, 3600000);
 | 
			
		||||
    };
 | 
			
		||||
  </script>
 | 
			
		||||
</head>
 | 
			
		||||
<body class="bg-neutral-800">
 | 
			
		||||
    <table class="table border-collapse border border-slate-500 text-center w-full">
 | 
			
		||||
        <tr class="bg-neutral-400">
 | 
			
		||||
            <th class="px-1"></th>
 | 
			
		||||
            <th class="px-1">00</th>
 | 
			
		||||
            <th class="px-1">01</th>
 | 
			
		||||
            <th class="px-1">02</th>
 | 
			
		||||
            <th class="px-1">03</th>
 | 
			
		||||
            <th class="px-1">04</th>
 | 
			
		||||
            <th class="px-1">05</th>
 | 
			
		||||
            <th class="px-1">06</th>
 | 
			
		||||
            <th class="px-1">07</th>
 | 
			
		||||
            <th class="px-1">08</th>
 | 
			
		||||
            <th class="px-1">09</th>
 | 
			
		||||
            <th class="px-1">10</th>
 | 
			
		||||
            <th class="px-1">11</th>
 | 
			
		||||
            <th class="px-1">12</th>
 | 
			
		||||
            <th class="px-1">13</th>
 | 
			
		||||
            <th class="px-1">14</th>
 | 
			
		||||
            <th class="px-1">15</th>
 | 
			
		||||
            <th class="px-1">16</th>
 | 
			
		||||
            <th class="px-1">17</th>
 | 
			
		||||
            <th class="px-1">18</th>
 | 
			
		||||
            <th class="px-1">19</th>
 | 
			
		||||
            <th class="px-1">20</th>
 | 
			
		||||
            <th class="px-1">21</th>
 | 
			
		||||
            <th class="px-1">22</th>
 | 
			
		||||
            <th class="px-1">23</th>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
            <td id="today" class="px-1 bg-neutral-200 w-16">Dnes</td>
 | 
			
		||||
            <td id="today0" class="px-1">-</td>
 | 
			
		||||
            <td id="today1" class="px-1">-</td>
 | 
			
		||||
            <td id="today2" class="px-1">-</td>
 | 
			
		||||
            <td id="today3" class="px-1">-</td>
 | 
			
		||||
            <td id="today4" class="px-1">-</td>
 | 
			
		||||
            <td id="today5" class="px-1">-</td>
 | 
			
		||||
            <td id="today6" class="px-1">-</td>
 | 
			
		||||
            <td id="today7" class="px-1">-</td>
 | 
			
		||||
            <td id="today8" class="px-1">-</td>
 | 
			
		||||
            <td id="today9" class="px-1">-</td>
 | 
			
		||||
            <td id="today10" class="px-1">-</td>
 | 
			
		||||
            <td id="today11" class="px-1">-</td>
 | 
			
		||||
            <td id="today12" class="px-1">-</td>
 | 
			
		||||
            <td id="today13" class="px-1">-</td>
 | 
			
		||||
            <td id="today14" class="px-1">-</td>
 | 
			
		||||
            <td id="today15" class="px-1">-</td>
 | 
			
		||||
            <td id="today16" class="px-1">-</td>
 | 
			
		||||
            <td id="today17" class="px-1">-</td>
 | 
			
		||||
            <td id="today18" class="px-1">-</td>
 | 
			
		||||
            <td id="today19" class="px-1">-</td>
 | 
			
		||||
            <td id="today20" class="px-1">-</td>
 | 
			
		||||
            <td id="today21" class="px-1">-</td>
 | 
			
		||||
            <td id="today22" class="px-1">-</td>
 | 
			
		||||
            <td id="today23" class="px-1">-</td>
 | 
			
		||||
        </tr>
 | 
			
		||||
        <tr>
 | 
			
		||||
            <td id="tomorrow" class="px-1 bg-neutral-200 w-16">Zítra</td>
 | 
			
		||||
            <td id="tomorrow0" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow1" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow2" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow3" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow4" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow5" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow6" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow7" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow8" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow9" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow10" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow11" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow12" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow13" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow14" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow15" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow16" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow17" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow18" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow19" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow20" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow21" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow22" class="px-1">-</td>
 | 
			
		||||
            <td id="tomorrow23" class="px-1">-</td>
 | 
			
		||||
        </tr>
 | 
			
		||||
    </table>
 | 
			
		||||
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
| 
						 | 
				
			
			@ -3,10 +3,11 @@ import calendar
 | 
			
		|||
from typing import List, Optional
 | 
			
		||||
 | 
			
		||||
from fastapi import FastAPI, HTTPException
 | 
			
		||||
from fastapi.responses import RedirectResponse
 | 
			
		||||
from fastapi.responses import RedirectResponse, HTMLResponse
 | 
			
		||||
from fastapi.middleware.cors import CORSMiddleware
 | 
			
		||||
 | 
			
		||||
from calculator.miner import PriceNotFound, get_energy_prices, get_eur_czk_ratio
 | 
			
		||||
from calculator.schema import CheapestHours, DayPrice, Price
 | 
			
		||||
from calculator.schema import CheapestHours, DayPrice, MostExpensiveHours, Price
 | 
			
		||||
 | 
			
		||||
from .consts import VAT
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +15,13 @@ def days_in_month(year: int, month: int) -> int:
 | 
			
		|||
    return calendar.monthrange(year, month)[1]
 | 
			
		||||
 | 
			
		||||
app = FastAPI(title="Spot market home calculator", version="0.1", description="Calculate your energy costs based on spot market prices")
 | 
			
		||||
app.add_middleware(
 | 
			
		||||
    CORSMiddleware,
 | 
			
		||||
    allow_origins=["*"],
 | 
			
		||||
    allow_credentials=True,
 | 
			
		||||
    allow_methods=["*"],
 | 
			
		||||
    allow_headers=["*"],
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@app.get("/", description="Redirect to /docs")
 | 
			
		||||
def docs():
 | 
			
		||||
| 
						 | 
				
			
			@ -31,6 +39,7 @@ Return spot prices for the whole day with all fees included.<br>
 | 
			
		|||
**kwh_fees_high** - additional fees per kWh in high tariff, usually distribution + other fees, default is 1.83474 (D57d, BezDodavatele)<br>
 | 
			
		||||
**sell_fees** - selling energy fees, default is 0.45 (BezDodavatele)<br>
 | 
			
		||||
**num_cheapest_hours** - number of cheapest hours to return, default is 8, use this to plan your consumption<br>
 | 
			
		||||
**num_most_expensive_hours** - number of the most expensive hours to return, default is 8, use this to plan your consumption<br>
 | 
			
		||||
**low_tariff_hours** - list of low tariff hours, default is 0,1,2,3,4,5,6,7,9,10,11,13,14,16,17,18,20,21,22,23 (D57d, ČEZ)<br>
 | 
			
		||||
<br>
 | 
			
		||||
Output:<br>
 | 
			
		||||
| 
						 | 
				
			
			@ -80,6 +89,7 @@ def read_item(
 | 
			
		|||
    low_tariff_hours:str="0,1,2,3,4,5,6,7,9,10,11,13,14,16,17,18,20,21,22,23", 
 | 
			
		||||
    no_cache:bool = False, 
 | 
			
		||||
    num_cheapest_hours:int = 8,
 | 
			
		||||
    num_most_expensive_hours:int = 8,
 | 
			
		||||
    ) -> DayPrice:
 | 
			
		||||
    
 | 
			
		||||
    if not date:
 | 
			
		||||
| 
						 | 
				
			
			@ -115,6 +125,7 @@ def read_item(
 | 
			
		|||
    spot_for_sell = Price(hours=spot_hours_for_sell, now=spot_hours_for_sell[str(hour)] if is_today else None)
 | 
			
		||||
 | 
			
		||||
    cheapest_hours = [int(k) for k, v in list(spot_hours_total_sorted.items())[0:num_cheapest_hours]]
 | 
			
		||||
    most_expensive_hours = [int(k) for k, v in list(reversed(spot_hours_total_sorted.items()))[0:num_most_expensive_hours]]
 | 
			
		||||
 | 
			
		||||
    data = DayPrice(
 | 
			
		||||
        monthly_fees=monthly_fees * VAT,
 | 
			
		||||
| 
						 | 
				
			
			@ -128,6 +139,13 @@ def read_item(
 | 
			
		|||
        spot=spot,
 | 
			
		||||
        total=spot_total,
 | 
			
		||||
        sell=spot_for_sell,
 | 
			
		||||
        cheapest_hours=CheapestHours(hours=cheapest_hours, is_cheapest=hour in cheapest_hours if is_today else None)
 | 
			
		||||
        cheapest_hours=CheapestHours(hours=cheapest_hours, is_cheapest=hour in cheapest_hours if is_today else None),
 | 
			
		||||
        most_expensive_hours=MostExpensiveHours(hours=most_expensive_hours, is_the_most_expensive=hour in most_expensive_hours if is_today else None),
 | 
			
		||||
    )
 | 
			
		||||
    return data
 | 
			
		||||
 | 
			
		||||
@app.get("/widget", response_class=HTMLResponse)
 | 
			
		||||
def get_widget():
 | 
			
		||||
    with open("calculator/index.html", "r") as file:
 | 
			
		||||
        html_content = file.read()
 | 
			
		||||
    return HTMLResponse(content=html_content)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,6 +14,12 @@ class CheapestHours:
 | 
			
		|||
    is_cheapest: Optional[bool] = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class MostExpensiveHours:
 | 
			
		||||
    hours: List[int]
 | 
			
		||||
    is_the_most_expensive: Optional[bool] = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class DayPrice:
 | 
			
		||||
    monthly_fees: float
 | 
			
		||||
| 
						 | 
				
			
			@ -28,6 +34,7 @@ class DayPrice:
 | 
			
		|||
    total: Price
 | 
			
		||||
    sell: Price
 | 
			
		||||
    cheapest_hours: CheapestHours
 | 
			
		||||
    most_expensive_hours: MostExpensiveHours
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue