Skip to content
This repository has been archived by the owner on Nov 30, 2021. It is now read-only.

Commit

Permalink
Publish/subscribe implemented for config
Browse files Browse the repository at this point in the history
  • Loading branch information
p3tecracknell committed Apr 17, 2014
1 parent ba50185 commit f64331a
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 45 deletions.
1 change: 1 addition & 0 deletions configspec.ini
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ apikey=string(default='')
outputFormat=string(default='json')
cookieKey=string(default='CHANGEME')
installed=boolean(default=False)
scheduler=boolean(default=True)
[sensors]
[jobs]
29 changes: 21 additions & 8 deletions tellprox/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
from schedulerApi import SchedulerAPI

# Resources
from bottle import template, request
from configobj import ConfigObj
from bottle import template, request
from configObserver import ConfigObserver
from validate import Validator
from beaker.middleware import SessionMiddleware
from werkzeug.security import check_password_hash
Expand All @@ -28,28 +29,38 @@
CONFIG_PATH = 'config.ini'
CONFIG_SPEC = 'configspec.ini'

config = ConfigObj(CONFIG_PATH, configspec = CONFIG_SPEC)
validator = Validator()
config = ConfigObserver(CONFIG_PATH, configspec = CONFIG_SPEC)
config.setValidator(validator)

bottle.TEMPLATE_PATH.insert(0, utilities.full_path('/views'))
app = bottle.Bottle()
api = None

def main():
global api

root_app = bottle.Bottle()
validator = Validator()
result = config.validate(validator, copy = True)

if result is False:
print "Config file validation failed"
sys.exit(1)

api = API(app, config)
ConfigAPI(api, config)
tellstick = TellstickAPI(api, config)
ConfigAPI(api, config, validator)

# TODO enable/disable scheduler here?
scheduler = Scheduler(config, tellstick)
SchedulerAPI(api, config, scheduler)
SchedulerAPI(api, config)

class ConfigWatcher(object):
def notify(self, observable, key):
print "writing"
config.write()

watcher = ConfigWatcher()
config.observe(watcher)

if not config['installed']:
api.install()
Expand All @@ -74,8 +85,10 @@ def main():

if scheduler:
scheduler.stop()

# Write out default values

# The goal was to have all changes to the config call the watcher
# method which writes to file. Unfortunately subsections wouldn't
# play along, so write to file once we've finished with the service
config.write()

def authenticated(func):
Expand Down
2 changes: 0 additions & 2 deletions tellprox/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,9 @@ def output(self, func):
for k, v in self.allroutes.iteritems()}

def restart(self, func):
self.config.write()
return bh.restart()

def shutdown(self, func):
self.config.write()
return bh.shutdown()

def install(self, func=''):
Expand Down
6 changes: 3 additions & 3 deletions tellprox/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
from werkzeug.security import generate_password_hash

