Moved to SQLAlchemy

Finished switch to JSONRPC

commit 9cf6dd9a0e03c71135c01d4ad4f7d3be0f1e3066
Author: Giovanni Harting <giovanni.harting@touchdata.net>
Date:   Sat Oct 10 21:09:20 2015 +0200

    fixed some bugs
    added some missing things from transition

commit 8ed44b8fcde739b541b1834049025b055a50e6fe
Author: Marius Schiffer <marius@mschiffer.de>
Date:   Sat Oct 10 05:53:41 2015 +0200

    Creating fresh database works now. Fixed pwm_freq property.

commit dc88ef0df427f90746a499912eff70dfce967c55
Author: Marius Schiffer <marius@mschiffer.de>
Date:   Sat Oct 10 04:40:41 2015 +0200

    Completed SQLAlchemy integration. Completed JSON-RPC integration.
    All daemon class functionality is now on module-level (required for
    JSON-RPC decorators).
    Migrations will have to be reimplemented with alembic.

commit a4cabdcd00a3e2a3cbbd92a3c9d59a4235e4d277
Author: Marius Schiffer <marius@mschiffer.de>
Date:   Sat Oct 10 03:00:19 2015 +0200

    First steps towards SQLAlchemy integration.
This commit is contained in:
Giovanni Harting
2015-10-10 21:13:29 +02:00
parent 87f2d12c22
commit d5f403d557
8 changed files with 341 additions and 363 deletions

10
ledd.py
View File

@@ -36,9 +36,6 @@ from pkgutil import iter_modules
from docopt import docopt from docopt import docopt
import ledd.daemon
import ledd
if "smbus" not in (name for loader, name, ispkg in iter_modules()): if "smbus" not in (name for loader, name, ispkg in iter_modules()):
print("smbus not found, installing replacement") 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'] = SMBusModule
sys.modules['smbus'].SMBus = SMBus sys.modules['smbus'].SMBus = SMBus
import ledd.daemon
import ledd
def pid_exists(processid): def pid_exists(processid):
if processid < 0: if processid < 0:
@@ -118,7 +118,7 @@ if __name__ == "__main__":
os.chdir(wdir) os.chdir(wdir)
with open("ledd.pid", 'w') as pidf: with open("ledd.pid", 'w') as pidf:
pidf.write(str(os.getpid()) + '\n') pidf.write(str(os.getpid()) + '\n')
daemon = ledd.daemon.Daemon() ledd.daemon.run()
else: else:
sys.exit() sys.exit()
else: else:
@@ -126,4 +126,4 @@ if __name__ == "__main__":
except OSError as e: except OSError as e:
log.fatal("Start failed: %s", e) log.fatal("Start failed: %s", e)
else: else:
daemon = ledd.daemon.Daemon() ledd.daemon.run()

View File

@@ -14,4 +14,13 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.ext.declarative import declarative_base
VERSION = "0.1" VERSION = "0.1"
engine = None
session = scoped_session(sessionmaker())
""" :type : sqlalchemy.orm.scoping.scoped_session """
Base = declarative_base()
Base.query = session.query_property()

View File

@@ -18,9 +18,12 @@ from json import JSONEncoder
import logging import logging
import time import time
import smbus from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import relationship
import smbus
from ledd.stripe import Stripe from ledd.stripe import Stripe
from . import Base
PCA9685_SUBADR1 = 0x2 PCA9685_SUBADR1 = 0x2
PCA9685_SUBADR2 = 0x3 PCA9685_SUBADR2 = 0x3
@@ -41,61 +44,25 @@ ALLLED_OFF_L = 0xFC
ALLLED_OFF_H = 0xFD 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. A controller controls a number of stripes.
""" """
@classmethod def __init__(self, *args, **kwargs):
def from_row(cls, db, row): super().__init__(*args, **kwargs)
# 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):
self._mode = None self._mode = None
self.channels = channels self.bus = smbus.SMBus(self.i2c_device)
self.i2c_device = i2c_device self._address = int(self.address, 16)
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()
def __repr__(self): def __repr__(self):
return "<Controller stripes={} cid={}>".format(len(self.stripes), self.id) return "<Controller stripes={} cid={}>".format(len(self.stripes), self.id)
@@ -114,9 +81,6 @@ class Controller:
def get_channel(self, channel): def get_channel(self, channel):
return self.bus.read_word_data(self._address, LED0_OFF_L + 4 * channel) / 4095 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): def reset(self):
self.mode = int("0b00100001", 2) # MODE1 -> 0b00000001 self.mode = int("0b00100001", 2) # MODE1 -> 0b00000001
time.sleep(0.015) time.sleep(0.015)

