Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

discobert.py 13 KiB

há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
há 5 anos
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  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 # Input: dico ball + DMX on/off
  27. pin_sun = 4 # Input: light bulb on/off
  28. pin_pir = 0 # Output: PIR sensor
  29. pin_door = 11 # Output: door open/closed sensor
  30. # for system stuff
  31. dmxScenes = {
  32. "fadecolors":[255,255,255,255,255,192],
  33. "plain-red":[255,255,0,0,0,0],
  34. "strobe":[190,255,255,255,0,0],
  35. "nini":[120,0,255,255,0,224],
  36. "black":[0,0,0,0,0,0]
  37. }
  38. bye_sayings = [
  39. "Goodbye!",
  40. "Bye!",
  41. "Bye bye!",
  42. "See you!",
  43. #"Ciao!",
  44. "Have a nice day!",
  45. "Thank's for using!",
  46. #"I'm off!",
  47. "Take it easy!",
  48. "I look forward to our next meeting!",
  49. "Take care!",
  50. "See you later!",
  51. "This was nice. See you!",
  52. "Peace!"
  53. ]
  54. dmxUserScenes = [
  55. [255,255,255,255,255,192],
  56. [255,0,180,180,0,0],
  57. [255,255,0,0,0,0],
  58. [255,0,255,0,0,0],
  59. [255,0,0,255,0,0],
  60. [190,255,255,255,0,0],
  61. [120,0,255,255,0,224]
  62. ]
  63. # no strobe etc.
  64. dmxStartupScenes = [
  65. [255,255,255,255,255,192],
  66. [255,0,180,180,0,0],
  67. [255,255,0,0,0,0],
  68. [255,0,255,0,0,0],
  69. [255,0,0,255,0,0],
  70. [120,0,255,255,0,224]
  71. ]
  72. # Set a dmx scene by name
  73. def setDmxScene(scene):
  74. # a universe of zeros
  75. cv = [0 for v in range(0, 512)]
  76. errorcode = [240,255,0,0,0,0]
  77. for index, val in enumerate(dmxScenes.get(scene,errorcode)):
  78. cv[index] = val
  79. dev.send_multi_value(1, cv)
  80. # Set random startup dmx scene
  81. def setStartupDmxScene():
  82. # a universe of zeros
  83. cv = [0 for v in range(0, 512)]
  84. errorcode = [240,255,0,0,0,0]
  85. scene = random.choice(dmxStartupScenes)
  86. for index, val in enumerate(dmxUserScenes[scene]):
  87. cv[index] = val
  88. dev.send_multi_value(1, cv)
  89. # Switch betweeb user dmx scenes
  90. def setUserDmxScene():
  91. # loop through scenes
  92. global dmxScene
  93. if dmxScene < len(dmxUserScenes)-1:
  94. dmxScene += 1
  95. else:
  96. dmxScene = 0
  97. # setup the universe
  98. cv = [0 for v in range(0, 512)]
  99. for index, val in enumerate(dmxUserScenes[dmxScene]):
  100. cv[index] = val
  101. dev.send_multi_value(1, cv)
  102. def setKugel(state):
  103. if state == 'on':
  104. wiringpi.digitalWrite(pin_kugel, 0)
  105. if state == 'off':
  106. wiringpi.digitalWrite(pin_kugel, 1)
  107. def setSun(state):
  108. if state == 'off':
  109. wiringpi.digitalWrite(pin_sun, 0)
  110. if state == 'on':
  111. wiringpi.digitalWrite(pin_sun, 1)
  112. def startMusic(playlist, single=False, shuffle=True, repeat=True):
  113. try:
  114. client.clear() # clear playlist
  115. except Exception:
  116. client.connect("localhost", 6600)
  117. client.clear() # clear playlist
  118. client.add(playlist) # add file/directory to playlist
  119. if shuffle:
  120. client.shuffle() # shuffle playlist
  121. if repeat:
  122. client.repeat(1) # set playback mode repeat
  123. else:
  124. client.repeat(0) # set playback mode repeat
  125. if single:
  126. client.repeat(0) # set playback mode repeat
  127. client.single(1) # set playback mode single
  128. else:
  129. client.single(0) # set playback mode single
  130. client.setvol(80)# set volume
  131. client.play() # play
  132. def playMusic():
  133. try:
  134. client.play()
  135. except Exception:
  136. client.connect("localhost", 6600)
  137. client.play()
  138. def pauseMusic():
  139. try:
  140. client.pause()
  141. except Exception:
  142. client.connect("localhost", 6600)
  143. client.pause()
  144. def stopMusic():
  145. try:
  146. client.stop()
  147. except Exception:
  148. client.connect("localhost", 6600)
  149. client.stop()
  150. def nextSong():
  151. try:
  152. client.next()
  153. except Exception:
  154. client.connect("localhost", 6600)
  155. client.next()
  156. def previousSong():
  157. try:
  158. client.previous()
  159. except Exception:
  160. client.connect("localhost", 6600)
  161. client.previous()
  162. def muteMusic():
  163. global uservolume
  164. if getMpdVolume() != 0: # if not muted
  165. setMpdVolume(0)
  166. else:
  167. setMpdVolume(uservolume)
  168. def changeVolume(change=5):
  169. global uservolume
  170. global volume
  171. newvol = uservolume + change
  172. if newvol > 100:
  173. newvol = 100
  174. if newvol < 0:
  175. newvol = 0
  176. try:
  177. client.setvol(newvol)
  178. except Exception:
  179. client.connect("localhost", 6600)
  180. client.setvol(newvol)
  181. uservolume = newvol
  182. volume = newvol
  183. def setMode(string):
  184. global mode
  185. global inUse
  186. mode = string
  187. if mode == "off":
  188. inUse = False
  189. def setDiscoMode(startup=False):
  190. setKugel('on')
  191. if startup:
  192. setStartupDmxScene()
  193. else:
  194. setUserDmxScene()
  195. sleep(0.3)
  196. setSun('off')
  197. setMode('disco')
  198. def getMpdVolume():
  199. try:
  200. vol = int(client.status()['volume'])
  201. except Exception:
  202. client.connect("localhost", 6600)
  203. vol = int(client.status()['volume'])
  204. if vol != 0: #only if not muted
  205. global uservolume
  206. uservolume = vol
  207. return vol
  208. def setMpdVolume(vol):
  209. try:
  210. client.setvol(vol)
  211. except Exception:
  212. client.connect("localhost", 6600)
  213. client.setvol(vol)
  214. if vol != 0: #only if not muted
  215. global uservolume
  216. uservolume = vol
  217. return True
  218. def seek(secs):
  219. try:
  220. client.seekcur(secs)
  221. except Exception:
  222. client.connect("localhost", 6600)
  223. client.seekcur(secs)
  224. return True
  225. def getTrackInfo():
  226. try:
  227. currentsong = client.currentsong()
  228. except Exception:
  229. client.connect("localhost", 6600)
  230. currentsong = client.currentsong()
  231. print(currentsong)
  232. volume = getMpdVolume()
  233. setMpdVolume(10)
  234. try:
  235. tts.say(currentsong['artist'] + ', ' + currentsong['title'])
  236. except Exception:
  237. tts.say('Willkommen am Discoklo!', 'de')
  238. setMpdVolume(volume)
  239. def tour():
  240. tts.say("Hello, I'm Discobert!", "en")
  241. sleep(0.3)
  242. tts.say("Press 1 for nice electronic music", "en")
  243. sleep(0.3)
  244. tts.say("Press 2 for hard electronic music", "en")
  245. sleep(0.3)
  246. tts.say("Press 3 for HGichT", "en")
  247. sleep(0.3)
  248. tts.say("Press 4 for a good laugh", "en")
  249. sleep(0.3)
  250. tts.say("Press 5 for more music", "en")
  251. sleep(0.3)
  252. def setWorkingMode():
  253. setSun('on')
  254. sleep(0.3)
  255. setKugel('off')
  256. setDmxScene('black')
  257. setMode('work')
  258. def startTimeoutCountdown():
  259. global lastUsed
  260. global inUse
  261. tts.say('Timeout in')
  262. countdown = [5,4,3,2,1] #10,9,8,7,6,
  263. for sec in countdown:
  264. tts.say(str(sec))
  265. sleep(0.5)
  266. remotesignal = lirc.nextcode()
  267. if wiringpi.digitalRead(pin_pir) == 1 or remotesignal:
  268. lastUsed = time.time()
  269. tts.say('Timeout cancelled!')
  270. inUse = True
  271. toilet = lirc.nextcode()
  272. break
  273. if not inUse:
  274. tts.say('Shutting down now.', 'en')
  275. closeService()
  276. def closeService(sleepsecs=0):
  277. setSun('off')
  278. sleep(0.3)
  279. setKugel('off')
  280. setDmxScene('black')
  281. for x in range(0, 20):
  282. changeVolume(-5)
  283. sleep(0.1)
  284. stopMusic()
  285. inUseBefore = False # Pfusch pfusch!
  286. setMode('off')
  287. sleep(sleepsecs)
  288. # function when user arrives
  289. def initService():
  290. startMusic('0', True) # start intro music
  291. setDiscoMode(True)
  292. global volume
  293. global defaultvolume
  294. global uservolume
  295. try:
  296. client.setvol(defaultvolume)
  297. except Exception:
  298. client.connect("localhost", 6600)
  299. client.setvol(defaultvolume)
  300. volume = defaultvolume
  301. uservolume = defaultvolume
  302. global starttime
  303. starttime = time.time()
  304. def say(text, lang="en"):
  305. originalvol = getMpdVolume()
  306. setMpdVolume(10)
  307. tts.say(text, lang)
  308. setMpdVolume(originalvol)
  309. def setOnOff():
  310. global mode
  311. stopMusic()
  312. if mode is not 'work':
  313. setWorkingMode()
  314. else:
  315. initService()
  316. def doorShutdown():
  317. tts.say(random.choice(bye_sayings), "en")
  318. # sleep to give user some time to close the door
  319. # (pir sensor also stays up for 2 sec)
  320. closeService(5)
  321. def bootstrap():
  322. wiringpi.wiringPiSetup()
  323. wiringpi.pinMode(pin_kugel, 1) # set Relay Disokugel mode to OUTPUT
  324. wiringpi.pinMode(pin_door, 0) # set Circuit Door mode to INPUT
  325. wiringpi.pinMode(pin_sun, 1) # set Relay Sun mode to OUTPUT # TODO: Set pin!
  326. wiringpi.pinMode(pin_pir, 0) # set PIR Sensor mode to INPUT
  327. def timestamp(stamp=time.time()):
  328. return datetime.fromtimestamp(stamp).strftime('%Y-%m-%d %H:%M:%S')
  329. dmxScene = 0
  330. bootstrap()
  331. dev = pyudmx.uDMXDevice()
  332. dev.open()
  333. client = mpd.MPDClient()
  334. client.connect("localhost", 6600)
  335. tts = talkey.Talkey(
  336. preferred_languages=['en'],
  337. engine_preference=['pico'],
  338. )
  339. site = server.Site(Simple())
  340. reactor.listenTCP(8080, site)
  341. reactor.startRunning(False)
  342. lirc.init("disco", "~/discobert/lircrc", blocking=False)
  343. starttime = time.time() # helper for doorshutdown
  344. lastUsed = time.time() # helper for timeout
  345. inUse = False # is toilet in use?
  346. inUseBefore = False # helper to check statechanges
  347. mode = "off" # can be: disco, work, off
  348. timeout = 5 * 60 - 5 # timeout since last user interaction
  349. defaultvolume = 90 # Volume when user enters the toilet
  350. volume = 90 # Global for actual volume
  351. uservolume = 90 # Global for user volume (ignores mute state)
  352. setSun('off')
  353. print(timestamp(), "Ready!")
  354. # Main event loop ...
  355. while True:
  356. sleep(0.25)
  357. pirstate = wiringpi.digitalRead(pin_pir)
  358. doorstate = wiringpi.digitalRead(pin_door)
  359. #print('pirstate: ', pirstate)
  360. # 0 => door closed
  361. # 1 => door open
  362. if doorstate == 1:
  363. # don't do a doorShutdown when user comes in
  364. if (time.time() > (starttime + 15) and inUse == True):
  365. doorShutdown()
  366. if pirstate == 1:
  367. lastUsed = time.time()
  368. inUse = True
  369. else:
  370. if(time.time() > lastUsed + timeout):
  371. inUse = False
  372. remotesignal = lirc.nextcode()
  373. if remotesignal:
  374. lastUsed = time.time() # user is active!
  375. inUse = True
  376. for code in remotesignal:
  377. print('received code:', code)
  378. if(code == "mode_disco"):
  379. setDiscoMode()
  380. if(code == "mode_work"):
  381. setWorkingMode()
  382. if(code == "mode_power"):
  383. setOnOff()
  384. #if(code == "mode_dmx_next"):
  385. if(code == "mode_music_play_1"):
  386. startMusic('1')
  387. if(code == "mode_music_play_2"):
  388. startMusic('2')
  389. if(code == "mode_music_play_3"):
  390. startMusic('3')
  391. if(code == "mode_music_play_4"):
  392. startMusic('4')
  393. if(code == "mode_music_play_5"):
  394. startMusic('5')
  395. if(code == "mode_play_fm4"):
  396. startMusic('http://185.85.29.141:8000')
  397. if(code == "mode_play_oe1"):
  398. startMusic('http://185.85.29.142:8000')
  399. if(code == "mode_music_previous"):
  400. previousSong()
  401. if(code == "mode_music_next"):
  402. nextSong()
  403. if(code == "mode_music_play"):
  404. playMusic()
  405. if(code == "mode_music_stop"):
  406. stopMusic()
  407. if(code == "mode_music_mute"):
  408. muteMusic()
  409. if(code == "mode_music_pause"):
  410. pauseMusic()
  411. if(code == "mode_music_rewind"):
  412. seek(-30)
  413. if(code == "mode_music_back"):
  414. seek(-5)
  415. if(code == "mode_music_forward"):
  416. seek(5)
  417. if(code == "mode_music_fastforward"):
  418. seek(30)
  419. if(code == "mode_volume_up"):
  420. changeVolume(5)
  421. if(code == "mode_volume_down"):
  422. changeVolume(-5)
  423. if(code == "mode_music_info"):
  424. getTrackInfo()
  425. if(code == "mode_record"):
  426. say("I'm sorry, I'm afraid I can't do that!")
  427. if(code == "mode_help"):
  428. tour()
  429. if(inUseBefore != inUse):
  430. print(timestamp(), "State change inUse:", inUseBefore, inUse)
  431. if inUse:
  432. initService()
  433. else:
  434. startTimeoutCountdown()
  435. inUseBefore = inUse
  436. # Webserver
  437. reactor.iterate()
  438. lirc.deinit() # Clean up lirc
  439. dev.close()