Browse Source

ESPurna OTA Manager implemented in python

i18n
Xose Pérez 7 years ago
parent
commit
77045d9bf9
5 changed files with 219 additions and 299 deletions
  1. +2
    -0
      .gitignore
  2. +210
    -0
      code/ota.py
  3. +0
    -225
      code/ota_flash.sh
  4. +0
    -74
      code/ota_list.sh
  5. +7
    -0
      code/requirements.txt

+ 2
- 0
.gitignore View File

@ -12,3 +12,5 @@ credentials.h
node_modules
code/utils
custom.h
.python
.env

+ 210
- 0
code/ota.py View File

@ -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', ''),
))
print
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:
print
count = 1
boards = get_boards()
for name in boards:
print "%3d\t%s" % (count, name)
count = count + 1
print
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
print description
print
# 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
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]: ")
print
if response == "y":
run(device, env)

+ 0
- 225
code/ota_flash.sh View File

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

+ 0
- 74
code/ota_list.sh View File

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

+ 7
- 0
code/requirements.txt View File

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

Loading…
Cancel
Save