diff options
author | Christian Franke <nobody@nowhere.ws> | 2013-03-17 12:42:09 +0100 |
---|---|---|
committer | Christian Franke <nobody@nowhere.ws> | 2013-03-17 12:47:40 +0100 |
commit | 625fc3e4fd50451b950c3d1e2bf2356fb74dcefa (patch) | |
tree | d10c9a187b505b881c3bc29c18dabbcf6bf5e8f7 | |
parent | 76c0532b5638eda76e71a748832290adb985f21a (diff) |
Add the other scripts (funny code is funny)
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | ethcan.py | 277 | ||||
-rw-r--r-- | ethcansend.py | 56 | ||||
-rw-r--r-- | hub_ctrl.py | 256 | ||||
-rw-r--r-- | subbot.py | 57 | ||||
-rw-r--r-- | subcan.py | 50 |
6 files changed, 698 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..26e3eaf --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.pyc +settings.py diff --git a/ethcan.py b/ethcan.py new file mode 100644 index 0000000..2449933 --- /dev/null +++ b/ethcan.py @@ -0,0 +1,277 @@ +import socket, select +from socket import AF_PACKET, SOCK_RAW, htons, ntohs +from struct import pack, unpack +from time import time, sleep, strftime +import json +import os, traceback +import hub_ctrl, usb +import contextlib + +def ts(): + return strftime('%Y-%m-%d %H:%M:%S') + +class SubCANDevice(object): + def __init__(s, addr, name, dsize = 1): + s.addr = addr + s.name = name + s.dsize = dsize + s.lastval = None + s.lastupd = None + s.lastchg = None + s.actorval = None + s.actorupd = None + s.actorchg = None + +class SubCANBool(SubCANDevice): + def __init__(s, addr, name, falseval, trueval): + SubCANDevice.__init__(s, addr, name, 1) + s.vals = [falseval, trueval] + def __str__(s): + if s.lastval is None: + return 'None' + return s.vals[s.lastval & 1] + def dict(s): + rv = {} + if s.lastval is not None: + rv['raw'] = s.lastval + rv['value'] = bool(s.lastval & 1) + rv['text'] = s.vals[s.lastval & 1] + rv['ts'] = int(s.lastupd) + rv['tschg'] = int(s.lastchg) + return rv + +class SubCANDALI(SubCANDevice): + def __str__(s): + if s.lastval is None or s.actorval is None: + return '' + return 'set: %02x actual: %02x' % (s.actorval, s.lastval) + def dict(s): + rv = {} + if s.lastval is not None: + rv['actual'] = s.lastval + rv['actual_ts'] = int(s.lastupd) + rv['actual_tschg'] = int(s.lastchg) + if s.actorval is not None: + rv['set'] = s.actorval + rv['set_ts'] = int(s.actorupd) + rv['set_tschg'] = int(s.actorchg) + return rv + +devices = [ + SubCANBool(0x100, 'door.right', 'open', 'closed'), + SubCANBool(0x101, 'door.left', 'open', 'closed'), + SubCANBool(0x102, 'door.light', 'triggered', 'normal'), + SubCANBool(0x103, 'door.lock', 'open', 'closed'), + SubCANDALI(0x441, 'dali.lounge_buehne'), + SubCANDALI(0x442, 'dali.lounge_buero'), + SubCANDALI(0x443, 'dali.lounge_bar'), + SubCANDALI(0x444, 'dali.lounge_durchreiche'), +# SubCANDALI(0x47f, 'dali.lswitch'), +] +def find_dev(addr): + for dev in devices: + if addr in range(dev.addr, dev.addr + dev.dsize): + return dev + return None + +class SubCANFrame(object): + def __init__(s, frame): + s.frame = frame + s.dstaddr = frame.eid & 0xfff + + def process(s): + data = s.frame.payload + addr = s.dstaddr + while len(data) > 0: + dev = find_dev(addr) + if dev is None: + addr += 1 + data = data[1:] + else: + val = None + devdata = data[:dev.dsize] + if dev.dsize == 1: + (val, ) = unpack('>B', devdata) + elif dev.dsize == 2: + (val, ) = unpack('>H', devdata) + + s.do_process(dev, devdata, val) + addr += dev.dsize + data = data[dev.dsize:] + + @classmethod + def create(s, frame): + for fclass in s.frametypes: + if frame.sid == fclass.matchsid: + return fclass(frame) + return None + +class SensorFrame(SubCANFrame): + matchsid = 0xe60 + def do_process(s, dev, data, val): + dev.lastupd = s.frame.ts + if val != dev.lastval: + dev.lastchg = s.frame.ts + dev.lastval = val + +class ActorFrame(SubCANFrame): + matchsid = 0xcc0 + def do_process(s, dev, data, val): + dev.actorupd = s.frame.ts + if val != dev.actorval: + dev.actorchg = s.frame.ts + dev.actorval = val + +SubCANFrame.frametypes = [SensorFrame, ActorFrame] + +class MacAddr(object): + def __init__(s, mac): + s.mac = mac + def __str__(s): + return ':'.join(['%02x' % (ord(c),) for c in s.mac]) + +class NotACANFrame(Exception): + pass +class InvalidCANFrame(Exception): + pass + +from collections import namedtuple +StatsTuple = namedtuple('StatsTuple', 'ethstat_tx_overrun, ethstat_tx_ok, ethstat_tx_error, ethstat_tx_fnord, ' + + 'ethstat_rx_overrun, ethstat_rx_ok, ethstat_rx_error, ethstat_lastrxerr, ' + + 'ethstat_again, ethstat_hasherr, ' + + 'mcp2515_errors, mcp2515_rx_ok, mcp2515_tx') + +class Frame(object): + def __init__(s, data): + (dst, src, proto, oui, subp, typ) = unpack('>6s6sH3sHB', data[:20]) + (tsv, ) = unpack('<I', data[20:24]) + s.data = data[24:] + s.dst = MacAddr(dst) + s.src = MacAddr(src) + s.proto = proto + s.ts = time() + s.tsr = tsv / 100. + + if s.proto != 0x88b7 or oui != '\x00\x80\x41' or subp != 0xaaaa or len(data) < 5: + raise NotACANFrame('invalid protocol/OUI/subp or too short') + if typ == 0x01: + stats = [] + data = data[24:] + while len(data) >= 4: + stats.append(unpack('<I', data[:4])[0]) + data = data[4:] + spretty = StatsTuple._make(stats[:len(StatsTuple._fields)]) + print '\033[33m%s stats: %s\033[m' % (ts(), repr(spretty)) + raise NotACANFrame('stats frame') + + if typ != 0x03: + raise NotACANFrame('invalid type %02x' % (typ)) + + (ts2, ) = unpack('<I', s.data[:4]) + (addr, dlc) = unpack('>IB', s.data[4:9]) + s.tsr2 = ts2 / 100. + + s.dlc = dlc & 0x0f + s.is_eid = addr & 0x00080000 + s.is_rtr = bool(dlc & 0x40 if s.is_eid else addr & 0x00100000) + s.addr = addr & 0xffe3ffff + s.sid = addr >> 20 + s.eid = addr & 0x3ffff + + if s.is_eid: + s.addrstr = '%03x-%05x' % (s.sid, s.eid) + else: + s.addrstr = '%03x-XXXXX' % (s.sid) + + if len(data) < 9 + s.dlc: + raise InvalidCANFrame('truncated frame') + s.payload = s.data[9:9+s.dlc] + + def __str__(s): + return '%s <- %s %8.2f d=%.2f %s %s: %s' % ( + s.dst, s.src, s.tsr, s.tsr - s.tsr2, + s.addrstr, 'RTR:' if s.is_rtr else 'norm', + ' '.join(['%02x' % (ord(c), ) for c in s.payload])) + +@contextlib.contextmanager +def USBHandle(dev): + uh = dev.open() + try: + yield uh + finally: + del uh + +def repower_r0ket(): + hubs = hub_ctrl.find_hubs(False, False) + for h in hubs: + if h['dev'].idVendor == 0x050f and h['dev'].idProduct == 0x0003: + break + else: + print 'could not find USB hub!' + return + + with USBHandle(h['dev']) as uh: + feat = hub_ctrl.USB_PORT_FEAT_POWER + index = 1 # port no + + req = usb.REQ_CLEAR_FEATURE + uh.controlMsg(requestType = hub_ctrl.USB_RT_PORT, request = req, value = feat, index = index, buffer = None, timeout = 1000) + print '\033[31;1m%s: port powered off.\033[m' % (ts(), ) + sleep(2) + req = usb.REQ_SET_FEATURE + uh.controlMsg(requestType = hub_ctrl.USB_RT_PORT, request = req, value = feat, index = index, buffer = None, timeout = 1000) + print '\033[31;1m%s: port powered on.\033[m' % (ts(), ) + + with open('/var/log/ethcan.log', 'a') as logf: + logf.write('%s: r0ket powercycled\n' % (ts(), )) + +def main(): + poller = select.poll() + poller.register(s, select.POLLIN) + timeout = 10 + while True: + ev = poller.poll(timeout * 1000) + if len(ev) == 0: + # no stats frame for 10s ... r0ket stuck. + try: repower_r0ket() + except: traceback.print_exc() + timeout = 20 + continue + else: + timeout = 10 + + (data, addr) = s.recvfrom(65536) + if addr[0] != 'br0': + continue + try: + pkt = Frame(data) + except NotACANFrame, e: + if str(e) != 'stats frame': + print e + continue + except InvalidCANFrame, e: + print e + continue + + scf = SubCANFrame.create(pkt) + if scf is not None: + scf.process() + + print ts(), addr[0], pkt + + rd = {} + for d in devices: + rd[d.name] = d.dict() + output_file = '/home/services/http/subcan.json' + with open(output_file + '.new', 'w') as output: + output.write(json.dumps(rd, sort_keys=True, indent=4)) + os.rename(output_file + '.new', output_file) + +if __name__ == '__main__': + ifindex = 3 + + s = socket.socket(AF_PACKET, SOCK_RAW, htons(0x88b7)) + mreq = pack('@iHH8s', ifindex, 0, 6, '\xff\x3a\xf6CAN\x00\x00') + s.setsockopt(263, 1, mreq) + + main() diff --git a/ethcansend.py b/ethcansend.py new file mode 100644 index 0000000..9aaaebd --- /dev/null +++ b/ethcansend.py @@ -0,0 +1,56 @@ +import socket +from socket import AF_PACKET, SOCK_RAW, htons, ntohs +from struct import pack, unpack +from time import time, sleep +import json +import hashlib + +ifindex = 3 + +s = socket.socket(AF_PACKET, SOCK_RAW, htons(0x88b7)) +# mreq = pack('@iHH8s', ifindex, 0, 6, '\xff\x3a\xf6CAN\x00\x00') +# s.setsockopt(263, 1, mreq) + +# (addr, dlc) = unpack('>IB', s.data[:5]) +import sys + +addr = int(sys.argv[1]) +data = '' +for d in sys.argv[2:]: + data += chr(int(d)) +dlc = len(data) +addr = 0xcc080440 + addr + +data = pack('>IB', addr, dlc) + data + '\x00' * (3 + 8 - len(data)) + +src = '\x00\x04\x23\xb6\xde\xe4' +dst = '\xff\x3a\xf6CAN' +# dst = '\xd2r0ket' +proto = 0x88b7 +oui = '\x00\x80\x41' +subp = 0xaaaa +typ = 3 + +data = pack('<BII', typ, 0, 0) + data + +key = ''.join([chr(i) for i in [ + 0x2f, 0x5d, 0xb5, 0xe4, 0x59, 0x6d, 0xc5, 0xf1, 0xb0, 0xf4, + 0xc3, 0xee, 0x8a, 0xc8, 0xff, 0x06, 0xbc, 0x28, 0x54, 0x08, + 0xa6, 0xc2, 0x96, 0x72, 0xd9, 0x0d, 0x22, 0x76, 0xd1, 0x98, + 0x0a, 0xdb, 0xca, 0xfe, 0xba, 0xbe, 0xde, 0xad, 0xbe, 0xef, +]]) + +dgst = hashlib.sha256() +# dgst.update(key) +dgst.update(data) + +data += dgst.digest() + +data = pack('>6s6sH3sH', dst, src, proto, oui, subp) + data + +s.sendto(data, ('bond0.4', 0)) +#sleep(0.1) +#s.sendto(data, ('bond0.4', 0)) +#sleep(0.1) +#s.sendto(data, ('bond0.4', 0)) + diff --git a/hub_ctrl.py b/hub_ctrl.py new file mode 100644 index 0000000..684b3bd --- /dev/null +++ b/hub_ctrl.py @@ -0,0 +1,256 @@ +#! /usr/bin/python + +""" +hub_ctrl.py - a tool to control port power/led of USB hub + +Copyright (C) 2006, 2011 Free Software Initiative of Japan + +Author: NIIBE Yutaka <gniibe@fsij.org> + +This file is a part of Gnuk, a GnuPG USB Token implementation. + +Gnuk 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. + +Gnuk is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +""" + +import usb + +USB_RT_HUB = (usb.TYPE_CLASS | usb.RECIP_DEVICE) +USB_RT_PORT = (usb.TYPE_CLASS | usb.RECIP_OTHER) +USB_PORT_FEAT_RESET = 4 +USB_PORT_FEAT_POWER = 8 +USB_PORT_FEAT_INDICATOR = 22 +USB_DIR_IN = 0x80 # device to host + +COMMAND_SET_NONE = 0 +COMMAND_SET_LED = 1 +COMMAND_SET_POWER = 2 + +HUB_LED_GREEN = 2 + +def find_hubs(listing, verbose, busnum=None, devnum=None, hub=None): + number_of_hubs_with_feature = 0 + hubs = [] + busses = usb.busses() + if not busses: + raise ValueError, "can't access USB" + + for bus in busses: + devices = bus.devices + for dev in devices: + if dev.deviceClass != usb.CLASS_HUB: + continue + + printout_enable = 0 + if (listing + or (verbose + and ((bus.dirname == busnum and dev.devnum == devnumd) + or hub == number_of_hubs_with_feature))): + printout_enable = 1 + + uh = dev.open() + + desc = None + try: + # Get USB Hub descriptor + desc = uh.controlMsg(requestType = USB_DIR_IN | USB_RT_HUB, + request = usb.REQ_GET_DESCRIPTOR, + value = usb.DT_HUB << 8, + index = 0, buffer = 1024, timeout = 1000) + finally: + del uh + + if not desc: + continue + + # desc[3] is lower byte of wHubCharacteristics + if (desc[3] & 0x80) == 0 and (desc[3] & 0x03) >= 2: + # Hub doesn't have features of controling port power/indicator + continue + + if printout_enable: + print "Hub #%d at %s:%03d" % (number_of_hubs_with_feature, + bus.dirname, dev.devnum) + if (desc[3] & 0x03) == 0: + print " INFO: ganged power switching." + elif (desc[3] & 0x03) == 1: + print " INFO: individual power switching." + elif (desc[3] & 0x03) == 2 or (desc[3] & 0x03) == 3: + print " WARN: no power switching." + + if (desc[3] & 0x80) == 0: + print " WARN: Port indicators are NOT supported." + + hubs.append({ 'busnum' : bus.dirname, 'devnum' : dev.devnum, + 'indicator_support' : (desc[3] & 0x80) == 0x80, + 'dev' : dev, 'num_ports' : desc[2] }) + number_of_hubs_with_feature += 1 + + return hubs + +def hub_port_status(handle, num_ports): + print " Hub Port Status:" + for i in range(num_ports): + port = i + 1 + status = handle.controlMsg(requestType = USB_RT_PORT | usb.ENDPOINT_IN, + request = usb.REQ_GET_STATUS, + value = 0, + index = port, buffer = 4, + timeout = 1000) + + print " Port %d: %02x%02x.%02x%02x" % (port, status[3], status[2], + status[1], status[0]), + if status[1] & 0x10: + print " indicator", + if status[1] & 0x08: + print " test" , + if status[1] & 0x04: + print " highspeed", + if status[1] & 0x02: + print " lowspeed", + if status[1] & 0x01: + print " power", + + if status[0] & 0x10: + print " RESET", + if status[0] & 0x08: + print " oc", + if status[0] & 0x04: + print " suspend", + if status[0] & 0x02: + print " enable", + if status[0] & 0x01: + print " connect", + + print + +import sys + +COMMAND_SET_NONE = 0 +COMMAND_SET_LED = 1 +COMMAND_SET_POWER = 2 +HUB_LED_GREEN = 2 + +def usage(progname): + print >> sys.stderr, """Usage: %s [{-h HUBNUM | -b BUSNUM -d DEVNUM}] + [-P PORT] [{-p [VALUE]|-l [VALUE]}] +""" % progname + +def exit_with_usage(progname): + usage(progname) + exit(1) + +if __name__ == '__main__': + busnum = None + devnum = None + listing = False + verbose = False + hub = None + port = 1 + cmd = COMMAND_SET_NONE + + if len(sys.argv) == 1: + listing = True + else: + try: + while len(sys.argv) >= 2: + option = sys.argv[1] + sys.argv.pop(1) + if option == '-h': + if busnum != None or devnum != None: + exit_with_usage(sys.argv[0]) + hub = int(sys.argv[1]) + sys.argv.pop(1) + elif option == '-b': + busnum = int(sys.argv[1]) + sys.argv.pop(1) + elif option == '-d': + devnum = int(sys.argv[1]) + sys.argv.pop(1) + elif option == '-P': + port = int(sys.argv[1]) + sys.argv.pop(1) + elif option == '-l': + if cmd != COMMAND_SET_NONE: + exit_with_usage(sys.argv[0]) + if len(sys.argv) > 1: + value = int(sys.argv[1]) + sys.argv.pop(1) + else: + value = HUB_LED_GREEN + cmd = COMMAND_SET_LED + elif option == '-p': + if cmd != COMMAND_SET_NONE: + exit_with_usage(sys.argv[0]) + if len(sys.argv) > 1: + value = int(sys.argv[1]) + sys.argv.pop(1) + else: + value = 0 + cmd = COMMAND_SET_POWER + elif option == '-v': + verbose = True + if len(sys.argv) == 1: + listing = True + else: + exit_with_usage(sys.argv[0]) + except: + exit_with_usage(sys.argv[0]) + + if ((busnum != None and devnum == None) + or (busnum == None and devnum != None)): + exit_with_usage(sys.argv[0]) + + if hub == None and busnum == None: + hub = 0 # Default hub = 0 + + if cmd == COMMAND_SET_NONE: + cmd = COMMAND_SET_POWER + + hubs = find_hubs(listing, verbose, busnum, devnum, hub) + if len(hubs) == 0: + print >> sys.stderr, "No hubs found." + exit(1) + if listing: + exit(0) + + if hub == None: + for h in hubs: + if h['busnum'] == busnum and h['devnum'] == devnum: + dev_hub = h['dev'] + nports = h['num_ports'] + else: + dev_hub = hubs[hub]['dev'] + nports = hubs[hub]['num_ports'] + + uh = dev_hub.open() + if cmd == COMMAND_SET_POWER: + feature = USB_PORT_FEAT_POWER + index = port + if value: + request = usb.REQ_SET_FEATURE + else: + request = usb.REQ_CLEAR_FEATURE + else: + request = usb.REQ_SET_FEATURE + feature = USB_PORT_FEAT_INDICATOR + index = (value << 8) | port + if verbose: + print "Send control message (REQUEST=%d, FEATURE=%d, INDEX=%d) " % (request, feature, index) + + uh.controlMsg(requestType = USB_RT_PORT, request = request, value = feature, + index = index, buffer = None, timeout = 1000) + if verbose: + hub_port_status(uh,nports) + + del uh diff --git a/subbot.py b/subbot.py new file mode 100644 index 0000000..7462bce --- /dev/null +++ b/subbot.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python + +import irc.bot +import json, time +import traceback + +import settings + +svcpw = settings.bot_svcpw + +class TestBot(irc.bot.SingleServerIRCBot): + def __init__(self, channel, nickname, server, port=6667): + irc.bot.SingleServerIRCBot.__init__(self, [(server, port)], nickname, nickname) + self.channel = channel + + def on_nicknameinuse(self, c, e): + c.nick(c.get_nickname() + "_") + + def on_welcome(self, c, e): + c.privmsg('NickServ', 'identify sublab|open %s' % (svcpw, )) + self.refresh() + c.join(self.channel) + + def refresh(self): + c = self.connection + havenick = c.get_nickname() + curtime = time.time() + + try: + sdata = json.load(file('/home/services/http/subcan.json', 'r')) + door = sdata[u'door.lock'] + + if door[u'ts'] < curtime - 120: + wantnick = 'sublab|error' + elif door[u'text'] == u'closed': + wantnick = 'sublab|closed' + elif door[u'text'] == u'open': + wantnick = 'sublab|open' + else: + wantnick = 'sublab|error' + except Exception, e: + traceback.print_exc() + wantnick = 'sublab|error' + + if wantnick != havenick: + print int(curtime), 'nick:', havenick, '->', wantnick + c.privmsg('NickServ', 'ghost %s %s' % (wantnick, svcpw)) + c.privmsg('NickServ', 'release %s %s' % (wantnick, svcpw)) + c.nick(wantnick) + self.ircobj.execute_delayed(5, self.refresh) + +def main(): + bot = TestBot('#sublab', 'sublab|closed', '172.22.24.1', 6667) + bot.start() + +if __name__ == "__main__": + main() diff --git a/subcan.py b/subcan.py new file mode 100644 index 0000000..40d476d --- /dev/null +++ b/subcan.py @@ -0,0 +1,50 @@ +import json +import sys +from lxml import etree +from time import time, sleep + +def run(): + doc = etree.parse('subcan.svg') + data = json.loads(file('/home/services/http/subcan.json').read()) + + for i in ['door.left', 'door.right', 'door.lock']: + cond = '%s=%s' % (i, data[i]['text']) + print cond + elems = doc.xpath('//svg:g[@inkscape:label="%s"]' % (cond,), + namespaces = { + 'svg': 'http://www.w3.org/2000/svg', + 'inkscape': 'http://www.inkscape.org/namespaces/inkscape', + }) + for e in elems: + e.set('style', 'display:inline') + + for i in data.keys(): + if not i.startswith('dali.'): + continue + for j in ['set', 'actual']: + #for i in [('dali.lounge', 'set'), ('dali.lounge', 'actual')]: + elems = doc.xpath('//svg:text[@id="%s_%s"]/svg:tspan' % (i, j), + namespaces = { + 'svg': 'http://www.w3.org/2000/svg', + }) + print 'elems for %s_%s: %d' % (i, j, len(elems)) + for e in elems: + try: + text = data[i][j] + if data[i][j + '_ts'] > time() - 300.: + text = u'%d%%' % (text / 254. * 100.) + else: + text = u'<?>' + e.text = text + except KeyError: + print 'error processing \'%s_%s\'' % (i, j) + e.text = u'<?>' + + file('/home/services/http/subcan.svg', 'w').write(etree.tostring(doc)) + +while True: + try: + run() + except Exception: + sys.excepthook(*sys.exc_info()) + sleep(10.) |