Skip to content

Commit 9fcc279

Browse files
committed
[py/alarm-clock] Implement core alarm functions.
1 parent 7593ebe commit 9fcc279

File tree

7 files changed

+307
-0
lines changed

7 files changed

+307
-0
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
**/node_modules/
44
**/xdk/
55

6+
# Python
7+
__pycache__/
8+
*.py[cod]
9+
610
# C++
711
**/Debug/
812

alarm-clock/python/__init__.py

Whitespace-only changes.

alarm-clock/python/__main__.py

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
from __future__ import print_function, division
2+
3+
# imports
4+
from simplejson import load as load_json
5+
from importlib import import_module
6+
from datetime import datetime, timedelta
7+
from bottle import route, request, run, static_file
8+
from arrow import utcnow
9+
10+
from events import scheduler, emitter, ms
11+
12+
# load config.json data
13+
with open("config.json") as data:
14+
config = load_json(data)
15+
16+
if "kit" in config:
17+
print("loading board config \"", config["kit"], "\" from config.json", sep = "")
18+
board = import_module(config["kit"])
19+
else:
20+
print("loading default board config")
21+
board = import_module("grove")
22+
23+
# clock functions
24+
current_time = utcnow().replace(seconds=-1)
25+
alarm_time = utcnow().replace(days=-1)
26+
27+
def after(t, c):
28+
return t.floor("second") > c.floor("second")
29+
30+
def same(t, c):
31+
return t.floor("second") == c.floor("second")
32+
33+
def start_clock():
34+
global current_time
35+
time = utcnow()
36+
if after(time, current_time):
37+
board.write_message(str(time.format("h:mm:ss A")))
38+
if same(time, alarm_time):
39+
start_alarm()
40+
41+
current_time = time
42+
43+
scheduler.add_job(start_clock, "interval", seconds=ms(50))
44+
45+
def start_alarm():
46+
global alarm_time
47+
48+
alarm_state = {
49+
"tick": False
50+
}
51+
52+
board.start_buzzer()
53+
board.set_screen_background("red")
54+
55+
def alarm_actions():
56+
tick = alarm_state["tick"]
57+
board.set_screen_background("white" if tick else "red")
58+
if tick:
59+
board.stop_buzzer()
60+
else:
61+
board.start_buzzer()
62+
alarm_state["tick"] = not tick
63+
64+
print("schedule alarm_actions")
65+
alarm_interval = scheduler.add_job(alarm_actions, "interval", seconds=ms(250))
66+
67+
def stop_alarm():
68+
global alarm_time
69+
alarm_interval.remove()
70+
alarm_time = alarm_time.replace(days=1)
71+
board.set_screen_background("white")
72+
board.stop_buzzer()
73+
74+
emitter.once("button-pressed", stop_alarm)
75+
76+
# server setup
77+
alarm_duration = {
78+
"hour": 0,
79+
"minute": 0,
80+
"second": 0
81+
}
82+
83+
@route('/')
84+
def serve_index():
85+
global alarm_time
86+
global alarm_duration
87+
print("query:", request.query)
88+
if { "hour", "minute", "second" } <= set(request.query):
89+
duration = {
90+
"hour": int(request.query.get("hour")) or 0,
91+
"minute": int(request.query.get("minute")) or 0,
92+
"second": int(request.query.get("second")) or 0
93+
}
94+
alarm_time = utcnow().replace(hours=duration["hour"], minutes=duration["minute"], seconds=duration["second"])
95+
alarm_duration = duration
96+
print("alarm set to go off at", alarm_time.format("h:mm:ss A"))
97+
return static_file('index.html', root = "")
98+
99+
@route('/alarm.json')
100+
def serve_json():
101+
payload = {
102+
"hour": alarm_duration["hour"],
103+
"minute": alarm_duration["minute"],
104+
"second": alarm_duration["second"]
105+
}
106+
return payload
107+
108+
def main():
109+
board.init_hardware(config)
110+
board.stop_buzzer()
111+
start_clock()
112+
run(host = "0.0.0.0", port = 5000)
113+
114+
if __name__ == "__main__":
115+
main()

alarm-clock/python/config.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"kit": "grove"
3+
}

alarm-clock/python/events.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from event_emitter import EventEmitter, on
2+
from apscheduler.schedulers.background import BackgroundScheduler
3+
4+
emitter = EventEmitter()
5+
scheduler = BackgroundScheduler()
6+
7+
def ms(ms):
8+
9+
"""
10+
Converts milliseconds to seconds
11+
"""
12+
13+
return ms * 0.001
14+
15+
scheduler.start()

