GPIO added

This commit is contained in:
manuel83 2019-01-04 09:29:09 +01:00
parent 6663a40cef
commit 00c7a465d7
36 changed files with 1129 additions and 1458 deletions

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,7 @@ import logging
from asyncio import Future from asyncio import Future
from cbpi_api import * from cbpi_api import *
from voluptuous import Schema
from core.controller.crud_controller import CRUDController from core.controller.crud_controller import CRUDController
from core.database.model import ActorModel from core.database.model import ActorModel
@ -29,23 +30,28 @@ class ActorController(CRUDController):
:return: :return:
""" """
await super(ActorController, self).init() await super(ActorController, self).init()
for id, value in self.cache.items(): for id, value in self.cache.items():
await self._init_actor(value) await self._init_actor(value)
def get_state(self):
return dict(items=self.cache,types=self.types)
async def _init_actor(self, actor): async def _init_actor(self, actor):
if actor.type in self.types: try:
if actor.type in self.types:
cfg = actor.config.copy() cfg = actor.config.copy()
cfg.update(dict(cbpi=self.cbpi, id=id, name=actor.name)) cfg.update(dict(cbpi=self.cbpi, id=id, name=actor.name))
clazz = self.types[actor.type]["class"]; clazz = self.types[actor.type]["class"];
self.cache[actor.id].instance = clazz(**cfg) self.cache[actor.id].instance = clazz(**cfg)
self.cache[actor.id].instance.init() self.cache[actor.id].instance.init()
await self.cbpi.bus.fire(topic="actor/%s/initialized" % actor.id, id=actor.id) await self.cbpi.bus.fire(topic="actor/%s/initialized" % actor.id, id=actor.id)
else: else:
print("NOT FOUND") print("NOT FOUND")
self.logger.error("Actor type '%s' not found (Available Actor Types: %s)" % (actor.type, ', '.join(self.types.keys()))) self.logger.error("Actor type '%s' not found (Available Actor Types: %s)" % (actor.type, ', '.join(self.types.keys())))
except Exception as e:
self.logger.error("Failed to init actor %s - Reason %s" % (actor.id, str(e)))
async def _stop_actor(self, actor): async def _stop_actor(self, actor):
actor.instance.stop() actor.instance.stop()
@ -93,6 +99,7 @@ class ActorController(CRUDController):
else: else:
actor.on() actor.on()
@on_event(topic="actor/+/off") @on_event(topic="actor/+/off")
async def off(self, actor_id, **kwargs) -> None: async def off(self, actor_id, **kwargs) -> None:
""" """
@ -102,7 +109,6 @@ class ActorController(CRUDController):
:param actor_id: the actor actor_id :param actor_id: the actor actor_id
:param kwargs: :param kwargs:
""" """
self.logger.debug("OFF %s" % actor_id) self.logger.debug("OFF %s" % actor_id)
actor_id = int(actor_id) actor_id = int(actor_id)
@ -110,6 +116,15 @@ class ActorController(CRUDController):
actor = self.cache[actor_id].instance actor = self.cache[actor_id].instance
actor.off() actor.off()
@on_event(topic="actor/+/action")
async def call_action(self, actor_id, data, **kwargs) -> None:
schema = Schema({"name":str, "parameter":dict})
schema(data)
name = data.get("name")
parameter = data.get("parameter")
actor = self.cache[actor_id].instance.__getattribute__(name)(**parameter)
async def _post_add_callback(self, m): async def _post_add_callback(self, m):
''' '''

View file

@ -30,6 +30,9 @@ class KettleController(CRUDController):
''' '''
await super(KettleController, self).init() await super(KettleController, self).init()
def get_state(self):
return dict(items=self.cache,types=self.types)
async def toggle_automtic(self, id): async def toggle_automtic(self, id):
''' '''

View file

@ -17,5 +17,6 @@ class NotificationController(object):
@on_event(topic="notification/#") @on_event(topic="notification/#")
async def _on_event(self, key, message, type, **kwargs): async def _on_event(self, key, message, type=None, **kwargs):
self.cbpi.ws.send("YES") self.cbpi.ws.send(dict(key=message))

View file

@ -33,6 +33,9 @@ class SensorController(CRUDController):
for id, value in self.cache.items(): for id, value in self.cache.items():
await self.init_sensor(value) await self.init_sensor(value)
def get_state(self):
return dict(items=self.cache,types=self.types)
async def init_sensor(self, sensor): async def init_sensor(self, sensor):
if sensor.type in self.types: if sensor.type in self.types:
cfg = sensor.config.copy() cfg = sensor.config.copy()

View file

@ -1,4 +1,5 @@
import asyncio import asyncio
import logging
import time import time
from cbpi_api import * from cbpi_api import *
@ -17,14 +18,17 @@ class StepController(CRUDController):
def __init__(self, cbpi): def __init__(self, cbpi):
super(StepController, self).__init__(cbpi) super(StepController, self).__init__(cbpi)
self.caching = False
self.is_stopping = False
self.cbpi = cbpi self.cbpi = cbpi
self.current_task = None self.current_task = None
self.is_next = False
self.types = {} self.types = {}
self.current_step = None self.current_step = None
self.current_job = None self.current_job = None
self.cbpi.register(self) self.cbpi.register(self)
self.logger = logging.getLogger(__name__)
self.starttime = None
async def init(self): async def init(self):
''' '''
@ -54,6 +58,8 @@ class StepController(CRUDController):
self.current_job = await self.cbpi.job.start_job(self.current_step.run(), step.name, "step") self.current_job = await self.cbpi.job.start_job(self.current_step.run(), step.name, "step")
def get_state(self):
return dict(items=self.get_all(),types=self.types,is_running=self.is_running(),current_step=self.current_step)
@on_event("step/action") @on_event("step/action")
async def handle_action(self, action, **kwargs): async def handle_action(self, action, **kwargs):
@ -72,7 +78,7 @@ class StepController(CRUDController):
@on_event("step/next") @on_event("step/next")
async def handle_next(self, **kwargs): async def next(self, **kwargs):
''' '''
Event Handler for "step/next". Event Handler for "step/next".
It start the next step It start the next step
@ -80,28 +86,20 @@ class StepController(CRUDController):
:param kwargs: :param kwargs:
:return: None :return: None
''' '''
if self.current_step is not None: print("REQUEST NEXT")
self.current_step.next() self.starttime = time.time()
pass if self.current_step is not None and self.is_next is False:
self.logger.info("Request Next Step to start. Stopping current step")
self.is_next = True
self.current_step.stop()
else:
self.logger.info("Can Start Next")
@on_event("step/reset")
async def handle_reset(self, **kwargs):
'''
Event Handler for "step/reset".
Resets the current step
:param kwargs:
:return: None
'''
if self.current_step is not None:
await self.stop()
self.is_stopping = True
await self.model.reset_all_steps()
@on_event("job/step/done") @on_event("job/step/done")
async def handle_step_done(self, topic, **kwargs): async def _step_done(self, topic, **kwargs):
''' '''
Event Handler for "step/+/done". Event Handler for "step/+/done".
@ -112,17 +110,23 @@ class StepController(CRUDController):
:return: :return:
''' '''
# SHUTDONW DO NOTHING
self.logger.info("HANDLE DONE IS SHUTDONW %s IS STOPPING %s IS NEXT %s" % ( self.cbpi.shutdown, self.is_stopping, self.is_next))
if self.cbpi.shutdown: if self.cbpi.shutdown:
return return
self.cache[self.current_step.id].state = "D" if self.is_stopping:
step_id = self.current_step.id self.is_stopping = False
self.current_step = None return
self.is_next = False
if self.current_step is not None:
await self.model.update_state(self.current_step.id, "D", int(time.time()))
self.current_step = None
await self.start()
if self.is_stopping is not True:
await self.start()
self.is_stopping = False
def _get_manged_fields_as_array(self, type_cfg): def _get_manged_fields_as_array(self, type_cfg):
@ -139,7 +143,7 @@ class StepController(CRUDController):
return False return False
@on_event("step/start") @on_event("step/start")
async def start(self, future, **kwargs): async def start(self, **kwargs):
''' '''
Start the first step Start the first step
@ -147,39 +151,58 @@ class StepController(CRUDController):
:return:None :return:None
''' '''
if self.current_step is None: if self.is_running() is False:
loop = asyncio.get_event_loop() next_step = await self.model.get_by_state("I")
open_step = False
inactive = await self.model.get_by_state("I") if next_step is not None:
active = await self.model.get_by_state("A") step_type = self.types[next_step.type]
if active is not None: config = dict(cbpi=self.cbpi, id=next_step.id, name=next_step.name, managed_fields=self._get_manged_fields_as_array(step_type))
active.state = 'D'
active.end = int(time.time())
# self.stop_step()
self.current_step = None
await self.model.update(**active.__dict__)
if inactive is not None:
step_type = self.types["CustomStepCBPi"]
config = dict(cbpi=self.cbpi, id=inactive.id, name=inactive.name, managed_fields=self._get_manged_fields_as_array(step_type))
self.current_step = step_type["class"](**config) self.current_step = step_type["class"](**config)
inactive.state = 'A' next_step.state = 'A'
inactive.stepstate = inactive.config next_step.stepstate = next_step.config
inactive.start = int(time.time()) next_step.start = int(time.time())
await self.model.update(**inactive.__dict__) await self.model.update(**next_step.__dict__)
self.current_job = await self.cbpi.job.start_job(self.current_step.run(), inactive.name, "step") if self.starttime is not None:
end = time.time()
d = end - self.starttime
print("DURATION", d)
else:
print("NORMAL START")
self.current_job = await self.cbpi.job.start_job(self.current_step.run(), next_step.name, "step")
await self.cbpi.bus.fire("step/%s/started" % self.current_step.id)
else: else:
await self.cbpi.bus.fire("step/berwing/finished") await self.cbpi.bus.fire("step/brewing/finished")
else:
future.set_result(True) self.logger.error("Process Already Running")
print("----------- END")
@on_event("step/stop") @on_event("step/stop")
async def stop(self, **kwargs): async def stop(self, **kwargs):
if self.current_job is not None:
if self.current_step is not None:
self.current_step.stop()
self.is_stopping = True self.is_stopping = True
await self.current_job.close() self.current_step = None
await self.model.reset_all_steps()
await self.model.reset_all_steps()
await self.cbpi.bus.fire("step/brewing/stopped")
@on_event("step/reset")
async def handle_reset(self, **kwargs):
'''
Event Handler for "step/reset".
Resets the current step
:param kwargs:
:return: None
'''
if self.current_step is not None:
await self.stop()
self.current_step = None
self.is_stopping = True
await self.model.reset_all_steps()

