You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

discobert.py 11 KiB

5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
5 jaren geleden
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. import subprocess
  2. import sys, inspect, os
  3. import wiringpi
  4. import time
  5. from datetime import datetime
  6. from time import sleep
  7. import lirc
  8. import random
  9. import mpd
  10. from pyudmx import pyudmx
  11. import talkey
  12. # HTTP Server
  13. from twisted.web import server, resource
  14. from twisted.internet import reactor
  15. class Simple(resource.Resource):
  16. isLeaf = True
  17. def render_GET(self, request):
  18. statustext = ""
  19. if inUse:
  20. statustext = "Disco in use!"
  21. else:
  22. statustext = "Ready to Disco!"
  23. html = '<html><body style="min-height:100vh;display:flex;flex-grow:1;align-items:center;justify-content:center;"><div style="display:flex;font-size:10vw">%s</div></body></html>' % statustext
  24. return html.encode('utf-8')
  25. # GPIO
  26. pin_kugel = 2
  27. pin_sun = 4
  28. pin_pir = 0
  29. pin_door = 11
  30. dmxScenes = {
  31. "fadecolors":[255,255,255,255,255,192],
  32. "plain-red":[255,255,0,0,0,0],
  33. "strobe":[190,255,255,255,0,0],
  34. "nini":[120,0,255,255,0,224],
  35. "black":[0,0,0,0,0,0]
  36. }
  37. bye_sayings = [
  38. "Goodbye!",
  39. "Bye!",
  40. "Bye bye!",
  41. "See you!",
  42. #"Ciao!",
  43. "Have a nice day!",
  44. "Thank's for using!",
  45. #"I'm off!",
  46. "Take it easy!",
  47. "I look forward to our next meeting!",
  48. "Take care!",
  49. "See you later!",
  50. "This was nice. See you!",
  51. "Peace!"
  52. ]
  53. dmxUserScenes = [
  54. [255,255,255,255,255,192],
  55. [255,0,180,180,0,0],
  56. [255,255,0,0,0,0],
  57. [255,0,255,0,0,0],
  58. [255,0,0,255,0,0],
  59. [190,255,255,255,0,0],
  60. [120,0,255,255,0,224]
  61. ]
  62. def setDmxScene(scene):
  63. cv = [0 for v in range(0, 512)]
  64. errorcode = [240,255,0,0,0,0]
  65. for index, val in enumerate(dmxScenes.get(scene,errorcode)):
  66. cv[index] = val
  67. dev.send_multi_value(1, cv)
  68. def setUserDmxScene():
  69. global dmxScene
  70. if dmxScene < len(dmxUserScenes)-1:
  71. dmxScene += 1
  72. else:
  73. dmxScene = 0
  74. cv = [0 for v in range(0, 512)]
  75. for index, val in enumerate(dmxUserScenes[dmxScene]):
  76. cv[index] = val
  77. dev.send_multi_value(1, cv)
  78. def setKugel(state):
  79. if state == 'on':
  80. wiringpi.digitalWrite(pin_kugel, 0)
  81. if state == 'off':
  82. wiringpi.digitalWrite(pin_kugel, 1)
  83. def setSun(state):
  84. if state == 'off':
  85. wiringpi.digitalWrite(pin_sun, 0)
  86. if state == 'on':
  87. wiringpi.digitalWrite(pin_sun, 1)
  88. def startMusic(playlist, single=False, shuffle=True, repeat=True):
  89. try:
  90. client.clear() # clear playlist
  91. except Exception:
  92. client.connect("localhost", 6600)
  93. client.clear() # clear playlist
  94. client.add(playlist) # add file/directory to playlist
  95. if shuffle:
  96. client.shuffle() # shuffle playlist
  97. if repeat:
  98. client.repeat(1) # set playback mode repeat
  99. else:
  100. client.repeat(0) # set playback mode repeat
  101. if single:
  102. client.repeat(0) # set playback mode repeat
  103. client.single(1) # set playback mode single
  104. else:
  105. client.single(0) # set playback mode single
  106. client.setvol(80)# set volume
  107. client.play() # play
  108. def stopMusic():
  109. try:
  110. client.stop()
  111. except Exception:
  112. client.connect("localhost", 6600)
  113. client.stop()
  114. def muteMusic():
  115. global uservolume
  116. if getMpdVolume() != 0: # if not muted
  117. setMpdVolume(0)
  118. else:
  119. setMpdVolume(uservolume)
  120. def changeVolume(change=5):
  121. global uservolume
  122. global volume
  123. newvol = uservolume + change
  124. if newvol > 100:
  125. newvol = 100
  126. if newvol < 0:
  127. newvol = 0
  128. try:
  129. client.setvol(newvol)
  130. except Exception:
  131. client.connect("localhost", 6600)
  132. client.setvol(newvol)
  133. uservolume = newvol
  134. volume = newvol
  135. def setMode(string):
  136. global mode
  137. global inUse
  138. mode = string
  139. if mode == "off":
  140. inUse = False
  141. def setDiscoMode():
  142. setKugel('on')
  143. setDmxScene('fadecolors')
  144. setUserDmxScene()
  145. sleep(0.3)
  146. setSun('off')
  147. setMode('disco')
  148. def getMpdVolume():
  149. try:
  150. vol = int(client.status()['volume'])
  151. except Exception:
  152. client.connect("localhost", 6600)
  153. vol = int(client.status()['volume'])
  154. if vol != 0: #only if not muted
  155. global uservolume
  156. uservolume = vol
  157. return vol
  158. def setMpdVolume(vol):
  159. try:
  160. client.setvol(vol)
  161. except Exception:
  162. client.connect("localhost", 6600)
  163. client.setvol(vol)
  164. if vol != 0: #only if not muted
  165. global uservolume
  166. uservolume = vol
  167. return True
  168. def getTrackInfo():
  169. try:
  170. currentsong = client.currentsong()
  171. except Exception:
  172. client.connect("localhost", 6600)
  173. currentsong = client.currentsong()
  174. print(currentsong)
  175. volume = getMpdVolume()
  176. setMpdVolume(10)
  177. try:
  178. tts.say(currentsong['artist'] + ', ' + currentsong['title'])
  179. except Exception:
  180. tts.say('Willkommen am Discoklo!', 'de')
  181. setMpdVolume(volume)
  182. def tour():
  183. tts.say("Hello, I'm Discobert!", "en")
  184. sleep(0.3)
  185. tts.say("Press 1 for nice electronic music", "en")
  186. sleep(0.3)
  187. tts.say("Press 2 for hard electronic music", "en")
  188. sleep(0.3)
  189. tts.say("Press 3 for HGichT", "en")
  190. sleep(0.3)
  191. tts.say("Press 4 for a good laugh", "en")
  192. sleep(0.3)
  193. tts.say("Press 5 for more music", "en")
  194. sleep(0.3)
  195. def setWorkingMode():
  196. setSun('on')
  197. sleep(0.3)
  198. setKugel('off')
  199. setDmxScene('black')
  200. setMode('work')
  201. def startTimeoutCountdown():
  202. global lastUsed
  203. global inUse
  204. tts.say('Timeout in')
  205. countdown = [5,4,3,2,1] #10,9,8,7,6,
  206. for sec in countdown:
  207. tts.say(str(sec))
  208. sleep(0.5)
  209. remotesignal = lirc.nextcode()
  210. if wiringpi.digitalRead(pin_pir) == 1 or remotesignal:
  211. lastUsed = time.time()
  212. tts.say('Timeout cancelled!')
  213. inUse = True
  214. toilet = lirc.nextcode()
  215. break
  216. if not inUse:
  217. tts.say('Shutting down now.', 'en')
  218. closeService()
  219. def closeService(sleepsecs=0):
  220. setSun('off')
  221. sleep(0.3)
  222. setKugel('off')
  223. setDmxScene('black')
  224. for x in range(0, 20):
  225. changeVolume(-5)
  226. sleep(0.1)
  227. stopMusic()
  228. setMode('off')
  229. sleep(sleepsecs)
  230. def initService():
  231. startMusic('0', True) # start intro music
  232. setDiscoMode()
  233. global volume
  234. global defaultvolume
  235. global uservolume
  236. try:
  237. client.setvol(defaultvolume)
  238. except Exception:
  239. client.connect("localhost", 6600)
  240. client.setvol(defaultvolume)
  241. volume = defaultvolume
  242. uservolume = defaultvolume
  243. global starttime
  244. starttime = time.time()
  245. def say(text, lang="en"):
  246. originalvol = getMpdVolume()
  247. setMpdVolume(10)
  248. tts.say(text, lang)
  249. setMpdVolume(originalvol)
  250. def setOnOff():
  251. global mode
  252. stopMusic()
  253. if mode is not 'work':
  254. setWorkingMode()
  255. else:
  256. initService()
  257. def doorShutdown():
  258. tts.say(random.choice(bye_sayings), "en")
  259. # sleep to give user some time to close the door
  260. # (pir sensor also stays up for 2 sec)
  261. closeService(5)
  262. def bootstrap():
  263. wiringpi.wiringPiSetup()
  264. wiringpi.pinMode(pin_kugel, 1) # set Relay Disokugel mode to OUTPUT
  265. wiringpi.pinMode(pin_door, 0) # set Circuit Door mode to INPUT
  266. wiringpi.pinMode(pin_sun, 1) # set Relay Sun mode to OUTPUT # TODO: Set pin!
  267. wiringpi.pinMode(pin_pir, 0) # set PIR Sensor mode to INPUT
  268. def timestamp(stamp=time.time()):
  269. return datetime.fromtimestamp(stamp).strftime('%Y-%m-%d %H:%M:%S')
  270. dmxScene = 0
  271. bootstrap()
  272. dev = pyudmx.uDMXDevice()
  273. dev.open()
  274. client = mpd.MPDClient()
  275. client.connect("localhost", 6600)
  276. tts = talkey.Talkey(
  277. preferred_languages=['en'],
  278. engine_preference=['pico'],
  279. )
  280. site = server.Site(Simple())
  281. reactor.listenTCP(8080, site)
  282. reactor.startRunning(False)
  283. lirc.init("disco", "~/discobert/lircrc", blocking=False)
  284. starttime = time.time() # helper for doorshutdown
  285. lastUsed = time.time() # helper for timeout
  286. inUse = False # is toilet in use?
  287. inUseBefore = False # helper to check statechanges
  288. mode = "off" # can be: disco, work, off
  289. timeout = 2 * 60 - 10 # timeout since last user interaction
  290. defaultvolume = 90 # Volume when user enters the toilet
  291. volume = 90 # Global for actual volume
  292. uservolume = 90 # Global for user volume (ignores mute state)
  293. setSun('off')
  294. print(timestamp(), "Ready!")
  295. # Main event loop ...
  296. while True:
  297. sleep(0.25)
  298. pirstate = wiringpi.digitalRead(pin_pir)
  299. doorstate = wiringpi.digitalRead(pin_door)
  300. #print('pirstate: ', pirstate)
  301. # 0 => door closed
  302. # 1 => door open
  303. if doorstate == 1:
  304. if (time.time() > (starttime + 15) and inUse == True):
  305. doorShutdown()
  306. inUseBefore = False # Pfusch pfusch!
  307. if pirstate == 1:
  308. lastUsed = time.time()
  309. inUse = True
  310. else:
  311. if(time.time() > lastUsed + timeout):
  312. inUse = False
  313. remotesignal = lirc.nextcode()
  314. if remotesignal:
  315. lastUsed = time.time() # user is active!
  316. inUse = True
  317. for code in remotesignal:
  318. print('received code:', code)
  319. if(code == "mode_disco"):
  320. setDiscoMode()
  321. if(code == "mode_work"):
  322. setWorkingMode()
  323. if(code == "mode_power"):
  324. setOnOff()
  325. #if(code == "mode_dmx_next"):
  326. if(code == "mode_music_play_1"):
  327. startMusic('1')
  328. if(code == "mode_music_play_2"):
  329. startMusic('2')
  330. if(code == "mode_music_play_3"):
  331. startMusic('3')
  332. if(code == "mode_music_play_4"):
  333. startMusic('4')
  334. if(code == "mode_music_play_5"):
  335. startMusic('5')
  336. if(code == "mode_play_fm4"):
  337. startMusic('http://185.85.29.141:8000')
  338. if(code == "mode_play_oe1"):
  339. startMusic('http://185.85.29.142:8000')
  340. if(code == "mode_music_stop"):
  341. stopMusic()
  342. if(code == "mode_music_mute"):
  343. muteMusic()
  344. if(code == "mode_volume_up"):
  345. changeVolume(5)
  346. if(code == "mode_volume_down"):
  347. changeVolume(-5)
  348. if(code == "mode_music_info"):
  349. getTrackInfo()
  350. if(code == "mode_record"):
  351. say("I'm sorry, I'm afraid I can't do that!")
  352. if(code == "mode_home"):
  353. tour()
  354. if(inUseBefore != inUse):
  355. print(timestamp(), "State change inUse:", inUseBefore, inUse)
  356. if inUse:
  357. initService()
  358. else:
  359. startTimeoutCountdown()
  360. inUseBefore = inUse
  361. # Webserver
  362. reactor.iterate()
  363. lirc.deinit() # Clean up lirc
  364. dev.close()