import subprocess import sys, inspect, os import wiringpi import time from datetime import datetime from time import sleep import lirc import random import mpd from pyudmx import pyudmx import talkey # HTTP Server from twisted.web import server, resource from twisted.internet import reactor class Simple(resource.Resource): isLeaf = True def render_GET(self, request): statustext = "" if inUse: statustext = "Disco in use!" else: statustext = "Ready to Disco!" html = '
%s
' % statustext return html.encode('utf-8') # GPIO pin_kugel = 2 # Input: dico ball + DMX on/off pin_sun = 4 # Input: light bulb on/off pin_pir = 0 # Output: PIR sensor pin_door = 11 # Output: door open/closed sensor # for system stuff dmxScenes = { "fadecolors":[255,255,255,255,255,192], "plain-red":[255,255,0,0,0,0], "strobe":[190,255,255,255,0,0], "nini":[120,0,255,255,0,224], "black":[0,0,0,0,0,0] } bye_sayings = [ "Goodbye!", "Bye!", "Bye bye!", "See you!", #"Ciao!", "Have a nice day!", "Thank's for using!", #"I'm off!", "Take it easy!", "I look forward to our next meeting!", "Take care!", "See you later!", "This was nice. See you!", "Peace!" ] dmxUserScenes = [ [255,255,255,255,255,192], [255,0,180,180,0,0], [255,255,0,0,0,0], [255,0,255,0,0,0], [255,0,0,255,0,0], [190,255,255,255,0,0], [120,0,255,255,0,224] ] # no strobe etc. dmxStartupScenes = [ [255,255,255,255,255,192], [255,0,180,180,0,0], [255,255,0,0,0,0], [255,0,255,0,0,0], [255,0,0,255,0,0], [120,0,255,255,0,224] ] # Set a dmx scene by name def setDmxScene(scene): # a universe of zeros cv = [0 for v in range(0, 512)] errorcode = [240,255,0,0,0,0] for index, val in enumerate(dmxScenes.get(scene,errorcode)): cv[index] = val dev.send_multi_value(1, cv) # Set random startup dmx scene def setStartupDmxScene(): # a universe of zeros cv = [0 for v in range(0, 512)] errorcode = [240,255,0,0,0,0] # get a random scene index scene = random.choice(list(enumerate(dmxStartupScenes)))[0] print(scene) for index, val in enumerate(dmxStartupScenes[scene]): cv[index] = val dev.send_multi_value(1, cv) # Switch betweeb user dmx scenes def setUserDmxScene(): # loop through scenes global dmxScene if dmxScene < len(dmxUserScenes)-1: dmxScene += 1 else: dmxScene = 0 # setup the universe cv = [0 for v in range(0, 512)] for index, val in enumerate(dmxUserScenes[dmxScene]): cv[index] = val dev.send_multi_value(1, cv) def setKugel(state): if state == 'on': wiringpi.digitalWrite(pin_kugel, 0) if state == 'off': wiringpi.digitalWrite(pin_kugel, 1) def setSun(state): if state == 'off': wiringpi.digitalWrite(pin_sun, 0) if state == 'on': wiringpi.digitalWrite(pin_sun, 1) def startMusic(playlist, single=False, shuffle=True, repeat=True): try: client.clear() # clear playlist except Exception: client.connect("localhost", 6600) client.clear() # clear playlist client.add(playlist) # add file/directory to playlist if shuffle: client.shuffle() # shuffle playlist if repeat: client.repeat(1) # set playback mode repeat else: client.repeat(0) # set playback mode repeat if single: client.repeat(0) # set playback mode repeat client.single(1) # set playback mode single else: client.single(0) # set playback mode single client.setvol(80)# set volume client.play() # play def getNewestPodcastUrl(xml): podcast = xml podcast_string = requests.get(podcast).text tree = ET.fromstring(podcast_string) return tree.find('.//enclosure').get('url') def playMusic(): try: client.play() except Exception: client.connect("localhost", 6600) client.play() def pauseMusic(): try: client.pause() except Exception: client.connect("localhost", 6600) client.pause() def stopMusic(): try: client.stop() except Exception: client.connect("localhost", 6600) client.stop() def nextSong(): try: client.next() except Exception: client.connect("localhost", 6600) client.next() def previousSong(): try: client.previous() except Exception: client.connect("localhost", 6600) client.previous() def muteMusic(): global uservolume if getMpdVolume() != 0: # if not muted setMpdVolume(0) else: setMpdVolume(uservolume) def changeVolume(change=5): global uservolume global volume newvol = uservolume + change if newvol > 100: newvol = 100 if newvol < 0: newvol = 0 try: client.setvol(newvol) except Exception: client.connect("localhost", 6600) client.setvol(newvol) uservolume = newvol volume = newvol def setMode(string): global mode global inUse mode = string if mode == "off": inUse = False def setDiscoMode(startup=False): setKugel('on') if startup: setStartupDmxScene() else: setUserDmxScene() sleep(0.3) setSun('off') setMode('disco') def getMpdVolume(): try: vol = int(client.status()['volume']) except Exception: client.connect("localhost", 6600) vol = int(client.status()['volume']) if vol != 0: #only if not muted global uservolume uservolume = vol return vol def setMpdVolume(vol): try: client.setvol(vol) except Exception: client.connect("localhost", 6600) client.setvol(vol) if vol != 0: #only if not muted global uservolume uservolume = vol return True def seek(secs): try: client.seekcur(secs) except Exception: client.connect("localhost", 6600) client.seekcur(secs) return True def getTrackInfo(): try: currentsong = client.currentsong() except Exception: client.connect("localhost", 6600) currentsong = client.currentsong() print(currentsong) volume = getMpdVolume() setMpdVolume(10) try: tts.say(currentsong['artist'] + ', ' + currentsong['title']) except Exception: tts.say('Willkommen am Discoklo!', 'de') setMpdVolume(volume) def setWorkingMode(): setSun('on') sleep(0.3) setKugel('off') setDmxScene('black') setMode('work') def startTimeoutCountdown(): global lastUsed global inUse tts.say('Timeout in') countdown = [5,4,3,2,1] #10,9,8,7,6, for sec in countdown: tts.say(str(sec)) sleep(0.5) remotesignal = lirc.nextcode() if wiringpi.digitalRead(pin_pir) == 1 or remotesignal: lastUsed = time.time() tts.say('Timeout cancelled!') inUse = True toilet = lirc.nextcode() break if not inUse: tts.say('Shutting down now.', 'en') closeService() def closeService(sleepsecs=0): setSun('off') sleep(0.3) setKugel('off') setDmxScene('black') for x in range(0, 20): changeVolume(-5) sleep(0.1) stopMusic() inUseBefore = False # Pfusch pfusch! setMode('off') sleep(sleepsecs) # function when user arrives def initService(): startMusic('0', True) # start intro music setDiscoMode(True) global volume global defaultvolume global uservolume try: client.setvol(defaultvolume) except Exception: client.connect("localhost", 6600) client.setvol(defaultvolume) volume = defaultvolume uservolume = defaultvolume global starttime starttime = time.time() def say(text, lang="en"): originalvol = getMpdVolume() setMpdVolume(10) tts.say(text, lang) setMpdVolume(originalvol) def setOnOff(): global mode stopMusic() if mode is not 'work': setWorkingMode() else: initService() def doorShutdown(): tts.say(random.choice(bye_sayings), "en") # sleep to give user some time to close the door # (pir sensor also stays up for 2 sec) closeService(5) def bootstrap(): wiringpi.wiringPiSetup() wiringpi.pinMode(pin_kugel, 1) # set Relay Disokugel mode to OUTPUT wiringpi.pinMode(pin_door, 0) # set Circuit Door mode to INPUT wiringpi.pinMode(pin_sun, 1) # set Relay Sun mode to OUTPUT # TODO: Set pin! wiringpi.pinMode(pin_pir, 0) # set PIR Sensor mode to INPUT def timestamp(stamp=time.time()): return datetime.fromtimestamp(stamp).strftime('%Y-%m-%d %H:%M:%S') dmxScene = 0 bootstrap() dev = pyudmx.uDMXDevice() dev.open() client = mpd.MPDClient() client.connect("localhost", 6600) tts = talkey.Talkey( preferred_languages=['en'], engine_preference=['pico'], ) site = server.Site(Simple()) reactor.listenTCP(8080, site) reactor.startRunning(False) lirc.init("disco", "~/discobert/lircrc", blocking=False) starttime = time.time() # helper for doorshutdown lastUsed = time.time() # helper for timeout inUse = False # is toilet in use? inUseBefore = False # helper to check statechanges mode = "off" # can be: disco, work, off timeout = 5 * 60 - 5 # timeout since last user interaction defaultvolume = 90 # Volume when user enters the toilet volume = 90 # Global for actual volume uservolume = 90 # Global for user volume (ignores mute state) setSun('off') print(timestamp(), "Ready!") # Main event loop ... while True: sleep(0.25) pirstate = wiringpi.digitalRead(pin_pir) doorstate = wiringpi.digitalRead(pin_door) #print('pirstate: ', pirstate) #print('doorstate: ', doorstate) # 0 => door closed # 1 => door open if doorstate == 1: # don't do a doorShutdown when user comes in # or when door stays open after user left if (time.time() > (starttime + 15) and inUse == True): doorShutdown() if pirstate == 1: lastUsed = time.time() inUse = True else: if(time.time() > lastUsed + timeout): inUse = False remotesignal = lirc.nextcode() #print('remotesignal: ', remotesignal) if remotesignal: lastUsed = time.time() # user is active! inUse = True for code in remotesignal: print('received code:', code) if(code == "mode_disco"): setDiscoMode() if(code == "mode_work"): setWorkingMode() if(code == "mode_power"): setOnOff() if(code == "mode_music_play_1"): startMusic('1') if(code == "mode_music_play_2"): startMusic('2') if(code == "mode_music_play_3"): startMusic('3') if(code == "mode_music_play_4"): startMusic('4') if(code == "mode_music_play_5"): startMusic('5') if(code == "mode_play_fm4"): startMusic('http://185.85.29.141:8000') if(code == "mode_play_oe1"): startMusic('http://185.85.29.142:8000') if(code == "mode_music_previous"): previousSong() if(code == "mode_music_next"): nextSong() if(code == "mode_music_play"): playMusic() if(code == "mode_music_stop"): stopMusic() if(code == "mode_music_mute"): muteMusic() if(code == "mode_music_pause"): pauseMusic() if(code == "mode_music_rewind"): seek('-30') if(code == "mode_music_back"): seek('-5') if(code == "mode_music_forward"): seek('+5') if(code == "mode_music_fastforward"): seek('+30') if(code == "mode_volume_up"): changeVolume(5) if(code == "mode_volume_down"): changeVolume(-5) if(code == "mode_music_info"): getTrackInfo() if(code == "mode_record"): say("I'm sorry, I'm afraid I can't do that!") if(code == "mode_news"): oejournalUrl = getNewestPodcastUrl('https://files.orf.at/podcast/oe1/oe1_journale.xml') startMusic(oejournalUrl) if(inUseBefore != inUse): print(timestamp(), "State change inUse:", inUseBefore, inUse) if inUse: initService() else: startTimeoutCountdown() inUseBefore = inUse # Webserver reactor.iterate() lirc.deinit() # Clean up lirc dev.close()