__init__.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import logging
  2. import os
  3. import voluptuous as vol
  4. from homeassistant.components import websocket_api
  5. from atomicwrites import AtomicWriter
  6. DOMAIN = 'config_editor'
  7. _LOGGER = logging.getLogger(__name__)
  8. async def async_setup(hass, config):
  9. hass.components.websocket_api.async_register_command(websocket_create)
  10. hass.states.async_set(DOMAIN+".version", 4)
  11. return True
  12. @websocket_api.require_admin
  13. @websocket_api.async_response
  14. @websocket_api.websocket_command(
  15. {
  16. vol.Required("type"): DOMAIN+"/ws",
  17. vol.Required("action"): str,
  18. vol.Required("file"): str,
  19. vol.Required("data"): str,
  20. vol.Required("ext"): str,
  21. vol.Optional("depth", default=2): int
  22. }
  23. )
  24. async def websocket_create(hass, connection, msg):
  25. action = msg["action"]
  26. ext = msg["ext"]
  27. if ext not in ["yaml","py","json","conf","js","txt","log","css","all"]:
  28. ext = "yaml"
  29. def extok(e):
  30. if len(e)<2:
  31. return False
  32. return ( ext == 'all' or e.endswith("."+ext) )
  33. def rec(p, q):
  34. r = [
  35. f for f in os.listdir(p) if os.path.isfile(os.path.join(p, f)) and
  36. extok(f)
  37. ]
  38. for j in r:
  39. p = j if q == '' else os.path.join(q, j)
  40. listyaml.append(p)
  41. def drec(r, s):
  42. for d in os.listdir(r):
  43. v = os.path.join(r, d)
  44. if os.path.isdir(v):
  45. p = d if s == '' else os.path.join(s, d)
  46. if(p.count(os.sep) < msg["depth"]) and ( ext == 'all' or p != 'custom_components' ):
  47. rec(v, p)
  48. drec(v, p)
  49. yamlname = msg["file"].replace("../", "/").strip('/')
  50. if not extok(msg["file"]):
  51. yamlname = "temptest."+ext
  52. fullpath = hass.config.path(yamlname)
  53. if (action == 'load'):
  54. _LOGGER.info('Loading '+fullpath)
  55. content = ''
  56. res = 'Loaded'
  57. try:
  58. with open(fullpath, encoding="utf-8") as fdesc:
  59. content = fdesc.read()
  60. except:
  61. res = 'Reading Failed'
  62. _LOGGER.exception("Reading failed: %s", fullpath)
  63. finally:
  64. connection.send_result(
  65. msg["id"],
  66. {'msg': res+': '+fullpath, 'file': yamlname, 'data': content, 'ext': ext}
  67. )
  68. elif (action == 'save'):
  69. _LOGGER.info('Saving '+fullpath)
  70. content = msg["data"]
  71. res = "Saved"
  72. try:
  73. dirnm = os.path.dirname(fullpath)
  74. if not os.path.isdir(dirnm):
  75. os.makedirs(dirnm, exist_ok=True)
  76. try:
  77. stat_res = os.stat(fullpath)
  78. mode = stat_res.st_mode
  79. uid = stat_res.st_uid
  80. gid = stat_res.st_gid
  81. except:
  82. mode = 0o666
  83. uid = 0
  84. gid = 0
  85. with AtomicWriter(fullpath, overwrite=True).open() as fdesc:
  86. fdesc.write(content)
  87. with open(fullpath, 'a') as fdesc:
  88. try:
  89. os.fchmod(fdesc.fileno(), mode)
  90. os.fchown(fdesc.fileno(), uid, gid)
  91. except:
  92. pass
  93. except:
  94. res = "Saving Failed"
  95. _LOGGER.exception(res+": %s", fullpath)
  96. finally:
  97. connection.send_result(
  98. msg["id"],
  99. {'msg': res+': '+fullpath}
  100. )
  101. elif (action == 'list'):
  102. dirnm = os.path.dirname(hass.config.path(yamlname))
  103. listyaml = []
  104. rec(dirnm, '')
  105. if msg["depth"]>0:
  106. drec(dirnm, '')
  107. if (len(listyaml) < 1):
  108. listyaml = ['list_error.'+ext]
  109. connection.send_result(
  110. msg["id"],
  111. {'msg': str(len(listyaml))+' File(s)', 'file': listyaml, 'ext': ext}
  112. )