GoodMorningSunshine/clock.py

828 lines
32 KiB
Python

# Many thanks to https://stackoverflow.com/questions/18923321/making-a-clock-in-kivy
import collections
import datetime
import math
import sys
import traceback
import copy
import os
def is_arm():
if (os.uname()[4][:3] == 'arm') or (os.uname()[4][:7] == 'aarch64'):
return True
else:
return False
if is_arm():
# Do not forget to create udev rule:
# echo 'SUBSYSTEM=="backlight",RUN+="/bin/chmod 666 /sys/class/backlight/%k/brightness /sys/class/backlight/%k/bl_power"' | sudo tee -a /etc/udev/rules.d/backlight-permissions.rules
from rpi_backlight import Backlight
from kivy.config import Config
#Config.set('graphics', 'width', '800')
#Config.set('graphics', 'height', '480')
Config.set('graphics', 'width', '667')
Config.set('graphics', 'height', '400')
Config.set('graphics', 'maxfps', '60')
from kivy.app import App
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.uix.slider import Slider
from kivy.uix.button import Button
from kivy.clock import Clock
from kivy.lang import Builder
from kivy.graphics import Color, Line, Rectangle
import pulsectl
import vlc
Builder.load_string('''
<MyClockWidget>:
on_pos: self.update_display()
on_size: self.update_display()
FloatLayout
id: face
size_hint: None, None
pos_hint: {"center_x":0.5, "center_y":0.5}
size: 0.9*min(root.size), 0.9*min(root.size)
canvas:
Color:
rgb: 0.1, 0.1, 0.1
Ellipse:
size: self.size
pos: self.pos
FloatLayout
id: hands
size_hint: None, None
pos_hint: {"center_x":0.5, "center_y":0.5}
size: 0.9*min(root.size), 0.9*min(root.size)
FloatLayout
id: set_alarm_button
size_hint: None, None
pos_hint: {"center_x":0.9, "center_y":0.1}
size: 0.1*min(root.size), 0.1*min(root.size)
FloatLayout
id: settings_button
size_hint: None, None
pos_hint: {"center_x":0.9, "center_y":0.9}
size: 0.1*310/560*min(root.size), 0.1*310/560*min(root.size)
FloatLayout
id: settings_menu
size_hint: None, None
pos_hint: {"center_x":0.5, "center_y":0.5}
size: 0.7*min(root.size), 0.7*min(root.size)
canvas:
Color:
rgb: 0.1, 0.1, 0.1
Rectangle:
size: self.size
pos: self.pos
GridLayout:
rows: 8
cols: 2
pos_hint: {"center_x":0.5, "center_y":0.5}
Label:
text: "Volume"
halign: "left"
valign: "middle"
text_size: self.size
Slider:
id: volume_slider
min: 0
max: 20
value: 15
on_value: root.volume_slider_value(*args)
value_track: True
value_track_color: [1, 0, 0, 1]
Label:
text:"Wake up sound"
halign: "left"
valign: "middle"
text_size: self.size
Button:
id: settings_menu_wake_up_sound_select_button
on_press: root.settings_menu_wake_up_sound_select_button_cb()
text: str(app.alarm_settings.sound_selected)
Label:
text:"Wake up brightness"
halign: "left"
valign: "middle"
text_size: self.size
Slider:
id: wake_up_bightness_slider
min: 0
max: 20
value: 20
on_value: root.wake_up_brightness_slider_value(*args)
value_track: True
value_track_color: [1, 0, 0, 1]
Label:
text:"Reading light brightness"
halign: "left"
valign: "middle"
text_size: self.size
Slider:
id: reading_light_brightness_slider
min: 0
max: 20
value: 1
on_value: root.reading_light_brightness_slider_value(*args)
value_track: True
value_track_color: [1, 0, 0, 1]
Label:
text:"Display brightness"
halign: "left"
valign: "middle"
text_size: self.size
Slider:
id: display_brightness_slider
min: 0
max: 20
value: 10
on_value: root.display_brightness_slider_value(*args)
value_track: True
value_track_color: [1, 0, 0, 1]
FloatLayout
id: settings_menu_wake_up_sound
size_hint: None, None
pos_hint: {"center_x":0.5, "center_y":0.5}
size: 0.7*min(root.size), 0.7*min(root.size)
canvas:
Color:
rgb: 0.1, 0.1, 0.1
Rectangle:
size: self.size
pos: self.pos
BoxLayout:
orientation: "vertical"
pos_hint: {"center_x":0.5, "center_y":0.5}
Label:
text: "Select wake up sound"
GridLayout:
cols: 2
CheckBox:
group: "settings_menu_wake_up_sound"
on_active: root.settings_menu_wake_up_sound_cb(self, self.active, "Birds")
id: settings_menu_wake_up_sound_Birds
Label:
text: "Birds"
halign: "left"
valign: "middle"
text_size: self.size
active: True
CheckBox:
group: "settings_menu_wake_up_sound"
on_active: root.settings_menu_wake_up_sound_cb(self, self.active, "NPO Radio 1")
id: settings_menu_wake_up_sound_NpoRadio1
Label:
text: "NPO Radio 1"
halign: "left"
valign: "middle"
text_size: self.size
Button:
id: settings_menu_wake_up_sound_Ok_button
on_press: root.settings_menu_wake_up_sound_Ok_button_cb()
text: "Ok"
''')
Position = collections.namedtuple('Position', 'x y')
class AlarmSettings():
alarm_time = datetime.datetime(2022, 12, 10, 7, 30, 0, 0)
alarm_activated = False
alarm_playing = False
alarm_modified = False
led_color = [0, 0, 0]
# sound_selected = "NPO Radio 1"
# sound_source = "https://icecast.omroep.nl/radio1-bb-mp3"
sound_selected = "Birds"
sound_source = "Woodpecker Chirps - QuickSounds.com.mp3"
seconds_to_sunrise = 30 * 60 # 30 minutes
volume = 15
wake_up_brightness = 20
reading_light_brightness = 1
display_brightness = 10
class Touch():
def __init__(self, x=None):
if x is None:
self.pos = []
self.spos = []
self.is_empty = True
self.angle = 0
else:
self.pos = [i for i in x.pos]
self.spos = [i for i in x.spos]
self.is_empty = False
x = self.spos[0] - 0.5
y = self.spos[1] - 0.5
self.angle = math.atan2(y, x) / math.pi; # angle is between -1 and 1
def __repr__(self):
return ".is_empty: " + str(self.is_empty) + ", .pos: " + str(self.pos) + ", .spos: " + str(self.spos)
def copy(self, x):
self = Touch(x)
def clear(self):
self = Touch()
class MyClockWidget(FloatLayout):
grabbed = ""
face_numbers = []
# if not grabbed, set_alarm_timeout_counter is incremented at every update call;
# this is used to blink the hands at 1 Hz when setting the alarm and releasing the hand
set_alarm_timeout_counter = 0
seconds_to_next_alarm = 0
led_color = [0, 0, 0]
# view can be one of the following strings:
# - "clock"
# - "set_alarm"
# - "settings_menu"
# - "settings_menu_wake_up_sound"
view = "clock"
# we need a dirty hack to sensure that radio button is always in sync with selected setting
settings_menu_wake_up_sound_select_button_cb_hack = False
# defer drawing to improve application speed: if list of draw calls for
# this frame is identical to list of previous frame then nothing needs to
# be drawed
draw_list_last_frame = []
draw_list_curr_frame = []
touch_prev = Touch()
vlc = vlc.Instance()
player = vlc.media_player_new()
is_arm = is_arm()
if is_arm:
backlight = Backlight()
else:
backlight = None
def play_sound(self, source):
alarm_settings = App.get_running_app().alarm_settings
if (self.player.get_state() == vlc.State.NothingSpecial) or \
(self.player.get_state() == vlc.State.Paused) or \
(self.player.get_state() == vlc.State.Stopped) or \
(self.player.get_state() == vlc.State.Ended) or \
(self.player.get_state() == vlc.State.Error):
print("beep beep! " + source)
media = self.vlc.media_new(source)
self.player.set_media(media)
self.player.play()
alarm_settings.alarm_playing = True
def stop_sound(self):
self.player.stop()
alarm_settings = App.get_running_app().alarm_settings
alarm_settings.alarm_playing = False
def set_backlight(self, x):
offset = 3
# 20 steps to from 1 to 100 - offset
c = (100 - offset)**(1/20)
y = c**x + offset
z = max(1, round(y))
self.backlight.brightness = z
def hide_widget(self, widget, hide=True):
if hasattr(widget, 'saved_attrs'):
if not hide:
widget.height, widget.size_hint_y, widget.opacity, widget.disabled = widget.saved_attrs
del widget.saved_attrs
elif hide:
widget.saved_attrs = widget.height, widget.size_hint_y, widget.opacity, widget.disabled
widget.height, widget.size_hint_y, widget.opacity, widget.disabled = 0, None, 0, True
def draw_face(self):
"""
Add number labels when added in widget hierarchy
"""
alarm_settings = App.get_running_app().alarm_settings
if self.view == "set_alarm":
time = alarm_settings.alarm_time
else:
time = datetime.datetime.now()
for i in range(1, 13):
if time.hour < 12:
offset = 0
else:
offset = 12
self.face_numbers.append(Label(
text=str(i + offset),
pos_hint={
# pos_hint is a fraction in range (0, 1)
"center_x": 0.5 + 0.45*math.sin(2 * math.pi * i/12),
"center_y": 0.5 + 0.45*math.cos(2 * math.pi * i/12),
}
))
self.ids["face"].add_widget(self.face_numbers[i - 1])
def update_face(self):
alarm_settings = App.get_running_app().alarm_settings
if self.view == "set_alarm":
time = alarm_settings.alarm_time
else:
time = datetime.datetime.now()
for i in range(0, 12):
if time.hour < 12:
offset = 0
else:
offset = 12
self.face_numbers[i].text=str(i + 1 + offset)
def on_parent(self, myclock, parent):
self.draw_face()
def position_on_clock(self, fraction, length):
"""
Calculate position in the clock using trygonometric functions
"""
center_x = self.size[0]/2
center_y = self.size[1]/2
return Position(
center_x + round(length * math.sin(2 * math.pi * fraction)),
center_y + round(length * math.cos(2 * math.pi * fraction)),
)
def update_set_alarm_button(self):
alarm_settings = App.get_running_app().alarm_settings
if (self.view == "set_alarm") or alarm_settings.alarm_activated:
source = 'alarm_on.png'
rgb = [0.9, 0.0, 0.0]
else:
source = 'alarm_off.png'
rgb = [1.0, 1.0, 1.0]
set_alarm_button = self.ids["set_alarm_button"]
self.draw_list_curr_frame.append(["canvas.clear()", set_alarm_button])
with set_alarm_button.canvas:
self.draw_list_curr_frame.append(["Color", set_alarm_button.canvas, rgb[0], rgb[1], rgb[2]])
# Rectangle(size=set_alarm_button.size, pos=set_alarm_button.pos, source=source)
self.draw_list_curr_frame.append(["Rectangle", set_alarm_button.canvas, set_alarm_button.size, set_alarm_button.pos, source])
def update_settings_button(self):
alarm_settings = App.get_running_app().alarm_settings
if (self.view.startswith("settings_menu")):
source = 'settings_visible.png'
rgb = [0.9, 0.0, 0.0]
else:
source = 'settings_not_visible.png'
rgb = [1.0, 1.0, 1.0]
settings_button = self.ids["settings_button"]
self.draw_list_curr_frame.append(["canvas.clear()", settings_button])
with settings_button.canvas:
self.draw_list_curr_frame.append(["Color", settings_button.canvas, rgb[0], rgb[1], rgb[2]])
# Rectangle(size=settings_button.size, pos=settings_button.pos, source=source)
self.draw_list_curr_frame.append(["Rectangle", settings_button.canvas, settings_button.size, settings_button.pos, source])
def sun_rise(self):
alarm_settings = App.get_running_app().alarm_settings
# to do: calculate brightness and color according to sun rise instead of linear increment
intensity = math.floor((1.0 - self.seconds_to_next_alarm / alarm_settings.seconds_to_sunrise) * 256.0)
if intensity < 0:
intensity = 0
led_color = [0, 0, 0]
for i in range(3):
led_color[i] = intensity
if self.led_color != led_color:
self.led_color = led_color
def check_sun_rise(self):
alarm_settings = App.get_running_app().alarm_settings
if alarm_settings.alarm_activated == False:
return
if self.seconds_to_next_alarm < alarm_settings.seconds_to_sunrise:
self.sun_rise()
def check_play_sound(self):
alarm_settings = App.get_running_app().alarm_settings
if alarm_settings.alarm_activated == False:
return
if (self.seconds_to_next_alarm < 0.1) or (alarm_settings.alarm_playing == True):
if alarm_settings.sound_source != "":
self.play_sound(alarm_settings.sound_source)
def calc_seconds_to_next_alarm(self):
alarm_settings = App.get_running_app().alarm_settings
if alarm_settings.alarm_activated == False:
return
# Make sure alarm_time is in the future but not more than 24 h from now
now = datetime.datetime.now()
d = alarm_settings.alarm_time - now
alarm_settings.alarm_time -= datetime.timedelta(days=d.days)
# Calculate number of seconds until next alarm
d = alarm_settings.alarm_time - now
self.seconds_to_next_alarm = d.days * 24 * 3600 + d.seconds + d.microseconds / 1000000.0
def check_alarm(self):
self.calc_seconds_to_next_alarm()
self.check_sun_rise()
self.check_play_sound()
def update_clock(self):
self.draw_list_curr_frame.append(["self.hide_widget", self.ids["face"], False])
self.draw_list_curr_frame.append(["self.hide_widget", self.ids["hands"], False])
alarm_settings = App.get_running_app().alarm_settings
if self.view == "set_alarm":
time = alarm_settings.alarm_time
else:
time = datetime.datetime.now()
hands = self.ids["hands"]
seconds_hand = self.position_on_clock(time.second/60, length=0.45*hands.size[0])
minutes_hand = self.position_on_clock(time.minute/60+time.second/3600, length=0.40*hands.size[0])
hours_hand = self.position_on_clock(time.hour/12 + time.minute/720, length=0.35*hands.size[0])
self.update_face()
#hands.canvas.clear()
self.draw_list_curr_frame.append(["canvas.clear()", hands])
update_rate = App.get_running_app().update_rate
with hands.canvas:
if self.view == "set_alarm":
if self.grabbed != "" or self.set_alarm_timeout_counter < 1 * update_rate or \
self.set_alarm_timeout_counter % update_rate <= update_rate / 2 or alarm_settings.alarm_modified == False:
self.draw_list_curr_frame.append(["Color", hands.canvas, 0.9, 0.0, 0.0])
# Line(points=[hands.center_x, hands.center_y, hours_hand.x, hours_hand.y], width=3, cap="round")
self.draw_list_curr_frame.append(["Line", hands.canvas, [hands.center_x, hands.center_y, hours_hand.x, hours_hand.y], 3, "round"])
self.draw_list_curr_frame.append(["Color", hands.canvas, 0.8, 0.0, 0.0])
#Line(points=[hands.center_x, hands.center_y, minutes_hand.x, minutes_hand.y], width=2, cap="round")
self.draw_list_curr_frame.append(["Line", hands.canvas, [hands.center_x, hands.center_y, minutes_hand.x, minutes_hand.y], 2, "round"])
if self.grabbed == "":
self.set_alarm_timeout_counter += 1
if self.set_alarm_timeout_counter >= 4.5 * update_rate:
self.view = "clock"
self.set_alarm_timeout_counter = 0
if alarm_settings.alarm_modified:
alarm_settings.alarm_activated = True
alarm_settings.alarm_modified = False
else:
self.draw_list_curr_frame.append(["Color", hands.canvas, 0.9, 0.9, 0.9])
# Line(points=[hands.center_x, hands.center_y, hours_hand.x, hours_hand.y], width=3, cap="round")
self.draw_list_curr_frame.append(["Line", hands.canvas, [hands.center_x, hands.center_y, hours_hand.x, hours_hand.y], 3, "round"])
self.draw_list_curr_frame.append(["Color", hands.canvas, 0.8, 0.8, 0.8])
# Line(points=[hands.center_x, hands.center_y, minutes_hand.x, minutes_hand.y], width=2, cap="round")
self.draw_list_curr_frame.append(["Line", hands.canvas, [hands.center_x, hands.center_y, minutes_hand.x, minutes_hand.y], 2, "round"])
self.draw_list_curr_frame.append(["Color", hands.canvas, 0.7, 0.7, 0.7])
# Line(points=[hands.center_x, hands.center_y, seconds_hand.x, seconds_hand.y], width=1, cap="round")
self.draw_list_curr_frame.append(["Line", hands.canvas, [hands.center_x, hands.center_y, seconds_hand.x, seconds_hand.y], 1, "round"])
def update_settings(self):
if self.view == "settings_menu":
self.draw_list_curr_frame.append(["self.hide_widget", self.ids["settings_menu"], False])
elif self.view == "settings_menu_wake_up_sound":
self.draw_list_curr_frame.append(["self.hide_widget", self.ids["settings_menu_wake_up_sound"], False])
def draw_display(self):
if self.draw_list_curr_frame != self.draw_list_last_frame:
for i in self.draw_list_curr_frame:
if i[0] == "self.hide_widget":
self.hide_widget(i[1], i[2])
elif i[0] == "canvas.clear()":
i[1].canvas.clear()
elif i[0] == "Color":
with i[1]:
Color(i[2], i[3], i[4])
elif i[0] == "Rectangle":
with i[1]:
Rectangle(size=i[2], pos=i[3], source=i[4])
elif i[0] == "Line":
with i[1]:
Line(points=i[2], width=i[3], cap=i[4])
else:
print("Unknown draw command: " + i[0])
self.draw_list_last_frame = self.draw_list_curr_frame
self.draw_list_curr_frame = []
def update_display(self, *args):
self.check_alarm()
# Hide all dynamic widgets; will be enabled when updating respecive view
self.draw_list_curr_frame.append(["self.hide_widget", self.ids["face"], True])
self.draw_list_curr_frame.append(["self.hide_widget", self.ids["hands"], True])
self.draw_list_curr_frame.append(["self.hide_widget", self.ids["settings_menu"], True])
self.draw_list_curr_frame.append(["self.hide_widget", self.ids["settings_menu_wake_up_sound"], True])
self.update_set_alarm_button()
self.update_settings_button()
if self.view == "clock" or self.view == "set_alarm":
self.update_clock()
elif self.view.startswith("settings_menu"):
self.update_settings()
else:
print("unknown view: " + self.view)
self.draw_display()
def settings_menu_wake_up_sound_select_button_cb(self):
self.settings_menu_wake_up_sound_select_button_cb_hack = True
alarm_settings = App.get_running_app().alarm_settings
self.ids["settings_menu_wake_up_sound_Birds"].active = False
self.ids["settings_menu_wake_up_sound_NpoRadio1"].active = False
print("sound selected: " + alarm_settings.sound_selected)
if alarm_settings.sound_selected == "Birds":
self.ids["settings_menu_wake_up_sound_Birds"].active = True
elif alarm_settings.sound_selected == "NPO Radio 1":
self.ids["settings_menu_wake_up_sound_NpoRadio1"].active = True
self.view = "settings_menu_wake_up_sound"
def settings_menu_wake_up_sound_Ok_button_cb(self):
alarm_settings = App.get_running_app().alarm_settings
self.ids["settings_menu_wake_up_sound_select_button"].text = alarm_settings.sound_selected
self.view = "settings_menu"
def settings_menu_wake_up_sound_cb(self, instance, value, sound):
alarm_settings = App.get_running_app().alarm_settings
if self.settings_menu_wake_up_sound_select_button_cb_hack:
self.settings_menu_wake_up_sound_select_button_cb_hack = False
if not (alarm_settings.sound_selected == "" and sound != ""):
return
if value == True:
print("You selected " + sound)
else:
print("You deselected " + sound)
alarm_settings.sound_source = ""
alarm_settings.sound_selected = ""
if self.ids["settings_menu_wake_up_sound_Birds"].active:
alarm_settings.sound_selected = sound
alarm_settings.sound_source = "Woodpecker Chirps - QuickSounds.com.mp3"
elif self.ids["settings_menu_wake_up_sound_NpoRadio1"].active:
alarm_settings.sound_selected = sound
alarm_settings.sound_source = "https://icecast.omroep.nl/radio1-bb-mp3"
def volume_slider_value(self, *args):
alarm_settings = App.get_running_app().alarm_settings
alarm_settings.volume = int(args[1])
print("Volume changed to " + str(alarm_settings.volume))
with pulsectl.Pulse('volume-increaser') as pulse:
for sink in pulse.sink_list():
# Volume is usually in 0-1.0 range, with >1.0 being soft-boosted
old_vol = pulse.volume_get_all_chans(sink)
pulse.volume_set_all_chans(sink, alarm_settings.volume / 20.0)
new_vol = pulse.volume_get_all_chans(sink)
print("HW volume changed from " + str(old_vol) + " to " + str(new_vol))
def wake_up_brightness_slider_value(self, *args):
alarm_settings = App.get_running_app().alarm_settings
alarm_settings.wake_up_brightness = int(args[1])
print("Wake up brightness changed to " + str(alarm_settings.wake_up_brightness))
def reading_light_brightness_slider_value(self, *args):
alarm_settings = App.get_running_app().alarm_settings
alarm_settings.reading_light_brightness = int(args[1])
print("Reading light brightness changed to " + str(alarm_settings.reading_light_brightness))
def display_brightness_slider_value(self, *args):
alarm_settings = App.get_running_app().alarm_settings
alarm_settings.display_brightness = int(args[1])
if self.is_arm:
self.set_backlight(alarm_settings.display_brightness)
def on_alarm_button_pressed(self):
alarm_settings = App.get_running_app().alarm_settings
alarm_settings.alarm_modified = False
self.set_alarm_timeout_counter = 0
if self.view == "set_alarm":
self.view = "clock"
alarm_settings.alarm_activated = False
else:
self.view = "set_alarm"
alarm_settings.alarm_activated = True
def on_settings_button_pressed(self):
print("settings button pressed from view " + self.view)
if self.view != "settings_menu":
self.view = "settings_menu"
else:
self.view = "clock"
print("view updated to " + self.view)
def on_touch_up(self, touch):
self.grabbed = ""
alarm_settings = App.get_running_app().alarm_settings
if (self.view == "set_alarm") and (self.grabbed == "hour" or self.grabbed == "minute"):
self.set_alarm_timeout_counter = 0
super(MyClockWidget, self).on_touch_up(touch)
def on_touch_move(self, touch):
if self.grabbed == "":
return
alarm_settings = App.get_running_app().alarm_settings
self.alarm_set_timeout = 0
touch_curr = Touch(touch)
# Ugly workaround for issue with Kivy and Raspberry Pi 3 + touch screen
tol = 0.5 # 0.5 is equal to 90 degrees
inc = 1 # 1 is equal to 180 degrees
if (self.touch_prev.is_empty == False) and (touch_curr.angle - self.touch_prev.angle >= tol):
while touch_curr.angle - self.touch_prev.angle >= tol:
touch_curr.angle -= inc
elif (self.touch_prev.is_empty == False) and (touch_curr.angle - self.touch_prev.angle <= -tol):
while touch_curr.angle - self.touch_prev.angle <= -tol:
touch_curr.angle += inc
self.touch_prev = copy.deepcopy(touch_curr)
if self.grabbed == "minute":
alarm_settings.alarm_modified = True
self.set_alarm_timeout_counter = 0
minute = round(-touch_curr.angle * 30 + 15)
while minute < 0:
minute += 60
while minute >= 60:
minute -= 60
# Sometimes the hand is 30 minutes ahead / behind the place where the user touches the screen --> correct for this behavior
if (((minute - alarm_settings.alarm_time.minute) >= 15) and ((minute - alarm_settings.alarm_time.minute) <= 45)):
minute = minute - 30
elif (((minute - alarm_settings.alarm_time.minute) <= -15) and ((minute - alarm_settings.alarm_time.minute) >= -45)):
minute = minute + 30
while minute < 0:
minute += 60
while minute >= 60:
minute -= 60
# hour correction
hour = alarm_settings.alarm_time.hour
if alarm_settings.alarm_time.minute >= 55 and minute <= 5:
hour += 1
elif alarm_settings.alarm_time.minute <= 5 and minute >= 55:
hour -= 1
while hour < 0:
hour += 24
while hour >= 24:
hour -= 24
alarm_settings.alarm_time = datetime.datetime(alarm_settings.alarm_time.year, \
alarm_settings.alarm_time.month, alarm_settings.alarm_time.day, \
hour, minute, alarm_settings.alarm_time.second, 0)
elif self.grabbed == "hour":
alarm_settings.alarm_modified = True
self.set_alarm_timeout_counter = 0
hour = round(-touch_curr.angle * 6 + 3)
while hour < 0:
hour += 12
while hour >= 12:
hour -= 12
# Sometimes the hand is 6 hours ahead / behind the place where the user touches the screen --> correct for this behavior
if (((hour - alarm_settings.alarm_time.hour) >= 3) and ((hour - alarm_settings.alarm_time.hour) <= 9)):
hour = hour - 6
if (((hour - alarm_settings.alarm_time.hour) <= -3) and ((hour - alarm_settings.alarm_time.hour) >= -9)):
hour = hour + 6
while hour < 0:
hour += 12
while hour >= 12:
hour -= 12
if alarm_settings.alarm_time.hour >= 12:
hour += 12
# AM / PM correction
if alarm_settings.alarm_time.hour == 11 and hour == 0:
hour = 12
elif alarm_settings.alarm_time.hour == 23 and hour == 12:
hour = 0
elif alarm_settings.alarm_time.hour == 0 and hour == 11:
hour = 23
elif alarm_settings.alarm_time.hour == 12 and hour == 23:
hour = 11
minute = alarm_settings.alarm_time.minute
alarm_settings.alarm_time = datetime.datetime(alarm_settings.alarm_time.year, \
alarm_settings.alarm_time.month, alarm_settings.alarm_time.day, \
hour, alarm_settings.alarm_time.minute, alarm_settings.alarm_time.second, 0)
super(MyClockWidget, self).on_touch_move(touch)
def on_touch_down(self, touch):
alarm_settings = App.get_running_app().alarm_settings
time = alarm_settings.alarm_time
hands = self.ids["hands"]
minutes_hand = self.position_on_clock(time.minute/60+time.second/3600, length=0.40*hands.size[0])
hours_hand = self.position_on_clock(time.hour/12 + time.minute/720, length=0.35*hands.size[0])
if (0.85 <= touch.spos[0] <= 0.95) and (0.05 <= touch.spos[1] <= 0.15):
self.on_alarm_button_pressed()
elif (0.85 <= touch.spos[0] <= 0.95) and (0.85 <= touch.spos[1] <= 0.95):
self.on_settings_button_pressed()
elif self.view == "set_alarm":
self.set_alarm_timeout_counter = 0
if self.grabbed == "":
if (minutes_hand.x - 0.1 * self.size[0] <= touch.pos[0] <= minutes_hand.x + 0.1 * self.size[0]) and \
(minutes_hand.y - 0.1 * self.size[1] <= touch.pos[1] <= minutes_hand.y + 0.1 * self.size[1]):
self.grabbed = "minute"
self.touch_prev.clear()
elif (hours_hand.x - 0.1 * self.size[0] <= touch.pos[0] <= hours_hand.x + 0.1 * self.size[0]) and \
(hours_hand.y - 0.1 * self.size[1] <= touch.pos[1] <= hours_hand.y + 0.1 * self.size[1]):
self.grabbed = "hour"
self.touch_prev.clear()
else:
self.grabbed = ""
print("grabbed: " + self.grabbed)
elif self.view == "settings_menu":
pass
elif self.view == "clock":
self.stop_sound()
super(MyClockWidget, self).on_touch_down(touch)
class MyApp(App):
alarm_settings = AlarmSettings()
update_rate = 60.0
# apply volume setting
with pulsectl.Pulse('volume-increaser') as pulse:
for sink in pulse.sink_list():
# Volume is usually in 0-1.0 range, with >1.0 being soft-boosted
old_vol = pulse.volume_get_all_chans(sink)
pulse.volume_set_all_chans(sink, alarm_settings.volume / 20.0)
new_vol = pulse.volume_get_all_chans(sink)
print("HW volume changed from " + str(old_vol) + " to " + str(new_vol))
def build(self):
clock_widget = MyClockWidget()
update_rate = App.get_running_app().update_rate
# update initially, just after construction of the widget is complete
Clock.schedule_once(clock_widget.update_display, 0)
# then update at update_rate times per second
Clock.schedule_interval(clock_widget.update_display, 1.0/update_rate)
return clock_widget
def except_hook(type, value, tb):
return
if __name__ == '__main__':
# sys.excepthook = except_hook
MyApp().run()