Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

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