#!/usr/bin/env python
from flask import Flask, request
from flask_restplus import Api, Resource, fields, abort
from .pisenseo import SenseoClassic, SenseoPreconditionError, SenseoCoffeeSizeError
from .utils import init_logger
import logging
logger = logging.getLogger(__name__)
app = Flask(__name__)
app.config.SWAGGER_UI_DOC_EXPANSION = 'list'
app.config.SWAGGER_UI_OPERATION_ID = True
app.config.SWAGGER_UI_REQUEST_DURATION = True
api = Api(
app,
version='1.0',
title='Senseo APi',
description='Manage Senseo coffee machine through Raspberry Pi GPIO',
)
ns_cm = api.namespace('senseo', description='Senseo coffee machine operations')
ns_coffee = api.namespace('coffee', description='Coffee operations')
# Will be used by a global
cm = None
# describe models
machine_status = api.model('Machine Status', {
'is_powered_on': fields.Boolean(
readOnly=True,
description='Is the coffee machine powered on?'
),
'is_ready': fields.Boolean(
readOnly=True,
description='Is the coffee machine ready to start a coffee?'
)
})
machine_power_op = api.model(
'Machine power operation', {
'power_on': fields.Boolean(
required=True,
description='Start or stop coffee machine.'
)
})
coffee_request = api.model('Start a coffee', {
'size': fields.Integer(
required=True,
description='Size of the requested coffeee (1 or 2 mug)',
min=1,
max=2,
example=1
)
})
message_op = api.model('Response message regarding the current request', {
'message': fields.String(
description='Description of the response made to the request.'
)
})
[docs]@ns_cm.route('/')
class CoffeeMachine(Resource):
"""Set and get power status for the coffee machine.
"""
[docs] @ns_cm.marshal_with(machine_status)
def get(self):
"""Get the current status of the coffee machine.
"""
logger.info("Current status of coffee machine is requested")
return {
'is_powered_on': cm.is_powered_on(),
'is_ready': cm.is_ready()
}
[docs] @ns_cm.marshal_with(message_op)
@ns_cm.expect(machine_power_op)
def post(self):
"""Update the power state of the coffee machine.
"""
req_status = request.json.get('power_on', True) # default is shutdown command
cur_status = cm.is_powered_on()
logger.info("Update of power status of the coffe machine is requested.")
# Require powered on and currently powered off
if req_status and not cur_status:
logger.debug("Start requested")
cm.start()
return {"message": "Powering on..."}
# Require powered off and currently powered on
if not req_status and cur_status:
logger.debug("Stop requested")
cm.stop()
return {"message": "Powering off..."}
# Other cases: do nothing
#if (req_status and cur_status) or (not req_status and not cur_status):
logger.info("Nothing to do: already at requested state.")
return {"message": "Nothing to do."}
[docs]@ns_coffee.route('/')
class CoffeeRun(Resource):
"""Start a coffee.
"""
[docs] @ns_cm.response(400, 'Invalid size')
@ns_cm.response(412, 'Precondition failed')
@ns_cm.response(500, 'Internal server error')
@ns_cm.marshal_with(message_op)
@ns_coffee.expect(coffee_request)
def post(self):
"""Send command to start coffee according to the requested size.
"""
size = request.json.get('size', 1) # default size is 1 cup
logger.info(f"{size} mug(s) coffee requested.")
try:
cm.coffee(size)
except SenseoPreconditionError as e:
abort(412, e.message)
except SenseoCoffeeSizeError as e:
abort(400, e.message)
except Exception as e:
abort(500, f"Unmanaged server error: {str(e)}")
return {"message": f"Starting coffee with size: {size} mug(s)"}
[docs]def main():
"""Execute the Senseo APi.
"""
global cm
init_logger()
cm = SenseoClassic('~/.senseo-api/senseo_config.json')
logger.info("Starting the Senseo APi...")
app.run(debug=False, host='0.0.0.0')
if __name__ == '__main__':
main()