Personal tools

Spacestate Switch

From Frack - Hackerspace Friesland

Jump to: navigation, search
Zie het artikel Dit project is niet meer actief. Zie Spacestate Switch v2 voor de huidige vervanger.
Project: Spacestate Switch
Deurswitch achter paneel.jpg
Status voltooid
Betrokkenen ,
Kennisgebied(en) Python
Afgeleide projecten Spacestate Switch v2
ProjectoverzichtProject toevoegen

Een deurschakelaar in het slot van de toegangsdeur gebaseerd op ethernet. Dit om de staat van de space automatisch te detecteren en veranderingen naar de online tracker te sturen.

Omdat we niets mogen verbouwen aan de space heb ik eens zitten denken aan andere mogelijkheden waarop we kunnen zien of de space open is. Na wat overwegen en bedenken kwam ik tot de conclusie dat er vast een trigger op het slot van de deur van de space gezet kon worden, eentje waarvoor niets gesloopt hoeft te worden.

Ideeën

Er waren een aantal methoden om een trigger op het slot te plaatsen zonder te hoeven slopen. Dit zou kunnen door middel van een metaal-detector achtig ding of met van die batterij veertjes in het slot-gedeelte van het kozijn die dan kortgesloten worden door het slot. Deze laatste optie was het makkelijkste in te bouwen, en daarvoor had ik de volgende ideeën om hem "uit te lezen":

  • Via een twee-pins kabeltje en een microcontroller.
  • Met een netwerkkabel en een switchpoort op een managed switch, link up/down detectie gebruiken.

De switch

Ik ben uiteindelijk voor de tweede optie gegaan omdat de managed switch reeds aanwezig is en een microcontroller er apart voor geprogrammeerd en aangeschaft moest worden.

Het doel van de netwerkkabel was om een link-up/link-down event te genereren op de switchpoort waar de kabel in zit. Ik heb van denz gehoord dat Hack42 ook een dergelijk systeem gebruiken voor de spacestate, dit is simpelweg te doen door de volgende pins van een UTP met elkaar te kortsluiten:

  • Pin 1 (oranje-wit) met Pin 3 (groen-wit) - Tx+ en Rx+
  • Pin 2 (oranje) met Pin 6 (groen) - Tx- en Rx-

Een van de twee pins kan permanent kortgesloten zijn, de andere pins kunnen kortgesloten worden door de deur.

Resultaat

Dit heeft geresulteerd in de volgende contraptie:

Spacestate switch01.jpg

Deze contraptie heb ik meegenomen naar de space en wonder boven wonder paste het meteen in de deur zonder modificaties! Dit is het resultaat:

Spacestate switch02.jpgSpacestate switch03.jpgSpacestate switch04.jpg

Nadat de "schakelaar" in de deur gezet was, even ter test een laptop aangesloten. Deur niet op slot gaf inderdaad netjes link-down, met de deur op slot gedraait kwam de link netjes op en negotiate die naar 100Mbit Full-duplex!

Kortom: Het werkt!!!

Gebruik

Failbaitr heeft geholpen met het uitzoeken welke switchpoort er beschikbaar was: Dit is poort 22 geworden (Kabel D-1-22, de gele die boven de deur hangt). De deurswitch is hierop aangesloten en de barpc is ingesteld dat deze via SNMP ook bij de switch kan. Hierna heb ik de volgende twee scripts op de BarPC gezet voor het automatisch switchen van de spacestate:

  • changespacestate.py, dit script praat met de Frack Spacestate API. Los gedraait met als keyword 'open' of 'close' checkt de state en verandert deze indien die niet klopt.
  • snmpdoorif.py, dit script leest de switch uit via SNMP, deze heeft changespacestate.py als module geïmporteerd en roept enkel de state check en de statechange functies aan.

Het script snmpdoorif.py is in de crontab gezet van de BarPC, deze draait iedere minuut. Met behulp van de sleutel van Iisschots even getest en het werkt!

Nooddeur

De nooduitgang van het gereedschapshok heeft een identieke schakelaar gekregen. Deze is aangesloten op poort 7 van switch #2 (Kabel D-2-7). Hiervoor is het snmpdoorif.py script uit de repository licht aangepast en draait nu op de server om de poort in de gaten te houden. Als de link down gaat word er een SpaceAnnounce message gegenereerd in het subdomein x02. In de FrackBot zit een plugin die deze announcements opvangt en daarop reageert.

Port statistieken

Op de space server draait inmiddels een MRTG proces. Deze neemt daarin ook de spacestate switch mee en dat levert een interessante grafiek op. De auto-sense van de switch genereerd genoeg verkeer om een open/close grafiek te maken: Zodra de space gesloten is er verkeer op de poort te zien. We hebben Space-state history op deze manier!