View file

@ -4,6 +4,8 @@ from aiojobs.aiohttp import get_scheduler_from_app
from cbpi_api import * from cbpi_api import *
from utils import json_dumps
class SystemController(): class SystemController():
@ -12,6 +14,16 @@ class SystemController():
self.service = cbpi.actor self.service = cbpi.actor
self.cbpi.register(self, "/system") self.cbpi.register(self, "/system")
@request_mapping("/state", method="GET", auth_required=False)
def state(self, request):
# TODO implement restart
return web.json_response(data=dict(
actor=self.cbpi.actor.get_state(),
sensor=self.cbpi.sensor.get_state(),
kettle=self.cbpi.kettle.get_state(),
step=self.cbpi.step.get_state())
, dumps=json_dumps)
@request_mapping("/restart", method="POST", name="RestartServer", auth_required=False) @request_mapping("/restart", method="POST", name="RestartServer", auth_required=False)
def restart(self, request): def restart(self, request):
# TODO implement restart # TODO implement restart

View file

@ -35,8 +35,6 @@ from http_endpoints.http_step import StepHttpEndpoints
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
@web.middleware @web.middleware
async def error_middleware(request, handler): async def error_middleware(request, handler):
try: try:
@ -57,7 +55,6 @@ async def error_middleware(request, handler):
class CraftBeerPi(): class CraftBeerPi():
def __init__(self): def __init__(self):
self.static_config = load_config(os.path.join(os.path.dirname(__file__), '../config/config.yaml')) self.static_config = load_config(os.path.join(os.path.dirname(__file__), '../config/config.yaml'))
self.database_file = "./craftbeerpi.db" self.database_file = "./craftbeerpi.db"
@ -117,7 +114,7 @@ class CraftBeerPi():
''' '''
self.register_http_endpoints(obj, url_prefix, static) self.register_http_endpoints(obj, url_prefix, static)
self.bus.register_object(obj) self.bus.register_object(obj)
self.ws.register_object(obj) #self.ws.register_object(obj)
self.job.register_background_task(obj) self.job.register_background_task(obj)
self.register_on_startup(obj) self.register_on_startup(obj)

View file

@ -58,6 +58,15 @@ class StepModel(DBModel):
cursor = await db.execute("UPDATE %s SET stepstate = ? WHERE id = ?" % cls.__table_name__, (json.dumps(state), step_id)) cursor = await db.execute("UPDATE %s SET stepstate = ? WHERE id = ?" % cls.__table_name__, (json.dumps(state), step_id))
await db.commit() await db.commit()
@classmethod
async def update_state(cls, step_id, state, end=None):
async with aiosqlite.connect(DATABASE_FILE) as db:
if end is not None:
await db.execute("UPDATE %s SET state = ?, end = ? WHERE id = ?" % cls.__table_name__, (state, end, step_id))
else:
await db.execute("UPDATE %s SET state = ? WHERE id = ?" % cls.__table_name__, (state, step_id))
await db.commit()
@classmethod @classmethod
async def get_by_state(cls, state, order=True): async def get_by_state(cls, state, order=True):
@ -73,6 +82,7 @@ class StepModel(DBModel):
@classmethod @classmethod
async def reset_all_steps(cls): async def reset_all_steps(cls):
print("RESET ALL STEPS NOW")
async with aiosqlite.connect(DATABASE_FILE) as db: async with aiosqlite.connect(DATABASE_FILE) as db:
await db.execute("UPDATE %s SET state = 'I', stepstate = NULL , start = NULL, end = NULL " % cls.__table_name__) await db.execute("UPDATE %s SET state = 'I', stepstate = NULL , start = NULL, end = NULL " % cls.__table_name__)
await db.commit() await db.commit()

View file

@ -51,6 +51,7 @@ class CBPiEventBus(object):
if method in self.registry: if method in self.registry:
raise RuntimeError("Method %s already registerd. Please unregister first!" % method.__name__) raise RuntimeError("Method %s already registerd. Please unregister first!" % method.__name__)
self.logger.info("Topic %s", topic) self.logger.info("Topic %s", topic)
node = self._root node = self._root
for sym in topic.split('/'): for sym in topic.split('/'):
node = node._children.setdefault(sym, self.Node()) node = node._children.setdefault(sym, self.Node())
@ -104,7 +105,6 @@ class CBPiEventBus(object):
self.loop = asyncio.get_event_loop() self.loop = asyncio.get_event_loop()
def sync_fire(self,topic: str,timeout=1, **kwargs): def sync_fire(self,topic: str,timeout=1, **kwargs):
self.loop.create_task(self.fire(topic=topic, timeout=timeout, **kwargs)) self.loop.create_task(self.fire(topic=topic, timeout=timeout, **kwargs))

View file

@ -1,44 +1,90 @@
import logging import logging
from unittest.mock import MagicMock, patch
from cbpi_api import * from cbpi_api import *
from cbpi_api.exceptions import CBPiException
logger = logging.getLogger(__name__)
try:
import RPi.GPIO as GPIO
except Exception:
logger.error("Failed to load RPi.GPIO. Using Mock")
MockRPi = MagicMock()
modules = {
"RPi": MockRPi,
"RPi.GPIO": MockRPi.GPIO
}
patcher = patch.dict("sys.modules", modules)
patcher.start()
import RPi.GPIO as GPIO
class CustomActor(CBPiActor): class CustomActor(CBPiActor):
# Custom property which can be configured by the user # Custom property which can be configured by the user
gpio = Property.Number(label="Test")
v1 = Property.Text(label="Test")
v2 = Property.Kettle(label="Test")
v3 = Property.Sensor(label="Test")
def init(self): def init(self):
pass pass
def stop(self): def on(self, power=0):
pass logger.info("ACTOR %s ON" % self.id)
self.state = True
@action(key="name", parameters={})
def myAction(self):
pass
def state(self):
super().state()
def off(self): def off(self):
logger.info("ACTOR %s OFF " % self.id)
print("OFF")
# Code to swtich the actor off goes here
self.state = False self.state = False
def on(self, power=100):
print("ON")
# Code to swtich the actor on goes here
class GPIOActor(CBPiActor):
# Custom property which can be configured by the user
gpio = Property.Select("GPIO", options=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27], description="GPIO to which the actor is connected")
def init(self):
try:
GPIO.setup(int(self.gpio), GPIO.OUT)
GPIO.output(int(self.gpio), 0)
except Exception as e:
raise CBPiException("FAILD TO INIT ACTOR")
def on(self, power=0):
print("GPIO ON %s" % str(self.gpio))
GPIO.output(int(self.gpio), 1)
self.state = True self.state = True
def off(self):
print("GPIO OFF %s" % str(self.gpio))
GPIO.output(int(self.gpio), 0)
self.state = False
class GPIORelayBoardActor(CBPiActor):
# Custom property which can be configured by the user
gpio = Property.Select("GPIO", options=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27], description="GPIO to which the actor is connected")
def init(self):
try:
GPIO.setup(int(self.gpio), GPIO.OUT)
GPIO.output(int(self.gpio), 1)
except Exception as e:
raise CBPiException("FAILD TO INIT ACTOR")
def on(self, power=0):
print("GPIO ON %s" % str(self.gpio))
GPIO.output(int(self.gpio), 0)
self.state = True
def off(self):
print("GPIO OFF %s" % str(self.gpio))
GPIO.output(int(self.gpio), 1)
self.state = False
def setup(cbpi): def setup(cbpi):

