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.

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