class ConfigAPI(object):
def __init__(self, api, config, validator):
def __init__(self, api, config):
self.api = api
self.config = config
self.validator = validator
#self.validator = validator
api.add_route('config', {
'getall': {
'fn': self.getall
Expand Down Expand Up @@ -43,5 +43,5 @@ def set(self, func, item, value):
value = generate_password_hash(value)

self.config[item] = value
result = self.config.validate(self.validator)
#result = self.config.validate(self.validator)
return bh.success_response()
51 changes: 51 additions & 0 deletions tellprox/configObserver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from UserDict import IterableUserDict
from configobj import ConfigObj

class ConfigObserver(ConfigObj):
def __init__(self, *args, **kw):
self.observers = []
self.observeKeys = {}

self.validator = None
self.validating = False
super(ConfigObserver, self).__init__(*args, **kw)

def setValidator(self, validator):
self.validator = validator

def observe(self, observer):
self.observers.append(observer)

def observeKey(self, key, observer):
if not key in self.observeKeys:
self.observeKeys[key] = []
self.observeKeys[key].append(observer)

def notify(self, key):
for o in self.observers:
o.notify(self, key)

def notifyKey(self, key):
self.notify(key)
if key in self.observeKeys:
for o in self.observeKeys[key]:
o.notify(self, key)

def __setitem__(self, key, value, unrepr = False):
currentValue = ''
if key in self:
currentValue = self[key]

ConfigObj.__setitem__(self, key, value, unrepr)
if currentValue == value: return

result = True
if self.validator:
if not self.validating:
self.validating = True
result = self.validate(self.validator, preserve_errors = True)

if result and self.validating:
self.notifyKey(key)
self.validating = False

25 changes: 19 additions & 6 deletions tellprox/scheduler.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,39 @@
from threading import Timer
from configObserver import ConfigObserver
import time, datetime
import bottle_helpers as bh

class Scheduler(object):
jobs = {}
nextJob = []
timer = None

def __init__(self, config, tellstick):
self.start(config)
self.config = config
self.jobs = config['jobs']
self.start()
self.tellstick = tellstick;

# Subscribe to changes in configs
class JobsWatcher(object):
def notify(subself, observable, key):
print "scheduler key changed"
self.start()

watcher = JobsWatcher()
config.observeKey('scheduler', watcher)
config.observeKey('jobs', watcher)

def stop(self):
if self.timer:
self.timer.cancel()
self.timer = None

def start(self, config):
self.jobs = config['jobs']
def start(self):
self.stop()

self.updateAndRunTimers()
self.calcAllRunTimes()
if self.config['scheduler']:
self.updateAndRunTimers()

def calcSoonestRunTime(self):
activeJobs = [
Expand All @@ -41,7 +54,6 @@ def getJobsByRunTime(self, searchTime):

def updateAndRunTimers(self):
nowInEpoch = bh.dateTimeToEpoch(datetime.datetime.now())
self.calcAllRunTimes()

if len(self.jobs) > 0:
soonestTime = self.calcSoonestRunTime()
Expand Down Expand Up @@ -69,6 +81,7 @@ def runCommand(self):
for job in self.nextJob:
self.runJob(job)

self.calcAllRunTimes()
self.updateAndRunTimers()

def calcAllRunTimes(self):
Expand Down
21 changes: 5 additions & 16 deletions tellprox/schedulerApi.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,11 @@
import bottle_helpers as bh

class SchedulerAPI(object):
config = None
core = td.TelldusCore()
jobs = {}
schedulerThread = None

def __init__(self, api, config, scheduler):
def __init__(self, api, config):
self.config = config

self.jobs = config['jobs']
self.schedulerThread = scheduler

api.add_route('scheduler', {
'joblist': {
Expand Down Expand Up @@ -93,14 +88,9 @@ def dateTimeToEpoch(self, timeObj):
def removejob(self, func, id):
if id in self.jobs:
del self.jobs[id]
self.restartSchedulerThread()
self.config.notifyKey('jobs')
return { "status" : "OK" }

def restartSchedulerThread(self):
if self.schedulerThread:
self.schedulerThread.stop()
self.schedulerThread.start(self.config)

def setjob(self, func, id, deviceId, method, methodValue, type, hour,
minute, offset, randomInterval, retries, retryInterval, reps, active, weekdays):

Expand Down Expand Up @@ -132,8 +122,7 @@ def setjob(self, func, id, deviceId, method, methodValue, type, hour,
}

self.jobs[id] = newJob
bh.calcNextRunTime(newJob)
self.config.notifyKey('jobs')
nextRunTime = self.jobs[id]['nextRunTime']

self.restartSchedulerThread()
self.config.write()
return { 'id' : id, 'nextRunTime': newJob['nextRunTime'] }
return { 'id' : id, 'nextRunTime': nextRunTime }
14 changes: 4 additions & 10 deletions tellprox/views/devices.tpl
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
<div id="deviceContainer" class="row"></div>
<div id="loading"></div>

<script type="text/x-template" class="itemCellTemplate2">
<div class="col-xs-12 col-sm-6 col-md-4 paletteContainer">
<div class="palette palette-peter-river dev-box"><h4 class="header">[DEVICE]</h4></div>
<div class="palette palette-belize-hole">
<div class="pagination">
<ul class="content" style="width: 100%">[CONTENT]</ul>
</div>
</div>
</div>
</script>
<script type="text/x-template" class="itemCellTemplate">
<div class="col-xs-12 col-sm-6 col-md-4 device-container">
<div class="device-box"><h4 class="header">[DEVICE]</h4></div>
Expand Down Expand Up @@ -128,6 +118,10 @@
$deviceContainer.text('Error: ' + data['error'] || 'Unknown error');
}
$('.dev-box').click(function() {
debugg;er
});
$('.evtBtn').click(onButtonClick);
$('#loading').hide()
Expand Down

0 comments on commit f64331a

Please sign in to comment.