View file

@ -14,14 +14,12 @@ class CustomStepCBPi(CBPiSimpleStep):
self.name="WOOHOO" self.name="WOOHOO"
async def run_cycle(self): async def run_cycle(self):
print("RUN", self.name)
#await asyncio.sleep(1)
self.i = self.i + 1 self.i = self.i + 1
self.name="HALLO WELT" if self.i == 5:
if self.i == 20: # print("NEXT")
self.next() self.next()
self.cbpi.notify(key="step", message="HELLO FROM STEP") #self.cbpi.notify(key="step", message="HELLO FROM STEP")

View file

@ -231,4 +231,39 @@ class ActorHttpEndpoints(HttpCrudEndpoints):
actor_id = int(request.match_info['id']) actor_id = int(request.match_info['id'])
await self.cbpi.bus.fire(topic="actor/%s/toggle" % actor_id, actor_id=actor_id) await self.cbpi.bus.fire(topic="actor/%s/toggle" % actor_id, actor_id=actor_id)
return web.Response(status=204)
@request_mapping(path="/{id:\d+}/action", method="POST", auth_required=auth)
async def http_action(self, request) -> web.Response:
"""
---
description: Toogle an actor on or off
tags:
- Actor
parameters:
- name: "id"
in: "path"
description: "Actor ID"
required: true
type: "integer"
format: "int64"
- in: body
name: body
description: Update an actor
required: false
schema:
type: object
properties:
name:
type: string
config:
type: object
responses:
"204":
description: successful operation
"""
actor_id = int(request.match_info['id'])
await self.cbpi.bus.fire(topic="actor/%s/action" % actor_id, actor_id=actor_id, data=await request.json())
return web.Response(status=204) return web.Response(status=204)

View file

@ -178,12 +178,10 @@ class StepHttpEndpoints(HttpCrudEndpoints):
""" """
if self.controller.is_running(): if self.controller.is_running():
raise CBPiException("Brewing Process Already Running") raise CBPiException("Brewing Process Already Running")
result = await self.cbpi.bus.fire("step/start") print("FIRE START FROM HTTP")
r = result.get("core.controller.step_controller.start") await self.cbpi.bus.fire("step/start")
if r[0] is True: return web.Response(status=204)
return web.Response(status=204)
else:
raise CBPiException("Failed to start brewing process")
@request_mapping(path="/reset", auth_required=False) @request_mapping(path="/reset", auth_required=False)
async def http_reset(self, request): async def http_reset(self, request):

View file

@ -2,7 +2,6 @@ from json import JSONEncoder
class ComplexEncoder(JSONEncoder): class ComplexEncoder(JSONEncoder):
def default(self, obj): def default(self, obj):
from core.database.orm_framework import DBModel from core.database.orm_framework import DBModel
@ -17,7 +16,7 @@ class ComplexEncoder(JSONEncoder):
#elif hasattr(obj, "callback"): #elif hasattr(obj, "callback"):
# return obj() # return obj()
else: else:
return None raise TypeError()
except TypeError: except TypeError:
pass pass
return None return None

View file

@ -1,11 +1,13 @@
import logging import logging
import weakref import weakref
from collections import defaultdict from collections import defaultdict
import json
import aiohttp import aiohttp
from aiohttp import web from aiohttp import web
from typing import Iterable, Callable
from cbpi_api import * from cbpi_api import *
from voluptuous import Schema
from utils import json_dumps
class CBPiWebSocket: class CBPiWebSocket:
@ -19,60 +21,53 @@ class CBPiWebSocket:
@on_event(topic="#") @on_event(topic="#")
async def listen(self, topic, **kwargs): async def listen(self, topic, **kwargs):
from core.utils.encoder import ComplexEncoder data = dict(topic=topic, data=dict(**kwargs))
data = json.dumps(dict(topic=topic, data=dict(**kwargs)),skipkeys=True, check_circular=True, cls=ComplexEncoder)
self.logger.info("PUSH %s " % data) self.logger.info("PUSH %s " % data)
self.send(data) self.send(data)
def send(self, data): def send(self, data):
self.logger.debug("broadcast to ws clients. Data: %s" % data)
for ws in self._clients: for ws in self._clients:
async def send_data(ws, data): async def send_data(ws, data):
await ws.send_str(data) await ws.send_json(data=data, dumps=json_dumps)
self.cbpi.app.loop.create_task(send_data(ws, data)) self.cbpi.app.loop.create_task(send_data(ws, data))
def add_callback(self, func: Callable, event: str) -> None:
self._callbacks[event].add(func)
def register_object(self, obj):
for method in [getattr(obj, f) for f in dir(obj) if callable(getattr(obj, f)) and hasattr(getattr(obj, f), "ws")]:
self.add_callback(method, method.__getattribute__("key"))
async def emit(self, event: str, *args, **kwargs) -> None:
for func in self._event_funcs(event):
await func(*args, **kwargs)
def _event_funcs(self, event: str) -> Iterable[Callable]:
for func in self._callbacks[event]:
yield func
async def websocket_handler(self, request): async def websocket_handler(self, request):
ws = web.WebSocketResponse() ws = web.WebSocketResponse()
await ws.prepare(request) await ws.prepare(request)
self._clients.add(ws) self._clients.add(ws)
peername = request.transport.get_extra_info('peername')
if peername is not None:
host, port = peername
else:
host, port = "Unknowen"
c = len(self._clients) - 1 self.logger.info("Client Connected - Host: %s Port: %s - client count: %s " % (host, port, len(self._clients)))
self.logger.info(ws)
self.logger.info(c)
try: try:
await ws.send_json(data=dict(topic="connection/success"))
async for msg in ws: async for msg in ws:
if msg.type == aiohttp.WSMsgType.TEXT: if msg.type == aiohttp.WSMsgType.TEXT:
if msg.data == 'close':
msg_obj = msg.json()
schema = Schema({"topic": str, "data": dict})
schema(msg_obj)
topic = msg_obj.get("topic")
data = msg_obj.get("data")
if topic == "close":
await ws.close() await ws.close()
self.logger.info("WS Close")
else: else:
msg_obj = msg.json() if data is not None:
await self.cbpi.bus.fire(topic=topic, **data)
await self.cbpi.bus.fire(msg_obj["topic"], id=1, power=22) else:
# await self.fire(msg_obj["key"], ws, msg) await self.cbpi.bus.fire(topic=topic)
# await ws.send_str(msg.data)
elif msg.type == aiohttp.WSMsgType.ERROR: elif msg.type == aiohttp.WSMsgType.ERROR:
self.logger.error('ws connection closed with exception %s' % ws.exception()) self.logger.error('ws connection closed with exception %s' % ws.exception())
except Exception as e:
self.logger.error("%s - Received Data %s" % (str(e), msg.data))
finally: finally:
self._clients.discard(ws) self._clients.discard(ws)

Binary file not shown.

View file

@ -230,7 +230,7 @@
</li> </li>
<li><a href="step.html#core.controller.step_controller.StepController.handle_done">handle_done() (core.controller.step_controller.StepController method)</a> <li><a href="step.html#core.controller.step_controller.StepController.handle_done">handle_done() (core.controller.step_controller.StepController method)</a>
</li> </li>
<li><a href="step.html#core.controller.step_controller.StepController.handle_next">handle_next() (core.controller.step_controller.StepController method)</a> <li><a href="step.html#core.controller.step_controller.StepController.handle_next">next() (core.controller.step_controller.StepController method)</a>
</li> </li>
<li><a href="step.html#core.controller.step_controller.StepController.handle_reset">handle_reset() (core.controller.step_controller.StepController method)</a> <li><a href="step.html#core.controller.step_controller.StepController.handle_reset">handle_reset() (core.controller.step_controller.StepController method)</a>
</li> </li>

View file

@ -220,7 +220,7 @@ Starts the next step</p>
<dl class="method"> <dl class="method">
<dt id="core.controller.step_controller.StepController.handle_next"> <dt id="core.controller.step_controller.StepController.handle_next">
<code class="descname">handle_next</code><span class="sig-paren">(</span><em>**kwargs</em><span class="sig-paren">)</span><a class="headerlink" href="#core.controller.step_controller.StepController.handle_next" title="Permalink to this definition"></a></dt> <code class="descname">next</code><span class="sig-paren">(</span><em>**kwargs</em><span class="sig-paren">)</span><a class="headerlink" href="#core.controller.step_controller.StepController.handle_next" title="Permalink to this definition"></a></dt>
<dd><p>Event Handler for “step/next”. <dd><p>Event Handler for “step/next”.
It start the next step</p> It start the next step</p>
<table class="docutils field-list" frame="void" rules="none"> <table class="docutils field-list" frame="void" rules="none">

