Fork of the espurna firmware for `mhsw` switches
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.

233 lines
6.9 KiB

  1. #!/usr/bin/env python
  2. # coding=utf-8
  3. # -------------------------------------------------------------------------------
  4. # ESPurna OTA manager
  5. # xose.perez@gmail.com
  6. #
  7. # Requires PlatformIO Core
  8. # -------------------------------------------------------------------------------
  9. from __future__ import print_function
  10. import argparse
  11. import re
  12. import socket
  13. import subprocess
  14. import sys
  15. from time import sleep
  16. from zeroconf import ServiceBrowser, ServiceStateChange, Zeroconf
  17. try:
  18. # noinspection PyUnresolvedReferences
  19. input = raw_input # Python2
  20. except NameError:
  21. pass # Python3
  22. # -------------------------------------------------------------------------------
  23. devices = []
  24. description = "ESPurna OTA Manager v0.1"
  25. # -------------------------------------------------------------------------------
  26. def on_service_state_change(zeroconf, service_type, name, state_change):
  27. """
  28. Callback that adds discovered devices to "devices" list
  29. """
  30. if state_change is ServiceStateChange.Added:
  31. info = zeroconf.get_service_info(service_type, name)
  32. if info:
  33. hostname = info.server.split(".")[0]
  34. device = {
  35. 'hostname': hostname.upper(),
  36. 'ip': socket.inet_ntoa(info.address)
  37. }
  38. device['app'] = info.properties.get('app_name', '')
  39. device['version'] = info.properties.get('app_version', '')
  40. device['device'] = info.properties.get('target_board', '')
  41. if 'mem_size' in info.properties:
  42. device['mem_size'] = info.properties.get('mem_size')
  43. if 'sdk_size' in info.properties:
  44. device['sdk_size'] = info.properties.get('sdk_size')
  45. if 'free_space' in info.properties:
  46. device['free_space'] = info.properties.get('free_space')
  47. devices.append(device)
  48. def list():
  49. """
  50. Shows the list of discovered devices
  51. """
  52. output_format="{:>3} {:<25}{:<25}{:<15}{:<15}{:<30}{:<10}{:<10}{:<10}"
  53. print(output_format.format(
  54. "#",
  55. "HOSTNAME",
  56. "IP",
  57. "APP",
  58. "VERSION",
  59. "DEVICE",
  60. "MEM_SIZE",
  61. "SDK_SIZE",
  62. "FREE_SPACE"
  63. ))
  64. print("-" * 146)
  65. index = 0
  66. for device in devices:
  67. index = index + 1
  68. print(output_format.format(
  69. index,
  70. device.get('hostname', ''),
  71. device.get('ip', ''),
  72. device.get('app', ''),
  73. device.get('version', ''),
  74. device.get('device', ''),
  75. device.get('mem_size', ''),
  76. device.get('sdk_size', ''),
  77. device.get('free_space', ''),
  78. ))
  79. print()
  80. def get_boards():
  81. """
  82. Grabs board types fro hardware.h file
  83. """
  84. boards = []
  85. for line in open("espurna/config/hardware.h"):
  86. m = re.search(r'defined\((\w*)\)', line)
  87. if m:
  88. boards.append(m.group(1))
  89. return sorted(boards)
  90. def flash():
  91. """
  92. Grabs info from the user about what device to flash
  93. """
  94. # Choose the board
  95. try:
  96. index = int(input("Choose the board you want to flash (empty if none of these): "))
  97. except:
  98. index = 0
  99. if index < 0 or len(devices) < index:
  100. print("Board number must be between 1 and %s\n" % str(len(devices)))
  101. return None
  102. board = {'board': '', 'ip': '', 'size': 0, 'auth': '', 'flags': ''}
  103. if index > 0:
  104. device = devices[index - 1]
  105. board['board'] = device.get('device', '')
  106. board['ip'] = device.get('ip', '')
  107. board['size'] = int(device.get('mem_size', 0) if device.get('mem_size', 0) == device.get('sdk_size', 0) else 0) / 1024
  108. # Choose board type if none before
  109. if len(board['board']) == 0:
  110. print()
  111. count = 1
  112. boards = get_boards()
  113. for name in boards:
  114. print("%3d\t%s" % (count, name))
  115. count = count + 1
  116. print()
  117. try:
  118. index = int(input("Choose the board type you want to flash: "))
  119. except:
  120. index = 0
  121. if index < 1 or len(boards) < index:
  122. print("Board number must be between 1 and %s\n" % str(len(boards)))
  123. return None
  124. board['board'] = boards[index - 1]
  125. # Choose board size of none before
  126. if board['size'] == 0:
  127. try:
  128. board['size'] = int(input("Board memory size (1 for 1M, 4 for 4M): "))
  129. except:
  130. print("Wrong memory size")
  131. return None
  132. # Choose IP of none before
  133. if len(board['ip']) == 0:
  134. try:
  135. board['ip'] = input("IP of the device to flash (empty for 192.168.4.1): ") or "192.168.4.1"
  136. except:
  137. print("Wrong IP")
  138. return None
  139. board['auth'] = input("Authorization key of the device to flash: ")
  140. board['flags'] = input("Extra flags for the build: ")
  141. return board
  142. def run(device, env):
  143. command = "export ESPURNA_IP=\"%s\"; export ESPURNA_BOARD=\"%s\"; export ESPURNA_AUTH=\"%s\"; export ESPURNA_FLAGS=\"%s\"; platformio run --silent --environment %s -t upload"
  144. command = command % (device['ip'], device['board'], device['auth'], device['flags'], env)
  145. subprocess.check_call(command, shell=True)
  146. # -------------------------------------------------------------------------------
  147. if __name__ == '__main__':
  148. # Parse command line options
  149. parser = argparse.ArgumentParser(description=description)
  150. parser.add_argument("-c", "--core", help="flash ESPurna core", default=0, action='count')
  151. parser.add_argument("-f", "--flash", help="flash device", default=0, action='count')
  152. parser.add_argument("-s", "--sort", help="sort devices list by field", default='hostname')
  153. args = parser.parse_args()
  154. print()
  155. print(description)
  156. print()
  157. # Look for sevices
  158. zeroconf = Zeroconf()
  159. browser = ServiceBrowser(zeroconf, "_arduino._tcp.local.", handlers=[on_service_state_change])
  160. sleep(5)
  161. zeroconf.close()
  162. if len(devices) == 0:
  163. print("Nothing found!\n")
  164. sys.exit(0)
  165. # Sort list
  166. field = args.sort.lower()
  167. if field not in devices[0]:
  168. print("Unknown field '%s'\n" % field)
  169. sys.exit(1)
  170. devices = sorted(devices, key=lambda device: device.get(field, ''))
  171. # List devices
  172. list()
  173. # Flash device
  174. if args.flash > 0:
  175. device = flash()
  176. if device:
  177. # Flash core version?
  178. if args.core > 0:
  179. device['flags'] = "-DESPURNA_CORE " + device['flags']
  180. env = "esp8266-%sm-ota" % device['size']
  181. # Summary
  182. print()
  183. print("ESPURNA_IP = %s" % device['ip'])
  184. print("ESPURNA_BOARD = %s" % device['board'])
  185. print("ESPURNA_AUTH = %s" % device['auth'])
  186. print("ESPURNA_FLAGS = %s" % device['flags'])
  187. print("ESPURNA_ENV = %s" % env)
  188. response = input("\nAre these values right [y/N]: ")
  189. print()
  190. if response == "y":
  191. run(device, env)