Optional Helper scripts (#356)

* Helper script support

---------

Co-authored-by: Sillyfrog <sillyfrog@users.noreply.github.com>
This commit is contained in:
sillyfrog
2023-04-15 23:21:13 +10:00
committed by GitHub
parent 7670477902
commit ca40fd0d2f
11 changed files with 375 additions and 0 deletions

9
.gitignore vendored
View File

@@ -151,3 +151,12 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
/actual-devices/
# Don't add users sepecific scripts to the repo
scripts/*.sh
scripts/*.py
.DS_Store

View File

@@ -23,6 +23,13 @@ if [ "${SUPPORTS_AP}" != "yes" ]; then
read -n 1 -s -r -p "Press any key to continue, or CTRL+C to exit"
fi
function run_helper_script () {
if [ -f "scripts/${1}.sh" ]; then
echo "Running helper script '${1}'"
source "scripts/${1}.sh"
fi
}
reset_nm () {
if [ -z ${RESETNM+x} ]; then

View File

@@ -56,6 +56,7 @@ echo "Long press the power/reset button on the device until it starts fast-blink
echo "See https://support.tuya.com/en/help/_detail/K9hut3w10nby8 for more information."
echo "================================================================================"
echo ""
run_helper_script "pre-wifi-exploit"
wifi_connect
if [ ! $? -eq 0 ]; then
echo "Failed to connect, please run this script again"
@@ -90,6 +91,7 @@ echo "See https://support.tuya.com/en/help/_detail/K9hut3w10nby8 for more inform
echo "================================================================================"
echo ""
sleep 5
run_helper_script "pre-wifi-config"
wifi_connect
if [ ! $? -eq 0 ]; then
echo "Failed to connect, please run this script again"

66
scripts/README.md Normal file
View File

@@ -0,0 +1,66 @@
# Helper Scripts
Scripts can be placed in this directory that will be run during the flashing process.
This can be used to automate flashing or configure things specific for your environment.
Scripts should be bash scripts, ending with a `.sh` extension, and have the execute bit set (`chmod +x *.sh`). If you want to call something that's not bash, call it from within the bash script (see `post-flash.sh-example` for an example).
## Available Scripts
The full list of available helper scripts is listed below. If a script is not included, it's skipped (ie: it's OK to just include the scripts you require).
### pre-setup.sh
If this script exists, it is called before the main script is run, after some initial basic checks have been performed.
### pre-wifi-exploit.sh
Called before the initial WiFi exploit (the first time the device is put into AP mode).
### pre-wifi-config.sh
Called before the device is configured to update to use the local server / get flashed (the second time the device is put into AP mode).
### pre-safety-checks.sh
Runs before the `tuya-cloudcutter` safety checks are performed before flashing custom firmware (configuring the local PC to be in AP mode).
### post-flash.sh
Called after the device has been successfully flashed.
# Example scripts
This directory includes a full set of example scripts to show what could be done. **These must be customized** for your specific use case and are not generic.
The example scripts, if renamed to remove the `-example`, could be called with:
```
sudo MQTT_HOST=10.0.0.1 SWITCH_TOPIC=cmnd/flashing/power1 ./tuya-cloudcutter.sh
```
Or, for something more automatic, include the full configuration, eg:
```
sudo MQTT_HOST=10.0.0.1 SWITCH_TOPIC=cmnd/flashing/power1 ./tuya-cloudcutter.sh -f <3rd-party-firmware.bin> -p <device-slug> -r
```
The script could also be called from another script to flash several devices at once, eg:
```
#!/usr/bin/env bash
# Flash 4 devices at once
echo Flashing via port 1
MQTT_HOST=10.0.0.1 SWITCH_TOPIC=cmnd/flashing/power1 ./tuya-cloudcutter.sh -f <3rd-party-firmware.bin> -p <device-slug> -r
echo Flashing via port 2
MQTT_HOST=10.0.0.1 SWITCH_TOPIC=cmnd/flashing/power2 ./tuya-cloudcutter.sh -f <3rd-party-firmware.bin> -p <device-slug> -r
echo Flashing via port 3
MQTT_HOST=10.0.0.1 SWITCH_TOPIC=cmnd/flashing/power3 ./tuya-cloudcutter.sh -f <3rd-party-firmware.bin> -p <device-slug> -r
echo Flashing via port 4
MQTT_HOST=10.0.0.1 SWITCH_TOPIC=cmnd/flashing/power4 ./tuya-cloudcutter.sh -f <3rd-party-firmware.bin> -p <device-slug> -r
```

View File

@@ -0,0 +1,219 @@
#!/usr/bin/env python3
"""
This example script configures an RGB downlight that has just been flashed with the
OpenBK7231N_App firmware.
It was used on a Ubuntu 20.04 machine, but should be customized for your own use case.
Search this file for "here>" to find the places that need to be customized.
See scripts/README.md for example usage
You will need to install requests and paho-mqtt:
pip install requests paho-mqtt
"""
import subprocess
import os
import time
import requests
import pathlib
import paho.mqtt.client as mqtt
AP_PREFIX = "OpenBK7231N_"
MQTT_HOST = os.environ["MQTT_HOST"]
SWITCH_TOPIC = os.environ["SWITCH_TOPIC"]
mqtt_client = mqtt.Client()
COLOR_MAP = {
"1": "#2200000000",
"2": "#0022000000",
"3": "#0000220000",
"4": "#2200220000",
}
def get_wifi_adapter():
# Get the wifi adapter name
# Return the adapter name
p = subprocess.run(
["nmcli", "dev", "status"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
for l in p.stdout.splitlines():
if " wifi " in l:
return l.split()[0]
raise ValueError("Failed to find wifi adapter")
def list_aps():
# List the available APs
# Return a list of APs
subprocess.run(["nmcli", "radio", "wifi", "off"])
time.sleep(1)
subprocess.run(["nmcli", "radio", "wifi", "on"])
time.sleep(1)
p = subprocess.run(
[
"nmcli",
"-t",
"-f",
"SSID,SECURITY",
"dev",
"wifi",
"list",
"--rescan",
"yes",
],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
open_ssids = []
for l in p.stdout.splitlines():
ssid, security = l.split(":")
if security == "":
open_ssids.append(ssid)
return open_ssids
def find_ap():
# Ensure the interface is in managed mode
subprocess.run(["nmcli", "device", "set", get_wifi_adapter(), "managed", "yes"])
while 1:
print("Scanning for APs...")
for ap in list_aps():
if ap.startswith(AP_PREFIX):
print(f"Found AP: {ap}")
return ap
time.sleep(3)
def connect_ap(ap):
# Connect to the AP
# Return True if successful, False otherwise
p = subprocess.run(
["nmcli", "dev", "wifi", "connect", ap, "name", ap],
)
if p.returncode != 0:
raise ValueError("Failed to connect to AP")
print("Connected to AP")
ID_PATH = pathlib.Path("light-id")
def get_id():
if not ID_PATH.exists():
ID_PATH.write_text("1")
return int(ID_PATH.read_text()) + 1
def write_id(id):
ID_PATH.write_text(str(id))
def make_req(url):
print("Making request: ", url)
RETRIES = 5
for i in range(RETRIES):
try:
r = requests.get(url, timeout=5)
if not r.ok:
raise ValueError(f"Failed to make request: {url}")
print("Done")
time.sleep(1)
return
except Exception as e:
print(i + 1, "Error making request:", e)
time.sleep(3)
raise ValueError(f"Failed to make request: {url} after {RETRIES} retries")
STATE = {"online": False}
def on_message(client, userdata, message):
print("Message received (IP Address): ", str(message.payload.decode("utf-8")))
parts = message.topic.split("/")
msg = message.payload.decode("utf-8")
if parts[-1] == "ip":
print("Online")
STATE["online"] = True
def send_command(command, value):
mqtt_client.publish(f"cmnd/{device_name}/{command}", value)
time.sleep(1)
def main():
global device_name
print(f"MQTT_HOST: {MQTT_HOST}, SWITCH_TOPIC: {SWITCH_TOPIC}")
dev_id = get_id()
device_name = f"downlight{dev_id}"
print("Device name:", device_name)
# Find the AP to connect to
ap = find_ap()
# Connect to the AP
connect_ap(ap)
# Configure the pins
make_req(
"http://192.168.4.1/cfg_pins?0=0&r0=0&1=0&r1=0&2=0&r2=0&3=0&r3=0&4=0&r4=0&5=0&r5=0&6=0&r6=0&7=23&r7=0&8=24&r8=0&9=0&r9=0&10=0&r10=0&11=0&r11=0&12=0&r12=0&13=0&r13=0&14=0&r14=0&15=0&r15=0&16=0&r16=0&17=0&r17=0&18=0&r18=0&19=0&r19=0&20=0&r20=0&21=0&r21=0&22=0&r22=0&23=0&r23=0&24=0&r24=0&25=0&r25=0&26=0&r26=0&27=0&r27=0&28=0&0=r28"
)
# Configure MQTT
make_req(
f"http://192.168.4.1/cfg_mqtt_set?host=<mqtt host here>&port=1883&client={device_name}&group=dining&user=&password="
)
# Set default startup color
make_req(
"http://192.168.4.1/startup_command?data=backlog+led_basecolor_rgbcw+%230000007777%3B+led_enableAll+1%3B&startup_cmd=1"
)
make_req(
"http://192.168.4.1/cfg_wifi_set?ssid=<your ssid here>&pass=<your wifi password here>"
)
mqtt_client.connect(MQTT_HOST)
mqtt_client.on_message = on_message
mqtt_client.loop_start()
mqtt_client.subscribe(f"{device_name}/ip")
# Turn off the switch
print("Rebooting device...")
mqtt_client.publish(SWITCH_TOPIC, "off")
time.sleep(2)
mqtt_client.publish(SWITCH_TOPIC, "on")
# Wait for the device to connect
print("Waiting for device to connect via MQTT...", end="", flush=True)
while not STATE["online"]:
time.sleep(1)
print(".", end="", flush=True)
print("Connected")
output_color = COLOR_MAP.get(SWITCH_TOPIC[-1], "#2200000000")
# Set the device name
send_command("ShortName", device_name)
send_command("FriendlyName", device_name)
send_command("led_basecolor_rgbcw", output_color)
send_command("power", "1")
write_id(dev_id)
print(f"Flashed device {device_name} successfully")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,13 @@
#!/usr/bin/env bash
# This runs after the device has been flashed
# This example connects to the new AP it creates, and configures the device using
# the configure-downlight.py script
# It then restores and restarts the network services, used on Ubuntu 20.04
python3 scripts/configure-downlight.py
sudo cp -ra /root/system-connections/ /etc/NetworkManager/
sudo systemctl restart NetworkManager
sudo systemctl enable systemd-resolved.service
sudo systemctl start systemd-resolved.service

View File

@@ -0,0 +1,10 @@
#!/usr/bin/env bash
# If this script exists, it is called before the local safety checks, including checking
# if there are local services running on required ports.
# This example forcebily shuts down systemd-resolved, and keeps it down, removing the
# need to prompt the user to shut it down while running.
# Note if using this script, you will need to manually enable systemd-resolved.service
# after the device has been flashed.
# sudo systemctl disable systemd-resolved.service
sudo systemctl stop systemd-resolved.service

View File

@@ -0,0 +1,8 @@
#!/usr/bin/env bash
# If this script exists, it is called before the main script is run, after some initial basic checks have been performed.
# This example turns on the device that is connected to a smart switch via MQTT
# See pre-setup.sh-example for example usage
# Turn on the device
mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m ON

View File

@@ -0,0 +1,23 @@
#!/usr/bin/env bash
# This runs before the second time we attempt to connect to the AP on the device
# This example uses a smart plug running via MQTT to put the device into AP mode automatically
# See scripts/README.md for example usage
mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m OFF
sleep 5
mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m ON
sleep 5
mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m OFF; sleep 1.4; mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m ON; sleep 1.4
mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m OFF; sleep 1.4; mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m ON; sleep 1.4
mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m OFF; sleep 1.4; mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m ON; sleep 1.4
mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m OFF; sleep 1.4; mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m ON; sleep 1.4
mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m ON
sleep 10
mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m OFF; sleep 1.4; mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m ON; sleep 1.4
mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m OFF; sleep 1.4; mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m ON; sleep 1.4
mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m OFF; sleep 1.4; mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m ON; sleep 1.4
mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m OFF; sleep 1.4; mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m ON; sleep 1.4
mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m ON

View File

@@ -0,0 +1,12 @@
#!/usr/bin/env bash
# This runs before the first time we attempt to connect to the AP on the device
# This example uses a smart plug running via MQTT to put the device into AP mode automatically
# See scripts/README.md for example usage
mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m ON
sleep 5
mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m OFF; sleep 1.4; mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m ON; sleep 1.4
mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m OFF; sleep 1.4; mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m ON; sleep 1.4
mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m OFF; sleep 1.4; mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m ON; sleep 1.4
mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m OFF; sleep 1.4; mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m ON; sleep 1.4
mosquitto_pub -h $MQTT_HOST -t $SWITCH_TOPIC -m ON

View File

@@ -62,6 +62,8 @@ fi
source common.sh
run_helper_script "pre-setup"
if [ ! $METHOD_DETACH ] && [ ! $METHOD_FLASH ]; then
PS3="[?] Select your desired operation [1/2]: "
select method in "Detach from the cloud and run Tuya firmware locally" "Flash 3rd Party Firmware"; do
@@ -100,6 +102,8 @@ if [ $METHOD_FLASH ]; then
fi
source common_run.sh
run_helper_script "pre-safety-checks"
source safety_checks.sh
if [ $METHOD_DETACH ]; then
@@ -153,3 +157,5 @@ if [ $METHOD_FLASH ]; then
exit 1
fi
fi
run_helper_script "post-flash"