Added smart alarm clock project code
This commit is contained in:
parent
caf810104d
commit
b16aa9c508
5 changed files with 14554 additions and 0 deletions
BIN
Smart_Alarm_Clock/alarm.wav
Normal file
BIN
Smart_Alarm_Clock/alarm.wav
Normal file
Binary file not shown.
116
Smart_Alarm_Clock/button.py
Normal file
116
Smart_Alarm_Clock/button.py
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import ssl
|
||||
import time
|
||||
import board
|
||||
import digitalio
|
||||
import socketpool
|
||||
import wifi
|
||||
import adafruit_minimqtt.adafruit_minimqtt as MQTT
|
||||
from adafruit_io.adafruit_io import IO_MQTT
|
||||
|
||||
led = digitalio.DigitalInOut(board.IO8)
|
||||
led.direction = digitalio.Direction.OUTPUT
|
||||
|
||||
btn1 = digitalio.DigitalInOut(board.IO9)
|
||||
btn1.direction = digitalio.Direction.INPUT
|
||||
btn1.pull = digitalio.Pull.DOWN
|
||||
|
||||
ALARM = None
|
||||
### WiFi ###
|
||||
|
||||
# Get wifi details and more from a secrets.py file
|
||||
try:
|
||||
from secrets import secrets
|
||||
except ImportError:
|
||||
print("WiFi secrets are kept in secrets.py, please add them there!")
|
||||
raise
|
||||
|
||||
print("Connecting to %s" % secrets["ssid"])
|
||||
wifi.radio.connect(secrets["ssid"], secrets["password"])
|
||||
print("Connected to %s!" % secrets["ssid"])
|
||||
|
||||
# Define callback functions which will be called when certain events happen.
|
||||
# pylint: disable=unused-argument
|
||||
def connected(client):
|
||||
# Connected function will be called when the client is connected to Adafruit IO.
|
||||
# This is a good place to subscribe to feed changes. The client parameter
|
||||
# passed to this function is the Adafruit IO MQTT client so you can make
|
||||
# calls against it easily.
|
||||
print("Connected to Adafruit IO!")
|
||||
client.subscribe("alarm-clock.alarm")
|
||||
|
||||
|
||||
def subscribe(client, userdata, topic, granted_qos):
|
||||
# This method is called when the client subscribes to a new feed.
|
||||
print("Subscribed to {0} with QOS level {1}".format(topic, granted_qos))
|
||||
|
||||
|
||||
def unsubscribe(client, userdata, topic, pid):
|
||||
# This method is called when the client unsubscribes from a feed.
|
||||
print("Unsubscribed from {0} with PID {1}".format(topic, pid))
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def disconnected(client):
|
||||
# Disconnected function will be called when the client disconnects.
|
||||
print("Disconnected from Adafruit IO!")
|
||||
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def message(client, feed_id, payload):
|
||||
# Message function will be called when a subscribed feed has a new value.
|
||||
# The feed_id parameter identifies the feed, and the payload parameter has
|
||||
# the new value.
|
||||
print("Feed {0} received new value: {1}".format(feed_id, payload))
|
||||
|
||||
|
||||
def on_alarm(client, feed_id, payload):
|
||||
global ALARM
|
||||
print(payload)
|
||||
ALARM = eval(payload)
|
||||
|
||||
|
||||
# Create a socket pool
|
||||
pool = socketpool.SocketPool(wifi.radio)
|
||||
|
||||
# Initialize a new MQTT Client object
|
||||
mqtt_client = MQTT.MQTT(
|
||||
broker="io.adafruit.com",
|
||||
username=secrets["aio_username"],
|
||||
password=secrets["aio_key"],
|
||||
socket_pool=pool,
|
||||
ssl_context=ssl.create_default_context(),
|
||||
)
|
||||
|
||||
# Initialize an Adafruit IO MQTT Client
|
||||
io = IO_MQTT(mqtt_client)
|
||||
|
||||
# Connect the callback methods defined above to Adafruit IO
|
||||
io.on_connect = connected
|
||||
io.on_disconnect = disconnected
|
||||
io.on_subscribe = subscribe
|
||||
io.on_unsubscribe = unsubscribe
|
||||
io.on_message = message
|
||||
|
||||
io.add_feed_callback("alarm-clock.alarm", on_alarm)
|
||||
|
||||
# Connect to Adafruit IO
|
||||
print("Connecting to Adafruit IO...")
|
||||
io.connect()
|
||||
|
||||
io.get("alarm-clock.alarm")
|
||||
|
||||
LAST = 0
|
||||
while True:
|
||||
io.loop()
|
||||
if ALARM and time.monotonic() - LAST >= 0.2:
|
||||
led.value = not led.value
|
||||
LAST = time.monotonic()
|
||||
if btn1.value:
|
||||
io.publish("alarm-clock.alarm", "False")
|
||||
led.value = False
|
||||
led.value = True
|
||||
time.sleep(1)
|
||||
led.value = False
|
||||
449
Smart_Alarm_Clock/code.py
Normal file
449
Smart_Alarm_Clock/code.py
Normal file
|
|
@ -0,0 +1,449 @@
|
|||
# SPDX-FileCopyrightText: 2021 Dylan Herrada for Adafruit Industries
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
# General imports
|
||||
import gc
|
||||
import time
|
||||
import math
|
||||
import board
|
||||
|
||||
# Display imports
|
||||
import displayio
|
||||
from adafruit_display_text import label
|
||||
from adafruit_display_shapes.circle import Circle
|
||||
from adafruit_display_shapes.line import Line
|
||||
import adafruit_datetime
|
||||
from adafruit_bitmap_font import bitmap_font
|
||||
import framebufferio
|
||||
import sharpdisplay
|
||||
|
||||
# Rotary encoder imports
|
||||
from adafruit_seesaw.seesaw import Seesaw
|
||||
from adafruit_seesaw.digitalio import DigitalIO
|
||||
from adafruit_seesaw.rotaryio import IncrementalEncoder
|
||||
|
||||
# LED imports
|
||||
import pwmio
|
||||
|
||||
# Adafruit IO imports
|
||||
import ssl
|
||||
import socketpool
|
||||
import wifi
|
||||
import adafruit_minimqtt.adafruit_minimqtt as MQTT
|
||||
|
||||
# Audio imputs
|
||||
import audiocore
|
||||
import audiobusio
|
||||
|
||||
TIMES = {}
|
||||
ENABLED = {}
|
||||
|
||||
ALARM = False
|
||||
WAIT = 0
|
||||
|
||||
LAST = False
|
||||
LAST_TIME = 0
|
||||
|
||||
WARM_PERCENT = 0.0
|
||||
COOL_PERCENT = 0.0
|
||||
|
||||
UTC_OFFSET = -4
|
||||
|
||||
days = {
|
||||
0: "monday",
|
||||
1: "tuesday",
|
||||
2: "wednesday",
|
||||
3: "thursday",
|
||||
4: "friday",
|
||||
5: "saturday",
|
||||
6: "sunday",
|
||||
}
|
||||
|
||||
try:
|
||||
from secrets import secrets
|
||||
except ImportError:
|
||||
print("WiFi secrets are kept in secrets.py, please add them there!")
|
||||
raise
|
||||
|
||||
# Set your Adafruit IO Username and Key in secrets.py
|
||||
# (visit io.adafruit.com if you need to create an account,
|
||||
# or if you need your Adafruit IO key.)
|
||||
aio_username = secrets["aio_username"]
|
||||
aio_key = secrets["aio_key"]
|
||||
|
||||
print("Connecting to %s" % secrets["ssid"])
|
||||
wifi.radio.connect(secrets["ssid"], secrets["password"])
|
||||
print("Connected to %s!" % secrets["ssid"])
|
||||
|
||||
# Define callback functions which will be called when certain events happen.
|
||||
# pylint: disable=unused-argument
|
||||
def connected(client, userdata, flags, rc):
|
||||
print("Connected to Adafruit IO!")
|
||||
feeds = [
|
||||
"monday",
|
||||
"monday-enable",
|
||||
"tuesday",
|
||||
"tuesday-enable",
|
||||
"wednesday",
|
||||
"wednesday-enable",
|
||||
"thursday",
|
||||
"thursday-enable",
|
||||
"friday",
|
||||
"friday-enable",
|
||||
"saturday",
|
||||
"saturday-enable",
|
||||
"sunday",
|
||||
"sunday-enable",
|
||||
"alarm",
|
||||
]
|
||||
for feed in feeds:
|
||||
feed_slug = secrets["aio_username"] + "/feeds/alarm-clock." + feed
|
||||
print("Subscribed to: alarm-clock." + feed)
|
||||
client.subscribe(feed_slug, 1)
|
||||
|
||||
|
||||
def disconnected(client, userdata, rc): # pylint: disable=unused-argument
|
||||
# Disconnected function will be called when the client disconnects.
|
||||
print("Disconnected from Adafruit IO!")
|
||||
|
||||
|
||||
displayio.release_displays()
|
||||
|
||||
|
||||
def fade(warm_val, cool_val):
|
||||
finished = False
|
||||
if warm_val < 100 and cool_val == 0:
|
||||
warm_val += 1
|
||||
warm.duty_cycle = duty_cycle(warm_val)
|
||||
elif warm_val == 100 and cool_val < 100:
|
||||
cool_val += 1
|
||||
cool.duty_cycle = duty_cycle(cool_val)
|
||||
elif cool_val == 100 and warm_val > 0:
|
||||
warm_val -= 1
|
||||
warm.duty_cycle = duty_cycle(warm_val)
|
||||
finished = True
|
||||
return warm_val, cool_val, finished
|
||||
|
||||
|
||||
def get(feed_key):
|
||||
mqtt_client.publish(f'{secrets["aio_username"]}/feeds/{feed_key}/get', "\0")
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
def on_iso(client, feed_id, payload):
|
||||
timezone = adafruit_datetime.timezone.utc
|
||||
timezone._offset = adafruit_datetime.timedelta(seconds=UTC_OFFSET * 3600)
|
||||
datetime = adafruit_datetime.datetime.fromisoformat(payload[:-1]).replace(
|
||||
tzinfo=timezone
|
||||
)
|
||||
local_datetime = datetime.tzinfo.fromutc(datetime)
|
||||
print(local_datetime)
|
||||
dt_hour = local_datetime.hour
|
||||
dt_minute = local_datetime.minute
|
||||
if not local_datetime.second % 10:
|
||||
theta = (dt_hour / 6 + dt_minute / 360) - 0.5
|
||||
y_1 = int((72 * math.sin(math.pi * theta)) + 128)
|
||||
x_1 = int((72 * math.cos(math.pi * theta)) + 114)
|
||||
new_hour = Line(114, 128, x_1, y_1, 0xFFFFFF)
|
||||
splash[-3] = new_hour
|
||||
|
||||
theta = (dt_minute / 30) - 0.5
|
||||
y_1 = int((96 * math.sin(math.pi * theta)) + 128)
|
||||
x_1 = int((96 * math.cos(math.pi * theta)) + 114)
|
||||
dt_minute = Line(114, 128, x_1, y_1, 0xFFFFFF)
|
||||
splash[-2] = dt_minute
|
||||
|
||||
theta = (local_datetime.second / 30) - 0.5
|
||||
y_1 = int((96 * math.sin(math.pi * theta)) + 128)
|
||||
x_1 = int((96 * math.cos(math.pi * theta)) + 114)
|
||||
new_second = Line(114, 128, x_1, y_1, 0x808080)
|
||||
splash[-1] = new_second
|
||||
|
||||
day = days[local_datetime.weekday()]
|
||||
alarm_hour, alarm_minute = TIMES[day].split(":")
|
||||
if dt_hour == int(alarm_hour):
|
||||
if (
|
||||
dt_minute == int(alarm_minute)
|
||||
and ENABLED[day]
|
||||
and not ALARM
|
||||
and WAIT < time.monotonic()
|
||||
):
|
||||
mqtt_client.publish(
|
||||
f"{secrets['aio_username']}/feeds/alarm-clock.alarm", "True"
|
||||
)
|
||||
get("alarm-clock.alarm")
|
||||
gc.collect()
|
||||
|
||||
|
||||
def on_alarm(client, feed_id, payload):
|
||||
global ALARM
|
||||
global WAIT
|
||||
global LAST
|
||||
ALARM = eval(payload)
|
||||
if ALARM is False and LAST is True:
|
||||
WAIT = time.monotonic() + 60
|
||||
LAST = ALARM
|
||||
|
||||
print(f"{feed_id}: {payload}")
|
||||
|
||||
|
||||
def on_time(client, feed_id, payload):
|
||||
global TIMES
|
||||
TIMES[feed_id.split(".")[-1]] = payload
|
||||
print(f"{feed_id}: {payload}")
|
||||
|
||||
|
||||
def on_enable(client, feed_id, payload):
|
||||
global ENABLED
|
||||
ENABLED[feed_id.split(".")[-1].split("-")[0]] = payload == "ON"
|
||||
print(f"{feed_id}: {payload}")
|
||||
|
||||
|
||||
# Set up rotary encoder
|
||||
i2c_bus = board.I2C()
|
||||
|
||||
seesaw = Seesaw(i2c_bus, addr=0x36)
|
||||
|
||||
seesaw_product = (seesaw.get_version() >> 16) & 0xFFFF
|
||||
print("Found product {}".format(seesaw_product))
|
||||
if seesaw_product != 4991:
|
||||
print("Wrong firmware loaded? Expected 4991")
|
||||
|
||||
button = DigitalIO(seesaw, 24)
|
||||
|
||||
encoder = IncrementalEncoder(seesaw)
|
||||
LAST_POSITION = 0
|
||||
|
||||
|
||||
# Set up display
|
||||
bus = board.SPI()
|
||||
chip_select_pin = board.IO12
|
||||
framebuffer = sharpdisplay.SharpMemoryFramebuffer(bus, chip_select_pin, 400, 240)
|
||||
|
||||
display = framebufferio.FramebufferDisplay(framebuffer)
|
||||
|
||||
splash = displayio.Group(max_size=13)
|
||||
display.show(splash)
|
||||
|
||||
|
||||
# Set up PWM LEDs
|
||||
WARM_PIN = board.IO5
|
||||
COOL_PIN = board.IO6
|
||||
|
||||
|
||||
FADE_SLEEP = 0.01 # Number of milliseconds to delay between changes.
|
||||
# Increase to slow down, decrease to speed up.
|
||||
|
||||
# Define PWM outputs:
|
||||
warm = pwmio.PWMOut(WARM_PIN)
|
||||
cool = pwmio.PWMOut(COOL_PIN)
|
||||
|
||||
|
||||
def duty_cycle(percent):
|
||||
return int(percent / 100.0 * 65535.0)
|
||||
|
||||
|
||||
# Set up UI
|
||||
def brighten_warm(i):
|
||||
global WARM_PERCENT
|
||||
WARM_PERCENT = max(min(WARM_PERCENT + float(i * 2), 100), 0)
|
||||
warm.duty_cycle = duty_cycle(WARM_PERCENT)
|
||||
|
||||
|
||||
def brighten_cool(i):
|
||||
global COOL_PERCENT
|
||||
COOL_PERCENT = max(min(COOL_PERCENT + float(i * 2), 100), 0)
|
||||
cool.duty_cycle = duty_cycle(COOL_PERCENT)
|
||||
|
||||
|
||||
menu_funcs = [brighten_warm, brighten_cool]
|
||||
font = bitmap_font.load_font("/fonts/LeagueGothic-Regular-36.bdf")
|
||||
|
||||
label1 = label.Label(
|
||||
font, text="Warm light", anchor_point=(0, 0.5), anchored_position=(235, 120)
|
||||
)
|
||||
splash.append(label1)
|
||||
|
||||
label2 = label.Label(
|
||||
font, text="Cool light", anchor_point=(0, 0.5), anchored_position=(235, 170)
|
||||
)
|
||||
splash.append(label2)
|
||||
|
||||
menu = [label1, label2]
|
||||
|
||||
|
||||
# Set up clock
|
||||
circle = Circle(114, 128, 96, outline=0xFFFFFF)
|
||||
splash.append(circle)
|
||||
|
||||
circle = Circle(114, 128, 3, outline=0xFFFFFF, fill=0xFFFFFF)
|
||||
splash.append(circle)
|
||||
|
||||
twelve = Line(114, 32, 114, 16, 0xFFFFFF)
|
||||
splash.append(twelve)
|
||||
|
||||
for i in range(-2, 9):
|
||||
y0 = int((96 * math.sin(math.pi * (i / 6))) + 128)
|
||||
x0 = int((96 * math.cos(math.pi * (i / 6))) + 114)
|
||||
y1 = int((108 * math.sin(math.pi * (i / 6))) + 128)
|
||||
x1 = int((108 * math.cos(math.pi * (i / 6))) + 114)
|
||||
hour = Line(x0, y0, x1, y1, 0xFFFFFF)
|
||||
splash.append(hour)
|
||||
|
||||
hour = Line(114, 128, 114, 128, 0xFFFFFF)
|
||||
splash.append(hour)
|
||||
|
||||
minute = Line(114, 128, 114, 128, 0xFFFFFF)
|
||||
splash.append(minute)
|
||||
|
||||
second = Line(114, 128, 114, 128, 0x808080)
|
||||
splash.append(second)
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
# Audio setup
|
||||
wave_file = open("alarm.wav", "rb")
|
||||
wave = audiocore.WaveFile(wave_file)
|
||||
|
||||
audio = audiobusio.I2SOut(board.IO8, board.IO14, board.IO13)
|
||||
|
||||
# Create a socket pool
|
||||
pool = socketpool.SocketPool(wifi.radio)
|
||||
|
||||
# Initialize a new MQTT Client object
|
||||
mqtt_client = MQTT.MQTT(
|
||||
broker="io.adafruit.com",
|
||||
username=secrets["aio_username"],
|
||||
password=secrets["aio_key"],
|
||||
socket_pool=pool,
|
||||
ssl_context=ssl.create_default_context(),
|
||||
)
|
||||
|
||||
# Connect the callback methods defined above to Adafruit IO
|
||||
mqtt_client.on_connect = connected
|
||||
mqtt_client.on_disconnect = disconnected
|
||||
|
||||
|
||||
# Connect to Adafruit IO
|
||||
mqtt_client.connect()
|
||||
|
||||
mqtt_client.add_topic_callback("time/ISO-8601", on_iso)
|
||||
|
||||
mqtt_client.add_topic_callback(
|
||||
secrets["aio_username"] + "/feeds/alarm-clock.alarm", on_alarm
|
||||
)
|
||||
|
||||
mqtt_client.add_topic_callback(
|
||||
secrets["aio_username"] + "/feeds/alarm-clock.sunday", on_time
|
||||
)
|
||||
mqtt_client.add_topic_callback(
|
||||
secrets["aio_username"] + "/feeds/alarm-clock.monday", on_time
|
||||
)
|
||||
mqtt_client.add_topic_callback(
|
||||
secrets["aio_username"] + "/feeds/alarm-clock.tuesday", on_time
|
||||
)
|
||||
mqtt_client.add_topic_callback(
|
||||
secrets["aio_username"] + "/feeds/alarm-clock.wednesday", on_time
|
||||
)
|
||||
mqtt_client.add_topic_callback(
|
||||
secrets["aio_username"] + "/feeds/alarm-clock.thursday", on_time
|
||||
)
|
||||
mqtt_client.add_topic_callback(
|
||||
secrets["aio_username"] + "/feeds/alarm-clock.friday", on_time
|
||||
)
|
||||
mqtt_client.add_topic_callback(
|
||||
secrets["aio_username"] + "/feeds/alarm-clock.saturday", on_time
|
||||
)
|
||||
|
||||
mqtt_client.add_topic_callback(
|
||||
secrets["aio_username"] + "/feeds/alarm-clock.sunday-enable", on_enable
|
||||
)
|
||||
mqtt_client.add_topic_callback(
|
||||
secrets["aio_username"] + "/feeds/alarm-clock.monday-enable", on_enable
|
||||
)
|
||||
mqtt_client.add_topic_callback(
|
||||
secrets["aio_username"] + "/feeds/alarm-clock.tuesday-enable", on_enable
|
||||
)
|
||||
mqtt_client.add_topic_callback(
|
||||
secrets["aio_username"] + "/feeds/alarm-clock.wednesday-enable", on_enable
|
||||
)
|
||||
mqtt_client.add_topic_callback(
|
||||
secrets["aio_username"] + "/feeds/alarm-clock.thursday-enable", on_enable
|
||||
)
|
||||
mqtt_client.add_topic_callback(
|
||||
secrets["aio_username"] + "/feeds/alarm-clock.friday-enable", on_enable
|
||||
)
|
||||
mqtt_client.add_topic_callback(
|
||||
secrets["aio_username"] + "/feeds/alarm-clock.saturday-enable", on_enable
|
||||
)
|
||||
|
||||
|
||||
get("alarm-clock.sunday")
|
||||
get("alarm-clock.monday")
|
||||
get("alarm-clock.tuesday")
|
||||
get("alarm-clock.wednesday")
|
||||
get("alarm-clock.thursday")
|
||||
get("alarm-clock.friday")
|
||||
get("alarm-clock.saturday")
|
||||
|
||||
get("alarm-clock.sunday-enable")
|
||||
get("alarm-clock.monday-enable")
|
||||
get("alarm-clock.tuesday-enable")
|
||||
get("alarm-clock.wednesday-enable")
|
||||
get("alarm-clock.thursday-enable")
|
||||
get("alarm-clock.friday-enable")
|
||||
get("alarm-clock.saturday-enable")
|
||||
|
||||
while len(TIMES) < 7 or len(ENABLED) < 7:
|
||||
mqtt_client.loop()
|
||||
|
||||
mqtt_client.publish(secrets["aio_username"] + "/feeds/alarm-clock.alarm", "False")
|
||||
mqtt_client.subscribe("time/ISO-8601", 1)
|
||||
|
||||
print("Starting")
|
||||
|
||||
SELECTED = 0
|
||||
while True:
|
||||
try:
|
||||
diff = time.monotonic() - LAST_TIME
|
||||
LAST_TIME = time.monotonic()
|
||||
|
||||
if diff > 0.1:
|
||||
mqtt_client.loop(0.25)
|
||||
if ALARM:
|
||||
if WAIT > time.monotonic():
|
||||
print("Alarm ringing")
|
||||
WARM_PERCENT, COOL_PERCENT, done = fade(WARM_PERCENT, COOL_PERCENT)
|
||||
if done and not audio.playing:
|
||||
audio.play(wave)
|
||||
|
||||
position = encoder.position
|
||||
if position < LAST_POSITION and button.value:
|
||||
SELECTED -= 1
|
||||
LAST_POSITION = position
|
||||
print("Position: {}".format(position))
|
||||
for i in menu:
|
||||
i.y += 50
|
||||
if position > LAST_POSITION and button.value:
|
||||
SELECTED += 1
|
||||
LAST_POSITION = position
|
||||
|
||||
for i in menu:
|
||||
i.y -= 50
|
||||
|
||||
if not button.value:
|
||||
if LAST_POSITION != position:
|
||||
menu_funcs[SELECTED](position - LAST_POSITION)
|
||||
LAST_POSITION = position
|
||||
|
||||
else:
|
||||
pass
|
||||
gc.collect()
|
||||
except (ValueError, RuntimeError) as err:
|
||||
print("Failed to get data, retrying\n", err)
|
||||
wifi.reset()
|
||||
mqtt_client.reconnect()
|
||||
continue
|
||||
except MemoryError:
|
||||
print("Buffer too large, retrying")
|
||||
continue
|
||||
13878
Smart_Alarm_Clock/fonts/LeagueGothic-Regular-36.bdf
Normal file
13878
Smart_Alarm_Clock/fonts/LeagueGothic-Regular-36.bdf
Normal file
File diff suppressed because it is too large
Load diff
111
Smart_Alarm_Clock/fonts/LeagueGothic-Regular-36.bdf.license
Normal file
111
Smart_Alarm_Clock/fonts/LeagueGothic-Regular-36.bdf.license
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
Copyright (c) 2020, Caleb Maclennan <caleb@alerque>
|
||||
|
||||
Copyright (c) 2010-2012, Micah Rich <micah@micahrich.com>,
|
||||
with Reserved Font Name: "League Gothic".
|
||||
|
||||
Copyright (c) 2010, Caroline Hadilaksono <caroline@hadilaksono>
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
Version 1.1 - 26 February 2007
|
||||
|
||||
----
|
||||
|
||||
SIL Open Font License
|
||||
=====================
|
||||
|
||||
|
||||
Preamble
|
||||
--------
|
||||
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
Definitions
|
||||
-----------
|
||||
|
||||
`"Font Software"` refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
`"Reserved Font Name"` refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
`"Original Version"` refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
`"Modified Version"` refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
`"Author"` refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
Permission & Conditions
|
||||
-----------------------
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1. Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2. Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3. No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4. The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5. The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
Termination
|
||||
-----------
|
||||
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
Disclaimer
|
||||
----------
|
||||
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
Loading…
Reference in a new issue