View file

@ -1,24 +1,34 @@
2019-01-02 20:34:10,10 2019-01-04 00:44:35,10
2019-01-02 20:34:15,10 2019-01-04 00:44:40,10
2019-01-02 20:34:20,10 2019-01-04 00:44:45,10
2019-01-02 20:34:25,10 2019-01-04 00:44:50,10
2019-01-02 20:34:32,10 2019-01-04 00:44:55,10
2019-01-02 20:34:37,10 2019-01-04 00:45:00,10
2019-01-02 20:34:42,10 2019-01-04 00:45:05,10
2019-01-02 20:34:47,10 2019-01-04 00:45:10,10
2019-01-02 20:34:52,10 2019-01-04 00:45:15,10
2019-01-02 20:34:57,10 2019-01-04 00:45:20,10
2019-01-02 20:35:02,10 2019-01-04 00:45:25,10
2019-01-02 20:35:07,10 2019-01-04 00:45:30,10
2019-01-02 20:35:12,10 2019-01-04 00:45:35,10
2019-01-02 20:35:17,10 2019-01-04 00:45:40,10
2019-01-02 20:35:22,10 2019-01-04 00:45:45,10
2019-01-02 20:35:30,10 2019-01-04 00:45:50,10
2019-01-02 20:35:35,10 2019-01-04 00:45:55,10
2019-01-02 20:35:40,10 2019-01-04 00:46:00,10
2019-01-02 20:35:45,10 2019-01-04 00:46:05,10
2019-01-02 20:35:50,10 2019-01-04 00:46:10,10
2019-01-02 20:35:55,10 2019-01-04 00:46:15,10
2019-01-02 20:36:00,10 2019-01-04 00:46:20,10
2019-01-02 20:36:05,10 2019-01-04 00:46:31,10
2019-01-02 20:36:10,10 2019-01-04 00:46:36,10
2019-01-04 00:46:41,10
2019-01-04 00:46:46,10
2019-01-04 00:46:51,10
2019-01-04 00:46:56,10
2019-01-04 00:47:01,10
2019-01-04 00:47:06,10
2019-01-04 00:47:11,10
2019-01-04 00:47:16,10
2019-01-04 00:47:21,10
2019-01-04 00:47:26,10

View file

@ -1,86 +1,86 @@
2019-01-02 20:26:40,10 2019-01-04 00:19:10,10
2019-01-02 20:26:45,10 2019-01-04 00:19:15,10
2019-01-02 20:26:50,10 2019-01-04 00:19:20,10
2019-01-02 20:26:55,10 2019-01-04 00:19:25,10
2019-01-02 20:27:00,10 2019-01-04 00:19:30,10
2019-01-02 20:27:05,10 2019-01-04 00:19:48,10
2019-01-02 20:27:10,10 2019-01-04 00:19:53,10
2019-01-02 20:27:15,10 2019-01-04 00:20:04,10
2019-01-02 20:27:20,10 2019-01-04 00:20:09,10
2019-01-02 20:27:25,10 2019-01-04 00:20:14,10
2019-01-02 20:27:30,10 2019-01-04 00:20:19,10
2019-01-02 20:27:35,10 2019-01-04 00:20:24,10
2019-01-02 20:27:40,10 2019-01-04 00:20:29,10
2019-01-02 20:27:45,10 2019-01-04 00:20:34,10
2019-01-02 20:27:51,10 2019-01-04 00:20:39,10
2019-01-02 20:27:56,10 2019-01-04 00:20:44,10
2019-01-02 20:28:01,10 2019-01-04 00:20:49,10
2019-01-02 20:28:06,10 2019-01-04 00:20:54,10
2019-01-02 20:28:11,10 2019-01-04 00:20:59,10
2019-01-02 20:28:16,10 2019-01-04 00:38:43,10
2019-01-02 20:28:21,10 2019-01-04 00:38:48,10
2019-01-02 20:28:26,10 2019-01-04 00:38:53,10
2019-01-02 20:28:31,10 2019-01-04 00:38:58,10
2019-01-02 20:28:36,10 2019-01-04 00:39:03,10
2019-01-02 20:28:41,10 2019-01-04 00:39:08,10
2019-01-02 20:28:46,10 2019-01-04 00:39:13,10
2019-01-02 20:28:51,10 2019-01-04 00:39:18,10
2019-01-02 20:28:56,10 2019-01-04 00:39:23,10
2019-01-02 20:29:01,10 2019-01-04 00:39:28,10
2019-01-02 20:29:06,10 2019-01-04 00:39:33,10
2019-01-02 20:29:11,10 2019-01-04 00:39:38,10
2019-01-02 20:29:16,10 2019-01-04 00:39:45,10
2019-01-02 20:29:21,10 2019-01-04 00:39:54,10
2019-01-02 20:29:26,10 2019-01-04 00:39:59,10
2019-01-02 20:29:31,10 2019-01-04 00:40:04,10
2019-01-02 20:29:36,10 2019-01-04 00:40:09,10
2019-01-02 20:29:41,10 2019-01-04 00:40:14,10
2019-01-02 20:29:46,10 2019-01-04 00:40:21,10
2019-01-02 20:29:51,10 2019-01-04 00:40:31,10
2019-01-02 20:29:56,10 2019-01-04 00:40:36,10
2019-01-02 20:30:01,10 2019-01-04 00:40:41,10
2019-01-02 20:30:06,10 2019-01-04 00:40:46,10
2019-01-02 20:30:11,10 2019-01-04 00:40:51,10
2019-01-02 20:30:16,10 2019-01-04 00:40:56,10
2019-01-02 20:30:21,10 2019-01-04 00:41:01,10
2019-01-02 20:30:26,10 2019-01-04 00:41:06,10
2019-01-02 20:30:31,10 2019-01-04 00:41:11,10
2019-01-02 20:30:36,10 2019-01-04 00:41:16,10
2019-01-02 20:30:41,10 2019-01-04 00:41:21,10
2019-01-02 20:30:46,10 2019-01-04 00:41:26,10
2019-01-02 20:30:51,10 2019-01-04 00:41:31,10
2019-01-02 20:30:56,10 2019-01-04 00:41:36,10
2019-01-02 20:31:01,10 2019-01-04 00:41:41,10
2019-01-02 20:31:06,10 2019-01-04 00:41:46,10
2019-01-02 20:31:11,10 2019-01-04 00:41:51,10
2019-01-02 20:31:16,10 2019-01-04 00:41:56,10
2019-01-02 20:31:21,10 2019-01-04 00:42:01,10
2019-01-02 20:31:26,10 2019-01-04 00:42:06,10
2019-01-02 20:31:31,10 2019-01-04 00:42:11,10
2019-01-02 20:31:36,10 2019-01-04 00:42:16,10
2019-01-02 20:31:41,10 2019-01-04 00:42:21,10
2019-01-02 20:31:46,10 2019-01-04 00:42:26,10
2019-01-02 20:31:51,10 2019-01-04 00:42:31,10
2019-01-02 20:31:56,10 2019-01-04 00:42:36,10
2019-01-02 20:32:01,10 2019-01-04 00:42:41,10
2019-01-02 20:32:06,10 2019-01-04 00:42:46,10
2019-01-02 20:32:11,10 2019-01-04 00:42:51,10
2019-01-02 20:32:16,10 2019-01-04 00:42:56,10
2019-01-02 20:32:21,10 2019-01-04 00:43:05,10
2019-01-02 20:32:26,10 2019-01-04 00:43:10,10
2019-01-02 20:32:31,10 2019-01-04 00:43:15,10
2019-01-02 20:32:36,10 2019-01-04 00:43:20,10
2019-01-02 20:32:41,10 2019-01-04 00:43:25,10
2019-01-02 20:33:05,10 2019-01-04 00:43:30,10
2019-01-02 20:33:10,10 2019-01-04 00:43:35,10
2019-01-02 20:33:15,10 2019-01-04 00:43:40,10
2019-01-02 20:33:20,10 2019-01-04 00:43:45,10
2019-01-02 20:33:25,10 2019-01-04 00:43:50,10
2019-01-02 20:33:30,10 2019-01-04 00:43:55,10
2019-01-02 20:33:35,10 2019-01-04 00:44:00,10
2019-01-02 20:33:40,10 2019-01-04 00:44:05,10
2019-01-02 20:33:45,10 2019-01-04 00:44:10,10
2019-01-02 20:33:50,10 2019-01-04 00:44:15,10
2019-01-02 20:33:55,10 2019-01-04 00:44:20,10
2019-01-02 20:34:00,10 2019-01-04 00:44:25,10
2019-01-02 20:34:05,10 2019-01-04 00:44:30,10