alarm-clock/python/grove.py

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
from __future__ import print_function, division
2+
3+
from math import floor
4+
5+
from pyupm_buzzer import Buzzer
6+
from pyupm_i2clcd import Jhd1313m1
7+
from pyupm_grove import GroveButton, GroveRotary
8+
9+
from mraa import addSubplatform, GENERIC_FIRMATA
10+
11+
from events import scheduler, emitter, ms
12+
13+
# pin mappings
14+
buzzer_pin = 6
15+
button_pin = 5
16+
rotary_pin = 0
17+
i2c_bus = 6
18+
19+
# hardware functions
20+
def start_buzzer():
21+
buzzer.setVolume(0.5)
22+
buzzer.playSound(2600, 0)
23+
24+
def stop_buzzer():
25+
buzzer.stopSound()
26+
buzzer.stopSound()
27+
28+
def write_message(message, line=0):
29+
message = message.ljust(16)
30+
screen.setCursor(line, 0)
31+
screen.write(message)
32+
33+
def set_screen_background(color):
34+
colors = {
35+
"red": lambda: screen.setColor(255, 0, 0),
36+
"white": lambda: screen.setColor(255, 255, 255)
37+
}
38+
colors.get(color, colors["white"])()
39+
40+
# setup hardware event dispatch
41+
def hardware_event_loop():
42+
global prev_button
43+
emitter.emit("rotary", value=rotary.abs_value())
44+
pressed = button.value()
45+
prev_button = 0
46+
47+
if pressed and not prev_button:
48+
emitter.emit("button-pressed")
49+
50+
if not pressed and prev_button:
51+
emitter.emit("button-released")
52+
53+
prev_button = pressed
54+
55+
scheduler.add_job(hardware_event_loop, "interval", seconds=ms(250))
56+
57+
def brightness_adjustment(value):
58+
start = 0
59+
end = 1020
60+
value = int(floor((value - start) / end * 255))
61+
value = 0 if value < 0 else 255 if value > 255 else value
62+
screen.setColor(value, value, value)
63+
64+
emitter.on("rotary", brightness_adjustment)
65+
66+
# hardware init
67+
def init_hardware(config):
68+
global buzzer_pin
69+
global button_pin
70+
global rotary_pin
71+
global i2c_bus
72+
73+
global buzzer
74+
global button
75+
global rotary
76+
global screen
77+
78+
if "platform" in config and config["platform"] == "firmata":
79+
addSubplatform("firmata", "/dev/ttyACM0")
80+
buzzer_pin += 512
81+
button_pin += 512
82+
rotary_pin += 512
83+
i2c_bus += 512
84+
85+
buzzer = Buzzer(buzzer_pin)
86+
button = GroveButton(button_pin)
87+
rotary = GroveRotary(rotary_pin)
88+
screen = Jhd1313m1(i2c_bus, 0x3E, 0x62)

alarm-clock/python/index.html

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<title>Alarm Clock</title>
5+
<style>
6+
</style>
7+
</head>
8+
<body>
9+
<h1>Alarm Clock</h1>
10+
11+
<form>
12+
<label for="hour">Hour</label>
13+
<select id="hour" name="hour"></select>
14+
15+
<label for="minute">Minute</label>
16+
<select id="minute" name="minute"></select>
17+
18+
<label for="second">Second</label>
19+
<select id="second" name="second"></select>
20+
21+
<input type="submit" value="Set Alarm">
22+
</form>
23+
24+
<script>
25+
// use JS to populate form drop-downs
26+
var hour = document.getElementById("hour"),
27+
minute = document.getElementById("minute"),
28+
second = document.getElementById("second");
29+
30+
function option(num) {
31+
var opt = document.createElement("option");
32+
opt.setAttribute("value", num);
33+
opt.text = num.toString();
34+
return opt;
35+
}
36+
37+
function range(start, end) {
38+
var arr = [start];
39+
40+
start++;
41+
42+
while(start <= end) {
43+
arr.push(start);
44+
start++;
45+
}
46+
47+
return arr;
48+
}
49+
50+
function setAlarm(req) {
51+
var json;
52+
53+
try {
54+
json = JSON.parse(this.response);
55+
} catch (e) {
56+
return; // unable to parse JSON, something went wrong
57+
}
58+
59+
hour.value = json.hour;
60+
minute.value = json.minute;
61+
second.value = json.second;
62+
}
63+
64+
function fetch() {
65+
var req = new XMLHttpRequest();
66+
req.addEventListener("load", setAlarm);
67+
req.open("get", "/alarm.json", true);
68+
req.send();
69+
}
70+
71+
function main() {
72+
range(0, 23).map(option).forEach(function(opt) { hour.add(opt); });
73+
range(0, 59).map(option).forEach(function(opt) { minute.add(opt); });
74+
range(0, 59).map(option).forEach(function(opt) { second.add(opt); });
75+
76+
fetch();
77+
}
78+
79+
main();
80+
</script>
81+
</body>
82+
</html>

0 commit comments

Comments
 (0)