Initial Commit
151
.gitignore
vendored
Normal 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
BIN
res/png/0.png
Executable file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
res/png/1.png
Executable file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
res/png/10.png
Executable file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
res/png/11.png
Executable file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
res/png/12.png
Executable file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
res/png/13.png
Executable file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
res/png/14.png
Executable file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
res/png/15.png
Executable file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
res/png/16.png
Executable file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
res/png/17.png
Executable file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
res/png/18.png
Executable file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
res/png/19.png
Executable file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
res/png/2.png
Executable file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
res/png/20.png
Executable file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
res/png/21.png
Executable file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
res/png/22.png
Executable file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
res/png/23.png
Executable file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
res/png/24.png
Executable file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
res/png/25.png
Executable file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
res/png/26.png
Executable file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
res/png/27.png
Executable file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
res/png/28.png
Executable file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
res/png/29.png
Executable file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
res/png/3.png
Executable file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
res/png/30.png
Executable file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
res/png/31.png
Executable file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
res/png/32.png
Executable file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
res/png/33.png
Executable file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
res/png/34.png
Executable file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
res/png/35.png
Executable file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
res/png/36.png
Executable file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
res/png/37.png
Executable file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
res/png/38.png
Executable file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
res/png/39.png
Executable file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
res/png/4.png
Executable file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
res/png/40.png
Executable file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
res/png/41.png
Executable file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
res/png/42.png
Executable file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
res/png/43.png
Executable file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
res/png/44.png
Executable file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
res/png/45.png
Executable file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
res/png/46.png
Executable file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
res/png/47.png
Executable file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
res/png/5.png
Executable file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
res/png/6.png
Executable file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
res/png/7.png
Executable file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
res/png/8.png
Executable file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
res/png/9.png
Executable file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
res/png/na.png
Executable file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
res/wav/bellhit1.wav
Executable file
BIN
res/wav/bellhit2.wav
Executable file
BIN
res/wav/bellhit3.wav
Executable file
BIN
res/wav/bellhit4.wav
Executable file
98
weatherclock.py
Executable 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
@@ -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()))
|
||||||