View file

@ -1,86 +1,5 @@
2019-01-02 18:48:13,10 2019-01-04 00:19:10,10
2019-01-02 18:48:18,10 2019-01-04 00:19:15,10
2019-01-02 18:48:23,10 2019-01-04 00:19:20,10
2019-01-02 18:48:28,10 2019-01-04 00:19:25,10
2019-01-02 18:48:33,10 2019-01-04 00:19:30,10
2019-01-02 18:48:38,10
2019-01-02 18:48:43,10
2019-01-02 18:48:48,10
2019-01-02 18:48:53,10
2019-01-02 18:48:58,10
2019-01-02 18:49:03,10
2019-01-02 18:49:07,10
2019-01-02 18:49:07,10
2019-01-02 18:49:07,10
2019-01-02 18:49:07,10
2019-01-02 18:49:07,10
2019-01-02 18:49:07,10
2019-01-02 18:49:07,10
2019-01-02 18:49:07,10
2019-01-02 18:49:07,10
2019-01-02 18:49:07,10
2019-01-02 18:49:07,10
2019-01-02 18:49:08,10
2019-01-02 18:49:13,10
2019-01-02 18:49:18,10
2019-01-02 18:49:23,10
2019-01-02 18:49:28,10
2019-01-02 18:49:33,10
2019-01-02 18:49:38,10
2019-01-02 18:49:43,10
2019-01-02 18:49:48,10
2019-01-02 18:49:53,10
2019-01-02 18:49:58,10
2019-01-02 18:49:58,10
2019-01-02 18:49:58,10
2019-01-02 18:49:58,10
2019-01-02 18:49:58,10
2019-01-02 18:49:58,10
2019-01-02 18:49:58,10
2019-01-02 18:49:58,10
2019-01-02 18:49:58,10
2019-01-02 18:49:58,10
2019-01-02 18:49:58,10
2019-01-02 18:49:58,10
2019-01-02 18:50:03,10
2019-01-02 18:50:08,10
2019-01-02 18:50:13,10
2019-01-02 18:50:18,10
2019-01-02 18:50:23,10
2019-01-02 18:50:28,10
2019-01-02 18:50:33,10
2019-01-02 18:50:38,10
2019-01-02 18:50:43,10
2019-01-02 18:50:48,10
2019-01-02 18:50:53,10
2019-01-02 18:50:58,10
2019-01-02 18:51:03,10
2019-01-02 18:51:08,10
2019-01-02 18:51:13,10
2019-01-02 18:51:18,10
2019-01-02 18:51:23,10
2019-01-02 18:51:28,10
2019-01-02 18:51:29,10
2019-01-02 18:51:29,10
2019-01-02 18:51:29,10
2019-01-02 18:51:29,10
2019-01-02 18:51:29,10
2019-01-02 18:51:29,10
2019-01-02 18:51:29,10
2019-01-02 18:51:29,10
2019-01-02 18:51:29,10
2019-01-02 18:51:29,10
2019-01-02 18:51:29,10
2019-01-02 18:51:33,10
2019-01-02 18:51:38,10
2019-01-02 18:51:43,10
2019-01-02 18:51:43,10
2019-01-02 18:51:43,10
2019-01-02 18:51:43,10
2019-01-02 18:51:43,10
2019-01-02 18:51:43,10
2019-01-02 18:51:43,10
2019-01-02 18:51:43,10
2019-01-02 18:51:43,10
2019-01-02 18:51:43,10
2019-01-02 18:51:43,10

View file

@ -1,86 +1,5 @@
2019-01-02 19:43:47,10 2019-01-04 00:19:10,10
2019-01-02 19:43:52,10 2019-01-04 00:19:15,10
2019-01-02 19:43:57,10 2019-01-04 00:19:20,10
2019-01-02 19:44:02,10 2019-01-04 00:19:25,10
2019-01-02 19:44:07,10 2019-01-04 00:19:30,10
2019-01-02 19:44:12,10
2019-01-02 19:44:17,10
2019-01-02 19:44:22,10
2019-01-02 19:44:27,10
2019-01-02 19:44:32,10
2019-01-02 19:44:37,10
2019-01-02 19:44:42,10
2019-01-02 19:44:47,10
2019-01-02 19:44:52,10
2019-01-02 19:44:57,10
2019-01-02 19:45:02,10
2019-01-02 19:45:07,10
2019-01-02 19:45:12,10
2019-01-02 19:45:17,10
2019-01-02 19:45:22,10
2019-01-02 19:45:27,10
2019-01-02 20:21:15,10
2019-01-02 20:21:20,10
2019-01-02 20:21:25,10
2019-01-02 20:21:30,10
2019-01-02 20:21:35,10
2019-01-02 20:21:40,10
2019-01-02 20:21:45,10
2019-01-02 20:21:50,10
2019-01-02 20:21:55,10
2019-01-02 20:22:00,10
2019-01-02 20:22:05,10
2019-01-02 20:22:10,10
2019-01-02 20:22:15,10
2019-01-02 20:22:20,10
2019-01-02 20:22:25,10
2019-01-02 20:22:30,10
2019-01-02 20:22:35,10
2019-01-02 20:22:40,10
2019-01-02 20:22:45,10
2019-01-02 20:22:50,10
2019-01-02 20:22:55,10
2019-01-02 20:23:00,10
2019-01-02 20:23:05,10
2019-01-02 20:23:10,10
2019-01-02 20:23:15,10
2019-01-02 20:23:20,10
2019-01-02 20:23:25,10
2019-01-02 20:23:30,10
2019-01-02 20:23:35,10
2019-01-02 20:23:40,10
2019-01-02 20:23:45,10
2019-01-02 20:23:50,10
2019-01-02 20:23:55,10
2019-01-02 20:24:00,10
2019-01-02 20:24:05,10
2019-01-02 20:24:10,10
2019-01-02 20:24:15,10
2019-01-02 20:24:20,10
2019-01-02 20:24:25,10
2019-01-02 20:24:30,10
2019-01-02 20:24:35,10
2019-01-02 20:24:40,10
2019-01-02 20:24:45,10
2019-01-02 20:24:50,10
2019-01-02 20:24:55,10
2019-01-02 20:25:00,10
2019-01-02 20:25:05,10
2019-01-02 20:25:10,10
2019-01-02 20:25:15,10
2019-01-02 20:25:20,10
2019-01-02 20:25:25,10
2019-01-02 20:25:30,10
2019-01-02 20:25:35,10
2019-01-02 20:25:40,10
2019-01-02 20:25:45,10
2019-01-02 20:25:50,10
2019-01-02 20:25:55,10
2019-01-02 20:26:00,10
2019-01-02 20:26:05,10
2019-01-02 20:26:10,10
2019-01-02 20:26:15,10
2019-01-02 20:26:20,10
2019-01-02 20:26:25,10
2019-01-02 20:26:30,10
2019-01-02 20:26:35,10

View file

@ -1,86 +1,5 @@
2019-01-02 19:36:04,10 2019-01-04 00:19:10,10
2019-01-02 19:36:09,10 2019-01-04 00:19:15,10
2019-01-02 19:36:14,10 2019-01-04 00:19:20,10
2019-01-02 19:36:19,10 2019-01-04 00:19:25,10
2019-01-02 19:36:24,10 2019-01-04 00:19:30,10
2019-01-02 19:36:29,10
2019-01-02 19:36:34,10
2019-01-02 19:36:39,10
2019-01-02 19:36:44,10
2019-01-02 19:36:49,10
2019-01-02 19:36:54,10
2019-01-02 19:36:59,10
2019-01-02 19:37:04,10
2019-01-02 19:37:09,10
2019-01-02 19:37:14,10
2019-01-02 19:37:19,10
2019-01-02 19:37:24,10
2019-01-02 19:37:30,10
2019-01-02 19:37:35,10
2019-01-02 19:37:40,10
2019-01-02 19:37:45,10
2019-01-02 19:37:50,10
2019-01-02 19:37:55,10
2019-01-02 19:38:00,10
2019-01-02 19:38:05,10
2019-01-02 19:38:10,10
2019-01-02 19:38:15,10
2019-01-02 19:38:20,10
2019-01-02 19:38:25,10
2019-01-02 19:38:30,10
2019-01-02 19:38:35,10
2019-01-02 19:38:44,10
2019-01-02 19:38:49,10
2019-01-02 19:38:54,10
2019-01-02 19:38:59,10
2019-01-02 19:39:04,10
2019-01-02 19:39:09,10
2019-01-02 19:39:14,10
2019-01-02 19:39:22,10
2019-01-02 19:39:27,10
2019-01-02 19:39:32,10
2019-01-02 19:39:37,10
2019-01-02 19:39:42,10
2019-01-02 19:39:47,10
2019-01-02 19:39:52,10
2019-01-02 19:39:58,10
2019-01-02 19:40:03,10
2019-01-02 19:40:08,10
2019-01-02 19:40:13,10
2019-01-02 19:40:18,10
2019-01-02 19:40:23,10
2019-01-02 19:40:28,10
2019-01-02 19:40:33,10
2019-01-02 19:40:43,10
2019-01-02 19:40:48,10
2019-01-02 19:40:53,10
2019-01-02 19:40:58,10
2019-01-02 19:41:03,10
2019-01-02 19:41:08,10
2019-01-02 19:41:20,10
2019-01-02 19:41:25,10
2019-01-02 19:41:36,10
2019-01-02 19:41:41,10
2019-01-02 19:41:46,10
2019-01-02 19:41:51,10
2019-01-02 19:41:56,10
2019-01-02 19:42:01,10
2019-01-02 19:42:06,10
2019-01-02 19:42:11,10
2019-01-02 19:42:16,10
2019-01-02 19:42:21,10
2019-01-02 19:42:26,10
2019-01-02 19:42:31,10
2019-01-02 19:42:36,10
2019-01-02 19:42:41,10
2019-01-02 19:42:46,10
2019-01-02 19:42:51,10
2019-01-02 19:42:56,10
2019-01-02 19:43:01,10
2019-01-02 19:43:06,10
2019-01-02 19:43:11,10
2019-01-02 19:43:22,10
2019-01-02 19:43:27,10
2019-01-02 19:43:32,10
2019-01-02 19:43:37,10
2019-01-02 19:43:42,10

