Initial Commit

This commit is contained in:
2017-04-18 22:49:47 -04:00
commit 2e2f3748d4
64 changed files with 682 additions and 0 deletions

151
.gitignore vendored Normal file
View File

@@ -0,0 +1,151 @@
# Created by .ignore support plugin (hsz.mobi)
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# dotenv
.env
# virtualenv
.venv
venv/
ENV/
# Spyder project settings
.spyderproject
# Rope project settings
.ropeproject
.idea/
bells.py
clock.json
countdown.py
events.txt
panel.py
panel_script.py
requirements.txt
res/
weatherclock.py
wxget.py

91
bells.py Executable file
View File

@@ -0,0 +1,91 @@
#!/usr/bin/env python
from subprocess import Popen
from pydub import AudioSegment
bell1path = "res/wav/bellhit1.wav"
bell2path = "res/wav/bellhit2.wav"
bell3path = "res/wav/bellhit3.wav"
bell4path = "res/wav/bellhit4.wav"
bell5path = bell4path
g = AudioSegment.from_file(bell1path)
e = AudioSegment.from_file(bell2path)
f = AudioSegment.from_file(bell3path)
b = AudioSegment.from_file(bell4path)
e3 = AudioSegment.from_file(bell5path)
try:
horn = AudioSegment.from_file("/home/pi/sound/traphorn.wav")
except FileNotFoundError:
horn = AudioSegment.empty()
phrase1 = [(g, .25), (f, .25), (e, .25), (b, .50)]
phrase2 = [(e, .25), (g, .25), (f, .25), (b, .50)]
phrase3 = [(e, .25), (f, .25), (g, .25), (e, .50)]
phrase4 = [(g, .25), (e, .25), (f, .25), (b, .50)]
phrase5 = [(b, .25), (f, .25), (g, .25), (e, .50)]
rest = [(AudioSegment.empty(), .50)]
strike = [(e3, .75)]
trap_horn = [(horn, 0)]
whole_time = 3000
def chime_length(chime):
return sum(c[1] for c in chime) * whole_time + len(chime[-1][0])
def play(audio):
audio.export('/tmp/clock_chime.wav', 'wav')
Popen(['aplay', '/tmp/clock_chime.wav', '-q'])
def play_phrase(phrase):
audio = AudioSegment.silent(chime_length(phrase))
time = 0
for a, t in phrase:
audio = audio.overlay(a, int(time))
time += t * whole_time
play(audio)
last_chime = None
def play_chime(hour, minute):
global last_chime
this_chime = hour, minute
if last_chime == this_chime:
return
last_chime = this_chime
chime = []
if hour == 16 and minute == 20:
chime = trap_horn
if minute == 15:
chime = phrase1
if minute == 30:
chime = phrase2 + phrase3
if minute == 45:
chime = phrase4 + phrase5 + phrase1
if minute == 0:
if hour > 12:
hour -= 12
if hour == 0:
hour = 12
chime = phrase2 + phrase3 + phrase4 + phrase5 + rest + strike * hour
if not chime:
return
play_phrase(chime)
if __name__ == '__main__':
play_chime(2, 00)

39
clock.json Normal file
View File

@@ -0,0 +1,39 @@
[
{
"surf": "panel_script::time",
"anchor": "0.5,0.5"
},
{
"surf": "500 500",
"anchor": "0.5,0.5",
"position": "0 250",
"children": [
{
"surf": "panel_script::date",
"position": "0 -60",
"anchor": ".5 .5"
},
{
"surf": "panel_script::icon",
"position": "0 80",
"anchor": ".5 .5",
"pivot": "1 .5"
},
{
"surf": "panel_script::temperature",
"position": "0 80",
"anchor": ".5 .5",
"pivot": "0 .5"
}
]
},
{
"surf": "1280 100",
"children": [
{
"surf": "panel_script::header",
"anchor": ".5 .5"
}
]
}
]

55
countdown.py Executable file
View File

