mirror of
https://github.com/tuya-cloudcutter/tuya-cloudcutter.git
synced 2026-02-19 21:51:18 +01:00
Optional Helper scripts (#356)
* Helper script support --------- Co-authored-by: Sillyfrog <sillyfrog@users.noreply.github.com>
This commit is contained in:
9
.gitignore
vendored
9
.gitignore
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
66
scripts/README.md
Normal 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
|
||||
```
|
||||
219
scripts/configure-downlight.py-example
Normal file
219
scripts/configure-downlight.py-example
Normal 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()
|
||||
13
scripts/post-flash.sh-example
Normal file
13
scripts/post-flash.sh-example
Normal 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
|
||||
10
scripts/pre-safety-checks.sh-example
Normal file
10
scripts/pre-safety-checks.sh-example
Normal 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
|
||||
8
scripts/pre-setup.sh-example
Normal file
8
scripts/pre-setup.sh-example
Normal 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
|
||||
23
scripts/pre-wifi-config.sh-example
Normal file
23
scripts/pre-wifi-config.sh-example
Normal 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
|
||||
|
||||
12
scripts/pre-wifi-exploit.sh-example
Normal file
12
scripts/pre-wifi-exploit.sh-example
Normal 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
|
||||
@@ -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"
|
||||
Reference in New Issue
Block a user