View File

@@ -17,58 +17,55 @@
import logging import logging
import configparser import configparser
import json import json
import sqlite3
import os import os
import sys import sys
import asyncio import asyncio
import signal import signal
from sqlalchemy import create_engine
from jsonrpc import JSONRPCResponseManager, dispatcher from jsonrpc import JSONRPCResponseManager, dispatcher
from jsonrpc.exceptions import JSONRPCError from jsonrpc.exceptions import JSONRPCError
import spectra 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.effectstack import EffectStack
from ledd.models import Meta
from ledd.stripe import Stripe from ledd.stripe import Stripe
from ledd.controller import Controller, ControllerEncoder
from . import Base, session
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class Daemon:
daemonSection = 'daemon' daemonSection = 'daemon'
databaseSection = 'db' databaseSection = 'db'
instance = None
""":type : Daemon """
loop = None loop = None
""" :type : asyncio.BaseEventLoop """ """ :type : asyncio.BaseEventLoop """
effects = [] effects = []
def __init__(self):
Daemon.instance = self
def run():
try: try:
# read config # read config
self.config = configparser.ConfigParser() config = configparser.ConfigParser()
try: try:
with open('ledd.config', 'w+') as f: with open('ledd.config', 'w+') as f:
self.config.read_file(f) config.read_file(f)
except FileNotFoundError: except FileNotFoundError:
log.info("No config file found!") log.info("No config file found!")
pass pass
# SQL init # SQL init
self.sqldb = sqlite3.connect(self.config.get(self.databaseSection, 'name', fallback='ledd.sqlite')) global engine
self.sqldb.row_factory = sqlite3.Row 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(): log.debug(Controller.query.all())
self.init_db()
self.sqldb.commit()
# init controllers from db
self.controllers = controller.Controller.from_db(self.sqldb)
log.debug(self.controllers)
logging.getLogger("asyncio").setLevel(log.getEffectiveLevel()) logging.getLogger("asyncio").setLevel(log.getEffectiveLevel())
# sigterm handler # sigterm handler
@@ -81,91 +78,74 @@ class Daemon:
# TODO: check all plugins for existing hooks # TODO: check all plugins for existing hooks
# main loop # main loop
self.loop = asyncio.get_event_loop() global loop
coro = self.loop.create_server(LedDProtocol, loop = asyncio.get_event_loop()
self.config.get(self.daemonSection, 'host', fallback='0.0.0.0'), coro = loop.create_server(LedDProtocol,
self.config.get(self.daemonSection, 'port', fallback=1425)) config.get(daemonSection, 'host', fallback='0.0.0.0'),
self.server = self.loop.run_until_complete(coro) config.get(daemonSection, 'port', fallback=1425))
self.loop.run_forever() global server
server = loop.run_until_complete(coro)
loop.run_forever()
except (KeyboardInterrupt, SystemExit): except (KeyboardInterrupt, SystemExit):
log.info("Exiting") log.info("Exiting")
try: try:
os.remove("ledd.pid") os.remove("ledd.pid")
except FileNotFoundError: except FileNotFoundError:
pass pass
self.sqldb.close() # TODO: close engine?
self.server.close() if server is not None:
self.loop.run_until_complete(self.server.wait_closed()) server.close()
self.loop.close() if loop is not None:
loop.run_until_complete(server.wait_closed())
loop.close()
sys.exit(0) sys.exit(0)
def check_db(self):
def check_db():
""" """
Checks database version Checks database version
:return: database validity :return: database validity
:rtype: bool :rtype: bool
""" """
c = self.sqldb.cursor()
try: try:
c.execute("SELECT value FROM meta WHERE option = 'db_version'") db_version = Meta.get_version()
db_version = c.fetchone()
c.close()
if db_version is not None: if db_version is not None:
log.info("DB connection established; db-version=%s", db_version[0]) log.info("DB connection established; db-version=%s", db_version)
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)
return True return True
else: except OperationalError:
return False return False
except sqlite3.OperationalError as e:
log.debug("SQLite error: %s", e)
c.close()
return False return False
def init_db(self):
self.sqldb.close() def init_db():
if os.path.exists("ledd.sqlite"): Base.metadata.drop_all()
os.remove("ledd.sqlite") Base.metadata.create_all()
self.sqldb = sqlite3.connect(self.config.get(self.databaseSection, 'name', fallback='ledd.sqlite')) session.add(Meta(option="db_version", value="2"))
self.sqldb.row_factory = sqlite3.Row session.commit()
with open("ledd/sql/ledd.sql", "r") as sqlfile: check_db()
c = self.sqldb.cursor()
c.executescript(sqlfile.read())
c.close()
self.check_db()
@dispatcher.add_method @dispatcher.add_method
def start_effect(self, **kwargs): def start_effect(**kwargs):
""" """
Part of the Color API. Used to start a specific effect. Part of the Color API. Used to start a specific effect.
Required JSON parameters: stripe IDs: sids; effect id: eid, effect options: eopt Required JSON parameters: stripe IDs: sids; effect id: eid, effect options: eopt
:param req_json: dict of request json :param req_json: dict of request json
""" """
stripes = [] stripes = []
if "sids" in kwargs: if "sids" in kwargs:
for sid in kwargs['sids']: for stripe in Stripe.query.filter(Stripe.id.in_(kwargs['sids'])):
found_s = self.find_stripe(sid)
if found_s is not None:
stripes.append(found_s)
if len(stripes) > 0:
# TODO: add anything required to start effect with req_json['eid'] # TODO: add anything required to start effect with req_json['eid']
# on stripes[] with options in req_json['eopt'] # on stripes[] with options in req_json['eopt']
effect = EffectStack() effect = EffectStack()
self.effects.append(effect) effects.append(effect)
effect.stripes.append(self.controllers[0].stripes[0]) effect.stripes.append(stripe)
effect.start() 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 = { rjson = {
'eident': None, # unique effect identifier that identifies excatly this effect started on this set of 'eident': None, # unique effect identifier that identifies excatly this effect started on this set of
@@ -176,8 +156,9 @@ class Daemon:
else: else:
return JSONRPCError(-1003, "Stripeid not found") return JSONRPCError(-1003, "Stripeid not found")
@dispatcher.add_method @dispatcher.add_method
def stop_effect(self, **kwargs): def stop_effect(**kwargs):
""" """
Part of the Color API. Used to stop a specific effect. Part of the Color API. Used to stop a specific effect.
Required JSON parameters: effect identifier: eident Required JSON parameters: effect identifier: eident
@@ -186,8 +167,9 @@ class Daemon:
# TODO: add stop effect by eident logic # TODO: add stop effect by eident logic
@dispatcher.add_method @dispatcher.add_method
def get_effects(self, **kwargs): def get_effects(**kwargs):
""" """
Part of the Color API. Used to show all available and running effects. Part of the Color API. Used to show all available and running effects.
Required JSON parameters: - Required JSON parameters: -
@@ -198,23 +180,23 @@ class Daemon:
# TODO: all effects get runtime only ids, "eid"'s. They are shown here for the client to start effects. # 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". # TODO: All options that an effect may have need to be transmitted here too with "eopt".
@dispatcher.add_method @dispatcher.add_method
def set_color(self, **kwargs): def set_color(**kwargs):
""" """
Part of the Color API. Used to set color of a stripe. 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 Required JSON parameters: stripe ID: sid; HSV values hsv: h,s,v, controller id: cid
:param req_json: dict of request json :param req_json: dict of request json
""" """
try:
found_s = self.find_stripe(kwargs['sid']) stripe = Stripe.query.filter(Stripe.id == kwargs['sid']).one()
stripe.set_color(spectra.hsv(kwargs['hsv']['h'], kwargs['hsv']['s'], kwargs['hsv']['v']))
if found_s is None: except NoResultFound:
log.warning("Stripe not found: id=%s", kwargs['sid']) 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 @dispatcher.add_method
def add_controller(self, **kwargs): def add_controller(**kwargs):
""" """
Part of the Color API. Used to add a controller. 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); Required JSON parameters: channels; i2c_dev: number of i2c device (e.g. /dev/i2c-1 would be i2c_dev = 1);
@@ -222,13 +204,14 @@ class Daemon:
:param req_json: dict of request json :param req_json: dict of request json
""" """
try: try:
ncontroller = controller.Controller(Daemon.instance.sqldb, kwargs['channels'], ncontroller = Controller(channels=int(kwargs['channels']), i2c_device=int(kwargs['i2c_dev']),
kwargs['i2c_dev'], kwargs['address']) address=kwargs['address'])
except OSError as e: except OSError as e:
log.error("Error opening i2c device: %s (%s)", kwargs['i2c_dev'], e) log.error("Error opening i2c device: %s (%s)", kwargs['i2c_dev'], e)
return JSONRPCError(-1004, "Error while opening i2c device", e) return JSONRPCError(-1004, "Error while opening i2c device", e)
self.controllers.append(ncontroller) session.add(ncontroller)
session.commit()
rjson = { rjson = {
'cid': ncontroller.id, 'cid': ncontroller.id,
@@ -236,28 +219,29 @@ class Daemon:
return rjson return rjson
@dispatcher.add_method @dispatcher.add_method
def get_color(self, **kwargs): def get_color(**kwargs):
""" """
Part of the Color API. Used to get the current color of an stripe. Part of the Color API. Used to get the current color of an stripe.
Required JSON parameters: stripes Required JSON parameters: stripes
:param req_json: dict of request json :param req_json: dict of request json
""" """
try:
found_s = self.find_stripe(kwargs['sid']) stripe = Stripe.query.filter(Stripe.id == kwargs['sid']).one()
except NoResultFound:
if found_s is None: log.warning("Stripe not found: id=%s", kwargs['sid'])
log.warning("StripeId not found: id=%s", kwargs['sid'])
return JSONRPCError(-1003, "Stripeid not found") return JSONRPCError(-1003, "Stripeid not found")
rjson = { rjson = {
'color': found_s.color.values, 'color': stripe.color.values,
} }
return rjson return rjson
@dispatcher.add_method @dispatcher.add_method
def add_stripe(self, **kwargs): def add_stripe(**kwargs):
""" """
Part of the Color API. Used to add stripes. 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 Required JSON parameters: name; rgb: bool; map: r: r-channel, g: g-channel, b: b-channel, cid
@@ -266,16 +250,15 @@ class Daemon:
if "stripe" in kwargs: if "stripe" in kwargs:
stripe = kwargs['stripe'] stripe = kwargs['stripe']
c = next((x for x in self.controllers if x.id == stripe['cid']), None) c = Controller.query.filter(Controller.id == int(stripe['cid'])).first()
""" :type c: ledd.controller.Controller """ """ :type c: ledd.controller.Controller """
if c is None: if c is None:
return JSONRPCError(-1002, "Controller not found") return JSONRPCError(-1002, "Controller not found")
s = Stripe(c, stripe['name'], stripe['rgb'], s = Stripe(name=stripe['name'], rgb=bool(stripe['rgb']),
(stripe['map']['r'], stripe['map']['g'], stripe['map']['b'])) channel_r=stripe['map']['r'], channel_g=stripe['map']['g'], channel_b=stripe['map']['b'])
s.controller = c
c.stripes.append(s)
log.debug("Added stripe %s to controller %s; new len %s", c.id, s.id, len(c.stripes)) log.debug("Added stripe %s to controller %s; new len %s", c.id, s.id, len(c.stripes))
rjson = { rjson = {
@@ -284,8 +267,9 @@ class Daemon:
return rjson return rjson
@dispatcher.add_method @dispatcher.add_method
def get_stripes(self, **kwargs): def get_stripes(**kwargs):
""" """
Part of the Color API. Used to get all registered stripes known to the daemon. Part of the Color API. Used to get all registered stripes known to the daemon.
Required JSON parameters: none Required JSON parameters: none
@@ -293,28 +277,30 @@ class Daemon:
""" """
rjson = { rjson = {
'ccount': len(Daemon.instance.controllers), 'ccount': len(Controller.query),
'controller': json.dumps(Daemon.instance.controllers, cls=controller.ControllerEncoder), 'controller': json.dumps(Controller.query, cls=ControllerEncoder),
} }
return rjson return rjson
@dispatcher.add_method @dispatcher.add_method
def test_channel(self, **kwargs): def test_channel(**kwargs):
""" """
Part of the Color API. Used to test a channel on a specified controller. Part of the Color API. Used to test a channel on a specified controller.
Required JSON parameters: controller id: cid, channel, value Required JSON parameters: controller id: cid, channel, value
:param req_json: dict of request json :param req_json: dict of request json
""" """
result = next(filter(lambda x: x.id == kwargs['cid'], self.controllers), None) result = Controller.query.filter(Controller.id == kwargs['cid']).first()
""" :type : ledd.controller.Controller """ """ :type : ledd.controller.Controller """
if result is not None: if result is not None:
result.set_channel(kwargs['channel'], kwargs['value'], 2.8) result.set_channel(kwargs['channel'], kwargs['value'], 2.8)
@dispatcher.add_method @dispatcher.add_method
def discover(self, **kwargs): def discover(**kwargs):
""" """
Part of the Color API. Used by mobile applications to find the controller. Part of the Color API. Used by mobile applications to find the controller.
Required JSON parameters: none Required JSON parameters: none
@@ -328,19 +314,17 @@ class Daemon:
return rjson return rjson
def find_stripe(self, sid):
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 Finds a given stripeid in the currently known controllers
:param sid stripe id :param sid stripe id
:return: stripe if found or none :return: stripe if found or none
:rtype: ledd.Stripe | 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): class LedDProtocol(asyncio.Protocol):

29
ledd/models.py Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.
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()

View File

@@ -13,44 +13,42 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from spectra import Color 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. 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): @property
self.controller = controller def channels(self):
self.name = name return self.channel_r, self.channel_b, self.channel_g
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()
def save_to_db(self): # TODO save channels to db
cur = self.controller.db.cursor()
if self.id == -1: @channels.setter
cur.execute("INSERT INTO stripes DEFAULT VALUES") def channels(self, t):
self.id = cur.lastrowid self.channel_r, self.channel_g, self.channel_b = t
cur.execute(
"UPDATE stripes SET " def __init__(self, *args, **kwargs):
"channel_r_gamma = ?," super().__init__(*args, **kwargs)
"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()
def read_color(self): def read_color(self):
rc = tuple([float(self.controller.get_channel(channel)) for channel in self.channels]) rc = tuple([float(self.controller.get_channel(channel)) for channel in self.channels])
@@ -60,12 +58,6 @@ class Stripe:
def __repr__(self): def __repr__(self):
return "<Stripe id={}>".format(self.id) return "<Stripe id={}>".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): def set_color(self, c):
self._color = c self._color = c
for channel, gamma_correct, value in zip(self.channels, self.gamma_correct, c.clamped_rgb): for channel, gamma_correct, value in zip(self.channels, self.gamma_correct, c.clamped_rgb):

View File

@@ -25,6 +25,6 @@ setup(name='LedD',
license='GPLv3', license='GPLv3',
packages=['ledd'], packages=['ledd'],
install_requires=[ install_requires=[
'nose', 'spectra', 'docopt', 'jsonrpc', 'nose', 'spectra', 'docopt', 'jsonrpc', 'sqlalchemy',
], ],
zip_safe=False) zip_safe=False)