screend.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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. limit = 2
  61. URL = 'https://transport.opendata.ch/v1/stationboard?id=8592223&limit={}&type=departure&transportations[]=bus'.format(limit)
  62. while True:
  63. data = requests.get(URL).json()
  64. times = []
  65. for i in range(limit):
  66. _ts = data['stationboard'][i]['stop']['departureTimestamp']
  67. _time = arrow.get(_ts).humanize()
  68. times.append(_time)
  69. f = open(PATH + '/bus.txt', 'w')
  70. f.write(' and '.join(times))
  71. f.close()
  72. time.sleep(30)
  73. def clock():
  74. while True:
  75. now = datetime.datetime.now()
  76. since_midnight = (
  77. now -
  78. now.replace(hour=0, minute=0, second=0)
  79. ).seconds
  80. send_data('screen/analogclock/clock/set', str(since_midnight))
  81. time.sleep(1)
  82. def random_image():
  83. #URL='http://aws.random.cat/meow'
  84. URL='https://picsum.photos/id/10/640/480'
  85. while True:
  86. j = requests.get(URL).json()
  87. response = requests.get(j['file'], stream=True)
  88. handle = open('./random_image.jpg', "wb")
  89. for chunk in response.iter_content(chunk_size=512):
  90. if chunk:
  91. handle.write(chunk)
  92. handle.close()
  93. time.sleep(60)
  94. # Copied from humanize.naturalsize and s/bytes/bits/g
  95. def naturalsize(value, binary=False, gnu=False, format='%.1f'):
  96. """Format a number of byteslike a human readable filesize (eg. 10 kB). By
  97. default, decimal suffixes (kB, MB) are used. Passing binary=true will use
  98. binary suffixes (KiB, MiB) are used and the base will be 2**10 instead of
  99. 10**3. If ``gnu`` is True, the binary argument is ignored and GNU-style
  100. (ls -sh style) prefixes are used (K, M) with the 2**10 definition.
  101. Non-gnu modes are compatible with jinja2's ``filesizeformat`` filter."""
  102. suffixes = {
  103. 'decimal': ('kb', 'Mb', 'Gb', 'Tb', 'Pb', 'Eb', 'Zb', 'Yb'),
  104. 'binary': ('Kib', 'Mib', 'Gib', 'Tib', 'Pib', 'Eib', 'Zib', 'Yib'),
  105. 'gnu': "KMGTPEZY",
  106. }
  107. if gnu: suffix = suffixes['gnu']
  108. elif binary: suffix = suffixes['binary']
  109. else: suffix = suffixes['decimal']
  110. base = 1024 if (gnu or binary) else 1000
  111. bytes = float(value)
  112. if bytes == 1 and not gnu: return '1 bit'
  113. elif bytes < base and not gnu: return '%d bits' % bytes
  114. elif bytes < base and gnu: return '%db' % bytes
  115. for i,s in enumerate(suffix):
  116. unit = base ** (i+2)
  117. if bytes < unit and not gnu:
  118. return (format + ' %s') % ((base * bytes / unit), s)
  119. elif bytes < unit and gnu:
  120. return (format + '%s') % ((base * bytes / unit), s)
  121. if gnu:
  122. return (format + '%s') % ((base * bytes / unit), s)
  123. return (format + ' %s') % ((base * bytes / unit), s)
  124. def snmp():
  125. INTERVAL = 3
  126. previous_input = 0
  127. previous_output = 0
  128. while True:
  129. 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')
  130. input = int(result[0].split(' ')[3])
  131. output = int(result[1].split(' ')[3])
  132. # print("input=%d, output=%d" % (input, output))
  133. in_bw = naturalsize(8 * round((input - previous_input) / INTERVAL, 1))
  134. out_bw = naturalsize(8 * round((output - previous_output) / INTERVAL, 1))
  135. if previous_input:
  136. send_data('screen/network/in/set', str(in_bw))
  137. send_data('screen/network/out/set', str(out_bw))
  138. previous_input = input
  139. previous_output = output
  140. time.sleep(INTERVAL)
  141. def spaceapi():
  142. while True:
  143. data = requests.get('https://fixme.ch/cgi-bin/spaceapi.py').json()
  144. filepath = PATH + '/spaceapi.json'
  145. if os.path.getsize(filepath) != len(json.dumps(data)):
  146. f = open(filepath, 'w+')
  147. f.write(json.dumps(data))
  148. f.close()
  149. time.sleep(10)
  150. def cleanExit(sig, frame):
  151. sys.exit(0)
  152. if __name__ == '__main__':
  153. signal(SIGINT, cleanExit)
  154. functions = [
  155. bus,
  156. webhook,
  157. calendar,
  158. power,
  159. clock,
  160. snmp,
  161. spaceapi,
  162. random_image,
  163. ]
  164. processes = {f: Process(target=f) for f in functions}
  165. [p.start() for p in processes.values()] # start the processes
  166. # Control loop
  167. while True:
  168. time.sleep(0.5)
  169. for f,p in processes.items():
  170. if not p.is_alive():
  171. processes[f] = Process(target=f)
  172. processes[f].start()
  173. print('restarted {}'.format(f))