stdd 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. #! /usr/bin/env python3
  2. # -*- coding: utf8 -*-
  3. #
  4. # This file is part of stdd, the simple time display daemon,
  5. # written by Helmut Pozimski <helmut@pozimski.eu> 2013-2014,
  6. # licensed under the 3-clause BSD license
  7. """ Main script for stdd, puts all the modules together, starts the daemon and
  8. takes care of sending the right values to the display.
  9. """
  10. import datetime
  11. import time
  12. import sys
  13. import signal
  14. import os
  15. import logging
  16. import logging.handlers
  17. from optparse import OptionParser
  18. import stddlib.configuration
  19. from stddlib import daemon
  20. from Adafruit_LED_Backpack import SevenSegment
  21. PARSER = OptionParser(prog="stdd", version="%prog 0.9.8", add_help_option=True)
  22. PARSER.add_option("-d", "--daemon", action="store_true", dest="daemon",
  23. help="run stdd as daemon")
  24. PARSER.add_option("-u", "--user", dest="user", help="define an unprivileged \
  25. user to run the daemon")
  26. PARSER.add_option("-g", "--group", dest="group", help="define an unprivileged\
  27. group to run the daemon")
  28. PARSER.add_option("-c", "--config", dest="config", help="define an\
  29. alternative path to the configuration file")
  30. (OPTIONS, ARGUMENTS) = PARSER.parse_args()
  31. """ define a sighandler to properly catch signals """
  32. def sighandler(signum, frame):
  33. """ Catches signals and ensures a clean exit. """
  34. if signum == 2:
  35. LOGGER.info("received SIGINT, stopping daemon")
  36. elif signum == 15:
  37. LOGGER.info("received SIGTERM, stopping daemon")
  38. DISPLAY.set_digit(0, 0)
  39. DISPLAY.set_digit(1, 0)
  40. DISPLAY.set_digit(2, 0)
  41. DISPLAY.set_digit(3, 0)
  42. DISPLAY.write_display()
  43. if OPTIONS.daemon:
  44. if DAEMON.stop():
  45. sys.exit(0)
  46. else:
  47. LOGGER.error("stopping daemon failed, PID file was not deleted!")
  48. sys.exit(1)
  49. sys.exit(0)
  50. # create the configuration object according to the given parameters and
  51. # read the file itself
  52. signal.signal(signal.SIGTERM, sighandler)
  53. signal.signal(signal.SIGINT, sighandler)
  54. CONFIG = stddlib.configuration.Conf()
  55. if OPTIONS.config is not None:
  56. CONFIG.read(OPTIONS.config)
  57. else:
  58. CONFIG.read()
  59. CONFIG.analyze()
  60. # create a logger to log errors according to configuration
  61. LOGGER = logging.getLogger("stdd")
  62. if CONFIG.syslog_level == "debug":
  63. LOGGER.setLevel(logging.DEBUG)
  64. elif CONFIG.syslog_level == "error":
  65. LOGGER.setLevel(logging.ERROR)
  66. else:
  67. LOGGER.setLevel(logging.INFO)
  68. if CONFIG.syslog_facility == "daemon":
  69. SYSLOG_HANDLER = logging.handlers.SysLogHandler(
  70. "/dev/log",
  71. facility=logging.handlers.SysLogHandler.LOG_DAEMON)
  72. else:
  73. SYSLOG_HANDLER = logging.handlers.SysLogHandler("/dev/log")
  74. CONSOLE_HANDLER = logging.StreamHandler()
  75. FORMATTER = logging.Formatter("%(name)s[" + str(os.getpid()) +
  76. "]: %(message)s")
  77. SYSLOG_HANDLER.setFormatter(FORMATTER)
  78. CONSOLE_HANDLER.setFormatter(FORMATTER)
  79. if OPTIONS.daemon:
  80. LOGGER.addHandler(SYSLOG_HANDLER)
  81. else:
  82. LOGGER.addHandler(CONSOLE_HANDLER)
  83. if OPTIONS.daemon:
  84. if os.access("/run", os.F_OK & os.W_OK):
  85. DAEMON = daemon.Daemon("/run/stdd", "stdd.pid")
  86. else:
  87. DAEMON = daemon.Daemon("/var/run/stdd", "stdd.pid")
  88. DAEMON.daemonize()
  89. DAEMON.start()
  90. LOGGER.info("daemon started")
  91. CMDLINE = ""
  92. CMDCOUNTER = 0
  93. for element in sys.argv:
  94. if CMDCOUNTER > 0:
  95. CMDLINE = CMDLINE + " " + element
  96. else:
  97. CMDLINE = CMDLINE + element
  98. CMDCOUNTER += 1
  99. DAEMON.set_name("stdd", CMDLINE)
  100. if OPTIONS.user is not None and OPTIONS.group is not None:
  101. DAEMON.drop_privileges(OPTIONS.user, OPTIONS.group)
  102. LOGGER.debug("dropped privileges, now running as " + OPTIONS.user +
  103. " and group" + OPTIONS.group)
  104. # Initialize the display object
  105. DISPLAY = SevenSegment.SevenSegment(address=int(CONFIG.hw_address))
  106. DISPLAY.begin()
  107. LOGGER.debug("opened hardware address %s", CONFIG.hw_address)
  108. # Set the brightness according to the configuration
  109. if CONFIG.set_brightness_high < datetime.datetime.now().time()\
  110. < CONFIG.set_brightness_low:
  111. LOGGER.debug("setting display brightness high")
  112. DISPLAY.set_brightness(CONFIG.brightness_high)
  113. else:
  114. LOGGER.debug("setting display brightness low")
  115. DISPLAY.set_brightness(CONFIG.brightness_low)
  116. # Define the main loop
  117. def main():
  118. """ Main loop of the daemon. """
  119. minute_written = 61
  120. while True:
  121. date_now = datetime.datetime.now()
  122. LOGGER.debug("got datetime: " + str(date_now))
  123. minute = date_now.minute
  124. hour = date_now.hour
  125. if CONFIG.blink_colon:
  126. LOGGER.debug("blinking middle colon")
  127. DISPLAY.set_colon(date_now.second % 2)
  128. else:
  129. DISPLAY.set_colon(True)
  130. # set the display brightness high or low when the point in time
  131. # defined is reached.
  132. if CONFIG.set_brightness_low.hour == hour:
  133. if CONFIG.set_brightness_low.minute == minute:
  134. LOGGER.debug("setting display brightness low")
  135. DISPLAY.set_brightness(CONFIG.brightness_low)
  136. if CONFIG.set_brightness_high.hour == hour:
  137. if CONFIG.set_brightness_high.minute == minute:
  138. LOGGER.debug("setting display brightness high")
  139. DISPLAY.set_brightness(CONFIG.brightness_high)
  140. if minute_written != minute:
  141. try:
  142. position2 = str(hour)[1]
  143. except IndexError:
  144. position2 = str(hour)[0]
  145. position1 = "0"
  146. else:
  147. position1 = str(hour)[0]
  148. try:
  149. position4 = str(minute)[1]
  150. except IndexError:
  151. position4 = str(minute)[0]
  152. position3 = "0"
  153. else:
  154. position3 = str(minute)[0]
  155. LOGGER.debug("writing time to display")
  156. DISPLAY.set_digit(0, position1)
  157. DISPLAY.set_digit(1, position2)
  158. DISPLAY.set_digit(2, position3)
  159. DISPLAY.set_digit(3, position4)
  160. minute_written = minute
  161. DISPLAY.write_display()
  162. time.sleep(1)
  163. if __name__ == "__main__":
  164. main()