v0.9.6
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
bb89bac237e750e9b1bf73761d7df97a6b81853091615878c03f13d7b6399aa7 ./README.md
|
||||
5bc736c7bc76ca80aaa406bb171d2aa91baf4c3aa8695dce0e09b888b6ab3146 ./common.sh
|
||||
6403786610ee6d2f628193c25aee0dd058d62e904aa1a31d5f62fdaae0e94b4f ./configure-gree-network.sh
|
||||
14076104c042fba1284ebb07531a6c3ff972df1f9f5b18f70da18ab774efed27 ./generate_ha_migration.py
|
||||
e4849261fd9ed1f01df96c0637c439c0c4eff8fa317b2918026167bba343af79 ./install-lxc.sh
|
||||
bb7cd2c5b27c9dceec1d1d2846fbad9600df9c08e0ad07d13fcde97af533091c ./install.sh
|
||||
e00d211e3885e30d7fed1e43b44e6fdad40a67019060156c0641816a93e3365f ./network-debug.sh
|
||||
01952aa92b217f8eae2493b88870e2dec595100cd15c4d561ff11ae2b936c46f ./regenerate-sha.sh
|
||||
81345b6a0b51736bdbc98fd23199b62e4c721b4e7437e02dab7ea79b97dff29a ./service.sh
|
||||
d84315307bcda654ca5d23f86ae50723f6d26c0b8046711076d503983c05eaf6 ./smoke.sh
|
||||
b50782b3742dfbf8a319c60571c968e93fdf8547db747c759edcffae68cb98bf ./update.sh
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env bash
|
||||
set -Eeuo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=common.sh
|
||||
source "$SCRIPT_DIR/common.sh"
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
MODE="debug"
|
||||
ACTION="run"
|
||||
INSTALL_DEPS=1
|
||||
RESET_DB=0
|
||||
HOST=""
|
||||
PORT=""
|
||||
|
||||
usage() {
|
||||
cat <<'TXT'
|
||||
GREE Controller - development environment
|
||||
|
||||
Usage:
|
||||
./scripts/dev.sh install missing tools, build and run
|
||||
./scripts/dev.sh --release run an optimized build
|
||||
./scripts/dev.sh --check formatting, tests, build and API smoke test
|
||||
./scripts/dev.sh --reset remove the local database before startup
|
||||
./scripts/dev.sh --no-install do not install system packages or Rust
|
||||
./scripts/dev.sh --host 0.0.0.0 --port 8787
|
||||
TXT
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--release) MODE="release"; shift ;;
|
||||
--check) ACTION="check"; shift ;;
|
||||
--reset) RESET_DB=1; shift ;;
|
||||
--no-install) INSTALL_DEPS=0; shift ;;
|
||||
--host) HOST="${2:?missing value for --host}"; shift 2 ;;
|
||||
--port) PORT="${2:?missing value for --port}"; shift 2 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) echo "Unknown argument: $1" >&2; usage; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ "$INSTALL_DEPS" -eq 1 ]]; then
|
||||
install_build_dependencies
|
||||
ensure_rust
|
||||
else
|
||||
command -v cargo >/dev/null 2>&1 || fail "Cargo is not installed and --no-install was requested."
|
||||
fi
|
||||
|
||||
|
||||
if [[ ! -f .env ]]; then
|
||||
cp .env.example .env
|
||||
say "Created .env from the example configuration"
|
||||
fi
|
||||
mkdir -p data
|
||||
if [[ "$RESET_DB" -eq 1 ]]; then
|
||||
rm -f data/gree-controller.db data/gree-controller.db-shm data/gree-controller.db-wal
|
||||
say "Removed the local database"
|
||||
fi
|
||||
|
||||
set -a
|
||||
# shellcheck disable=SC1091
|
||||
source ./.env
|
||||
set +a
|
||||
|
||||
if [[ -n "$HOST" || -n "$PORT" ]]; then
|
||||
current="${GREE_CONTROLLER_BIND:-0.0.0.0:8787}"
|
||||
current_host="${current%:*}"
|
||||
current_port="${current##*:}"
|
||||
export GREE_CONTROLLER_BIND="${HOST:-$current_host}:${PORT:-$current_port}"
|
||||
fi
|
||||
|
||||
if [[ "$ACTION" == "check" ]]; then
|
||||
say "Checking formatting"
|
||||
cargo fmt --all -- --check
|
||||
say "Running Rust tests"
|
||||
cargo test --all-targets
|
||||
say "Building the application"
|
||||
cargo build
|
||||
say "Running HTTP/API smoke test"
|
||||
"$SCRIPT_DIR/smoke.sh"
|
||||
say "All checks passed"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$MODE" == "release" ]]; then
|
||||
say "Building release version"
|
||||
cargo build --release
|
||||
BINARY="$PROJECT_ROOT/target/release/gree-controller"
|
||||
else
|
||||
say "Building debug version"
|
||||
cargo build
|
||||
BINARY="$PROJECT_ROOT/target/debug/gree-controller"
|
||||
fi
|
||||
|
||||
bind="${GREE_CONTROLLER_BIND:-0.0.0.0:8787}"
|
||||
display_host="${bind%:*}"
|
||||
[[ "$display_host" == "0.0.0.0" ]] && display_host="127.0.0.1"
|
||||
say "Panel: http://${display_host}:${bind##*:}"
|
||||
say "Stop: Ctrl+C"
|
||||
exec "$BINARY"
|
||||
Wspierane przez Gitea
|
||||
Wersja: 1.27.2
|
||||
Strona: 30ms Szablon: 4ms
|
||||
@@ -54,6 +54,70 @@ grep -q '"control_temperature"' "$TMP/history.json"
|
||||
curl -fsS "http://127.0.0.1:$PORT/api/readings?device_id=sim-salon&hours=1" >"$TMP/readings.json"
|
||||
grep -q '"readings"' "$TMP/readings.json"
|
||||
|
||||
# Visual Flow: source graph, dry-run, export/import and optimistic concurrency.
|
||||
cat >"$TMP/flow-create.json" <<EOF
|
||||
{"name":"Smoke Flow","enabled":true,"description":"smoke","nodes":[{"id":"cond","kind":"constant","x":20,"y":20,"config":{"value":true}},{"id":"action","kind":"zone_thermostat","x":240,"y":20,"config":{"zone_id":"$ZONE_ID","preset":"comfort","mode":"auto","cooldown_seconds":60}}],"edges":[{"id":"edge","from":"cond","to":"action"}]}
|
||||
EOF
|
||||
curl -fsS -X POST -H 'Content-Type: application/json' --data-binary @"$TMP/flow-create.json" "http://127.0.0.1:$PORT/api/flows" >"$TMP/flow.json"
|
||||
grep -q '"revision":1' "$TMP/flow.json"
|
||||
FLOW_ID="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["id"])' "$TMP/flow.json")"
|
||||
FLOW_REVISION="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["revision"])' "$TMP/flow.json")"
|
||||
python3 - "$TMP/flow-create.json" "$TMP/flow-simulate.json" "$FLOW_ID" <<'PYFLOW'
|
||||
import json,sys,datetime
|
||||
flow=json.load(open(sys.argv[1]))
|
||||
json.dump({"flow":flow,"flow_id":sys.argv[3],"at":datetime.datetime.now(datetime.timezone.utc).isoformat(),"overrides":{"cond":True},"log":True},open(sys.argv[2],"w"))
|
||||
PYFLOW
|
||||
curl -fsS -X POST -H 'Content-Type: application/json' --data-binary @"$TMP/flow-simulate.json" "http://127.0.0.1:$PORT/api/flows/simulate" >"$TMP/flow-dry-run.json"
|
||||
grep -q '"dry_run":true' "$TMP/flow-dry-run.json"
|
||||
curl -fsS "http://127.0.0.1:$PORT/api/flows/$FLOW_ID/export" >"$TMP/flow-export.json"
|
||||
grep -q '"format":"gree-controller-flow"' "$TMP/flow-export.json"
|
||||
curl -fsS -X POST -H 'Content-Type: application/json' --data-binary @"$TMP/flow-export.json" "http://127.0.0.1:$PORT/api/flows/import" >"$TMP/flow-import.json"
|
||||
grep -q '"revision":1' "$TMP/flow-import.json"
|
||||
python3 - "$TMP/flow-create.json" "$TMP/flow-update.json" "$FLOW_REVISION" <<'PYFLOW'
|
||||
import json,sys
|
||||
flow=json.load(open(sys.argv[1])); flow["expected_revision"]=int(sys.argv[3]); flow["description"]="updated"
|
||||
json.dump(flow,open(sys.argv[2],"w"))
|
||||
PYFLOW
|
||||
curl -fsS -X PUT -H 'Content-Type: application/json' --data-binary @"$TMP/flow-update.json" "http://127.0.0.1:$PORT/api/flows/$FLOW_ID" >"$TMP/flow-updated.json"
|
||||
grep -q '"revision":2' "$TMP/flow-updated.json"
|
||||
STALE_STATUS="$(curl -sS -o /dev/null -w '%{http_code}' -X PUT -H 'Content-Type: application/json' --data-binary @"$TMP/flow-update.json" "http://127.0.0.1:$PORT/api/flows/$FLOW_ID")"
|
||||
[[ "$STALE_STATUS" == "409" ]]
|
||||
|
||||
# A schedule-compatible Flow must keep using the native schedule engine.
|
||||
cat >"$TMP/flow-schedule.json" <<EOF
|
||||
{"name":"Native schedule Flow","enabled":true,"nodes":[{"id":"days","kind":"weekday","x":20,"y":20,"config":{"days":[1,2,3,4,5,6,7]}},{"id":"time","kind":"time_range","x":180,"y":20,"config":{"start":"06:00","end":"08:00"}},{"id":"action","kind":"zone_thermostat","x":360,"y":20,"config":{"zone_id":"$ZONE_ID","preset":"comfort","mode":"auto","cooldown_seconds":60}}],"edges":[{"id":"e1","from":"days","to":"time"},{"id":"e2","from":"time","to":"action"}]}
|
||||
EOF
|
||||
curl -fsS -X POST -H 'Content-Type: application/json' --data-binary @"$TMP/flow-schedule.json" \
|
||||
"http://127.0.0.1:$PORT/api/flows" >"$TMP/flow-schedule-result.json"
|
||||
python3 - "$TMP/flow-schedule-result.json" <<'PYFLOW'
|
||||
import json,sys
|
||||
flow=json.load(open(sys.argv[1]))
|
||||
assert len(flow["compiled_schedule_ids"]) == 1, flow
|
||||
assert len(flow["compiled_automation_ids"]) == 0, flow
|
||||
PYFLOW
|
||||
|
||||
# HA errors must fail closed: even a != condition cannot become true just because HA is unavailable.
|
||||
cat >"$TMP/flow-ha-safe.json" <<EOF
|
||||
{"flow":{"name":"HA fail closed","enabled":true,"nodes":[{"id":"ha","kind":"ha_state","x":20,"y":20,"config":{"entity_id":"binary_sensor.missing","operator":"neq","value":"on"}},{"id":"action","kind":"zone_thermostat","x":260,"y":20,"config":{"zone_id":"$ZONE_ID","preset":"comfort","mode":"auto","cooldown_seconds":60}}],"edges":[{"id":"e1","from":"ha","to":"action"}]},"log":false}
|
||||
EOF
|
||||
curl -fsS -X POST -H 'Content-Type: application/json' --data-binary @"$TMP/flow-ha-safe.json" \
|
||||
"http://127.0.0.1:$PORT/api/flows/simulate" >"$TMP/flow-ha-safe-result.json"
|
||||
python3 - "$TMP/flow-ha-safe-result.json" <<'PYFLOW'
|
||||
import json,sys
|
||||
result=json.load(open(sys.argv[1]))
|
||||
action=result["actions"][0]
|
||||
assert action["matched"] is False, result
|
||||
assert action["would_execute"] is False, result
|
||||
PYFLOW
|
||||
|
||||
# Direct GREE Flow actions use the existing DeviceCommand surface beyond power/mode/target.
|
||||
cat >"$TMP/flow-device-options.json" <<EOF
|
||||
{"flow":{"name":"GREE options","enabled":true,"nodes":[{"id":"yes","kind":"constant","x":20,"y":20,"config":{"value":true}},{"id":"action","kind":"device_action","x":260,"y":20,"config":{"device_id":"sim-salon","light":false,"turbo":true,"cooldown_seconds":60}}],"edges":[{"id":"e1","from":"yes","to":"action"}]},"log":false}
|
||||
EOF
|
||||
curl -fsS -X POST -H 'Content-Type: application/json' --data-binary @"$TMP/flow-device-options.json" \
|
||||
"http://127.0.0.1:$PORT/api/flows/simulate" >"$TMP/flow-device-options-result.json"
|
||||
grep -q '"would_execute":true' "$TMP/flow-device-options-result.json"
|
||||
|
||||
# Home Assistant gets its own generated, restricted controller token.
|
||||
curl -fsS -X POST -H 'Content-Type: application/json' \
|
||||
-d '{"name":"Smoke Home Assistant"}' \
|
||||
|
||||
Reference in New Issue
Block a user