@ -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 |