diff --git a/.gitignore b/.gitignore index b84bb87..adc257d 100755 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + # temp files from editors *~ *.bak @@ -16,8 +21,6 @@ nbproject/ /node_modules/ # application -/db_repository/ -app.db ir_tmp_code.txt # node-red @@ -26,6 +29,4 @@ node-red/* !node-red/Dockerfile_rpi !node-red/smart_remote.json -# Docker files -/docker/* -!/docker/.* +*.log diff --git a/Dockerfile b/Dockerfile index f435a43..b80506e 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,15 @@ -FROM python:2 +FROM python:3 + +ENV TZ=Asia/Tokyo +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone RUN mkdir /app WORKDIR /app -COPY requirements.txt ./ -RUN pip install --no-cache-dir -r requirements.txt +# RUN pip3 install -U pip +# RUN pip install -U setuptools +# RUN pip install -U wheel +RUN pip install --upgrade pip +COPY requirements.txt ./ +RUN pip3 install --no-cache-dir -r requirements.txt diff --git a/Dockerfile_rpi b/Dockerfile_rpi deleted file mode 100644 index c4a3a2b..0000000 --- a/Dockerfile_rpi +++ /dev/null @@ -1,23 +0,0 @@ -FROM resin/rpi-raspbian:jessie - -# Install dependencies -RUN apt-get update && apt-get upgrade && apt-get install -y \ - python \ - python-dev \ - python-pip \ - python-virtualenv \ - lirc \ - --no-install-recommends && \ - rm -rf /var/lib/apt/lists/* - -RUN mkdir /app -WORKDIR /app - -COPY requirements.txt ./ -RUN pip install -r requirements.txt - -ADD lirc/hardware.conf /etc/lirc/hardware.conf -ADD lirc/lirc_options.conf /etc/lirc/lirc_options.conf - -RUN echo 'lirc_dev' >> /etc/modules -RUN echo 'lirc_rpi gpio_in_pin=18 gpio_out_pin=17' >> /etc/modules diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..568a2a3 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +ALL: up-dev +build-dev: + docker-compose -f docker-compose.yml build +build-server: + docker-compose -f docker-compose-server.yml build +up-dev: + docker-compose -f docker-compose.yml up -d +up-server: + docker-compose -f docker-compose-server.yml up -d +stop: + docker-compose stop +migrate: + docker-compose exec web flask db upgrade +rollback: + docker-compose exec web flask db downgrade diff --git a/README.md b/README.md index f3de4db..5ff53bf 100755 --- a/README.md +++ b/README.md @@ -8,14 +8,14 @@ $ git clone https://github.com/notfoundsam/smart-remote.git $ cd smart-remote ``` -Install python requirements +## Run services on local machine by docker or create each service by yourself (mysql, node-red, influxdb, grafana, mosquitto) ```bash -$ pip install --no-cache-dir -r requirements.txt +$ docker-compose -f docker-compose-server.yml up -d ``` -Create Data Base +## Install python requirements ```bash -$ python db_create.py +$ pip install --no-cache-dir -r requirements.txt ``` Run the application @@ -28,6 +28,17 @@ Check it http://localhost:5000 ``` +## Flask create migration +```bash +$ flask db migrate --rev-id 001 +``` + +## Flask migrate db +```bash +$ flask db upgrade +$ flask db downgrade +``` + ## Run development mode on docker ```bash @@ -38,3 +49,19 @@ $ docker-compose up ```bash $ docker stack deploy --compose-file docker-compose-swarm.yml smart-home ``` + + + + + + + + + +### node red as volume in ubuntu, set chmod 777 to mounted folder + +### pip update packages +`pip list --outdated --format=freeze | grep -v '^\-e' | cut -d = -f 1 | xargs -n1 pip install -U` + +### generate requirements +pip freeze > requirements.txt diff --git a/app/__init__.py b/app/__init__.py index d8c6213..35419f0 100755 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,26 +1,531 @@ -from __future__ import print_function -import os, sys -from flask import Flask, g +import os, functools, json, logging +from flask import Flask, redirect, session, request, g, jsonify, make_response, abort, send_from_directory from flask_sqlalchemy import SQLAlchemy -from flask_login import LoginManager -from flask_socketio import SocketIO +from flask_login import LoginManager, login_user, logout_user, current_user, login_required +from flask_socketio import SocketIO, emit +from flask_cors import CORS +from flask_migrate import Migrate +from app.bootstrap import Config +from threading import Lock -app = Flask(__name__) -app.config.from_object('config') -db = SQLAlchemy(app) -so = SocketIO(app) +flask_app = Flask(__name__) +config = Config(flask_app) +CORS(flask_app, supports_credentials=True) + +db = SQLAlchemy(flask_app) +mg = Migrate(flask_app, db) +so = SocketIO(flask_app) lm = LoginManager() -lm.init_app(app) -lm.login_view = 'login' - -from app import routes, models, sockets -from sensor import StateChecker -import threading -# from drivers import wire_recive -from run import arduino - -@app.before_first_request -def before_first_request(): - arduino.startQueue() - status = StateChecker() - status.start() +lm.init_app(flask_app) + +from app import service +from app.helpers import RcHelper, ButtonHelper, NodeHelper, ArduinoHelper, RadioHelper +from app.models import User + +serv = service.Service(config) + +@flask_app.before_first_request +def activate_services(): + serv.activateDiscoverService() + serv.activateNodeService() + +@lm.user_loader +def load_user(id): + return User.query.get(int(id)) + +@lm.unauthorized_handler +def unauthorized(): + return make_response(jsonify({'error': 'Unauthorized'}), 401) + +@flask_app.before_request +def before_request(): + g.user = current_user + +@flask_app.errorhandler(404) +def not_found(error): + return make_response(jsonify({'error': 'Not found'}), 404) + +@flask_app.errorhandler(400) +def not_found(error): + return make_response(jsonify({'error': 'Bad request'}), 400) + +@flask_app.route('/favicon.ico') +def favicon(): + return send_from_directory(os.path.join(flask_app.root_path, 'static'), + 'favicon.ico', mimetype='image/vnd.microsoft.icon') + +@flask_app.route('/api/v1/login', methods=['POST']) +def login(): + if g.user is not None and g.user.is_authenticated: + return jsonify({'result': True}) + + if request.json and 'username' in request.json and 'password' in request.json: + username = request.json['username'] + password = request.json['password'] + + user = User.query.filter_by(username=username).first() + + if user is not None and user.password == password: + session['remember_me'] = True + login_user(user) + + return jsonify({'result': True}) + + return jsonify({'result': False}), 403 + +@flask_app.route('/api/v1/logout', methods=['GET']) +def logout(): + logout_user() + return jsonify({'result': True}) + +@login_required +@flask_app.route('/api/v1/user', methods=['GET']) +def get_user(): + return jsonify({'username': g.user.username}) + +# Rc routes +@flask_app.route('/api/v1/rcs', methods=['GET']) +@login_required +def get_rcs(): + rch = RcHelper() + return jsonify({'rcs': rch.getRcs()}) + +@flask_app.route('/api/v1/rcs', methods=['POST']) +@login_required +def create_rc(): + if not request.json or not 'name' in request.json or not 'icon' in request.json or not 'order' in request.json or not 'public' in request.json: + abort(400) + + rch = RcHelper() + rc = rch.createRc(request.json) + so.emit('updateRcs', {'rcs': rch.getRcs()}, broadcast=True) + return jsonify({'rc': rc}), 201 + +@flask_app.route('/api/v1/rcs/', methods=['GET']) +@login_required +def get_rc(rc_id): + rch = RcHelper(rc_id) + rc = rch.getRc() + + if rc is None: + abort(404) + + return jsonify({'rc': rc}) + +@flask_app.route('/api/v1/rcs/', methods=['PUT']) +@login_required +def update_rc(rc_id): + rch = RcHelper(rc_id) + + if not request.json or not 'name' in request.json or not 'icon' in request.json or not 'order' in request.json or not 'public' in request.json: + abort(400) + + rc = rch.updateRc(request.json) + + if rc is None: + abort(404) + + so.emit('updateRcs', {'rcs': rch.getRcs()}, broadcast=True) + return jsonify({'rc': rc}) + +@flask_app.route('/api/v1/rcs/', methods=['DELETE']) +@login_required +def delete_rc(rc_id): + rch = RcHelper(rc_id) + result = rch.deleteRc() + + if result is None: + abort(404) + + so.emit('updateRcs', {'rcs': rch.getRcs()}, broadcast=True) + return jsonify({'result': result}) + +@flask_app.route('/api/v1/rcs//buttons', methods=['GET']) +@login_required +def get_rc_buttons(rc_id): + rch = RcHelper(rc_id) + buttons = rch.getButtons() + + if buttons is None: + abort(404) + + return jsonify({'buttons': buttons}) + +# Button routes +@flask_app.route('/api/v1/buttons', methods=['POST']) +@login_required +def create_button(): + bh = ButtonHelper() + + if not request.json or not 'rc_id' in request.json or not 'name' in request.json or not 'order_hor' in request.json or not 'order_ver' in request.json or not 'color' in request.json or not 'message' in request.json or not 'type' in request.json or not 'radio_id' in request.json: + abort(400) + + button = bh.createButton(request.json) + + if button is None: + abort(404) + + rch = RcHelper(request.json['rc_id']) + so.emit('updateButtons', {'rc_id': button['rc_id'], 'buttons': rch.getButtons()}, broadcast=True) + return jsonify({'button': button}), 201 + +@flask_app.route('/api/v1/buttons/', methods=['GET']) +@login_required +def get_button(btn_id): + bh = ButtonHelper(btn_id) + button = bh.getButton() + + if button is None: + abort(404) + + return jsonify({'button': button}) + +@flask_app.route('/api/v1/buttons/', methods=['PUT']) +@login_required +def update_button(btn_id): + bh = ButtonHelper(btn_id) + + if not request.json or not 'name' in request.json or not 'order_hor' in request.json or not 'order_ver' in request.json or not 'color' in request.json or not 'message' in request.json or not 'type' in request.json or not 'radio_id' in request.json: + abort(400) + + button = bh.updateButton(request.json) + + if button is None: + abort(404) + + rch = RcHelper(bh.get().rc_id) + so.emit('updateButtons', {'rc_id': button['rc_id'], 'buttons': rch.getButtons()}, broadcast=True) + return jsonify({'button': button}) + +@flask_app.route('/api/v1/buttons/', methods=['DELETE']) +@login_required +def delete_button(btn_id): + bh = ButtonHelper(btn_id) + button = bh.deleteButton() + + if button is None: + abort(404) + + so.emit('updateButtons', {'rc_id': button['rc_id'], 'buttons': bh.getButtons()}, broadcast=True) + return jsonify({'result': True}) + +@flask_app.route('/api/v1/buttons//push', methods=['GET']) +@login_required +def push_button(btn_id): + bh = ButtonHelper(btn_id) + button = bh.getButton() + + if bh.get() is None: + abort(404) + + if bh.get().type == 'radio': + logging.info(bh.getHostName()) + event = { + 'event': 'pushButton', + 'user_id': g.user.id, + 'button_id': bh.get().id, + 'host_name': bh.getHostName() + } + result = serv.node_sevice.pushToNode(event) + + return jsonify({'result': result}) + +# Node routes +@flask_app.route('/api/v1/nodes', methods=['GET']) +@login_required +def get_nodes(): + nh = NodeHelper() + return jsonify({'nodes': nh.getNodes()}) + +@flask_app.route('/api/v1/nodes', methods=['POST']) +@login_required +def create_node(): + if not request.json or not 'name' in request.json or not 'host_name' in request.json or not 'order' in request.json: + abort(400) + + nh = NodeHelper() + node = nh.createNode(request.json) + so.emit('updateNodes', {'nodes': nh.getNodes()}, broadcast=True) + return jsonify({'node': node}), 201 + +@flask_app.route('/api/v1/nodes/', methods=['GET']) +@login_required +def get_node(node_id): + nh = NodeHelper(node_id) + node = nh.getNode() + + if node is None: + abort(404) + + return jsonify({'node': node}) + +@flask_app.route('/api/v1/nodes/', methods=['PUT']) +@login_required +def update_node(node_id): + nh = NodeHelper(node_id) + + if not request.json or not 'host_name' in request.json or not 'order' in request.json: + abort(400) + + node = nh.updateNode(request.json) + + if node is None: + abort(404) + + so.emit('updateNodes', {'nodes': nh.getNodes()}, broadcast=True) + return jsonify({'node': node}) + +@flask_app.route('/api/v1/nodes/', methods=['DELETE']) +@login_required +def delete_node(node_id): + nh = NodeHelper(node_id) + result = nh.deleteNode() + + if result is None: + abort(404) + + so.emit('updateNodes', {'nodes': nh.getNodes()}, broadcast=True) + return jsonify({'result': result}) + +# Arduino routes +@flask_app.route('/api/v1/arduinos', methods=['GET']) +@login_required +def get_arduinos(): + ah = ArduinoHelper() + arduinos = ah.getArduinos() + + if arduinos is None: + abort(404) + + return jsonify({'arduinos': arduinos}) + +@flask_app.route('/api/v1/arduinos', methods=['POST']) +@login_required +def create_arduino(): + ah = ArduinoHelper() + + if not request.json or not 'usb' in request.json or not 'node_id' in request.json or not 'name' in request.json or not 'order' in request.json: + abort(400) + + arduino = ah.createArduino(request.json) + + if arduino is None: + abort(404) + + so.emit('updateArduinos', {'node_id': arduino['node_id'], 'arduinos': ah.getArduinos()}, broadcast=True) + event = { + 'event': 'restart', + 'host_name': ah.getNode().host_name + } + if serv.node_sevice.pushToNode(event) == False: + pass + # so.emit('recievedIr', {'result': 'error', 'errors': 'Node is offline'}) + + return jsonify({'arduino': arduino}), 201 + +@flask_app.route('/api/v1/arduinos/', methods=['GET']) +@login_required +def get_arduino(arduino_id): + ah = ArduinoHelper(arduino_id) + arduino = ah.getArduino() + + if arduino is None: + abort(404) + + return jsonify({'arduino': arduino}) + +@flask_app.route('/api/v1/arduinos/', methods=['PUT']) +@login_required +def update_arduino(arduino_id): + ah = ArduinoHelper(arduino_id) + + if not request.json or not 'usb' in request.json or not 'node_id' in request.json or not 'name' in request.json or not 'order' in request.json: + abort(400) + + arduino = ah.updateArduino(request.json) + + if arduino is None: + abort(404) + + so.emit('updateArduinos', {'node_id': arduino['node_id'], 'arduinos': ah.getArduinos()}, broadcast=True) + event = { + 'event': 'restart', + 'host_name': ah.getNode().host_name + } + if serv.node_sevice.pushToNode(event) == False: + pass + # so.emit('recievedIr', {'result': 'error', 'errors': 'Node is offline'}) + + return jsonify({'arduino': arduino}) + +@flask_app.route('/api/v1/arduinos/', methods=['DELETE']) +@login_required +def delete_arduino(arduino_id): + ah = ArduinoHelper(arduino_id) + host_name = ah.getNode().host_name + arduino = ah.deleteArduino() + + if arduino is None: + abort(404) + + so.emit('updateArduinos', {'node_id': arduino['node_id'], 'arduinos': ah.getArduinos()}, broadcast=True) + event = { + 'event': 'restart', + 'host_name': host_name + } + if serv.node_sevice.pushToNode(event) == False: + pass + # so.emit('recievedIr', {'result': 'error', 'errors': 'Node is offline'}) + + return jsonify({'result': True}) + +# Radio routes +@flask_app.route('/api/v1/radios', methods=['GET']) +@login_required +def get_radios(): + rh = RadioHelper() + return jsonify({'radios': rh.getRadios()}) + +@flask_app.route('/api/v1/radios', methods=['POST']) +@login_required +def create_radio(): + if not request.json or not 'arduino_id' in request.json or not 'type' in request.json or not 'name' in request.json or not 'pipe' in request.json or not 'order' in request.json or not 'on_request' in request.json or not 'expired_after' in request.json or not 'enabled' in request.json: + abort(400) + + rh = RadioHelper() + radio = rh.createRadio(request.json) + + so.emit('updateRadios', {'radios': rh.getRadios()}, broadcast=True) + return jsonify({'radio': radio}), 201 + +@flask_app.route('/api/v1/radios/', methods=['GET']) +@login_required +def get_radio(radio_id): + rh = RadioHelper(radio_id) + radio = rh.getRadio() + + if radio is None: + abort(404) + + return jsonify({'radio': radio}) + +@flask_app.route('/api/v1/radios/', methods=['PUT']) +@login_required +def update_radio(radio_id): + rh = RadioHelper(radio_id) + + if not request.json or not 'pipe' in request.json or not 'name' in request.json or not 'enabled' in request.json or not 'order' in request.json: + abort(400) + + radio = rh.updateRadio(request.json) + + if radio is None: + abort(404) + + so.emit('updateRadios', {'radios': rh.getRadios()}, broadcast=True) + return jsonify({'radio': radio}) + +@flask_app.route('/api/v1/radios/', methods=['DELETE']) +@login_required +def delete_radio(radio_id): + rh = RadioHelper(radio_id) + result = rh.deleteRadio() + + if result is None: + abort(404) + + so.emit('updateRadios', {'radios': rh.getRadios()}, broadcast=True) + return jsonify({'result': result}) + +############# +# Socket io # +############# +def authenticated_only(f): + @functools.wraps(f) + def wrapped(*args, **kwargs): + if not current_user.is_authenticated: + disconnect() + else: + return f(*args, **kwargs) + return wrapped + +@so.on('connect') +def handle_connect(): + id = request.sid + logging.info("%s socket connected" % id) + emit('customEmit', {'data': 'Connected', 'count': 0}, broadcast=True) + +@so.on('json') +@authenticated_only +def handle_json(data): + # Debug + logging.info("received json: %s" % data) + + # if data['action'] == 'catch_ir_signal': + # # signal = ir_reader.read_signal() + # emit('json', {'response': {'result': 'success', 'callback': 'ir_signal', 'signal': '1500 800 800 800 1500 1500'}}) + + +# thread = None +# thread_lock = Lock() + +# def background_thread(): +# """Example of how to send server generated events to clients.""" +# count = 0 +# # status = True +# while True: +# so.sleep(20) +# count += 1 +# so.emit('test', {'count': count}, broadcast=True) + + # if status: + # status = False + # event = { + # 'event': 'stop', + # 'host_name': 'rpi-node-1' + # } + # else: + # status = True + # event = { + # 'event': 'start', + # 'host_name': 'rpi-node-1' + # } + + # event = { + # 'event': 'restart', + # 'host_name': 'rpi-node-1' + # } + # if serv.node_sevice.pushToNode(event) == False: + # pass + # so.emit('recievedIr', {'result': 'error', 'errors': 'Node is offline'}) + # nh = NodeHelper() + # so.emit('updateNodes', {'nodes': nh.getNodes()}, broadcast=True) + +# @so.on('connect') +# def test_connect(): +# global thread +# with thread_lock: +# if thread is None: +# thread = so.start_background_task(target=background_thread) + # so.emit('test', ('foo', 'bar'), broadcast=True) + +@so.on('catch_ir') +def handle_catch_ir(json_data): + data = json.loads(json_data) + + if 'node_id' in data: + node = NodeHelper(data['node_id']).get() + + if node is not None: + event = { + 'event': 'catchIr', + 'user_id': current_user.id, + 'host_name': node.host_name + } + + if serv.node_sevice.pushToNode(event) == False: + so.emit('recievedIr', {'result': 'error', 'errors': 'Node is offline'}) + +@so.on('emit_method') +def handle_emit_method(message): + logging.info("received emit_method: %s" % message) diff --git a/app/bootstrap.py b/app/bootstrap.py new file mode 100644 index 0000000..d67895c --- /dev/null +++ b/app/bootstrap.py @@ -0,0 +1,74 @@ +import os, logging +import requests, time +import threading + +class FirstRequest(threading.Thread): + + def __init__(self): + threading.Thread.__init__(self) + + def run(self): + time.sleep(2) + r = requests.get('http://127.0.0.1:5000/') + +class Config: + + def __init__(self, app): + self.app = app + self.debug = True + + # Application settings + if 'APP_DOCKER' in os.environ: + self.NODE_RED_HOST = 'node-red' + self.NODE_RED_PORT = 9090 + self.DB_HOST = 'db' + self.DB_PORT = '3306' + self.DB_NAME = 'smart_remote' + self.DB_USER = 'root' + self.DB_PASS = 'root' + else: + self.NODE_RED_HOST = '192.168.100.111' + self.NODE_RED_PORT = 9090 + self.DB_HOST = '192.168.100.111' + self.DB_PORT = '3306' + self.DB_NAME = 'smart_remote' + self.DB_USER = 'root' + self.DB_PASS = 'root' + + # DiscoverService settings + self.BROADCAST_MASK = '255.255.255.255' + self.BROADCAST_PORT = 32000 + self.BROADCAST_INTERVAL = 5 + + # RpiNode settings + self.SOCKET_BIND_ADDRESS = '' + self.SOCKET_BIND_PORT = 32001 + self.SOCKET_CONNECTIONS = 5 + + # Flask settings + self.app.config['TRAP_HTTP_EXCEPTIONS'] = True + self.app.config['CSRF_ENABLED'] = True + self.app.config['SQLALCHEMY_POOL_RECYCLE'] = 280 + self.app.config['SQLALCHEMY_POOL_SIZE'] = 20 + self.app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True + self.app.config['SECRET_KEY'] = 'you-will-never-guess' + self.app.config['SQLALCHEMY_DATABASE_URI'] = self.createDbUri() + + if 'FLASK_ENV' in os.environ and os.environ['FLASK_ENV'] == 'development': + self.debug = True + self.app.config['SQLALCHEMY_ECHO'] = True + self.app.config['PRESERVE_CONTEXT_ON_EXCEPTION'] = True + self.app.config['SQLALCHEMY_RECORD_QUERIES'] = True + + # Logging settings + logging.basicConfig( + format='%(asctime)s - [%(levelname)s] %(message)s', + datefmt='%Y-%m-%d %H:%M:%S', + level= logging.DEBUG if self.debug else logging.ERROR, + handlers=[ + logging.FileHandler("app.log"), + logging.StreamHandler() + ]) + + def createDbUri(self): + return 'mysql+mysqlconnector://%s:%s@%s:%s/%s' % (self.DB_USER,self.DB_PASS,self.DB_HOST,self.DB_PORT,self.DB_NAME) diff --git a/app/config.py b/app/config.py deleted file mode 100755 index 58a461b..0000000 --- a/app/config.py +++ /dev/null @@ -1,9 +0,0 @@ -# List of responce codes -status_code = { - 'login_success' : 10, - 'alredy_logedin': 20, - 'login_faild' : 30, - 'logout' : 40, - 'failed' : 90, - 'success' : 100, -} diff --git a/app/drivers/arduino.py b/app/drivers/arduino.py deleted file mode 100755 index 893283d..0000000 --- a/app/drivers/arduino.py +++ /dev/null @@ -1,369 +0,0 @@ -from __future__ import print_function -import serial -import time, random, socket, json -import array -import os, sys -import threading, Queue -import requests -from app import so - -class Singleton: - """ - A non-thread-safe helper class to ease implementing singletons. - This should be used as a decorator -- not a metaclass -- to the - class that should be a singleton. - - The decorated class can define one `__init__` function that - takes only the `self` argument. Also, the decorated class cannot be - inherited from. Other than that, there are no restrictions that apply - to the decorated class. - - To get the singleton instance, use the `Instance` method. Trying - to use `__call__` will result in a `TypeError` being raised. - - """ - - def __init__(self, decorated): - self._decorated = decorated - - def Instance(self): - """ - Returns the singleton instance. Upon its first call, it creates a - new instance of the decorated class and calls its `__init__` method. - On all subsequent calls, the already created instance is returned. - - """ - try: - return self._instance - except AttributeError: - self._instance = self._decorated() - return self._instance - - def __call__(self): - raise TypeError('Singletons must be accessed through `Instance()`.') - - def __instancecheck__(self, inst): - return isinstance(inst, self._decorated) - -@Singleton -class Arduino(): - ser = None - queue = None - starter = None - - def startQueue(self): - self.queue = ArduinoQueue(self.ser) - self.queue.start() - - def activateQueueStarter(self): - self.starter = ArduinoQueueStarter() - self.starter.start() - - def send(self, btn, pipe, sid): - self.queue.putItem(ArduinoQueueItem(self.ser, btn, pipe, sid, 1)) - - def status(self, radio): - self.queue.putItem(ArduinoQueueRadio(self.ser, radio, 5)) - - def connect(self, env = ''): - if self.ser is None: - print('Connect to /dev/ttyUSB0', file=sys.stderr) - - if env == 'dev': - self.ser = SerialDev() - else: - self.ser = serial.Serial() - - self.ser.baudrate = 500000 - self.ser.port = '/dev/ttyUSB0' - self.ser.timeout = 10 - self.ser.open() - - # Only after write sketch into Arduino - time.sleep(2) - self.ser.flushInput() - self.ser.flushOutput() - # print(repr(self.ser.readline()), file=sys.stderr) - self.activateQueueStarter() - -class ArduinoQueue(threading.Thread): - - def __init__(self, ser): - threading.Thread.__init__(self) - self.ser = ser - self.workQueue = Queue.PriorityQueue() - - def run(self): - while True: - if not self.workQueue.empty(): - # print('get new item', file=sys.stderr) - queue_item = self.workQueue.get() - queue_item.run() - # print('run over', file=sys.stderr) - else: - time.sleep(0.05) - - def putItem(self, item): - self.workQueue.put(item) - -class ArduinoQueueStarter(threading.Thread): - - def __init__(self): - threading.Thread.__init__(self) - - def run(self): - time.sleep(2) - r = requests.get('http://127.0.0.1:5000/') - print('Send first request', file=sys.stderr) - -class ArduinoQueueItem(): - - def __init__(self, ser, btn, pipe, sid, priority): - self.signal = '' - self.buffer = 32 - self.ser = ser - self.btn = btn - self.sid = sid - self.priority = priority - self.pipe = pipe - - print("--------------", file=sys.stderr) - print(self.pipe, file=sys.stderr) - - def __cmp__(self, other): - return cmp(self.priority, other.priority) - - def encodeBits(self, data): - counter = 0 - zero = None - encode = '' - - for digit in data: - if digit == '0': - if zero == None: - zero = True - - if counter > 0 and zero == False: - encode += str(counter) + 'b' - counter = 1 - zero = True - else: - counter += 1 - - elif digit == '1': - if zero == None: - zero = False - - if counter > 0 and zero == True: - encode += str(counter) + 'a' - counter = 1 - zero = False - else: - counter += 1 - - if counter > 0: - if zero == True: - encode += str(counter) + 'a' - if zero == False: - encode += str(counter) + 'b' - - - return encode - - def prepareIrSignal(self): - pre_data = [] - data = [] - pre_data.append('%si' % self.pipe.replace('0x', '')) - - zero = [] - one = [] - compressed = '' - - for value in self.btn.signal.split(' '): - x = int(value) - if x > 65000: - data.append('65000') - if compressed != '': - data.append("[%s]" % self.encodeBits(compressed)) - compressed = '' - else: - if x < 1800: - code = '0' - if x < 1000: - zero.append(x) - elif 1000 <= x: - one.append(x) - code = '1' - compressed += code - else: - if compressed != '': - data.append("[%s]" % self.encodeBits(compressed)) - compressed = '' - data.append(value) - - if compressed != '': - data.append("[%s]" % self.encodeBits(compressed)) - - data.append('\n') - - pre_data.append(str(sum(zero)/len(zero))) - pre_data.append(str(sum(one)/len(one))) - - self.signal = ' '.join(pre_data + data) - print(self.signal, file=sys.stderr) - - def prepareCommand(self): - self.signal = '%sc%s\n' % (self.pipe.replace('0x', ''), self.btn.signal) - - def run(self): - self.ser.flushInput() - self.ser.flushOutput() - - if self.btn.type == 'ir': - self.prepareIrSignal() - elif self.btn.type == 'cmd': - self.prepareCommand() - - partial_signal = [self.signal[i:i+self.buffer] for i in range(0, len(self.signal), self.buffer)] - - response = "" - - for part in partial_signal: - b_arr = bytearray(part.encode()) - self.ser.write(b_arr) - self.ser.flush() - - response = self.ser.readline() - response = response.rstrip() - - if response != 'next': - break; - - response = "" - - if response == "": - response = ser.readline() - - data = response.split(':') - - if 1 < len(data): - if data[1] == 'FAIL': - so.emit('json', {'response': {'result': 'error', 'message': data[0]}}, namespace='/remotes', room=self.sid) - elif data[1] == 'OK': - so.emit('json', {'response': {'result': 'error', 'message': data[0]}}, namespace='/remotes', room=self.sid) - else: - so.emit('json', {'response': {'result': 'error', 'message': 'Unknown error'}}, namespace='/remotes', room=self.sid) - -class ArduinoQueueRadio(): - - def __init__(self, ser, radio, priority): - self.signal = '' - self.buffer = 32 - self.ser = ser - self.radio = radio - self.priority = priority - - if 'APP_DOCKER' in os.environ: - self.host = 'node-red' - else: - self.host = '192.168.100.10' - - def __cmp__(self, other): - return cmp(self.priority, other.priority) - - def run(self): - self.ser.flushInput() - self.ser.flushOutput() - - self.signal = '%sc%s\n' % (self.radio.pipe.replace('0x', ''), 'status') - - partial_signal = [self.signal[i:i+self.buffer] for i in range(0, len(self.signal), self.buffer)] - - response = "" - - for part in partial_signal: - b_arr = bytearray(part.encode()) - self.ser.write(b_arr) - self.ser.flush() - - response = self.ser.readline() - response = response.rstrip() - - if response != 'next': - break; - - response = "" - - if response == "": - response = ser.readline() - - data = response.split(':') - print(repr(response), file=sys.stderr) - - if 1 < len(data): - if data[1] == 'FAIL': - arduino_json = '{"type":"arduino", "id":"%s", "status":"offline", "msg":"%s"}' % (self.radio.pipe, data[0]) - s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) - s.connect((self.host, 9090)) - s.send(arduino_json) - s.close() - so.emit('json', {'response': {'result': 'error', 'message': data[0]}}, namespace='/radios') - elif data[1] == 'OK': - self.getStatus(data[0]) - else: - so.emit('json', {'response': {'result': 'error', 'message': 'Unknown error'}}, namespace='/radios') - - def getStatus(self, data): - sensors_data = dict(s.split(' ') for s in data.split(',')) - sensors = {} - - if self.radio.dht == 1: - if 'h' in sensors_data: - sensors['hum'] = sensors_data['h'] - - if 't' in sensors_data: - sensors['temp'] = sensors_data['t'] - - if self.radio.battery == 1: - if 'b' in sensors_data: - sensors['bat'] = sensors_data['b'] - - arduino_json = '[{"tempValue":%.2f,"humiValue":%.2f, "batValue":%.2f},{"type":"arduino", "id":"%s"}]' % (float(sensors_data['t']), float(sensors_data['h']), float(sensors_data['b']), self.radio.pipe) - s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) - s.connect((self.host, 9090)) - s.send(arduino_json) - s.close() - so.emit('json', {'response': {'result': 'success', 'callback': 'radio_sensor_refresh', 'id': self.radio.id, 'sensors': sensors}}, namespace='/radios') - -class SerialDev(): - transfer = False - - def open(self): - print('SERIAL DEV: Connect to /dev/ttyUSB0', file=sys.stderr) - - def flushInput(self): - print('SERIAL DEV: flushInput', file=sys.stderr) - - def flushOutput(self): - print('SERIAL DEV: flushOutput', file=sys.stderr) - - def flush(self): - print('SERIAL DEV: flush', file=sys.stderr) - - def write(self, data): - print('SERIAL DEV: Recieved bytearray', file=sys.stderr) - if data.endswith("\n"): - self.transfer = False - else: - self.transfer = True - print(data, file=sys.stderr) - - - def readline(self): - if self.transfer == False: - temp = random.uniform(18, 26) - hum = random.uniform(35, 65) - bat = random.uniform(0.1, 1) - return "t %.2f,h %.2f,b %.2f:OK\n" % (temp, hum, bat) - else: - return "next\n" diff --git a/app/drivers/driver.py b/app/drivers/driver.py deleted file mode 100755 index d67ddea..0000000 --- a/app/drivers/driver.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python -######################################################################## -# Filename : led.py -# Description : Turn On/Off a led -# auther : Sosetc -# modification: 2017/01/16 -######################################################################## -from devices.tv import Tv -from devices.led import Led -from devices.therm import Therm - -try: - import RPi.GPIO as GPIO - import Adafruit_DHT as DHT -except ImportError: - print("Module RPi.GPIO not found. Switch to development mode") - import GPIO_Dev as GPIO - import Adafruit_DHT_Dev as DHT - # print("Load Adafruit_DHT in development mode") - # import Adafruit_DHT_Dev as Adafruit_DHT - -drivers = {} - -def init(devices): - # global ledPin - global drivers - - print("DRIVER: Setup GPIO") - GPIO.setmode(GPIO.BOARD) # Numbers GPIOs by physical location - - for d in devices: - if d == 'led': - drivers['led'] = Led(GPIO) - continue - if d == 'tv': - drivers['tv'] = Tv(GPIO) - continue - if d == 'therm': - drivers['therm'] = Therm(DHT) - continue - - for k, v in drivers.items(): - v.status() - -def destroy(): - GPIO.cleanup() # Release resource - -def run(device, command): - global drivers - # global ledStatus - - if device in drivers: - print("DRIVER: device found") - return drivers[device].run(command) - else: - print("DRIVER: device [%s] not found" % device) - return False - diff --git a/app/drivers/ir_reader.py b/app/drivers/ir_reader.py deleted file mode 100755 index 668a4f5..0000000 --- a/app/drivers/ir_reader.py +++ /dev/null @@ -1,74 +0,0 @@ -from __future__ import print_function -import sys -import math -import os -from datetime import datetime -import time - -def read_signal(): - # This pin is also referred to as GPIO18 - INPUT_WIRE = 12 - - t_end = time.time() + 60 / 10 - - # Using for development - if 'APP_ENV' in os.environ and os.environ['APP_ENV'] == 'development': - print('%s - Waiting - %s' % (time.time(), t_end), file=sys.stderr) - return "500 1200 500 500 500 500" - else: - import RPi.GPIO as GPIO - - GPIO.setmode(GPIO.BOARD) - GPIO.setup(INPUT_WIRE, GPIO.IN) - - print('--- start ---', file=sys.stderr) - value = 1 - # Loop until we read a 0 - while value and time.time() < t_end: - value = GPIO.input(INPUT_WIRE) - if value: - return False - - print('--- signal ---', file=sys.stderr) - # Grab the start time of the command - startTime = datetime.now() - - # Used to buffer the command pulses - command = [] - - # The end of the "command" happens when we read more than - # a certain number of 1s (1 is off for my IR receiver) - numOnes = 0 - - # Used to keep track of transitions from 1 to 0 - previousVal = 0 - - while True: - if value != previousVal: - # The value has changed, so calculate the length of this run - now = datetime.now() - pulseLength = now - startTime - startTime = now - - command.append((previousVal, pulseLength.microseconds)) - - if value: - numOnes = numOnes + 1 - else: - numOnes = 0 - - # 10000 is arbitrary, adjust as necessary - if numOnes > 80000: - break - - previousVal = value - value = GPIO.input(INPUT_WIRE) - - result = [] - - for (val, pulse) in command: - result.append(str(pulse)) - - text = ' '.join(result) - - return text diff --git a/app/drivers/lirc.py b/app/drivers/lirc.py index 7bc642c..0fe6e82 100755 --- a/app/drivers/lirc.py +++ b/app/drivers/lirc.py @@ -1,7 +1,7 @@ from __future__ import print_function import time import os, sys -from ..models import Remote +from ..models import Rc class Common(): @@ -29,7 +29,7 @@ def addTestSignal(self, test_signal): def regenerateLircCommands(self): - ir_remotes = Remote.query.all() + ir_remotes = Rc.query.all() if ir_remotes is not None: print('---REGENERATE START---', file=sys.stderr) diff --git a/app/helpers.py b/app/helpers.py new file mode 100755 index 0000000..68caf61 --- /dev/null +++ b/app/helpers.py @@ -0,0 +1,601 @@ +import logging +from app.models import Rc, Button, Node, Arduino, Radio +from app import db +from datetime import datetime + +class RcHelper: + + def __init__(self, rc_id = None): + self.set(rc_id) + + def get(self): + return self.rc + + def set(self, rc_id): + self.rc = Rc.query.filter_by(id = rc_id).first() + + def getRcs(self): + rcs = [] + + for rc in Rc.query.order_by(Rc.order).all(): + r = { + 'id': rc.id, + 'name': rc.name, + 'icon': rc.icon, + 'order': rc.order, + 'public': rc.public + } + + rcs.append(r) + + return rcs + + def createRc(self, params): + rc = Rc(name = params['name'], + icon = params['icon'], + order = params['order'], + public = params['public'], + timestamp = datetime.utcnow()) + + db.session.add(rc) + db.session.commit() + + return {'id': rc.id, + 'name': rc.name, + 'icon': rc.icon, + 'order': rc.order, + 'public': rc.public} + + def getRc(self): + if self.rc is None: + return None + + return {'id': self.rc.id, + 'name': self.rc.name, + 'icon': self.rc.icon, + 'order': self.rc.order, + 'public': self.rc.public} + + def updateRc(self, params): + if self.rc is None: + return None + + self.rc.name = params['name'] + self.rc.icon = params['icon'] + self.rc.order = params['order'] + self.rc.public = params['public'] + self.rc.timestamp = datetime.utcnow() + + db.session.commit() + + return {'id': self.rc.id, + 'name': self.rc.name, + 'icon': self.rc.icon, + 'order': self.rc.order, + 'public': self.rc.public} + + def deleteRc(self): + if self.rc is None: + return None + + for btn in self.rc.buttons: + db.session.delete(btn) + + db.session.commit() + db.session.delete(self.rc) + db.session.commit() + self.rc = None + + return True + + def getButtons(self): + if self.rc is None: + return None + + buttons = [] + + for button in self.rc.buttons.order_by(Button.order_ver.asc(), Button.order_hor.asc()).all(): + b = {'id': button.id, + 'rc_id': button.rc_id, + 'radio_id': button.radio_id, + 'name': button.name, + 'color': button.color, + 'order_ver': button.order_ver, + 'order_hor': button.order_hor, + 'message': button.message, + 'type': button.type} + + buttons.append(b) + + return buttons +class BgroupHelper: + + def __init__(self, rc_id, group_id = None): + self.rc = Rc.query.filter_by(id = rc_id).first() + self.set(group_id) + + def get(self): + return self.rc + + def set(self, group_id): + if self.rc is None: + self.button = None + else: + self.button = self.rc.bgroups.filter((Bgroup.id == group_id)).first() + + # def getRcs(self): + # rcs = [] + + # for rc in Rc.query.order_by(Rc.order).all(): + # r = { + # 'id': rc.id, + # 'name': rc.name, + # 'icon': rc.icon, + # 'order': rc.order, + # 'public': rc.public + # } + + # rcs.append(r) + + # return rcs + + # def createRc(self, params): + # rc = Rc(name = params['name'], + # icon = params['icon'], + # order = params['order'], + # public = params['public'], + # timestamp = datetime.utcnow()) + + # db.session.add(rc) + # db.session.commit() + + # return {'id': rc.id, + # 'name': rc.name, + # 'icon': rc.icon, + # 'order': rc.order, + # 'public': rc.public} + + # def getRc(self): + # if self.rc is None: + # return None + + # return {'id': self.rc.id, + # 'name': self.rc.name, + # 'icon': self.rc.icon, + # 'order': self.rc.order, + # 'public': self.rc.public} + + # def updateRc(self, params): + # if self.rc is None: + # return None + + # self.rc.name = params['name'] + # self.rc.icon = params['icon'] + # self.rc.order = params['order'] + # self.rc.public = params['public'] + # self.rc.timestamp = datetime.utcnow() + + # db.session.commit() + + # return {'id': self.rc.id, + # 'name': self.rc.name, + # 'icon': self.rc.icon, + # 'order': self.rc.order, + # 'public': self.rc.public} + + # def deleteRc(self): + # if self.rc is None: + # return None + + # for btn in self.rc.buttons: + # db.session.delete(btn) + + # db.session.commit() + # db.session.delete(self.rc) + # db.session.commit() + # self.rc = None + + # return True + +class ButtonHelper: + + def __init__(self, btn_id = None): + self.set(btn_id) + + def get(self): + return self.button + + def set(self, btn_id): + self.button = Button.query.filter_by(id = btn_id).first() if btn_id else None + + def createButton(self, params): + btn = Button(rc_id = params['rc_id'], + radio_id = params['radio_id'], + name = params['name'], + order_hor = params['order_hor'], + order_ver = params['order_ver'], + color = params['color'], + type = params['type'], + message = params['message'], + timestamp = datetime.utcnow()) + + db.session.add(btn) + db.session.commit() + + self.button = btn + + return {'id': btn.id, + 'rc_id' : btn.rc_id, + 'radio_id': btn.radio_id, + 'name': btn.name, + 'order_hor': btn.order_hor, + 'order_ver': btn.order_ver, + 'color': btn.color, + 'type': btn.type, + 'message': btn.message} + + def getButton(self): + if self.button is None: + return None + + return {'id': self.button.id, + 'rc_id' : self.button.rc_id, + 'radio_id': self.button.radio_id, + 'name': self.button.name, + 'order_hor': self.button.order_hor, + 'order_ver': self.button.order_ver, + 'color': self.button.color, + 'message': self.button.message, + 'type': self.button.type} + + def updateButton(self, params): + if self.button is None: + return None + + self.button.radio_id = params['radio_id'] + self.button.name = params['name'] + self.button.order_hor = params['order_hor'] + self.button.order_ver = params['order_ver'] + self.button.color = params['color'] + self.button.type = params['type'] + self.button.message = params['message'] + self.button.timestamp = datetime.utcnow() + + db.session.commit() + + return {'id': self.button.id, + 'rc_id' : self.button.rc_id, + 'radio_id': self.button.radio_id, + 'name': self.button.name, + 'order_hor': self.button.order_hor, + 'order_ver': self.button.order_ver, + 'color': self.button.color, + 'type': self.button.type, + 'message': self.button.message} + + def deleteButton(self): + if self.button is None: + return None + + button = {'id': self.button.id, + 'rc_id' : self.button.rc_id, + 'radio_id': self.button.radio_id, + 'name': self.button.name, + 'order_hor': self.button.order_hor, + 'order_ver': self.button.order_ver, + 'color': self.button.color, + 'type': self.button.type, + 'message': self.button.message} + + db.session.delete(self.button) + db.session.commit() + self.button = None + return button + + # def getNode(self): + # if self.rc is None or self.button is None: + # return None + + # return Node.query.filter_by(id = self.button.node_id).first() + + def catchIrSignal(self, node_sevice, event): + node = Node.query.filter_by(id = self.button.node_id).first() + + event.host_name = node.host_name + event.button_id = self.button.id + + if node is not None and node_sevice.pushToNode(event): + return True + + return False + + def getHostName(self): + if self.button is None: + return None + + radio = Radio.query.filter_by(id = self.button.radio_id).first() + return radio.arduino.node.host_name + +class NodeHelper: + + def __init__(self, node_id = None): + self.set(node_id) + + def get(self): + return self.node + + def set(self, node_id): + self.node = Node.query.filter_by(id = node_id).first() if node_id else None + + def getNodes(self): + nodes = [] + + for node in Node.query.order_by(Node.order).all(): + n = {'id': node.id, + 'name': node.name, + 'host_name': node.host_name, + 'order': node.order} + + nodes.append(n) + + return nodes + + def createNode(self, params): + node = Node( + # name = params['name'], + host_name = params['host_name'], + order = params['order'], + timestamp = datetime.utcnow()) + + db.session.add(node) + db.session.commit() + + return {'id': node.id, + 'name': node.name, + 'host_name': node.host_name, + 'order': node.order} + + def getNode(self): + if self.node is None: + return None + + return {'id': self.node.id, + # 'name': self.node.name, + 'host_name': self.node.host_name, + 'order': self.node.order} + + def updateNode(self, params): + if self.node is None: + return None + + # self.node.name = params['name'] + self.node.host_name = params['host_name'] + self.node.order = params['order'] + self.node.timestamp = datetime.utcnow() + + db.session.commit() + + return {'id': self.node.id, + # 'name': self.node.name, + 'host_name': self.node.host_name, + 'order': self.node.order} + + def deleteNode(self): + if self.node is None: + return None + + for arduino in self.node.arduinos: + db.session.delete(arduino) + + db.session.commit() + db.session.delete(self.node) + db.session.commit() + self.node = None + return True + + def getNodeByName(self, host_name): + try: + node = Node.query.filter_by(host_name = host_name).first() + except Exception as e: + db.session.rollback() + logging.error('[helpers] db session error. rolled back') + logging.error(str(e)) + return False + + return node + +class ArduinoHelper: + + def __init__(self, arduino_id = None): + self.set(arduino_id) + + def get(self): + return self.arduino + + def getNode(self): + if self.arduino is None: + return None + + return self.arduino.node + + def set(self, arduino_id): + self.arduino = Arduino.query.filter_by(id = arduino_id).first() if arduino_id else None + + def getArduinos(self): + arduinos = [] + + for arduino in Arduino.query.order_by(Arduino.order).all(): + a = {'id': arduino.id, + 'node_id': arduino.node_id, + 'usb': arduino.usb, + 'name': arduino.name, + 'order': arduino.order} + + arduinos.append(a) + + return arduinos + + def createArduino(self, params): + arduino = Arduino(node_id = params['node_id'], + usb = params['usb'], + name = params['name'], + order = params['order'], + timestamp = datetime.utcnow()) + + db.session.add(arduino) + db.session.commit() + + self.arduino = arduino + + return {'id': arduino.id, + 'node_id': arduino.node_id, + 'usb': arduino.usb, + 'name': arduino.name, + 'order': arduino.order} + + def getArduino(self): + if self.arduino is None: + return None + + return {'id': self.arduino.id, + 'node_id': self.arduino.node_id, + 'usb': self.arduino.usb, + 'name': self.arduino.name, + 'order': self.arduino.order} + + def updateArduino(self, params): + if self.arduino is None: + return None + + self.arduino.usb = params['usb'] + self.arduino.name = params['name'] + self.arduino.order = params['order'] + self.arduino.timestamp = datetime.utcnow() + + db.session.commit() + + return {'id': self.arduino.id, + 'node_id': self.arduino.node_id, + 'usb': self.arduino.usb, + 'name': self.arduino.name, + 'order': self.arduino.order} + + def deleteArduino(self): + if self.arduino is None: + return None + + arduino = {'id': self.arduino.id, + 'node_id': self.arduino.node_id, + 'usb': self.arduino.usb, + 'name': self.arduino.name, + 'order': self.arduino.order} + + db.session.delete(self.arduino) + db.session.commit() + self.arduino = None + return arduino + +class RadioHelper: + + def __init__(self, radio_id = None): + self.set(radio_id) + + def get(self): + return self.radio + + def set(self, radio_id): + self.radio = Radio.query.filter_by(id = radio_id).first() + + def getRadios(self): + radios = [] + + for radio in Radio.query.order_by(Radio.order).all(): + r = {'id': radio.id, + 'arduino_id': radio.arduino_id, + 'type': radio.type, + 'pipe': radio.pipe, + 'name': radio.name, + 'on_request': radio.on_request, + 'expired_after': radio.expired_after, + 'enabled': radio.enabled, + 'order': radio.order} + + radios.append(r) + + return radios + + def createRadio(self, params): + radio = Radio(arduino_id = params['arduino_id'], + type = params['type'], + pipe = params['pipe'], + name = params['name'], + enabled = params['enabled'], + on_request = params['on_request'], + expired_after = params['expired_after'], + order = params['order'], + timestamp = datetime.utcnow()) + + db.session.add(radio) + db.session.commit() + + return {'id': radio.id, + 'arduino_id': radio.arduino_id, + 'type': radio.type, + 'pipe': radio.pipe, + 'name': radio.name, + 'on_request': radio.on_request, + 'expired_after': radio.expired_after, + 'enabled': radio.enabled, + 'order': radio.order} + + def getRadio(self): + if self.radio is None: + return None + + return {'id': self.radio.id, + 'arduino_id': self.radio.arduino_id, + 'type': self.radio.type, + 'pipe': self.radio.pipe, + 'name': self.radio.name, + 'on_request': self.radio.on_request, + 'expired_after': self.radio.expired_after, + 'enabled': self.radio.enabled, + 'order': self.radio.order} + + def updateRadio(self, params): + if self.radio is None: + return None + + self.radio.arduino_id = params['arduino_id'] + self.radio.type = params['type'] + self.radio.pipe = params['pipe'] + self.radio.name = params['name'] + self.radio.on_request = params['on_request'] + self.radio.expired_after = params['expired_after'] + self.radio.enabled = params['enabled'] + self.radio.order = params['order'] + self.radio.timestamp = datetime.utcnow() + + db.session.commit() + + return {'id': self.radio.id, + 'arduino_id': self.radio.arduino_id, + 'type': self.radio.type, + 'pipe': self.radio.pipe, + 'name': self.radio.name, + 'on_request': self.radio.on_request, + 'expired_after': self.radio.expired_after, + 'enabled': self.radio.enabled, + 'order': self.radio.order} + + def deleteRadio(self): + if self.radio is None: + return None + + db.session.delete(self.radio) + db.session.commit() + self.radio = None + return True + + def getByPipe(self, pipe): + return Radio.query.filter_by(pipe = pipe).first() \ No newline at end of file diff --git a/app/models.py b/app/models.py index 4f7f252..ba40ab2 100755 --- a/app/models.py +++ b/app/models.py @@ -5,10 +5,10 @@ class User(db.Model): id = db.Column(db.Integer, primary_key = True) - username = db.Column(db.String(120), unique = True) + username = db.Column(db.String(50), unique = True) password = db.Column(db.String(255)) role = db.Column(db.SmallInteger, default = ROLE_ADMIN) - email = db.Column(db.String(120), unique = True) + email = db.Column(db.String(190), unique = True) @property def is_authenticated(self): @@ -31,45 +31,82 @@ def get_id(self): def __repr__(self): return '' % (self.username) -class Remote(db.Model): +class Rc(db.Model): id = db.Column(db.Integer, primary_key = True) - identificator = db.Column(db.String(200)) - name = db.Column(db.String(200)) + name = db.Column(db.String(50)) public = db.Column(db.Boolean) order = db.Column(db.Integer) icon = db.Column(db.String(200)) timestamp = db.Column(db.DateTime) - buttons = db.relationship('Button', backref = 'remote', lazy = 'dynamic') + buttons = db.relationship('Button', backref = 'rc', lazy = 'dynamic') + # bgroups = db.relationship('Bgroup', backref = 'rc', lazy = 'dynamic') def __repr__(self): - return '' % (self.identificator) + return '' % (self.id) + +# class Bgroup(db.Model): +# id = db.Column(db.Integer, primary_key = True) +# rc_id = db.Column(db.Integer, db.ForeignKey('rc.id')) +# name = db.Column(db.String(200)) +# order = db.Column(db.Integer) +# timestamp = db.Column(db.DateTime) +# buttons = db.relationship('Button', backref = 'bgroup', lazy = 'dynamic') + +# def __repr__(self): +# return '' % (self.id) class Button(db.Model): id = db.Column(db.Integer, primary_key = True) - identificator = db.Column(db.String(200)) - name = db.Column(db.String(200)) + rc_id = db.Column(db.Integer, db.ForeignKey('rc.id')) + radio_id = db.Column(db.Integer) + name = db.Column(db.String(50)) order_hor = db.Column(db.Integer) order_ver = db.Column(db.Integer) color = db.Column(db.String(10)) - timestamp = db.Column(db.DateTime) - signal = db.Column(db.Text) - remote_id = db.Column(db.Integer, db.ForeignKey('remote.id')) - radio_id = db.Column(db.Integer, db.ForeignKey('radio.id')) + # radio / mqtt type = db.Column(db.String(20)) + message = db.Column(db.Text) + timestamp = db.Column(db.DateTime) def __repr__(self): - return '