diff --git a/ledd.py b/ledd.py
index d92899d..a016338 100644
--- a/ledd.py
+++ b/ledd.py
@@ -36,9 +36,6 @@ from pkgutil import iter_modules
from docopt import docopt
-import ledd.daemon
-import ledd
-
if "smbus" not in (name for loader, name, ispkg in iter_modules()):
print("smbus not found, installing replacement")
@@ -65,6 +62,9 @@ if "smbus" not in (name for loader, name, ispkg in iter_modules()):
sys.modules['smbus'] = SMBusModule
sys.modules['smbus'].SMBus = SMBus
+import ledd.daemon
+import ledd
+
def pid_exists(processid):
if processid < 0:
@@ -118,7 +118,7 @@ if __name__ == "__main__":
os.chdir(wdir)
with open("ledd.pid", 'w') as pidf:
pidf.write(str(os.getpid()) + '\n')
- daemon = ledd.daemon.Daemon()
+ ledd.daemon.run()
else:
sys.exit()
else:
@@ -126,4 +126,4 @@ if __name__ == "__main__":
except OSError as e:
log.fatal("Start failed: %s", e)
else:
- daemon = ledd.daemon.Daemon()
+ ledd.daemon.run()
diff --git a/ledd/__init__.py b/ledd/__init__.py
index 8738588..9dd686d 100644
--- a/ledd/__init__.py
+++ b/ledd/__init__.py
@@ -14,4 +14,13 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+from sqlalchemy.orm import sessionmaker, scoped_session
+from sqlalchemy.ext.declarative import declarative_base
+
VERSION = "0.1"
+
+engine = None
+session = scoped_session(sessionmaker())
+""" :type : sqlalchemy.orm.scoping.scoped_session """
+Base = declarative_base()
+Base.query = session.query_property()
diff --git a/ledd/controller.py b/ledd/controller.py
index 2c5f978..a0e21b8 100644
--- a/ledd/controller.py
+++ b/ledd/controller.py
@@ -18,9 +18,12 @@ from json import JSONEncoder
import logging
import time
-import smbus
+from sqlalchemy import Column, Integer, String
+from sqlalchemy.orm import relationship
+import smbus
from ledd.stripe import Stripe
+from . import Base
PCA9685_SUBADR1 = 0x2
PCA9685_SUBADR2 = 0x3
@@ -41,61 +44,25 @@ ALLLED_OFF_L = 0xFC
ALLLED_OFF_H = 0xFD
-class Controller:
+class Controller(Base):
+ __tablename__ = "controller"
+
+ id = Column(Integer, primary_key=True)
+ channels = Column(Integer)
+ i2c_device = Column(Integer)
+ address = Column(String)
+ stripes = relationship("Stripe", backref="controller")
+ _pwm_freq = Column("pwm_freq", Integer)
+
"""
A controller controls a number of stripes.
"""
- @classmethod
- def from_row(cls, db, row):
- # load from db
- return cls(db, pwm_freq=row["pwm_freq"], channels=row["channels"], i2c_device=row["i2c_device"],
- address=row["address"], cid=row["id"], from_db=True)
-
- @staticmethod
- def from_db(db):
- l = []
- cur = db.cursor()
- for row in cur.execute("select * from controller"):
- c = Controller.from_row(db, row)
- l.append(c)
- cur.close()
- return l
-
- def save_to_db(self):
- cur = self.db.cursor()
- if self.id == -1:
- cur.execute("INSERT INTO controller (pwm_freq, channels, i2c_device, address) VALUES (?,?,?,?)",
- (self._pwm_freq, self.channels, self.i2c_device, self.address))
- self.id = cur.lastrowid
- else:
- cur.execute("UPDATE controller SET pwm_freq=?, channels=?, i2c_device=?, address=? WHERE id = ?",
- (self._pwm_freq, self.channels, self.i2c_device, self.address, self.id))
- cur.close()
- self.db.commit()
-
- def __init__(self, db, channels, i2c_device, address, pwm_freq=1526, cid=-1, from_db=False):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
self._mode = None
- self.channels = channels
- self.i2c_device = i2c_device
- self.bus = smbus.SMBus(i2c_device)
- self.address = address
- self._address = int(address, 16)
- self.id = cid
- self.db = db
- self.stripes = []
- self._pwm_freq = None
- self.pwm_freq = pwm_freq
- if not from_db:
- self.save_to_db()
- self.load_stripes()
-
- def load_stripes(self):
- cur = self.db.cursor()
- for stripe in cur.execute("select * from stripes where controller_id = ?", (self.id,)):
- self.stripes.append(Stripe.from_db(self, stripe))
- logging.getLogger(__name__).debug("Loaded %s stripes for controller %s", len(self.stripes), self.id)
- cur.close()
+ self.bus = smbus.SMBus(self.i2c_device)
+ self._address = int(self.address, 16)
def __repr__(self):
return "".format(len(self.stripes), self.id)
@@ -114,9 +81,6 @@ class Controller:
def get_channel(self, channel):
return self.bus.read_word_data(self._address, LED0_OFF_L + 4 * channel) / 4095
- def add_stripe(self, stripe):
- self.stripes.append(stripe)
-
def reset(self):
self.mode = int("0b00100001", 2) # MODE1 -> 0b00000001
time.sleep(0.015)
diff --git a/ledd/daemon.py b/ledd/daemon.py
index bc3436b..21f9d77 100644
--- a/ledd/daemon.py
+++ b/ledd/daemon.py
@@ -17,155 +17,135 @@
import logging
import configparser
import json
-import sqlite3
import os
import sys
import asyncio
import signal
+from sqlalchemy import create_engine
from jsonrpc import JSONRPCResponseManager, dispatcher
-
from jsonrpc.exceptions import JSONRPCError
import spectra
+from sqlalchemy.exc import OperationalError
+from sqlalchemy.orm.exc import NoResultFound
-from ledd import controller, VERSION
+from ledd import VERSION
from ledd.effectstack import EffectStack
+from ledd.models import Meta
from ledd.stripe import Stripe
+from ledd.controller import Controller, ControllerEncoder
+from . import Base, session
log = logging.getLogger(__name__)
+daemonSection = 'daemon'
+databaseSection = 'db'
+loop = None
+""" :type : asyncio.BaseEventLoop """
+effects = []
-class Daemon:
- daemonSection = 'daemon'
- databaseSection = 'db'
- instance = None
- """:type : Daemon """
- loop = None
- """ :type : asyncio.BaseEventLoop """
- effects = []
-
- def __init__(self):
- Daemon.instance = self
+def run():
+ try:
+ # read config
+ config = configparser.ConfigParser()
try:
- # read config
- self.config = configparser.ConfigParser()
- try:
- with open('ledd.config', 'w+') as f:
- self.config.read_file(f)
- except FileNotFoundError:
- log.info("No config file found!")
- pass
+ with open('ledd.config', 'w+') as f:
+ config.read_file(f)
+ except FileNotFoundError:
+ log.info("No config file found!")
+ pass
- # SQL init
- self.sqldb = sqlite3.connect(self.config.get(self.databaseSection, 'name', fallback='ledd.sqlite'))
- self.sqldb.row_factory = sqlite3.Row
+ # SQL init
+ global engine
+ engine = create_engine("sqlite:///" + config.get(databaseSection, 'name', fallback='ledd.sqlite'),
+ echo=log.getEffectiveLevel() == logging.DEBUG)
+ session.configure(bind=engine)
+ Base.metadata.bind = engine
+ if not check_db():
+ init_db()
- if not self.check_db():
- self.init_db()
+ log.debug(Controller.query.all())
+ logging.getLogger("asyncio").setLevel(log.getEffectiveLevel())
- self.sqldb.commit()
+ # sigterm handler
+ def sigterm_handler():
+ raise SystemExit
- # init controllers from db
- self.controllers = controller.Controller.from_db(self.sqldb)
- log.debug(self.controllers)
- logging.getLogger("asyncio").setLevel(log.getEffectiveLevel())
+ signal.signal(signal.SIGTERM, sigterm_handler)
- # sigterm handler
- def sigterm_handler():
- raise SystemExit
+ # init plugins
+ # TODO: check all plugins for existing hooks
- signal.signal(signal.SIGTERM, sigterm_handler)
-
- # init plugins
- # TODO: check all plugins for existing hooks
-
- # main loop
- self.loop = asyncio.get_event_loop()
- coro = self.loop.create_server(LedDProtocol,
- self.config.get(self.daemonSection, 'host', fallback='0.0.0.0'),
- self.config.get(self.daemonSection, 'port', fallback=1425))
- self.server = self.loop.run_until_complete(coro)
- self.loop.run_forever()
- except (KeyboardInterrupt, SystemExit):
- log.info("Exiting")
- try:
- os.remove("ledd.pid")
- except FileNotFoundError:
- pass
- self.sqldb.close()
- self.server.close()
- self.loop.run_until_complete(self.server.wait_closed())
- self.loop.close()
- sys.exit(0)
-
- def check_db(self):
- """
- Checks database version
- :return: database validity
- :rtype: bool
- """
- c = self.sqldb.cursor()
+ # main loop
+ global loop
+ loop = asyncio.get_event_loop()
+ coro = loop.create_server(LedDProtocol,
+ config.get(daemonSection, 'host', fallback='0.0.0.0'),
+ config.get(daemonSection, 'port', fallback=1425))
+ global server
+ server = loop.run_until_complete(coro)
+ loop.run_forever()
+ except (KeyboardInterrupt, SystemExit):
+ log.info("Exiting")
try:
- c.execute("SELECT value FROM meta WHERE option = 'db_version'")
- db_version = c.fetchone()
- c.close()
+ os.remove("ledd.pid")
+ except FileNotFoundError:
+ pass
+ # TODO: close engine?
+ if server is not None:
+ server.close()
+ if loop is not None:
+ loop.run_until_complete(server.wait_closed())
+ loop.close()
+ sys.exit(0)
- if db_version is not None:
- log.info("DB connection established; db-version=%s", db_version[0])
- if int(db_version[0]) < 2:
- with open("ledd/sql/upgrade_1_2.sql", "r") as ufile:
- u = self.sqldb.cursor()
- u.executescript(ufile.read())
- u.close()
- log.info("Database upgraded to version %s", 2)
+def check_db():
+ """
+ Checks database version
+ :return: database validity
+ :rtype: bool
+ """
+ try:
+ db_version = Meta.get_version()
- return True
- else:
- return False
- except sqlite3.OperationalError as e:
- log.debug("SQLite error: %s", e)
- c.close()
- return False
+ if db_version is not None:
+ log.info("DB connection established; db-version=%s", db_version)
+ return True
+ except OperationalError:
+ return False
+ return False
- def init_db(self):
- self.sqldb.close()
- if os.path.exists("ledd.sqlite"):
- os.remove("ledd.sqlite")
- self.sqldb = sqlite3.connect(self.config.get(self.databaseSection, 'name', fallback='ledd.sqlite'))
- self.sqldb.row_factory = sqlite3.Row
- with open("ledd/sql/ledd.sql", "r") as sqlfile:
- c = self.sqldb.cursor()
- c.executescript(sqlfile.read())
- c.close()
- self.check_db()
- @dispatcher.add_method
- def start_effect(self, **kwargs):
- """
- Part of the Color API. Used to start a specific effect.
- Required JSON parameters: stripe IDs: sids; effect id: eid, effect options: eopt
- :param req_json: dict of request json
- """
- stripes = []
+def init_db():
+ Base.metadata.drop_all()
+ Base.metadata.create_all()
+ session.add(Meta(option="db_version", value="2"))
+ session.commit()
+ check_db()
- if "sids" in kwargs:
- for sid in kwargs['sids']:
- found_s = self.find_stripe(sid)
- if found_s is not None:
- stripes.append(found_s)
+@dispatcher.add_method
+def start_effect(**kwargs):
+ """
+ Part of the Color API. Used to start a specific effect.
+ Required JSON parameters: stripe IDs: sids; effect id: eid, effect options: eopt
+ :param req_json: dict of request json
+ """
- if len(stripes) > 0:
+ stripes = []
+
+ if "sids" in kwargs:
+ for stripe in Stripe.query.filter(Stripe.id.in_(kwargs['sids'])):
# TODO: add anything required to start effect with req_json['eid']
# on stripes[] with options in req_json['eopt']
effect = EffectStack()
- self.effects.append(effect)
- effect.stripes.append(self.controllers[0].stripes[0])
+ effects.append(effect)
+ effect.stripes.append(stripe)
effect.start()
- # asyncio.ensure_future(asyncio.get_event_loop().run_in_executor(self.executor, effect.execute))
+ # asyncio.ensure_future(asyncio.get_event_loop().run_in_executor(executor, effect.execute))
rjson = {
'eident': None, # unique effect identifier that identifies excatly this effect started on this set of
@@ -176,171 +156,175 @@ class Daemon:
else:
return JSONRPCError(-1003, "Stripeid not found")
- @dispatcher.add_method
- def stop_effect(self, **kwargs):
- """
- Part of the Color API. Used to stop a specific effect.
- Required JSON parameters: effect identifier: eident
- :param req_json: dict of request json
- """
- # TODO: add stop effect by eident logic
+@dispatcher.add_method
+def stop_effect(**kwargs):
+ """
+ Part of the Color API. Used to stop a specific effect.
+ Required JSON parameters: effect identifier: eident
+ :param req_json: dict of request json
+ """
- @dispatcher.add_method
- def get_effects(self, **kwargs):
- """
- Part of the Color API. Used to show all available and running effects.
- Required JSON parameters: -
- :param req_json: dict of request json
- """
+ # TODO: add stop effect by eident logic
- # TODO: list all effects here and on which stripes they run atm
- # TODO: all effects get runtime only ids, "eid"'s. They are shown here for the client to start effects.
- # TODO: All options that an effect may have need to be transmitted here too with "eopt".
- @dispatcher.add_method
- def set_color(self, **kwargs):
- """
- Part of the Color API. Used to set color of a stripe.
- Required JSON parameters: stripe ID: sid; HSV values hsv: h,s,v, controller id: cid
- :param req_json: dict of request json
- """
+@dispatcher.add_method
+def get_effects(**kwargs):
+ """
+ Part of the Color API. Used to show all available and running effects.
+ Required JSON parameters: -
+ :param req_json: dict of request json
+ """
- found_s = self.find_stripe(kwargs['sid'])
+ # TODO: list all effects here and on which stripes they run atm
+ # TODO: all effects get runtime only ids, "eid"'s. They are shown here for the client to start effects.
+ # TODO: All options that an effect may have need to be transmitted here too with "eopt".
- if found_s is None:
- log.warning("Stripe not found: id=%s", kwargs['sid'])
- else:
- found_s.set_color(spectra.hsv(kwargs['hsv']['h'], kwargs['hsv']['s'], kwargs['hsv']['v']))
- @dispatcher.add_method
- def add_controller(self, **kwargs):
- """
- Part of the Color API. Used to add a controller.
- Required JSON parameters: channels; i2c_dev: number of i2c device (e.g. /dev/i2c-1 would be i2c_dev = 1);
- address: hexdecimal address of controller on i2c bus, e.g. 0x40
- :param req_json: dict of request json
- """
- try:
- ncontroller = controller.Controller(Daemon.instance.sqldb, kwargs['channels'],
- kwargs['i2c_dev'], kwargs['address'])
- except OSError as e:
- log.error("Error opening i2c device: %s (%s)", kwargs['i2c_dev'], e)
- return JSONRPCError(-1004, "Error while opening i2c device", e)
+@dispatcher.add_method
+def set_color(**kwargs):
+ """
+ Part of the Color API. Used to set color of a stripe.
+ Required JSON parameters: stripe ID: sid; HSV values hsv: h,s,v, controller id: cid
+ :param req_json: dict of request json
+ """
+ try:
+ stripe = Stripe.query.filter(Stripe.id == kwargs['sid']).one()
+ stripe.set_color(spectra.hsv(kwargs['hsv']['h'], kwargs['hsv']['s'], kwargs['hsv']['v']))
+ except NoResultFound:
+ log.warning("Stripe not found: id=%s", kwargs['sid'])
- self.controllers.append(ncontroller)
+
+@dispatcher.add_method
+def add_controller(**kwargs):
+ """
+ Part of the Color API. Used to add a controller.
+ Required JSON parameters: channels; i2c_dev: number of i2c device (e.g. /dev/i2c-1 would be i2c_dev = 1);
+ address: hexdecimal address of controller on i2c bus, e.g. 0x40
+ :param req_json: dict of request json
+ """
+ try:
+ ncontroller = Controller(channels=int(kwargs['channels']), i2c_device=int(kwargs['i2c_dev']),
+ address=kwargs['address'])
+ except OSError as e:
+ log.error("Error opening i2c device: %s (%s)", kwargs['i2c_dev'], e)
+ return JSONRPCError(-1004, "Error while opening i2c device", e)
+
+ session.add(ncontroller)
+ session.commit()
+
+ rjson = {
+ 'cid': ncontroller.id,
+ }
+
+ return rjson
+
+
+@dispatcher.add_method
+def get_color(**kwargs):
+ """
+ Part of the Color API. Used to get the current color of an stripe.
+ Required JSON parameters: stripes
+ :param req_json: dict of request json
+ """
+ try:
+ stripe = Stripe.query.filter(Stripe.id == kwargs['sid']).one()
+ except NoResultFound:
+ log.warning("Stripe not found: id=%s", kwargs['sid'])
+ return JSONRPCError(-1003, "Stripeid not found")
+
+ rjson = {
+ 'color': stripe.color.values,
+ }
+
+ return rjson
+
+
+@dispatcher.add_method
+def add_stripe(**kwargs):
+ """
+ Part of the Color API. Used to add stripes.
+ Required JSON parameters: name; rgb: bool; map: r: r-channel, g: g-channel, b: b-channel, cid
+ :param req_json: dict of request json
+ """
+
+ if "stripe" in kwargs:
+ stripe = kwargs['stripe']
+ c = Controller.query.filter(Controller.id == int(stripe['cid'])).first()
+ """ :type c: ledd.controller.Controller """
+
+ if c is None:
+ return JSONRPCError(-1002, "Controller not found")
+
+ s = Stripe(name=stripe['name'], rgb=bool(stripe['rgb']),
+ channel_r=stripe['map']['r'], channel_g=stripe['map']['g'], channel_b=stripe['map']['b'])
+ s.controller = c
+ log.debug("Added stripe %s to controller %s; new len %s", c.id, s.id, len(c.stripes))
rjson = {
- 'cid': ncontroller.id,
+ 'sid': s.id,
}
return rjson
- @dispatcher.add_method
- def get_color(self, **kwargs):
- """
- Part of the Color API. Used to get the current color of an stripe.
- Required JSON parameters: stripes
- :param req_json: dict of request json
- """
- found_s = self.find_stripe(kwargs['sid'])
+@dispatcher.add_method
+def get_stripes(**kwargs):
+ """
+ Part of the Color API. Used to get all registered stripes known to the daemon.
+ Required JSON parameters: none
+ :param req_json: dict of request json
+ """
- if found_s is None:
- log.warning("StripeId not found: id=%s", kwargs['sid'])
- return JSONRPCError(-1003, "Stripeid not found")
+ rjson = {
+ 'ccount': len(Controller.query),
+ 'controller': json.dumps(Controller.query, cls=ControllerEncoder),
+ }
- rjson = {
- 'color': found_s.color.values,
- }
+ return rjson
- return rjson
- @dispatcher.add_method
- def add_stripe(self, **kwargs):
- """
- Part of the Color API. Used to add stripes.
- Required JSON parameters: name; rgb: bool; map: r: r-channel, g: g-channel, b: b-channel, cid
- :param req_json: dict of request json
- """
+@dispatcher.add_method
+def test_channel(**kwargs):
+ """
+ Part of the Color API. Used to test a channel on a specified controller.
+ Required JSON parameters: controller id: cid, channel, value
+ :param req_json: dict of request json
+ """
- if "stripe" in kwargs:
- stripe = kwargs['stripe']
- c = next((x for x in self.controllers if x.id == stripe['cid']), None)
- """ :type c: ledd.controller.Controller """
+ result = Controller.query.filter(Controller.id == kwargs['cid']).first()
+ """ :type : ledd.controller.Controller """
- if c is None:
- return JSONRPCError(-1002, "Controller not found")
+ if result is not None:
+ result.set_channel(kwargs['channel'], kwargs['value'], 2.8)
- s = Stripe(c, stripe['name'], stripe['rgb'],
- (stripe['map']['r'], stripe['map']['g'], stripe['map']['b']))
- c.stripes.append(s)
- log.debug("Added stripe %s to controller %s; new len %s", c.id, s.id, len(c.stripes))
+@dispatcher.add_method
+def discover(**kwargs):
+ """
+ Part of the Color API. Used by mobile applications to find the controller.
+ Required JSON parameters: none
+ :param req_json: dict of request json
+ """
+ log.debug("recieved action: %s", kwargs['action'])
- rjson = {
- 'sid': s.id,
- }
+ rjson = {
+ 'version': VERSION
+ }
- return rjson
+ return rjson
- @dispatcher.add_method
- def get_stripes(self, **kwargs):
- """
- Part of the Color API. Used to get all registered stripes known to the daemon.
- Required JSON parameters: none
- :param req_json: dict of request json
- """
- rjson = {
- 'ccount': len(Daemon.instance.controllers),
- 'controller': json.dumps(Daemon.instance.controllers, cls=controller.ControllerEncoder),
- }
+def find_stripe(sid):
+ """
+ Deprecated. Use a query instead. Or this should be moved to a classmethod in Stripe
+ Finds a given stripeid in the currently known controllers
+ :param sid stripe id
+ :return: stripe if found or none
+ :rtype: ledd.Stripe | None
+ """
- return rjson
-
- @dispatcher.add_method
- def test_channel(self, **kwargs):
- """
- Part of the Color API. Used to test a channel on a specified controller.
- Required JSON parameters: controller id: cid, channel, value
- :param req_json: dict of request json
- """
-
- result = next(filter(lambda x: x.id == kwargs['cid'], self.controllers), None)
- """ :type : ledd.controller.Controller """
-
- if result is not None:
- result.set_channel(kwargs['channel'], kwargs['value'], 2.8)
-
- @dispatcher.add_method
- def discover(self, **kwargs):
- """
- Part of the Color API. Used by mobile applications to find the controller.
- Required JSON parameters: none
- :param req_json: dict of request json
- """
- log.debug("recieved action: %s", kwargs['action'])
-
- rjson = {
- 'version': VERSION
- }
-
- return rjson
-
- def find_stripe(self, sid):
- """
- Finds a given stripeid in the currently known controllers
- :param sid stripe id
- :return: stripe if found or none
- :rtype: ledd.Stripe | None
- """
- for c in self.controllers:
- for s in c.stripes:
- if s.id == sid:
- return s
-
- return None
+ return Stripe.query.filter(Stripe.id == sid).first()
class LedDProtocol(asyncio.Protocol):
diff --git a/ledd/models.py b/ledd/models.py
new file mode 100644
index 0000000..d28ed8d
--- /dev/null
+++ b/ledd/models.py
@@ -0,0 +1,29 @@
+# LEDD Project
+# Copyright (C) 2015 LEDD Team
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+from sqlalchemy import String, Column
+
+from . import Base
+
+
+class Meta(Base):
+ __tablename__ = "meta"
+ option = Column(String, primary_key=True)
+ value = Column(String)
+
+ @classmethod
+ def get_version(cls):
+ return cls.query.filter(Meta.option == "db_version").first()
diff --git a/ledd/sql/ledd.sql b/ledd/sql/ledd.sql
index ddc56e1..381ef02 100644
--- a/ledd/sql/ledd.sql
+++ b/ledd/sql/ledd.sql
@@ -1,24 +1,24 @@
CREATE TABLE `stripes` (
- `id` INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
- `name` TEXT,
- `rgb` INTEGER,
- `controller_id` INTEGER,
- `channel_r` INTEGER,
- `channel_g` INTEGER,
- `channel_b` INTEGER,
- `channel_r_gamma` REAL DEFAULT 2.8,
- `channel_g_gamma` REAL DEFAULT 2.8,
- `channel_b_gamma` REAL DEFAULT 2.8
+ `id` INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
+ `name` TEXT,
+ `rgb` INTEGER,
+ `controller_id` INTEGER,
+ `channel_r` INTEGER,
+ `channel_g` INTEGER,
+ `channel_b` INTEGER,
+ `channel_r_gamma` REAL DEFAULT 2.8,
+ `channel_g_gamma` REAL DEFAULT 2.8,
+ `channel_b_gamma` REAL DEFAULT 2.8
);
CREATE TABLE "meta" (
- `option` TEXT,
- `value` TEXT
+ `option` TEXT,
+ `value` TEXT
);
-INSERT INTO `meta` VALUES ('db_version','2');
+INSERT INTO `meta` VALUES ('db_version', '2');
CREATE TABLE "controller" (
- `id` INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
- `address` TEXT,
- `i2c_device` INTEGER,
- `channels` INTEGER,
- `pwm_freq` INTEGER
+ `id` INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
+ `address` TEXT,
+ `i2c_device` INTEGER,
+ `channels` INTEGER,
+ `pwm_freq` INTEGER
);
\ No newline at end of file
diff --git a/ledd/stripe.py b/ledd/stripe.py
index b5758c0..c42263a 100644
--- a/ledd/stripe.py
+++ b/ledd/stripe.py
@@ -13,44 +13,42 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+
from spectra import Color
+from sqlalchemy import Integer, ForeignKey, String, Float, Boolean
+from sqlalchemy import Column
+
+from . import Base
-class Stripe:
+class Stripe(Base):
+ __tablename__ = "stripe"
"""
A stripe is the smallest controllable unit.
"""
+ id = Column(Integer, primary_key=True)
+ controller_id = Column(Integer, ForeignKey('controller.id'))
+ name = Column(String)
+ channel_r = Column(Integer)
+ channel_g = Column(Integer)
+ channel_b = Column(Integer)
+ channel_r_gamma = Column(Float, default=2.8)
+ channel_g_gamma = Column(Float, default=2.8)
+ channel_b_gamma = Column(Float, default=2.8)
+ rgb = Column(Boolean)
- def __init__(self, controller, name, rgb, channels, sid=-1, gamma_correct=(2.8, 2.8, 2.8), from_db=False):
- self.controller = controller
- self.name = name
- self.rgb = bool(rgb)
- self.channels = channels
- self.id = sid
- self._color = None
- self.gamma_correct = gamma_correct
- self.read_color()
- if not from_db:
- self.save_to_db()
+ @property
+ def channels(self):
+ return self.channel_r, self.channel_b, self.channel_g
- def save_to_db(self):
- cur = self.controller.db.cursor()
- if self.id == -1:
- cur.execute("INSERT INTO stripes DEFAULT VALUES")
- self.id = cur.lastrowid
- cur.execute(
- "UPDATE stripes SET "
- "channel_r_gamma = ?,"
- "channel_g_gamma = ?,"
- "channel_b_gamma = ?,"
- "channel_r = ?,"
- "channel_g = ?,"
- "channel_b = ?,"
- "controller_id = ?,"
- "name = ? WHERE id = ?",
- self.gamma_correct + self.channels + (self.controller.id, self.name, self.id))
- cur.close()
- self.controller.db.commit()
+ # TODO save channels to db
+
+ @channels.setter
+ def channels(self, t):
+ self.channel_r, self.channel_g, self.channel_b = t
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
def read_color(self):
rc = tuple([float(self.controller.get_channel(channel)) for channel in self.channels])
@@ -60,12 +58,6 @@ class Stripe:
def __repr__(self):
return "".format(self.id)
- @classmethod
- def from_db(cls, controller, row):
- return cls(controller, name=row["name"], rgb=row["rgb"],
- channels=(row["channel_r"], row["channel_g"], row["channel_b"]), sid=row["id"],
- gamma_correct=(row["channel_r_gamma"], row["channel_g_gamma"], row["channel_b_gamma"]), from_db=True)
-
def set_color(self, c):
self._color = c
for channel, gamma_correct, value in zip(self.channels, self.gamma_correct, c.clamped_rgb):
diff --git a/setup.py b/setup.py
index 8106093..81e5ff8 100644
--- a/setup.py
+++ b/setup.py
@@ -25,6 +25,6 @@ setup(name='LedD',
license='GPLv3',
packages=['ledd'],
install_requires=[
- 'nose', 'spectra', 'docopt', 'jsonrpc',
+ 'nose', 'spectra', 'docopt', 'jsonrpc', 'sqlalchemy',
],
zip_safe=False)