Basic functionality and API call to ESV.org is complete along with the parsing of the returned object to something more presentable.
This commit is contained in:
25
README.md
Normal file
25
README.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Bible Clock Project
|
||||||
|
|
||||||
|
As I was thinking about a gift for my partner, I saw an ad on Instagram a few weeks ago that was a simple box with a e-ink screen that simple displays a verse based on the time. So if it is 3:16, the screen would display the verse for a book in the Bible that contains a verse at chapter 3, verse 16. I thought that would be simple enough to build myself and give for her birthday.
|
||||||
|
|
||||||
|
The hardware is pretty easy to conceptualize (subject to change once I start putting wires to hardware):
|
||||||
|
* ESP powered device
|
||||||
|
* eInk display
|
||||||
|
* (optional) RTC sensor
|
||||||
|
|
||||||
|
Since Python is the language I'm the most comfortable with, I've been looking for a project to dip my feet into [Micropython](https://micropython.org/).
|
||||||
|
|
||||||
|
Current status:
|
||||||
|
* Logic for making an API call to [Crossway's ESV Endpoint](https://api.esv.org/)
|
||||||
|
* Parse returned JSON to make it more presentable (fixing special characters, closing open quotes, removing footnotes, etc)
|
||||||
|
* Add timeout decorator for times that are do not commonly have a verse/chapter association (i.e 11:58).
|
||||||
|
* Remove the 0 for any "xx:0x" timestamp as verses do not have 0s for numbers under 10.
|
||||||
|
|
||||||
|
To Do List:
|
||||||
|
- [ ] If timeout triggers, display generic verse on screen with the current time
|
||||||
|
- [ ] Check time every x seconds and run the functions if the time has changed from previous value.
|
||||||
|
- [ ] Setup micropython files & functions, including display libraries.
|
||||||
|
|
||||||
|
Longer term plans to make this "giftable":
|
||||||
|
- [ ] Create Wi-Fi SSID & Interface to connect to your own Wi-Fi network
|
||||||
|
- [ ] Web UI to remove/add specific books from being shown
|
||||||
BIN
__pycache__/timeout.cpython-313.pyc
Normal file
BIN
__pycache__/timeout.cpython-313.pyc
Normal file
Binary file not shown.
8
requirements.txt
Normal file
8
requirements.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
certifi==2024.12.14
|
||||||
|
charset-normalizer==3.4.1
|
||||||
|
idna==3.10
|
||||||
|
levenshtein==0.26.1
|
||||||
|
pip==24.3.1
|
||||||
|
rapidfuzz==3.11.0
|
||||||
|
requests==2.32.3
|
||||||
|
urllib3==2.3.0
|
||||||
26
timeout.py
Normal file
26
timeout.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import errno
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import functools
|
||||||
|
|
||||||
|
class TimeoutError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def timeout(seconds=10, error_message=os.strerror(errno.ETIME)):
|
||||||
|
def decorator(func):
|
||||||
|
def _handle_timeout(signum, frame):
|
||||||
|
raise TimeoutError(error_message)
|
||||||
|
|
||||||
|
@functools.wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
signal.signal(signal.SIGALRM, _handle_timeout)
|
||||||
|
signal.alarm(seconds)
|
||||||
|
try:
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
finally:
|
||||||
|
signal.alarm(0)
|
||||||
|
return result
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
return decorator
|
||||||
153
verse-time-display.py
Normal file
153
verse-time-display.py
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import requests
|
||||||
|
from Levenshtein import distance as lev
|
||||||
|
import time
|
||||||
|
import re
|
||||||
|
import random
|
||||||
|
import timeit
|
||||||
|
from timeout import timeout
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
VERSION = "en-lsv"
|
||||||
|
APIKEY = "Token f562cf2d890151d682065696dacdc0f86938a18e"
|
||||||
|
HEADERS = {"Authorization": APIKEY}
|
||||||
|
BIBLE_BOOKS = [
|
||||||
|
"Genesis",
|
||||||
|
"Exodus",
|
||||||
|
"Leviticus",
|
||||||
|
"Numbers",
|
||||||
|
"Deuteronomy",
|
||||||
|
"Joshua",
|
||||||
|
"Judges",
|
||||||
|
"Ruth",
|
||||||
|
"1 Samuel",
|
||||||
|
"2 Samuel",
|
||||||
|
"1 Kings",
|
||||||
|
"2 Kings",
|
||||||
|
"1 Chronicles",
|
||||||
|
"2 Chronicles",
|
||||||
|
"Ezra",
|
||||||
|
"Nehemiah",
|
||||||
|
"Esther",
|
||||||
|
"Job",
|
||||||
|
"Psalm",
|
||||||
|
"Proverbs",
|
||||||
|
"Ecclesiastes",
|
||||||
|
"Song of Solomon",
|
||||||
|
"Isaiah",
|
||||||
|
"Jeremiah",
|
||||||
|
"Lamentations",
|
||||||
|
"Ezekiel",
|
||||||
|
"Daniel",
|
||||||
|
"Hosea",
|
||||||
|
"Joel",
|
||||||
|
"Amos",
|
||||||
|
"Obadiah",
|
||||||
|
"Jonah",
|
||||||
|
"Micah",
|
||||||
|
"Nahum",
|
||||||
|
"Habakkuk",
|
||||||
|
"Zephaniah",
|
||||||
|
"Haggai",
|
||||||
|
"Zechariah",
|
||||||
|
"Malachi",
|
||||||
|
"Matthew",
|
||||||
|
"Mark",
|
||||||
|
"Luke",
|
||||||
|
"John",
|
||||||
|
"Acts",
|
||||||
|
"Romans",
|
||||||
|
"1 Corinthians",
|
||||||
|
"2 Corinthians",
|
||||||
|
"Galatians",
|
||||||
|
"Ephesians",
|
||||||
|
"Philippians",
|
||||||
|
"Colossians",
|
||||||
|
"1 Thessalonians",
|
||||||
|
"2 Thessalonians",
|
||||||
|
"1 Timothy",
|
||||||
|
"2 Timothy",
|
||||||
|
"Titus",
|
||||||
|
"Philemon",
|
||||||
|
"Hebrews",
|
||||||
|
"James",
|
||||||
|
"1 Peter",
|
||||||
|
"2 Peter",
|
||||||
|
"1 John",
|
||||||
|
"2 John",
|
||||||
|
"3 John",
|
||||||
|
"Jude",
|
||||||
|
"Revelation",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def get_time():
|
||||||
|
gettime = datetime.datetime.now()
|
||||||
|
time = gettime.strftime("%H:%M")
|
||||||
|
return time
|
||||||
|
|
||||||
|
|
||||||
|
def choose_book():
|
||||||
|
rand_book = random.choice(BIBLE_BOOKS)
|
||||||
|
return rand_book
|
||||||
|
|
||||||
|
|
||||||
|
# @timeout(15)
|
||||||
|
def get_verse():
|
||||||
|
curr_time = get_time()
|
||||||
|
extract = ""
|
||||||
|
distance = lev(extract, curr_time)
|
||||||
|
while True:
|
||||||
|
book = choose_book()
|
||||||
|
time.sleep(2)
|
||||||
|
url = f"https://api.esv.org/v3/passage/text/?q={book} {curr_time}"
|
||||||
|
response = requests.get(url, headers=HEADERS)
|
||||||
|
query = response.json()["query"]
|
||||||
|
print(query)
|
||||||
|
chap_verse = re.search(r"(\d{1,3}:\d{1,3})", query)
|
||||||
|
try:
|
||||||
|
extract = chap_verse.group(0)
|
||||||
|
except AttributeError as a:
|
||||||
|
print(a)
|
||||||
|
|
||||||
|
distance = lev(extract, curr_time)
|
||||||
|
if "0" in curr_time and not curr_time.endswith("0"):
|
||||||
|
curr_time = curr_time.replace("0", "")
|
||||||
|
print(curr_time)
|
||||||
|
|
||||||
|
if distance < 1:
|
||||||
|
print(f"{extract} is equal to {curr_time}!")
|
||||||
|
break
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def parse_verse():
|
||||||
|
response = get_verse()
|
||||||
|
passage = response.json()["passages"]
|
||||||
|
verse = response.json()["query"]
|
||||||
|
pass_extract = re.split(r"\[\d{1,3}\]", str(passage[0]))
|
||||||
|
simplified = re.sub(r"(^\s|\s{2,}|\s\(ESV\)$)", "", pass_extract[1])
|
||||||
|
simplified2 = re.sub(r"(\\n|,[a-zA-Z])", " ", simplified)
|
||||||
|
if "(1)Footnotes" in simplified2:
|
||||||
|
new_list = simplified2.split("(1)Footnotes")[0]
|
||||||
|
print(new_list)
|
||||||
|
else:
|
||||||
|
new_list = simplified2
|
||||||
|
|
||||||
|
double_close = new_list.count('”')
|
||||||
|
double_open = new_list.count('“')
|
||||||
|
single_open = new_list.count("'")
|
||||||
|
single_open = new_list.count("‘")
|
||||||
|
single_closed = new_list.count('’')
|
||||||
|
char_tuple = (double_open, double_close, single_open, single_closed)
|
||||||
|
if char_tuple == (1, 0, 0, 0):
|
||||||
|
new_list += '”'
|
||||||
|
elif char_tuple == (1, 1, 1, 0):
|
||||||
|
new_list += '’'
|
||||||
|
elif char_tuple == (1, 0, 1, 0):
|
||||||
|
new_list += '”’'
|
||||||
|
print(new_list)
|
||||||
|
print(verse)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parse_verse()
|
||||||
Reference in New Issue
Block a user