summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob McDonnell <jacob@simplelittledream.com>2022-09-27 13:06:14 -0400
committerJacob McDonnell <jacob@simplelittledream.com>2022-09-27 13:06:14 -0400
commit380b7f432f93b82edd23e5c7921cb77c3479fbd3 (patch)
tree9989b2ad2ce58832862012101d17f8455c74cc10
Initial Commit
-rw-r--r--README.md30
-rw-r--r--__pycache__/cal.cpython-310.pycbin0 -> 1304 bytes
-rw-r--r--__pycache__/cal.cpython-39.pycbin0 -> 1294 bytes
-rw-r--r--__pycache__/natDay.cpython-310.pycbin0 -> 665 bytes
-rw-r--r--__pycache__/natDay.cpython-39.pycbin0 -> 653 bytes
-rw-r--r--__pycache__/news.cpython-310.pycbin0 -> 844 bytes
-rw-r--r--__pycache__/news.cpython-39.pycbin0 -> 832 bytes
-rw-r--r--__pycache__/quote.cpython-310.pycbin0 -> 602 bytes
-rw-r--r--__pycache__/quote.cpython-39.pycbin0 -> 590 bytes
-rw-r--r--__pycache__/settings.cpython-310.pycbin0 -> 380 bytes
-rw-r--r--__pycache__/settings.cpython-39.pycbin0 -> 378 bytes
-rw-r--r--__pycache__/weather.cpython-310.pycbin0 -> 1622 bytes
-rw-r--r--__pycache__/weather.cpython-39.pycbin0 -> 1604 bytes
-rw-r--r--cal.py46
-rw-r--r--ipqr.py16
-rw-r--r--main.py50
-rw-r--r--natDay.py20
-rw-r--r--news.py30
-rw-r--r--quote.py19
-rw-r--r--requirements.txt7
-rw-r--r--settings.json30
-rw-r--r--settings.py15
-rw-r--r--setup.py12
-rw-r--r--weather.py51
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
new file mode 100644
index 0000000..2f41663
--- /dev/null
+++ b/__pycache__/cal.cpython-310.pyc
Binary files differ
diff --git a/__pycache__/cal.cpython-39.pyc b/__pycache__/cal.cpython-39.pyc
new file mode 100644
index 0000000..5b90adf
--- /dev/null
+++ b/__pycache__/cal.cpython-39.pyc
Binary files differ
diff --git a/__pycache__/natDay.cpython-310.pyc b/__pycache__/natDay.cpython-310.pyc
new file mode 100644
index 0000000..c1c7433
--- /dev/null
+++ b/__pycache__/natDay.cpython-310.pyc
Binary files differ
diff --git a/__pycache__/natDay.cpython-39.pyc b/__pycache__/natDay.cpython-39.pyc
new file mode 100644
index 0000000..bd91c23
--- /dev/null
+++ b/__pycache__/natDay.cpython-39.pyc
Binary files differ
diff --git a/__pycache__/news.cpython-310.pyc b/__pycache__/news.cpython-310.pyc
new file mode 100644
index 0000000..cb7bfb1
--- /dev/null
+++ b/__pycache__/news.cpython-310.pyc
Binary files differ
diff --git a/__pycache__/news.cpython-39.pyc b/__pycache__/news.cpython-39.pyc
new file mode 100644
index 0000000..9f4a227
--- /dev/null
+++ b/__pycache__/news.cpython-39.pyc
Binary files differ
diff --git a/__pycache__/quote.cpython-310.pyc b/__pycache__/quote.cpython-310.pyc
new file mode 100644
index 0000000..608f74f
--- /dev/null
+++ b/__pycache__/quote.cpython-310.pyc
Binary files differ
diff --git a/__pycache__/quote.cpython-39.pyc b/__pycache__/quote.cpython-39.pyc
new file mode 100644
index 0000000..6b0e847
--- /dev/null
+++ b/__pycache__/quote.cpython-39.pyc
Binary files differ
diff --git a/__pycache__/settings.cpython-310.pyc b/__pycache__/settings.cpython-310.pyc
new file mode 100644
index 0000000..016635d
--- /dev/null
+++ b/__pycache__/settings.cpython-310.pyc
Binary files differ
diff --git a/__pycache__/settings.cpython-39.pyc b/__pycache__/settings.cpython-39.pyc
new file mode 100644
index 0000000..5dade3b
--- /dev/null
+++ b/__pycache__/settings.cpython-39.pyc
Binary files differ
diff --git a/__pycache__/weather.cpython-310.pyc b/__pycache__/weather.cpython-310.pyc
new file mode 100644
index 0000000..379b763
--- /dev/null
+++ b/__pycache__/weather.cpython-310.pyc
Binary files differ
diff --git a/__pycache__/weather.cpython-39.pyc b/__pycache__/weather.cpython-39.pyc
new file mode 100644
index 0000000..8c751b5
--- /dev/null
+++ b/__pycache__/weather.cpython-39.pyc
Binary files differ
diff --git a/cal.py b/cal.py
new file mode 100644
index 0000000..4f32465
--- /dev/null
+++ b/cal.py
@@ -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
diff --git a/ipqr.py b/ipqr.py
new file mode 100644
index 0000000..33a4f94
--- /dev/null
+++ b/ipqr.py
@@ -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
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..61f9374
--- /dev/null
+++ b/main.py
@@ -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"]
diff --git a/news.py b/news.py
new file mode 100644
index 0000000..28bb374
--- /dev/null
+++ b/news.py
@@ -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