config fix

This commit is contained in:
manuel83 2019-01-28 22:21:31 +01:00
parent b1526fa247
commit 0496b04608
23 changed files with 881 additions and 599 deletions

File diff suppressed because it is too large Load diff

View file

@ -12,6 +12,8 @@ class CBPiSensor(CBPiExtension):
self.data_logger = None self.data_logger = None
self.state = False self.state = False
def get_parameter(self, name, default):
return self.cbpi.config.get(name, default)
def log_data(self, value): def log_data(self, value):
@ -27,7 +29,7 @@ class CBPiSensor(CBPiExtension):
self.data_logger = logging.getLogger('cbpi.sensor.%s' % self.id) self.data_logger = logging.getLogger('cbpi.sensor.%s' % self.id)
self.data_logger.propagate = False self.data_logger.propagate = False
self.data_logger.setLevel(logging.DEBUG) self.data_logger.setLevel(logging.DEBUG)
handler = RotatingFileHandler('./logs/sensors/sensor_%s.log' % self.id, maxBytes=2000, backupCount=10) handler = RotatingFileHandler('./logs/sensor_%s.log' % self.id, maxBytes=2000, backupCount=10)
self.data_logger.addHandler(handler) self.data_logger.addHandler(handler)
pass pass
@ -39,4 +41,7 @@ class CBPiSensor(CBPiExtension):
pass pass
def get_value(self): def get_value(self):
pass
def get_unit(self):
pass pass

View file

@ -8,12 +8,13 @@ class CBPiSimpleStep(metaclass=ABCMeta):
__dirty = False __dirty = False
managed_fields = [] managed_fields = []
_interval = 0.1 _interval = 1
_max_exceptions = 2 _max_exceptions = 2
_exception_count = 0 _exception_count = 0
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
print(kwargs)
for a in kwargs: for a in kwargs:
super(CBPiSimpleStep, self).__setattr__(a, kwargs.get(a)) super(CBPiSimpleStep, self).__setattr__(a, kwargs.get(a))
self.id = kwargs.get("id") self.id = kwargs.get("id")
@ -60,14 +61,15 @@ class CBPiSimpleStep(metaclass=ABCMeta):
await asyncio.sleep(self._interval) await asyncio.sleep(self._interval)
if self.is_dirty(): if self.is_dirty():
print("DIRTY")
# Now we have to store the managed props # Now we have to store the managed props
state = {} state = {}
for field in self.managed_fields: for field in self.managed_fields:
state[field] = self.__getattribute__(field) state[field] = self.__getattribute__(field)
#step_controller.model.update_step_state(step_controller.current_step.id, state)
await self.cbpi.step.model.update_step_state(self.id, state)
await self.cbpi.step.model.update_step_state(self.id, state)
await self.cbpi.bus.fire("step/update")
self.reset_dirty() self.reset_dirty()
@abstractmethod @abstractmethod

View file