@@ -0,0 +1,55 @@
#!/usr/bin/python3
import datetime
import os
import timestring
class Event(object):
def __init__(self, date, label):
self.label = label
self.date = date
@property
def time(self):
r = timestring.Date(self.date).date - timestring.now().date # type: datetime.timedelta
s = int(r.seconds)
m, s = s // 60, s % 60
h, m = m // 60, m % 60
d, h = r.days, h % 25
return {'days': d, 'hours': h, 'minutes': m, 'seconds': s, 'label': self.label}
def __format__(self, format_spec):
return format_spec.format(**self.time)
def __str__(self):
return format(self, '{days}d {hours}h {minutes}m {seconds}s to {label}')
__events = []
__event_load = 0
def update_events(force=False):
global __events, __event_load
event_time = os.stat('events.txt').st_mtime_ns
if force or __event_load != event_time:
with open('events.txt') as f:
etext = [tuple(i.strip() for i in l.split('|')) for l in f.readlines() if
l.strip() and not l.startswith('#')]
__events = [Event(date, lbl) for lbl, date in etext]
__event_load = event_time
def get_events(force_update=False):
update_events(force_update)
return __events
if __name__ == '__main__':
print(timestring.now())
for e in get_events():
print(e)

8
events.txt Executable file
View File

@@ -0,0 +1,8 @@
# datetime | event name
# space before and after | omitted
Solar Eclipse | 8/21/2017 2:46:22PM
Untrump | 1/20/2021 9:00AM
Move-out | 5/12/2017 noon
# Dinner Tomorrow | Tomorrow 6:30pm

141
panel.py Normal file
View File

@@ -0,0 +1,141 @@
import importlib
import json
import re
import pygame
class Panel(object):
def __init__(self, surf, position=None, anchor=None, pivot=None, children=None):
if isinstance(surf, tuple) or not surf:
self.__surf = pygame.Surface(surf or (0, 0), flags=pygame.SRCALPHA)
else:
self.__surf = surf
self.position = position or (0, 0)
self.anchor = anchor or (0, 0)
self.pivot = pivot or self.anchor
self.children = children or []
@property
def size(self):
surf = self.__surf
if callable(surf):
surf = surf()
if isinstance(surf, Panel):
return surf.size
return surf.get_width(), surf.get_height()
@property
def surf(self):
surf = self.__surf
if callable(surf):
surf = surf()
elif isinstance(surf, Panel):
surf = surf.surf
else:
surf = surf.copy()
for child in self.children:
c_surf = child
position = (0, 0)
if isinstance(child, Panel):
c_surf = child.surf
position = tuple(int(a * s - v * c + p) for a, v, s, c, p in
zip(child.anchor, child.pivot, self.size, child.size, child.position))
if callable(c_surf):
c_surf = c_surf()
if isinstance(c_surf, Panel):
c_surf=c_surf.surf
surf.blit(c_surf, position)
return surf
old_modules = {}
def load(file):
loaded_modules = {}
def ordered_pair(s, dtype=float):
if not s:
return None
try:
op = tuple(dtype(c) for c in (re.split(r'\s*[,x\s]\s*', s)))
except ValueError:
return None
if len(op) != 2:
return None
return op
def script(s):
if not s:
return None
pair = re.split(r'::', s)
if len(pair) != 2:
return None
mod, func = pair
if mod not in loaded_modules:
if mod in old_modules:
importlib.reload(old_modules[mod])
try:
loaded_modules[mod] = importlib.import_module(mod)
except ModuleNotFoundError:
return None
mod = loaded_modules[mod]
try:
func = getattr(mod, func)
except AttributeError:
return None
return func
def from_dict(d: dict):
surf = d.get('surf', None)
position = d.get('position', None)
anchor = d.get('anchor', None)
pivot = d.get('pivot', None)
children = d.get('children', [])
surf = ordered_pair(surf, int) or script(surf)
position = ordered_pair(position, int)
anchor = ordered_pair(anchor)
pivot = ordered_pair(pivot)
children = [from_dict(d) for d in children]
return Panel(surf, position, anchor, pivot, children)
with open(file) as f:
j = json.load(f)
if isinstance(j, list):
ls = [from_dict(d) for d in j]
else:
ls = [from_dict(j)]
old_modules.clear()
for k, v in loaded_modules.items():
old_modules[k] = v
return ls
if __name__ == '__main__':
print(load('clock.json'))

