Skip to content

Commit b56965c

Browse files
authored
Display timestamp of last update (#3)
* Add update timestamp * Bump version * Update README * poetry update * black formatting * Change timestamp formatting * Low battery on display
1 parent ffc8eb0 commit b56965c

File tree

7 files changed

+53
-34
lines changed

7 files changed

+53
-34
lines changed

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,21 @@ IMAGE_WIDTH | No | 1200 | Width of image to be generated for display
6262
IMAGE_HEIGHT | No | 825 | Height of image to be generated for display
6363
ROTATE_ANGLE | No | 0 | If image is rendered in portrait orientation, angle to rotate to fit screen
6464

65-
## Limitations
66-
67-
* Recurring events in the ICS calendar feed are currently not supported
68-
6965
## Development
7066

7167
Run
7268

69+
```shell
70+
poetry run python src/main.py
71+
```
72+
73+
or
74+
7375
```shell
7476
docker compose -f docker-compose.dev.yml up --build
7577
```
7678

77-
locally to build a new image and start it, API docs will be served at <http://localhost:5000/docs>.
79+
locally to start the application, API docs will be served at <http://localhost:5000/docs>.
7880

7981
## Acknowledgements
8082

inkplate/inkplate.ino

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
/*
22
Adapted from the Inkplate10_Image_Frame_From_Web example for Soldered Inkplate 10
3-
https://github.com/SolderedElectronics/Inkplate-Arduino-library/blob/2dd263f2f9548aadac8638413a143beddf068a64/examples/Inkplate10/Projects/Inkplate10_Image_Frame_From_Web/Inkplate10_Image_Frame_From_Web.ino
3+
https://github.com/SolderedElectronics/Inkplate-Arduino-library/blob/2dd263f2f9548aadac8638413a143beddf068a64/examples/Inkplate10/Projects/Inkplate10_Image_Frame_From_Web/Inkplate10_Image_Frame_From_Web.ino and
4+
https://github.com/SolderedElectronics/Inkplate-Arduino-library/blob/2dd263f2f9548aadac8638413a143beddf068a64/examples/Inkplate10/Advanced/Other/Inkplate10_Read_Battery_Voltage/Inkplate10_Read_Battery_Voltage.ino
45
56
What this code does:
67
1. Connect to a WiFi access point
78
2. Retrieve an image from a web address
89
3. Display the image on the Inkplate 10 device
9-
4. (Optional) Check the battery level on the Inkplate device
10+
4. Check the battery level on the Inkplate device
1011
5. Set a sleep timer for 60 minutes, and allow the Inkplate to go into deep sleep to conserve battery
1112
*/
1213

@@ -23,10 +24,9 @@ const char ssid[] = "YOUR WIFI SSID"; // Your WiFi SSID
2324
const char *password = "YOUR WIFI PASSWORD"; // Your WiFi password
2425
const char *imgurl = "http://url.to.your.server/image"; // Your dashboard image web address
2526

26-
// Battery values
27-
#define BATTV_MAX 4.1 // maximum voltage of battery
28-
#define BATTV_MIN 3.2 // what we regard as an empty battery
29-
#define BATTV_LOW 3.4 // voltage considered to be low battery
27+
#define BATTV_MAX 4.1 // maximum voltage of battery
28+
#define BATTV_MIN 3.2 // what we regard as an empty battery
29+
#define BATTV_LOW 3.4 // voltage considered to be low battery
3030

3131
void setup()
3232
{
@@ -36,25 +36,29 @@ void setup()
3636
// Join wifi
3737
display.connectWiFi(ssid, password);
3838

39+
// Display image from API
3940
if (!display.drawImage(imgurl, display.PNG, 0, 0))
4041
{
4142
// If is something failed (wrong filename or format), write error message on the screen.
4243
display.println("Image open error");
4344
}
44-
display.display();
4545

4646
double battvoltage = display.readBattery();
4747
int battpc = calc_battery_percentage(battvoltage);
4848
if (battvoltage < BATTV_LOW) {
49-
char msg [100];
50-
sprintf (msg, "Inkplate battery at %d%%, voltage at %.2fV", battpc, battvoltage);
51-
Serial.println(msg);
49+
char msg [20];
50+
sprintf (msg, "Battery: %d%%", battpc);
51+
display.setCursor(1100, 800); // Inkplate 10 has a 9.7 inch, 1,200 x 825 pixel display
52+
display.setTextSize(2);
53+
display.setTextColor(BLACK);
54+
display.print(msg);
5255
}
5356

54-
// Let display go to sleep to conserve battery, and wake up an hour later
57+
display.display(); // Send everything to display (refresh the screen)
58+
5559
Serial.println("Going to sleep");
5660
delay(100);
57-
esp_sleep_enable_timer_wakeup(60ll * 60 * 1000 * 1000); //wakeup in 60min time - 60min * 60s * 1000ms * 1000us
61+
esp_sleep_enable_timer_wakeup(60ll * 60 * 1000 * 1000); // Wake up in 60min time - 60min * 60s * 1000ms * 1000us
5862
esp_deep_sleep_start();
5963
}
6064

@@ -74,4 +78,4 @@ int calc_battery_percentage(double battv)
7478
battery_percentage = 100;
7579

7680
return battery_percentage;
77-
}
81+
}

