@ -0,0 +1,210 @@ | |||||
#!/usr/bin/env python | |||||
#------------------------------------------------------------------------------- | |||||
# ESPurna OTA manager | |||||
# xose.perez@gmail.com | |||||
# | |||||
# Requires PlatformIO Core | |||||
#------------------------------------------------------------------------------- | |||||
import sys | |||||
import re | |||||
import logging | |||||
import socket | |||||
import argparse | |||||
import subprocess | |||||
from time import sleep | |||||
from zeroconf import ServiceBrowser, ServiceStateChange, Zeroconf | |||||
#------------------------------------------------------------------------------- | |||||
devices = [] | |||||
description = "ESPurna OTA Manager v0.1" | |||||
#------------------------------------------------------------------------------- | |||||
def on_service_state_change(zeroconf, service_type, name, state_change): | |||||
''' | |||||
Callback that adds discovered devices to "devices" list | |||||
''' | |||||
if state_change is ServiceStateChange.Added: | |||||
info = zeroconf.get_service_info(service_type, name) | |||||
if info: | |||||
hostname = info.server.split(".")[0] | |||||
device = { | |||||
'hostname': hostname.upper(), | |||||
'ip': socket.inet_ntoa(info.address) | |||||
} | |||||
device['app'] = info.properties.get('app_name', '') | |||||
device['version'] = info.properties.get('app_version', '') | |||||
device['device'] = info.properties.get('target_board', '') | |||||
device['mem_size'] = info.properties.get('mem_size', '') | |||||
device['sdk_size'] = info.properties.get('sdk_size', '') | |||||
devices.append(device) | |||||
def list(): | |||||
''' | |||||
Shows the list of discovered devices | |||||
''' | |||||
output_format="{:>3} {:<25}{:<25}{:<15}{:<15}{:<30}{:<10}{:<10}" | |||||
print(output_format.format( | |||||
"#", | |||||
"HOSTNAME", | |||||
"IP", | |||||
"APP", | |||||
"VERSION", | |||||
"DEVICE", | |||||
"MEM_SIZE", | |||||
"SDK_SIZE", | |||||
)) | |||||
print "-" * 135 | |||||
index = 0 | |||||
for device in devices: | |||||
index = index + 1 | |||||
print(output_format.format( | |||||
index, | |||||
device.get('hostname', ''), | |||||
device.get('ip', ''), | |||||
device.get('app', ''), | |||||
device.get('version', ''), | |||||
device.get('device', ''), | |||||
device.get('mem_size', ''), | |||||
device.get('sdk_size', ''), | |||||
)) | |||||
def get_boards(): | |||||
''' | |||||
Grabs board types fro hardware.h file | |||||
''' | |||||
boards = [] | |||||
for line in open("espurna/config/hardware.h"): | |||||
m = re.search(r'defined\((\w*)\)', line) | |||||
if m: | |||||
boards.append(m.group(1)) | |||||
return sorted(boards) | |||||
def flash(): | |||||
''' | |||||
Grabs info from the user about what device to flash | |||||
''' | |||||
# Choose the board | |||||
try: | |||||
index = int(input("Choose the board you want to flash (empty if none of these): ")) | |||||
except: | |||||
index = 0 | |||||
if index < 0 or len(devices) < index: | |||||
print "Board number must be between 1 and %s\n" % str(len(devices)) | |||||
return None | |||||
board = {'board': '', 'ip': '', 'size': 0 , 'auth': '', 'flags': ''} | |||||
if index > 0: | |||||
device = devices[index-1] | |||||
board['board'] = device.get('device', '') | |||||
board['ip'] = device.get('ip', '') | |||||
board['size'] = int(device.get('mem_size', 0) if device.get('mem_size', 0) == device.get('sdk_size', 0) else 0) / 1024 | |||||
# Choose board type if none before | |||||
if len(board['board']) == 0: | |||||
count = 1 | |||||
boards = get_boards() | |||||
for name in boards: | |||||
print "%3d\t%s" % (count, name) | |||||
count = count + 1 | |||||
try: | |||||
index = int(input("Choose the board type you want to flash: ")) | |||||
except: | |||||
index = 0 | |||||
if index < 1 or len(boards) < index: | |||||
print "Board number must be between 1 and %s\n" % str(len(boards)) | |||||
return None | |||||
board['board'] = boards[index-1] | |||||
# Choose board size of none before | |||||
if board['size'] == 0: | |||||
try: | |||||
board['size'] = int(input("Board memory size (1 for 1M, 4 for 4M): ")) | |||||
except: | |||||
print "Wrong memory size" | |||||
return None | |||||
# Choose IP of none before | |||||
if len(board['ip']) == 0: | |||||
try: | |||||
board['ip'] = raw_input("IP of the device to flash (empty for 192.168.4.1): ") or "192.168.4.1" | |||||
except: | |||||
print "Wrong IP" | |||||
return None | |||||
board['auth'] = raw_input("Authorization key of the device to flash: ") | |||||
board['flags'] = raw_input("Extra flags for the build: ") | |||||
return board | |||||
def run(device, env): | |||||
command = "export ESPURNA_IP=\"%s\"; export ESPURNA_BOARD=\"%s\"; export ESPURNA_AUTH=\"%s\"; export ESPURNA_FLAGS=\"%s\"; platformio run --silent --environment %s -t upload" | |||||
command = command % (device['ip'], device['board'], device['auth'], device['flags'], env) | |||||
subprocess.check_call(command, shell=True) | |||||
#------------------------------------------------------------------------------- | |||||
if __name__ == '__main__': | |||||
# Parse command line options | |||||
parser = argparse.ArgumentParser(description=description) | |||||
#parser.add_argument("-v", "--verbose", help="show verbose output", default=0, action='count') | |||||
parser.add_argument("-f", "--flash", help="flash device", default=0, action='count') | |||||
parser.add_argument("-s", "--sort", help="sort devices list by field", default='hostname') | |||||
args = parser.parse_args() | |||||
print description | |||||
# Enable logging if verbose | |||||
#logging.basicConfig(level=logging.DEBUG) | |||||
#logging.getLogger('zeroconf').setLevel(logging.DEBUG) | |||||
# Look for sevices | |||||
zeroconf = Zeroconf() | |||||
browser = ServiceBrowser(zeroconf, "_arduino._tcp.local.", handlers=[on_service_state_change]) | |||||
sleep(1) | |||||
zeroconf.close() | |||||
# Sort list | |||||
field = args.sort.lower() | |||||
if field not in devices[0]: | |||||
print "Unknown field '%s'\n" % field | |||||
sys.exit(1) | |||||
devices = sorted(devices, key=lambda device: device.get(field, '')) | |||||
# List devices | |||||
list() | |||||
# Flash device | |||||
if args.flash > 0: | |||||
device = flash() | |||||
if device: | |||||
env = "esp8266-%sm-ota" % device['size'] | |||||
# Summary | |||||
print "ESPURNA_IP = %s" % device['ip'] | |||||
print "ESPURNA_BOARD = %s" % device['board'] | |||||
print "ESPURNA_AUTH = %s" % device['auth'] | |||||
print "ESPURNA_FLAGS = %s" % device['flags'] | |||||
print "ESPURNA_ENV = %s" % env | |||||
response = raw_input("\nAre these values right [y/N]: ") | |||||
if response == "y": | |||||
run(device, env) |
@ -1,225 +0,0 @@ | |||||
#!/bin/bash | |||||
ip= | |||||
board= | |||||
size= | |||||
auth= | |||||
flags= | |||||
export boards=() | |||||
ips="" | |||||
exists() { | |||||
command -v "$1" >/dev/null 2>&1 | |||||
} | |||||
echo_pad() { | |||||
string=$1 | |||||
pad=$2 | |||||
printf '%s' "$string" | |||||
printf '%*s' $(( $pad - ${#string} )) | |||||
} | |||||
useAvahi() { | |||||
echo_pad "#" 4 | |||||
echo_pad "HOSTNAME" 25 | |||||
echo_pad "IP" 25 | |||||
echo_pad "APP" 15 | |||||
echo_pad "VERSION" 15 | |||||
echo_pad "DEVICE" 30 | |||||
echo_pad "MEM_SIZE" 10 | |||||
echo_pad "SDK_SIZE" 10 | |||||
echo | |||||
printf -v line '%*s\n' 134 | |||||
echo ${line// /-} | |||||
counter=0 | |||||
ip_file="/tmp/espurna.flash.ips" | |||||
board_file="/tmp/espurna.flash.boards" | |||||
count_file="/tmp/espurna.flash.count" | |||||
size_file="/tmp/espurna.flash.size" | |||||
echo -n "" > $ip_file | |||||
echo -n "" > $board_file | |||||
echo -n "" > $size_file | |||||
echo -n "$counter" > $count_file | |||||
avahi-browse -t -r -p "_arduino._tcp" 2>/dev/null | grep ^= | sort -t ';' -k 3 | while read line; do | |||||
(( counter++ )) | |||||
echo "$counter" > $count_file | |||||
hostname=`echo $line | cut -d ';' -f4` | |||||
ip=`echo $line | cut -d ';' -f8` | |||||
txt=`echo $line | cut -d ';' -f10` | |||||
app_name=`echo $txt | sed -n "s/.*app_name=\([^\"]*\).*/\1/p"` | |||||
app_version=`echo $txt | sed -n "s/.*app_version=\([^\"]*\).*/\1/p"` | |||||
board=`echo $txt | sed -n "s/.*target_board=\([^\"]*\).*/\1/p"` | |||||
mem_size=`echo $txt | sed -n "s/.*mem_size=\([^\"]*\).*/\1/p"` | |||||
sdk_size=`echo $txt | sed -n "s/.*sdk_size=\([^\"]*\).*/\1/p"` | |||||
echo_pad "$counter" 4 | |||||
echo_pad "$hostname" 25 | |||||
echo_pad "http://$ip" 25 | |||||
echo_pad "$app_name" 15 | |||||
echo_pad "$app_version" 15 | |||||
echo_pad "$board" 30 | |||||
echo_pad "$mem_size" 10 | |||||
echo_pad "$sdk_size" 10 | |||||
echo | |||||
echo -n "$ip;" >> $ip_file | |||||
echo -n "$board;" >> $board_file | |||||
if [ "$mem_size" == "$sdk_size" ]; then | |||||
mem_size=`echo $mem_size | head -c 1` | |||||
echo -n "$mem_size;" >> $size_file | |||||
else | |||||
echo -n ";" >> $size_file | |||||
fi | |||||
done | |||||
echo | |||||
read -p "Choose the board you want to flash (empty if none of these): " num | |||||
# None of these | |||||
if [ "$num" == "" ]; then | |||||
return | |||||
fi | |||||
# Check boundaries | |||||
counter=`cat $count_file` | |||||
if [ $num -lt 1 ] || [ $num -gt $counter ]; then | |||||
echo "Board number must be between 1 and $counter" | |||||
exit 1 | |||||
fi | |||||
# Fill the fields | |||||
ip=`cat $ip_file | cut -d ';' -f$num` | |||||
board=`cat $board_file | cut -d ';' -f$num` | |||||
size=`cat $size_file | cut -d ';' -f$num` | |||||
} | |||||
getBoard() { | |||||
boards=(`cat espurna/config/hardware.h | grep "defined" | sed "s/.*(\(.*\)).*/\1/" | sort`) | |||||
echo_pad "#" 4 | |||||
echo_pad "DEVICE" 30 | |||||
echo | |||||
printf -v line '%*s\n' 34 | |||||
echo ${line// /-} | |||||
counter=0 | |||||
for board in "${boards[@]}"; do | |||||
(( counter++ )) | |||||
echo_pad "$counter" 4 | |||||
echo_pad "$board" 30 | |||||
echo | |||||
done | |||||
echo | |||||
read -p "Choose the board you want to flash (empty if none of these): " num | |||||
# None of these | |||||
if [ "$num" == "" ]; then | |||||
return | |||||
fi | |||||
# Check boundaries | |||||
counter=${#boards[*]} | |||||
if [ $num -lt 1 ] || [ $num -gt $counter ]; then | |||||
echo "Board code must be between 1 and $counter" | |||||
exit 1 | |||||
fi | |||||
# Fill the fields | |||||
(( num -- )) | |||||
board=${boards[$num]} | |||||
} | |||||
# ------------------------------------------------------------------------------ | |||||
# Welcome | |||||
echo | |||||
echo "--------------------------------------------------------------" | |||||
echo "ESPURNA FIRMWARE OTA FLASHER" | |||||
# Get current version | |||||
version=`cat espurna/config/version.h | grep APP_VERSION | awk '{print $3}' | sed 's/"//g'` | |||||
echo "Building for version $version" | |||||
echo "--------------------------------------------------------------" | |||||
echo | |||||
if exists avahi-browse; then | |||||
useAvahi | |||||
fi | |||||
if [ "$board" == "" ]; then | |||||
getBoard | |||||
fi | |||||
if [ "$board" == "" ]; then | |||||
read -p "Board type of the device to flash: " -e -i "NODEMCU_LOLIN" board | |||||
fi | |||||
if [ "$board" == "" ]; then | |||||
echo "You must define the board type" | |||||
exit 2 | |||||
fi | |||||
if [ "$size" == "" ]; then | |||||
read -p "Board memory size (1 for 1M, 4 for 4M): " -e size | |||||
fi | |||||
if [ "$size" == "" ]; then | |||||
echo "You must define the board memory size" | |||||
exit 2 | |||||
fi | |||||
if [ "$ip" == "" ]; then | |||||
read -p "IP of the device to flash: " -e -i 192.168.4.1 ip | |||||
fi | |||||
if [ "$ip" == "" ]; then | |||||
echo "You must define the IP of the device" | |||||
exit 2 | |||||
fi | |||||
if [ "$auth" == "" ]; then | |||||
read -p "Authorization key of the device to flash: " auth | |||||
fi | |||||
if [ "$flags" == "" ]; then | |||||
read -p "Extra flags for the build: " -e -i "" flags | |||||
fi | |||||
env="esp8266-${size}m-ota" | |||||
echo | |||||
echo "ESPURNA_IP = $ip" | |||||
echo "ESPURNA_BOARD = $board" | |||||
echo "ESPURNA_AUTH = $auth" | |||||
echo "ESPURNA_FLAGS = $flags" | |||||
echo "ESPURNA_ENV = $env" | |||||
echo | |||||
echo -n "Are these values corrent [y/N]: " | |||||
read response | |||||
if [ "$response" != "y" ]; then | |||||
exit | |||||
fi | |||||
export ESPURNA_IP=$ip | |||||
export ESPURNA_BOARD=$board | |||||
export ESPURNA_AUTH=$auth | |||||
export ESPURNA_FLAGS=$flags | |||||
platformio run --silent --environment $env -t upload |
@ -1,74 +0,0 @@ | |||||
#!/bin/bash | |||||
exists() { | |||||
command -v "$1" >/dev/null 2>&1 | |||||
} | |||||
echo_pad() { | |||||
string=$1 | |||||
pad=$2 | |||||
printf '%s' "$string" | |||||
printf '%*s' $(( $pad - ${#string} )) | |||||
} | |||||
useAvahi() { | |||||
echo_pad "#" 4 | |||||
echo_pad "HOSTNAME" 25 | |||||
echo_pad "IP" 25 | |||||
echo_pad "APP" 15 | |||||
echo_pad "VERSION" 15 | |||||
echo_pad "DEVICE" 30 | |||||
echo_pad "MEM_SIZE" 10 | |||||
echo_pad "SDK_SIZE" 10 | |||||
echo | |||||
printf -v line '%*s\n' 134 | |||||
echo ${line// /-} | |||||
counter=0 | |||||
avahi-browse -t -r -p "_arduino._tcp" 2>/dev/null | grep ^= | sort -t ';' -k 3 | while read line; do | |||||
(( counter++ )) | |||||
hostname=`echo $line | cut -d ';' -f4` | |||||
ip=`echo $line | cut -d ';' -f8` | |||||
txt=`echo $line | cut -d ';' -f10` | |||||
app_name=`echo $txt | sed -n "s/.*app_name=\([^\"]*\).*/\1/p"` | |||||
app_version=`echo $txt | sed -n "s/.*app_version=\([^\"]*\).*/\1/p"` | |||||
board=`echo $txt | sed -n "s/.*target_board=\([^\"]*\).*/\1/p"` | |||||
mem_size=`echo $txt | sed -n "s/.*mem_size=\([^\"]*\).*/\1/p"` | |||||
sdk_size=`echo $txt | sed -n "s/.*sdk_size=\([^\"]*\).*/\1/p"` | |||||
echo_pad "$counter" 4 | |||||
echo_pad "$hostname" 25 | |||||
echo_pad "http://$ip" 25 | |||||
echo_pad "$app_name" 15 | |||||
echo_pad "$app_version" 15 | |||||
echo_pad "$board" 30 | |||||
echo_pad "$mem_size" 10 | |||||
echo_pad "$sdk_size" 10 | |||||
echo | |||||
done | |||||
echo | |||||
} | |||||
# ------------------------------------------------------------------------------ | |||||
# Welcome | |||||
echo | |||||
echo "--------------------------------------------------------------" | |||||
echo "OTA-UPDATABLE DEVICES" | |||||
echo "--------------------------------------------------------------" | |||||
echo | |||||
if exists avahi-browse; then | |||||
useAvahi | |||||
else | |||||
echo "Avahi not installed" | |||||
exit 1 | |||||
fi |
@ -0,0 +1,7 @@ | |||||
enum-compat==0.0.2 | |||||
enum34==1.1.6 | |||||
netifaces==0.10.6 | |||||
ordereddict==1.1 | |||||
six==1.11.0 | |||||
sortedcontainers==1.5.9 | |||||
zeroconf==0.19.1 |