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.state = False
def get_parameter(self, name, default):
return self.cbpi.config.get(name, default)
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.propagate = False
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)
pass
@ -40,3 +42,6 @@ class CBPiSensor(CBPiExtension):
def get_value(self):
pass
def get_unit(self):
pass

View file

@ -8,12 +8,13 @@ class CBPiSimpleStep(metaclass=ABCMeta):
__dirty = False
managed_fields = []
_interval = 0.1
_interval = 1
_max_exceptions = 2
_exception_count = 0
def __init__(self, *args, **kwargs):
self.logger = logging.getLogger(__name__)
print(kwargs)
for a in kwargs:
super(CBPiSimpleStep, self).__setattr__(a, kwargs.get(a))
self.id = kwargs.get("id")
@ -60,14 +61,15 @@ class CBPiSimpleStep(metaclass=ABCMeta):
await asyncio.sleep(self._interval)
if self.is_dirty():
print("DIRTY")
# Now we have to store the managed props
state = {}
for field in self.managed_fields:
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()
@abstractmethod

View file

@ -39,16 +39,19 @@ class ActorController(CRUDController):
try:
if actor.type in self.types:
cfg = actor.config.copy()
cfg.update(dict(cbpi=self.cbpi, id=id, name=actor.name))
clazz = self.types[actor.type]["class"];
self.cache[actor.id].instance = clazz(**cfg)
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)
else:
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):
@ -153,4 +156,5 @@ class ActorController(CRUDController):
await self._stop_actor(actor)
async def _post_update_callback(self, actor):
await self._init_actor(actor)

View file

@ -95,11 +95,11 @@ class CRUDController(metaclass=ABCMeta):
:return:
'''
self.logger.debug("Update Sensor %s - %s " % (id, data))
id = int(id)
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))
data["id"] = id
@ -107,17 +107,18 @@ class CRUDController(metaclass=ABCMeta):
try:
### DELETE INSTANCE BEFORE UPDATE
del data["instance"]
except:
except Exception as e:
pass
if self.caching is True:
await self._pre_update_callback(self.cache[id])
self.cache[id].__dict__.update(**data)
m = await self.model.update(**self.cache[id].__dict__)
await self._post_update_callback(m)
else:
m = await self.model.update(**data)
m = self.cache[id] = await self.model.update(**self.cache[id].__dict__)
await self._post_update_callback(self.cache[id])
else:
m = await self.model.update(**data)
return m

View file

@ -53,11 +53,13 @@ class KettleController(CRUDController):
match = re.match("kettle_logic_(\d+)", key)
if match is not None:
kid = match.group(1)
await self.cbpi.bus.fire(topic="kettle/%s/logic/stop" % kid)
kettle = self.cache[int(kid)]
kettle.instance = None
kettle.state = False
print("FIRE")
await self.cbpi.bus.fire(topic="kettle/%s/logic/stop" % kid)
@on_event(topic="kettle/+/automatic")
async def handle_automtic_event(self, id, **kwargs):
@ -89,7 +91,7 @@ class KettleController(CRUDController):
cfg.update(dict(cbpi=self.cbpi))
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
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()
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")
await self.cbpi.bus.fire(topic="sensor/%s/initialized" % sensor.id, id=sensor.id)
else:
self.logger.error("Sensor type '%s' not found (Available Sensor Types: %s)" % (sensor.type, ', '.join(self.types.keys())))
async def stop_sensor(self, sensor):
print("STOP", sensor.id)
sensor.instance.stop()
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)
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:
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)
next_step.state = 'A'
@ -176,6 +180,12 @@ class StepController(CRUDController):
self.logger.error("Process Already Running")
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")
async def stop(self, **kwargs):
@ -204,7 +214,13 @@ class StepController(CRUDController):
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)
async def _pre_add_callback(self, data):

View file

@ -1,5 +1,8 @@
import datetime
import re
from aiohttp import web
import os
from aiojobs.aiohttp import get_scheduler_from_app
from cbpi.api import *
@ -49,7 +52,7 @@ class SystemController():
return web.Response(text="NOT IMPLEMENTED")
@request_mapping("/shutdown", method="POST", name="ShutdownSerer", auth_required=False)
def restart(self, request):
def shutdown(self, request):
"""
---
description: Shutdown System - Not implemented
@ -94,3 +97,50 @@ class SystemController():
"""
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"
__json_fields__ = ["options"]
__priamry_key__ = "name"
__order_by__ = "name"
class KettleModel(DBModel):
@ -53,6 +54,7 @@ class StepModel(DBModel):
@classmethod
async def update_step_state(cls, step_id, state):
print("NOW UPDATE", state)
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))
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.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
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)
'''
value = 0
while self.running:
print("RUN", self.test)
value = await self.cbpi.sensor.get_value(1)
value = value + 1
print(value)
if value >= 10:
break
await asyncio.sleep(1)

