Projet de remplacement du "RPiPasserelle" d'Otec.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. #!/usr/bin/env python3
  2. import os
  3. import re
  4. from datetime import datetime
  5. from configparser import ConfigParser
  6. from starlette.requests import Request
  7. from starlette.routing import Route, Router
  8. from starlette.responses import PlainTextResponse, JSONResponse
  9. from pprint import pprint
  10. import uvicorn
  11. import json
  12. from pyheatpump.logger import logger_init
  13. logger = logger_init()
  14. default_config = {
  15. 'heatpump': {
  16. 'serial_port': '/dev/ttyUSB0',
  17. 'baudrate': 19200,
  18. 'mac_address': 'None',
  19. 'ip_address': 'dhcp',
  20. 'read_only': 'False',
  21. 'database': '/var/run/pyheatpump/pyheatpump.sqlite3'
  22. },
  23. 'supervisor': {
  24. 'scheme': 'http',
  25. 'host': '127.0.0.1',
  26. 'port': 8008,
  27. 'certificate': 'None',
  28. 'get_path': '/',
  29. 'post_path': '/',
  30. 'interval': 10000,
  31. 'auth': 'None',
  32. 'initialization': str(datetime.now()),
  33. 'heatpump_id': 'None',
  34. 'last_update': 0
  35. },
  36. 'api': {
  37. 'host': '127.0.0.1',
  38. 'port': 80
  39. }
  40. }
  41. CONFIG_FILES=[
  42. '/etc/pyheatpump.ini',
  43. './pyheatpump.ini']
  44. config = ConfigParser()#default_section='heatpump')
  45. config.read_dict(default_config)
  46. config.read(filenames=CONFIG_FILES)
  47. API_HOST=config.get('api', 'host')
  48. API_PORT=config.get('api', 'port')
  49. def save_config():
  50. with open(CONFIG_FILES[-1], 'w') as conf_file:
  51. config.write(conf_file)
  52. def mac_address_init():
  53. from netifaces import gateways, ifaddresses, AF_INET, AF_LINK
  54. interface = gateways()['default'][AF_INET][1]
  55. if len(ifaddresses(interface)) == 0:
  56. logger.error("Can't find interface")
  57. sys.exit(1)
  58. if len(ifaddresses(interface)[AF_LINK]) == 0:
  59. logger.error("Can't find interface")
  60. sys.exit(1)
  61. addr = ifaddresses(interface)[AF_LINK][0]['addr']
  62. config.set('heatpump', 'mac_address', addr)
  63. save_config()
  64. logger.info(f'Got new mac address {addr}')
  65. return addr
  66. mac_address_init()
  67. def get_config_dict():
  68. c = config
  69. return dict([ (s, dict([ (i, j) for i, j in c.items(s) ]) ) for s in c.sections() ])
  70. async def get_config(request):
  71. return JSONResponse(get_config_dict())
  72. async def set_config(request):
  73. d_body = await request.json()
  74. for sect in d_body.keys():
  75. if type(d_body[sect]) is not dict:
  76. continue
  77. for item in d_body[sect].keys():
  78. if not config.has_option(sect, item):
  79. raise HTTPException(404, f'The option {sect}.{item} does not exists')
  80. else:
  81. config.set(sect, item, value=d_body[sect][item])
  82. save_config()
  83. return PlainTextResponse('OK')
  84. async def config_route(request, *args, **kwargs):
  85. config.read(filenames=CONFIG_FILES)
  86. if request['method'] == 'GET':
  87. return await get_config(request)
  88. elif request['method'] == 'POST':
  89. return await set_config(request)
  90. def get_mac():
  91. return config.get('heatpump','mac_address')
  92. async def mac_route(request, *args, **kwargs):
  93. config.read(filenames=CONFIG_FILES)
  94. if request['method'] == 'GET':
  95. return PlainTextResponse(get_mac())
  96. elif request['method'] == 'POST':
  97. body = await request.body()
  98. mac_address = body.decode()
  99. if len(mac_address) == 0:
  100. mac_address = mac_address_init()
  101. elif re.match('^([0-9a-f]{2}:?){6}$',
  102. mac_address, re.I) is not None:
  103. config.set('heatpump','mac_address',mac_address)
  104. save_config()
  105. else:
  106. print(f'Wrong mac format : {mac_address}')
  107. return PlainTextResponse(' '.join((
  108. 'Current ->',
  109. get_mac(),
  110. ', New -> ', mac_address)))
  111. def get_last_update():
  112. return config.get('supervisor', 'last_update')
  113. def set_last_update(last_update: int=0):
  114. config.set('supervisor', 'last_update', str(last_update))
  115. save_config()
  116. return get_last_update()
  117. async def last_update_route(request, *args, **kwargs):
  118. """
  119. last_update :
  120. == 0 : since current date - interval
  121. < 0 : since current_date + last_update - interval
  122. > 0 : since current_data - last_update - interval (same behaviour as < 0)
  123. """
  124. if type(request) != Request or request['method'] == 'GET':
  125. return PlainTextResponse(get_last_update())
  126. elif request['method'] == 'POST':
  127. body = await request.body()
  128. last_update = int(body.decode() or 0)
  129. if last_update == 0:
  130. print(f'Reset last update to current time')
  131. last_update = int(datetime.now().strftime('%s'))
  132. else:
  133. print(f'{"Forward" if last_update < 0 else "Rewind"} of {last_update} seconds')
  134. last_update = int(datetime.now().strftime('%s')) - last_update
  135. return PlainTextResponse(str(set_last_update(last_update)))
  136. ROUTES=[
  137. Route('/', config_route, methods=['GET', 'POST']),
  138. Route('/mac', mac_route, methods=['GET', 'POST']),
  139. Route('/last_update', last_update_route, methods=['GET', 'POST'])
  140. ]
  141. app = Router(routes=ROUTES)
  142. if __name__ == '__main__':
  143. uvicorn.run('pyHeatpump:conf.app',
  144. host='127.0.0.1',
  145. port=8000)