import subprocess import sys, inspect, os import wiringpi import time from datetime import datetime from time import sleep import lirc import random import mpd import requests from xml.etree import ElementTree as ET 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 = "true" else: statustext = "false" html = '[{"inuse":"%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 inactiveShutdown(): 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! hadUserInteraction = False # reset variable 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) 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) hadUserInteraction = False # setSun('off') print(timestamp(), "Ready!") lirc.init("disco", "~/discobert/lircrc", blocking=False) # 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: # Auto timeout if(time.time() > lastUsed + timeout and not hadUserInteraction): inUse = False remotesignal = lirc.nextcode() print('remotesignal: ', remotesignal) if remotesignal: lastUsed = time.time() # user is active! hadUserInteraction = True 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: inactiveShutdown() #startTimeoutCountdown() inUseBefore = inUse # Webserver reactor.iterate() lirc.deinit() # Clean up lirc dev.close()