View file

@ -1,86 +1,5 @@
2019-01-02 19:28:35,10 2019-01-04 00:19:10,10
2019-01-02 19:28:40,10 2019-01-04 00:19:15,10
2019-01-02 19:28:47,10 2019-01-04 00:19:20,10
2019-01-02 19:28:52,10 2019-01-04 00:19:25,10
2019-01-02 19:28:57,10 2019-01-04 00:19:30,10
2019-01-02 19:29:02,10
2019-01-02 19:29:07,10
2019-01-02 19:29:12,10
2019-01-02 19:29:17,10
2019-01-02 19:29:22,10
2019-01-02 19:29:27,10
2019-01-02 19:29:32,10
2019-01-02 19:29:44,10
2019-01-02 19:29:49,10
2019-01-02 19:29:54,10
2019-01-02 19:29:59,10
2019-01-02 19:30:04,10
2019-01-02 19:30:09,10
2019-01-02 19:30:14,10
2019-01-02 19:30:19,10
2019-01-02 19:30:24,10
2019-01-02 19:30:29,10
2019-01-02 19:30:34,10
2019-01-02 19:30:39,10
2019-01-02 19:30:46,10
2019-01-02 19:30:51,10
2019-01-02 19:30:56,10
2019-01-02 19:31:01,10
2019-01-02 19:31:09,10
2019-01-02 19:31:14,10
2019-01-02 19:31:19,10
2019-01-02 19:31:24,10
2019-01-02 19:31:29,10
2019-01-02 19:31:34,10
2019-01-02 19:31:39,10
2019-01-02 19:31:44,10
2019-01-02 19:31:49,10
2019-01-02 19:31:54,10
2019-01-02 19:31:59,10
2019-01-02 19:32:04,10
2019-01-02 19:32:13,10
2019-01-02 19:32:18,10
2019-01-02 19:32:23,10
2019-01-02 19:32:28,10
2019-01-02 19:32:33,10
2019-01-02 19:32:38,10
2019-01-02 19:32:43,10
2019-01-02 19:32:48,10
2019-01-02 19:32:53,10
2019-01-02 19:32:58,10
2019-01-02 19:33:03,10
2019-01-02 19:33:08,10
2019-01-02 19:33:13,10
2019-01-02 19:33:18,10
2019-01-02 19:33:23,10
2019-01-02 19:33:28,10
2019-01-02 19:33:33,10
2019-01-02 19:33:38,10
2019-01-02 19:33:43,10
2019-01-02 19:33:48,10
2019-01-02 19:33:53,10
2019-01-02 19:33:58,10
2019-01-02 19:34:03,10
2019-01-02 19:34:08,10
2019-01-02 19:34:13,10
2019-01-02 19:34:18,10
2019-01-02 19:34:23,10
2019-01-02 19:34:28,10
2019-01-02 19:34:33,10
2019-01-02 19:34:38,10
2019-01-02 19:34:44,10
2019-01-02 19:34:49,10
2019-01-02 19:34:54,10
2019-01-02 19:34:59,10
2019-01-02 19:35:04,10
2019-01-02 19:35:09,10
2019-01-02 19:35:14,10
2019-01-02 19:35:19,10
2019-01-02 19:35:24,10
2019-01-02 19:35:29,10
2019-01-02 19:35:34,10
2019-01-02 19:35:39,10
2019-01-02 19:35:44,10
2019-01-02 19:35:49,10
2019-01-02 19:35:54,10
2019-01-02 19:35:59,10

View file

@ -1,86 +1,5 @@
2019-01-02 19:21:17,10 2019-01-04 00:19:10,10
2019-01-02 19:21:22,10 2019-01-04 00:19:15,10
2019-01-02 19:21:27,10 2019-01-04 00:19:20,10
2019-01-02 19:21:32,10 2019-01-04 00:19:25,10
2019-01-02 19:21:37,10 2019-01-04 00:19:30,10
2019-01-02 19:21:42,10
2019-01-02 19:21:47,10
2019-01-02 19:21:52,10
2019-01-02 19:21:57,10
2019-01-02 19:22:02,10
2019-01-02 19:22:07,10
2019-01-02 19:22:12,10
2019-01-02 19:22:19,10
2019-01-02 19:22:24,10
2019-01-02 19:22:29,10
2019-01-02 19:22:34,10
2019-01-02 19:22:39,10
2019-01-02 19:22:44,10
2019-01-02 19:22:49,10
2019-01-02 19:22:54,10
2019-01-02 19:22:59,10
2019-01-02 19:23:04,10
2019-01-02 19:23:09,10
2019-01-02 19:23:14,10
2019-01-02 19:23:19,10
2019-01-02 19:23:24,10
2019-01-02 19:23:29,10
2019-01-02 19:23:34,10
2019-01-02 19:23:39,10
2019-01-02 19:23:44,10
2019-01-02 19:23:49,10
2019-01-02 19:23:54,10
2019-01-02 19:24:05,10
2019-01-02 19:24:10,10
2019-01-02 19:24:15,10
2019-01-02 19:24:20,10
2019-01-02 19:24:25,10
2019-01-02 19:24:30,10
2019-01-02 19:24:35,10
2019-01-02 19:24:40,10
2019-01-02 19:24:45,10
2019-01-02 19:24:50,10
2019-01-02 19:24:55,10
2019-01-02 19:25:00,10
2019-01-02 19:25:05,10
2019-01-02 19:25:10,10
2019-01-02 19:25:15,10
2019-01-02 19:25:20,10
2019-01-02 19:25:25,10
2019-01-02 19:25:30,10
2019-01-02 19:25:35,10
2019-01-02 19:25:40,10
2019-01-02 19:25:45,10
2019-01-02 19:25:50,10
2019-01-02 19:25:55,10
2019-01-02 19:26:00,10
2019-01-02 19:26:05,10
2019-01-02 19:26:10,10
2019-01-02 19:26:15,10
2019-01-02 19:26:20,10
2019-01-02 19:26:25,10
2019-01-02 19:26:30,10
2019-01-02 19:26:35,10
2019-01-02 19:26:40,10
2019-01-02 19:26:45,10
2019-01-02 19:26:50,10
2019-01-02 19:26:55,10
2019-01-02 19:27:00,10
2019-01-02 19:27:05,10
2019-01-02 19:27:10,10
2019-01-02 19:27:15,10
2019-01-02 19:27:20,10
2019-01-02 19:27:25,10
2019-01-02 19:27:30,10
2019-01-02 19:27:35,10
2019-01-02 19:27:40,10
2019-01-02 19:27:45,10
2019-01-02 19:27:50,10
2019-01-02 19:27:55,10
2019-01-02 19:28:00,10
2019-01-02 19:28:05,10
2019-01-02 19:28:10,10
2019-01-02 19:28:15,10
2019-01-02 19:28:20,10
2019-01-02 19:28:25,10
2019-01-02 19:28:30,10

View file