59
panel_script.py Normal file
View File

@@ -0,0 +1,59 @@
from datetime import datetime
import pygame
from countdown import get_events
from panel import Panel
from wxget import get_weather
pygame.font.init()
WHITE = (255, 255, 255)
GRAY = (175, 175, 175)
BLACK = (0, 0, 0)
font = {
'RobotoSlab': {
30: pygame.font.Font("res/RobotoSlab-Regular.ttf", 30),
45: pygame.font.Font("res/RobotoSlab-Regular.ttf", 45),
60: pygame.font.Font("res/RobotoSlab-Regular.ttf", 60),
90: pygame.font.Font("res/RobotoSlab-Regular.ttf", 90),
360: pygame.font.Font("res/RobotoSlab-Regular.ttf", 360),
}
}
def time():
return font['RobotoSlab'][360].render(format(datetime.now(), '%I:%H').lstrip('0'), True, WHITE)
def date():
return font['RobotoSlab'][60].render(format(datetime.now(), '%A, %B %d'), True, GRAY)
def temperature():
f = font['RobotoSlab'][90]
return f.render('{0[current_conditions][temperature]}\u00b0'.format(get_weather()), True, WHITE)
def icon():
return pygame.image.load('res/png/12.png')
def header():
es = get_events()
def item(i):
f = font['RobotoSlab'][30]
e = es[i]
l = e.label
t = '{days}d {hours}h {minutes}m'.format(**e.time)
return Panel((1280 // len(es), 70), anchor=((i + 1) / (len(es) + 1), .5), children=[
Panel(f.render(l, True, WHITE), anchor=(.5, .5), pivot=(.5, .9)),
Panel(f.render(t, True, GRAY), anchor=(.5, .5), pivot=(.5, .1)),
])
return Panel((1280, 70), anchor=(.5, .5), children=[
item(i) for i in range(len(get_events()))
]).surf

4
requirements.txt Normal file
View File

@@ -0,0 +1,4 @@
pygame
pydub
timestring
https://launchpad.net/python-weather-api/trunk/0.3.8/+download/pywapi-0.3.8.tar.gz

BIN
res/RobotoSlab-Regular.ttf Executable file

Binary file not shown.

BIN
res/png/0.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
res/png/1.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
res/png/10.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
res/png/11.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
res/png/12.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
res/png/13.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
res/png/14.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
res/png/15.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
res/png/16.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
res/png/17.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
res/png/18.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
res/png/19.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
res/png/2.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
res/png/20.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
res/png/21.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
res/png/22.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
res/png/23.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
res/png/24.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
res/png/25.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
res/png/26.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
res/png/27.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
res/png/28.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
res/png/29.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
res/png/3.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
res/png/30.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
res/png/31.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
res/png/32.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
res/png/33.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
res/png/34.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
res/png/35.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
res/png/36.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

BIN
res/png/37.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
res/png/38.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
res/png/39.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
res/png/4.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
res/png/40.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
res/png/41.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
res/png/42.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
res/png/43.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
res/png/44.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
res/png/45.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
res/png/46.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
res/png/47.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
res/png/5.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
res/png/6.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
res/png/7.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
res/png/8.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
res/png/9.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
res/png/na.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
res/wav/bellhit1.wav Executable file

Binary file not shown.

BIN
res/wav/bellhit2.wav Executable file

Binary file not shown.

BIN
res/wav/bellhit3.wav Executable file

Binary file not shown.

BIN
res/wav/bellhit4.wav Executable file

Binary file not shown.

98
weatherclock.py Executable file
View File

