screend.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. #!/usr/bin/env python3
  2. from http.server import HTTPServer, BaseHTTPRequestHandler
  3. from io import BytesIO
  4. import json
  5. from subprocess import check_call, check_output
  6. from multiprocessing import Process
  7. import os
  8. import time, arrow
  9. import requests
  10. import socket, datetime
  11. from signal import signal, SIGINT
  12. import sys
  13. import humanize
  14. PATH = os.path.dirname(os.path.realpath(__file__))
  15. # {'commits': [{'timestamp': '2019-02-22T22:40:15+01:00', 'id': 'b679892b2176a0a23195784f082ee7902709cd10', 'modified': ['README.md'], 'author': {'name': 'Francois Deppierraz', 'username': 'francois', 'email': 'francois@ctrlaltdel.ch'}, 'removed': [], 'added': [], 'url': 'https://git.fixme.ch/francois/screen/commit/b679892b2176a0a23195784f082ee7902709cd10', 'committer': {'name': 'Francois Deppierraz', 'username': 'francois', 'email': 'francois@ctrlaltdel.ch'}, 'message': 'test\n'}], 'before': 'e82e9dc32b5fbe7bf405e4b514de029a87a01e3a', 'pusher': {'id': 19, 'login': 'francois', 'username': 'francois', 'full_name': '', 'avatar_url': 'https://git.fixme.ch/avatars/19', 'email': 'francois@ctrlaltdel.ch'}, 'compare_url': 'https://git.fixme.ch/francois/screen/compare/e82e9dc32b5fbe7bf405e4b514de029a87a01e3a...b679892b2176a0a23195784f082ee7902709cd10', 'ref': 'refs/heads/master', 'sender': {'id': 19, 'login': 'francois', 'username': 'francois', 'full_name': '', 'avatar_url': 'https://git.fixme.ch/avatars/19', 'email': 'francois@ctrlaltdel.ch'}, 'after': 'b679892b2176a0a23195784f082ee7902709cd10', 'repository': {'stars_count': 0, 'size': 110592, 'website': '', 'full_name': 'francois/screen', 'html_url': 'https://git.fixme.ch/francois/screen', 'name': 'screen', 'mirror': False, 'forks_count': 0, 'clone_url': 'https://git.fixme.ch/francois/screen.git', 'open_issues_count': 0, 'description': '', 'updated_at': '2019-02-22T22:39:49+01:00', 'created_at': '2019-02-22T22:23:09+01:00', 'empty': False, 'fork': False, 'watchers_count': 1, 'id': 219, 'default_branch': 'master', 'parent': None, 'private': False, 'ssh_url': 'ssh://gogs@git.fixme.ch:1337/francois/screen.git', 'owner': {'id': 19, 'login': 'francois', 'username': 'francois', 'full_name': '', 'avatar_url': 'https://git.fixme.ch/avatars/19', 'email': 'francois@ctrlaltdel.ch'}}}
  16. def send_data(uri, data):
  17. # Limited to 1.5KB
  18. sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  19. sock.sendto(b'%s:%s' % (uri.encode('ascii'), data.encode('utf8')) , ('127.0.0.1', 4444))
  20. def webhook():
  21. class WebhookHTTPRequestHandler(BaseHTTPRequestHandler):
  22. def do_POST(self):
  23. content_length = int(self.headers['Content-Length'])
  24. body = self.rfile.read(content_length)
  25. self.send_response(200)
  26. self.end_headers()
  27. data = json.loads(body.decode('utf-8'))
  28. print(data)
  29. check_call(('git', 'fetch'))
  30. check_call(('git', 'reset', '--hard', 'origin/master'))
  31. httpd = HTTPServer(('0.0.0.0', 8000), WebhookHTTPRequestHandler)
  32. httpd.serve_forever()
  33. def calendar():
  34. import cal
  35. while True:
  36. events = cal.get_data()
  37. filepath = PATH + '/events.json'
  38. if os.path.getsize(filepath) != len(json.dumps(events)):
  39. f = open(filepath, 'w+')
  40. f.write(json.dumps(events))
  41. f.close()
  42. time.sleep(3600)
  43. def power():
  44. SENSORS = (
  45. '34cde81adabfb1ce819eca8fea6949b6',
  46. 'b7755b5f3ec05fcdc67f449241a9912a',
  47. 'e67e0685f747b30d855108ab781abdfc'
  48. )
  49. URL = "http://62.220.135.196:8080/sensor/{}?version=1.0&interval=minute&unit=watt"
  50. while True:
  51. power = 0
  52. for sensor in SENSORS:
  53. try:
  54. power += requests.get(URL.format(sensor)).json()[-2][1]
  55. except:
  56. print("Failed to read sensor {}".format(sensor))
  57. send_data('screen/power/set', str(power))
  58. time.sleep(1)
  59. def bus():
  60. URL = 'https://transport.opendata.ch/v1/stationboard?id=8592223&limit=1&type=departure&transportations[]=bus'
  61. while True:
  62. data = requests.get(URL).json()
  63. ts = data['stationboard'][0]['stop']['departureTimestamp']
  64. f = open(PATH + '/bus.txt', 'w')
  65. f.write(arrow.get(ts).humanize())
  66. f.close()
  67. time.sleep(30)
  68. def clock():
  69. while True:
  70. now = datetime.datetime.now()
  71. since_midnight = (
  72. now -
  73. now.replace(hour=0, minute=0, second=0)
  74. ).seconds
  75. send_data('screen/analogclock/clock/set', str(since_midnight))
  76. time.sleep(1)
  77. # Copied from humanize.naturalsize and s/bytes/bits/g
  78. def naturalsize(value, binary=False, gnu=False, format='%.1f'):
  79. """Format a number of byteslike a human readable filesize (eg. 10 kB). By
  80. default, decimal suffixes (kB, MB) are used. Passing binary=true will use
  81. binary suffixes (KiB, MiB) are used and the base will be 2**10 instead of
  82. 10**3. If ``gnu`` is True, the binary argument is ignored and GNU-style
  83. (ls -sh style) prefixes are used (K, M) with the 2**10 definition.
  84. Non-gnu modes are compatible with jinja2's ``filesizeformat`` filter."""
  85. suffixes = {
  86. 'decimal': ('kb', 'Mb', 'Gb', 'Tb', 'Pb', 'Eb', 'Zb', 'Yb'),
  87. 'binary': ('Kib', 'Mib', 'Gib', 'Tib', 'Pib', 'Eib', 'Zib', 'Yib'),
  88. 'gnu': "KMGTPEZY",
  89. }
  90. if gnu: suffix = suffixes['gnu']
  91. elif binary: suffix = suffixes['binary']
  92. else: suffix = suffixes['decimal']
  93. base = 1024 if (gnu or binary) else 1000
  94. bytes = float(value)
  95. if bytes == 1 and not gnu: return '1 bit'
  96. elif bytes < base and not gnu: return '%d bits' % bytes
  97. elif bytes < base and gnu: return '%db' % bytes
  98. for i,s in enumerate(suffix):
  99. unit = base ** (i+2)
  100. if bytes < unit and not gnu:
  101. return (format + ' %s') % ((base * bytes / unit), s)
  102. elif bytes < unit and gnu:
  103. return (format + '%s') % ((base * bytes / unit), s)
  104. if gnu:
  105. return (format + '%s') % ((base * bytes / unit), s)
  106. return (format + ' %s') % ((base * bytes / unit), s)
  107. def snmp():
  108. INTERVAL = 3
  109. previous_input = 0
  110. previous_output = 0
  111. while True:
  112. result = check_output(('snmpget', '-c', 'uDee4pie', '-v', '2c', '192.168.130.1', '1.3.6.1.2.1.31.1.1.1.6.7', '1.3.6.1.2.1.31.1.1.1.10.7')).decode('ascii').split('\n')
  113. input = int(result[0].split(' ')[3])
  114. output = int(result[1].split(' ')[3])
  115. # print("input=%d, output=%d" % (input, output))
  116. in_bw = naturalsize(8 * round((input - previous_input) / INTERVAL, 1))
  117. out_bw = naturalsize(8 * round((output - previous_output) / INTERVAL, 1))
  118. if previous_input:
  119. send_data('screen/network/in/set', str(in_bw))
  120. send_data('screen/network/out/set', str(out_bw))
  121. previous_input = input
  122. previous_output = output
  123. time.sleep(INTERVAL)
  124. def spaceapi():
  125. while True:
  126. data = requests.get('https://fixme.ch/cgi-bin/spaceapi.py').json()
  127. filepath = PATH + '/spaceapi.json'
  128. if os.path.getsize(filepath) != len(json.dumps(data)):
  129. f = open(filepath, 'w+')
  130. f.write(json.dumps(data))
  131. f.close()
  132. time.sleep(10)
  133. def cleanExit(sig, frame):
  134. sys.exit(0)
  135. if __name__ == '__main__':
  136. signal(SIGINT, cleanExit)
  137. functions = [
  138. bus,
  139. webhook,
  140. calendar,
  141. power,
  142. clock,
  143. snmp,
  144. spaceapi,
  145. ]
  146. processes = {f: Process(target=f) for f in functions}
  147. [p.start() for p in processes.values()] # start the processes
  148. # Control loop
  149. while True:
  150. time.sleep(0.5)
  151. for f,p in processes.items():
  152. if not p.is_alive():
  153. processes[f] = Process(target=f)
  154. processes[f].start()
  155. print('restarted {}'.format(f))