@ -1,86 +1,5 @@
2019-01-02 19:14:06,10 2019-01-04 00:19:10,10
2019-01-02 19:14:11,10 2019-01-04 00:19:15,10
2019-01-02 19:14:16,10 2019-01-04 00:19:20,10
2019-01-02 19:14:21,10 2019-01-04 00:19:25,10
2019-01-02 19:14:26,10 2019-01-04 00:19:30,10
2019-01-02 19:14:32,10
2019-01-02 19:14:37,10
2019-01-02 19:14:42,10
2019-01-02 19:14:47,10
2019-01-02 19:14:52,10
2019-01-02 19:14:57,10
2019-01-02 19:15:02,10
2019-01-02 19:15:07,10
2019-01-02 19:15:12,10
2019-01-02 19:15:17,10
2019-01-02 19:15:22,10
2019-01-02 19:15:27,10
2019-01-02 19:15:32,10
2019-01-02 19:15:37,10
2019-01-02 19:15:42,10
2019-01-02 19:15:47,10
2019-01-02 19:15:52,10
2019-01-02 19:15:57,10
2019-01-02 19:16:02,10
2019-01-02 19:16:07,10
2019-01-02 19:16:12,10
2019-01-02 19:16:17,10
2019-01-02 19:16:22,10
2019-01-02 19:16:27,10
2019-01-02 19:16:32,10
2019-01-02 19:16:37,10
2019-01-02 19:16:42,10
2019-01-02 19:16:47,10
2019-01-02 19:16:52,10
2019-01-02 19:16:57,10
2019-01-02 19:17:02,10
2019-01-02 19:17:07,10
2019-01-02 19:17:12,10
2019-01-02 19:17:17,10
2019-01-02 19:17:22,10
2019-01-02 19:17:27,10
2019-01-02 19:17:32,10
2019-01-02 19:17:37,10
2019-01-02 19:17:42,10
2019-01-02 19:17:47,10
2019-01-02 19:17:52,10
2019-01-02 19:17:57,10
2019-01-02 19:18:02,10
2019-01-02 19:18:07,10
2019-01-02 19:18:12,10
2019-01-02 19:18:17,10
2019-01-02 19:18:22,10
2019-01-02 19:18:27,10
2019-01-02 19:18:32,10
2019-01-02 19:18:37,10
2019-01-02 19:18:42,10
2019-01-02 19:18:47,10
2019-01-02 19:18:52,10
2019-01-02 19:18:57,10
2019-01-02 19:19:02,10
2019-01-02 19:19:07,10
2019-01-02 19:19:12,10
2019-01-02 19:19:17,10
2019-01-02 19:19:22,10
2019-01-02 19:19:27,10
2019-01-02 19:19:32,10
2019-01-02 19:19:37,10
2019-01-02 19:19:42,10
2019-01-02 19:19:47,10
2019-01-02 19:19:52,10
2019-01-02 19:19:57,10
2019-01-02 19:20:02,10
2019-01-02 19:20:07,10
2019-01-02 19:20:12,10
2019-01-02 19:20:17,10
2019-01-02 19:20:22,10
2019-01-02 19:20:27,10
2019-01-02 19:20:32,10
2019-01-02 19:20:37,10
2019-01-02 19:20:42,10
2019-01-02 19:20:47,10
2019-01-02 19:20:52,10
2019-01-02 19:20:57,10
2019-01-02 19:21:02,10
2019-01-02 19:21:07,10
2019-01-02 19:21:12,10

View file

@ -1,86 +1,5 @@
2019-01-02 19:02:41,10 2019-01-04 00:19:10,10
2019-01-02 19:02:46,10 2019-01-04 00:19:15,10
2019-01-02 19:02:51,10 2019-01-04 00:19:20,10
2019-01-02 19:02:56,10 2019-01-04 00:19:25,10
2019-01-02 19:03:01,10 2019-01-04 00:19:30,10
2019-01-02 19:03:06,10
2019-01-02 19:03:11,10
2019-01-02 19:03:16,10
2019-01-02 19:03:21,10
2019-01-02 19:03:33,10
2019-01-02 19:03:33,10
2019-01-02 19:03:33,10
2019-01-02 19:03:33,10
2019-01-02 19:03:33,10
2019-01-02 19:03:33,10
2019-01-02 19:03:33,10
2019-01-02 19:03:33,10
2019-01-02 19:03:33,10
2019-01-02 19:03:33,10
2019-01-02 19:03:33,10
2019-01-02 19:08:33,10
2019-01-02 19:08:38,10
2019-01-02 19:08:43,10
2019-01-02 19:08:48,10
2019-01-02 19:08:53,10
2019-01-02 19:08:58,10
2019-01-02 19:09:03,10
2019-01-02 19:09:08,10
2019-01-02 19:09:16,10
2019-01-02 19:09:21,10
2019-01-02 19:09:26,10
2019-01-02 19:09:31,10
2019-01-02 19:09:36,10
2019-01-02 19:09:41,10
2019-01-02 19:09:46,10
2019-01-02 19:09:51,10
2019-01-02 19:09:56,10
2019-01-02 19:10:01,10
2019-01-02 19:10:06,10
2019-01-02 19:10:11,10
2019-01-02 19:10:16,10
2019-01-02 19:10:21,10
2019-01-02 19:10:26,10
2019-01-02 19:10:31,10
2019-01-02 19:10:36,10
2019-01-02 19:10:41,10
2019-01-02 19:10:46,10
2019-01-02 19:10:51,10
2019-01-02 19:10:56,10
2019-01-02 19:11:01,10
2019-01-02 19:11:06,10
2019-01-02 19:11:11,10
2019-01-02 19:11:16,10
2019-01-02 19:11:21,10
2019-01-02 19:11:26,10
2019-01-02 19:11:31,10
2019-01-02 19:11:36,10
2019-01-02 19:11:41,10
2019-01-02 19:11:46,10
2019-01-02 19:11:51,10
2019-01-02 19:11:56,10
2019-01-02 19:12:01,10
2019-01-02 19:12:06,10
2019-01-02 19:12:11,10
2019-01-02 19:12:16,10
2019-01-02 19:12:21,10
2019-01-02 19:12:26,10
2019-01-02 19:12:31,10
2019-01-02 19:12:36,10
2019-01-02 19:12:41,10
2019-01-02 19:12:46,10
2019-01-02 19:12:51,10
2019-01-02 19:12:56,10
2019-01-02 19:13:01,10
2019-01-02 19:13:06,10
2019-01-02 19:13:11,10
2019-01-02 19:13:16,10
2019-01-02 19:13:21,10
2019-01-02 19:13:26,10
2019-01-02 19:13:31,10
2019-01-02 19:13:36,10
2019-01-02 19:13:41,10
2019-01-02 19:13:46,10
2019-01-02 19:13:51,10
2019-01-02 19:13:56,10
2019-01-02 19:14:01,10

View file

@ -1,86 +1,5 @@
2019-01-02 18:51:48,10 2019-01-04 00:19:10,10
2019-01-02 18:51:53,10 2019-01-04 00:19:15,10
2019-01-02 18:51:53,10 2019-01-04 00:19:20,10
2019-01-02 18:51:53,10 2019-01-04 00:19:25,10
2019-01-02 18:51:53,10 2019-01-04 00:19:30,10
2019-01-02 18:51:58,10
2019-01-02 18:52:03,10
2019-01-02 18:52:08,10
2019-01-02 18:52:13,10
2019-01-02 18:52:18,10
2019-01-02 18:52:23,10
2019-01-02 18:52:28,10
2019-01-02 18:52:33,10
2019-01-02 18:52:38,10
2019-01-02 18:52:43,10
2019-01-02 18:52:48,10
2019-01-02 18:52:53,10
2019-01-02 18:52:58,10
2019-01-02 18:53:03,10
2019-01-02 18:53:08,10
2019-01-02 18:53:13,10
2019-01-02 18:53:13,10
2019-01-02 18:53:13,10
2019-01-02 18:53:13,10
2019-01-02 18:53:13,10
2019-01-02 18:53:13,10
2019-01-02 18:53:13,10
2019-01-02 18:53:13,10
2019-01-02 18:53:13,10
2019-01-02 18:53:13,10
2019-01-02 18:53:13,10
2019-01-02 18:53:13,10
2019-01-02 18:53:22,10
2019-01-02 18:53:27,10
2019-01-02 18:53:32,10
2019-01-02 18:53:37,10
2019-01-02 18:53:42,10
2019-01-02 18:53:47,10
2019-01-02 18:53:52,10
2019-01-02 18:53:57,10
2019-01-02 18:54:02,10
2019-01-02 18:54:07,10
2019-01-02 18:54:12,10
2019-01-02 18:54:17,10
2019-01-02 18:54:22,10
2019-01-02 18:54:27,10
2019-01-02 18:54:32,10
2019-01-02 18:54:37,10
2019-01-02 18:54:42,10
2019-01-02 18:54:47,10
2019-01-02 18:54:52,10
2019-01-02 18:54:57,10
2019-01-02 18:55:02,10
2019-01-02 18:55:07,10
2019-01-02 18:55:12,10
2019-01-02 18:55:17,10
2019-01-02 18:55:22,10
2019-01-02 18:55:27,10
2019-01-02 18:55:32,10
2019-01-02 18:55:37,10
2019-01-02 18:55:42,10
2019-01-02 18:55:47,10
2019-01-02 18:55:52,10
2019-01-02 18:55:57,10
2019-01-02 18:56:02,10
2019-01-02 18:56:07,10
2019-01-02 18:56:12,10
2019-01-02 18:56:17,10
2019-01-02 18:56:22,10
2019-01-02 18:56:27,10
2019-01-02 18:56:32,10
2019-01-02 18:56:37,10
2019-01-02 18:56:42,10
2019-01-02 18:56:47,10
2019-01-02 18:56:52,10
2019-01-02 18:56:57,10
2019-01-02 18:57:02,10
2019-01-02 18:57:07,10
2019-01-02 18:57:12,10
2019-01-02 18:57:17,10
2019-01-02 18:57:22,10
2019-01-02 18:57:27,10
2019-01-02 18:57:32,10
2019-01-02 18:57:37,10
2019-01-02 19:02:31,10
2019-01-02 19:02:36,10

