Skip to content
Browse files

New: Bottle AJAX web app

Added an Bottle ( AJAX web app to switch power outlets.

The bottle app is really helpful if you want to be able to
isolate the device from your other network hardware:
  * Have your router forward the TCP traffic to the
    'Telnet port' of the NETIO230A
  * Run the Bottle web app in your local network and make
    it available from outside (from the internet).
  * You may want to secure the access to the Bottle web app
    with a password or network ACL ;-)

A nice way to access the box from your mobile devices (Android, iPhone, etc.)
  • Loading branch information...
pklaus committed Apr 23, 2012
1 parent 27e6640 commit 2b848ffe5948d879d97c0fca63b8f3384d8c2d9a
BIN +481 Bytes resources/error.png
Binary file not shown.
BIN +3.19 KB resources/off.png
Binary file not shown.
BIN +3.33 KB resources/on.png
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,195 @@
<!DOCTYPE html>
<title>NETIO230A Status</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<link rel="shortcut icon" href="/static/netio230a_icon.png">
<!-- Let's include jQuery and jQuery UI ( and the CSS
from Google CDN: -->
<script src="" type="text/javascript"></script>
<!-- We include some Google Web Fonts ( -->
<link href='' rel='stylesheet' type='text/css'>
<!-- Now follows our custom CSS -->
<style type="text/css">
h1, h2, h3, h4, h5, h6, p, span { font-family: 'Salsa', cursive; color: white;}
h1 { margin: -4px 0 5px 0; }
a { color: yellow; }
a:hover { color: Gold; }
#netio230a {
text-align: center;
width: 80%;
min-width: 230px;
max-width: 270px;
margin: auto 20px;
padding: 20px;
-moz-border-radius: 15px;
border-radius: 15px;
/* Gradient from */
background-image: linear-gradient(bottom, rgb(140,12,16) 23%, rgb(168,39,42) 62%, rgb(202,67,72) 81%);
background-image: -o-linear-gradient(bottom, rgb(140,12,16) 23%, rgb(168,39,42) 62%, rgb(202,67,72) 81%);
background-image: -moz-linear-gradient(bottom, rgb(140,12,16) 23%, rgb(168,39,42) 62%, rgb(202,67,72) 81%);
background-image: -webkit-linear-gradient(bottom, rgb(140,12,16) 23%, rgb(168,39,42) 62%, rgb(202,67,72) 81%);
background-image: -ms-linear-gradient(bottom, rgb(140,12,16) 23%, rgb(168,39,42) 62%, rgb(202,67,72) 81%);
background-image: -webkit-gradient(
left bottom,
left top,
color-stop(0.23, rgb(140,12,16)),
color-stop(0.62, rgb(168,39,42)),
color-stop(0.81, rgb(202,67,72))
div#loading , div#error {
-moz-border-radius: 10px; border-radius: 10px;
padding: 5px 10px 5px 25px;
div#loading {
background: #CCF url(/static/updating.gif) no-repeat;
color: #300;
div#error {
display: none;
background: #A33 url(/static/error.png) no-repeat 10px 30%;
color: #CFF;
div#powersockets {
display: table;
width: 100%;
margin-top: 20px;
} {
height: 55px;
display: table-row;
padding: 10px;
} span {
text-align: left;
display: table-cell;
} span.pslabel {
width: 20%;
} span.psname {
width: 50%;
font-size: large;
color: #AFF;
} span.psstatus {
width: 30%;
#inputText {
padding: 3px 15px 3px 5px;
border: 3px solid #c7c7c7;
border-radius: 100px; -moz-border-radius: 100px; -webkit-border-radius: 100px;
font: italic 14px Georgia; color: #898989;
outline: none; /*Remove Chrome and Safari glows on focus*/
} {
border: 3px solid #abd2ff;
#about {
font-size: x-small;
} a {
background-image: url(/static/off.png);
background-repeat: no-repeat;
cursor: pointer;
border-radius: 10px; -moz-border-radius: 10px; -webkit-border-radius: 10px;
border-top: solid 2px #eaeaea;
border-left: solid 2px #eaeaea;
border-bottom: solid 2px #777;
border-right: solid 2px #777;
padding: 10px 10px 15px 50px;
} a.on {
background-image: url(/static/on.png);
border-top: solid 2px #777;
border-left: solid 2px #777;
border-bottom:solid 2px #eaeaea;
border-right: solid 2px #eaeaea;
<script type="text/javascript">
function showUpdatingStatus() {
$('#loading').text('Updating Status...');
function showProcessingRequest() {
$('#loading').text('Processing Request...');
function errorOccured(errorObj, message){
if (errorObj.status == 500)
$('#error').text("The server could not process the request. Please contact the system administrator.");
$('#error').text("An unknown error occured. Please contact the system administrator.");
function updateStatus(){
$.ajax( {
url: "/api/status",
dataType: 'json',
timeout: 4500,
success: function(data) {
document.title = data.device_alias;
$.each(data.power_sockets, function(i,item){
// $("<div/>", {text:}).appendTo("#powersockets");
$("<div/>", { id: 'ps'+i, class: 'ps'} ).appendTo("#powersockets");
$("<span/>", { class: 'pslabel', text: '#'+(i+1) }).appendTo("#ps"+i);
$("<span/>", { class: 'psname', text: }).appendTo("#ps"+i);
$("<span/>", { id: 'psstatus'+i, class: 'psstatus' }).appendTo("#ps"+i);
$("<a/>", { id: 'psbutton'+i, title: 'Click to switch '+(!item.power_on ? 'On' : 'Off'), text: (item.power_on ? 'On' : 'Off') }).appendTo("#psstatus"+i);
if (item.power_on) $('a#psbutton'+i).addClass('on');
if ($('#loading').is(":visible")) return;
var num = $(this).attr("id").slice(-1);
type: 'POST',
url: "/api/port",
dataType: 'json',
timeout: 4500,
data: { port: parseInt(num)+1,
power_on: !$(this).hasClass("on") },
success: function() {
error: errorOccured,
error: errorOccured,
} );
$(document).ready(function () {
<!-- <body onload="setInitialQRcode();"> -->
<div id="netio230a">
<h1 id="alias">NETIO 230A</h1>
<form action="javascript:updateQRcode()">
<input id="inputText" type="text" onchange="this.form.submit();" />
<div id="powersockets"> </div>
<div id="loading">Updating Status...</div>
<div id="error"></div>
<div id="infobox"></div>
<p id="about"> This is a <a title="Bottle is a fast, simple and lightweight WSGI micro web-framework for Python." href="">bottle</a> project by <a href="">pklaus</a> for <a href="">netio230a</a> hosted on <a href="">Github</a> </p>
@@ -0,0 +1,88 @@
#!/usr/bin/env python
# -*- encoding: UTF8 -*-

# Author: Philipp Klaus, philipp.l.klaus AT

# This file is part of netio230a.
# netio230a is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# netio230a is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with netio230a. If not, see <>.

## import the netio230a class:
import netio230a
from . import netio230a

host = ""
pw = "your choosen password"
tcp_port = 23

from bottle import Bottle, run, request, static_file, HTTPError

api = Bottle()

def getnetio():
netio = netio230a.netio230a(host, "admin", pw, True, tcp_port)
raise HTTPError(code=500, output='Could not communicate with the device')
return netio'/port')
def port():
port_num = int(request.forms.get('port'))
power_on = request.forms.get('power_on').lower() in ['true', '1', 'on']
netio = getnetio()
status = dict()
status['port'] = port_num
status['power_on'] = power_on
status['success'] = True
netio = None
return status

def status():
netio = getnetio()
status = dict()
status['version'] = netio.getFirmwareVersion()
power_sockets = []
for power_socket_object in netio.getAllPowerSockets():
power_sockets.append( {'power_on': power_socket_object.getPowerOn(), 'name': power_socket_object.getName()} )
status['power_sockets'] = power_sockets
status['device_alias'] = netio.getDeviceAlias()
status['system_discoverable'] = netio.getSystemDiscoverableUsingTool()
netio = None
return status

root = Bottle()
root.mount(api, '/api')

def static(path):
return static_file(path, root='./resources')

def index():
return static('webserver-ajax-template.html')

run( root, server='cherrypy', host="::", port=8080, debug=True)

0 comments on commit 2b848ff

Please sign in to comment.
You can’t perform that action at this time.