Add basic device endpoints
parent
004bdbd0f3
commit
b85e90cfb4
|
@ -15,9 +15,9 @@ def create_account(username, email, password):
|
||||||
:type username: string
|
:type username: string
|
||||||
:type email: string
|
:type email: string
|
||||||
:type password: string
|
:type password: string
|
||||||
:returns True if account is successfully created
|
:returns: True if account is successfully created
|
||||||
:rtype Boolean
|
:rtype: Boolean
|
||||||
:raises ValueError if account already exists
|
:raises: ValueError if account already exists
|
||||||
"""
|
"""
|
||||||
if not Account.exists_with_any_of(username=username, email=email):
|
if not Account.exists_with_any_of(username=username, email=email):
|
||||||
pw_hash = bcrypt.generate_password_hash(password).decode('utf-8')
|
pw_hash = bcrypt.generate_password_hash(password).decode('utf-8')
|
||||||
|
@ -37,9 +37,9 @@ def create_token(username, password):
|
||||||
:param password: password of Account
|
:param password: password of Account
|
||||||
:type username: string
|
:type username: string
|
||||||
:type password: string
|
:type password: string
|
||||||
:returns created token
|
:returns: created token
|
||||||
:rtype string
|
:rtype: string
|
||||||
:raises ValueError if credentials are invalid or account does not exist
|
:raises: ValueError if credentials are invalid or account does not exist
|
||||||
"""
|
"""
|
||||||
if not Account.exists(username=username):
|
if not Account.exists(username=username):
|
||||||
raise ValueError("Invalid credentials")
|
raise ValueError("Invalid credentials")
|
||||||
|
@ -57,7 +57,7 @@ def validate_token(token):
|
||||||
|
|
||||||
:param token: auth token to validate
|
:param token: auth token to validate
|
||||||
:type token: string
|
:type token: string
|
||||||
:returns created token
|
:returns: created token
|
||||||
:rtype Account
|
:rtype: Account
|
||||||
"""
|
"""
|
||||||
return Account.validate_token(token)
|
return Account.validate_token(token)
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
from app import bcrypt, status
|
|
||||||
from flask import request
|
|
||||||
from .models import Account
|
|
||||||
|
|
||||||
|
|
||||||
def initialize_routes(accounts):
|
|
||||||
@accounts.route("", methods=['POST'])
|
|
||||||
def create_account():
|
|
||||||
print(request.data)
|
|
||||||
user = request.data.get('user')
|
|
||||||
if not Account.exists_with_any_of(
|
|
||||||
username=user.get('username'), email=user.get('email')):
|
|
||||||
password_hash = bcrypt.generate_password_hash(
|
|
||||||
user.get('password')
|
|
||||||
).decode('utf-8')
|
|
||||||
acct = Account(user.get('username'),
|
|
||||||
password_hash,
|
|
||||||
user.get('email'))
|
|
||||||
acct.save()
|
|
||||||
response = {
|
|
||||||
'status': 'success',
|
|
||||||
'message': 'Success!'
|
|
||||||
}
|
|
||||||
return response, status.HTTP_200_OK
|
|
||||||
else:
|
|
||||||
response = {
|
|
||||||
'status': 'error',
|
|
||||||
'message': 'User already exists!'
|
|
||||||
}
|
|
||||||
return response, status.HTTP_422_UNPROCESSABLE_ENTITY
|
|
||||||
|
|
||||||
@accounts.route("/token", methods=['POST'])
|
|
||||||
def create_token():
|
|
||||||
print(request.data)
|
|
||||||
user = request.data.get('user')
|
|
||||||
if not user:
|
|
||||||
response = {
|
|
||||||
'status': 'error',
|
|
||||||
'message': 'Invalid request'
|
|
||||||
}
|
|
||||||
return response, status.HTTP_400_BAD_REQUEST
|
|
||||||
|
|
||||||
if not Account.exists(username=user.get('username')):
|
|
||||||
response = {
|
|
||||||
'status': 'error',
|
|
||||||
'message': 'Invalid credentials'
|
|
||||||
}
|
|
||||||
return response, status.HTTP_401_UNAUTHORIZED
|
|
||||||
|
|
||||||
account = Account.get(username=user.get('username'))
|
|
||||||
if not bcrypt.check_password_hash(
|
|
||||||
account.password, user.get('password')):
|
|
||||||
response = {
|
|
||||||
'status': 'error',
|
|
||||||
'message': 'Invalid credentials'
|
|
||||||
}
|
|
||||||
return response, status.HTTP_401_UNAUTHORIZED
|
|
||||||
|
|
||||||
response = {
|
|
||||||
'status': 'success',
|
|
||||||
'message': 'Successfully logged in',
|
|
||||||
'token': account.create_auth_token()
|
|
||||||
}
|
|
||||||
return response, status.HTTP_200_OK
|
|
|
@ -1,6 +1,7 @@
|
||||||
import jwt
|
import jwt
|
||||||
import datetime
|
import datetime
|
||||||
from app import db, app
|
from app import db, app
|
||||||
|
from calendar import timegm
|
||||||
|
|
||||||
|
|
||||||
class Account(db.Model):
|
class Account(db.Model):
|
||||||
|
@ -108,10 +109,13 @@ class Account(db.Model):
|
||||||
app.config.get('SECRET_KEY'),
|
app.config.get('SECRET_KEY'),
|
||||||
algorithms=['HS256']
|
algorithms=['HS256']
|
||||||
)
|
)
|
||||||
|
current_time = timegm(datetime.datetime.utcnow().utctimetuple())
|
||||||
|
if current_time > payload['exp']:
|
||||||
|
raise ValueError("Expired token")
|
||||||
return Account.get(id=payload['sub'])
|
return Account.get(id=payload['sub'])
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<Account (name=%s, role=%s)>' % self.username, self.role
|
return '<Account (name=%s, role=%s)>' % (self.username, self.role)
|
||||||
|
|
||||||
|
|
||||||
class Role(db.Model):
|
class Role(db.Model):
|
||||||
|
@ -146,4 +150,4 @@ class Role(db.Model):
|
||||||
return Role.query.filter_by(id=roleId)
|
return Role.query.filter_by(id=roleId)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<Role %s>' % self.name
|
return '<Role %s>' % self.display_name
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import sys
|
||||||
from flask import Blueprint, request, g
|
from flask import Blueprint, request, g
|
||||||
from flask_restful import Api, Resource, abort
|
from flask_restful import Api, Resource, abort
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
@ -22,8 +23,13 @@ def protected(func):
|
||||||
if not g.current_account:
|
if not g.current_account:
|
||||||
abort(401, message='Unauthorized', status='error')
|
abort(401, message='Unauthorized', status='error')
|
||||||
except Exception:
|
except Exception:
|
||||||
|
error_type, error_instance, traceback = sys.exc_info()
|
||||||
|
print(str(error_type))
|
||||||
|
print(str(error_instance))
|
||||||
abort(401, message='Unauthorized', status='error')
|
abort(401, message='Unauthorized', status='error')
|
||||||
|
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
return protected_function
|
return protected_function
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,10 +40,17 @@ class ProtectedResource(Resource):
|
||||||
def add_resources():
|
def add_resources():
|
||||||
from .resources.account import AccountResource, AccountListResource
|
from .resources.account import AccountResource, AccountListResource
|
||||||
from .resources.token import TokenResource
|
from .resources.token import TokenResource
|
||||||
|
from .resources.device import (DeviceResource,
|
||||||
|
DeviceRecordingResource,
|
||||||
|
DeviceListResource)
|
||||||
|
|
||||||
api.add_resource(AccountResource, '/v1/accounts/<int:account_id>')
|
api.add_resource(AccountResource, '/v1/accounts/<int:account_id>')
|
||||||
api.add_resource(AccountListResource, '/v1/accounts')
|
api.add_resource(AccountListResource, '/v1/accounts')
|
||||||
api.add_resource(TokenResource, '/v1/token')
|
api.add_resource(TokenResource, '/v1/token')
|
||||||
|
api.add_resource(DeviceResource, '/v1/devices/<int:device_id>')
|
||||||
|
api.add_resource(DeviceRecordingResource,
|
||||||
|
'/v1/devices/<int:device_id>/recordings')
|
||||||
|
api.add_resource(DeviceListResource, '/v1/devices')
|
||||||
|
|
||||||
|
|
||||||
add_resources()
|
add_resources()
|
||||||
|
|
|
@ -1,31 +1,32 @@
|
||||||
from flask_restful import Resource, abort
|
from flask_restful import Resource, abort
|
||||||
from flask import g
|
from flask import g
|
||||||
from webargs import fields
|
from marshmallow import Schema, fields
|
||||||
from webargs.flaskparser import use_args
|
from webargs.flaskparser import use_args
|
||||||
from flasgger import swag_from
|
from flasgger import swag_from
|
||||||
import app.accounts as accounts
|
import app.accounts as accounts
|
||||||
from app.api import ProtectedResource, protected
|
from app.api import ProtectedResource
|
||||||
|
|
||||||
user_args = {
|
|
||||||
'user': fields.Nested({
|
class UserSchema(Schema):
|
||||||
'username': fields.Str(required=True),
|
username = fields.Str(required=True)
|
||||||
'email': fields.Email(required=True),
|
email = fields.Email(required=True)
|
||||||
'password': fields.Str(required=True)
|
password = fields.Str(required=True, load_only=True)
|
||||||
}, required=True, location='json')
|
|
||||||
}
|
|
||||||
|
class UserWrapperSchema(Schema):
|
||||||
|
user = fields.Nested(UserSchema, required=True, location='json')
|
||||||
|
|
||||||
|
|
||||||
class AccountResource(ProtectedResource):
|
class AccountResource(ProtectedResource):
|
||||||
|
|
||||||
@swag_from('swagger/get_account_spec.yaml')
|
@swag_from('swagger/get_account_spec.yaml')
|
||||||
def get(self, account_id):
|
def get(self, account_id):
|
||||||
if g.current_account.id == account_id:
|
if g.current_account.id == account_id:
|
||||||
return g.current_account, 200
|
return UserWrapperSchema().dump({'user': g.current_account}), 200
|
||||||
abort(403, message='You can only get your own account', status='error')
|
abort(403, message='You can only get your own account', status='error')
|
||||||
|
|
||||||
|
|
||||||
class AccountListResource(Resource):
|
class AccountListResource(Resource):
|
||||||
@use_args(user_args)
|
@use_args(UserWrapperSchema())
|
||||||
@swag_from('swagger/create_account_spec.yaml')
|
@swag_from('swagger/create_account_spec.yaml')
|
||||||
def post(self, args):
|
def post(self, args):
|
||||||
try:
|
try:
|
||||||
|
@ -38,7 +39,3 @@ class AccountListResource(Resource):
|
||||||
return '', 201
|
return '', 201
|
||||||
except ValueError:
|
except ValueError:
|
||||||
abort(422, message='Account already exists', status='error')
|
abort(422, message='Account already exists', status='error')
|
||||||
|
|
||||||
@protected
|
|
||||||
def get(self):
|
|
||||||
return '', 200
|
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
from marshmallow import Schema, fields
|
||||||
|
from webargs.flaskparser import use_args
|
||||||
|
from flasgger import swag_from
|
||||||
|
import app.devices as devices
|
||||||
|
from app.api import ProtectedResource
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceSchema(Schema):
|
||||||
|
id = fields.Integer(dump_only=True)
|
||||||
|
name = fields.Str(required=True)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceWrapperSchema(Schema):
|
||||||
|
device = fields.Nested(DeviceSchema, required=True, location='json')
|
||||||
|
|
||||||
|
|
||||||
|
class RecordingsSchema(Schema):
|
||||||
|
recorded_at = fields.DateTime()
|
||||||
|
record_type = fields.Integer()
|
||||||
|
record_value = fields.String()
|
||||||
|
|
||||||
|
|
||||||
|
class RecordingsWrapperSchema(Schema):
|
||||||
|
recordings = fields.Nested(RecordingsSchema, required=True,
|
||||||
|
location='json', many=True)
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceResource(ProtectedResource):
|
||||||
|
@swag_from('swagger/get_device_spec.yaml')
|
||||||
|
def get(self, device_id):
|
||||||
|
return DeviceWrapperSchema().dump(
|
||||||
|
{'device': devices.get_device(device_id)}), 200
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceRecordingResource(ProtectedResource):
|
||||||
|
@swag_from('swagger/get_device_recordings_spec.yaml')
|
||||||
|
def get(self, device_id):
|
||||||
|
return RecordingsWrapperSchema().dump(
|
||||||
|
{'recordings': devices.get_device_recordings(device_id)}), 200
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceListResource(ProtectedResource):
|
||||||
|
@use_args(DeviceWrapperSchema())
|
||||||
|
@swag_from('swagger/create_device_spec.yaml')
|
||||||
|
def post(self, args):
|
||||||
|
args = args['device']
|
||||||
|
success = devices.create_device(
|
||||||
|
args['name'])
|
||||||
|
if success:
|
||||||
|
return '', 201
|
|
@ -14,10 +14,6 @@ parameters:
|
||||||
- user
|
- user
|
||||||
properties:
|
properties:
|
||||||
user:
|
user:
|
||||||
required:
|
|
||||||
- username
|
|
||||||
- password
|
|
||||||
- email
|
|
||||||
$ref: '#/definitions/User'
|
$ref: '#/definitions/User'
|
||||||
security: []
|
security: []
|
||||||
responses:
|
responses:
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
Creates new device
|
||||||
|
Requires Device object and creates device
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Device
|
||||||
|
parameters:
|
||||||
|
- in: body
|
||||||
|
name: body
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- device
|
||||||
|
properties:
|
||||||
|
device:
|
||||||
|
$ref: '#/definitions/Device'
|
||||||
|
responses:
|
||||||
|
201:
|
||||||
|
description: Successful creation
|
|
@ -1,11 +1,17 @@
|
||||||
Gets a user account
|
Gets a user account
|
||||||
|
User may only get own account. Accessing other accounts will return 403.
|
||||||
---
|
---
|
||||||
tags:
|
tags:
|
||||||
- Account
|
- Account
|
||||||
parameters:
|
parameters:
|
||||||
- in: body
|
- in: path
|
||||||
name: body
|
name: user_id
|
||||||
required: true
|
required: true
|
||||||
|
type: integer
|
||||||
|
description: Id of the user
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Success
|
||||||
schema:
|
schema:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
|
@ -13,10 +19,7 @@ parameters:
|
||||||
properties:
|
properties:
|
||||||
user:
|
user:
|
||||||
$ref: '#/definitions/User'
|
$ref: '#/definitions/User'
|
||||||
responses:
|
403:
|
||||||
201:
|
description: Accessed a different account
|
||||||
description: Successful creation
|
|
||||||
422:
|
|
||||||
description: Account already exists
|
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Error'
|
$ref: '#/definitions/Error'
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
Gets all recordings for given device
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Device
|
||||||
|
- Recording
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: device_id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
description: Id of the device
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Success
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- recordings
|
||||||
|
properties:
|
||||||
|
recordings:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/Recording'
|
|
@ -0,0 +1,21 @@
|
||||||
|
Gets a device
|
||||||
|
---
|
||||||
|
tags:
|
||||||
|
- Device
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: device_id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
description: Id of the device
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Success
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
-device
|
||||||
|
properties:
|
||||||
|
device:
|
||||||
|
$ref: '#/definitions/Device'
|
||||||
|
|
|
@ -1,30 +1,71 @@
|
||||||
import atexit
|
import atexit
|
||||||
from flask import Blueprint
|
from flask import Blueprint
|
||||||
from .mqtt_client import MqttClient
|
from .mqtt_client import MqttClient
|
||||||
|
from .models import Device, Recording
|
||||||
|
|
||||||
devices_bp = Blueprint('devices', __name__)
|
devices_bp = Blueprint('devices', __name__)
|
||||||
mqtt_client = None
|
|
||||||
|
|
||||||
|
|
||||||
# When app dies, stop mqtt connection
|
# When app dies, stop mqtt connection
|
||||||
def on_stop():
|
def on_stop():
|
||||||
if mqtt_client:
|
MqttClient.tear_down()
|
||||||
mqtt_client.tear_down()
|
|
||||||
|
|
||||||
|
|
||||||
atexit.register(on_stop)
|
atexit.register(on_stop)
|
||||||
|
|
||||||
|
|
||||||
# Routes
|
# Setup
|
||||||
@devices_bp.route("/")
|
|
||||||
def hello():
|
|
||||||
return "Hello from devices!"
|
|
||||||
|
|
||||||
|
|
||||||
@devices_bp.record
|
@devices_bp.record
|
||||||
def on_blueprint_setup(setup_state):
|
def __on_blueprint_setup(setup_state):
|
||||||
print('Blueprint setup')
|
print('Blueprint setup')
|
||||||
mqtt_client = MqttClient()
|
MqttClient.setup(setup_state.app)
|
||||||
|
|
||||||
if mqtt_client:
|
|
||||||
mqtt_client.setup(setup_state.app)
|
# Public interface
|
||||||
|
def create_device(name, device_type=1):
|
||||||
|
"""
|
||||||
|
Tries to create device with given parameters
|
||||||
|
|
||||||
|
:param name: Desired device name
|
||||||
|
:param device_type: Id of desired device type.
|
||||||
|
By default it is 1 (STANDARD)
|
||||||
|
:type name: string
|
||||||
|
:type device_type: int
|
||||||
|
:returns: True if device is successfully created
|
||||||
|
:rtype: Boolean
|
||||||
|
"""
|
||||||
|
device = Device(name, None, device_type)
|
||||||
|
device.save()
|
||||||
|
|
||||||
|
|
||||||
|
def get_device_recordings(device_id):
|
||||||
|
"""
|
||||||
|
Tries to get device recording for device with given parameters. Raises
|
||||||
|
error on failure
|
||||||
|
|
||||||
|
:param device_id: Id of device
|
||||||
|
:type device_id: int
|
||||||
|
:returns: List of Recordings for given device
|
||||||
|
:rtype: List of Recording
|
||||||
|
:raises: ValueError if device does not exist
|
||||||
|
"""
|
||||||
|
if not Device.exists(id=device_id):
|
||||||
|
raise ValueError("Device with id %s does not exist" % device_id)
|
||||||
|
|
||||||
|
return Recording.get_many(device_id=device_id)
|
||||||
|
|
||||||
|
|
||||||
|
def get_device(device_id):
|
||||||
|
"""
|
||||||
|
Tries to get device with given parameters. Raises error on failure
|
||||||
|
|
||||||
|
:param device_id: Id of device
|
||||||
|
:type device_id: int
|
||||||
|
:returns: Requested device
|
||||||
|
:rtype: Device
|
||||||
|
:raises: ValueError if device does not exist
|
||||||
|
"""
|
||||||
|
if not Device.exists(id=device_id):
|
||||||
|
raise ValueError("Device with id %s does not exist" % device_id)
|
||||||
|
|
||||||
|
return Device.get(id=device_id)
|
||||||
|
|
|
@ -137,6 +137,15 @@ class Device(db.Model):
|
||||||
"""
|
"""
|
||||||
return Device.query.filter_by(**kwargs).first()
|
return Device.query.filter_by(**kwargs).first()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def exists(**kwargs):
|
||||||
|
"""
|
||||||
|
Checks if device with all of the given arguments exists
|
||||||
|
"""
|
||||||
|
if Device.query.filter_by(**kwargs).first():
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<Device (name=%s, type=%s)>' % (
|
return '<Device (name=%s, type=%s)>' % (
|
||||||
self.name, self.device_type_id)
|
self.name, self.device_type_id)
|
||||||
|
|
|
@ -2,66 +2,55 @@ import sys
|
||||||
import json
|
import json
|
||||||
from flask_mqtt import Mqtt
|
from flask_mqtt import Mqtt
|
||||||
from .models import Recording
|
from .models import Recording
|
||||||
from app import db, app
|
from app import app
|
||||||
|
|
||||||
|
|
||||||
class MqttClient:
|
class MqttClient:
|
||||||
class __MqttClient:
|
__initialized = False
|
||||||
def __init__(self):
|
mqtt = Mqtt()
|
||||||
self.mqtt = Mqtt()
|
|
||||||
self.initialized = False
|
|
||||||
def __str__(self):
|
|
||||||
return repr(self)
|
|
||||||
|
|
||||||
|
|
||||||
instance = None
|
|
||||||
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
if not MqttClient.instance:
|
|
||||||
MqttClient.instance = MqttClient.__MqttClient()
|
|
||||||
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
return getattr(self.instance, name)
|
|
||||||
|
|
||||||
# Mqtt setup
|
# Mqtt setup
|
||||||
def setup(self, app):
|
@staticmethod
|
||||||
if not self.initialized:
|
def setup(app):
|
||||||
self.mqtt.init_app(app)
|
if not MqttClient.__initialized:
|
||||||
self.mqtt.client.on_message = self.handle_mqtt_message
|
MqttClient.mqtt.init_app(app)
|
||||||
self.mqtt.client.on_subscribe = self.handle_subscribe
|
MqttClient.mqtt.client.on_message = MqttClient.handle_mqtt_message
|
||||||
initialized = True
|
MqttClient.mqtt.client.on_subscribe = MqttClient.handle_subscribe
|
||||||
|
MqttClient.__initialized = True
|
||||||
|
|
||||||
@self.mqtt.on_connect()
|
@MqttClient.mqtt.on_connect()
|
||||||
def handle_connect(client, userdata, flags, rc):
|
def handle_connect(client, userdata, flags, rc):
|
||||||
print('MQTT client connected')
|
print('MQTT client connected')
|
||||||
self.mqtt.subscribe('device/+')
|
MqttClient.mqtt.subscribe('device/+')
|
||||||
|
|
||||||
@self.mqtt.on_disconnect()
|
@MqttClient.mqtt.on_disconnect()
|
||||||
def handle_disconnect():
|
def handle_disconnect():
|
||||||
print('MQTT client disconnected')
|
print('MQTT client disconnected')
|
||||||
|
|
||||||
print('MQTT client initialized')
|
print('MQTT client initialized')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def tear_down(self):
|
def tear_down():
|
||||||
self.mqtt.unsubscribe_all()
|
if MqttClient.__initialized:
|
||||||
if hasattr(self.mqtt, 'client') and self.mqtt.client is not None:
|
MqttClient.mqtt.unsubscribe_all()
|
||||||
self.mqtt.client.disconnect()
|
if (hasattr(MqttClient.mqtt, 'client') and
|
||||||
|
MqttClient.mqtt.client is not None):
|
||||||
|
MqttClient.mqtt.client.disconnect()
|
||||||
print('MQTT client destroyed')
|
print('MQTT client destroyed')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def handle_subscribe(self, client, userdata, mid, granted_qos):
|
def handle_subscribe(client, userdata, mid, granted_qos):
|
||||||
print('MQTT client subscribed')
|
print('MQTT client subscribed')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def handle_mqtt_message(self, client, userdata, message):
|
def handle_mqtt_message(client, userdata, message):
|
||||||
print("Received message!")
|
print("Received message!")
|
||||||
print("Topic: " + message.topic)
|
print("Topic: " + message.topic)
|
||||||
print("Payload: " + message.payload.decode())
|
print("Payload: " + message.payload.decode())
|
||||||
try:
|
try:
|
||||||
# If type is JSON
|
# If type is JSON
|
||||||
recording = self.parse_json_message(message.topic, message.payload.decode())
|
recording = MqttClient.parse_json_message(
|
||||||
|
message.topic, message.payload.decode())
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
recording.save()
|
recording.save()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
@ -71,11 +60,11 @@ class MqttClient:
|
||||||
print("Instance: " + str(error_instance))
|
print("Instance: " + str(error_instance))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def parse_json_message(self, topic, payload) -> Recording:
|
def parse_json_message(topic, payload) -> Recording:
|
||||||
try:
|
try:
|
||||||
json_msg = json.loads(payload)
|
json_msg = json.loads(payload)
|
||||||
device_id = self.get_device_id(topic)
|
device_id = MqttClient.get_device_id(topic)
|
||||||
return Recording(device_id=device_id,
|
return Recording(device_id=device_id,
|
||||||
record_type=json_msg["record_type"],
|
record_type=json_msg["record_type"],
|
||||||
record_value=json_msg["record_value"],
|
record_value=json_msg["record_value"],
|
||||||
|
@ -86,8 +75,8 @@ class MqttClient:
|
||||||
raise ValueError("JSON parsing failed! Key error: "
|
raise ValueError("JSON parsing failed! Key error: "
|
||||||
+ str(error_instance))
|
+ str(error_instance))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
def get_device_id(self, topic) -> int:
|
def get_device_id(topic) -> int:
|
||||||
device_token, device_id = topic.split("/")
|
device_token, device_id = topic.split("/")
|
||||||
if device_token == "device":
|
if device_token == "device":
|
||||||
return int(device_id)
|
return int(device_id)
|
||||||
|
|
|
@ -12,6 +12,25 @@ definitions:
|
||||||
description: User's name in the system
|
description: User's name in the system
|
||||||
default: testusername
|
default: testusername
|
||||||
|
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
description: ID
|
||||||
|
default: 1
|
||||||
|
|
||||||
|
datetime:
|
||||||
|
type: string
|
||||||
|
description: Time
|
||||||
|
|
||||||
|
devicename:
|
||||||
|
type: string
|
||||||
|
description: Name of device
|
||||||
|
default: My device
|
||||||
|
|
||||||
|
devicetype:
|
||||||
|
type: int
|
||||||
|
description: Type of device
|
||||||
|
default: 1
|
||||||
|
|
||||||
email:
|
email:
|
||||||
type: string
|
type: string
|
||||||
format: email
|
format: email
|
||||||
|
@ -57,6 +76,33 @@ definitions:
|
||||||
email:
|
email:
|
||||||
$ref: '#/definitions/email'
|
$ref: '#/definitions/email'
|
||||||
|
|
||||||
|
Recording:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- recorded_at
|
||||||
|
- record_type
|
||||||
|
- record_value
|
||||||
|
properties:
|
||||||
|
recorded_at:
|
||||||
|
$ref: '#/definitions/datetime'
|
||||||
|
record_type:
|
||||||
|
$ref: '#/definitions/id'
|
||||||
|
record_value:
|
||||||
|
type: string
|
||||||
|
description: Value of the recording
|
||||||
|
default: '25 degrees'
|
||||||
|
|
||||||
|
Device:
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
- name
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
$ref: '#/definitions/id'
|
||||||
|
name:
|
||||||
|
$ref: '#/definitions/devicename'
|
||||||
|
|
||||||
UnauthorizedError:
|
UnauthorizedError:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
|
|
Loading…
Reference in New Issue