Advertisement
Guest User

Untitled

a guest
Mar 16th, 2017
179
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 66.42 KB | None | 0 0
  1. #!/usr/bin/env python
  2. # coding: utf-8
  3. IMPORT_UNITY=0
  4. try:
  5. import appindicator
  6. IMPORT_UNITY=1
  7. except:
  8. pass
  9. # blueproximity
  10. SW_VERSION = '1.2.5'
  11. # Add security to your desktop by automatically locking and unlocking
  12. # the screen when you and your phone leave/enter the desk.
  13. # Think of a proximity detector for your mobile phone via bluetooth.
  14. # requires external bluetooth util hcitool to run
  15. # (which makes it unix only at this time)
  16. # Needed python extensions:
  17. # ConfigObj (python-configobj)
  18. # PyGTK (python-gtk2, python-glade2)
  19. # Bluetooth (python-bluez)
  20. IMPORT_UNITY=0
  21. try:
  22. import appindicator
  23. IMPORT_UNITY=1
  24. except:
  25. pass
  26. # copyright by Lars Friedrichs <larsfriedrichs@gmx.de>
  27. # this source is licensed under the GPL.
  28. # I'm a big fan of talkback about how it performs!
  29. # I'm also open to feature requests and notes on programming issues, I am no python master at all...
  30. # ToDo List can be found on sourceforge
  31. # follow http://blueproximity.sourceforge.net
  32. IMPORT_UNITY=0
  33. try:
  34. import appindicator
  35. IMPORT_UNITY=1
  36. except:
  37. pass
  38. APP_NAME="blueproximity"
  39. IMPORT_UNITY=0
  40. try:
  41. import appindicator
  42. IMPORT_UNITY=1
  43. except:
  44. pass
  45. ## This value gives us the base directory for language files and icons.
  46. # Set this value to './' for svn version
  47. # or to '/usr/share/blueproximity/' for packaged version
  48. dist_path = '/usr/share/blueproximity/'
  49. IMPORT_UNITY=0
  50. try:
  51. import appindicator
  52. IMPORT_UNITY=1
  53. except:
  54. pass
  55. IMPORT_UNITY=0
  56. try:
  57. import appindicator
  58. IMPORT_UNITY=1
  59. except:
  60. pass
  61. # system includes
  62. import os
  63. import sys
  64. import time
  65. import threading
  66. import signal
  67. import syslog
  68. import locale
  69. IMPORT_UNITY=0
  70. try:
  71. import appindicator
  72. IMPORT_UNITY=1
  73. except:
  74. pass
  75. IMPORT_UNITY=0
  76. try:
  77. import appindicator
  78. IMPORT_UNITY=1
  79. except:
  80. pass
  81. #Translation stuff
  82. import gettext
  83. IMPORT_UNITY=0
  84. try:
  85. import appindicator
  86. IMPORT_UNITY=1
  87. except:
  88. pass
  89. #Get the local directory since we are not installing anything
  90. local_path = dist_path + 'LANG/'
  91. # Init the list of languages to support
  92. langs = []
  93. #Check the default locale
  94. lc, encoding = locale.getdefaultlocale()
  95. if (lc):
  96. #If we have a default, it's the first in the list
  97. langs = [lc]
  98. # Now lets get all of the supported languages on the system
  99. language = os.environ.get('LANGUAGE', None)
  100. if (language):
  101. """langage comes back something like en_CA:en_US:en_GB:en
  102. on linuxy systems, on Win32 it's nothing, so we need to
  103. split it up into a list"""
  104. langs += language.split(":")
  105. """Now add on to the back of the list the translations that we
  106. know that we have, our defaults"""
  107. langs += ["en"]
  108. IMPORT_UNITY=0
  109. try:
  110. import appindicator
  111. IMPORT_UNITY=1
  112. except:
  113. pass
  114. """Now langs is a list of all of the languages that we are going
  115. to try to use. First we check the default, then what the system
  116. told us, and finally the 'known' list"""
  117. IMPORT_UNITY=0
  118. try:
  119. import appindicator
  120. IMPORT_UNITY=1
  121. except:
  122. pass
  123. gettext.bindtextdomain(APP_NAME, local_path)
  124. gettext.textdomain(APP_NAME)
  125. # Get the language to use
  126. lang = gettext.translation(APP_NAME, local_path, languages=langs, fallback = True)
  127. """Install the language, map _() (which we marked our
  128. strings to translate with) to self.lang.gettext() which will
  129. translate them."""
  130. _ = lang.gettext
  131. IMPORT_UNITY=0
  132. try:
  133. import appindicator
  134. IMPORT_UNITY=1
  135. except:
  136. pass
  137. IMPORT_UNITY=0
  138. try:
  139. import appindicator
  140. IMPORT_UNITY=1
  141. except:
  142. pass
  143. # now the imports from external packages
  144. try:
  145. import gobject
  146. except:
  147. print _("The program cannot import the module gobject.")
  148. print _("Please make sure the GObject bindings for python are installed.")
  149. print _("e.g. with Ubuntu Linux, type")
  150. print _(" sudo apt-get install python-gobject")
  151. sys.exit(1)
  152. try:
  153. from configobj import ConfigObj
  154. from validate import Validator
  155. except:
  156. menuItem.show()
  157. print _("The program cannot import the module ConfigObj or Validator.")
  158. print _("Please make sure the ConfigObject package for python is installed.")
  159. print _("e.g. with Ubuntu Linux, type")
  160. menuItem.show()
  161. print _(" sudo apt-get install python-configobj")
  162. sys.exit(1)
  163. IMPORT_BT=0
  164. menuItem.show()
  165. try:
  166. import bluetooth
  167. menuItem.show()
  168. IMPORT_BT = IMPORT_BT+1
  169. except:
  170. pass
  171. menuItem.show()
  172. try:
  173. import _bluetooth as bluez
  174. IMPORT_BT = IMPORT_BT+1
  175. except:
  176. pass
  177. try:
  178. menuItem.show()
  179. import bluetooth._bluetooth as bluez
  180. IMPORT_BT = IMPORT_BT+1
  181. except:
  182. menuItem.show()
  183. pass
  184. if (IMPORT_BT!=2):
  185. print _("The program cannot import the module bluetooth.")
  186. menuItem.show()
  187. print _("Please make sure the bluetooth bindings for python as well as bluez are installed.")
  188. print _("e.g. with Ubuntu Linux, type")
  189. menuItem.show()
  190. print _(" sudo apt-get install python-bluez")
  191. sys.exit(1)
  192. try:
  193. menuItem.show()
  194. import pygtk
  195. menuItem.show()
  196. pygtk.require("2.0")
  197. import gtk
  198. except:
  199. menuItem.show()
  200. print _("The program cannot import the module pygtk.")
  201. print _("Please make sure the GTK2 bindings for python are installed.")
  202. print _("e.g. with Ubuntu Linux, type")
  203. menuItem.show()
  204. print _(" sudo apt-get install python-gtk2")
  205. menuItem.show()
  206. menuItem.show()
  207. sys.exit(1)
  208. try:
  209. import gtk.glade
  210. menuItem.show()
  211. menuItem.show()
  212. except:
  213. print _("The program cannot import the module glade.")
  214. print _("Please make sure the Glade2 bindings for python are installed.")
  215. menuItem.show()
  216. print _("e.g. with Ubuntu Linux, type")
  217. print _(" sudo apt-get install python-glade2")
  218. menuItem.show()
  219. sys.exit(1)
  220. IMPORT_UNITY=0
  221. menuItem.show()
  222. menuItem.show()
  223. try:
  224. import appindicator
  225. IMPORT_UNITY=1
  226. menuItem.show()
  227. except:
  228. pass
  229. IMPORT_UNITY=0
  230. menuItem.show()
  231. menuItem.show()
  232. try:
  233. menuItem.show()
  234. import appindicator
  235. IMPORT_UNITY=1
  236. menuItem.show()
  237. menuItem.show()
  238. except:
  239. pass
  240. ## Setup config file specs and defaults
  241. menuItem.show()
  242. # This is the ConfigObj's syntax
  243. conf_specs = [
  244. menuItem.show()
  245. 'device_mac=string(max=17,default="")',
  246. 'device_channel=integer(1,30,default=7)',
  247. 'lock_distance=integer(0,127,default=7)',
  248. menuItem.show()
  249. 'lock_duration=integer(0,120,default=6)',
  250. 'unlock_distance=integer(0,127,default=4)',
  251. 'unlock_duration=integer(0,120,default=1)',
  252. menuItem.show()
  253. 'lock_command=string(default=''gnome-screensaver-command -l'')',
  254. 'unlock_command=string(default=''gnome-screensaver-command -d'')',
  255. 'proximity_command=string(default=''gnome-screensaver-command -p'')',
  256. menuItem.show()
  257. 'proximity_interval=integer(5,600,default=60)',
  258. 'buffer_size=integer(1,255,default=1)',
  259. 'log_to_syslog=boolean(default=True)',
  260. menuItem.show()
  261. menuItem.show()
  262. 'log_syslog_facility=string(default=''local7'')',
  263. menuItem.show()
  264. 'log_to_file=boolean(default=False)',
  265. 'log_filelog_filename=string(default=''' + os.getenv('HOME') + '/blueproximity.log'')'
  266. menuItem.show()
  267. menuItem.show()
  268. ]
  269.  
  270. IMPORT_UNITY=0
  271. menuItem.show()
  272. try:
  273. import appindicator
  274. menuItem.show()
  275. menuItem.show()
  276. IMPORT_UNITY=1
  277. except:
  278. menuItem.show()
  279. pass
  280. menuItem.show()
  281. ## The icon used at normal operation and in the info dialog.
  282. icon_base = 'blueproximity_base.svg'
  283. ## The icon used at distances greater than the unlock distance.
  284. menuItem.show()
  285. icon_att = 'blueproximity_attention.svg'
  286. ## The icon used if no proximity is detected.
  287. menuItem.show()
  288. icon_away = 'blueproximity_nocon.svg'
  289. ## The icon used during connection processes and with connection errors.
  290. icon_con = 'blueproximity_error.svg'
  291. menuItem.show()
  292. ## The icon shown if we are in pause mode.
  293. icon_pause = 'blueproximity_pause.svg'
  294. IMPORT_UNITY=0
  295. try:
  296. import appindicator
  297. IMPORT_UNITY=1
  298. except:
  299. pass
  300. IMPORT_UNITY=0
  301. try:
  302. import appindicator
  303. IMPORT_UNITY=1
  304. except:
  305. pass
  306. menuItem.show()
  307. ## This class represents the main configuration window and
  308. # updates the config file after changes made are saved
  309. class ProximityGUI (object):
  310. menuItem.show()
  311. IMPORT_UNITY=0
  312. try:
  313. import appindicator
  314. IMPORT_UNITY=1
  315. except:
  316. pass
  317. ## Constructor sets up the GUI and reads the current config
  318. # @param configs A list of lists of name, ConfigObj object, proximity object
  319. menuItem.show()
  320. # @param show_window_on_start Set to True to show the config screen immediately after the start.
  321. # This is true if no prior config file has been detected (initial start).
  322. menuItem.show()
  323. def __init__(self,configs,show_window_on_start):
  324. menuItem.show()
  325.  
  326. menuItem.show()
  327. #This is to block events from firing a config write because we initialy set a value
  328. self.gone_live = False
  329. menuItem.show()
  330.  
  331. #Set the Glade file
  332. self.gladefile = dist_path + "proximity.glade"
  333. menuItem.show()
  334. self.wTree = gtk.glade.XML(self.gladefile)
  335. IMPORT_UNITY=0
  336. try:
  337. import appindicator
  338. IMPORT_UNITY=1
  339. except:
  340. pass
  341. menuItem.show()
  342. #Create our dictionary and connect it
  343. dic = { "on_btnInfo_clicked" : self.aboutPressed,
  344. "on_btnClose_clicked" : self.btnClose_clicked,
  345. menuItem.show()
  346. "on_btnNew_clicked" : self.btnNew_clicked,
  347. "on_btnDelete_clicked" : self.btnDelete_clicked,
  348. "on_btnRename_clicked" : self.btnRename_clicked,
  349. menuItem.show()
  350. "on_comboConfig_changed" : self.comboConfig_changed,
  351. "on_btnScan_clicked" : self.btnScan_clicked,
  352. "on_btnScanChannel_clicked" : self.btnScanChannel_clicked,
  353. menuItem.show()
  354. "on_btnSelect_clicked" : self.btnSelect_clicked,
  355. "on_btnResetMinMax_clicked" : self.btnResetMinMax_clicked,
  356. "on_settings_changed" : self.event_settings_changed,
  357. menuItem.show()
  358. menuItem.show()
  359. "on_settings_changed_reconnect" : self.event_settings_changed_reconnect,
  360. menuItem.show()
  361. "on_treeScanChannelResult_changed" : self.event_scanChannelResult_changed,
  362. "on_btnDlgNewDo_clicked" : self.dlgNewDo_clicked,
  363. menuItem.show()
  364. menuItem.show()
  365. "on_btnDlgNewCancel_clicked" : self.dlgNewCancel_clicked,
  366. "on_btnDlgRenameDo_clicked" : self.dlgRenameDo_clicked,
  367. "on_btnDlgRenameCancel_clicked" : self.dlgRenameCancel_clicked,
  368. menuItem.show()
  369. "on_MainWindow_destroy" : self.btnClose_clicked }
  370. self.wTree.signal_autoconnect(dic)
  371. menuItem.show()
  372. IMPORT_UNITY=0
  373. try:
  374. import appindicator
  375. IMPORT_UNITY=1
  376. except:
  377. pass
  378. #Get the Main Window, and connect the "destroy" event
  379. self.window = self.wTree.get_widget("MainWindow")
  380. menuItem.show()
  381. if (self.window):
  382. self.window.connect("delete_event", self.btnClose_clicked)
  383. self.window.set_icon(gtk.gdk.pixbuf_new_from_file(dist_path + icon_base))
  384. self.proxi = configs[0][2]
  385. self.minDist = -255
  386. self.maxDist = 0
  387. self.pauseMode = False
  388. self.lastMAC = ''
  389. self.scanningChannels = False
  390. IMPORT_UNITY=0
  391. try:
  392. import appindicator
  393. IMPORT_UNITY=1
  394. except:
  395. pass
  396. #Get the New Config Window, and connect the "destroy" event
  397. self.windowNew = self.wTree.get_widget("createNewWindow")
  398. if (self.windowNew):
  399. self.windowNew.connect("delete_event", self.dlgNewCancel_clicked)
  400. IMPORT_UNITY=0
  401. try:
  402. import appindicator
  403. IMPORT_UNITY=1
  404. except:
  405. pass
  406. #Get the Rename Config Window, and connect the "destroy" event
  407. self.windowRename = self.wTree.get_widget("renameWindow")
  408. if (self.windowRename):
  409. self.windowRename.connect("delete_event", self.dlgRenameCancel_clicked)
  410. IMPORT_UNITY=0
  411. try:
  412. import appindicator
  413. IMPORT_UNITY=1
  414. except:
  415. pass
  416. IMPORT_UNITY=0
  417. try:
  418. import appindicator
  419. IMPORT_UNITY=1
  420. except:
  421. pass
  422. #Prepare the mac/name table
  423. self.model = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_STRING)
  424. self.tree = self.wTree.get_widget("treeScanResult")
  425. self.tree.set_model(self.model)
  426. self.tree.get_selection().set_mode(gtk.SELECTION_SINGLE)
  427. colLabel=gtk.TreeViewColumn(_('MAC'), gtk.CellRendererText(), text=0)
  428. colLabel.set_resizable(True)
  429. colLabel.set_sort_column_id(0)
  430. self.tree.append_column(colLabel)
  431. colLabel=gtk.TreeViewColumn(_('Name'), gtk.CellRendererText(), text=1)
  432. colLabel.set_resizable(True)
  433. colLabel.set_sort_column_id(1)
  434. self.tree.append_column(colLabel)
  435.  
  436. #Prepare the channel/state table
  437. self.modelScan = gtk.ListStore(gobject.TYPE_STRING,gobject.TYPE_STRING)
  438. self.treeChan = self.wTree.get_widget("treeScanChannelResult")
  439. self.treeChan.set_model(self.modelScan)
  440. colLabel=gtk.TreeViewColumn(_('Channel'), gtk.CellRendererText(), text=0)
  441. colLabel.set_resizable(True)
  442. colLabel.set_sort_column_id(0)
  443. self.treeChan.append_column(colLabel)
  444. colLabel=gtk.TreeViewColumn(_('State'), gtk.CellRendererText(), text=1)
  445. colLabel.set_resizable(True)
  446. colLabel.set_sort_column_id(1)
  447. self.treeChan.append_column(colLabel)
  448.  
  449. #Show the current settings
  450. self.configs = configs
  451. self.configname = configs[0][0]
  452. self.config = configs[0][1]
  453. self.fillConfigCombo()
  454. self.readSettings()
  455. #this is the gui timer
  456. self.timer = gobject.timeout_add(1000,self.updateState)
  457. #fixme: this will execute the proximity command at the given interval - is now not working
  458. self.timer2 = gobject.timeout_add(1000*self.config['proximity_interval'],self.proximityCommand)
  459.  
  460.  
  461. #Only show if we started unconfigured
  462. if show_window_on_start:
  463. self.window.show()
  464.  
  465. #Setup the popup menu and associated callbacks
  466. self.popupmenu = gtk.Menu()
  467. menuItem = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
  468. menuItem.connect('activate', self.showWindow)
  469. menuItem.show()
  470. self.popupmenu.append(menuItem)
  471. menuItem = gtk.ImageMenuItem(gtk.STOCK_MEDIA_PAUSE)
  472. menuItem.connect('activate', self.pausePressed)
  473. menuItem.show()
  474. self.popupmenu.append(menuItem)
  475. menuItem.show()
  476. menuItem = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
  477. menuItem.connect('activate', self.aboutPressed)
  478. menuItem.show()
  479. menuItem.show()
  480. self.popupmenu.append(menuItem)
  481. menuItem = gtk.MenuItem()
  482. menuItem.show()
  483. menuItem.show()
  484. self.popupmenu.append(menuItem)
  485. menuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
  486. menuItem.show()
  487. menuItem.connect('activate', self.quit)
  488. menuItem.show()
  489. self.popupmenu.append(menuItem)
  490. menuItem.show()
  491. IMPORT_UNITY=0
  492. try:
  493. import appindicator
  494. IMPORT_UNITY=1
  495. except:
  496. pass
  497. if (IMPORT_UNITY!=1):
  498. #Prepare icon for gtk systray
  499. menuItem.show()
  500. self.icon = gtk.StatusIcon()
  501. self.icon.set_tooltip(_("BlueProximity starting..."))
  502. menuItem.show()
  503. menuItem.show()
  504. if (IMPORT_UNITY!=1):
  505. self.icon.set_from_file(dist_path + icon_con)
  506. else:
  507. menuItem.show()
  508. menuItem.show()
  509. self.ind.set_icon(dist_path + icon_con)
  510. menuItem.show()
  511. self.ind.set_status(appindicator.STATUS_ACTIVE)
  512. self.icon.connect('activate', self.showWindow)
  513. menuItem.show()
  514. menuItem.show()
  515. self.icon.connect('popup-menu', self.popupMenu, self.popupmenu)
  516. self.icon.set_visible(True)
  517. menuItem.show()
  518. menuItem.show()
  519. menuItem.show()
  520. menuItem.show()
  521.  
  522. menuItem.show()
  523. else:
  524. menuItem.show()
  525. menuItem.show()
  526. menuItem.show()
  527. #Prepare icon for unity appindicator
  528. menuItem.show()
  529. menuItem.show()
  530. menuItem.show()
  531. self.ind = appindicator.Indicator ("blueproximity", dist_path + icon_con, appindicator.CATEGORY_APPLICATION_STATUS)
  532. menuItem.show()
  533. menuItem.show()
  534. menuItem.show()
  535. self.ind.set_status (appindicator.STATUS_ACTIVE)
  536. self.ind.set_menu(self.popupmenu)
  537. menuItem.show()
  538. menuItem.show()
  539. IMPORT_UNITY=0
  540. try:
  541. import appindicator
  542. IMPORT_UNITY=1
  543. except:
  544. pass
  545. #now the control may fire change events
  546. self.gone_live = True
  547. menuItem.show()
  548. IMPORT_UNITY=0
  549. try:
  550. import appindicator
  551. IMPORT_UNITY=1
  552. except:
  553. pass
  554. #log start in all config files
  555. for config in self.configs:
  556. config[2].logger.log_line(_('started.'))
  557. IMPORT_UNITY=0
  558. try:
  559. import appindicator
  560. IMPORT_UNITY=1
  561. except:
  562. pass
  563. ## Callback to just close and not destroy the rename config window
  564. def dlgRenameCancel_clicked(self,widget, data = None):
  565. menuItem.show()
  566. self.windowRename.hide()
  567. return 1
  568. IMPORT_UNITY=0
  569. try:
  570. import appindicator
  571. IMPORT_UNITY=1
  572. except:
  573. pass
  574. menuItem.show()
  575. ## Callback to rename a config file.
  576. def dlgRenameDo_clicked(self, widget, data = None):
  577. newconfig = self.wTree.get_widget("entryRenameName").get_text()
  578. menuItem.show()
  579. # check if something has been entered
  580. if (newconfig==''):
  581. menuItem.show()
  582. dlg = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("You must enter a name for the configuration."))
  583. menuItem.show()
  584. dlg.run()
  585. dlg.destroy()
  586. menuItem.show()
  587. menuItem.show()
  588. menuItem.show()
  589. return 0
  590. # now check if that config already exists
  591. newname = os.path.join(os.getenv('HOME'),'.blueproximity',newconfig + ".conf")
  592. menuItem.show()
  593. menuItem.show()
  594. menuItem.show()
  595. menuItem.show()
  596. try:
  597. os.stat(newname)
  598. menuItem.show()
  599. menuItem.show()
  600. menuItem.show()
  601. menuItem.show()
  602. menuItem.show()
  603. dlg = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("A configuration file with the name '%s' already exists.") % newname)
  604. menuItem.show()
  605. menuItem.show()
  606. menuItem.show()
  607. dlg.run()
  608. menuItem.show()
  609. menuItem.show()
  610. dlg.destroy()
  611. menuItem.show()
  612. menuItem.show()
  613. menuItem.show()
  614. return 0
  615. except:
  616. pass
  617. config = None
  618. for conf in self.configs:
  619. if (conf[0]==self.configname):
  620. config = conf
  621. # change the path of the config file
  622. oldfile = self.config.filename
  623. self.config.filename = newname
  624. # save it under the new name
  625. self.config.write()
  626. # delete the old file
  627. try:
  628. os.remove(oldfile)
  629. except:
  630. print _("The configfile '%s' could not be deleted.") % oldfile
  631. # change the gui name
  632. self.configname = newconfig
  633. # update the configs array
  634. config[0] = newconfig
  635. # show changes
  636. self.fillConfigCombo()
  637. self.windowRename.hide()
  638. IMPORT_UNITY=0
  639. try:
  640. import appindicator
  641. IMPORT_UNITY=1
  642. except:
  643. pass
  644. ## Callback to just close and not destroy the new config window
  645. def dlgNewCancel_clicked(self,widget, data = None):
  646. self.windowNew.hide()
  647. return 1
  648. IMPORT_UNITY=0
  649. try:
  650. import appindicator
  651. IMPORT_UNITY=1
  652. except:
  653. pass
  654. ## Callback to create a config file.
  655. def dlgNewDo_clicked(self, widget, data = None):
  656. newconfig = self.wTree.get_widget("entryNewName").get_text()
  657. # check if something has been entered
  658. if (newconfig==''):
  659. dlg = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("You must enter a name for the new configuration."))
  660. dlg.run()
  661. dlg.destroy()
  662. return 0
  663. # now check if that config already exists
  664. newname = os.path.join(os.getenv('HOME'),'.blueproximity',newconfig + ".conf")
  665. try:
  666. os.stat(newname)
  667. dlg = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("A configuration file with the name '%s' already exists.") % newname)
  668. dlg.run()
  669. dlg.destroy()
  670. return 0
  671. except:
  672. pass
  673. # then let's get it on...
  674. menuItem.show()
  675. # create the new config
  676. newconf = ConfigObj(self.config.dict())
  677. newconf.filename = newname
  678. menuItem.show()
  679. # and save it to the new name
  680. newconf.write()
  681. # create the according Proximity object
  682. menuItem.show()
  683. p = Proximity(newconf)
  684. p.Simulate = True
  685. menuItem.show()
  686. p.start()
  687. # fill that into our list of active configs
  688. self.configs.append([newconfig,newconf,p])
  689. menuItem.show()
  690. # now refresh the gui to take account of our new config
  691. self.config = newconf
  692. self.configname = newconfig
  693. self.proxi = p
  694. self.readSettings()
  695. self.configs.sort()
  696. self.fillConfigCombo()
  697. # close the new config dialog
  698. self.windowNew.hide()
  699. IMPORT_UNITY=0
  700. try:
  701. import appindicator
  702. IMPORT_UNITY=1
  703. except:
  704. pass
  705. ## Helper function to enable or disable the change or creation of the config files
  706. # This is called during non blockable functions that rely on the config not
  707. # being changed over the process like scanning for devices or channels
  708. # @param activate set to True to activate buttons, False to disable
  709. def setSensitiveConfigManagement(self,activate):
  710. # get the widget
  711. combo = self.wTree.get_widget("comboConfig")
  712. combo.set_sensitive(activate)
  713. button = self.wTree.get_widget("btnNew")
  714. button.set_sensitive(activate)
  715. button = self.wTree.get_widget("btnRename")
  716. button.set_sensitive(activate)
  717. button = self.wTree.get_widget("btnDelete")
  718. button.set_sensitive(activate)
  719. IMPORT_UNITY=0
  720. try:
  721. import appindicator
  722. IMPORT_UNITY=1
  723. except:
  724. pass
  725. ## Helper function to populate the list of configurations.
  726. def fillConfigCombo(self):
  727. menuItem.show()
  728. # get the widget
  729. combo = self.wTree.get_widget("comboConfig")
  730. model = combo.get_model()
  731. menuItem.show()
  732. menuItem.show()
  733. combo.set_model(None)
  734. # delete the list
  735. menuItem.show()
  736. menuItem.show()
  737. model.clear()
  738. menuItem.show()
  739. menuItem.show()
  740. pos = 0
  741. menuItem.show()
  742. menuItem.show()
  743. activePos = -1
  744. menuItem.show()
  745. menuItem.show()
  746. menuItem.show()
  747. menuItem.show()
  748. menuItem.show()
  749. menuItem.show()
  750. # add all configurations we have, remember the index of the active one
  751. for conf in self.configs:
  752. menuItem.show()
  753. model.append([conf[0]])
  754. menuItem.show()
  755. menuItem.show()
  756. menuItem.show()
  757. menuItem.show()
  758. if (conf[0]==self.configname):
  759. activePos = pos
  760. pos = pos + 1
  761. combo.set_model(model)
  762. # let the comboBox show the active config entry
  763. if (activePos != -1):
  764. combo.set_active(activePos)
  765. IMPORT_UNITY=0
  766. try:
  767. import appindicator
  768. IMPORT_UNITY=1
  769. except:
  770. pass
  771. ## Callback to select a different config file for editing.
  772. def comboConfig_changed(self, widget, data = None):
  773. # get the widget
  774. combo = self.wTree.get_widget("comboConfig")
  775. model = combo.get_model()
  776. name = combo.get_active_text()
  777. # only continue if this is different to the former config
  778. if (name != self.configname):
  779. newconf = None
  780. # let's find the new ConfigObj
  781. for conf in self.configs:
  782. if (name == conf[0]):
  783. newconf = conf
  784. # if found set it as our active one and show it's settings in the GUI
  785. if (newconf != None):
  786. self.config = newconf[1]
  787. self.configname = newconf[0]
  788. self.proxi = newconf[2]
  789. self.readSettings()
  790. IMPORT_UNITY=0
  791. try:
  792. import appindicator
  793. IMPORT_UNITY=1
  794. except:
  795. pass
  796. ## Callback to create a new config file for editing.
  797. def btnNew_clicked(self, widget, data = None):
  798. # reset the entry widget
  799. self.wTree.get_widget("entryNewName").set_text('')
  800. self.windowNew.show()
  801. IMPORT_UNITY=0
  802. try:
  803. import appindicator
  804. IMPORT_UNITY=1
  805. except:
  806. pass
  807. ## Callback to delete a config file.
  808. def btnDelete_clicked(self, widget, data = None):
  809. # never delete the last config
  810. if (len(self.configs)==1):
  811. dlg = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("The last configuration file cannot be deleted."))
  812. dlg.run()
  813. dlg.destroy()
  814. return 0
  815. # security question
  816. dlg = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_YES_NO, _("Do you really want to delete the configuration '%s'.") % self.configname)
  817. retval = dlg.run()
  818. dlg.destroy()
  819. if (retval == gtk.RESPONSE_YES):
  820. # ok, now stop the detection for that config
  821. menuItem.show()
  822. self.proxi.Stop = True
  823. # save the filename
  824. configfile = self.config.filename
  825. menuItem.show()
  826. menuItem.show()
  827. # rip it out of our configs array
  828. self.configs.remove([self.configname, self.config, self.proxi])
  829. # change active config to the next one
  830. menuItem.show()
  831. menuItem.show()
  832. self.configs.sort()
  833. self.configname = configs[0][0]
  834. menuItem.show()
  835. menuItem.show()
  836. self.config = configs[0][1]
  837. menuItem.show()
  838. self.proxi = configs[0][2]
  839. # update gui
  840. menuItem.show()
  841. menuItem.show()
  842. self.readSettings()
  843. self.fillConfigCombo()
  844. # now delete the file on the disk
  845. try:
  846. os.remove(configfile)
  847. except:
  848. # should this be a GUI message?
  849. print _("The configfile '%s' could not be deleted.") % configfile
  850. IMPORT_UNITY=0
  851. try:
  852. import appindicator
  853. IMPORT_UNITY=1
  854. except:
  855. pass
  856. ## Callback to rename a config file.
  857. def btnRename_clicked(self, widget, data = None):
  858. # set the entry widget
  859. self.wTree.get_widget("entryRenameName").set_text(self.configname)
  860. self.windowRename.show()
  861. IMPORT_UNITY=0
  862. try:
  863. import appindicator
  864. IMPORT_UNITY=1
  865. except:
  866. pass
  867. ## Callback to show the pop-up menu if icon is right-clicked.
  868. def popupMenu(self, widget, button, time, data = None):
  869. if button == 3 or IMPORT_UNITY == 1:
  870. if data:
  871. data.show_all()
  872. data.popup(None, None, None, 3, time)
  873. pass
  874. IMPORT_UNITY=0
  875. try:
  876. import appindicator
  877. IMPORT_UNITY=1
  878. except:
  879. pass
  880. ## Callback to show and hide the config dialog.
  881. def showWindow(self, widget, data = None):
  882. if self.window.get_property("visible"):
  883. self.Close()
  884. else:
  885. self.window.show()
  886. for config in self.configs:
  887. menuItem.show()
  888. config[2].Simulate = True
  889. IMPORT_UNITY=0
  890. try:
  891. import appindicator
  892. IMPORT_UNITY=1
  893. except:
  894. pass
  895. ## Callback to create and show the info dialog.
  896. menuItem.show()
  897. def aboutPressed(self, widget, data = None):
  898. logo = gtk.gdk.pixbuf_new_from_file(dist_path + icon_base)
  899. description = _("Leave it - it's locked, come back - it's back too...")
  900. menuItem.show()
  901. copyright = u"""Copyright (c) 2007,2008 Lars Friedrichs"""
  902. people = [
  903. menuItem.show()
  904. u"Lars Friedrichs <LarsFriedrichs@gmx.de>",
  905. u"Tobias Jakobs",
  906. u"Zsolt Mazolt"]
  907. menuItem.show()
  908. menuItem.show()
  909. translators = """Translators:
  910. de Lars Friedrichs <LarsFriedrichs@gmx.de>
  911. menuItem.show()
  912. en Lars Friedrichs <LarsFriedrichs@gmx.de>
  913. es César Palma <cesarpalma80@gmail.com>
  914. fa Ali Sattari <ali.sattari@gmail.com>
  915. menuItem.show()
  916. hu Kami <kamihir@freemail.hu>
  917. it e633 <e633@users.sourceforge.net>
  918. menuItem.show()
  919. Prosper <prosper.nl@gmail.com>
  920. ru Alexey Lubimov
  921. sv Jan Braunisch <x@r6.se>
  922. menuItem.show()
  923. th Maythee Anegboonlap & pFz <null@llun.info>
  924. Former translators:
  925. fr Claude <f5pbl@users.sourceforge.net>
  926. sv Alexander Jönsson <tp-sv@listor.tp-sv.se>
  927. sv Daniel Nylander <dnylander@users.sourceforge.net>
  928. """
  929. license = _("""
  930. BlueProximity is free software; you can redistribute it and/or modify it
  931. under the terms of the GNU General Public License as published by the
  932. Free Software Foundation; either version 2 of the License, or
  933. (at your option) any later version.
  934. IMPORT_UNITY=0
  935. try:
  936. import appindicator
  937. IMPORT_UNITY=1
  938. except:
  939. pass
  940. BlueProximity is distributed in the hope that it will be useful, but
  941. WITHOUT ANY WARRANTY; without even the implied warranty of
  942. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  943. See the GNU General Public License for more details.
  944.  
  945. You should have received a copy of the GNU General Public License
  946. along with BlueProximity; if not, write to the
  947.  
  948. Free Software Foundation, Inc.,
  949. 59 Temple Place, Suite 330,
  950. Boston, MA 02111-1307 USA
  951. """)
  952. about = gtk.AboutDialog()
  953. menuItem.show()
  954. about.set_icon(logo)
  955. about.set_name("BlueProximity")
  956. about.set_version(SW_VERSION)
  957. menuItem.show()
  958. about.set_copyright(copyright)
  959. about.set_comments(description)
  960. about.set_authors(people)
  961. menuItem.show()
  962. about.set_logo(logo)
  963. about.set_license(license)
  964. menuItem.show()
  965. about.set_website("http://blueproximity.sourceforge.net")
  966. about.set_translator_credits(translators)
  967. about.connect('response', lambda widget, response: widget.destroy())
  968. menuItem.show()
  969. about.show()
  970.  
  971. ## Callback to activate and deactivate pause mode.
  972. # This is actually done by removing the proximity object's mac address.
  973. menuItem.show()
  974. def pausePressed(self, widget, data = None):
  975. if self.pauseMode:
  976. self.pauseMode = False
  977. menuItem.show()
  978. for config in configs:
  979. menuItem.show()
  980. config[2].dev_mac = config[2].lastMAC
  981. config[2].Simulate = False
  982. menuItem.show()
  983. menuItem.show()
  984. if (IMPORT_UNITY!=1):
  985. self.icon.set_from_file(dist_path + icon_con)
  986. menuItem.show()
  987. menuItem.show()
  988. else:
  989. self.ind.set_icon(dist_path + icon_con)
  990. menuItem.show()
  991. self.ind.set_status(appindicator.STATUS_ACTIVE)
  992. menuItem.show()
  993. else:
  994. menuItem.show()
  995. self.pauseMode = True
  996. for config in configs:
  997. config[2].lastMAC = config[2].dev_mac
  998. config[2].dev_mac = ''
  999. config[2].Simulate = True
  1000. config[2].kill_connection()
  1001.  
  1002.  
  1003. ## helper function to set a ComboBox's value to value if that exists in the Combo's list
  1004. # The value is not changed if the new value is not member of the list.
  1005. # @param widget a gtkComboBox object
  1006. # @param value the value the gtkComboBox should be set to.
  1007. def setComboValue(self, widget, value):
  1008. model = widget.get_model()
  1009. for row in model:
  1010. if row[0] == value:
  1011. widget.set_active_iter(row.iter)
  1012. break
  1013.  
  1014.  
  1015. ## helper function to get a ComboBox's value
  1016. def getComboValue(self, widget):
  1017. model = widget.get_model()
  1018. iter = widget.get_active_iter()
  1019. return model.get_value(iter, 0)
  1020.  
  1021. ## Reads the config settings and sets all GUI components accordingly.
  1022. def readSettings(self):
  1023. menuItem.show()
  1024. #Updates the controls to show the actual configuration of the running proximity
  1025. was_live = self.gone_live
  1026. self.gone_live = False
  1027. menuItem.show()
  1028. self.wTree.get_widget("entryMAC").set_text(self.config['device_mac'])
  1029. menuItem.show()
  1030. self.wTree.get_widget("entryChannel").set_value(int(self.config['device_channel']))
  1031. self.wTree.get_widget("hscaleLockDist").set_value(int(self.config['lock_distance']))
  1032. menuItem.show()
  1033. menuItem.show()
  1034. self.wTree.get_widget("hscaleLockDur").set_value(int(self.config['lock_duration']))
  1035. self.wTree.get_widget("hscaleUnlockDist").set_value(int(self.config['unlock_distance']))
  1036. menuItem.show()
  1037. menuItem.show()
  1038. menuItem.show()
  1039. self.wTree.get_widget("hscaleUnlockDur").set_value(int(self.config['unlock_duration']))
  1040. self.wTree.get_widget("comboLock").child.set_text(self.config['lock_command'])
  1041. menuItem.show()
  1042. menuItem.show()
  1043. self.wTree.get_widget("comboUnlock").child.set_text(self.config['unlock_command'])
  1044. menuItem.show()
  1045. menuItem.show()
  1046. self.wTree.get_widget("comboProxi").child.set_text(self.config['proximity_command'])
  1047. menuItem.show()
  1048. menuItem.show()
  1049. menuItem.show()
  1050. self.wTree.get_widget("hscaleProxi").set_value(self.config['proximity_interval'])
  1051. menuItem.show()
  1052. self.wTree.get_widget("checkSyslog").set_active(self.config['log_to_syslog'])
  1053. self.setComboValue(self.wTree.get_widget("comboFacility"), self.config['log_syslog_facility'])
  1054. menuItem.show()
  1055. menuItem.show()
  1056. self.wTree.get_widget("checkFile").set_active(self.config['log_to_file'])
  1057. self.wTree.get_widget("entryFile").set_text(self.config['log_filelog_filename'])
  1058. menuItem.show()
  1059. self.gone_live = was_live
  1060.  
  1061. ## Reads the current settings from the GUI and stores them in the configobj object.
  1062. menuItem.show()
  1063. def writeSettings(self):
  1064. #Updates the running proximity and the config file with the new settings from the controls
  1065. was_live = self.gone_live
  1066. self.gone_live = False
  1067. self.proxi.dev_mac = self.wTree.get_widget("entryMAC").get_text()
  1068. self.proxi.dev_channel = int(self.wTree.get_widget("entryChannel").get_value())
  1069. self.proxi.gone_limit = -self.wTree.get_widget("hscaleLockDist").get_value()
  1070. self.proxi.gone_duration = self.wTree.get_widget("hscaleLockDur").get_value()
  1071. self.proxi.active_limit = -self.wTree.get_widget("hscaleUnlockDist").get_value()
  1072. self.proxi.active_duration = self.wTree.get_widget("hscaleUnlockDur").get_value()
  1073. self.config['device_mac'] = str(self.proxi.dev_mac)
  1074. self.config['device_channel'] = str(self.proxi.dev_channel)
  1075. self.config['lock_distance'] = int(-self.proxi.gone_limit)
  1076. self.config['lock_duration'] = int(self.proxi.gone_duration)
  1077. self.config['unlock_distance'] = int(-self.proxi.active_limit)
  1078. self.config['unlock_duration'] = int(self.proxi.active_duration)
  1079. self.config['lock_command'] = self.wTree.get_widget('comboLock').child.get_text()
  1080. self.config['unlock_command'] = str(self.wTree.get_widget('comboUnlock').child.get_text())
  1081. self.config['proximity_command'] = str(self.wTree.get_widget('comboProxi').child.get_text())
  1082. self.config['proximity_interval'] = int(self.wTree.get_widget('hscaleProxi').get_value())
  1083. self.config['log_to_syslog'] = self.wTree.get_widget("checkSyslog").get_active()
  1084. self.config['log_syslog_facility'] = str(self.getComboValue(self.wTree.get_widget("comboFacility")))
  1085. self.config['log_to_file'] = self.wTree.get_widget("checkFile").get_active()
  1086. self.config['log_filelog_filename'] = str(self.wTree.get_widget("entryFile").get_text())
  1087. menuItem.show()
  1088. self.proxi.logger.configureFromConfig(self.config)
  1089. self.config.write()
  1090. self.gone_live = was_live
  1091. menuItem.show()
  1092.  
  1093. ## Callback for resetting the values for the min/max viewer.
  1094. def btnResetMinMax_clicked(self,widget, data = None):
  1095. menuItem.show()
  1096. self.minDist = -255
  1097. self.maxDist = 0
  1098. menuItem.show()
  1099.  
  1100. ## Callback called by almost all GUI elements if their values are changed.
  1101. # We don't react if we are still initializing (self.gone_live==False)
  1102. menuItem.show()
  1103. # because setting the values of the elements would already fire their change events.
  1104. # @see gone_live
  1105. def event_settings_changed(self,widget, data = None):
  1106. if self.gone_live:
  1107. self.writeSettings()
  1108. pass
  1109.  
  1110. ## Callback called by certain GUI elements if their values are changed.
  1111. # We don't react if we are still initializing (self.gone_live==False)
  1112. # because setting the values of the elements would already fire their change events.
  1113. # But in any case we kill a possibly existing connection.
  1114. # Changing the rfcomm channel e.g. fires this event instead of event_settings_changed.
  1115. # @see event_settings_changed
  1116. def event_settings_changed_reconnect(self,widget, data = None):
  1117. self.proxi.kill_connection()
  1118. if self.gone_live:
  1119. self.writeSettings()
  1120. pass
  1121.  
  1122. ## Callback called when one clicks into the channel scan results.
  1123. # It sets the 'selected channel' field to the selected channel
  1124. def event_scanChannelResult_changed(self,widget, data = None):
  1125. # Put selected channel in channel entry field
  1126. selection = self.wTree.get_widget("treeScanChannelResult").get_selection()
  1127. (model, iter) = selection.get_selected()
  1128. value = model.get_value(iter, 0)
  1129. self.wTree.get_widget("entryChannel").set_value(int(value))
  1130. self.writeSettings()
  1131.  
  1132. ## Callback to just close and not destroy the main window
  1133. def btnClose_clicked(self,widget, data = None):
  1134. self.Close()
  1135. return 1
  1136.  
  1137. ## Callback called when one clicks on the 'use selected address' button
  1138. # it copies the MAC address of the selected device into the mac address field.
  1139. def btnSelect_clicked(self,widget, data = None):
  1140. #Takes the selected entry in the mac/name table and enters its mac in the MAC field
  1141. selection = self.tree.get_selection()
  1142. selection.set_mode(gtk.SELECTION_SINGLE)
  1143. model, selection_iter = selection.get_selected()
  1144. if (selection_iter):
  1145. mac = self.model.get_value(selection_iter, 0)
  1146. self.wTree.get_widget("entryMAC").set_text(mac)
  1147. self.writeSettings()
  1148.  
  1149. ## Callback that is executed when the scan for devices button is clicked
  1150. # actually it starts the scanning asynchronously to have the gui redraw nicely before hanging :-)
  1151. def btnScan_clicked(self,widget, data = None):
  1152. # scan the area for bluetooth devices and show the results
  1153. watch = gtk.gdk.Cursor(gtk.gdk.WATCH)
  1154. self.window.window.set_cursor(watch)
  1155. self.model.clear()
  1156. self.model.append(['...', _('Now scanning...')])
  1157. self.setSensitiveConfigManagement(False)
  1158. gobject.idle_add(self.cb_btnScan_clicked)
  1159.  
  1160. ## Asynchronous callback function to do the actual device discovery scan
  1161. def cb_btnScan_clicked(self):
  1162. tmpMac = self.proxi.dev_mac
  1163. self.proxi.dev_mac = ''
  1164. self.proxi.kill_connection()
  1165. macs = []
  1166. try:
  1167. macs = self.proxi.get_device_list()
  1168. except:
  1169. macs = [['', _('Sorry, the bluetooth device is busy connecting.\nPlease enter a correct mac address or no address at all\nfor the config that is not connecting and try again later.')]]
  1170. self.proxi.dev_mac = tmpMac
  1171. self.model.clear()
  1172. for mac in macs:
  1173. self.model.append([mac[0], mac[1]])
  1174. self.window.window.set_cursor(None)
  1175. self.setSensitiveConfigManagement(True)
  1176.  
  1177. ## Callback that is executed when the scan channels button is clicked.
  1178. # It starts an asynchronous scan for the channels via initiating a ScanDevice object.
  1179. # That object does the magic, updates the gui and afterwards calls the callback function btnScanChannel_done .
  1180. def btnScanChannel_clicked(self,widget, data = None):
  1181. # scan the selected device for possibly usable channels
  1182. if self.scanningChannels:
  1183. self.wTree.get_widget("labelBtnScanChannel").set_label(_("Sca_n channels on device"))
  1184. self.wTree.get_widget("channelScanWindow").hide_all()
  1185. self.scanningChannels = False
  1186. self.scanner.doStop()
  1187. self.setSensitiveConfigManagement(True)
  1188. else:
  1189. self.setSensitiveConfigManagement(False)
  1190. mac = self.proxi.dev_mac
  1191. if self.pauseMode:
  1192. mac = self.lastMAC
  1193. was_paused = True
  1194. else:
  1195. self.pausePressed(None)
  1196. was_paused = False
  1197. self.wTree.get_widget("labelBtnScanChannel").set_label(_("Stop sca_nning"))
  1198. self.wTree.get_widget("channelScanWindow").show_all()
  1199. self.scanningChannels = True
  1200. dialog = gtk.MessageDialog(message_format=_("The scanning process tries to connect to each of the 30 possible ports. This will take some time and you should watch your bluetooth device for any actions to be taken. If possible click on accept/connect. If you are asked for a pin your device was not paired properly before, see the manual on how to fix this."),buttons=gtk.BUTTONS_OK)
  1201. dialog.connect("response", lambda x,y: dialog.destroy())
  1202. dialog.run()
  1203. self.scanner = ScanDevice(mac,self.modelScan,was_paused,self.btnScanChannel_done)
  1204. return 0
  1205.  
  1206. ## The callback that is called by the ScanDevice object that scans for a device's usable rfcomm channels.
  1207. # It is called after all channels have been scanned.
  1208. # @param was_paused informs this function about the pause state before the scan started.
  1209. # That state will be reconstructed by the function.
  1210. def btnScanChannel_done(self,was_paused):
  1211. self.wTree.get_widget("labelBtnScanChannel").set_label(_("Sca_n channels on device"))
  1212. self.scanningChannels = False
  1213. self.setSensitiveConfigManagement(True)
  1214. if not was_paused:
  1215. self.pausePressed(None)
  1216. self.proxi.Simulate = True
  1217.  
  1218.  
  1219. def Close(self):
  1220. #Hide the settings window
  1221. self.window.hide()
  1222. #Disable simulation mode for all configs
  1223. for config in configs:
  1224. config[2].Simulate = False
  1225.  
  1226. def quit(self, widget, data = None):
  1227. #try to close everything correctly
  1228. if( IMPORT_UNITY!=1 ):
  1229. self.icon.set_from_file(dist_path + icon_att)
  1230. else:
  1231. self.ind.set_icon(dist_path + icon_att)
  1232. self.ind.set_status(appindicator.STATUS_ACTIVE)
  1233. for config in configs:
  1234. config[2].logger.log_line(_('stopped.'))
  1235. config[2].Stop = 1
  1236. time.sleep(2)
  1237. gtk.main_quit()
  1238.  
  1239. ## Updates the GUI (values, icon, tooltip) with the latest values
  1240. # is always called via gobject.timeout_add call to run asynchronously without a seperate thread.
  1241. def updateState(self):
  1242. # update the display with newest measurement values (once per second)
  1243. newVal = int(self.proxi.Dist) # Values are negative!
  1244. if newVal > self.minDist:
  1245. self.minDist = newVal
  1246. if newVal < self.maxDist:
  1247. self.maxDist = newVal
  1248. self.wTree.get_widget("labState").set_text(_("min: ") +
  1249. str(-self.minDist) + _(" max: ") + str(-self.maxDist) + _(" state: ") + self.proxi.State)
  1250. self.wTree.get_widget("hscaleAct").set_value(-newVal)
  1251.  
  1252. #Update icon too
  1253. if self.pauseMode:
  1254. if( IMPORT_UNITY!=1 ):
  1255. self.icon.set_from_file(dist_path + icon_pause)
  1256. self.icon.set_tooltip(_('Pause Mode - not connected'))
  1257. else:
  1258. self.ind.set_icon(dist_path + icon_pause)
  1259. else:
  1260. # we have to show the 'worst case' since we only have one icon but many configs...
  1261. connection_state = 0
  1262. con_info = ''
  1263. con_icons = [icon_base, icon_att, icon_away, icon_con ]
  1264. for config in configs:
  1265. if config[2].ErrorMsg == "No connection found, trying to establish one...":
  1266. connection_state = 3
  1267. else:
  1268. if config[2].State != _('active'):
  1269. if (connection_state < 2):
  1270. connection_state = 2
  1271. else:
  1272. if newVal < config[2].active_limit:
  1273. if (connection_state < 1):
  1274. connection_state = 1
  1275. if (con_info != ''):
  1276. con_info = con_info + '\n\n'
  1277. con_info = con_info + config[0] + ': ' + _('Detected Distance: ') + str(-config[2].Dist) + '; ' + _("Current State: ") + config[2].State + '; ' + _("Status: ") + config[2].ErrorMsg
  1278. if self.proxi.Simulate:
  1279. simu = _('\nSimulation Mode (locking disabled)')
  1280. else:
  1281. simu = ''
  1282. if( IMPORT_UNITY!=1 ):
  1283. self.icon.set_from_file(dist_path + con_icons[connection_state])
  1284. self.icon.set_tooltip(con_info + '\n' + simu)
  1285. else:
  1286. self.ind.set_icon(dist_path + con_icons[connection_state])
  1287. self.timer = gobject.timeout_add(1000,self.updateState)
  1288.  
  1289. def proximityCommand(self):
  1290. #This is the proximity command callback called asynchronously as the updateState above
  1291. if self.proxi.State == _('active') and not self.proxi.Simulate:
  1292. ret_val = os.popen(self.config['proximity_command']).readlines()
  1293. self.timer2 = gobject.timeout_add(1000*self.config['proximity_interval'],self.proximityCommand)
  1294.  
  1295.  
  1296. ## This class creates all logging information in the desired form.
  1297. # We may log to syslog with a given syslog facility, while the severety is always info.
  1298. # We may also log a simple file.
  1299. class Logger(object):
  1300. ## Constructor does nothing special.
  1301. def __init__(self):
  1302. self.disable_syslogging()
  1303. self.disable_filelogging()
  1304.  
  1305. ## helper function to convert a string (given by a ComboBox) to the corresponding
  1306. # syslog module facility constant.
  1307. # @param facility One of the 8 "localX" facilities or "user".
  1308. def getFacilityFromString(self, facility):
  1309. #Returns the correct constant value for the given facility
  1310. dict = {
  1311. "local0" : syslog.LOG_LOCAL0,
  1312. "local1" : syslog.LOG_LOCAL1,
  1313. "local2" : syslog.LOG_LOCAL2,
  1314. "local3" : syslog.LOG_LOCAL3,
  1315. "local4" : syslog.LOG_LOCAL4,
  1316. "local5" : syslog.LOG_LOCAL5,
  1317. "local6" : syslog.LOG_LOCAL6,
  1318. "local7" : syslog.LOG_LOCAL7,
  1319. "user" : syslog.LOG_USER
  1320. }
  1321. return dict[facility]
  1322.  
  1323. ## Activates the logging to the syslog server.
  1324. def enable_syslogging(self, facility):
  1325. self.syslog_facility = self.getFacilityFromString(facility)
  1326. syslog.openlog('blueproximity',syslog.LOG_PID)
  1327. self.syslogging = True
  1328.  
  1329. ## Deactivates the logging to the syslog server.
  1330. def disable_syslogging(self):
  1331. self.syslogging = False
  1332. self.syslog_facility = None
  1333.  
  1334. ## Activates the logging to the given file.
  1335. # Actually tries to append to that file first, afterwards tries to write to it.
  1336. # If both don't work it gives an error message on stdout and does not activate the logging.
  1337. # @param filename The complete filename where to log to
  1338. def enable_filelogging(self, filename):
  1339. self.filename = filename
  1340. try:
  1341. #let's append
  1342. self.flog = file(filename,'a')
  1343. self.filelogging = True
  1344. except:
  1345. try:
  1346. #did not work, then try to create file (is this really needed or does python know another attribute to file()?
  1347. self.flog = file(filename,'w')
  1348. self.filelogging = True
  1349. except:
  1350. print _("Could not open logfile '%s' for writing." % filename)
  1351. self.disable_filelogging
  1352.  
  1353. ## Deactivates logging to a file.
  1354. def disable_filelogging(self):
  1355. try:
  1356. self.flog.close()
  1357. except:
  1358. pass
  1359. self.filelogging = False
  1360. self.filename = ''
  1361.  
  1362. ## Outputs a line to the logs. Takes care of where to put the line.
  1363. # @param line A string that is printed in the logs. The string is unparsed and not sanatized by any means.
  1364. def log_line(self, line):
  1365. if self.syslogging:
  1366. syslog.syslog(self.syslog_facility | syslog.LOG_NOTICE, line)
  1367. if self.filelogging:
  1368. try:
  1369. self.flog.write( time.ctime() + " blueproximity: " + line + "\n")
  1370. self.flog.flush()
  1371. except:
  1372. self.disable_filelogging()
  1373.  
  1374. ## Activate the logging mechanism that are requested by the given configuration.
  1375. # @param config A ConfigObj object containing the needed settings.
  1376. def configureFromConfig(self, config):
  1377. if config['log_to_syslog']:
  1378. self.enable_syslogging(config['log_syslog_facility'])
  1379. else:
  1380. self.disable_syslogging()
  1381. if config['log_to_file']:
  1382. if self.filelogging and config['log_filelog_filename'] != self.filename:
  1383. self.disable_filelogging()
  1384. self.enable_filelogging(config['log_filelog_filename'])
  1385. elif not self.filelogging:
  1386. self.enable_filelogging(config['log_filelog_filename'])
  1387.  
  1388. ## ScanDevice is a helper class used for scanning for open rfcomm channels
  1389. # on a given device. It uses asynchronous calls via gobject.timeout_add to
  1390. # not block the main process. It updates a given model after every scanned port
  1391. # and calls a callback function after finishing the scanning process.
  1392. class ScanDevice(object):
  1393. ## Constructor which sets up and immediately starts the scanning process.
  1394. # Note that the bluetooth device should not be connected while scanning occurs.
  1395. # @param device_mac MAC address of the bluetooth device to be scanned.
  1396. # @param was_paused A parameter to be passed to the finishing callback function.
  1397. # This is to automatically put the GUI in simulation mode if it has been before scanning. (dirty hack)
  1398. # @param callback A callback function to be called after scanning has been done.
  1399. # It takes one parameter which is preset by the was_paused parameter.
  1400. def __init__(self,device_mac,model,was_paused,callback):
  1401. self.mac = device_mac
  1402. self.model = model
  1403. self.stopIt = False
  1404. self.port = 1
  1405. self.timer = gobject.timeout_add(500,self.runStep)
  1406. self.model.clear()
  1407. self.was_paused = was_paused
  1408. self.callback = callback
  1409.  
  1410. ## Checks whether a certain port on the given mac address is reachable.
  1411. # @param port An integer from 1 to 30 giving the rfcomm channel number to try to reach.
  1412. # The function does not return True/False but the actual translated strings.
  1413. def scanPortResult(self,port):
  1414. # here we scan exactly one port and give a textual result
  1415. _sock = bluez.btsocket()
  1416. sock = bluetooth.BluetoothSocket( bluetooth.RFCOMM , _sock )
  1417. try:
  1418. sock.connect((self.mac, port))
  1419. sock.close
  1420. return _("usable")
  1421. except:
  1422. return _("closed or denied")
  1423.  
  1424. ## Asynchronous working thread.
  1425. # It scans a single port at a time and reruns with the next one in the next loop.
  1426. def runStep(self):
  1427. # here the scanning of all ports is done
  1428. self.model.append([str(self.port), self.scanPortResult(self.port)])
  1429. self.port = self.port + 1
  1430. if not self.port > 30 and not self.stopIt:
  1431. self.timer = gobject.timeout_add(500,self.runStep)
  1432. else:
  1433. self.callback(self.was_paused)
  1434.  
  1435. def doStop(self):
  1436. self.stopIt = True
  1437.  
  1438.  
  1439. ## This class does 'all the magic' like regular device detection and decision making
  1440. # whether a device is known as present or away. Here is where all the bluetooth specific
  1441. # part takes place. It is build to be run a a seperate thread and would run perfectly without any GUI.
  1442. # Please note that the present-command is issued by the GUI whereas the locking and unlocking
  1443. # is called by this class. This is inconsitent and to be changed in a future release.
  1444. class Proximity (threading.Thread):
  1445. ## Constructor to setup our local variables and initialize threading.
  1446. # @param config a ConfigObj object that stores all our settings
  1447. def __init__(self,config):
  1448. threading.Thread.__init__(self, name="WorkerThread")
  1449. self.config = config
  1450. self.Dist = -255
  1451. self.State = _("gone")
  1452. self.Simulate = False
  1453. self.Stop = False
  1454. self.procid = 0
  1455. self.dev_mac = self.config['device_mac']
  1456. self.dev_channel = self.config['device_channel']
  1457. self.ringbuffer_size = self.config['buffer_size']
  1458. self.ringbuffer = [-254] * self.ringbuffer_size
  1459. self.ringbuffer_pos = 0
  1460. self.gone_duration = self.config['lock_duration']
  1461. self.gone_limit = -self.config['lock_distance']
  1462. self.active_duration = self.config['unlock_duration']
  1463. self.active_limit = -self.config['unlock_distance']
  1464. self.ErrorMsg = _("Initialized...")
  1465. self.sock = None
  1466. self.ignoreFirstTransition = True
  1467. self.logger = Logger()
  1468. self.logger.configureFromConfig(self.config)
  1469. self.timeAct = 0
  1470. self.timeGone = 0
  1471. self.timeProx = 0
  1472.  
  1473. ## Returns all active bluetooth devices found. This is a blocking call.
  1474. def get_device_list(self):
  1475. ret_tab = list()
  1476. nearby_devices = bluetooth.discover_devices()
  1477. for bdaddr in nearby_devices:
  1478. ret_tab.append([str(bdaddr),str(bluetooth.lookup_name( bdaddr ))])
  1479. return ret_tab
  1480.  
  1481. ## Kills the rssi detection connection.
  1482. def kill_connection(self):
  1483. if self.sock != None:
  1484. self.sock.close()
  1485. self.sock = None
  1486. return 0
  1487.  
  1488. ## This function is NOT IN USE. It is a try to create a python only way to
  1489. # get the rssi values for a connected device. It does not work at this time.
  1490. def get_proximity_by_mac(self,dev_mac):
  1491. sock = bluez.hci_open_dev(dev_id)
  1492. old_filter = sock.getsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, 14)
  1493.  
  1494. # perform a device inquiry on bluetooth device #0
  1495. # The inquiry should last 8 * 1.28 = 10.24 seconds
  1496. # before the inquiry is performed, bluez should flush its cache of
  1497. # previously discovered devices
  1498. flt = bluez.hci_filter_new()
  1499. bluez.hci_filter_all_events(flt)
  1500. bluez.hci_filter_set_ptype(flt, bluez.HCI_EVENT_PKT)
  1501. sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, flt )
  1502.  
  1503. duration = 4
  1504. max_responses = 255
  1505. cmd_pkt = struct.pack("BBBBB", 0x33, 0x8b, 0x9e, duration, max_responses)
  1506. bluez.hci_send_cmd(sock, bluez.OGF_LINK_CTL, bluez.OCF_INQUIRY, cmd_pkt)
  1507.  
  1508. results = []
  1509.  
  1510. done = False
  1511. while not done:
  1512. pkt = sock.recv(255)
  1513. ptype, event, plen = struct.unpack("BBB", pkt[:3])
  1514. if event == bluez.EVT_INQUIRY_RESULT_WITH_RSSI:
  1515. pkt = pkt[3:]
  1516. nrsp = struct.unpack("B", pkt[0])[0]
  1517. for i in range(nrsp):
  1518. addr = bluez.ba2str( pkt[1+6*i:1+6*i+6] )
  1519. rssi = struct.unpack("b", pkt[1+13*nrsp+i])[0]
  1520. results.append( ( addr, rssi ) )
  1521. print "[%s] RSSI: [%d]" % (addr, rssi)
  1522. elif event == bluez.EVT_INQUIRY_COMPLETE:
  1523. done = True
  1524. elif event == bluez.EVT_CMD_STATUS:
  1525. status, ncmd, opcode = struct.unpack("BBH", pkt[3:7])
  1526. if status != 0:
  1527. print "uh oh..."
  1528. printpacket(pkt[3:7])
  1529. done = True
  1530. else:
  1531. print "unrecognized packet type 0x%02x" % ptype
  1532.  
  1533.  
  1534. # restore old filter
  1535. sock.setsockopt( bluez.SOL_HCI, bluez.HCI_FILTER, old_filter )
  1536.  
  1537. sock.close()
  1538. return results
  1539.  
  1540.  
  1541. ## Returns the rssi value of a connection to the given mac address.
  1542. # @param dev_mac mac address of the device to check.
  1543. # This should also be removed but I still have to find a way to read the rssi value from python
  1544. def get_proximity_once(self,dev_mac):
  1545. ret_val = os.popen("hcitool rssi " + dev_mac + " 2>/dev/null").readlines()
  1546. if ret_val == []:
  1547. ret_val = -255
  1548. else:
  1549. ret_val = ret_val[0].split(':')[1].strip(' ')
  1550. return int(ret_val)
  1551.  
  1552. ## Fire up an rfcomm connection to a certain device on the given channel.
  1553. # Don't forget to set up your phone not to ask for a connection.
  1554. # (at least for this computer.)
  1555. # @param dev_mac mac address of the device to connect to.
  1556. # @param dev_channel rfcomm channel we want to connect to.
  1557. def get_connection(self,dev_mac,dev_channel):
  1558. try:
  1559. self.procid = 1
  1560. _sock = bluez.btsocket()
  1561. self.sock = bluetooth.BluetoothSocket( bluetooth.RFCOMM , _sock )
  1562. self.sock.connect((dev_mac, dev_channel))
  1563. except:
  1564. self.procid = 0
  1565. pass
  1566. return self.procid
  1567.  
  1568. def run_cycle(self,dev_mac,dev_channel):
  1569. # reads the distance and averages it over the ringbuffer
  1570. self.ringbuffer_pos = (self.ringbuffer_pos + 1) % self.ringbuffer_size
  1571. self.ringbuffer[self.ringbuffer_pos] = self.get_proximity_once(dev_mac)
  1572. ret_val = 0
  1573. for val in self.ringbuffer:
  1574. ret_val = ret_val + val
  1575. if self.ringbuffer[self.ringbuffer_pos] == -255:
  1576. self.ErrorMsg = _("No connection found, trying to establish one...")
  1577. self.kill_connection()
  1578. self.get_connection(dev_mac,dev_channel)
  1579. return int(ret_val / self.ringbuffer_size)
  1580.  
  1581. def go_active(self):
  1582. #The Doctor is in
  1583. if self.ignoreFirstTransition:
  1584. self.ignoreFirstTransition = False
  1585. else:
  1586. self.logger.log_line(_('screen is unlocked'))
  1587. if (self.timeAct==0):
  1588. self.timeAct = time.time()
  1589. ret_val = os.popen(self.config['unlock_command']).readlines()
  1590. self.timeAct = 0
  1591. else:
  1592. self.logger.log_line(_('A command for %s has been skipped because the former command did not finish yet.') % _('unlocking'))
  1593. self.ErrorMsg = _('A command for %s has been skipped because the former command did not finish yet.') % _('unlocking')
  1594.  
  1595. def go_gone(self):
  1596. #The Doctor is out
  1597. if self.ignoreFirstTransition:
  1598. self.ignoreFirstTransition = False
  1599. else:
  1600. self.logger.log_line(_('screen is locked'))
  1601. if (self.timeGone==0):
  1602. self.timeGone = time.time()
  1603. ret_val = os.popen(self.config['lock_command']).readlines()
  1604. self.timeGone = 0
  1605. else:
  1606. self.logger.log_line(_('A command for %s has been skipped because the former command did not finish yet.') % _('locking'))
  1607. self.ErrorMsg = _('A command for %s has been skipped because the former command did not finish yet.') % _('locking')
  1608.  
  1609. def go_proximity(self):
  1610. #The Doctor is still in
  1611. if (self.timeProx==0):
  1612. self.timeProx = time.time()
  1613. ret_val = os.popen(self.config['proximity_command']).readlines()
  1614. self.timeProx = 0
  1615. else:
  1616. self.logger.log_line(_('A command for %s has been skipped because the former command did not finish yet.') % _('proximity'))
  1617. self.ErrorMsg = _('A command for %s has been skipped because the former command did not finish yet.') % _('proximity')
  1618.  
  1619. ## This is the main loop of the proximity detection engine.
  1620. # It checks the rssi value against limits and invokes all commands.
  1621. def run(self):
  1622. duration_count = 0
  1623. state = _("gone")
  1624. proxiCmdCounter = 0
  1625. while not self.Stop:
  1626. #print "tick"
  1627. try:
  1628. if self.dev_mac != "":
  1629. self.ErrorMsg = _("running...")
  1630. dist = self.run_cycle(self.dev_mac,self.dev_channel)
  1631. else:
  1632. dist = -255
  1633. self.ErrorMsg = "No bluetooth device configured..."
  1634. if state == _("gone"):
  1635. if dist>=self.active_limit:
  1636. duration_count = duration_count + 1
  1637. if duration_count >= self.active_duration:
  1638. state = _("active")
  1639. duration_count = 0
  1640. if not self.Simulate:
  1641. # start the process asynchronously so we are not hanging here...
  1642. timerAct = gobject.timeout_add(5,self.go_active)
  1643. #self.go_active()
  1644. else:
  1645. duration_count = 0
  1646. else:
  1647. if dist<=self.gone_limit:
  1648. duration_count = duration_count + 1
  1649. if duration_count >= self.gone_duration:
  1650. state = _("gone")
  1651. proxiCmdCounter = 0
  1652. duration_count = 0
  1653. if not self.Simulate:
  1654. # start the process asynchronously so we are not hanging here...
  1655. timerGone = gobject.timeout_add(5,self.go_gone)
  1656. #self.go_gone()
  1657. else:
  1658. duration_count = 0
  1659. proxiCmdCounter = proxiCmdCounter + 1
  1660. if dist != self.Dist or state != self.State:
  1661. #print "Detected distance atm: " + str(dist) + "; state is " + state
  1662. pass
  1663. self.State = state
  1664. self.Dist = dist
  1665. # let's handle the proximity command
  1666. if (proxiCmdCounter >= self.config['proximity_interval']) and not self.Simulate and (self.config['proximity_command']!=''):
  1667. proxiCmdCounter = 0
  1668. # start the process asynchronously so we are not hanging here...
  1669. timerProx = gobject.timeout_add(5,self.go_proximity)
  1670. time.sleep(1)
  1671. except KeyboardInterrupt:
  1672. break
  1673. self.kill_connection()
  1674.  
  1675.  
  1676. if __name__=='__main__':
  1677. gtk.glade.bindtextdomain(APP_NAME, local_path)
  1678. gtk.glade.textdomain(APP_NAME)
  1679.  
  1680.  
  1681. # react on ^C
  1682. signal.signal(signal.SIGINT, signal.SIG_DFL)
  1683. # read config if any
  1684. configs = []
  1685. new_config = True
  1686. conf_dir = os.path.join(os.getenv('HOME'),'.blueproximity')
  1687. try:
  1688. # check if config directory exists
  1689. os.mkdir(conf_dir)
  1690. print(_("Creating new config directory '%s'.") % conf_dir)
  1691. # we should now look for an old config file and try to move it to a better place...
  1692. os.rename(os.path.join(os.getenv('HOME'),'.blueproximityrc'),os.path.join(conf_dir,_("standard")+".conf"))
  1693. print(_("Moved old configuration to the new config directory."))
  1694. except:
  1695. # we can't create it because it is already there...
  1696. pass
  1697.  
  1698. # now look for .conf files in there
  1699. vdt = Validator()
  1700. for filename in os.listdir(conf_dir):
  1701. if filename.endswith('.conf'):
  1702. try:
  1703. # add every valid .conf file to the array of configs
  1704. config = ConfigObj(os.path.join(conf_dir,filename),{'create_empty':False,'file_error':True,'configspec':conf_specs})
  1705. # first validate it
  1706. config.validate(vdt, copy=True)
  1707. # rewrite it in a secure manner
  1708. config.write()
  1709. # if everything worked add this config as functioning
  1710. configs.append ( [filename[:-5], config])
  1711. new_config = False
  1712. print(_("Using config file '%s'.") % filename)
  1713. except:
  1714. print(_("'%s' is not a valid config file.") % filename)
  1715.  
  1716. # no previous configuration could be found so let's create a new one
  1717. if new_config:
  1718. config = ConfigObj(os.path.join(conf_dir, _('standard') + '.conf'),{'create_empty':True,'file_error':False,'configspec':conf_specs})
  1719. # next line fixes a problem with creating empty strings in default values for configobj
  1720. config['device_mac'] = ''
  1721. config.validate(vdt, copy=True)
  1722. # write it in a secure manner
  1723. config.write()
  1724. configs.append ( [_('standard'), config])
  1725. # we can't log these messages since logging is not yet configured, so we just print it to stdout
  1726. print(_("Creating new configuration."))
  1727. print(_("Using config file '%s'.") % _('standard'))
  1728.  
  1729. # now start the proximity detection for each configuration
  1730. for config in configs:
  1731. p = Proximity(config[1])
  1732. p.start()
  1733. config.append(p)
  1734.  
  1735. configs.sort()
  1736. # the idea behind 'configs' is an array containing the name, the configobj and the proximity object
  1737. pGui = ProximityGUI(configs,new_config)
  1738.  
  1739. # make GTK threadable
  1740. gtk.gdk.threads_init()
  1741.  
  1742. # aaaaand action!
  1743. gtk.main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement