Skip to content

Commit

Permalink
app improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
cekk committed Jan 6, 2020
1 parent ef7d0f9 commit daaae92
Show file tree
Hide file tree
Showing 13 changed files with 111 additions and 120 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ buildout.cfg
eggs/
parts/
var/
develop-eggs/
src/
.mr.developer.cfg
5 changes: 1 addition & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ buildout:

.PHONY: dev
dev:
pipenv run python app.py

dev-ssl:
pipenv run python app.py --cert /Users/cekk/letsencrypt/config/live/cekk.changeip.co/fullchain.pem --key /Users/cekk/letsencrypt/config/live/cekk.changeip.co/privkey.pem
FLASK_APP=dev.py bin/flask run -p 8000

prod:
bin/gunicorn -c gunicorn_config.py wsgi:app
Expand Down
132 changes: 29 additions & 103 deletions app.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,35 @@
# -*- coding: utf-8 -*-
from flask import Blueprint
from flask import Flask
from flask import Flask
from flask import current_app
from flask import render_template
from flask import request
from flask import send_from_directory
from flask_mqtt import Mqtt
from flask_restful import Resource, Api, reqparse, abort
from flask_socketio import SocketIO
from flask_restful import abort
from flask_restful import Api
from flask_restful import reqparse
from flask_restful import Resource
from functools import wraps
from routes.api import api_bp
from routes.frontend import frontend
from utils import createSocketMessage
from utils import mqtt
from utils import socketio

from flask_jwt_extended import (
JWTManager,
jwt_required,
create_access_token,
get_jwt_identity,
)
from functools import wraps

import logging
import os
import re

app = Flask(__name__, instance_relative_config=True, static_folder="static/build")
app.config.from_pyfile("config.py")

api_bp = Blueprint("api", __name__)
api = Api(api_bp)
app.register_blueprint(api_bp)

socketio = SocketIO(app)
mqtt = Mqtt(app)
jwt = JWTManager(app)

parser = reqparse.RequestParser()
parser.add_argument("command", type=str, help="")


@mqtt.on_connect()
def handle_connect(client, userdata, flags, rc):
# mqtt.subscribe("shellies/command")
for blind in app.config.get("BLINDS", []):
mqtt.subscribe("shellies/{id}/online".format(id=blind.get("id", "")))
mqtt.subscribe("shellies/{id}/roller/0/pos".format(id=blind.get("id", "")))
mqtt.subscribe("shellies/{id}/roller/0".format(id=blind.get("id", "")))


@mqtt.on_message()
def handle_mqtt_message(client, userdata, message):
if message.payload == "announce":
Expand All @@ -60,84 +46,24 @@ def connect_handler():
mqtt.publish("shellies/command", "announce")


# @app.route("/", defaults={"path": ""})
# @app.route("/<path:path>")
# def serve(path):
# if path != "" and os.path.exists(app.static_folder + "/" + path):
# return send_from_directory(app.static_folder, path)
# else:
# app.logger.info("AAAA")
# app.logger.debug("BBB")
# app.logger.warning("CCC")
# app.logger.error("DDD")
# return send_from_directory(app.static_folder, "index.html")


class ProtectedResource(Resource):
method_decorators = [jwt_required]
def create_app(debug=False, local=False):
"""Create an application."""
app = Flask(__name__, instance_relative_config=True, static_folder="static/build")
app.config.from_pyfile("config.py")
api = Api(api_bp)
app.register_blueprint(api_bp)

def check_skill_id(*args, **kwargs):
if not app.config["SKILL_ID"]:
abort(401)
skill_id = get_jwt_identity()
if skill_id != app.config["SKILL_ID"]:
abort(401)
if local:
app.config["LOCAL"] = True
app.register_blueprint(frontend)

jwt = JWTManager(app)
socketio.init_app(app)
mqtt.init_app(app)

class Blinds(ProtectedResource):
def get(self):
self.check_skill_id()
return app.config.get("BLINDS", [])


class Announce(Resource):
def get(self):
# force all shellies to announce their status
mqtt.publish("shellies/command", "announce")
return "", 204


class Update(Resource):
def get(self):
# force all shellies to announce their status
mqtt.publish("shellies/command", "update")
return "", 204


class Action(ProtectedResource):
def publish(self, id, action):
mqtt.publish("shellies/{id}/roller/0/command".format(id=id), action)

def get(self, id, action):
self.check_skill_id()
if action not in ["close", "open", "stop"]:
return (
{"message": 'Valid actions are "close", "open", "stop" or "rc"'},
400,
)
if id == "all":
for blind in app.config.get("BLINDS", []):
self.publish(id=blind.get("id", ""), action=action)
else:
self.publish(id=id, action=action)
return "", 204


# class Command(Resource):
# def post(self):
# args = parser.parse_args()
# mqtt.publish("shellies/shellyswitch25-68E5F8/roller/0/command", "close")
# return {todo_id: todos[todo_id]}


api.add_resource(Blinds, "/blinds")
api.add_resource(Announce, "/announce")
api.add_resource(Update, "/update")
# api.add_resource(Command, "/command")
api.add_resource(Action, "/roller/<string:id>/<string:action>")
for blind in app.config.get("BLINDS", []):
mqtt.subscribe("shellies/{id}/online".format(id=blind.get("id", "")))
mqtt.subscribe("shellies/{id}/roller/0/pos".format(id=blind.get("id", "")))
mqtt.subscribe("shellies/{id}/roller/0".format(id=blind.get("id", "")))

