Prosty, szybki kalkulator kosztów energii elektrycznej działający w całości w przeglądarce.
Bez backendu, bez kont, bez wysyłania danych — wszystko trzymane lokalnie w localStorage.
Strona nie pobiera niczego z zewnątrz (także czcionek: używa Inter, jeśli jest zainstalowany
w systemie, a w przeciwnym razie czcionki systemowej).
Energy consumption cost calculator (Polish UI). Pure HTML/CSS/JS, no build step, no backend.
- Urządzenia z harmonogramem pracy — moc (W), ilość sztuk, do 6 przedziałów godzin na urządzenie z wyborem dni tygodnia.
- Trzy typy taryf:
- Stała — jedna cena za kWh,
- Zmienna (strefy) — strefy godzinowe wspólne dla całego tygodnia,
- ZmiennaX (zaawansowana) — osobne strefy dla każdego dnia tygodnia, z edycją zbiorczą.
- Przełączanie typu w oknie taryfy nie kasuje wartości pozostałych typów — wpisane lub wcześniej zapisane strefy i cena wracają po powrocie do danego typu.
- Podsumowanie kosztów — dziennie / miesięcznie / rocznie (kWh i zł), w polskim zapisie liczb (przecinek dziesiętny, grupowanie cyfr: „12 345,50 zł”), z opisem używanej taryfy.
- Wybór okresu rozliczeniowego (daty od 2000-01-01 do 2100-12-31):
- standardowe 30 dni,
- konkretny miesiąc kalendarzowy (dokładna liczba dni i układ dni tygodnia),
- niestandardowy zakres dat (np. 1 lipca – 15 września) — z wyliczoną liczbą dni, przybliżeniem w miesiącach/latach oraz średnim kosztem miesiąca 30-dniowego.
- Wybrany okres jest zapamiętywany między sesjami, a każdy profil quick save trzyma własny okres.
- Quick save — 3 sloty profili pod menu górnym. Każdy slot zapisuje pełną konfigurację
(taryfa + urządzenia + wybrany okres). Kliknięcie przełącza profil
bez eksportu/importu JSON:
- klik w pusty slot → zapisuje bieżące wyliczenie,
- klik w zapisany slot → przełącza na ten profil; aktywny profil jest automatycznie dopisywany na bieżąco, a jeśli bieżące wyliczenie nie jest zapisane w żadnym profilu (np. po imporcie, „Przywróć domyślne” albo usunięciu aktywnego profilu), kalkulator najpierw pyta: zapisać je w wolnym slocie, przełączyć bez zapisu czy anulować,
- przycisk
✎obok zapisanego slotu (albo dwuklik na slocie) → zmiana nazwy profilu (do 24 znaków),✕obok slotu → usunięcie profilu (po potwierdzeniu), - „Zapisano:” w podpowiedzi slotu to czas ostatniej zmiany profilu.
- Eksport / import JSON — kopia zapasowa lub przeniesienie konfiguracji (taryfa, urządzenia i wybrany okres; profile nie są eksportowane).
- Wydruk / PDF — osobny arkusz stylów do druku: nagłówek z datą wydruku, profilem, okresem i taryfą (wszystkie strefy i ceny), urządzenia w układzie jak na komputerze, nigdy dzielone między strony.
- Walidacja — kolizje godzin, luki w pokryciu doby, brakujące lub błędne ceny, puste godziny; komunikat pojawia się przy polu, wierszu albo liście, której dotyczy (po opuszczeniu pola lub przy próbie zapisu) i znika od razu po poprawieniu.
- Obsługa z klawiatury: Tab / Shift+Tab przechodzą przez wszystkie elementy (dni tygodnia, miesiące, sloty profili, zakładki dni taryfy ZmiennaX — te ostatnie strzałkami, Home i End), spacja zaznacza dzień, Enter uruchamia element z fokusem, a w polu tekstowym zatwierdza formularz tego pola (np. „Zapisz”, „Zastosuj zakres”). Enter nigdy nie potwierdza usunięcia ani przywrócenia ustawień „z daleka” — w oknach potwierdzenia fokus startuje na „Anuluj”.
- Okna dialogowe: fokus wchodzi do okna i w nim zostaje, Esc albo kliknięcie obok anulują (nigdy nie zatwierdzają), po zamknięciu fokus wraca na przycisk, który okno otworzył. Strona pod oknem jest nieaktywna i się nie przewija.
- Czytniki ekranu: pola mają etykiety, przyciski z ikonami nazwy, okna rolę i tytuł, zaznaczenia są odczytywane, nowe komunikaty walidacji są czytane na głos.
- Wygląd: kontrast tekstu i krawędzi pól zgodny z WCAG AA, zaznaczenia widoczne w trybie wysokiego kontrastu Windows, bez animacji przy ustawieniu „ogranicz ruch”, układ bez przewijania w bok od 320 px szerokości (powiększenie 400 %), okna przewijane w całości na niskich ekranach (telefon poziomo, powiększenie 200 %), na ekranach dotykowych elementy co najmniej 44 × 44 px.
Jeden zestaw zasad (js/rules.js) sprawdza formularze, import pliku, dane odczytywane z
przeglądarki i dane wejściowe silnika obliczeń — limity są wszędzie te same:
| wartość | dozwolone |
|---|---|
| cena (taryfa stała i każda strefa) | 0–1000 zł/kWh |
| moc urządzenia | 0–1 000 000 W |
| ilość sztuk | liczba całkowita 1–10 000 |
| nazwa urządzenia / profilu | 1–120 / 1–24 znaków (emoji nie jest dzielone) |
| przedziały godzin urządzenia | 1–6; każdy z co najmniej jednym dniem, dłuższy niż 0 h, bez nakładania się z innym przedziałem tego urządzenia w tym samym dniu |
| strefy taryfy (na dzień w ZmiennaX) | 1–8; razem pokrywają całą dobę dokładnie raz, każda z ceną, żadna o długości 0 h |
| urządzenia | do 500 |
| daty okresu | 2000-01-01 – 2100-12-31 (prawdziwe daty, np. 2026-02-30 jest odrzucane) |
Godziny w zapisie GG:MM; puste lub niedokończone pole godziny jest błędem (nigdy nie jest
liczone jako północ). Liczby w zwykłym zapisie dziesiętnym z kropką lub przecinkiem („1,5”);
zapis wykładniczy („1e3”), szesnastkowy („0x10”) itp. jest odrzucany z komunikatem.
Formularz nie zapisze wartości, która łamie zasadę. Dane odczytywane z przeglądarki i pliki importu są czytane w ten sam sposób: część, której nie da się użyć, jest pomijana, drobne usterki są poprawiane, a strona wymienia każdą zmianę (zob. „Dane w przeglądarce” i „Import”).
- Kalkulator używa tylko kluczy
kwh_*wlocalStorage. „Przywróć domyślne” usuwa wyłącznie bieżące dane (taryfę, urządzenia, okres, wskazanie aktywnego profilu) — zapisane profile zostają, a dane innych stron z tej samej domeny (np. GitHub Pages) nie są ruszane. - Przy starcie zapisane dane są sprawdzane tymi samymi zasadami. Dane z wcześniejszej wersji
albo uszkodzone są poprawiane: element, którego nie da się policzyć, jest pomijany
(przedział godzin, urządzenie; taryfa → taryfa domyślna, okres → standardowe 30 dni),
a strona pokazuje komunikat z listą zmian. Kopia pierwotnych wartości zostaje pod kluczem
kwh_backup. - Jeśli pamięć przeglądarki jest zablokowana albo pełna, kalkulator działa dalej w pamięci karty i mówi o tym komunikatem (zmiany znikną po zamknięciu karty — można je zachować eksportem).
- Dwie otwarte karty: karta odświeża widok, gdy druga zmieni dane (a otwarte okno z nieaktualnymi danymi zamyka); zmiana wprowadzona w karcie z nieaktualnym widokiem nie jest zapisywana — widok jest odświeżany i trzeba ją powtórzyć. Dzięki temu dane jednego profilu nie trafiają do innego.
- Każdy eksport kalkulatora się wczytuje — z wersji 1.0.0, 1.0.1 i 1.1.0. Plik jest czytany dokładnie tak jak dane zapisane w przeglądarce (te same zasady i ten sam kod): to, czego nie da się użyć, jest pomijane — przedział godzin (pusta lub niedokończona godzina, 0 h, brak dni), urządzenie (np. moc lub ilość spoza zakresu, ułamek sztuk, tekst czy znaczniki HTML zamiast liczby), taryfa (np. cena spoza zakresu, luka w strefach) albo okres (data spoza 2000–2100) — a drobne poprawki są wprowadzane automatycznie (nazwa dłuższa niż 120 znaków, nieznane lub powtórzone nazwy dni, nieprawidłowe wartości nieaktywnego typu taryfy, ponad 6 przedziałów albo ponad 500 urządzeń). Nakładające się przedziały zostają i liczą się raz.
- Po imporcie komunikat u góry strony wymienia każdą zmianę, z nazwą urządzenia lub strefy i dozwolonym zakresem (tak samo jak przy danych poprawianych przy starcie). Bez komunikatu są tylko obcinane spacje wokół nazwy i nowy identyfikator dla urządzenia z powtórzonym albo nieprawidłowym (inaczej edycja jednego urządzenia zmieniałaby drugie).
- Pominięta część pliku zostawia bieżącą: taryfa z pliku z ceną spoza zakresu nie zmienia bieżącej taryfy, a lista z samymi błędnymi urządzeniami nie usuwa bieżących. Plik tylko z taryfą albo tylko z urządzeniami zmienia tylko tę część; plik bez okresu (eksport z wersji 1.0.x) zostawia bieżący okres.
- Odrzucany — bez żadnej zmiany — jest tylko plik, który nie jest eksportem kalkulatora (np. inny plik JSON), albo taki, w którym nie zostało nic poprawnego; komunikat podaje powody.
- Import nie nadpisuje zapisanego profilu (po imporcie żaden profil nie jest aktywny).
- Każde urządzenie ma wyliczone zużycie (kWh) i koszt dla każdego dnia tygodnia. Wynik okresu to suma tych wartości razy liczba poszczególnych dni tygodnia w okresie — liczba dni i dni tygodnia pochodzą z arytmetyki kalendarza (bez strefy czasowej przeglądarki), więc odświeżenie trwa tyle samo dla 30 dni co dla 100 lat.
- „Miesięcznie (30 dni)” to umowny miesiąc standardowy: 30 × średnia dzienna z tygodnia. Wybór konkretnego miesiąca lub zakresu dat liczy dokładnie, z rzeczywistą liczbą każdego dnia tygodnia. Przy zakresie dat „śr. miesięcznie (30 dni)” to 30 × średnia dzienna zakresu (np. dla stycznia: okres 31 dni, a średnia na 30 dni).
- „Rocznie (rok)” to rok kalendarzowy podany w nawiasie: bieżący (dla 30 dni), rok wybranego miesiąca albo rok, w którym zaczyna się zakres dat (zakres 2026-12-25 – 2027-01-05 → 2026).
- Strefy taryfowe i harmonogramy mogą przechodzić przez północ (np. 22:00–06:00). Godzina końcowa 00:00 oznacza 24:00; 00:00–00:00 to cała doba.
- Uproszczenie: przedział godzin przechodzący przez północ jest w całości przypisywany do dnia,
w którym się zaczyna, razem ze strefami taryfowymi tego dnia (Pt 22:00–06:00 = 8 h w piątek).
Skutki:
- przy taryfie ZmiennaX koszt nocnej końcówki liczony jest po stawkach dnia startu (np. Pt 22:00–06:00 przy tańszej sobocie: 8,00 zł/tydzień zamiast 5,00 zł przy cenach 1,00 / 0,50 zł/kWh);
- na granicach miesiąca lub zakresu: przedział zaczynający się w ostatnim dniu okresu liczy się w całości (także godziny po jego końcu), a końcówka przedziału z dnia przed początkiem okresu nie liczy się wcale. Sumy energii są dokładne, gdy taki przedział działa codziennie (oba skutki się znoszą) albo okres to pełne tygodnie; np. tylko soboty 22:00–06:00 w październiku 2026 (zaczyna się w czwartek, kończy w sobotę), 1 kW: kalkulator liczy 40 kWh, naprawdę w tym miesiącu urządzenie zużyje 34 kWh (35 kWh z godziną zmiany czasu 25 października);
- kontrola kolizji używa tego samego modelu: Pon 22:00–06:00 i Pon 03:00–04:00 są zgłaszane jako kolizja (choć naprawdę nie działają jednocześnie), a Pon 22:00–06:00 i Wt 05:00–07:00 są przyjmowane, choć naprawdę nakładają się we wtorek 05:00–06:00 — ta godzina liczy się wtedy dwa razy.
- Przedziały jednego urządzenia nakładające się w tym samym dniu (formularz na to nie pozwala, ale mogą przyjść z pliku albo z danych starszej wersji) liczą się raz: urządzenie albo pracuje, albo nie.
- Każda doba ma 24 godziny — zmiana czasu nie jest modelowana: w dniu zmiany na czas letni liczona jest godzina, której nie ma, a w dniu zmiany na zimowy powtórzona godzina liczy się raz (urządzenie pracujące całą dobę: marzec 2026 — 744 kWh zamiast 743, październik — 744 zamiast 745). Liczba dni i dni tygodnia są zawsze dokładne, w każdej strefie czasowej.
- Kwoty i kWh są pokazywane w zaokrągleniu do 0,01 (połówki w górę: 0,115 → 0,12), ale sumowane bez zaokrągleń — suma zaokrąglonych wartości urządzeń może różnić się od zaokrąglonej sumy o grosz.
Aplikacja jest w pełni statyczna. Ze względu na moduły ES (type="module") wymaga
serwera HTTP (nie zadziała z file://):
# dowolny serwer statyczny, np.:
npx serve .
# lub
php -S localhost:8080
# lub po prostu wrzuć katalog do htdocs/www (Apache, nginx, WAMP/XAMPP)Następnie otwórz http://localhost:8080 (lub odpowiedni adres).
Działa też od ręki na GitHub Pages — Settings → Pages → Deploy from branch → main / root.
Strona ma ścisłą politykę Content-Security-Policy w <meta> (tylko własne skrypty, style
i obrazy, bez kodu i stylów inline). Ochrony przed osadzaniem w ramce (frame-ancestors) nie da
się ustawić w <meta> — jeśli jest potrzebna, serwer musi wysłać ją w nagłówku.
Testy silnika, zasad, importu, formatowania liczb i zabezpieczeń zapisu — bez zależności, wbudowany runner Node.js (wersja 22 lub nowsza):
node --test tests/
TZ=Europe/Warsaw node --test tests/ # wyniki nie mogą zależeć od strefy czasowejGitHub Actions (.github/workflows/ci.yml) uruchamia je przy każdym pushu do main, tagu v*
i pull requeście — w Node 22 i 24, w strefach UTC i Europe/Warsaw. Nieudane testy są widoczne
jako adnotacje na stronie przebiegu.
index.html # struktura UI + okna dialogowe
css/style.css # style (dark mode), wysoki kontrast, ograniczony ruch, arkusz wydruku
js/app.js # start aplikacji, taryfy, okresy, eksport/import
js/calculator.js # silnik obliczeń (strefy, harmonogramy, liczba dni tygodnia w okresie)
js/rules.js # wspólne zasady: limity, format liczb i godzin, strefy, harmonogramy, import
js/calendar.js # arytmetyka kalendarza (dni, dni tygodnia, lata przestępne)
js/format.js # liczby, kwoty, daty i długość okresu po polsku
js/ui.js # lista urządzeń, okna taryfy i urządzenia, komunikaty walidacji
js/dialog.js # okna dialogowe: fokus, klawiatura (Tab, Esc, Enter), blokada przewijania
js/slots.js # quick save — 3 sloty profili
js/state.js # dane w localStorage, sprawdzane przy odczycie
js/storage.js # dostęp do localStorage odporny na blokadę i brak miejsca
js/notice.js # komunikaty u góry strony
tests/ # testy (node --test tests/)
Vanilla JavaScript (ES Modules), HTML5, CSS3. Zero zależności runtime.
MIT © TryHackX — tryhackx.org