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)