aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Stomps/README.md14
-rw-r--r--Stomps/SimpleWebSocketServer/SimpleWebSocketServer.py735
-rw-r--r--Stomps/SimpleWebSocketServer/__init__.py3
-rw-r--r--Stomps/read_stomp.py56
-rw-r--r--Stomps/stomp.py (renamed from Stomps/write_stomp.py)64
-rw-r--r--Stomps/stomp_control.py131
-rw-r--r--Stomps/stomp_scroll.py23
-rw-r--r--Stomps/stomp_server.py98
8 files changed, 931 insertions, 193 deletions
diff --git a/Stomps/README.md b/Stomps/README.md
index da4fe92..9d995ec 100644
--- a/Stomps/README.md
+++ b/Stomps/README.md
@@ -1,3 +1,17 @@
+## Installation
+
+1) Take this folder `Stomps` and put it anywhere you like on the QC
+
+## Usage
+
+`cd` into the `Stomps` directory installed on the QC and run any of the Python scripts.
+
+There are a couple of fun things to play around with.
+1) **read_stomp.py:** Allows you to decode and read stomp device traffic
+2) **stomp_control.py:** CLI tool to activate stomps
+3) **stomp_scroll.py:** Just a fun visual effect
+4) **stomp_server.py:** Spawns a websocket server that allows you to control stomps over IP
+
## Stomp message byte examples:
**Action: button | Value: pressed**
diff --git a/Stomps/SimpleWebSocketServer/SimpleWebSocketServer.py b/Stomps/SimpleWebSocketServer/SimpleWebSocketServer.py
new file mode 100644
index 0000000..ea528ee
--- /dev/null
+++ b/Stomps/SimpleWebSocketServer/SimpleWebSocketServer.py
@@ -0,0 +1,735 @@
+'''
+The MIT License (MIT)
+Copyright (c) 2013 Dave P.
+'''
+import sys
+VER = sys.version_info[0]
+if VER >= 3:
+ import socketserver
+ from http.server import BaseHTTPRequestHandler
+ from io import StringIO, BytesIO
+else:
+ import SocketServer
+ from BaseHTTPServer import BaseHTTPRequestHandler
+ from StringIO import StringIO
+
+import hashlib
+import base64
+import socket
+import struct
+import ssl
+import errno
+import codecs
+from collections import deque
+from select import select
+
+__all__ = ['WebSocket',
+ 'SimpleWebSocketServer',
+ 'SimpleSSLWebSocketServer']
+
+def _check_unicode(val):
+ if VER >= 3:
+ return isinstance(val, str)
+ else:
+ return isinstance(val, basestring)
+
+class HTTPRequest(BaseHTTPRequestHandler):
+ def __init__(self, request_text):
+ if VER >= 3:
+ self.rfile = BytesIO(request_text)
+ else:
+ self.rfile = StringIO(request_text)
+ self.raw_requestline = self.rfile.readline()
+ self.error_code = self.error_message = None
+ self.parse_request()
+
+_VALID_STATUS_CODES = [1000, 1001, 1002, 1003, 1007, 1008,
+ 1009, 1010, 1011, 3000, 3999, 4000, 4999]
+
+HANDSHAKE_STR = (
+ "HTTP/1.1 101 Switching Protocols\r\n"
+ "Upgrade: WebSocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Accept: %(acceptstr)s\r\n\r\n"
+)
+
+FAILED_HANDSHAKE_STR = (
+ "HTTP/1.1 426 Upgrade Required\r\n"
+ "Upgrade: WebSocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Version: 13\r\n"
+ "Content-Type: text/plain\r\n\r\n"
+ "This service requires use of the WebSocket protocol\r\n"
+)
+
+GUID_STR = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
+
+STREAM = 0x0
+TEXT = 0x1
+BINARY = 0x2
+CLOSE = 0x8
+PING = 0x9
+PONG = 0xA
+
+HEADERB1 = 1
+HEADERB2 = 3
+LENGTHSHORT = 4
+LENGTHLONG = 5
+MASK = 6
+PAYLOAD = 7
+
+MAXHEADER = 65536
+MAXPAYLOAD = 33554432
+
+class WebSocket(object):
+
+ def __init__(self, server, sock, address):
+ self.server = server
+ self.client = sock
+ self.address = address
+
+ self.handshaked = False
+ self.headerbuffer = bytearray()
+ self.headertoread = 2048
+
+ self.fin = 0
+ self.data = bytearray()
+ self.opcode = 0
+ self.hasmask = 0
+ self.maskarray = None
+ self.length = 0
+ self.lengtharray = None
+ self.index = 0
+ self.request = None
+ self.usingssl = False
+
+ self.frag_start = False
+ self.frag_type = BINARY
+ self.frag_buffer = None
+ self.frag_decoder = codecs.getincrementaldecoder('utf-8')(errors='strict')
+ self.closed = False
+ self.sendq = deque()
+
+ self.state = HEADERB1
+
+ # restrict the size of header and payload for security reasons
+ self.maxheader = MAXHEADER
+ self.maxpayload = MAXPAYLOAD
+
+ def handleMessage(self):
+ """
+ Called when websocket frame is received.
+ To access the frame data call self.data.
+
+ If the frame is Text then self.data is a unicode object.
+ If the frame is Binary then self.data is a bytearray object.
+ """
+ pass
+
+ def handleConnected(self):
+ """
+ Called when a websocket client connects to the server.
+ """
+ pass
+
+ def handleClose(self):
+ """
+ Called when a websocket server gets a Close frame from a client.
+ """
+ pass
+
+ def _handlePacket(self):
+ if self.opcode == CLOSE:
+ pass
+ elif self.opcode == STREAM:
+ pass
+ elif self.opcode == TEXT:
+ pass
+ elif self.opcode == BINARY:
+ pass
+ elif self.opcode == PONG or self.opcode == PING:
+ if len(self.data) > 125:
+ raise Exception('control frame length can not be > 125')
+ else:
+ # unknown or reserved opcode so just close
+ raise Exception('unknown opcode')
+
+ if self.opcode == CLOSE:
+ status = 1000
+ reason = u''
+ length = len(self.data)
+
+ if length == 0:
+ pass
+ elif length >= 2:
+ status = struct.unpack_from('!H', self.data[:2])[0]
+ reason = self.data[2:]
+
+ if status not in _VALID_STATUS_CODES:
+ status = 1002
+
+ if len(reason) > 0:
+ try:
+ reason = reason.decode('utf8', errors='strict')
+ except:
+ status = 1002
+ else:
+ status = 1002
+
+ self.close(status, reason)
+ return
+
+ elif self.fin == 0:
+ if self.opcode != STREAM:
+ if self.opcode == PING or self.opcode == PONG:
+ raise Exception('control messages can not be fragmented')
+
+ self.frag_type = self.opcode
+ self.frag_start = True
+ self.frag_decoder.reset()
+
+ if self.frag_type == TEXT:
+ self.frag_buffer = []
+ utf_str = self.frag_decoder.decode(self.data, final = False)
+ if utf_str:
+ self.frag_buffer.append(utf_str)
+ else:
+ self.frag_buffer = bytearray()
+ self.frag_buffer.extend(self.data)
+
+ else:
+ if self.frag_start is False:
+ raise Exception('fragmentation protocol error')
+
+ if self.frag_type == TEXT:
+ utf_str = self.frag_decoder.decode(self.data, final = False)
+ if utf_str:
+ self.frag_buffer.append(utf_str)
+ else:
+ self.frag_buffer.extend(self.data)
+
+ else:
+ if self.opcode == STREAM:
+ if self.frag_start is False:
+ raise Exception('fragmentation protocol error')
+
+ if self.frag_type == TEXT:
+ utf_str = self.frag_decoder.decode(self.data, final = True)
+ self.frag_buffer.append(utf_str)
+ self.data = u''.join(self.frag_buffer)
+ else:
+ self.frag_buffer.extend(self.data)
+ self.data = self.frag_buffer
+
+ self.handleMessage()
+
+ self.frag_decoder.reset()
+ self.frag_type = BINARY
+ self.frag_start = False
+ self.frag_buffer = None
+
+ elif self.opcode == PING:
+ self._sendMessage(False, PONG, self.data)
+
+ elif self.opcode == PONG:
+ pass
+
+ else:
+ if self.frag_start is True:
+ raise Exception('fragmentation protocol error')
+
+ if self.opcode == TEXT:
+ try:
+ self.data = self.data.decode('utf8', errors='strict')
+ except Exception as exp:
+ raise Exception('invalid utf-8 payload')
+
+ self.handleMessage()
+
+
+ def _handleData(self):
+ # do the HTTP header and handshake
+ if self.handshaked is False:
+
+ try:
+ data = self.client.recv(self.headertoread)
+ except (ssl.SSLWantReadError, ssl.SSLWantWriteError):
+ # SSL socket not ready to read yet, wait and try again
+ return
+ if not data:
+ raise Exception('remote socket closed')
+
+ else:
+ # accumulate
+ self.headerbuffer.extend(data)
+
+ if len(self.headerbuffer) >= self.maxheader:
+ raise Exception('header exceeded allowable size')
+
+ # indicates end of HTTP header
+ if b'\r\n\r\n' in self.headerbuffer:
+ self.request = HTTPRequest(self.headerbuffer)
+
+ # handshake rfc 6455
+ try:
+ key = self.request.headers['Sec-WebSocket-Key']
+ k = key.encode('ascii') + GUID_STR.encode('ascii')
+ k_s = base64.b64encode(hashlib.sha1(k).digest()).decode('ascii')
+ hStr = HANDSHAKE_STR % {'acceptstr': k_s}
+ self.sendq.append((BINARY, hStr.encode('ascii')))
+ self.handshaked = True
+ self.handleConnected()
+ except Exception as e:
+ hStr = FAILED_HANDSHAKE_STR
+ self._sendBuffer(hStr.encode('ascii'), True)
+ self.client.close()
+ raise Exception('handshake failed: %s', str(e))
+
+ # else do normal data
+ else:
+ try:
+ data = self.client.recv(16384)
+ except (ssl.SSLWantReadError, ssl.SSLWantWriteError):
+ # SSL socket not ready to read yet, wait and try again
+ return
+ if not data:
+ raise Exception("remote socket closed")
+
+ if VER >= 3:
+ for d in data:
+ self._parseMessage(d)
+ else:
+ for d in data:
+ self._parseMessage(ord(d))
+
+ def close(self, status = 1000, reason = u''):
+ """
+ Send Close frame to the client. The underlying socket is only closed
+ when the client acknowledges the Close frame.
+
+ status is the closing identifier.
+ reason is the reason for the close.
+ """
+ try:
+ if self.closed is False:
+ close_msg = bytearray()
+ close_msg.extend(struct.pack("!H", status))
+ if _check_unicode(reason):
+ close_msg.extend(reason.encode('utf-8'))
+ else:
+ close_msg.extend(reason)
+
+ self._sendMessage(False, CLOSE, close_msg)
+
+ finally:
+ self.closed = True
+
+
+ def _sendBuffer(self, buff, send_all = False):
+ size = len(buff)
+ tosend = size
+ already_sent = 0
+
+ while tosend > 0:
+ try:
+ # i should be able to send a bytearray
+ sent = self.client.send(buff[already_sent:])
+ if sent == 0:
+ raise RuntimeError('socket connection broken')
+
+ already_sent += sent
+ tosend -= sent
+
+ except (ssl.SSLWantReadError, ssl.SSLWantWriteError):
+ # SSL socket not ready to send yet, wait and try again
+ if send_all:
+ continue
+ return buff[already_sent:]
+
+ except socket.error as e:
+ # if we have full buffers then wait for them to drain and try again
+ if e.errno in [errno.EAGAIN, errno.EWOULDBLOCK]:
+ if send_all:
+ continue
+ return buff[already_sent:]
+ else:
+ raise e
+
+ return None
+
+ def sendFragmentStart(self, data):
+ """
+ Send the start of a data fragment stream to a websocket client.
+ Subsequent data should be sent using sendFragment().
+ A fragment stream is completed when sendFragmentEnd() is called.
+
+ If data is a unicode object then the frame is sent as Text.
+ If the data is a bytearray object then the frame is sent as Binary.
+ """
+ opcode = BINARY
+ if _check_unicode(data):
+ opcode = TEXT
+ self._sendMessage(True, opcode, data)
+
+ def sendFragment(self, data):
+ """
+ see sendFragmentStart()
+
+ If data is a unicode object then the frame is sent as Text.
+ If the data is a bytearray object then the frame is sent as Binary.
+ """
+ self._sendMessage(True, STREAM, data)
+
+ def sendFragmentEnd(self, data):
+ """
+ see sendFragmentEnd()
+
+ If data is a unicode object then the frame is sent as Text.
+ If the data is a bytearray object then the frame is sent as Binary.
+ """
+ self._sendMessage(False, STREAM, data)
+
+ def sendMessage(self, data):
+ """
+ Send websocket data frame to the client.
+
+ If data is a unicode object then the frame is sent as Text.
+ If the data is a bytearray object then the frame is sent as Binary.
+ """
+ opcode = BINARY
+ if _check_unicode(data):
+ opcode = TEXT
+ self._sendMessage(False, opcode, data)
+
+
+ def _sendMessage(self, fin, opcode, data):
+
+ payload = bytearray()
+
+ b1 = 0
+ b2 = 0
+ if fin is False:
+ b1 |= 0x80
+ b1 |= opcode
+
+ if _check_unicode(data):
+ data = data.encode('utf-8')
+
+ length = len(data)
+ payload.append(b1)
+
+ if length <= 125:
+ b2 |= length
+ payload.append(b2)
+
+ elif length >= 126 and length <= 65535:
+ b2 |= 126
+ payload.append(b2)
+ payload.extend(struct.pack("!H", length))
+
+ else:
+ b2 |= 127
+ payload.append(b2)
+ payload.extend(struct.pack("!Q", length))
+
+ if length > 0:
+ payload.extend(data)
+
+ self.sendq.append((opcode, payload))
+
+
+ def _parseMessage(self, byte):
+ # read in the header
+ if self.state == HEADERB1:
+
+ self.fin = byte & 0x80
+ self.opcode = byte & 0x0F
+ self.state = HEADERB2
+
+ self.index = 0
+ self.length = 0
+ self.lengtharray = bytearray()
+ self.data = bytearray()
+
+ rsv = byte & 0x70
+ if rsv != 0:
+ raise Exception('RSV bit must be 0')
+
+ elif self.state == HEADERB2:
+ mask = byte & 0x80
+ length = byte & 0x7F
+
+ if self.opcode == PING and length > 125:
+ raise Exception('ping packet is too large')
+
+ if mask == 128:
+ self.hasmask = True
+ else:
+ self.hasmask = False
+
+ if length <= 125:
+ self.length = length
+
+ # if we have a mask we must read it
+ if self.hasmask is True:
+ self.maskarray = bytearray()
+ self.state = MASK
+ else:
+ # if there is no mask and no payload we are done
+ if self.length <= 0:
+ try:
+ self._handlePacket()
+ finally:
+ self.state = HEADERB1
+ self.data = bytearray()
+
+ # we have no mask and some payload
+ else:
+ #self.index = 0
+ self.data = bytearray()
+ self.state = PAYLOAD
+
+ elif length == 126:
+ self.lengtharray = bytearray()
+ self.state = LENGTHSHORT
+
+ elif length == 127:
+ self.lengtharray = bytearray()
+ self.state = LENGTHLONG
+
+
+ elif self.state == LENGTHSHORT:
+ self.lengtharray.append(byte)
+
+ if len(self.lengtharray) > 2:
+ raise Exception('short length exceeded allowable size')
+
+ if len(self.lengtharray) == 2:
+ self.length = struct.unpack_from('!H', self.lengtharray)[0]
+
+ if self.hasmask is True:
+ self.maskarray = bytearray()
+ self.state = MASK
+ else:
+ # if there is no mask and no payload we are done
+ if self.length <= 0:
+ try:
+ self._handlePacket()
+ finally:
+ self.state = HEADERB1
+ self.data = bytearray()
+
+ # we have no mask and some payload
+ else:
+ #self.index = 0
+ self.data = bytearray()
+ self.state = PAYLOAD
+
+ elif self.state == LENGTHLONG:
+
+ self.lengtharray.append(byte)
+
+ if len(self.lengtharray) > 8:
+ raise Exception('long length exceeded allowable size')
+
+ if len(self.lengtharray) == 8:
+ self.length = struct.unpack_from('!Q', self.lengtharray)[0]
+
+ if self.hasmask is True:
+ self.maskarray = bytearray()
+ self.state = MASK
+ else:
+ # if there is no mask and no payload we are done
+ if self.length <= 0:
+ try:
+ self._handlePacket()
+ finally:
+ self.state = HEADERB1
+ self.data = bytearray()
+
+ # we have no mask and some payload
+ else:
+ #self.index = 0
+ self.data = bytearray()
+ self.state = PAYLOAD
+
+ # MASK STATE
+ elif self.state == MASK:
+ self.maskarray.append(byte)
+
+ if len(self.maskarray) > 4:
+ raise Exception('mask exceeded allowable size')
+
+ if len(self.maskarray) == 4:
+ # if there is no mask and no payload we are done
+ if self.length <= 0:
+ try:
+ self._handlePacket()
+ finally:
+ self.state = HEADERB1
+ self.data = bytearray()
+
+ # we have no mask and some payload
+ else:
+ #self.index = 0
+ self.data = bytearray()
+ self.state = PAYLOAD
+
+ # PAYLOAD STATE
+ elif self.state == PAYLOAD:
+ if self.hasmask is True:
+ self.data.append( byte ^ self.maskarray[self.index % 4] )
+ else:
+ self.data.append( byte )
+
+ # if length exceeds allowable size then we except and remove the connection
+ if len(self.data) >= self.maxpayload:
+ raise Exception('payload exceeded allowable size')
+
+ # check if we have processed length bytes; if so we are done
+ if (self.index+1) == self.length:
+ try:
+ self._handlePacket()
+ finally:
+ #self.index = 0
+ self.state = HEADERB1
+ self.data = bytearray()
+ else:
+ self.index += 1
+
+
+class SimpleWebSocketServer(object):
+ def __init__(self, host, port, websocketclass, selectInterval = 0.1):
+ self.websocketclass = websocketclass
+
+ if (host == ''):
+ host = None
+
+ hostInfo = socket.getaddrinfo(host, port, socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP, socket.AI_PASSIVE)
+ self.serversocket = socket.socket(socket.AF_INET, hostInfo[0][1], hostInfo[0][2])
+ self.serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ self.serversocket.bind(hostInfo[0][4])
+ self.serversocket.listen(5)
+ self.selectInterval = selectInterval
+ self.connections = {}
+ self.listeners = [self.serversocket]
+
+ def _decorateSocket(self, sock):
+ return sock
+
+ def _constructWebSocket(self, sock, address):
+ return self.websocketclass(self, sock, address)
+
+ def close(self):
+ self.serversocket.close()
+
+ for desc, conn in self.connections.items():
+ conn.close()
+ self._handleClose(conn)
+
+ def _handleClose(self, client):
+ client.client.close()
+ # only call handleClose when we have a successful websocket connection
+ if client.handshaked:
+ try:
+ client.handleClose()
+ except:
+ pass
+
+ def serveonce(self):
+ writers = []
+ for fileno in self.listeners:
+ if fileno == self.serversocket:
+ continue
+ client = self.connections[fileno]
+ if client.sendq:
+ writers.append(fileno)
+
+ rList, wList, xList = select(self.listeners, writers, self.listeners, self.selectInterval)
+
+ for ready in wList:
+ client = self.connections[ready]
+ try:
+ while client.sendq:
+ opcode, payload = client.sendq.popleft()
+ remaining = client._sendBuffer(payload)
+ if remaining is not None:
+ client.sendq.appendleft((opcode, remaining))
+ break
+ else:
+ if opcode == CLOSE:
+ raise Exception('received client close')
+
+ except Exception as n:
+ self._handleClose(client)
+ del self.connections[ready]
+ self.listeners.remove(ready)
+
+ for ready in rList:
+ if ready == self.serversocket:
+ sock = None
+ try:
+ sock, address = self.serversocket.accept()
+ newsock = self._decorateSocket(sock)
+ newsock.setblocking(0)
+ fileno = newsock.fileno()
+ self.connections[fileno] = self._constructWebSocket(newsock, address)
+ self.listeners.append(fileno)
+ except Exception as n:
+ if sock is not None:
+ sock.close()
+ else:
+ if ready not in self.connections:
+ continue
+ client = self.connections[ready]
+ try:
+ client._handleData()
+ except Exception as n:
+ self._handleClose(client)
+ del self.connections[ready]
+ self.listeners.remove(ready)
+
+ for failed in xList:
+ if failed == self.serversocket:
+ self.close()
+ raise Exception('server socket failed')
+ else:
+ if failed not in self.connections:
+ continue
+ client = self.connections[failed]
+ self._handleClose(client)
+ del self.connections[failed]
+ self.listeners.remove(failed)
+
+ def serveforever(self):
+ while True:
+ self.serveonce()
+
+class SimpleSSLWebSocketServer(SimpleWebSocketServer):
+
+ def __init__(self, host, port, websocketclass, certfile = None,
+ keyfile = None, version = ssl.PROTOCOL_TLSv1_2, selectInterval = 0.1, ssl_context = None):
+
+ SimpleWebSocketServer.__init__(self, host, port,
+ websocketclass, selectInterval)
+
+ if ssl_context is None:
+ self.context = ssl.SSLContext(version)
+ self.context.load_cert_chain(certfile, keyfile)
+ else:
+ self.context = ssl_context
+
+ def close(self):
+ super(SimpleSSLWebSocketServer, self).close()
+
+ def _decorateSocket(self, sock):
+ sslsock = self.context.wrap_socket(sock, server_side=True)
+ return sslsock
+
+ def _constructWebSocket(self, sock, address):
+ ws = self.websocketclass(self, sock, address)
+ ws.usingssl = True
+ return ws
+
+ def serveforever(self):
+ super(SimpleSSLWebSocketServer, self).serveforever()
diff --git a/Stomps/SimpleWebSocketServer/__init__.py b/Stomps/SimpleWebSocketServer/__init__.py
new file mode 100644
index 0000000..c7e52b8
--- /dev/null
+++ b/Stomps/SimpleWebSocketServer/__init__.py
@@ -0,0 +1,3 @@
+from .SimpleWebSocketServer import *
+
+name="SimpleWebSocketServer"
diff --git a/Stomps/read_stomp.py b/Stomps/read_stomp.py
index 65dcdae..d1ff7f0 100644
--- a/Stomps/read_stomp.py
+++ b/Stomps/read_stomp.py
@@ -1,61 +1,7 @@
# Quick and simple Python 2 script to read the raw bytes,
# from a stomp device on the Quad Cortex.
-from datetime import datetime
-
-class StompEvent:
- def __init__(self, raw_bytes):
- self.raw_bytes = raw_bytes
- self.action = "unknown"
- self.value = "unknown"
- self.system_epoch = datetime.now().strftime("%s")
- self.system_datetime = datetime.now()
- self.decode_bytes()
-
- def print_bytes(self):
- bytes_str = ""
- for i in range(len(self.raw_bytes)):
- bytes_str = bytes_str + self.raw_bytes[i].encode('hex') + ' '
- print bytes_str
- print '---'*32
-
- def decode_bytes(self):
- self.decode_action()
- self.decode_value()
- self.decode_time()
-
- def decode_action(self):
- action_byte_1 = self.raw_bytes[10].encode('hex')
- action_byte_2 = self.raw_bytes[11].encode('hex')
- if(action_byte_1 == "11" and action_byte_2 == "01"):
- self.action = "button"
- elif(action_byte_1 == "08" and action_byte_2 == "00"):
- self.action = "rotary"
-
- def decode_value(self):
- value_byte = self.raw_bytes[12].encode('hex')
- if(value_byte == "00"):
- self.value = "released"
- elif(value_byte == "01"):
- if(self.action == "button"):
- self.value = "pressed"
- elif(self.action == "rotary"):
- self.value = "clock wise"
- elif(value_byte == "ff"):
- self.value = "counter clock"
-
- def decode_time(self):
- # the first 4 bytes form the time
- time_bytes = self.raw_bytes[0:4]
- time_bytes.reverse()
- time_hex = ""
- for i in range(len(time_bytes)):
- time_hex = time_hex + time_bytes[i].encode('hex')
- self.epoch = int(time_hex, 16)
- self.date_time = datetime.fromtimestamp(self.epoch)
-
- def __str__(self):
- return "Action: " + self.action + "\nValue: " + self.value + "\nEpoch: " + str(self.epoch) + "\nDatetime: " + str(self.date_time)
+from stomp import StompEvent
with open('/dev/zencoder/knob_stomp2', 'rb') as file:
byte_buffer = []
diff --git a/Stomps/write_stomp.py b/Stomps/stomp.py
index 002acd4..e244ead 100644
--- a/Stomps/write_stomp.py
+++ b/Stomps/stomp.py
@@ -1,6 +1,3 @@
-# Quick and simple Python 2 script to write the raw bytes,
-# to a stomp device on the Quad Cortex.
-
from datetime import datetime
class StompCommand:
@@ -58,19 +55,56 @@ class StompCommand:
def __str__(self):
return "Action: " + self.action + "\nValue: " + self.value + "\nBytes: " + self.bytes_to_hex()
+
+
+class StompEvent:
+ def __init__(self, raw_bytes):
+ self.raw_bytes = raw_bytes
+ self.action = "unknown"
+ self.value = "unknown"
+ self.decode_bytes()
+ def print_bytes(self):
+ bytes_str = ""
+ for i in range(len(self.raw_bytes)):
+ bytes_str = bytes_str + self.raw_bytes[i].encode('hex') + ' '
+ print bytes_str
+ print '---'*32
-with open('/dev/zencoder/knob_stomp2', 'wb') as file:
- # write a button press
- command = StompCommand("button", "pressed")
- print command
- file.write(command.raw_bytes)
+ def decode_bytes(self):
+ self.decode_action()
+ self.decode_value()
+ self.decode_time()
- print ''
+ def decode_action(self):
+ action_byte_1 = self.raw_bytes[10].encode('hex')
+ action_byte_2 = self.raw_bytes[11].encode('hex')
+ if(action_byte_1 == "11" and action_byte_2 == "01"):
+ self.action = "button"
+ elif(action_byte_1 == "08" and action_byte_2 == "00"):
+ self.action = "rotary"
- # write a button release
- command = StompCommand("button", "released")
- print command
- file.write(command.raw_bytes)
- # close the file
- file.close() \ No newline at end of file
+ def decode_value(self):
+ value_byte = self.raw_bytes[12].encode('hex')
+ if(value_byte == "00"):
+ self.value = "released"
+ elif(value_byte == "01"):
+ if(self.action == "button"):
+ self.value = "pressed"
+ elif(self.action == "rotary"):
+ self.value = "clock wise"
+ elif(value_byte == "ff"):
+ self.value = "counter clock"
+
+ def decode_time(self):
+ # the first 4 bytes form the time
+ time_bytes = self.raw_bytes[0:4]
+ time_bytes.reverse()
+ time_hex = ""
+ for i in range(len(time_bytes)):
+ time_hex = time_hex + time_bytes[i].encode('hex')
+ self.epoch = int(time_hex, 16)
+ self.date_time = datetime.fromtimestamp(self.epoch)
+
+ def __str__(self):
+ return "Action: " + self.action + "\nValue: " + self.value + "\nEpoch: " + str(self.epoch) + "\nDatetime: " + str(self.date_time)
diff --git a/Stomps/stomp_control.py b/Stomps/stomp_control.py
index add3dd8..86aae20 100644
--- a/Stomps/stomp_control.py
+++ b/Stomps/stomp_control.py
@@ -1,131 +1,16 @@
# Code copied to be easy to use as one script
-
-from datetime import datetime
-
-class StompCommand:
- def __init__(self, action, value):
- self.action = action
- self.value = value
- self.raw_bytes = bytearray(32)
- self.encode_bytes()
-
- def encode_bytes(self):
- self.encode_time()
- self.encode_action()
- self.encode_value()
- self.build_last_16_bytes()
-
- def encode_time(self):
- epoch = datetime.now().strftime("%s")
- epoch_hex = hex(int(epoch))[2:]
- epoch_bytes = bytearray.fromhex(epoch_hex)
- epoch_bytes.reverse()
- for i in range(4):
- self.raw_bytes[i] = epoch_bytes[i]
-
- def encode_action(self):
- self.raw_bytes[9] = 0x00
- if(self.action == "button"):
- self.raw_bytes[8] = 0x01
- self.raw_bytes[10] = 0x11
- self.raw_bytes[11] = 0x01
- elif(self.action == "rotary"):
- self.raw_bytes[8] = 0x01
- self.raw_bytes[10] = 0x08
- self.raw_bytes[11] = 0x00
-
- def encode_value(self):
- if(self.value == "pressed"):
- self.raw_bytes[12] = 0x01
- elif(self.value == "released"):
- self.raw_bytes[12] = 0x00
- elif(self.value == "clock wise"):
- self.raw_bytes[12] = 0x01
- elif(self.value == "counter clock"):
- self.raw_bytes[12] = 0xff
-
- def build_last_16_bytes(self):
- # build the last 16 bytes
- for i in range(6):
- self.raw_bytes[16+i] = self.raw_bytes[i]
-
- def bytes_to_hex(self):
- bytes_str = ""
- for i in range(len(self.raw_bytes)):
- bytes_str = bytes_str + str(self.raw_bytes[i]) + ' '
- return bytes_str
-
- def __str__(self):
- return "Action: " + self.action + "\nValue: " + self.value + "\nBytes: " + self.bytes_to_hex()
+from stomp import StompCommand
-class StompEvent:
- def __init__(self, raw_bytes):
- self.raw_bytes = raw_bytes
- self.action = "unknown"
- self.value = "unknown"
- self.system_epoch = datetime.now().strftime("%s")
- self.system_datetime = datetime.now()
- self.decode_bytes()
-
- def print_bytes(self):
- bytes_str = ""
- for i in range(len(self.raw_bytes)):
- bytes_str = bytes_str + self.raw_bytes[i].encode('hex') + ' '
- print bytes_str
- print '---'*32
-
- def decode_bytes(self):
- self.decode_action()
- self.decode_value()
- self.decode_time()
-
- def decode_action(self):
- action_byte_1 = self.raw_bytes[10].encode('hex')
- action_byte_2 = self.raw_bytes[11].encode('hex')
- if(action_byte_1 == "11" and action_byte_2 == "01"):
- self.action = "button"
- elif(action_byte_1 == "08" and action_byte_2 == "00"):
- self.action = "rotary"
-
- def decode_value(self):
- value_byte = self.raw_bytes[12].encode('hex')
- if(value_byte == "00"):
- self.value = "released"
- elif(value_byte == "01"):
- if(self.action == "button"):
- self.value = "pressed"
- elif(self.action == "rotary"):
- self.value = "clock wise"
- elif(value_byte == "ff"):
- self.value = "counter clock"
-
- def decode_time(self):
- # the first 4 bytes form the time
- time_bytes = self.raw_bytes[0:4]
- time_bytes.reverse()
- time_hex = ""
- for i in range(len(time_bytes)):
- time_hex = time_hex + time_bytes[i].encode('hex')
- self.epoch = int(time_hex, 16)
- self.date_time = datetime.fromtimestamp(self.epoch)
-
- def __str__(self):
- return "Action: " + self.action + "\nValue: " + self.value + "\nEpoch: " + str(self.epoch) + "\nDatetime: " + str(self.date_time)
-
-# Open knob_stomp1 to 11, listen for numeric input and switch on input
-dev_file_names = []
-for i in range(1,12):
- dev_file_names.append('/dev/zencoder/knob_stomp' + str(i))
-
def switch_stomp(selected_stomp):
- with open(dev_file_names[selected_stomp-1], 'wb') as file:
+ with open('/dev/zencoder/knob_stomp' + str(selected_stomp), 'wb') as file:
press_command = StompCommand("button", "pressed")
file.write(press_command.raw_bytes)
release_command = StompCommand("button", "released")
file.write(release_command.raw_bytes)
-while True:
- selected_stomp = int(input("Enter a stomp between 1 and 11: "))
- if(selected_stomp > 0 and selected_stomp < 12):
- print "Switching stomp " + str(selected_stomp)
- switch_stomp(selected_stomp) \ No newline at end of file
+if __name__ == "__main__":
+ while True:
+ selected_stomp = int(input("Enter a stomp between 1 and 11: "))
+ if(selected_stomp > 0 and selected_stomp < 12):
+ print "Switching stomp " + str(selected_stomp)
+ switch_stomp(selected_stomp) \ No newline at end of file
diff --git a/Stomps/stomp_scroll.py b/Stomps/stomp_scroll.py
new file mode 100644
index 0000000..c10ddd2
--- /dev/null
+++ b/Stomps/stomp_scroll.py
@@ -0,0 +1,23 @@
+from stomp import StompCommand
+
+# Open knob_stomp1 to 11, listen for numeric input and switch on input
+dev_file_names = []
+for i in range(1,12):
+ dev_file_names.append('/dev/zencoder/knob_stomp' + str(i))
+
+def switch_stomp(selected_stomp):
+ with open(dev_file_names[selected_stomp-1], 'wb') as file:
+ press_command = StompCommand("button", "pressed")
+ file.write(press_command.raw_bytes)
+ release_command = StompCommand("button", "released")
+ file.write(release_command.raw_bytes)
+
+selected_stomp = 1
+while True:
+ if(selected_stomp > 0 and selected_stomp < 10):
+ if selected_stomp != 5:
+ print "Switching stomp " + str(selected_stomp)
+ switch_stomp(selected_stomp)
+ selected_stomp = selected_stomp + 1
+ else:
+ selected_stomp = 1 \ No newline at end of file
diff --git a/Stomps/stomp_server.py b/Stomps/stomp_server.py
new file mode 100644
index 0000000..9ef213b
--- /dev/null
+++ b/Stomps/stomp_server.py
@@ -0,0 +1,98 @@
+'''
+The MIT License (MIT)
+Copyright (c) 2013 Dave P.
+'''
+
+import signal
+import sys
+import ssl
+from SimpleWebSocketServer import WebSocket, SimpleWebSocketServer, SimpleSSLWebSocketServer
+from optparse import OptionParser
+import json
+from stomp_control import switch_stomp
+
+class StompServer(WebSocket):
+
+ # Request is a JSON object with the following fields:
+ # {
+ # "type": "button",
+ # "action": "activate",
+ # "index": 1
+ # }
+
+ def handleMessage(self):
+ print "Received" + self.data
+
+ try:
+ json_request = json.loads(self.data)
+ except ValueError:
+ self.sendMessage("Invalid request")
+ return
+ print "JSON request: " + str(json_request) + "\n Validating..."
+
+ try:
+ type = json_request['type']
+ action = json_request['action']
+ index = json_request['index']
+ except:
+ self.sendMessage("Invalid request")
+ return
+
+ print "Type: " + type + ", action: " + action + ", index: " + str(index)
+
+ if type == 'button':
+ if action == 'activate':
+ switch_stomp(int(index))
+
+ self.sendMessage("Success")
+
+ def handleConnected(self):
+ print (self.address, 'connected')
+
+ def handleClose(self):
+ print (self.address, 'closed')
+
+ def validate_request(req):
+ if not req:
+ return False
+ if not req['type']:
+ return False
+ if not req['action']:
+ return False
+ if not req['index']:
+ return False
+
+ if req['type'] != 'button' and req['type'] != 'rotary':
+ return False
+ if req['action'] != 'activate':
+ return False
+ if req['index'] < 1 or req['index'] > 11:
+ return False
+ return True
+
+
+if __name__ == "__main__":
+ parser = OptionParser(usage="usage: %prog [options]", version="%prog 1.0")
+ parser.add_option("--host", default='', type='string', action="store", dest="host", help="hostname (localhost)")
+ parser.add_option("--port", default=8000, type='int', action="store", dest="port", help="port (8000)")
+ parser.add_option("--ssl", default=0, type='int', action="store", dest="ssl", help="ssl (1: on, 0: off (default))")
+ parser.add_option("--cert", default='./cert.pem', type='string', action="store", dest="cert", help="cert (./cert.pem)")
+ parser.add_option("--key", default='./key.pem', type='string', action="store", dest="key", help="key (./key.pem)")
+ parser.add_option("--ver", default=ssl.PROTOCOL_TLSv1, type=int, action="store", dest="ver", help="ssl version")
+
+ (options, args) = parser.parse_args()
+ print "Starting server on port " + str(options.port)
+ cls = StompServer
+
+ if options.ssl == 1:
+ server = SimpleSSLWebSocketServer(options.host, options.port, cls, options.cert, options.key, version=options.ver)
+ else:
+ server = SimpleWebSocketServer(options.host, options.port, cls)
+
+ def close_sig_handler(signal, frame):
+ server.close()
+ sys.exit()
+
+ signal.signal(signal.SIGINT, close_sig_handler)
+
+ server.serveforever()