if __name__ == "__main__":
gunicorn_logger = logging.getLogger("gunicorn.error")
app.logger.handlers = gunicorn_logger.handlers
app.logger.setLevel(gunicorn_logger.level)
socketio.run(app, debug=True, host="0.0.0.0", port=4000)
return app
4 changes: 4 additions & 0 deletions dev.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from app import create_app
from app import socketio

app = create_app(debug=True, local=True)
5 changes: 3 additions & 2 deletions flask.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ parts =
recipe = zc.recipe.egg
eggs =
flask
flask_jwt_extended
flask-mqtt
flask-restful
gunicorn
flask-socketio
gevent
flask_jwt_extended
gunicorn
requests
1 change: 1 addition & 0 deletions static/.nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
stable
3 changes: 2 additions & 1 deletion static/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"@emotion/styled": "^10.0.23",
"axios": "^0.19.0",
"emotion-theming": "^10.0.19",
"lodash.isequal": "^4.5.0",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-icons": "^3.8.0",
Expand Down Expand Up @@ -35,5 +36,5 @@
"last 1 safari version"
]
},
"proxy": "http://localhost:5000"
"proxy": "http://localhost:8000"
}
6 changes: 6 additions & 0 deletions static/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Heading, Box } from "@chakra-ui/core";
import BlindsList from "./BlindsList";
import io from "socket.io-client";
import axios from "axios";
import isEqual from "lodash.isequal";

class App extends Component {
constructor() {
Expand All @@ -18,6 +19,11 @@ class App extends Component {

updateBlindInfos = data => {
const { blinds } = this.state;
const blind = blinds.filter(device => device.id === data.id)[0];
if (isEqual(blind, { ...blind, ...data })) {
// no changes
return;
}
const updatedValues = blinds.map(blind => {
if (blind.id !== data.id) {
return blind;
Expand Down
41 changes: 38 additions & 3 deletions static/src/BlindItem.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,48 @@
import React from "react";
import { Box, Heading, Text, Badge, IconButton } from "@chakra-ui/core";
import {
Box,
Heading,
Text,
Badge,
IconButton,
Slider,
SliderTrack,
SliderFilledTrack,
SliderThumb
} from "@chakra-ui/core";

import {
FaPauseCircle,
FaArrowAltCircleUp,
FaArrowAltCircleDown
} from "react-icons/fa";
import axios from "axios";

function PositionSelector({ position, id }) {
const doAction = value => {
axios.get(`/roller/${id}/position/${value}`);
};

if (!position) {
return "";
}
return (
<Slider defaultValue={position} onChange={doAction}>
<SliderTrack bg="red.100" />
<SliderFilledTrack bg="tomato" />
<SliderThumb size={6}>
<Box color="tomato" as={FaArrowAltCircleDown} />
</SliderThumb>
</Slider>
);
}

function BlindItem(data) {
const { name, id, online = false, action = "stop" } = data;
const { name, id, online = false, action = "stop", position } = data;

const doAction = ({ id, action }) => {
axios.get(`/roller/${id}/${action}`);
};

return (
<Box height="200px" bg="gray.200" textAlign="center">
<Heading>{name}</Heading>
Expand All @@ -28,6 +57,12 @@ function BlindItem(data) {
Current action: {action}
</Text>
</Box>
<Box>
<Text fontSize="sm" fontWeight="bold">
Current position: {position ? `${position}%` : "..."}
</Text>
<PositionSelector id={id} position={position} />
</Box>
<IconButton
variant="outline"
variantColor="teal"
Expand Down
4 changes: 4 additions & 0 deletions static/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5619,6 +5619,10 @@ lodash._reinterpolate@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"

lodash.isequal@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"

lodash.memoize@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
Expand Down
9 changes: 9 additions & 0 deletions utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
# -*- coding: utf-8 -*-
from flask_mqtt import Mqtt
from flask_socketio import SocketIO
from flask import current_app as app

import re


socketio = SocketIO()
mqtt = Mqtt()


def createSocketMessage(message):
blindId = ""
data = dict(topic=message.topic, payload=message.payload.decode(), id=blindId)
Expand Down
11 changes: 6 additions & 5 deletions versions.cfg
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
[versions]
aniso8601 = 8.0.0
Click = 7.0
collective.recipe.supervisor = 1.0.0
Flask = 1.1.1
Flask-MQTT = 1.0.5
Flask-RESTful = 0.3.7
Flask-SocketIO = 4.2.1
Jinja2 = 2.10.1
MarkupSafe = 1.1.1
Werkzeug = 0.16.0
aniso8601 = 8.0.0
collective.recipe.supervisor = 1.0.0
gevent = 1.4.0
greenlet = 0.4.15
gunicorn = 20.0.4
itsdangerous = 1.1.0
Jinja2 = 2.10.1
MarkupSafe = 1.1.1
meld3 = 1.0.2
python-engineio = 3.11.1
python-socketio = 4.4.0
requests = 2.22.0
six = 1.12.0
supervisor = 4.0.3
Werkzeug = 0.16.0
zc.recipe.egg = 2.0.7

# Required by:
Expand Down
7 changes: 5 additions & 2 deletions wsgi.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from app import app
from app import create_app
from app import socketio

app = create_app(debug=False)

if __name__ == "__main__":
app.run(debug=False)
socketio.run(app)

0 comments on commit daaae92

Please sign in to comment.