Update webgui

parent 60ed8f6c
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
], ],
"start_url": "/gui/", "start_url": "/gui/",
"display": "fullscreen", "display": "standalone",
"orientation": "portrait", "orientation": "portrait",
"developer": { "developer": {
"name": "Franco (nextime) Lanza", "name": "Franco (nextime) Lanza",
......
...@@ -37,6 +37,9 @@ body { ...@@ -37,6 +37,9 @@ body {
z-index: 10000; z-index: 10000;
overflow: hidden; overflow: hidden;
background-color: #333; background-color: #333;
position: fixed;
top: 0;
width:100%;
} }
/* Navbar links */ /* Navbar links */
...@@ -55,19 +58,12 @@ body { ...@@ -55,19 +58,12 @@ body {
} }
/* The sticky class is added to the navbar with JS when it reaches its scroll position */ #content::before {
.sticky { content: "";
position: fixed; display: block;
top: 0; height: 40px;
width: 100%
}
/* Add some top padding to the page content to prevent sudden quick movement (as the navigation bar gets a new position at the top of the page (position:fixed and top:0) */
.sticky + .content {
padding-top: 60px;
} }
/* The side navigation menu */ /* The side navigation menu */
.sidenav { .sidenav {
height: 100%; /* 100% Full-height */ height: 100%; /* 100% Full-height */
...@@ -107,10 +103,19 @@ body { ...@@ -107,10 +103,19 @@ body {
.column { .column {
float: left; float: left;
width: 33.33%; width: 33%;
overflow-y: scroll; }
height: 400px;
/* border: 1px solid white; */ .coltitle {
background-color: #4a66ce;
color: white;
text-align: center;
padding: 6px;
padding-top: 14px;
padding-bottom: 14px;
margin: 4px;
border-radius: 10px;
} }
/* Clear floats after the columns */ /* Clear floats after the columns */
...@@ -189,8 +194,135 @@ div .menubtn div { ...@@ -189,8 +194,135 @@ div .menubtn div {
background-color: #0d1f29; background-color: #0d1f29;
} }
.button {
background-color: #3e3c3c;
border-radius: 10px;
margin: 4px;
padding: 10px;
padding-top: 14px;
padding-bottom: 14px;
}
.btnname {
color: #bbbbbb;
float: left;
}
.btnact {
float: right;
}
.button::after {
content: "";
display: table;
clear: both;
}
.onoffswitch {
position: relative; width: 110px;
-webkit-user-select:none;
-moz-user-select:none;
-ms-user-select: none;
}
.onoffswitch-checkbox {
display: none;
}
.onoffswitch-label {
display: block;
overflow: hidden;
cursor: pointer;
border: 2px solid #999999;
border-radius: 10px;
}
.onoffswitch-inner {
display: block;
width: 200%;
margin-left: -100%;
transition: margin 0.2s ease-in 0s;
}
.onoffswitch-inner-on, .onoffswitch-inner-off {
display: block;
float: left;
width: 50%;
height: 30px;
padding: 0;
line-height: 30px;
font-size: 14px;
color: white;
font-family: Trebuchet, Arial, sans-serif;
font-weight: bold;
box-sizing: border-box;
}
.onoffswitch-inner-on {
padding-left: 10px;
background-color: #38CDFF;
color: #FFFFFF;
}
.onoffswitch-inner-off {
padding-right: 10px;
background-color: #EEEEEE;
color: #999999;
text-align: right;
}
.onoffswitch-switch {
display: block;
width: 10px;
margin: 6px;
background: #cccccc;
position: absolute;
top: 0;
bottom: 0;
right: 84px;
border: 2px solid #999999;
border-radius: 8px;
transition: all 0.2s ease-in 0s;
}
.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-inner {
margin-left: 0;
}
.onoffswitch-checkbox:checked + .onoffswitch-label .onoffswitch-switch {
right: 0px;
}
@media (min-width: 601px) {
.column {
height: 400px;
overflow-y: auto;
}
}
@media (max-width: 1280px) {
.column {
width: 50%;
}
}
@media (max-width: 640px) {
.column {
width: 100%;
}
}
</style> </style>
<script language="javascript" src="/sockjs.min.js"></script>
<script language="javascript"> <script language="javascript">
var sock = new SockJS('/sockjs');
sock.onopen = function() {
console.log('open');
sock.send('test');
};
sock.onmessage = function(e) {
console.log('message', e.data);
};
sock.onclose = function() {
console.log('close');
};
sideSize = "400px"; sideSize = "400px";
if ( window.innerWidth <= parseInt(sideSize)+200) { if ( window.innerWidth <= parseInt(sideSize)+200) {
sideSize = "100%"; sideSize = "100%";
...@@ -256,21 +388,16 @@ function CloseNav() { ...@@ -256,21 +388,16 @@ function CloseNav() {
</div> </div>
<div id="SidenavLeft" class="sidenav sidenavleft"> <div id="SidenavLeft" class="sidenav sidenavleft">
<a href="#">About</a> <a href="/gui/">Home</a>
<a href="#">Services</a> @PLUGINS@
<a href="#">Clients</a>
<a href="#">Contact</a>
</div> </div>
<div id="SidenavRight" class="sidenav sidenavright"> <div id="SidenavRight" class="sidenav sidenavright">
<div class="simplebtn"><a href="/__LOGOUT__/">Logout</a></div> <div class="simplebtn"><a href="/__LOGOUT__/">Logout</a></div>
</div> </div>
<div id="content" class="tblack" > <div id="content" class="tblack" >
<div class="row"> <div class="row">
<div class="column"></div> @CONTENT@
<div class="column"></div>
<div class="column"></div>
</div> </div>
@CONTENT@
</div> </div>
</body> </body>
</html> </html>
......
<div class=button>
<div class="btnname">
@BTNNAME@
</div>
<div class="btnact">
@BTNACT@
</div>
</div>
<div class="column">
<div class="coltitle">
<span><b>@COLTITLE@</b></span>
</div>
@COLCONTENT@
</div>
<div class="onoffswitch">
<input type="checkbox" name="@ID@" class="onoffswitch-checkbox" id="@ID@">
<label class="onoffswitch-label" for="@ID@">
<span class="onoffswitch-inner">
<span class="onoffswitch-inner-on">@ON@</span>
<span class="onoffswitch-inner-off">@OFF@</span>
</span>
<span class="onoffswitch-switch"></span>
</label>
</div>
File mode changed from 100644 to 100755
...@@ -33,6 +33,13 @@ class IModules(Interface): ...@@ -33,6 +33,13 @@ class IModules(Interface):
def initialize(self, callback, logger): def initialize(self, callback, logger):
""" initialize a plugin module passing a callback """ """ initialize a plugin module passing a callback """
def getWebHome(self):
""" get HTML for webgui homepage """
def getWebPage(self):
""" get HTML for plugin specific page """
def context2section(ctx): def context2section(ctx):
if int(ctx) in C.SECTIONS.keys(): if int(ctx) in C.SECTIONS.keys():
section=C.SECTIONS[int(ctx)] section=C.SECTIONS[int(ctx)]
......
...@@ -42,7 +42,7 @@ from singleton import Singleton ...@@ -42,7 +42,7 @@ from singleton import Singleton
import pluggable import pluggable
import ikap import ikap
from web import web from web import web
from singleton.ajaxconnections import AJAXCONNREG
try: try:
...@@ -67,6 +67,7 @@ class penguidomService(service.Service): ...@@ -67,6 +67,7 @@ class penguidomService(service.Service):
initialized=False initialized=False
udp=False udp=False
tcp=False tcp=False
plugins={}
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
log.info('Initializing Core') log.info('Initializing Core')
...@@ -135,3 +136,17 @@ class penguidomService(service.Service): ...@@ -135,3 +136,17 @@ class penguidomService(service.Service):
cfg.readConfig() cfg.readConfig()
return cfg return cfg
def on_getPluginList(self):
return self.plugins.plugreg.keys()
def on_getPlugin(self, plugname):
if plugname in self.plugins.plugreg.keys():
return self.plugins.plugreg[plugname]
return None
def plugin_on_getWebParts(self):
return web.PageParts()
def web_on_test(self):
for c in AJAXCONNREG.connections.keys():
c.transport.write('CORE:test:OK')
...@@ -56,7 +56,7 @@ class PluginLogger(object): ...@@ -56,7 +56,7 @@ class PluginLogger(object):
self.name = plugname self.name = plugname
def _format(self, what): def _format(self, what):
return str(self.name)+": "+what return str(self.name)+": "+str(what)
def info(self, what): def info(self, what):
log.info(self._format(what)) log.info(self._format(what))
...@@ -84,8 +84,8 @@ class Loader(object): ...@@ -84,8 +84,8 @@ class Loader(object):
try: try:
if genutils.isTrue(self.core.configGet('plugins', name)): if genutils.isTrue(self.core.configGet('plugins', name)):
p = getPlugin(name, core) p = getPlugin(name, core)
self.plugreg[name] = p
p.initialize(ConvenienceCaller(lambda c: self._plugincback(name, c)), PluginLogger(name)) p.initialize(ConvenienceCaller(lambda c: self._plugincback(name, c)), PluginLogger(name))
self.plugreg[name] = p
except NoSectionError: except NoSectionError:
log.error("Trying to load "+name+" plugin but no value in config file plugins section") log.error("Trying to load "+name+" plugin but no value in config file plugins section")
......
...@@ -118,7 +118,11 @@ class ParadoxEventMap(Singleton): ...@@ -118,7 +118,11 @@ class ParadoxEventMap(Singleton):
def setsiteNameLabel(self, number, value): def setsiteNameLabel(self, number, value):
self.eventMap._siteNameLabel.update({number: value}) self.eventMap._siteNameLabel.update({number: value})
return return
def getsiteNameLabel(self):
self.eventMap._siteNameLabel
def setwirelessSirenLabel(self, number, value): def setwirelessSirenLabel(self, number, value):
self.eventMap._wirelessSiren.update({number: value}) self.eventMap._wirelessSiren.update({number: value})
return return
......
...@@ -56,6 +56,7 @@ class ParadoxProtocol(BaseProtocol): ...@@ -56,6 +56,7 @@ class ParadoxProtocol(BaseProtocol):
packettimeout = 0 packettimeout = 0
proxy = False proxy = False
zonemap = False zonemap = False
boardname = ''
def __init__(self, logger, core, cfg): def __init__(self, logger, core, cfg):
self.log = logger self.log = logger
...@@ -208,6 +209,7 @@ class ParadoxProtocol(BaseProtocol): ...@@ -208,6 +209,7 @@ class ParadoxProtocol(BaseProtocol):
def _detectBoard(self, reply, cbargs=None): def _detectBoard(self, reply, cbargs=None):
board = p37b.getPanelName(reply) board = p37b.getPanelName(reply)
self.boardname = board
self.log.info('Detected Panel Board as '+board) self.log.info('Detected Panel Board as '+board)
if self.autodetect: if self.autodetect:
try: try:
...@@ -357,4 +359,27 @@ class Paradox(object): ...@@ -357,4 +359,27 @@ class Paradox(object):
logger.info("Plugin initialized") logger.info("Plugin initialized")
def getWebHome(self):
webparts = self.core.getWebParts()
labelreg = REGISTERS.getsiteNameLabelRegister()
try:
if labelreg.items()[0][1][0] is not None:
colname = str(labelreg.items()[0][1][0])
else:
colname = " ".join(['Paradox', self.serproto.boardname])
except:
colname = 'paradox'
partitions = EVENTMAP.getAllpartitionLabel()
#self.log.info(EVENTMAP.getsiteNameLabel())
colcont = ''
for partition in partitions.items():
partname = partition[1]
partidx = partition[0]
colcont+=webparts.getButton(title=str(partname), act=webparts.getOnOffSwitch("".join(['paradox_partition_', str(partidx)]), on='ARMED', off='DISARMED'))
return webparts.getColumn(title=colname, content=colcont)
def getWebPage(self):
return REGISTERS.getsiteNameLabelRegister()
...@@ -29,6 +29,7 @@ from twisted.web import resource, static, server ...@@ -29,6 +29,7 @@ from twisted.web import resource, static, server
import time import time
from txsockjs.factory import SockJSResource, SockJSFactory from txsockjs.factory import SockJSResource, SockJSFactory
import dmjson as dmj import dmjson as dmj
from penguidom.singleton.ajaxconnections import AJAXCONNREG as CONNREG
log = logging.getLogger( 'Webgui' ) log = logging.getLogger( 'Webgui' )
...@@ -44,22 +45,18 @@ class AjaxProtocol(object): ...@@ -44,22 +45,18 @@ class AjaxProtocol(object):
f=getattr(self, 'on_command_'+message.split(":")[0], None) f=getattr(self, 'on_command_'+message.split(":")[0], None)
if f and callable(f): if f and callable(f):
log.debug("Received "+str(message.split(":")[0])+" command") log.debug("Received "+str(message.split(":")[0])+" command")
return f(message) f(message)
else: else:
self.messageSend(message, binary) self.messageSend(message, binary)
def _getIOStatus(self, d, ts): def on_command_test(self, data):
rs = self.factory.core.getRelays(ts) log.debug("TEST AJAX COMMAND "+str(data))
rs.addCallback(relstatus, False, d) if str(data) == 'test':
return rs.addCallback(dmj.jsonize_text) self.messageSend('test:OK')
splitted = data.split(':')
def on_command_getIOStatus(self, data): if len(splitted) > 1:
wts=data.split(":")[1].split("=")[1] if splitted[1] == 'broadcast':
ret=self.factory.core.getActionStatus() self.factory.core.test()
ts=int(time.time())-1
ist=self.factory.core.getInputs(wts)
ist.addCallback(inputstatus, ts, ret).addCallback(self._getIOStatus, wts)
return ist.addCallback(self.messageSend)
def connectionMade(self, who): def connectionMade(self, who):
log.debug('AJAX connectionMade called by '+str(who)) log.debug('AJAX connectionMade called by '+str(who))
...@@ -73,16 +70,18 @@ class AjaxProtocol(object): ...@@ -73,16 +70,18 @@ class AjaxProtocol(object):
class SockJSServerProtocolWrapper(protocol.Protocol, AjaxProtocol): class SockJSServerProtocolWrapper(protocol.Protocol, AjaxProtocol):
def dataReceived(self, data): def dataReceived(self, data):
log.debug("SockJS RECEIVED: "+str(data)) log.info("SockJS RECEIVED: "+str(data))
self.messageReceived(data, False) self.messageReceived(data, False)
def messageSend(self, msg, binary=False): def messageSend(self, msg, binary=False):
self.transport.write(msg) self.transport.write(msg)
def connectionMade(self): def connectionMade(self):
CONNREG.connections[self] = 1
AjaxProtocol.connectionMade(self, 'sockjs') AjaxProtocol.connectionMade(self, 'sockjs')
def connectionLost(self, reason): def connectionLost(self, reason):
del CONNREG.connections[self]
AjaxProtocol.connectionLost(self, 'sockjs', reason) AjaxProtocol.connectionLost(self, 'sockjs', reason)
......
...@@ -48,6 +48,8 @@ log = logging.getLogger( 'Webgui' ) ...@@ -48,6 +48,8 @@ log = logging.getLogger( 'Webgui' )
curdir=os.path.abspath(os.path.dirname(sys.argv[0])) curdir=os.path.abspath(os.path.dirname(sys.argv[0]))
LOGINFILE=os.path.normpath("/".join([curdir, 'Web/resources/login.html'])) LOGINFILE=os.path.normpath("/".join([curdir, 'Web/resources/login.html']))
GUIFILE=os.path.normpath("/".join([curdir, 'Web/resources/page.html'])) GUIFILE=os.path.normpath("/".join([curdir, 'Web/resources/page.html']))
GUIPARTSDIR=os.path.normpath("/".join([curdir, 'Web/resources/parts/']))
from common import uni, GzipRequest, StaticFile, codeOk, permissionDenied, RedirectToHome, neededPermission from common import uni, GzipRequest, StaticFile, codeOk, permissionDenied, RedirectToHome, neededPermission
import ajax import ajax
...@@ -130,6 +132,16 @@ class GuiPage(BasePage): ...@@ -130,6 +132,16 @@ class GuiPage(BasePage):
log.info(path) log.info(path)
html = self.html html = self.html
html = html.replace("@SCRIPT@", "") html = html.replace("@SCRIPT@", "")
plist = ''
phome = ''
for p in self.core.getPluginList():
plist+="".join(['<a href="/gui/plugin/', str(p), '">', str(p), '</a>\n'])
plug = self.core.getPlugin(p)
if plug:
log.debug("Plugin getWebHome for "+str(p)+" "+str(plug))
phome+=str(plug.getWebHome())
html = html.replace("@PLUGINS@", plist)
html = html.replace("@CONTENT@", phome)
return html return html
def childFactory(self, ctx, name): def childFactory(self, ctx, name):
...@@ -177,8 +189,8 @@ class RootPage(BasePage): ...@@ -177,8 +189,8 @@ class RootPage(BasePage):
if len(pl)>2: if len(pl)>2:
pname=pl[2] pname=pl[2]
pconf=os.path.normpath("/".join([curdir, 'plugins', pname, 'conf', pname+".conf" ])) pconf=os.path.normpath("/".join([curdir, 'plugins', pname, 'conf', pname+".conf" ]))
log.debug("trying to read "+str(dconf)) log.debug("trying to read "+str(pconf))
if os.path.isfile(dconf): if os.path.isfile(fconf):
try: try:
pcfg=configFile(pconf) pcfg=configFile(pconf)
pcfg.readConfig() pcfg.readConfig()
...@@ -530,3 +542,44 @@ def getAuthResource(core): ...@@ -530,3 +542,44 @@ def getAuthResource(core):
site.core = core site.core = core
return site return site
class PageParts(object):
parts = {}
def _getPartFile(self, part):
if part in self.parts.keys():
return self.parts[part]
partfile = ''.join([GUIPARTSDIR, '/', part, '.html'])
log.info(partfile)
if os.path.isfile(partfile):
try:
lf = open(partfile, "r")
html = lf.read()
lf.close()
self.parts[part] = html
return html
except:
pass
return ''
def getColumn(self, title="Title", content=""):
col = self._getPartFile('column')
col = col.replace('@COLTITLE@', title)
col = col.replace('@COLCONTENT@', content)
return col
def getButton(self, title="Button", act="Act"):
btn = self._getPartFile('button')
btn = btn.replace('@BTNNAME@', title)
btn = btn.replace('@BTNACT@', act)
return btn
def getOnOffSwitch(self, sid='myonoffswitch', on='ON', off='OFF'):
swh = self._getPartFile('onoffswitch')
swh = swh.replace('@ON@', on)
swh = swh.replace('@OFF@', off)
swh = swh.replace('@ID@' , sid)
return swh
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment