Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
123 commits
Select commit Hold shift + click to select a range
f7c5d7c
fixing unsupported operand type
oxivanisher Dec 15, 2019
fd4abd7
fixing unsupported operand type
oxivanisher Dec 15, 2019
3cb2cad
fixing unsupported operand type
oxivanisher Dec 15, 2019
df43c92
fixing unsupported operand type
oxivanisher Dec 15, 2019
08152b8
removing one function call
oxivanisher Dec 15, 2019
e281bff
fixing oauth flow
oxivanisher Dec 15, 2019
e7e322a
ignoring rendered screens
oxivanisher Dec 15, 2019
50acde6
if no api key is configure, don't fail
oxivanisher Dec 15, 2019
e5c2b98
remove unused import
oxivanisher Dec 15, 2019
10a8d5d
force python2 since the project is not python3 ready (yet)
oxivanisher Dec 15, 2019
7a7fad0
remove unused import
oxivanisher Dec 15, 2019
c98d04a
documenting how to add google secrets (to use the google APIs)
oxivanisher Dec 15, 2019
c0f3610
fixing indentation
oxivanisher Dec 15, 2019
cd474be
making PEP8 linter happy
oxivanisher Dec 15, 2019
7b030cd
more text on error handling
oxivanisher Dec 15, 2019
b5166a0
format error output
oxivanisher Dec 15, 2019
c516cb4
fixing unicode errors
oxivanisher Dec 15, 2019
f7d4903
fixing unicode error in debug message
oxivanisher Dec 15, 2019
b539482
implement "metric" time
oxivanisher Dec 15, 2019
167d637
testing if tasklist is hardcoded
oxivanisher Dec 15, 2019
1b01cf4
removed hardcoded task list and display all tasks where "todo" is in …
oxivanisher Dec 15, 2019
734468a
use google todo module
oxivanisher Dec 15, 2019
23f9a6c
only work if there are tasks in list
oxivanisher Dec 15, 2019
3e0aea2
working on utf-8 problems
oxivanisher Dec 16, 2019
2062622
working on utf-8 problems
oxivanisher Dec 16, 2019
b571824
unicoding
oxivanisher Dec 16, 2019
c8eb1d9
unicoding
oxivanisher Dec 16, 2019
d0c7d57
more internationalization
oxivanisher Dec 16, 2019
ecbc343
fixing import
oxivanisher Dec 16, 2019
a31b898
fixing import
oxivanisher Dec 16, 2019
adcfbeb
working on character encodings/charsets
oxivanisher Dec 16, 2019
9ffd9a7
implementing multiple google calendar support
oxivanisher Dec 16, 2019
35a58ce
implementing multiple google calendar support
oxivanisher Dec 16, 2019
19adf26
implementing multiple google calendar support
oxivanisher Dec 16, 2019
2437af5
implementing ignored entries
oxivanisher Dec 16, 2019
255a81a
redo usless fix
oxivanisher Dec 16, 2019
c15073e
Documenting SPI
oxivanisher Dec 17, 2019
719c3a7
only updating the screen if new information is available
oxivanisher Dec 19, 2019
2b53c2a
Merge remote-tracking branch 'origin/master'
oxivanisher Dec 19, 2019
d874357
adding screensaver script
oxivanisher Jan 30, 2020
2d03afb
adding screensaver script
oxivanisher Jan 30, 2020
549385c
adding screensaver script
oxivanisher Jan 30, 2020
f5eac4b
disable debug output
oxivanisher Jan 30, 2020
0195402
documenting screen saver
oxivanisher Jan 30, 2020
4eea813
fixing documentation
oxivanisher Jan 30, 2020
04bc8bf
adding sleep after update to prevent e-ink damage
oxivanisher Jan 30, 2020
dffc6f1
also adding clear() on screen saver
oxivanisher Jan 30, 2020
d41e2a5
removing clear, since it is not available in this driver
oxivanisher Jan 30, 2020
50bd9e5
marking today for calendar events and using more space for text
oxivanisher May 22, 2020
62ccec2
* more space for event text due to the use of colors to mark today ev…
oxivanisher May 23, 2020
249847f
* dynamically calculating calendar event text length
oxivanisher May 25, 2020
47de99e
calculating max char width more intelligently
oxivanisher May 25, 2020
d35e9e0
* supporting more than two events with the same start date
oxivanisher Jun 2, 2020
22a651c
* events started in the past are now also "active"
oxivanisher Jun 2, 2020
a7189dd
Including birthdays calendar
oxivanisher Oct 31, 2020
36defd4
* events today are now red
oxivanisher Jul 2, 2021
b22c158
converting to python3
oxivanisher Dec 3, 2021
deb3119
ignoring pycharm
oxivanisher Dec 3, 2021
fefd2cd
only showing tasks with a due-date in the next two days
oxivanisher Dec 4, 2021
bb3c75f
sorting of tasks is now way more intelligent
oxivanisher Dec 4, 2021
bf5785a
adding reference to "bug" for google tasks not showing up
oxivanisher Mar 10, 2022
091f556
changes to support waveshare 7.5 inch 3 color display v2 with the hig…
oxivanisher Jul 2, 2022
0114535
improve readability and rewrite image creation
oxivanisher Jul 3, 2022
b02fae5
Merge pull request #2 from oxivanisher/feature/support_e-ink_screen_v2
oxivanisher Oct 18, 2022
8816b26
updating the readme
oxivanisher Mar 22, 2023
a40c4f7
* improve date/time readability
oxivanisher May 6, 2023
7e40531
implement "weeks_away" red line
oxivanisher May 7, 2023
14e4b78
increase "weeks_away" red line thickness
oxivanisher May 8, 2023
212519b
increase "weeks_away" red line thickness
oxivanisher May 8, 2023
8dae153
fetch more events since we now have 2 columns
oxivanisher Sep 18, 2023
4b3057c
fetch more cal items since we now have 2 columns
oxivanisher Sep 18, 2023
63e491e
Merge remote-tracking branch 'origin/master'
oxivanisher Sep 18, 2023
ad3b88b
add more divider line styles for calendar
oxivanisher Nov 3, 2023
6d2d218
add more divider line styles for calendar
oxivanisher Nov 3, 2023
5dfd839
add more divider line styles for calendar
oxivanisher Nov 3, 2023
1397c06
add more divider line styles for calendar
oxivanisher Nov 3, 2023
8e9154a
add more divider line styles for calendar
oxivanisher Nov 3, 2023
121bc2f
add more divider line styles for calendar
oxivanisher Nov 3, 2023
e5e7e98
add more divider line styles for calendar
oxivanisher Nov 3, 2023
3441e72
add more divider line styles for calendar
oxivanisher Nov 3, 2023
bc7721d
add more divider line styles for calendar
oxivanisher Nov 3, 2023
25ac0f1
add more divider line styles for calendar
oxivanisher Nov 3, 2023
2ddef76
add more divider line styles for calendar
oxivanisher Nov 5, 2023
0f65cf4
add more divider line styles for calendar
oxivanisher Nov 5, 2023
f9a8fbe
add more divider line styles for calendar
oxivanisher Nov 5, 2023
97b6343
add more divider line styles for calendar
oxivanisher Nov 5, 2023
d5cb5cb
add more divider line styles for calendar
oxivanisher Nov 5, 2023
1ed278d
add more divider line styles for calendar
oxivanisher Nov 5, 2023
6c5f5fa
add more divider line styles for calendar
oxivanisher Nov 5, 2023
ada1bfa
add more divider line styles for calendar
oxivanisher Nov 5, 2023
854f44c
add more divider line styles for calendar
oxivanisher Nov 5, 2023
4c11a8f
add more divider line styles for calendar
oxivanisher Nov 5, 2023
c6f4f04
Working on deprecation stuff
oxivanisher Dec 11, 2023
23bc1f9
Also ignore the driver directory
oxivanisher Dec 11, 2023
2ba6601
Also ignore the driver directory
oxivanisher Dec 11, 2023
0b5c5fb
Working on Pillow 10
oxivanisher Dec 11, 2023
1e16d5c
Pillow deprication cleanup
oxivanisher Dec 11, 2023
6ba233c
Fixing a typo. Merci Michi ;)
oxivanisher Mar 19, 2024
071b015
Update requirements.txt
oxivanisher Jan 19, 2025
203a6c7
Add caldav support and allow to use google and caldav together
oxivanisher Mar 27, 2025
4aa0626
Replace emoji with ascii "art" and calculate age
oxivanisher Mar 27, 2025
c0ffb48
Merge pull request #3 from oxivanisher/feature/caldav_support
oxivanisher Mar 27, 2025
ff88265
WIP
oxivanisher Apr 9, 2025
3b9321a
This seems to work :)
oxivanisher Apr 11, 2025
27e2da7
This seems to work :)
oxivanisher Apr 11, 2025
c18ddf6
Allow to disable google and caldav calendar and todo modules
oxivanisher Apr 11, 2025
0db96a5
Allow to disable google and caldav calendar and todo modules
oxivanisher Apr 11, 2025
6ee3cc4
New birthday ascii cake
oxivanisher Apr 11, 2025
720638b
Support tasks with start and due
oxivanisher Apr 12, 2025
f0633e9
Update mod_caldav.py
oxivanisher Apr 16, 2025
c099b6d
Update mod_caldav.py
oxivanisher May 22, 2025
3a20192
Use the better property
oxivanisher May 22, 2025
7cb5842
Add some minimal CalDAV documentation
oxivanisher May 22, 2025
2b2439e
No longer use deprecated datetime method
oxivanisher May 22, 2025
e083cb3
No longer use deprecated datetime method
oxivanisher May 24, 2025
ad0e9bb
Improve screensaver.py
oxivanisher Dec 26, 2025
6934e90
Add .gitattributes to enforce LF line endings across platforms"
oxivanisher Dec 26, 2025
58cd9ae
Randomize colors
oxivanisher Mar 11, 2026
d8adb3b
Improve tmp file handling
oxivanisher Jun 3, 2026
c48822e
Timeout network connections
oxivanisher Jun 3, 2026
dfcdd49
Handle network connection problems
oxivanisher Jun 3, 2026
12ee984
Feature/code cleanup (#4)
oxivanisher Jun 3, 2026
a4f4f3e
Feature/combine todo and calendar column (#5)
oxivanisher Jun 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Set default behavior to automatically normalize line endings
* text=auto

# Force LF line endings for all text files
*.py text eol=lf
*.txt text eol=lf
*.md text eol=lf
*.json text eol=lf
*.sh text eol=lf
*.yml text eol=lf
*.yaml text eol=lf

# Binary files
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.ttf binary
*.pickle binary
10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@ google_secret.json
*.pickle
*.code-workspace
*.pyc
__pycache__/
*.egg-info/
dist/build/
.mypy_cache/
icons/*.png
test.py
config.json

infowindow.jpg
venv/
.venv/
.idea/
driver
2 changes: 1 addition & 1 deletion 3d_files/fusion360/README.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
This file is the "Assembly" of all the parts needed to print. I left out the model of the Raspberry PI due to licensing. If you need the raspberry pi model, do a search on GrabCad there are a ton!
This file is the "Assembly" of all the parts needed to print. I left out the model of the Raspberry PI due to licensing. If you need the raspberry pi model, do a search on GrabCad there are a ton!
234 changes: 201 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,55 +1,223 @@
![alt text](infowindow.jpg)
![InfoWindow on a shelf](infowindow.jpg)

# InfoWindow

# Infowindow
Rapsberry pi powered e-ink display for displaying information in an always on state. There are several other iterations of this project online, but they didnt do quite what I wanted them to. This is my version. Also keeping up my python skills as they dont get used as much as they used to!
Raspberry Pi powered e-ink display showing calendar, todo items and weather in an always-on state.
Built for the **Waveshare 7.5" V2 three-colour (black/red/white)** e-ink display.

The goal is not a full-featured PIM — it is an *in your face* reminder of what is coming up next.
Details stay in your calendar app, phone, or browser.

The functionality is not meant to be an "end all solution for calendaring and Todo lists" The intent is to provide an *always on* display to show me what is coming up next. I can then check in browser, phone, etc for details and updates to the data. In your face reminder.
<div align="center">
<a href="#features">Features</a> |
<a href="#installation">Installation</a> |
<a href="#configuration">Configuration</a> |
<a href="#running">Running</a> |
<a href="#hardware-setup">Hardware</a> |
<a href="#installation">Installation</a> |
<a href="#configuration">Configuration</a> |
<a href="#running">Running</a> |
<a href="#local-development">Local development</a>
</div>

---

## Features
* **Calendar**
* Google Calendar is the only calendar currently supported
* **Todo List**
* Todoist
* Teamwork.com
* **Weather**
* Open Weather Map current data only. Future plan for forecast data.

| Area | Backends |
|---|---|
| **Calendar** | Google Calendar, CalDAV (Nextcloud etc.) |
| **Todo** | Google Tasks, CalDAV (Nextcloud etc.), Teamwork |
| **Weather** | OpenWeatherMap (current conditions) |

Multiple backends can be enabled simultaneously — results are merged and sorted.

---

## Hardware setup

### Enable SPI
```bash
sudo raspi-config # Interface Options → SPI → Enable
sudo reboot
```

### System packages
```bash
sudo apt install python3-dev libopenjp2-7 libxslt1.1
```

### Clone this repo
```bash
git clone https://github.com/oxivanisher/InfoWindow.git /home/pi/InfoWindow
```

### Clone the Waveshare e-Paper driver
The driver is not bundled — clone it separately and symlink it into the project.
Waveshare occasionally restructures their repo, so the path may need adjusting.
```bash
git clone https://github.com/waveshareteam/e-Paper.git /home/pi/e-Paper
ln -s /home/pi/e-Paper/RaspberryPi_JetsonNano/python/lib/waveshare_epd/ \
/home/pi/InfoWindow/driver
```

---

## Installation
### Get software
Clone this repo onto your raspberry pi. Does not really matter where it is, but good option is in the `pi` users home directory: `/home/pi/InfoWindow`

### Setup python modules
Run `pip install -r requirements.txt`. This should install all required modules. I stuck to basic standard modules for ease of installation.
```bash
cd /home/pi/InfoWindow
python3 -m venv venv
source venv/bin/activate
pip install -e ".[rpi,google,caldav]"
```

Install only the extras you need:

| Extra | Installs |
|---|---|
| `rpi` | Raspberry Pi GPIO libraries (required on Pi) |
| `google` | Google Calendar / Tasks API client |
| `caldav` | CalDAV client (Nextcloud, etc.) |
| `todoist` | Todoist stub *(v8 API discontinued — not functional)* |

---

## Configuration
You will need to configure a few things such as API Keys and location. Copy config.json-sample to config.json. Edit config.json to add your api keys and other information.

Copy the sample and fill in your details:
```bash
cp config.json-sample config.json
```

### General
* rotation: 0 - This is the rotation of the display in degrees. Leave at zero if you use it as a desktop display. Change to 180 if you have it mounted and hanging from a shelf.
| Key | Values | Description |
|---|---|---|
| `rotation` | `0` / `180` | `0` for desktop use, `180` if mounted hanging from a shelf |
| `timeformat` | `12h` / `24h` | Clock format used throughout |
| `sunday_first_dow` | `true` / `false` | Week start day for calendar grouping |
| `cell_spacing` | integer | Pixel padding inside cells |
| `timezone` | e.g. `Europe/Zurich` | Local timezone |

### Weather — OpenWeatherMap
```json
"weather": {
"api_key": "your-owm-key",
"city": "Zurich,CH",
"units": "metric"
}
```
`units` accepts `metric`, `imperial`, or `standard` (Kelvin).

### Google Calendar and Tasks

1. Go to the [Google Cloud Console](https://console.cloud.google.com/apis/).
2. Create a project (e.g. `infowindow`).
3. Create an [OAuth consent screen](https://console.cloud.google.com/apis/credentials/consent).
4. Create an [OAuth 2.0 client ID](https://console.cloud.google.com/apis/credentials) (type: *Desktop app*).
5. Download the JSON file and save it as `google_secret.json` in the project directory.

Enable the backends in `config.json`:
```json
"calendar_google": { "enabled": true, "additional": ["Contacts", "Birthdays"] },
"todo_google": { "enabled": true }
```

> **Note:** There is a known Google API bug where repeated tasks stop appearing once one
> instance is marked complete. See the [Google support thread](https://support.google.com/calendar/thread/3706294).

### Todo
Todoist is the current active module in this code. It only requires `api_key`. Teamwork also requires a 'site' key. If using google tasks, leave this as null `todo: null`
* api_key: Enter your todoist API key.
### CalDAV (Nextcloud etc.)

### Weather
Open Weather Map is where the data is coming from in the default module. This requires a few keys.
* api_key: Get your api key from OWM website.
* city: Look at OWM docs to figure what your city name is. Mine is "Sacramento,US"
* units: This can either be `imperial` or `metric`
For Nextcloud the CalDAV URL is usually:
`https://cloud.example.com/remote.php/dav/calendars/USERNAME`

```json
"calendar_caldav": {
"enabled": true,
"caldav_url": "https://cloud.example.com/remote.php/dav/calendars/USERNAME",
"username": "john",
"password": "secret",
"additional": ["Personal", "Birthdays"]
},
"todo_caldav": {
"enabled": true,
"caldav_url": "https://cloud.example.com/remote.php/dav/calendars/USERNAME",
"username": "john",
"password": "secret",
"additional": ["Tasks"]
}
```

`additional` is a list of calendar names to include. Leave it empty (`[]`) to include all.

`ignored` (under `calendar`) is a list of event titles to suppress from the display.

---

## Running
### First Run
You should run the script manually the first time so that Googles auth modules can run interactivly. Once that has completed you will want to add this to CRON so it runs every few minutes automatically.

### Cron Run (Normal use)
* Run `crontab -e`
* insert `*/6 * * * * /usr/bin/python /home/pi/InfoWindow/infowindow.py --cron`
### First run — Google authentication
If you use Google backends, run the script **manually once** so the OAuth flow can open
interactively and save a token:
```bash
source venv/bin/activate
python infowindow.py
```

Subsequent runs use the saved `token.pickle` and refresh it automatically.

### systemd (recommended)

The `dist/` directory contains ready-to-use systemd units.
Link them into place — no need to copy, so pulling updates keeps them current:

```bash
sudo ln -s /home/pi/InfoWindow/dist/infowindow.service /etc/systemd/system/
sudo ln -s /home/pi/InfoWindow/dist/infowindow.timer /etc/systemd/system/
sudo ln -s /home/pi/InfoWindow/dist/infowindow-screensaver.service /etc/systemd/system/
sudo ln -s /home/pi/InfoWindow/dist/infowindow-screensaver.timer /etc/systemd/system/

sudo systemctl daemon-reload
sudo systemctl enable --now infowindow.timer
sudo systemctl enable --now infowindow-screensaver.timer
```

> **Note:** The service files currently hardcode `User=pi`. Adjust if your username differs.

The **updater** runs every 6 minutes. The **screensaver** runs once daily at 05:01 to cycle
the display through black, red, and white — preventing image retention.

#### Useful commands
```bash
journalctl -u infowindow -f # live logs from the updater
journalctl -u infowindow-screensaver # screensaver logs
systemctl status infowindow.timer # next scheduled run
```

---

## Optional: extend SD-card lifetime

Add this line to `/etc/fstab` and reboot to keep `/tmp` in RAM:
```
tmpfs /tmp tmpfs defaults,noatime,nosuid,size=100m 0 0
```

---

## Local development

The display driver is not available outside a Raspberry Pi, but a **mock display**
activates automatically on any other machine. It composites the black and red layers
into a colour preview PNG and opens it with your default image viewer.

```bash
# One-time setup (no rpi extra needed)
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[google,caldav]"

# Run
python infowindow.py
# → preview saved to /tmp/InfoWindowPreview.png
```

The mock faithfully reproduces the Waveshare rendering rules: red wins over black when
both layers have ink at the same pixel.
3 changes: 0 additions & 3 deletions clearScreen.py

This file was deleted.

58 changes: 44 additions & 14 deletions config.json-sample
Original file line number Diff line number Diff line change
@@ -1,14 +1,44 @@
{
"general": {
"rotation": 180
},
"todo": {
"api_key": "1234"
},
"calendar": null,
"weather": {
"api_key": "1234",
"city": "Sacramento,US",
"units": "imperial"
}
}
{
"general": {
"rotation": 180,
"timeformat": "12h",
"sunday_first_dow": false,
"cell_spacing": 2,
"timezone": "Europe/Zurich"
},
"calendar": {
"font_size": 22,
"ignored": ["Buy ticket!"],
"today_text_color": "red",
"today_background_color": "white"
},
"todo": {
"font_size": 22
},
"calendar_google": {
"enabled": true,
"additional": ["Contacts", "Birthdays"]
},
"calendar_caldav": {
"enabled": true,
"caldav_url": "https://your.domain.tld/some/caldav",
"username": "john",
"password": "supersecret",
"additional": ["Contact birthdays", "some calendar"]
},
"todo_google": {
"enabled": true
},
"todo_caldav": {
"enabled": true,
"caldav_url": "https://your.domain.tld/some/caldav",
"username": "john",
"password": "supersecret",
"additional": ["another calendar"]
},
"weather": {
"api_key": "1234",
"city": "Sacramento,US",
"units": "imperial"
}
}
20 changes: 20 additions & 0 deletions dist/infowindow-screensaver.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[Unit]
Description=InfoWindow screensaver
After=network.target

[Service]
ExecStart=/home/pi/InfoWindow/venv/bin/python3 /home/pi/InfoWindow/screensaver.py
WorkingDirectory=/home/pi/InfoWindow/
CacheDirectory=InfoWindow
TimeoutStartSec=3m
StandardOutput=journal
StandardError=journal
SyslogIdentifier=infowindow-screensaver
NotifyAccess=all
User=pi
Group=pi
Nice=1
Type=oneshot

[Install]
WantedBy=multi-user.target
10 changes: 10 additions & 0 deletions dist/infowindow-screensaver.timer
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[Unit]
Description=InfoWindow screensaver

[Timer]
Unit=infowindow-screensaver.service
#OnCalendar=5:1
OnCalendar=5:1

[Install]
WantedBy=timers.target
Loading