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.

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