poetry.lock

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "mag-ink-dash-plus"
3-
version = "0.1.0"
3+
version = "0.2.0"
44
description = "E-Ink Magic Dashboard that runs off a battery powered Inkplate 10; displaying content from an ICS calendar feed and OpenWeatherMap that are retrieved and rendered by a Docker container."
55
authors = ["speedyg0nz", "stefanthoss"]
66
license = "Apache License 2.0"

src/main.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
cfg = MagInkDashConfig.get_config()
2727

28-
app = FastAPI(title="MagInkDashPlus Server", version="0.1.0")
28+
app = FastAPI(title="MagInkDashPlus Server", version="0.2.0")
2929

3030
logger = structlog.get_logger()
3131

@@ -38,7 +38,10 @@ def health_check():
3838
return {"status": "ok"}
3939

4040

41-
@app.get("/test", summary="Background image for testing",)
41+
@app.get(
42+
"/test",
43+
summary="Background image for testing",
44+
)
4245
def get_background() -> FileResponse:
4346
return FileResponse("render/background.png", media_type="image/png")
4447

@@ -54,7 +57,8 @@ def get_image() -> FileResponse:
5457
)
5558

5659
# Retrieve Calendar Data
57-
currDate = dt.now(cfg.DISPLAY_TZ).date()
60+
currTime = dt.now(cfg.DISPLAY_TZ)
61+
currDate = currTime.date()
5862
calStartDatetime = cfg.DISPLAY_TZ.localize(dt.combine(currDate, dt.min.time()))
5963
calEndDatetime = cfg.DISPLAY_TZ.localize(
6064
dt.combine(
@@ -87,7 +91,7 @@ def get_image() -> FileResponse:
8791
cfg.IMAGE_WIDTH, cfg.IMAGE_HEIGHT, cfg.ROTATE_ANGLE
8892
)
8993
renderService.process_inputs(
90-
currDate,
94+
currTime,
9195
current_weather,
9296
hourly_forecast,
9397
daily_forecast,

src/render/dashboard_template.html

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
<link rel="stylesheet" href="css/styles.css">
99
<link rel="stylesheet" href="css/weather-icons.min.css">
1010
</head>
11-
<body style="background: url('background.png') no-repeat center center fixed; background-size: cover;>
11+
<body style="background: url('background.png') no-repeat center center fixed; background-size: cover;">
1212
<div class="container">
13-
<!-- Calendar -->
1413
<div class="row justify-content-center">
14+
<!-- Weather -->
1515
<div class="col-md-6">
1616
<div class="row">
1717
<div class="col-md-12">
@@ -88,9 +88,15 @@ <h3>{dayafter}<br />{dayafter_weather_pop}% | {dayafter_weather_min}-{dayafter_w
8888
<div class="col-md-12" style="height: 50px"></div>
8989
</div>
9090
</div>
91+
<!-- Calendar -->
9192
<div class="col-md-6">
9293
<div class="row align-items-start ">
93-
<div class="col-md-12" style="height: 50px"></div>
94+
<div class="col-md-12 text-right" style="height: 25px">
95+
Last Updated: {update_time}
96+
</div>
97+
</div>
98+
<div class="row align-items-start ">
99+
<div class="col-md-12" style="height: 25px"></div>
94100
</div>
95101
<div class="row align-items-start ">
96102
<div class="col-md-12">

src/render/render.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def get_short_time(self, datetimeObj, is24hour=False):
7171
datetime_str = "{}:{:02d}".format(datetimeObj.hour, datetimeObj.minute)
7272
else:
7373
if datetimeObj.minute > 0:
74-
datetime_str = ".{:02d}".format(datetimeObj.minute)
74+
datetime_str = ":{:02d}".format(datetimeObj.minute)
7575

7676
if datetimeObj.hour == 0:
7777
datetime_str = "12{}am".format(datetime_str)
@@ -85,7 +85,7 @@ def get_short_time(self, datetimeObj, is24hour=False):
8585

8686
def process_inputs(
8787
self,
88-
current_date,
88+
current_time,
8989
current_weather,
9090
hourly_forecast,
9191
daily_forecast,
@@ -97,6 +97,8 @@ def process_inputs(
9797
with open(self.currPath + "/dashboard_template.html", "r") as file:
9898
dashboard_template = file.read()
9999

100+
current_date = current_time.date()
101+
100102
# Populate the date and events
101103
cal_events_list = []
102104
for i in range(num_cal_days):
@@ -124,6 +126,7 @@ def process_inputs(
124126
htmlFile = open(self.currPath + "/dashboard.html", "w")
125127
htmlFile.write(
126128
dashboard_template.format(
129+
update_time=current_time.strftime("%x %H:%M"),
127130
day=current_date.strftime("%-d"),
128131
month=current_date.strftime("%B"),
129132
weekday=current_date.strftime("%A"),

0 commit comments

Comments
 (0)