diff options
| -rw-r--r-- | README.md | 30 | ||||
| -rw-r--r-- | __pycache__/cal.cpython-310.pyc | bin | 0 -> 1304 bytes | |||
| -rw-r--r-- | __pycache__/cal.cpython-39.pyc | bin | 0 -> 1294 bytes | |||
| -rw-r--r-- | __pycache__/natDay.cpython-310.pyc | bin | 0 -> 665 bytes | |||
| -rw-r--r-- | __pycache__/natDay.cpython-39.pyc | bin | 0 -> 653 bytes | |||
| -rw-r--r-- | __pycache__/news.cpython-310.pyc | bin | 0 -> 844 bytes | |||
| -rw-r--r-- | __pycache__/news.cpython-39.pyc | bin | 0 -> 832 bytes | |||
| -rw-r--r-- | __pycache__/quote.cpython-310.pyc | bin | 0 -> 602 bytes | |||
| -rw-r--r-- | __pycache__/quote.cpython-39.pyc | bin | 0 -> 590 bytes | |||
| -rw-r--r-- | __pycache__/settings.cpython-310.pyc | bin | 0 -> 380 bytes | |||
| -rw-r--r-- | __pycache__/settings.cpython-39.pyc | bin | 0 -> 378 bytes | |||
| -rw-r--r-- | __pycache__/weather.cpython-310.pyc | bin | 0 -> 1622 bytes | |||
| -rw-r--r-- | __pycache__/weather.cpython-39.pyc | bin | 0 -> 1604 bytes | |||
| -rw-r--r-- | cal.py | 46 | ||||
| -rw-r--r-- | ipqr.py | 16 | ||||
| -rw-r--r-- | main.py | 50 | ||||
| -rw-r--r-- | natDay.py | 20 | ||||
| -rw-r--r-- | news.py | 30 | ||||
| -rw-r--r-- | quote.py | 19 | ||||
| -rw-r--r-- | requirements.txt | 7 | ||||
| -rw-r--r-- | settings.json | 30 | ||||
| -rw-r--r-- | settings.py | 15 | ||||
| -rw-r--r-- | setup.py | 12 | ||||
| -rw-r--r-- | weather.py | 51 |
24 files changed, 326 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..6740481 --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +# The Morning Paper +The Morning Paper is a project that prints out different information to a +receipt printer in the morning. The project will include template modules for +today's calendar events, the weather, a quote, top news headlines, and the +national day. + +## Modules +The modules provided are written in python and will use the [adafruit thermal +printer library](https://github.com/adafruit/Python-Thermal-Printer) to +interface with the +[adafruit thermal printer used](https://www.adafruit.com/product/600). + +## TODO +- Better line formatting +- Switch to Adafruit Thermal print library +- Stock Tracker +- Better error handling +- Test print page with ip address +- Web interface for settings + - Settings print out with qr code to ip address + +## Parts +- [Adafruit Thermal Printer](https://www.adafruit.com/product/600) +- Microcontroller (I used a raspberry pi 4 but a zero w will work as well) + +## APIs used +- [Quotable](https://quotable.io) +- [National Weather Service](https://www.weather.gov/documentation/services-web-api) +- [News API](https://newsapi.org/) +- [National Day API](https://national-api-day.herokuapp.com)
\ No newline at end of file diff --git a/__pycache__/cal.cpython-310.pyc b/__pycache__/cal.cpython-310.pyc Binary files differnew file mode 100644 index 0000000..2f41663 --- /dev/null +++ b/__pycache__/cal.cpython-310.pyc diff --git a/__pycache__/cal.cpython-39.pyc b/__pycache__/cal.cpython-39.pyc Binary files differnew file mode 100644 index 0000000..5b90adf --- /dev/null +++ b/__pycache__/cal.cpython-39.pyc diff --git a/__pycache__/natDay.cpython-310.pyc b/__pycache__/natDay.cpython-310.pyc Binary files differnew file mode 100644 index 0000000..c1c7433 --- /dev/null +++ b/__pycache__/natDay.cpython-310.pyc diff --git a/__pycache__/natDay.cpython-39.pyc b/__pycache__/natDay.cpython-39.pyc Binary files differnew file mode 100644 index 0000000..bd91c23 --- /dev/null +++ b/__pycache__/natDay.cpython-39.pyc diff --git a/__pycache__/news.cpython-310.pyc b/__pycache__/news.cpython-310.pyc Binary files differnew file mode 100644 index 0000000..cb7bfb1 --- /dev/null +++ b/__pycache__/news.cpython-310.pyc diff --git a/__pycache__/news.cpython-39.pyc b/__pycache__/news.cpython-39.pyc Binary files differnew file mode 100644 index 0000000..9f4a227 --- /dev/null +++ b/__pycache__/news.cpython-39.pyc diff --git a/__pycache__/quote.cpython-310.pyc b/__pycache__/quote.cpython-310.pyc Binary files differnew file mode 100644 index 0000000..608f74f --- /dev/null +++ b/__pycache__/quote.cpython-310.pyc diff --git a/__pycache__/quote.cpython-39.pyc b/__pycache__/quote.cpython-39.pyc Binary files differnew file mode 100644 index 0000000..6b0e847 --- /dev/null +++ b/__pycache__/quote.cpython-39.pyc diff --git a/__pycache__/settings.cpython-310.pyc b/__pycache__/settings.cpython-310.pyc Binary files differnew file mode 100644 index 0000000..016635d --- /dev/null +++ b/__pycache__/settings.cpython-310.pyc diff --git a/__pycache__/settings.cpython-39.pyc b/__pycache__/settings.cpython-39.pyc Binary files differnew file mode 100644 index 0000000..5dade3b --- /dev/null +++ b/__pycache__/settings.cpython-39.pyc diff --git a/__pycache__/weather.cpython-310.pyc b/__pycache__/weather.cpython-310.pyc Binary files differnew file mode 100644 index 0000000..379b763 --- /dev/null +++ b/__pycache__/weather.cpython-310.pyc diff --git a/__pycache__/weather.cpython-39.pyc b/__pycache__/weather.cpython-39.pyc Binary files differnew file mode 100644 index 0000000..8c751b5 --- /dev/null +++ b/__pycache__/weather.cpython-39.pyc @@ -0,0 +1,46 @@ +import icalendar +import recurring_ical_events +import urllib.request +import datetime +from settings import calendars + +today = datetime.date.today() +output = [] + +''' + For some reason Icloud calendars seem to be reversed, + so the must be reversed to put events in the proper order. +''' + + +def isIcloud(url): + index = url.find("icloud") + if index > -1: + return True + return False + + +def getEvents(url, cName): + ical_string = urllib.request.urlopen(url).read() + calendar = icalendar.Calendar.from_ical(ical_string) + events = recurring_ical_events.of(calendar).at(today) + if len(events) != 0: + if isIcloud(url): + events.reverse() + output.append(f"\nToday's events from {cName} calendar:") + for event in events: + name = event["SUMMARY"] + try: + start = event["DTSTART"].dt.strftime("%H:%M") + end = event["DTEND"].dt.strftime("%H:%M") + output.append(f"{name} from {start} to {end}") + except: + output.append(f"{name} All Day") + + +def getEventsFromCal(): + for calendar in calendars: + url = calendars[calendar] + getEvents(url, calendar) + output[-1] = output[-1] + "\n" + return output @@ -0,0 +1,16 @@ +import pyqrcode +import socket + + +def getIpAdress(): + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.connect(("8.8.8.8", 80)) + ipaddress = s.getsockname()[0] + return ipaddress + + +def getQrCode(): + ip = str(getIpAdress()) + url = pyqrcode.create(ip) + + url.png('ipqr.png', scale=6)
\ No newline at end of file @@ -0,0 +1,50 @@ +from cal import getEventsFromCal +from natDay import getNationalDay +from news import getHeadlines +from quote import getQuote +from weather import getForecast +import datetime +from settings import modules, general +from time import sleep +from Adafruit_Thermal import * +import textwrap + +name = general["name"] +today = datetime.date.today().strftime("%A %m-%d-%Y") +printer = Adafruit_Thermal("/dev/ttyS0", 19200, timeout=5) +lineWidth = 32 + +output = ["The Morning Paper:", f"Good Morning {name}, Today is {today}"] + +mods = { + "quote": getQuote(), + "national day": getNationalDay(), + "weather": getForecast(), + "calendar": getEventsFromCal(), + "news": getHeadlines() +} + + +def main(): + for module in modules: + if modules[module]: + for line in mods[module]: + line = line.replace("\n", "") + printer.println(textwrap.fill(line, lineWidth)) + printer.feed(1) + +printer.wake() +printer.setSize('M') +sleep(10) + + +for line in output: + printer.println(textwrap.fill(line, lineWidth)) +try: + main() +except: + sleep(30) + main() + +printer.feed(6) +printer.sleep() diff --git a/natDay.py b/natDay.py new file mode 100644 index 0000000..c5e384f --- /dev/null +++ b/natDay.py @@ -0,0 +1,20 @@ +import requests +import json +import time +import random + +url = "https://national-api-day.herokuapp.com/api/today" + + +def getNationalDay(): + resp = requests.get(url) + + while resp.status_code != 200: + resp = requests.get(url) + time.sleep(10) + + data = json.loads(resp.text) + holidays = data["holidays"] + index = random.randint(0, len(holidays) - 1) + + return [f"Today's National Day:\n{holidays[index]}\n"] @@ -0,0 +1,30 @@ +import requests +import json +from time import sleep +from settings import news + +apiKey = "17d4d578091e47db9791e84d790391b5" +countryCode = news["country"] + +url = f"https://newsapi.org/v2/top-headlines?country={countryCode}&apiKey={apiKey}" + + +def getHeadlines(): + resp = requests.get(url) + while resp.status_code != 200: + resp = requests.get(url) + sleep(10) + + respData = json.loads(resp.text) + + articles = respData["articles"] + + output = ["Top 5 Headlines in the US:"] + + for i in range(5): + article = articles[i] + title = article["title"] + output.append(title) + output.append("\n") + + return output diff --git a/quote.py b/quote.py new file mode 100644 index 0000000..2953a28 --- /dev/null +++ b/quote.py @@ -0,0 +1,19 @@ +import requests +import json +import time + +quoteUrl = "https://quotable.io/random" + + +def getQuote(): + resp = requests.get(quoteUrl) + + while resp.status_code != 200: + resp = requests.get(quoteUrl) + time.sleep(10) + + quoteData = json.loads(resp.text) + content = quoteData["content"] + author = quoteData["author"] + + return [f"Here is today's quote:\n\"{content}\" - {author}\n"] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..9482120 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +icalendar~=4.1.0 +PyQRCode~=1.2.1 +requests~=2.28.1 +drawing~=0.0.3 +setuptools~=56.0.0 +Adafruit-Thermal +recurring-ical-events
\ No newline at end of file diff --git a/settings.json b/settings.json new file mode 100644 index 0000000..47baca9 --- /dev/null +++ b/settings.json @@ -0,0 +1,30 @@ +{ + "calendars": { + "School": "https://outlook.office365.com/owa/calendar/412f8b78a8564989926618247daea585@psu.edu/2cd13fc6692a4dfa978b34987cb2277a8238352227218615013/calendar.ics", + "Personal": "https://p71-caldav.icloud.com/published/2/MTY4Njk4MjQ4NjAxNjg2OUkA_BmX5UzH5rSvF5SwlIEXsORegc3ndydzGjQdvBCe", + "Family": "https://p71-caldav.icloud.com/published/2/MTY4Njk4MjQ4NjAxNjg2OUkA_BmX5UzH5rSvF5SwlIHdQtKK2Blxp39QaXZJbzGagcAtA1ewsskMgUHIAOpeV-OHMazKljffEQIYmOMtqsk", + "Work": "https://p71-caldav.icloud.com/published/2/MTY4Njk4MjQ4NjAxNjg2OUkA_BmX5UzH5rSvF5SwlIFA9IsJGmQmyPJL3YEE9lJc" + }, + "weather": { + "gridX": 39, + "gridY": 86, + "WFO": "PHI", + "Units": "si", + "Detailed": 1, + "Hourly": 1, + "Hours": 24 + }, + "news": { + "country": "us" + }, + "modules": { + "quote": 1, + "national day": 1, + "weather": 1, + "calendar": 1, + "news": 0 + }, + "general": { + "name": "Jacob" + } +}
\ No newline at end of file diff --git a/settings.py b/settings.py new file mode 100644 index 0000000..5e92981 --- /dev/null +++ b/settings.py @@ -0,0 +1,15 @@ +import json + +settingsJSON = open('settings.json') + +settings = json.load(settingsJSON) + +calendars = settings["calendars"] +weather = settings["weather"] +news = settings["news"] +modules = settings["modules"] +general = settings["general"] + +settingsJSON.close() + + diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..5b964e9 --- /dev/null +++ b/setup.py @@ -0,0 +1,12 @@ +from setuptools import setup + +setup( + name='The Moring Paper', + version='0.1', + packages=[''], + url='', + license='', + author='Jacob McDonnell', + author_email='jacob@jacobmcdonnell.com', + description='A morning paper with personal events' +) diff --git a/weather.py b/weather.py new file mode 100644 index 0000000..5996f77 --- /dev/null +++ b/weather.py @@ -0,0 +1,51 @@ +import requests +import json +from time import sleep +from datetime import datetime +from settings import weather + +gridX = weather["gridX"] +gridY = weather["gridY"] +wfo = weather["WFO"] +units = weather["Units"] # either si or us +hourly = weather["Hourly"] +detailed = weather["Detailed"] +hours = weather["Hours"] + + +def getForecast(): + output = [] + + dayForecastUrl = f"https://api.weather.gov/gridpoints/{wfo}/{gridX},{gridY}/forecast?units={units}" + hourlyForecastUrl = f"https://api.weather.gov/gridpoints/{wfo}/{gridX},{gridY}/forecast/hourly?units={units}" + + dayResp = requests.get(dayForecastUrl) + hourResp = requests.get(hourlyForecastUrl) + + while (dayResp.status_code != 200) and (hourResp.status_code != 200): + dayResp = requests.get(dayForecastUrl) + hourResp = requests.get(hourlyForecastUrl) + sleep(10) + + dayData = json.loads(dayResp.text) + hourData = json.loads(hourResp.text) + dayPeriods = dayData["properties"]["periods"] + hourPeriods = hourData["properties"]["periods"] + + todayFor = dayPeriods[0] + tonightFor = dayPeriods[1] + + if detailed: + output.append("Today's Forecast: " + todayFor["detailedForecast"]) + output.append("\nTonight's Forecast: " + tonightFor["detailedForecast"] + "\n") + + if hourly: + output.append(f"{hours} Hour Forecast") + for i in range(hours): + forecast = hourPeriods[i] + sTime = datetime.strptime(forecast["startTime"], "%Y-%m-%dT%H:%M:%S%z") + formatTime = sTime.strftime("%m-%d %H:%M") + temp = str(forecast["temperature"]) + "°" + forecast["temperatureUnit"] + " " + forecast["shortForecast"] + hourlyForecast = f"{formatTime}: {temp}" + output.append(hourlyForecast) + return output |
