A modular, configuration-driven Python system that generates a personalized daily briefing as a PDF and optionally prints it via CUPS. Every section of the report is a self-contained module that can be enabled, disabled, reordered, or swapped out entirely through a single YAML file.
- Modular Architecture: Add, remove, or reorder any section using
config.yaml. - Extensible Module System: Easily write and plug in your own Python modules for custom data.
- Multi-OS Support: Automatic font discovery for Linux, Windows, and macOS.
- Automated Printing: Direct integration with CUPS for daily physical reports.
The inspiration for this project came from a Reddit post where someone was printing their daily reports using a thermal receipt printer. That sparked the idea for a similar, but more comprehensive system that works with any regular home printer.
The goal of DPPR is to provide a curated, physical report to start your morning. Instead of immediately diving into your phone and getting lost in notifications, you can sit down with a cup of coffee and read a one-page summary of everything that matters to you: your local weather, the latest news, your financial portfolios, and your own server's health. It's about taking back the first few minutes of your day with a focused, reading experience.
The system is built around three core ideas:
- Modules are small, independent Python files that each fetch one type of data.
- Layout is defined in
config.yaml, which controls the order and configuration of modules. - The Engine takes module output and renders it into a formatted PDF.
For complete setup and configuration details, please see our guides:
- Getting Started - Installation and OS-specific requirements.
- Configuration Guide - Deep dive into
config.yamland.env. - Module Catalog - Full list of all available modules and their options.
- Development Guide - How to write your own custom modules.
DPPR/
βββ main.py Entry point, loads config and runs modules
βββ config.yaml Your personal configuration (gitignored)
βββ config.template.yaml Reference config with all available options
βββ .env API keys and credentials (gitignored)
βββ requirements.txt Python dependencies
βββ holidays.json Custom holiday dates (gitignored)
βββ quotes.json Custom quotes/lyrics for the footer (gitignored)
βββ signature.png Optional signature image (gitignored)
β
βββ engine/
β βββ pdf_generator.py Renders report data into a PDF
β βββ printer.py Sends the PDF to a CUPS printer
β
βββ modules/
βββ environment/
β βββ weather/weather.py Current weather via Open-Meteo
β βββ astronomy.py Sunrise and sunset times
β βββ aqi/
β βββ openmeteo.py US AQI via Open-Meteo (Global)
β βββ breatheoss.py US AQI via BreatheOSS (Jammu and Kashmir, India)
β
βββ news/
β βββ news.py RSS/Atom feed aggregator with Reddit support
β βββ hacker_news.py Top stories from Hacker News
β βββ github_trending.py Trending GitHub repositories
β
βββ finances/
β βββ exchange_rate.py Forex rates
β βββ markets.py Stock prices via Yahoo Finance
β βββ crypto.py Crypto prices via CoinGecko
β βββ commodities.py Gold, Silver, Oil via Yahoo Finance
β βββ actual.py Bank balances via Actual Budget
β βββ currency_util.py Shared currency conversion helpers
β
βββ planning/
β βββ calendar.py Upcoming holidays and next Sunday
β
βββ home_automation/
β βββ ha.py Entity states from Home Assistant
β
βββ communication/
β βββ email_inbox.py Recent emails via IMAP
β
βββ android/
β βββ gplay.py Google Play download stats
β
βββ knowledge/
β βββ fact_of_the_day.py Daily fact from Useless Facts API
β
βββ social/
β βββ shower_thoughts.py Top posts from r/ShowerThoughts
β
βββ random/
β βββ word_of_the_day.py Word of the Day from Merriam-Webster
β βββ daily_joke.py Dad joke from icanhazdadjoke
β βββ quotes.py Random quote/lyric for the footer
β
βββ system/
β βββ system.py Disk, RAM, and CPU stats
β
βββ essentials/
βββ essentials.py Static confirmation line
Every module is a Python file inside modules/ that exposes a single function:
def get_data(config):
return ["Line 1", "Line 2", "Line 3"]configis a dictionary passed from the module's entry inconfig.yaml.- The function returns a list of strings. Each string becomes one line in the report.
- The engine handles text wrapping, encoding, and page breaks automatically.
Modules are discovered by filename. If you create modules/custom/my_thing.py, you reference it in the layout as module: "my_thing". The engine walks the modules/ directory tree to find it.
The quotes module is special. Instead of appearing in the report body, its output is rendered as a footer quote at the bottom of the page. It does not need a title in the layout.
git clone https://github.com/FlashWreck/DPPR && cd DPPR
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txtcp config.template.yaml config.yamlEdit config.yaml to:
- Set your
nameandgreetingunderuser. - Set your
latandlonunderuser(used by weather, AQI, and astronomy modules). - Set
master_currencyundersettingsto auto-convert all financial data. - Add, remove, or reorder modules in the
layoutlist.
Create a .env file for credentials that should not live in the config:
EMAIL_USER=[email protected]
EMAIL_PASS=your_app_password
EMAIL_HOST=imap.gmail.com
HA_URL=http://homeassistant.local:8123
HA_ACCESS_TOKEN=your_long_lived_token
ACTUAL_SERVER_URL=https://actual.example.com
ACTUAL_PASSWORD=your_password
ACTUAL_SYNC_ID=your_sync_id
ACTUAL_ENCRYPTION_PASSWORD=optional
PACKAGE_NAME=com.example.appOnly the modules you enable need their corresponding variables. Unused modules will not throw errors for missing credentials.
| File | Purpose |
|---|---|
holidays.json |
A {"YYYY-MM-DD": "Name"} mapping of holidays for the calendar module |
quotes.json |
A list of {"text": "...", "attribution": "..."} objects for the footer |
signature.png |
An image rendered in the bottom-right corner of the report |
The PDF generator looks for JetBrains Mono in standard font directories across Linux, Windows, and macOS. If not found, it falls back to Courier. To install JetBrains Mono:
- Linux:
sudo pacman -S ttf-jetbrains-monoorsudo apt install fonts-jetbrains-mono - macOS:
brew install --cask font-jetbrains-mono - Windows: Download from JetBrains and install to
C:\Windows\Fonts
python main.py # Generate PDF and print
python main.py --no-print # Generate PDF onlyThe output is saved to daily_report.pdf in the project root.
The news module ships with a built-in directory of RSS feeds. Use these keys in your config instead of raw URLs:
| Region | Keys |
|---|---|
| Global | bbc, aljazeera, reuters, ap, npr, nyt |
| Europe | dw, france24, euronews, guardian |
| Asia | cna, bangkokpost, hindu, toi, ndtv, yourstory, scmp, technode, caixin |
| Oceania | abc_au, rnz |
| Latin America | mercopress, batimes |
| Tech | techcrunch, verge, wired, arstechnica, theregister, hackernews, noted, selfh_st |
You can also pull from any subreddit:
- module: "news"
title: "REDDIT"
feeds:
- subreddit: "selfhosted"
count: 3
- subreddit: "linux"
count: 2Custom RSS/Atom Feeds: You can also add any custom RSS or Atom feed URL in the news module:
- module: "news"
title: "CUSTOM FEED"
feeds:
- url: "https://example.com/feed.xml"
count: 2All financial modules (stocks, crypto, commodities) support automatic currency conversion. Set master_currency in settings and every price will be converted from its native currency:
settings:
master_currency: "INR"Conversion rates are fetched live from Yahoo Finance.
DPPR integrates with CUPS for automated printing. Enable it in config.yaml:
settings:
printing:
enabled: true
cups_printer: "HP_LaserJet"If cups_printer is not set, it defaults to the first available printer. Requires pycups (pip install pycups) and a working CUPS setup on the host.
- Create a file anywhere inside
modules/, e.g.modules/custom/uptime.py. - Implement
get_data(config):
import requests
def get_data(config):
url = config.get('url')
try:
resp = requests.get(url, timeout=5)
return [f"Status: {resp.status_code}"]
except Exception as e:
return [f"Error: {e}"]- Add it to your layout:
- module: "uptime"
title: "UPTIME CHECK"
config:
url: "https://example.com"That is it. The engine discovers the module by filename and passes the config block to get_data().
If you make a custom module for your own needs, please be kind enough to make a Pull Request and help others out!
To maintain code quality, please follow these guidelines:
- Signed Commits: All commits must be signed. Use
git commit -s. - Commit Format: Use the format:
DPPR: [type]: [description]- Types:
fix,feat,chore,docs,refactor, etc. - Example:
DPPR: docs: add essentials module to catalog - Type is not strictly required, but use wherever possible.
- Types:
Your contributions help keep the project diverse and useful for everyone.
MIT