diff --git a/ledd/daemon.py b/ledd/daemon.py index a1713a3..526caab 100644 --- a/ledd/daemon.py +++ b/ledd/daemon.py @@ -20,14 +20,15 @@ import json import sqlite3 import os import sys -import traceback import asyncio import signal +from jsonrpc import JSONRPCResponseManager, dispatcher + +from jsonrpc.exceptions import JSONRPCError import spectra from ledd import controller, VERSION -from ledd.decorators import ledd_protocol from ledd.effectstack import EffectStack from ledd.stripe import Stripe @@ -41,7 +42,6 @@ class Daemon: """:type : Daemon """ loop = None """ :type : asyncio.BaseEventLoop """ - protocol = {} effects = [] def __init__(self): @@ -73,7 +73,7 @@ class Daemon: # sigterm handler def sigterm_handler(): - sys.exit(0) + raise SystemExit signal.signal(signal.SIGTERM, sigterm_handler) @@ -141,19 +141,17 @@ class Daemon: c.close() self.check_db() - @ledd_protocol(protocol) - def start_effect(self, req_json): + @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 """ - log.debug("recieved action: %s", req_json['action']) - stripes = [] - if "sids" in req_json: - for sid in req_json['sids']: + if "sids" in kwargs: + for sid in kwargs['sids']: found_s = self.find_stripe(sid) if found_s is not None: @@ -170,141 +168,109 @@ class Daemon: # asyncio.ensure_future(asyncio.get_event_loop().run_in_executor(self.executor, effect.execute)) rjson = { - 'success': True, 'eident': None, # unique effect identifier that identifies excatly this effect started on this set of # stripes, used to stop them later and to give informations about running effects - 'ref': req_json['ref'] } - return json.dumps(rjson) + return rjson else: - rjson = { - 'success': False, - 'message': "No stripe with this id found", - 'ref': req_json['ref'] - } + return JSONRPCError(-1003, "Stripeid not found") - return json.dumps(rjson) - - @ledd_protocol(protocol) - def stop_effect(self, req_json): + @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 """ - log.debug("recieved action: %s", req_json['action']) # TODO: add stop effect by eident logic - @ledd_protocol(protocol) - def get_effects(self, req_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 """ - log.debug("recieved action: %s", req_json['action']) # 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". - @ledd_protocol(protocol) - def set_color(self, req_json): + @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 """ - log.debug("recieved action: %s", req_json['action']) - found_s = self.find_stripe(req_json['sid']) + found_s = self.find_stripe(kwargs['sid']) if found_s is None: - log.warning("Stripe not found: id=%s", req_json['sid']) + log.warning("Stripe not found: id=%s", kwargs['sid']) else: - found_s.set_color(spectra.hsv(req_json['hsv']['h'], req_json['hsv']['s'], req_json['hsv']['v'])) + found_s.set_color(spectra.hsv(kwargs['hsv']['h'], kwargs['hsv']['s'], kwargs['hsv']['v'])) - @ledd_protocol(protocol) - def add_controller(self, req_json): + @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 """ - log.debug("recieved action: %s", req_json['action']) try: - ncontroller = controller.Controller(Daemon.instance.sqldb, req_json['channels'], - req_json['i2c_dev'], req_json['address']) + 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)", req_json['i2c_dev'], os.strerror(int(str(e)))) - rjson = { - 'success': False, - 'message': "Error while opening i2c device", - 'message_detail': os.strerror(int(str(e))), - 'ref': req_json['ref'] - } - return json.dumps(rjson) + log.error("Error opening i2c device: %s (%s)", kwargs['i2c_dev'], e) + return JSONRPCError(-1004, "Error while opening i2c device", e) self.controllers.append(ncontroller) rjson = { - 'success': True, 'cid': ncontroller.id, - 'ref': req_json['ref'] } - return json.dumps(rjson) + return rjson - @ledd_protocol(protocol) - def get_color(self, req_json): + @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 """ - log.debug("recieved action: %s", req_json['action']) - found_s = self.find_stripe(req_json['sid']) + found_s = self.find_stripe(kwargs['sid']) if found_s is None: - log.warning("Stripe not found: id=%s", req_json['sid']) - return { - 'success': False, - 'message': "Stripe not found", - 'ref': req_json['ref'] - } + log.warning("StripeId not found: id=%s", kwargs['sid']) + return JSONRPCError(-1003, "Stripeid not found") rjson = { - 'success': True, 'color': found_s.color.values, - 'ref': req_json['ref'] } - return json.dumps(rjson) + return rjson - @ledd_protocol(protocol) - def add_stripe(self, req_json): + @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 """ - log.debug("recieved action: %s", req_json['action']) - if "stripe" in req_json: - stripe = req_json['stripe'] + 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 """ if c is None: - return { - 'success': False, - 'message': "Controller not found", - 'ref': stripe['ref'] - } + return JSONRPCError(-1002, "Controller not found") s = Stripe(c, stripe['name'], stripe['rgb'], (stripe['map']['r'], stripe['map']['g'], stripe['map']['b'])) @@ -313,77 +279,54 @@ class Daemon: log.debug("Added stripe %s to controller %s; new len %s", c.id, s.id, len(c.stripes)) rjson = { - 'success': True, 'sid': s.id, - 'ref': req_json['ref'] } - return json.dumps(rjson) + return rjson - @ledd_protocol(protocol) - def get_stripes(self, req_json): + @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 """ - log.debug("recieved action: %s", req_json['action']) rjson = { - 'success': True, 'ccount': len(Daemon.instance.controllers), - 'controller': Daemon.instance.controllers, - 'ref': req_json['ref'] + 'controller': json.dumps(Daemon.instance.controllers, cls=controller.ControllerEncoder), } - return json.dumps(rjson, cls=controller.ControllerEncoder) + return rjson - @ledd_protocol(protocol) - def test_channel(self, req_json): + @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 """ - log.debug("recieved action: %s", req_json['action']) - result = next(filter(lambda x: x.id == req_json['cid'], self.controllers), None) + result = next(filter(lambda x: x.id == kwargs['cid'], self.controllers), None) """ :type : ledd.controller.Controller """ if result is not None: - result.set_channel(req_json['channel'], req_json['value'], 2.8) + result.set_channel(kwargs['channel'], kwargs['value'], 2.8) - rjson = { - 'success': True, - 'ref': req_json['ref'] - } - - return json.dumps(rjson) - - @ledd_protocol(protocol) - def discover(self, req_json): + @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", req_json['action']) + log.debug("recieved action: %s", kwargs['action']) rjson = { - 'success': True, - 'ref': req_json['ref'], 'version': VERSION } - return json.dumps(rjson) - - def no_action_found(self, req_json): - rjson = { - 'success': False, - 'message': "No action found", - 'ref': req_json['ref'] - } - return json.dumps(rjson) + return rjson def find_stripe(self, sid): """ @@ -418,25 +361,11 @@ class LedDProtocol(asyncio.Protocol): def select_task(self, data): if data: - try: - data_split = data.splitlines() - log.debug(data_split) - for line in data_split: - if line: - json_decoded = json.loads(line) - - if "action" in json_decoded and "ref" in json_decoded: - return_data = Daemon.instance.protocol.get(json_decoded['action'], Daemon.no_action_found)( - Daemon.instance, json_decoded) - - if return_data is not None: - self.transport.write("{}\n".format(return_data).encode()) - else: - log.debug("no action or ref value found in JSON, ignoring") - except TypeError: - log.debug("No valid JSON found: %s", traceback.format_exc()) - except ValueError: - log.debug("No valid JSON detected: %s", traceback.format_exc()) + data_split = data.splitlines() + log.debug(data_split) + for line in data_split: + if line: + self.transport.write(JSONRPCResponseManager.handle(line, dispatcher)) def connection_lost(self, exc): # The socket has been closed, stop the event loop diff --git a/ledd/decorators.py b/ledd/decorators.py deleted file mode 100644 index 3d873c5..0000000 --- a/ledd/decorators.py +++ /dev/null @@ -1,33 +0,0 @@ -# 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 . - - -def ledd_protocol(proto): - """ - Decorator used to add functions to action dict - :param proto: dict to add to - :type proto: dict - """ - - def wrap(f): - proto[f.__name__] = f - - def wrapped_f(*args): - f(*args) - - return wrapped_f - - return wrap diff --git a/setup.py b/setup.py index 6d4c2e9..8106093 100644 --- a/setup.py +++ b/setup.py @@ -25,6 +25,6 @@ setup(name='LedD', license='GPLv3', packages=['ledd'], install_requires=[ - 'nose', 'spectra', 'docopt', + 'nose', 'spectra', 'docopt', 'jsonrpc', ], zip_safe=False)