@@ -0,0 +1,98 @@
#!/usr/bin/python3
import os
import pygame
import countdown
import panel
import wxget
CONFIG_PATH = 'clock.json'
pygame.init()
pygame.mouse.set_visible(False)
SIZE = (1280, 1024)
mode = pygame.display.set_mode(SIZE, 1)
pygame.display.set_caption("weather clock")
clock = pygame.time.Clock()
def game_loop():
panel_load = None
parent = None
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
pygame.quit()
quit()
if event.key == pygame.K_r:
wxget.update_weather(True)
countdown.update_events(True)
if event.type == pygame.QUIT:
pygame.quit()
quit()
panel_time = os.stat(CONFIG_PATH).st_mtime_ns
if panel_time != panel_load:
parent = panel.Panel(SIZE, children=panel.load(CONFIG_PATH))
panel_load = panel_time
wxget.update_weather()
mode.fill((0, 0, 0))
mode.blit(parent.surf, (0, 0))
pygame.display.update()
clock.tick(8)
# weather = wxget.__weather
#
# if iconName != weather['current_conditions']['icon']:
# iconName = str(int(weather['current_conditions']['icon']))
# icon = pygame.image.load('res/png/' + iconName + '.png')
#
# temp = '{0[current_conditions][temperature]}\u00b0{0[units][temperature]}'.format(wxget.get_weather())
# temp = font60.render(temp, True, WHITE)
# feels_like = 'Feels like ' + weather['current_conditions']['feels_like']
# feels_like = font45.render(feels_like, True, GRAY)
# lastupdate = Date(weather['current_conditions']['last_updated']).format('Last updated %I:%M')
# lastupdate = font30.render(lastupdate, True, GRAY)
#
# status = join_horizontal(icon, join_vertical(temp, feels_like, 10, TOP_LEFT, BOTTOM_LEFT), 0)
# status = join_vertical(status, lastupdate, 10)
#
# draw_surf(status, (0, -30), BOTTOM_CENTER, BOTTOM_CENTER)
#
# now = datetime.now()
# time = now.strftime('%I:%M')
# time = font360.render(time, True, WHITE)
# date = now.strftime('%A, %B %d')
# date = font60.render(date, True, GRAY)
#
# time = join_vertical(time, date, -100)
#
# draw_surf(time, ZERO, CENTER, (0.5, 0.4))
#
# event_surfs = []
# events = countdown.events
#
# for e in events:
# elbl = font45.render(e.label, True, WHITE)
# etime = font30.render(format(e, '{days}d {hours}h {minutes}m'), True, GRAY)
# event = join_vertical(elbl, etime, 5)
# event_surfs.append(event)
#
# timer = pygame.Surface((0, 0))
# max_w = max((e.get_width() for e in event_surfs))
# for es in event_surfs:
# timer = join_horizontal(timer, join_vertical(es, pygame.Surface((max_w, 1)), -1), 15)
#
# draw_surf(timer, ZERO, TOP_CENTER, TOP_CENTER)
#
# bells.play_chime(now.hour, now.minute)
game_loop()

36
wxget.py Executable file
View File

@@ -0,0 +1,36 @@
#!/usr/bin/python3
from datetime import datetime
import pywapi
import timestring
LOCATION = '28223'
UNITS = 'imperial'
UPDATE_INTERVAL = 60 * 20
__weather = {}
def update_weather(force=False):
global __weather
now = datetime.now()
last_check = timestring.Date(__weather.get('current_conditions', {}).get('last_checked', now)).date
interval = (now - last_check).total_seconds()
if force or interval <= 0 or interval > UPDATE_INTERVAL:
__weather = pywapi.get_weather_from_weather_com(LOCATION, UNITS)
__weather.setdefault('current_conditions', {}).setdefault('last_checked', str(now))
print('updated weather at', now)
def get_weather(force_update=False):
update_weather(force_update)
return __weather
if __name__ == '__main__':
while True:
print('{0[current_conditions][temperature]}\u00b0{0[units][temperature]}'.format(get_weather()))