Commit 3c815194 authored by JackV's avatar JackV

Remove unused folders/modules/files

parent 6b7cd5bd
Cos'è bits
==========
BITS è un indicatore in tempo reale dello stato della sede del
[POuL](http://www.poul.org). È stato progettato per sapere se c'è qualcuno
in sede senza dover andare a controllare di persona, consultando direttamente
il proprio portatile o smartphone.
L'architettura di BITS è composta da un Arduino, una Fonera, e un web server.
![](/static/bits1_small.jpg)
![](/static/bits2_small.jpg)
Il codice sorgente è scritto in Python e C++ ed è Free Software/Open Source,
disponibile su [GitHub](https://github.com/esseks/bitsd).
Stato della sede
================
Questa è la funzione principale del sito BITS: informare in tempo reale se la
sede è aperta o chiusa. Viene anche indicata la data e l'ora dell'ultima volta
in cui la sede è stata aperta/chiusa.
Non è necessario ricaricare la pagina per avere un informazione aggiornata,
tramite l'utilizzo di WebSocket la pagina si aggiorna da sola.
Previsione presenze
-------------------
La sede del POuL non ha orari prefissati, è aperta quando almeno uno dei suoi
membri decide di farci un salto. Di solito questo avviene in corrispondenza
delle ore buche tra una lezione e l'altra.
Poichè le ora buche sono uguali tutte le settimane all'interno di un semestre,
al sito è stata aggiunta una previsione della presenza di persone in sede,
basata sullo storico delle presenze nelle settimane passate.
Le previsioni sono rappresentate graficamente in una tabella che, per ogni ora
e giorno della settimana, mostra la probabilità che la sede sia aperta tramite
un colore (rosso=bassa probabilità, verde=alta probabilità).
![Grafico delle presenze](/bits_presence.png)
Temperatura in sede
-------------------
BITS è anche dotato di un sensore che registra la temperatura in sede.
Storico presenze
----------------
Le informazioni sulle presenze in sede sono salvate in un database, usato per
la previsione delle presenze ma anche [direttamente accessibile](/data).
\ No newline at end of file
include AUTHORS
include README.md
include COPYING
\ No newline at end of file
......@@ -18,18 +18,6 @@ This is also a valid Python file, but don't abuse that.
## Unix socket the web server will be listening on.
#web_usocket = ''
## Port for fonera server. Only if ws_usocket is not defined.
#control_local_port = 8888
## Unix socket the remote control will be listening on.
#control_local_usocket = ''
## The address the remote control will be bound to.
#control_local_address = "127.0.0.1"
## The address of the remote control unit (Fonera).
#control_remote_address = "127.0.0.1"
## UID to chown the unix sockets to
#usocket_uid = 1000
......
#
# Copyright (C) 2013 Stefano Sanfilippo
# Copyright (C) 2013 BITS development team
#
# This file is part of bitsd, which is released under the terms of
# GNU GPLv3. See COPYING at top level for more information.
#
"""
Client proxies. Submodules will handle
"""
\ No newline at end of file
#
# Copyright (C) 2013 Stefano Sanfilippo
# Copyright (C) 2013 BITS development team
#
# This file is part of bitsd, which is released under the terms of
# GNU GPLv3. See COPYING at top level for more information.
#
"""
Listeners are server components waiting for commands on given
ports/hosts/address or events (on the countrary, a server will actually
*serve* content to the client).
"""
from tornado.options import options
from .handlers import RemoteListener
from bitsd.common import bind, LOG
from . import hooks
def start():
"""Connect and bind listeners. **MUST** be called at startup."""
__inject_broadcast()
fonera = RemoteListener()
LOG.info('Starting remote control...')
LOG.info(
'My IP address is {}, remote IP address is {}'.format(
options.control_local_address,
options.control_remote_address
)
)
bind(
fonera,
options.control_local_port,
options.control_local_usocket,
address=options.control_local_address
)
def __inject_broadcast():
"""Lazily load broadcast() function to break circular dependencies"""
from bitsd.server.handlers import broadcast
hooks.broadcast = broadcast
#
# Copyright (C) 2013 Stefano Sanfilippo
# Copyright (C) 2013 BITS development team
#
# This file is part of bitsd, which is released under the terms of
# GNU GPLv3. See COPYING at top level for more information.
#
"""
TCP server receiving raw messages and invoke correct handlers
(from module `.hooks`). Listens for remote commands on BITS-miniprotocol
and dispatches to Fonera via bitsd.client.fonera.Fonera proxy.
"""
import tornado.tcpserver
from tornado.options import options
from tornado.iostream import StreamClosedError
from bitsd.common import LOG
from .hooks import *
def send(string):
if RemoteListener.STREAM is None:
LOG.error("No Fonera connected! Not sending %r", string)
return
try:
RemoteListener.STREAM.write(string.encode('utf-8'))
except StreamClosedError as error:
LOG.error('Could not push message to Fonera! %s', error)
class RemoteListener(tornado.tcpserver.TCPServer):
"""
Handle incoming commands via BITS mini protocol.
Trying to do something KISS.
Commands are rpc-like: function name and eventual args, separated by spaces.
One command per line, "\\n" as line separator.
Numeric argument are printed as-is, string arguments are encoded in base64.
**status** <int>
Parameter 0 is "closed", 1 is "open".
>>> "status 1\\n"
**enter** <int>
One person is added to the list of persone in sede.
The first parameter is the number inserted on the numeric keypad.
>>> "enter 5\\n"
**leave** <int>
One person is removed from the list of persone in sede.
The first parameter is the number inserted on the numeric keypad.
>>> "leave 5\\n"
**message** <string>
A message is added to the list of messages shown on the display.
>>> "message bG9sCg==\\n"
**sound** <int>
Play a sound on the fonera.
The parameter is an index into a list of predefined
sounds. Sad trombone anyone?
>>> "sound 0\\n"
"""
ACTIONS = {
b'temperature': handle_temperature_command,
b'status': handle_status_command,
b'enter': handle_enter_command,
b'leave': handle_leave_command,
b'message': handle_message_command,
b'sound': handle_sound_command,
}
STREAM = None
def __init__(self):
super(RemoteListener, self).__init__()
def handle_stream(self, stream, address):
"""Handles inbound TCP connections asynchronously."""
LOG.info("New connection from Fonera.")
if address[0] != options.control_remote_address:
LOG.error(
"Connection from `%s`, expected from `%s`. Ignoring.",
address,
options.control_remote_address
)
return
if RemoteListener.STREAM is not None:
LOG.warning("Another connection was open, closing the previous one.")
RemoteListener.STREAM.close()
RemoteListener.STREAM = stream
RemoteListener.STREAM.read_until(b'\n', self.handle_command)
def handle_command(self, command):
"""Reacts to received commands (callback).
Will separate args and call appropriate handlers."""
# Meanwhile, go on with commands...
RemoteListener.STREAM.read_until(b'\n', self.handle_command)
command = command.strip(b'\n')
if command:
args = command.split(b' ')
action = args[0]
try:
handler = RemoteListener.ACTIONS[action]
except KeyError:
LOG.warning('Remote received unknown command `%s`', args)
else:
# Execute handler (index 0) with args (index 1->end)
try:
handler(*args[1:])
except TypeError:
LOG.error(
'Command `%s` called with wrong number of args', action
)
else:
LOG.warning('Remote received empty command.')
#
# Copyright (C) 2013 Stefano Sanfilippo
# Copyright (C) 2013 BITS development team
#
# This file is part of bitsd, which is released under the terms of
# GNU GPLv3. See COPYING at top level for more information.
#
"""
Hooks called by `.handlers` to handle specific commands.
"""
# NOTE: don't forget to register your handler in RemoteListener.ACTIONS
# : and in __all__ below!!
import base64
from bitsd.listener import notifier
from bitsd.persistence.engine import session_scope
from bitsd.persistence.models import Status
import bitsd.persistence.query as query
from bitsd.common import LOG
#: This will be initialized by bitsd.listener.start()
broadcast = None
__all__ = [
'handle_temperature_command',
'handle_status_command',
'handle_enter_command',
'handle_leave_command',
'handle_message_command',
'handle_sound_command'
]
def handle_temperature_command(sensorid, value):
"""Receives and log data received from remote sensor."""
LOG.info('Received temperature: sensorid=%r, value=%r', sensorid, value)
try:
sensorid = int(sensorid)
value = float(value)
except ValueError:
LOG.error('Wrong type for parameters in temperature command!')
return
with session_scope() as session:
temp = query.log_temperature(session, value, sensorid, 'BITS')
broadcast(temp.jsondict())
def handle_status_command(status):
"""Update status.
Will reject two identical and consecutive updates
(prevents opening when already open and vice-versa)."""
LOG.info('Received status: %r', status)
try:
status = int(status)
except ValueError:
LOG.error('Wrong type for parameters in temperature command')
return
if status not in (0, 1):
LOG.error('Non existent status %r, ignoring.', status)
return
textstatus = Status.OPEN if status == 1 else Status.CLOSED
with session_scope() as session:
curstatus = query.get_current_status(session)
if curstatus is None or curstatus.value != textstatus:
status = query.log_status(session, textstatus, 'BITS')
broadcast(status.jsondict())
notifier.send_status(textstatus)
else:
LOG.error('BITS already open/closed! Ignoring.')
def handle_enter_command(userid):
"""Handles signal triggered when a new user enters."""
LOG.info('Received enter command: id=%r', userid)
try:
userid = int(userid)
except ValueError:
LOG.error('Wrong type for parameters in temperature command!')
return
LOG.error('handle_enter_command not implemented.')
def handle_leave_command(userid):
"""Handles signal triggered when a known user leaves."""
LOG.info('Received leave command: id=%r', userid)
try:
userid = int(userid)
except ValueError:
LOG.error('Wrong type for parameters in temperature command!')
return
LOG.error('handle_leave_command not implemented.')
def handle_message_command(message):
"""Handles message broadcast requests."""
LOG.info('Received message command: message=%r', message)
try:
decodedmex = base64.b64decode(message)
except TypeError:
LOG.error('Received message is not valid base64: %r', message)
else:
text = decodedmex.decode('utf8')
#FIXME maybe get author ID from message?
user = "BITS"
with session_scope() as session:
user = query.get_user(session, user)
if not user:
LOG.error("Non-existent user %r, not logging message.", user)
return
message = query.log_message(session, user, text)
broadcast(message.jsondict())
notifier.send_message(text)
def handle_sound_command(soundid):
"""Handles requests to play a sound."""
LOG.info('Received sound command: id=%r', soundid)
try:
soundid = int(soundid)
except ValueError:
LOG.error('Wrong type for parameters in temperature command!')
return
else:
notifier.send_sound(soundid)
#
# Copyright (C) 2013 Stefano Sanfilippo
# Copyright (C) 2013 BITS development team
#
# This file is part of bitsd, which is released under the terms of
# GNU GPLv3. See COPYING at top level for more information.
#
"""
Send BITS-miniprotocol notification messages to remote host.
"""
import base64
from bitsd.persistence.models import Status
from . import handlers
def send_message(text):
"""
A message is added to the list of messages shown on the Fonera display.
"""
handlers.send("message {}\n".format(base64.b64encode(text.encode('utf-8'))))
def send_status(value):
"""
Send open or close status to the BITS Fonera.
Status can be either 0 / 1 or Status.CLOSED / Status.OPEN
"""
try:
value = int(value)
except ValueError:
value = 1 if value == Status.OPEN else 0
handlers.send("status {}\n".format(value))
def send_sound(soundid):
"""
Play a sound on the fonera.
The parameter is an index into a list of predefined sounds.
Sad trombone anyone?
"""
handlers.send("sound {}\n".format(soundid))
......@@ -14,7 +14,6 @@ BITSd entry point and unix signal handlers.
import bitsd.properties
import bitsd.server as server
import bitsd.listener as listener
import bitsd.mqtt_listener as mqtt_listener
import bitsd.persistence as persistence
......@@ -67,7 +66,6 @@ def main():
persistence.start()
server.start()
#listener.start()
mqtt_listener.start()
# Add signal handlers...
......
......@@ -23,29 +23,6 @@ define("web_usocket",
group="Networking"
)
define("control_local_port",
default=8888,
help="Port for fonera server. Only if ws_usocket is not defined.",
group='Networking'
)
define("control_local_usocket",
default='', help="Unix socket the remote control will be listening on.",
group="Networking"
)
define("control_local_address",
default="127.0.0.1",
help="The address the remote control will be bound to.",
group='Networking'
)
define("control_remote_address",
default="127.0.0.1",
help="The address of the remote control unit (Fonera).",
group="Networking"
)
define("reverse_proxied",
default=False,
help="Get remote IP address via X- headers (use when reverse proxied).",
......
#
# Copyright (C) 2013 Stefano Sanfilippo
# Copyright (C) 2013 BITS development team
#
# This file is part of bitsd, which is released under the terms of
# GNU GPLv3. See COPYING at top level for more information.
#
"""
Unit tests package
"""
\ No newline at end of file
......@@ -3,7 +3,6 @@ markdown
sqlalchemy>=0.7
tornado==4.*
paho-mqtt
#mysqlclient
psycopg2
svgwrite~=1.3
colour~=0.1
......
[bdist_rpm]
release = 1
packager = Stefano Sanfilippo <a.little.coder@gmail.com>
doc_files = README.md AUTHORS COPYING
requires = python-tornado python-sqlalchemy python-passlib python-concurrent.futures
#! /usr/bin/env python3
#
# Copyright (C) 2013 Stefano Sanfilippo
# Copyright (C) 2013 BITS development team
#
# This file is part of bitsd, which is released under the terms of
# GNU GPLv3. See COPYING at top level for more information.
#
from setuptools import setup
setup(
name='bitsd',
version='2.1',
license='GPLv3',
description='Presence server, logger and remote listener.',
author='Stefano Sanfilippo et al.',
author_email='a.little.coder@gmail.com',
url='https://github.com/esseks/bitsd',
install_requires=[
'tornado >= 2.3',
'sqlalchemy >= 0.7',
'markdown',
'futures',
'pycares'
],
packages=[
'bitsd',
'bitsd.client',
'bitsd.listener',
'bitsd.persistence',
'bitsd.server',
'bitsd.test',
],
scripts=[
'bitsd.py',
'usermanage.py',
'bootstrap.py'
],
package_data={
'bitsd.server': [
'templates/*.html',
'assets/*',
'assets/lib/*',
'scripts/*'
],
},
data_files=[
('etc', [
'bitsd.conf'
])
]
)
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