Skip to content

Commit

Permalink
add battery icon
Browse files Browse the repository at this point in the history
  • Loading branch information
ugomeda committed Feb 7, 2025
1 parent 0e413b3 commit 36e01e1
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 32 deletions.
3 changes: 1 addition & 2 deletions inkplate_dashboard/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,4 @@
with open("config.toml", "rb") as fd:
config = parse_config(fd)


app.display = config.display # type: ignore[attr-defined]
app.state.display = config.display
8 changes: 6 additions & 2 deletions inkplate_dashboard/chrome.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,12 @@ def _convert_image(screenshot: bytes) -> tuple[bytes, str]:
class ChromiumInstance:
browser: Browser

async def screenshot(self) -> tuple[bytes, str]:
page = await self.browser.new_page(viewport={"width": 825, "height": 1200})
async def screenshot(
self, extra_headers: dict[str, str] | None = None
) -> tuple[bytes, str]:
page = await self.browser.new_page(
viewport={"width": 825, "height": 1200}, extra_http_headers=extra_headers
)
try:
await page.goto("http://127.0.0.1:8000/live/html", timeout=5000)
screenshot = await page.screenshot()
Expand Down
2 changes: 2 additions & 0 deletions inkplate_dashboard/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
"Chrome/129.0.0.0 "
"Safari/537.36"
)

BATTERY_HEADER = "X-Inkplate-Battery-Voltage"
16 changes: 15 additions & 1 deletion inkplate_dashboard/display.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ class EpaperContext:
weather: list[WeatherObservation]
headlines: list[HeadlineEntry]
date: str
battery_icon: str


def generate_html(display: DisplayConfiguration) -> str:
def generate_html(display: DisplayConfiguration, voltage: float | None) -> str:
# Load RSS and weather
headlines = get_headlines(str(display.rss_url))
weather = get_weather(display)
Expand All @@ -26,6 +27,18 @@ def generate_html(display: DisplayConfiguration) -> str:
now = datetime.now(tz=get_timezone(display.timezone))
date_str = format_date(now, format="full", locale=display.locale)

# Get battery level
battery_icon = "battery-warning"
if voltage is not None:
if voltage >= 4.2:
battery_icon = "battery-full"
elif voltage >= 4.0:
battery_icon = "battery-medium"
elif voltage >= 3.8:
battery_icon = "battery-low"
else:
battery_icon = "battery"

# Generate HTML
env = Environment(
loader=PackageLoader("inkplate_dashboard", "templates"),
Expand All @@ -39,5 +52,6 @@ def generate_html(display: DisplayConfiguration) -> str:
weather=weather,
headlines=headlines,
date=date_str,
battery_icon=battery_icon,
)
)
17 changes: 0 additions & 17 deletions inkplate_dashboard/state.py

This file was deleted.

21 changes: 17 additions & 4 deletions inkplate_dashboard/styles/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,32 @@ html, body {
font-family: $font-family-default;
}

h1 {
.widget-header {
height: 100px;
padding: $spacing;
margin: 0;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;

color: #fff;
background-color: #000;

font-family: $font-family-serif;
font-size: $font-size-huge;
font-weight: 700;
line-height: 100px - 2*$spacing;

color: #fff;
background-color: #000;
svg {
width: 24px;
height: 24px;
transform: scale(1.5); /* Sizing does not work, see https://github.com/lucide-icons/lucide/issues/2768 */
stroke: currentColor;
fill: none;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
}
}

.widget-headlines {
Expand Down
5 changes: 4 additions & 1 deletion inkplate_dashboard/templates/display.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
</head>
<body>
<div id="display">
<h1>{{ data.date }}</h1>
<div class="widget-header">
<div>{{ data.date }}</div>
<svg xmlns="http://www.w3.org/2000/svg"><use href="/static/icons/lucide/sprite.svg#{{ data.battery_icon }}" /></svg>
</div>
{{ widget_weather(data.weather) }}
{{ widget_headlines(data.headlines) }}
</div>
Expand Down
28 changes: 28 additions & 0 deletions inkplate_dashboard/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from typing import cast

from starlette.requests import Request

from inkplate_dashboard.chrome import ChromiumInstance
from inkplate_dashboard.config import DisplayConfiguration
from inkplate_dashboard.constants import BATTERY_HEADER


def get_chromium(request: Request) -> ChromiumInstance:
return cast(ChromiumInstance, request.state.chromium)


def get_display(request: Request) -> DisplayConfiguration:
return cast(DisplayConfiguration, request.app.state.display)


def get_voltage(request: Request) -> float | None:
voltage = request.headers.get(BATTERY_HEADER)
if voltage is None:
return None
if not voltage.endswith("V"):
return None

try:
return float(voltage[:-1])
except ValueError:
return None
22 changes: 17 additions & 5 deletions inkplate_dashboard/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@
from starlette.requests import Request
from starlette.responses import HTMLResponse, PlainTextResponse, Response

from inkplate_dashboard.constants import BATTERY_HEADER
from inkplate_dashboard.display import generate_html
from inkplate_dashboard.state import get_state
from inkplate_dashboard.utils import (
get_chromium,
get_display,
get_voltage,
)


def compile_styles() -> str:
Expand All @@ -26,17 +31,24 @@ async def get(self, request: Request) -> PlainTextResponse:
class DisplayHtmlEndpoint(HTTPEndpoint):
async def get(self, request: Request) -> HTMLResponse:
return HTMLResponse(
await run_in_threadpool(generate_html, get_state(request).display)
await run_in_threadpool(
generate_html, get_display(request), get_voltage(request)
)
)


class DisplayPngEndpoint(HTTPEndpoint):
async def get(self, request: Request) -> Response:
state = get_state(request)
image, etag = await state.chromium.screenshot()
extra_headers = {}
if BATTERY_HEADER in request.headers:
extra_headers[BATTERY_HEADER] = request.headers[BATTERY_HEADER]

image, etag = await get_chromium(request).screenshot(
extra_headers=extra_headers
)
headers = {
"etag": etag,
"Cache-Control": f"max-age={state.display.refresh_interval_sec}",
"Cache-Control": f"max-age={get_display(request).refresh_interval_sec}",
}

# Tell the display to avoid a refresh if the image did not change
Expand Down

0 comments on commit 36e01e1

Please sign in to comment.