Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

504 rader
12 KiB

  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 playMusic():
  109. try:
  110. client.play()
  111. except Exception:
  112. client.connect("localhost", 6600)
  113. client.play()
  114. def pauseMusic():
  115. try:
  116. client.pause()
  117. except Exception:
  118. client.connect("localhost", 6600)
  119. client.pause()
  120. def stopMusic():
  121. try:
  122. client.stop()
  123. except Exception:
  124. client.connect("localhost", 6600)
  125. client.stop()
  126. def nextSong():
  127. try:
  128. client.next()
  129. except Exception:
  130. client.connect("localhost", 6600)
  131. client.next()
  132. def previousSong():
  133. try:
  134. client.previous()
  135. except Exception:
  136. client.connect("localhost", 6600)
  137. client.previous()
  138. def muteMusic():
  139. global uservolume
  140. if getMpdVolume() != 0: # if not muted
  141. setMpdVolume(0)
  142. else:
  143. setMpdVolume(uservolume)
  144. def changeVolume(change=5):
  145. global uservolume
  146. global volume
  147. newvol = uservolume + change
  148. if newvol > 100:
  149. newvol = 100
  150. if newvol < 0:
  151. newvol = 0
  152. try:
  153. client.setvol(newvol)
  154. except Exception:
  155. client.connect("localhost", 6600)
  156. client.setvol(newvol)
  157. uservolume = newvol
  158. volume = newvol
  159. def setMode(string):
  160. global mode
  161. global inUse
  162. mode = string
  163. if mode == "off":
  164. inUse = False
  165. def setDiscoMode():
  166. setKugel('on')
  167. setDmxScene('fadecolors')
  168. setUserDmxScene()
  169. sleep(0.3)
  170. setSun('off')
  171. setMode('disco')
  172. def getMpdVolume():
  173. try:
  174. vol = int(client.status()['volume'])
  175. except Exception:
  176. client.connect("localhost", 6600)
  177. vol = int(client.status()['volume'])
  178. if vol != 0: #only if not muted
  179. global uservolume
  180. uservolume = vol
  181. return vol
  182. def setMpdVolume(vol):
  183. try:
  184. client.setvol(vol)
  185. except Exception:
  186. client.connect("localhost", 6600)
  187. client.setvol(vol)
  188. if vol != 0: #only if not muted
  189. global uservolume
  190. uservolume = vol
  191. return True
  192. def seek(secs):
  193. try:
  194. client.seekcur(secs)
  195. except Exception:
  196. client.connect("localhost", 6600)
  197. client.seekcur(secs)
  198. return True
  199. def getTrackInfo():
  200. try:
  201. currentsong = client.currentsong()
  202. except Exception:
  203. client.connect("localhost", 6600)
  204. currentsong = client.currentsong()
  205. print(currentsong)
  206. volume = getMpdVolume()
  207. setMpdVolume(10)
  208. try:
  209. tts.say(currentsong['artist'] + ', ' + currentsong['title'])
  210. except Exception:
  211. tts.say('Willkommen am Discoklo!', 'de')
  212. setMpdVolume(volume)
  213. def tour():
  214. tts.say("Hello, I'm Discobert!", "en")
  215. sleep(0.3)
  216. tts.say("Press 1 for nice electronic music", "en")
  217. sleep(0.3)
  218. tts.say("Press 2 for hard electronic music", "en")
  219. sleep(0.3)
  220. tts.say("Press 3 for HGichT", "en")
  221. sleep(0.3)
  222. tts.say("Press 4 for a good laugh", "en")
  223. sleep(0.3)
  224. tts.say("Press 5 for more music", "en")
  225. sleep(0.3)
  226. def setWorkingMode():
  227. setSun('on')
  228. sleep(0.3)
  229. setKugel('off')
  230. setDmxScene('black')
  231. setMode('work')
  232. def startTimeoutCountdown():
  233. global lastUsed
  234. global inUse
  235. tts.say('Timeout in')
  236. countdown = [5,4,3,2,1] #10,9,8,7,6,
  237. for sec in countdown:
  238. tts.say(str(sec))
  239. sleep(0.5)
  240. remotesignal = lirc.nextcode()
  241. if wiringpi.digitalRead(pin_pir) == 1 or remotesignal:
  242. lastUsed = time.time()
  243. tts.say('Timeout cancelled!')
  244. inUse = True
  245. toilet = lirc.nextcode()
  246. break
  247. if not inUse:
  248. tts.say('Shutting down now.', 'en')
  249. closeService()
  250. def closeService(sleepsecs=0):
  251. setSun('off')
  252. sleep(0.3)
  253. setKugel('off')
  254. setDmxScene('black')
  255. for x in range(0, 20):
  256. changeVolume(-5)
  257. sleep(0.1)
  258. stopMusic()
  259. inUseBefore = False # Pfusch pfusch!
  260. setMode('off')
  261. sleep(sleepsecs)
  262. def initService():
  263. startMusic('0', True) # start intro music
  264. setDiscoMode()
  265. global volume
  266. global defaultvolume
  267. global uservolume
  268. try:
  269. client.setvol(defaultvolume)
  270. except Exception:
  271. client.connect("localhost", 6600)
  272. client.setvol(defaultvolume)
  273. volume = defaultvolume
  274. uservolume = defaultvolume
  275. global starttime
  276. starttime = time.time()
  277. def say(text, lang="en"):
  278. originalvol = getMpdVolume()
  279. setMpdVolume(10)
  280. tts.say(text, lang)
  281. setMpdVolume(originalvol)
  282. def setOnOff():
  283. global mode
  284. stopMusic()
  285. if mode is not 'work':
  286. setWorkingMode()
  287. else:
  288. initService()
  289. def doorShutdown():
  290. tts.say(random.choice(bye_sayings), "en")
  291. # sleep to give user some time to close the door
  292. # (pir sensor also stays up for 2 sec)
  293. closeService(5)
  294. def bootstrap():
  295. wiringpi.wiringPiSetup()
  296. wiringpi.pinMode(pin_kugel, 1) # set Relay Disokugel mode to OUTPUT
  297. wiringpi.pinMode(pin_door, 0) # set Circuit Door mode to INPUT
  298. wiringpi.pinMode(pin_sun, 1) # set Relay Sun mode to OUTPUT # TODO: Set pin!
  299. wiringpi.pinMode(pin_pir, 0) # set PIR Sensor mode to INPUT
  300. def timestamp(stamp=time.time()):
  301. return datetime.fromtimestamp(stamp).strftime('%Y-%m-%d %H:%M:%S')
  302. dmxScene = 0
  303. bootstrap()
  304. dev = pyudmx.uDMXDevice()
  305. dev.open()
  306. client = mpd.MPDClient()
  307. client.connect("localhost", 6600)
  308. tts = talkey.Talkey(
  309. preferred_languages=['en'],
  310. engine_preference=['pico'],
  311. )
  312. site = server.Site(Simple())
  313. reactor.listenTCP(8080, site)
  314. reactor.startRunning(False)
  315. lirc.init("disco", "~/discobert/lircrc", blocking=False)
  316. starttime = time.time() # helper for doorshutdown
  317. lastUsed = time.time() # helper for timeout
  318. inUse = False # is toilet in use?
  319. inUseBefore = False # helper to check statechanges
  320. mode = "off" # can be: disco, work, off
  321. timeout = 2 * 60 - 10 # timeout since last user interaction
  322. defaultvolume = 90 # Volume when user enters the toilet
  323. volume = 90 # Global for actual volume
  324. uservolume = 90 # Global for user volume (ignores mute state)
  325. setSun('off')
  326. print(timestamp(), "Ready!")
  327. # Main event loop ...
  328. while True:
  329. sleep(0.25)
  330. pirstate = wiringpi.digitalRead(pin_pir)
  331. doorstate = wiringpi.digitalRead(pin_door)
  332. #print('pirstate: ', pirstate)
  333. # 0 => door closed
  334. # 1 => door open
  335. if doorstate == 1:
  336. if (time.time() > (starttime + 15) and inUse == True):
  337. doorShutdown()
  338. if pirstate == 1:
  339. lastUsed = time.time()
  340. inUse = True
  341. else:
  342. if(time.time() > lastUsed + timeout):
  343. inUse = False
  344. remotesignal = lirc.nextcode()
  345. if remotesignal:
  346. lastUsed = time.time() # user is active!
  347. inUse = True
  348. for code in remotesignal:
  349. print('received code:', code)
  350. if(code == "mode_disco"):
  351. setDiscoMode()
  352. if(code == "mode_work"):
  353. setWorkingMode()
  354. if(code == "mode_power"):
  355. setOnOff()
  356. #if(code == "mode_dmx_next"):
  357. if(code == "mode_music_play_1"):
  358. startMusic('1')
  359. if(code == "mode_music_play_2"):
  360. startMusic('2')
  361. if(code == "mode_music_play_3"):
  362. startMusic('3')
  363. if(code == "mode_music_play_4"):
  364. startMusic('4')
  365. if(code == "mode_music_play_5"):
  366. startMusic('5')
  367. if(code == "mode_play_fm4"):
  368. startMusic('http://185.85.29.141:8000')
  369. if(code == "mode_play_oe1"):
  370. startMusic('http://185.85.29.142:8000')
  371. if(code == "mode_music_previous"):
  372. previousSong()
  373. if(code == "mode_music_next"):
  374. nextSong()
  375. if(code == "mode_music_play"):
  376. playMusic()
  377. if(code == "mode_music_stop"):
  378. stopMusic()
  379. if(code == "mode_music_mute"):
  380. muteMusic()
  381. if(code == "mode_music_pause"):
  382. pauseMusic()
  383. if(code == "mode_music_rewind"):
  384. seek(-30)
  385. if(code == "mode_music_back"):
  386. seek(-5)
  387. if(code == "mode_music_forward"):
  388. seek(5)
  389. if(code == "mode_music_fastforward"):
  390. seek(30)
  391. if(code == "mode_volume_up"):
  392. changeVolume(5)
  393. if(code == "mode_volume_down"):
  394. changeVolume(-5)
  395. if(code == "mode_music_info"):
  396. getTrackInfo()
  397. if(code == "mode_record"):
  398. say("I'm sorry, I'm afraid I can't do that!")
  399. if(code == "mode_help"):
  400. tour()
  401. if(inUseBefore != inUse):
  402. print(timestamp(), "State change inUse:", inUseBefore, inUse)
  403. if inUse:
  404. initService()
  405. else:
  406. startTimeoutCountdown()
  407. inUseBefore = inUse
  408. # Webserver
  409. reactor.iterate()
  410. lirc.deinit() # Clean up lirc
  411. dev.close()