WAARSCHUWING

Icoon waarschuwing geel.svg Als de switch PoE (Power over Ethernet) ondersteunt, is het verstandig dit uit te schakelen voor de poort waar de deurschakelaar aan hangt!

In theorie zou het goed moeten gaan als de gebruikte PoE techniek IEEE 802.3af is, deze schakelt de stroom pas in als deze een 25k Ohm weerstand detecteerd. Echter bij bijvoorbeeld Cisco Systems hun eigen Inline Power implementatie word de detectie gedaan door een 340kHz toon op de TX te zetten. Als deze terug komt verwacht de cisco switch dat er een PoE device is en schakelt deze de 48v/15,4 watt powersupply in. Deze staat dan effectief in kortsluiting op de switch. DO NOT WANT!

Kortom: Preventief Power over Ethernet disablen op de poort waar de deur aan hangt!

Scripts

snmpdoorif.py

import netsnmp
import sys
import ConfigParser
import os
import changespacestate
 
def GetIfStatus(oid, host, community):
  """ This polls the requested OID and returns True if the interface is UP."""
  if netsnmp.snmpget(netsnmp.Varbind(oid), Version = 1, DestHost = host,
                         Community=community)[0] == '1':
    return True
  return False
 
def DoorPoller(conffile):
  """ The doorpoller, reads the specified configfile and then uses that data to
  get the interface status of a distinct switchport and compares it's state with
  the spacesstate. It changes the spacestate accordingly. """
  config = ConfigParser.RawConfigParser()
  config.read(conffile)
  ifstate = GetIfStatus(config.get('snmp', 'oid'),
                        config.get('snmp', 'host'),
                        config.get('snmp', 'community'))
  space = changespacestate.DoSpaceStateCheck(config.get('space', 'statefile'))
  if ifstate == space:
    newstate = '1'
    if ifstate:
      newstate = '0'
    changespacestate.DoChangeSpaceState(config.get('space', 'stateurl'),
                                        config.get('space', 'statefile'),
                                        config.get('space', 'key'), newstate)
 
if __name__ == '__main__':
  try:
    CONF = sys.argv[1]
    MSG = "ERROR: configfile %s not found." % CONF
  except IndexError:
    # WARNING: This does NOT work from CRON, when running from CRON always use
    # the full config path to avoid it from trying the default value.
    CONF = os.path.expanduser("~/.snmpdoorif.conf")
    MSG = "ERROR: Default config (%s) not found, please specify a file." % CONF
  if os.path.isfile(CONF):
    DoorPoller(CONF)
  else:
    print "%s\r\nUsage: %s [conffile]" % (MSG, sys.argv[0])

changespacestate.py

import urllib2
import urllib
import sys
 
STATEURL = "http://frack.nl/spacestate/"
STATEFILE = "http://frack.nl/spacestate/status.txt"
KEY = "password"
 
 
def DoChangeSpaceState(stateurl, statefile, key, newstate):
  """ This function closes/opens the space. Returns True if successful """
  opts = { 'pass': key, 'newstate': newstate }
  reqstate = False
  if newstate == "1":
    reqstate = True
  try:
    urllib2.urlopen(stateurl, urllib.urlencode(opts), 10).readlines()
    if DoSpaceStateCheck(statefile) == reqstate:
      return True
  except urllib2.URLError, (err):
    print "ERROR: Failed to open the URL (%s)" % err
  return False
 
def DoSpaceStateCheck(statefile):
  """ Checks if the space is opened (returns True if yes). """
  try:
    if urllib2.urlopen(statefile, timeout=10).readlines()[0] == '1':
      return True
  except urllib2.URLError, (err):
    print "ERROR: Failed to open the URL (%s)" % err
  return False
 
def AutoChangeSpaceState(stateurl, statefile, key, newstate):
  """ Main function that calls the other functions in order. """
  reqstate = False
  if newstate == "1":
    reqstate = True
  if DoSpaceStateCheck(statefile) == reqstate:
    return "INFO: Space is already in the requested state."
  else:
    if DoChangeSpaceState(stateurl, statefile, key, newstate):
      return "INFO: Spacestate has been changed as requested."
    else:
      return "WARNING: Failed to change the spacestate!"
 
if __name__ == '__main__':
  try:
    ARGS = sys.argv[1]
  except IndexError:
    print "Usage: %s [open|close]" % sys.argv[0]
    sys.exit(1)
 
  if ARGS == "open":
    print AutoChangeSpaceState(STATEURL, STATEFILE, KEY, "1")
  elif ARGS == "close":
    print AutoChangeSpaceState(STATEURL, STATEFILE, KEY, "0")
  else:
    print "Usage: %s [open|close]" % sys.argv[0]