View file

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

View file

@ -1,26 +1,31 @@
import asyncio
import time
from cbpi.api import *
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
@action(key="name", parameters=None)
def test(self, **kwargs):
self.name="WOOHOO"
async def run_cycle(self):
print("RUN", self.name)
print("RUN", self.name1, self.managed_fields, self.timer_end)
self.i = self.i + 1
await asyncio.sleep(5)
print("WAIT")
self.next()
#if self.i == 5:
# print("NEXT")
if self.timer_end is None:
self.timer_end = time.time() + 10
if self.i == 10:
self.next()
#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)
async def http_add(self, request):
data = await request.json()
obj = await self.controller.add(**data)
return web.json_response(obj, dumps=json_dumps)
@ -42,8 +40,7 @@ class HttpCrudEndpoints():
id = int(request.match_info['id'])
data = await request.json()
obj = await self.controller.update(id, data)
return web.json_response(await self.controller.get_one(id), dumps=json_dumps)
return web.json_response(obj, dumps=json_dumps)
@request_mapping(path="/{id}", method="DELETE", auth_required=False)
async def http_delete_one(self, request):

View file

@ -144,6 +144,9 @@ class SensorHttpEndpoints(HttpCrudEndpoints):
"""
return await super().http_delete_one(request)
@request_mapping(path="/{id:\d+}/log", auth_required=False)
async def http_get_log(self, request):
sensor_id = request.match_info['id']
@ -162,7 +165,7 @@ class SensorHttpEndpoints(HttpCrudEndpoints):
sensor_id = request.match_info['id']
for filename in sorted(os.listdir("./logs/sensors"), reverse=True):
print(filename)
if filename == "sensor_%s.log" % sensor_id:
with open(os.path.join("./logs/sensors/%s" % filename), 'w'):
pass
@ -171,3 +174,38 @@ class SensorHttpEndpoints(HttpCrudEndpoints):
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)

View file

@ -162,7 +162,8 @@ class StepHttpEndpoints(HttpCrudEndpoints):
"204":
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)
@ -243,5 +244,5 @@ class StepHttpEndpoints(HttpCrudEndpoints):
@request_mapping(path="/sort", method="POST", auth_required=False)
async def http_sort(self, request):
data = await request.json()
await self.cbpi.step.sort(data)
await self.cbpi.bus.fire("step/sort", data=data)
return web.Response(status=204)

View file

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

Binary file not shown.

View file

@ -46,7 +46,7 @@ class ConfigTestCase(AioHTTPTestCase):
await self.cbpi.config.set(key, 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 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))
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
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):
# Test Index Page
resp = await self.client.get(path="/abc")
assert resp.status == 200
assert resp.status == 500
@unittest_run_loop
async def test_wrong_login(self):

View file

@ -37,14 +37,14 @@ class KettleTestCase(AioHTTPTestCase):
@unittest_run_loop
async def test_temp(self):
resp = await self.client.get("/kettle/1/temp")
assert resp.status == 200
assert resp.status == 204
resp = await self.client.get("/kettle/1/targettemp")
assert resp.status == 200
@unittest_run_loop
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