@ -39,16 +39,19 @@ class ActorController(CRUDController):
try: try:
if actor.type in self.types: 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()
print(actor.id, self.cache[actor.id].instance)
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:
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: except Exception as e:
self.logger.error("Failed to init actor %s - Reason %s" % (actor.id, str(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):
@ -153,4 +156,5 @@ class ActorController(CRUDController):
await self._stop_actor(actor) await self._stop_actor(actor)
async def _post_update_callback(self, actor): async def _post_update_callback(self, actor):
await self._init_actor(actor) await self._init_actor(actor)

View file

@ -95,11 +95,11 @@ class CRUDController(metaclass=ABCMeta):
:return: :return:
''' '''
self.logger.debug("Update Sensor %s - %s " % (id, data))
id = int(id) id = int(id)
if id not in self.cache: if id not in self.cache:
self.logger.debug("Sensor %s Not in Cache" % (id,))
raise CBPiException("%s with id %s not found" % (self.name,id)) raise CBPiException("%s with id %s not found" % (self.name,id))
data["id"] = id data["id"] = id
@ -107,17 +107,18 @@ class CRUDController(metaclass=ABCMeta):
try: try:
### DELETE INSTANCE BEFORE UPDATE ### DELETE INSTANCE BEFORE UPDATE
del data["instance"] del data["instance"]
except: except Exception as e:
pass pass
if self.caching is True: if self.caching is True:
await self._pre_update_callback(self.cache[id]) await self._pre_update_callback(self.cache[id])
self.cache[id].__dict__.update(**data) self.cache[id].__dict__.update(**data)
m = await self.model.update(**self.cache[id].__dict__) m = self.cache[id] = await self.model.update(**self.cache[id].__dict__)
await self._post_update_callback(m) await self._post_update_callback(self.cache[id])
else:
m = await self.model.update(**data)
else:
m = await self.model.update(**data)
return m return m

View file

@ -53,11 +53,13 @@ class KettleController(CRUDController):
match = re.match("kettle_logic_(\d+)", key) match = re.match("kettle_logic_(\d+)", key)
if match is not None: if match is not None:
kid = match.group(1) kid = match.group(1)
await self.cbpi.bus.fire(topic="kettle/%s/logic/stop" % kid)
kettle = self.cache[int(kid)] kettle = self.cache[int(kid)]
kettle.instance = None kettle.instance = None
kettle.state = False kettle.state = False
print("FIRE")
await self.cbpi.bus.fire(topic="kettle/%s/logic/stop" % kid)
@on_event(topic="kettle/+/automatic") @on_event(topic="kettle/+/automatic")
async def handle_automtic_event(self, id, **kwargs): async def handle_automtic_event(self, id, **kwargs):
@ -89,7 +91,7 @@ class KettleController(CRUDController):
cfg.update(dict(cbpi=self.cbpi)) cfg.update(dict(cbpi=self.cbpi))
kettle.instance = clazz(**cfg) kettle.instance = clazz(**cfg)
await self.cbpi.job.start_job(kettle.instance.run(), "Kettle_logic_%s" % kettle.id, "kettle_logic%s" % id) await self.cbpi.job.start_job(kettle.instance.run(), "kettle_logic_%s" % kettle.id, "kettle_logic%s" % id)
kettle.state = True kettle.state = True
await self.cbpi.bus.fire(topic="kettle/%s/logic/start" % id) await self.cbpi.bus.fire(topic="kettle/%s/logic/start" % id)

View file

@ -42,13 +42,14 @@ class SensorController(CRUDController):
self.cache[sensor.id].instance.init() self.cache[sensor.id].instance.init()
scheduler = get_scheduler_from_app(self.cbpi.app) scheduler = get_scheduler_from_app(self.cbpi.app)
self.cache[sensor.id].instance.job = await scheduler.spawn(self.cache[sensor.id].instance.run(self.cbpi), sensor.name, "sensor") self.cache[sensor.id].instance.job = await scheduler.spawn(self.cache[sensor.id].instance.run(self.cbpi), sensor.name, "sensor")
await self.cbpi.bus.fire(topic="sensor/%s/initialized" % sensor.id, id=sensor.id)
else: else:
self.logger.error("Sensor type '%s' not found (Available Sensor Types: %s)" % (sensor.type, ', '.join(self.types.keys()))) self.logger.error("Sensor type '%s' not found (Available Sensor Types: %s)" % (sensor.type, ', '.join(self.types.keys())))
async def stop_sensor(self, sensor): async def stop_sensor(self, sensor):
print("STOP", sensor.id)
sensor.instance.stop() sensor.instance.stop()
await self.cbpi.bus.fire(topic="sensor/%s/stopped" % sensor.id, id=sensor.id) await self.cbpi.bus.fire(topic="sensor/%s/stopped" % sensor.id, id=sensor.id)
@ -78,4 +79,4 @@ class SensorController(CRUDController):
await self.stop_sensor(sensor) await self.stop_sensor(sensor)
async def _post_update_callback(self, sensor): async def _post_update_callback(self, sensor):
self.init_sensor(sensor) await self.init_sensor(sensor)

View file

@ -154,8 +154,12 @@ class StepController(CRUDController):
if next_step is not None: if next_step is not None:
step_type = self.types[next_step.type] step_type = self.types[next_step.type]
print(step_type)
managed_fields = self._get_manged_fields_as_array(step_type)
config = dict(cbpi=self.cbpi, id=next_step.id, name=next_step.name, managed_fields=managed_fields)
config.update(**next_step.config)
self._set_default(step_type, config, managed_fields)
config = dict(cbpi=self.cbpi, id=next_step.id, name=next_step.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)
next_step.state = 'A' next_step.state = 'A'
@ -176,6 +180,12 @@ class StepController(CRUDController):
self.logger.error("Process Already Running") self.logger.error("Process Already Running")
print("----------- END") print("----------- END")
def _set_default(self, step_type, config, managed_fields):
for key in managed_fields:
if key not in config:
config[key] = None
@on_event("step/stop") @on_event("step/stop")
async def stop(self, **kwargs): async def stop(self, **kwargs):
@ -204,7 +214,13 @@ class StepController(CRUDController):
await self.model.reset_all_steps() await self.model.reset_all_steps()
async def sort(self, data): @on_event("step/clear")
async def clear_all(self, **kwargs):
await self.model.delete_all()
self.cbpi.notify(key="Steps Cleared", message="Steps cleared successfully", type="success")
@on_event("step/sort")
async def sort(self, topic, data, **kwargs):
await self.model.sort(data) await self.model.sort(data)
async def _pre_add_callback(self, data): async def _pre_add_callback(self, data):

View file

@ -1,5 +1,8 @@
import datetime import datetime
import re
from aiohttp import web from aiohttp import web
import os
from aiojobs.aiohttp import get_scheduler_from_app from aiojobs.aiohttp import get_scheduler_from_app
from cbpi.api import * from cbpi.api import *
@ -49,7 +52,7 @@ class SystemController():
return web.Response(text="NOT IMPLEMENTED") return web.Response(text="NOT IMPLEMENTED")
@request_mapping("/shutdown", method="POST", name="ShutdownSerer", auth_required=False) @request_mapping("/shutdown", method="POST", name="ShutdownSerer", auth_required=False)
def restart(self, request): def shutdown(self, request):
""" """
--- ---
description: Shutdown System - Not implemented description: Shutdown System - Not implemented
@ -94,3 +97,50 @@ class SystemController():
""" """
return web.json_response(data=self.cbpi.bus.dump()) return web.json_response(data=self.cbpi.bus.dump())
@request_mapping(path="/logs", auth_required=False)
async def http_get_log(self, request):
result = []
file_pattern = re.compile("^(\w+.).log(.?\d*)")
for filename in sorted(os.listdir("./logs"), reverse=True):#
if file_pattern.match(filename):
result.append(filename)
return web.json_response(result)
@request_mapping(path="/logs/{name}", method="DELETE", auth_required=False)
async def http_delete_log(self, request):
log_name = request.match_info['name']
file_patter = re.compile("^(\w+.).log(.?\d*)")
file_sensor_log = re.compile("^sensor_(\d).log(.?\d*)")
if file_patter.match(log_name):
pass
@request_mapping(path="/logs", method="DELETE", auth_required=False)
async def http_delete_logs(self, request):
sensor_log_pattern = re.compile("sensor_([\d]).log$")
sensor_log_pattern2 = re.compile("sensor_([\d]).log.[\d]*$")
app_log_pattern = re.compile("app.log$")
for filename in sorted(os.listdir("./logs"), reverse=True):#
if app_log_pattern.match(filename):
with open(os.path.join("./logs/%s" % filename), 'w'):
pass
continue
for filename in sorted(os.listdir("./logs/sensors"), reverse=True):
if sensor_log_pattern.match(filename):
with open(os.path.join("./logs/sensors/%s" % filename), 'w'):
pass
continue
elif sensor_log_pattern2.match(filename):
os.remove(os.path.join("./logs/sensors/%s" % filename))
return web.Response(status=204)

View file

@ -38,6 +38,7 @@ class ConfigModel(DBModel):
__table_name__ = "config" __table_name__ = "config"
__json_fields__ = ["options"] __json_fields__ = ["options"]
__priamry_key__ = "name" __priamry_key__ = "name"
__order_by__ = "name"
class KettleModel(DBModel): class KettleModel(DBModel):
@ -53,6 +54,7 @@ class StepModel(DBModel):
@classmethod @classmethod
async def update_step_state(cls, step_id, state): async def update_step_state(cls, step_id, state):
print("NOW UPDATE", state)
async with aiosqlite.connect(DATABASE_FILE) as db: async with aiosqlite.connect(DATABASE_FILE) as db:
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()

View file

@ -95,6 +95,12 @@ class DBModel(object):
await db.execute("DELETE FROM %s WHERE %s = ? " % (cls.__table_name__, cls.__priamry_key__), (id,)) await db.execute("DELETE FROM %s WHERE %s = ? " % (cls.__table_name__, cls.__priamry_key__), (id,))
await db.commit() await db.commit()
@classmethod
async def delete_all(cls):
async with aiosqlite.connect(DATABASE_FILE) as db:
await db.execute("DELETE FROM %s" % cls.__table_name__)
await db.commit()
@classmethod @classmethod
async def insert(cls, **kwargs): async def insert(cls, **kwargs):

View file

@ -54,15 +54,15 @@ class CustomLogic(CBPiKettleLogic):
result = await self.wait_for_event("sensor/1/data", callback=my_callback) result = await self.wait_for_event("sensor/1/data", callback=my_callback)
''' '''
value = 0
while self.running: while self.running:
print("RUN", self.test) value = value + 1
value = await self.cbpi.sensor.get_value(1)
print(value) print(value)
if value >= 10: if value >= 10:
break break
await asyncio.sleep(1) await asyncio.sleep(1)

View file

@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
import asyncio import asyncio
from aiohttp import web from aiohttp import web
from cbpi.api import * from cbpi.api import *
@ -29,10 +30,15 @@ class CustomSensor(CBPiSensor):
def get_state(self): def get_state(self):
return self.state return self.state
def get_value(self): def get_value(self):
return self.value return self.value
def get_unit(self):
return "°%s" % self.get_parameter("TEMP_UNIT", "C")
def stop(self): def stop(self):
pass pass
@ -40,9 +46,8 @@ class CustomSensor(CBPiSensor):
self.value = 0 self.value = 0
while True: while True:
await asyncio.sleep(self.interval) await asyncio.sleep(self.interval)
self.log_data(10)
self.value = self.value + 1 self.value = self.value + 1
self.log_data(self.value)
await cbpi.bus.fire("sensor/%s/data" % self.id, value=self.value) await cbpi.bus.fire("sensor/%s/data" % self.id, value=self.value)
cache = {} cache = {}

View file

@ -1,26 +1,31 @@
import asyncio import asyncio
import time
from cbpi.api import * from cbpi.api import *
class CustomStepCBPi(CBPiSimpleStep): class CustomStepCBPi(CBPiSimpleStep):
name = Property.Number(label="Test") name1 = Property.Number(label="Test", configurable=True)
timer_end = Property.Number(label="Test", default_value=None)
temp = Property.Number(label="Temperature", default_value=50, configurable=True)
i = 0 i = 0
@action(key="name", parameters=None) @action(key="name", parameters=None)
def test(self, **kwargs): def test(self, **kwargs):
self.name="WOOHOO" self.name="WOOHOO"
async def run_cycle(self): async def run_cycle(self):
print("RUN", self.name) print("RUN", self.name1, self.managed_fields, self.timer_end)
self.i = self.i + 1 self.i = self.i + 1
await asyncio.sleep(5)
print("WAIT") if self.timer_end is None:
self.next() self.timer_end = time.time() + 10
#if self.i == 5:
# print("NEXT") if self.i == 10:
self.next()
#self.cbpi.notify(key="step", message="HELLO FROM STEP") #self.cbpi.notify(key="step", message="HELLO FROM STEP")

View file

@ -32,8 +32,6 @@ class HttpCrudEndpoints():
@request_mapping(path="/", method="POST", auth_required=False) @request_mapping(path="/", method="POST", auth_required=False)
async def http_add(self, request): async def http_add(self, request):
data = await request.json() data = await request.json()
obj = await self.controller.add(**data) obj = await self.controller.add(**data)
return web.json_response(obj, dumps=json_dumps) return web.json_response(obj, dumps=json_dumps)
@ -42,8 +40,7 @@ class HttpCrudEndpoints():
id = int(request.match_info['id']) id = int(request.match_info['id'])
data = await request.json() data = await request.json()
obj = await self.controller.update(id, data) obj = await self.controller.update(id, data)
return web.json_response(obj, dumps=json_dumps)
return web.json_response(await self.controller.get_one(id), dumps=json_dumps)
@request_mapping(path="/{id}", method="DELETE", auth_required=False) @request_mapping(path="/{id}", method="DELETE", auth_required=False)
async def http_delete_one(self, request): async def http_delete_one(self, request):

View file

@ -144,6 +144,9 @@ class SensorHttpEndpoints(HttpCrudEndpoints):
""" """
return await super().http_delete_one(request) return await super().http_delete_one(request)
@request_mapping(path="/{id:\d+}/log", auth_required=False) @request_mapping(path="/{id:\d+}/log", auth_required=False)
async def http_get_log(self, request): async def http_get_log(self, request):
sensor_id = request.match_info['id'] sensor_id = request.match_info['id']
@ -162,7 +165,7 @@ class SensorHttpEndpoints(HttpCrudEndpoints):
sensor_id = request.match_info['id'] sensor_id = request.match_info['id']
for filename in sorted(os.listdir("./logs/sensors"), reverse=True): for filename in sorted(os.listdir("./logs/sensors"), reverse=True):
print(filename)
if filename == "sensor_%s.log" % sensor_id: if filename == "sensor_%s.log" % sensor_id:
with open(os.path.join("./logs/sensors/%s" % filename), 'w'): with open(os.path.join("./logs/sensors/%s" % filename), 'w'):
pass pass
@ -170,4 +173,39 @@ class SensorHttpEndpoints(HttpCrudEndpoints):
if filename.startswith("sensor_%s" % sensor_id): if filename.startswith("sensor_%s" % sensor_id):
os.remove(os.path.join("./logs/sensors/%s" % filename)) os.remove(os.path.join("./logs/sensors/%s" % filename))
return web.Response(status=204)
@request_mapping(path="/{id:\d+}/action", method="POST", auth_required=False)
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
"""
sensor_id = int(request.match_info['id'])
await self.cbpi.bus.fire(topic="sensor/%s/action" % sensor_id, sensor_id=sensor_id, data=await request.json())
return web.Response(status=204) return web.Response(status=204)

View file

@ -162,7 +162,8 @@ class StepHttpEndpoints(HttpCrudEndpoints):
"204": "204":
description: successful operation description: successful operation
""" """
self.cbpi.notify(key="step_delete_all", message="NOT IMPLEMENTE", type="danger")
await self.cbpi.bus.fire("step/clear")
return web.Response(status=204) return web.Response(status=204)
@ -243,5 +244,5 @@ class StepHttpEndpoints(HttpCrudEndpoints):
@request_mapping(path="/sort", method="POST", auth_required=False) @request_mapping(path="/sort", method="POST", auth_required=False)
async def http_sort(self, request): async def http_sort(self, request):
data = await request.json() data = await request.json()
await self.cbpi.step.sort(data) await self.cbpi.bus.fire("step/sort", data=data)
return web.Response(status=204) return web.Response(status=204)

View file

@ -23,6 +23,7 @@ class ComplexEncoder(JSONEncoder):
elif isinstance(obj, SensorModel): elif isinstance(obj, SensorModel):
data = dict(**obj.__dict__) data = dict(**obj.__dict__)
data["value"] = value=obj.instance.get_value() data["value"] = value=obj.instance.get_value()
data["unit"] = value = obj.instance.get_unit()
data["state"] = obj.instance.get_state() data["state"] = obj.instance.get_state()
del data["instance"] del data["instance"]
return data return data
@ -35,6 +36,6 @@ class ComplexEncoder(JSONEncoder):
else: else:
raise TypeError() raise TypeError()
except Exception as e: except Exception as e:
print(e)
pass pass
return None return None

Binary file not shown.

View file

@ -46,7 +46,7 @@ class ConfigTestCase(AioHTTPTestCase):
await self.cbpi.config.set(key, value) await self.cbpi.config.set(key, value)
assert self.cbpi.config.get(key, 1) == value assert self.cbpi.config.get(key, 1) == value
resp = await self.client.request("POST", "/config/%s/" % key, json={'value': '1'}) resp = await self.client.request("PUT", "/config/%s/" % key, json={'value': '1'})
assert resp.status == 204 assert resp.status == 204
assert self.cbpi.config.get(key, -1) == "1" assert self.cbpi.config.get(key, -1) == "1"

View file

@ -60,7 +60,8 @@ class DashboardTestCase(AioHTTPTestCase):
resp = await self.client.get(path="/dashboard/%s/content" % (dashboard_id)) resp = await self.client.get(path="/dashboard/%s/content" % (dashboard_id))
assert resp.status == 200 assert resp.status == 200
resp = await self.client.put(path="/dashboard/%s/content/%s/move" % (dashboard_id, content_id), json=dict(x=1,y=1))
resp = await self.client.post(path="/dashboard/%s/content/%s/move" % (dashboard_id, content_id), json=dict(x=1,y=1))
assert resp.status == 200 assert resp.status == 200
resp = await self.client.delete(path="/dashboard/%s/content/%s" % (dashboard_id, content_id)) resp = await self.client.delete(path="/dashboard/%s/content/%s" % (dashboard_id, content_id))

View file

@ -22,7 +22,7 @@ class IndexTestCase(AioHTTPTestCase):
async def test_404(self): async def test_404(self):
# Test Index Page # Test Index Page
resp = await self.client.get(path="/abc") resp = await self.client.get(path="/abc")
assert resp.status == 200 assert resp.status == 500
@unittest_run_loop @unittest_run_loop
async def test_wrong_login(self): async def test_wrong_login(self):

View file

@ -37,14 +37,14 @@ class KettleTestCase(AioHTTPTestCase):
@unittest_run_loop @unittest_run_loop
async def test_temp(self): async def test_temp(self):
resp = await self.client.get("/kettle/1/temp") resp = await self.client.get("/kettle/1/temp")
assert resp.status == 200 assert resp.status == 204
resp = await self.client.get("/kettle/1/targettemp") resp = await self.client.get("/kettle/1/targettemp")
assert resp.status == 200 assert resp.status == 200
@unittest_run_loop @unittest_run_loop
async def test_automatic(self): async def test_automatic(self):
resp = await self.client.get("/kettle/1/automatic") resp = await self.client.post("/kettle/1/automatic")
assert resp.status == 204 assert resp.status == 204