mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-27 19:57:38 +00:00
252 lines
7.2 KiB
Python
252 lines
7.2 KiB
Python
### Generic lowlevel ESP and espeasy per-node stuff.
|
|
### Used for things like flashing, serial communication, resetting, wificonfig, http communication
|
|
|
|
import serial
|
|
import sys
|
|
import time
|
|
import subprocess
|
|
import wificonfig
|
|
import requests
|
|
import serial.tools.miniterm
|
|
import re
|
|
|
|
from espcore import *
|
|
|
|
|
|
class Node():
|
|
|
|
|
|
def __init__(self, config, id, skip_power=False):
|
|
self.log=logging.getLogger(id)
|
|
self.log.debug("{type} has ip {ip}".format(id=id, **config))
|
|
self._config=config
|
|
self._id=id
|
|
self._url="http://{ip}/".format(**self._config)
|
|
self._serial_initialized=False
|
|
self._skip_power=True
|
|
|
|
def serial_needed(self):
|
|
"""call this at least once if you need serial stuff"""
|
|
if not hasattr(self,"_serial"):
|
|
self._serial=serial.Serial(port=self._config['port'], baudrate=115200, timeout=1, write_timeout=1)
|
|
|
|
|
|
def pingserial(self, timeout=60):
|
|
"""waits until espeasy reponds via serial"""
|
|
|
|
self.serial_needed()
|
|
self._serial.reset_input_buffer();
|
|
self.log.debug("Waiting for serial response")
|
|
start_time=time.time()
|
|
|
|
while (time.time()-start_time)< int(timeout):
|
|
self._serial.write(bytes('.\n', 'ascii'));
|
|
a=True
|
|
while a!=b'':
|
|
a=self._serial.readline()
|
|
if a==b"Unknown command!\r\n":
|
|
self.log.debug("Got serial response")
|
|
self._serial.reset_input_buffer();
|
|
return
|
|
|
|
|
|
raise(Exception("Timeout!"))
|
|
|
|
|
|
def reboot(self):
|
|
'''reboot the esp via the serial DTR line'''
|
|
self.log.debug("Rebooting")
|
|
|
|
self.serial_needed()
|
|
self._serial.setDTR(0)
|
|
time.sleep(0.1)
|
|
self._serial.setDTR(1)
|
|
|
|
def powercycle(self):
|
|
"""powercycle the device"""
|
|
if self._skip_power:
|
|
self.log.warning("Skipping power cycle "+self._id)
|
|
return False
|
|
|
|
|
|
self.poweroff()
|
|
self.poweron()
|
|
return True
|
|
|
|
def poweroff(self):
|
|
"""power off device"""
|
|
|
|
if self._skip_power:
|
|
self.log.warning("Skipping power off "+self._id)
|
|
return False
|
|
|
|
#cant yet be done automaticly unfortunatly
|
|
self.log.info("Please power off node "+self._id)
|
|
|
|
done=False
|
|
while not done:
|
|
try:
|
|
self.serial_needed()
|
|
self._serial.readline()
|
|
except serial.SerialException:
|
|
done=True
|
|
if hasattr(self, '_serial'):
|
|
del self._serial
|
|
|
|
self.log.debug("Detected power off")
|
|
return True
|
|
|
|
def poweron(self):
|
|
"""power on device"""
|
|
|
|
if self._skip_power:
|
|
self.log.warning("Skipping power on"+self._id)
|
|
return False
|
|
|
|
self.log.info("Please power on node "+self._id)
|
|
|
|
done=False
|
|
while not done:
|
|
try:
|
|
self.serial_needed()
|
|
self._serial.readline()
|
|
done=True
|
|
except serial.SerialException:
|
|
time.sleep(0.1)
|
|
|
|
self.log.debug("Detected power on")
|
|
return True
|
|
|
|
def pingwifi(self, timeout=60):
|
|
"""waits until espeasy reponds via wifi"""
|
|
|
|
self.log.debug("Waiting for ping reply")
|
|
start_time=time.time()
|
|
|
|
while (time.time()-start_time)< int(timeout):
|
|
# log(".")
|
|
if not subprocess.call(["ping", "-w", "1", "-c", "1", self._config['ip']], stdout=subprocess.DEVNULL):
|
|
self.log.debug("Got ping reply")
|
|
return
|
|
|
|
raise(Exception("Timeout!"))
|
|
|
|
|
|
def wificonfig(self, timeout=60):
|
|
"""configure wifi via serial and make sure esp is online and pingable."""
|
|
|
|
self.serial_needed()
|
|
|
|
self.reboot()
|
|
self.pingserial(timeout=timeout)
|
|
|
|
self.log.info("Configuring wifi")
|
|
|
|
serial_str="wifissid {ssid}\nwifikey {password}\nip {ip}\nsubnet {subnet}\ngateway {gateway}\nsave\nreboot\n".format(ssid=wificonfig.ssid, password=wificonfig.password, ip=self._config['ip'], subnet=self._config['subnet'], gateway=self._config['gateway'])
|
|
self._serial.write(bytes(serial_str, 'ascii'));
|
|
|
|
self.pingwifi(timeout=timeout)
|
|
|
|
|
|
def serialcmd(self, command):
|
|
"""send command via serial"""
|
|
|
|
self.serial_needed()
|
|
self.log.debug("Send serial command: "+command)
|
|
serial_str=command+"\n"
|
|
self._serial.write(bytes(serial_str, 'ascii'));
|
|
|
|
|
|
def build(self):
|
|
"""compile binary"""
|
|
|
|
self.log.debug("Building...")
|
|
subprocess.check_call(self._config['build_cmd'].format(**self._config), shell=True, cwd='..')
|
|
|
|
|
|
def flashserial(self):
|
|
"""flash binary to esp via serial"""
|
|
|
|
self.serial_needed()
|
|
|
|
self.log.debug("Flashing...")
|
|
subprocess.check_call(self._config['flash_cmd'].format(**self._config), shell=True, cwd='..')
|
|
|
|
time.sleep(1)
|
|
#to prevent hangs when ESPEasy tries to reboot via ESP.reboot (due to an ESP/lib bug)
|
|
self.reboot()
|
|
|
|
|
|
def serial(self):
|
|
"""open serial terminal to esp"""
|
|
self.serial_needed()
|
|
self.log.debug("Opening serial terminal")
|
|
subprocess.check_call("platformio serialports monitor --baud 115200 --port {port} --echo".format(**self._config), shell=True, cwd='..')
|
|
# print("JA")
|
|
# term=serial.tools.miniterm.Miniterm(self._serial)
|
|
# term.start()
|
|
# term.join()
|
|
# print("kk")
|
|
|
|
|
|
def erase(self):
|
|
"""erase flash via serial"""
|
|
self.serial_needed()
|
|
self.log.debug("Erasing...")
|
|
subprocess.check_call("esptool.py --port {port} -b 1500000 erase_flash".format(**self._config), shell=True, cwd='..')
|
|
|
|
|
|
def bfs(self):
|
|
"""build + flashserial + serial, all in one"""
|
|
self.build()
|
|
self.flashserial()
|
|
self.serial()
|
|
|
|
def bf(self):
|
|
"""build + flashserial"""
|
|
self.build()
|
|
self.flashserial()
|
|
|
|
|
|
def http_post(self, page, params=None, data=None, twice=False):
|
|
"""http post to espeasy webinterface. (GET if data is None)"""
|
|
|
|
# transform easy copy/pastable chromium data into a dict
|
|
|
|
if params:
|
|
params_dict={}
|
|
for line in params.split("\n"):
|
|
m=re.match(" *(.*?):(.*)",line)
|
|
if (m):
|
|
params_dict[m.group(1)]=m.group(2)
|
|
else:
|
|
params_dict=None
|
|
|
|
if data:
|
|
data_dict={}
|
|
for line in data.split("\n"):
|
|
m=re.match(" *(.*?):(.*)",line)
|
|
if (m):
|
|
data_dict[m.group(1)]=m.group(2)
|
|
else:
|
|
data_dict=None
|
|
|
|
|
|
url=self._url+page
|
|
self.log.debug("HTTP POST {url} with params {params} and data {data}".format(url=url,params=params,data=data))
|
|
|
|
r=requests.post(
|
|
url,
|
|
params=params_dict,
|
|
data=data_dict
|
|
)
|
|
r.raise_for_status()
|
|
|
|
if twice:
|
|
r=requests.post(
|
|
self._url+page,
|
|
params=params_dict,
|
|
data=data_dict
|
|
)
|
|
r.raise_for_status()
|