View file

@ -1,9 +1,5 @@
2019-01-02 18:51:43,10 2019-01-04 00:19:10,10
2019-01-02 18:51:53,10 2019-01-04 00:19:15,10
2019-01-02 18:51:53,10 2019-01-04 00:19:20,10
2019-01-02 18:51:53,10 2019-01-04 00:19:25,10
2019-01-02 18:51:53,10 2019-01-04 00:19:30,10
2019-01-02 18:51:53,10
2019-01-02 18:51:53,10
2019-01-02 18:51:53,10
2019-01-02 18:51:53,10

BIN
tests/craftbeerpi.db Normal file

Binary file not shown.

View file

@ -1,4 +1,5 @@
import aiohttp from unittest import mock
from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop
from core.craftbeerpi import CraftBeerPi from core.craftbeerpi import CraftBeerPi
@ -6,14 +7,21 @@ from core.craftbeerpi import CraftBeerPi
class ActorTestCase(AioHTTPTestCase): class ActorTestCase(AioHTTPTestCase):
async def get_application(self): async def get_application(self):
self.cbpi = CraftBeerPi() self.cbpi = CraftBeerPi()
await self.cbpi.init_serivces() await self.cbpi.init_serivces()
return self.cbpi.app return self.cbpi.app
@unittest_run_loop
async def test_actor_mock(self):
with mock.patch.object(self.cbpi.bus, 'fire', wraps=self.cbpi.bus.fire) as mock_obj:
# Send HTTP POST
resp = await self.client.request("POST", "/actor/1/on")
# Check Result
assert resp.status == 204
# Check if Event are fired
assert mock_obj.call_count == 2
@unittest_run_loop @unittest_run_loop
async def test_actor_switch(self): async def test_actor_switch(self):
@ -92,3 +100,8 @@ class ActorTestCase(AioHTTPTestCase):
# Update not existing actor # Update not existing actor
resp = await self.client.put(path="/actor/%s" % 9999, json=data) resp = await self.client.put(path="/actor/%s" % 9999, json=data)
assert resp.status == 500 assert resp.status == 500
@unittest_run_loop
async def test_actor_action(self):
resp = await self.client.post(path="/actor/1/action", json=dict(name="myAction", parameter=dict(name="Manuel")))
assert resp.status == 204

49
tests/test_gpio.py Normal file
View file

@ -0,0 +1,49 @@
import unittest
from unittest import mock
from unittest.mock import MagicMock, patch
try:
import RPi.GPIO as GPIO
except Exception:
print("Error importing RPi.GPIO!")
MockRPi = MagicMock()
modules = {
"RPi": MockRPi,
"RPi.GPIO": MockRPi.GPIO
}
patcher = patch.dict("sys.modules", modules)
patcher.start()
import RPi.GPIO as GPIO
class HelloWorld(object):
def test(self, a):
return a
class TestSwitch(unittest.TestCase):
GPIO_NUM = 22
@patch("RPi.GPIO.setup")
def test_switch_inits_gpio(self, patched_setup):
GPIO.setmode(GPIO.BCM)
GPIO.setup(self.GPIO_NUM, GPIO.OUT)
patched_setup.assert_called_once_with(self.GPIO_NUM, GPIO.OUT)
@patch("RPi.GPIO.output")
def test_switch_without_scheduler_starts_disabled(self, patched_output):
GPIO.output(self.GPIO_NUM, GPIO.LOW)
patched_output.assert_called_once_with(self.GPIO_NUM, GPIO.LOW)
def test_hello_world(self):
h = HelloWorld()
with mock.patch.object(HelloWorld, 'test', wraps=h.test) as fake_increment:
#print(h.test("HALLO"))
print(h.test("ABC"))
print(fake_increment.call_args)
print(h.test("HALLO"))
print(fake_increment.call_args_list)

View file

@ -18,6 +18,12 @@ class IndexTestCase(AioHTTPTestCase):
resp = await self.client.get(path="/") resp = await self.client.get(path="/")
assert resp.status == 200 assert resp.status == 200
@unittest_run_loop
async def test_404(self):
# Test Index Page
resp = await self.client.get(path="/abc")
assert resp.status == 200
@unittest_run_loop @unittest_run_loop
async def test_wrong_login(self): async def test_wrong_login(self):
resp = await self.client.post(path="/login", data={"username": "beer", "password": "123"}) resp = await self.client.post(path="/login", data={"username": "beer", "password": "123"})

View file

@ -1,4 +1,6 @@
import asyncio import asyncio
from unittest import mock
from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop
from core.craftbeerpi import CraftBeerPi from core.craftbeerpi import CraftBeerPi
@ -51,20 +53,46 @@ class StepTestCase(AioHTTPTestCase):
resp = await self.client.delete(path="/step/%s" % sensor_id) resp = await self.client.delete(path="/step/%s" % sensor_id)
assert resp.status == 204 assert resp.status == 204
def create_wait_callback(self, topic):
future = self.cbpi.app.loop.create_future()
async def test(**kwargs):
print("GOON")
future.set_result("OK")
self.cbpi.bus.register(topic, test, once=True)
return future
async def wait(self, future):
done, pending = await asyncio.wait({future})
if future in done:
pass
@unittest_run_loop @unittest_run_loop
async def test_process(self): async def test_process(self):
resp = await self.client.request("GET", "/step/stop") await self.cbpi.step.stop()
assert resp.status == 204
resp = await self.client.request("GET", "/step/start") with mock.patch.object(self.cbpi.step, 'start', wraps=self.cbpi.step.start) as mock_obj:
assert resp.status == 204
resp = await self.client.request("GET", "/step/next") future = self.create_wait_callback("step/+/started")
assert resp.status == 204 await self.cbpi.step.start()
await self.wait(future)
resp = await self.client.request("GET", "/step/stop") future = self.create_wait_callback("step/+/started")
assert resp.status == 204 await self.cbpi.step.next()
await self.wait(future)
future = self.create_wait_callback("step/+/started")
await self.cbpi.step.next()
await self.wait(future)
future = self.create_wait_callback("step/+/started")
await self.cbpi.step.next()
await self.wait(future)
future = self.create_wait_callback("job/step/done")
await self.cbpi.step.stop()
await self.wait(future)
print("COUNT", mock_obj.call_count)
print("ARGS", mock_obj.call_args_list)

56
tests/test_ws.py Normal file
View file

@ -0,0 +1,56 @@
import asyncio
import aiohttp
from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop
from core.craftbeerpi import CraftBeerPi
class WebSocketTestCase(AioHTTPTestCase):
async def get_application(self):
self.cbpi = CraftBeerPi()
await self.cbpi.init_serivces()
return self.cbpi.app
@unittest_run_loop
async def test_brewing_process(self):
count_step_done = 0
async with self.client.ws_connect('/ws') as ws:
await ws.send_json(data=dict(topic="step/stop"))
await ws.send_json(data=dict(topic="step/start"))
async for msg in ws:
if msg.type == aiohttp.WSMsgType.TEXT:
try:
msg_obj = msg.json()
topic = msg_obj.get("topic")
if topic == "job/step/done":
count_step_done = count_step_done + 1
if topic == "step/brewing/finished":
await ws.send_json(data=dict(topic="close"))
except Exception as e:
print(e)
break
elif msg.type == aiohttp.WSMsgType.ERROR:
break
assert count_step_done == 4
@unittest_run_loop
async def test_wrong_format(self):
async with self.client.ws_connect('/ws') as ws:
await ws.send_json(data=dict(a="close"))
async for msg in ws:
print("MSG TYP", msg.type, msg.data)
if msg.type == aiohttp.WSMsgType.TEXT:
msg_obj = msg.json()
if msg_obj["topic"] != "connection/success":
print(msg.data)
raise Exception()
else:
raise Exception()