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.

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