|
- import subprocess
- import sys, inspect, os
-
- import wiringpi
- import time
- from datetime import datetime
- from time import sleep
-
- from evdev import InputDevice, categorize, ecodes
-
- import random
- import mpd
-
- import requests
- from xml.etree import ElementTree as ET
-
- import evdev
-
- 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')
-
-
- # Preparing IR Device
- def get_ir_device():
- devices = [evdev.InputDevice(path) for path in evdev.list_devices()]
- for device in devices:
- if (device.name == "gpio_ir_recv"):
- print("Using device", device.path, "\n")
- return device
- print("No device found!")
-
- ir_dev = get_ir_device()
-
- remote_keys = {
- 0x100028: 'mode_disco', # phono start
- 0x100029: 'mode_work', # phono stop
- 0x10000f: 'mode_power', # tuner band
- 0x100000: 'mode_music_play_1', # tuner 1
- 0x100001: 'mode_music_play_2', # tuner 2
- 0x100002: 'mode_music_play_3', # tuner 3
- 0x100003: 'mode_music_play_4', # tuner 4
- 0x100004: 'mode_music_play_5', # tuner 5
- 0x100033: 'mode_music_rewind', # tape fast rewind
- 0x100037: 'mode_music_back', # tape slow rewind
- 0x100032: 'mode_music_forward', # tape slow forward
- 0x100034: 'mode_music_fastforward', # tape fast forward
- 0x100038: 'mode_music_stop', # tape stop
- 0x100039: 'mode_music_pause', # tape pause
- 0x10003f: 'mode_music_info', # tape stoprec
- 0x110032: 'mode_music_play', # cd play
- 0x110030: 'mode_music_previous', # cd previous
- 0x110031: 'mode_music_next', # cd next
- 0x110038: 'mode_music_stop', # cd stop
- 0x100025: 'mode_news', # source cd
- 0x10001e: 'mode_play_fm4', # source video 2
- 0x100022: 'mode_play_oe1', # source video 1
- 0x100012: 'mode_volume_up', # vol up
- 0x100013: 'mode_volume_down', # vol down
- 0x100014: 'mode_music_mute', # vol mute
- }
-
-
- 0x100000
-
-
- # 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]
- ]
-
-
- def interpretRemoteInputs():
- events = ir_dev.read()
-
- try:
- ir_event_list = [event.value for event in events]
- print("Received commands:", ir_event_list)
- if ir_event_list and len(ir_event_list) > 0:
- remote_key = ir_event_list[0]
- print(remote_key)
- code = None
- try:
- code = remote_keys[remote_key]
- except KeyError:
- print('received invalid signal: ' + str(remote_key))
-
- lastUsed = time.time() # user is active!
- hadUserInteraction = True
- inUse = True
- print('received code:', str(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('https://orf-live.ors-shoutcast.at/fm4-q2a')
- if(code == "mode_play_oe1"):
- startMusic('https://orf-live.ors-shoutcast.at/oe1-q2a')
-
- 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)
-
-
- except BlockingIOError:
- # ir_event_list = None
- print("No command received.\n")
-
-
-
- # 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("0.0.0.0", 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 != '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()
- print('dmx device: ' + str(dev))
-
- 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:
- print("start new loop round")
- sleep(0.3)
- 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
-
-
- interpretRemoteInputs()
-
- 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()
|