104 lines
5.1 KiB
Python
104 lines
5.1 KiB
Python
#!/usr/bin/env python3
|
|
"""Static release checks for the two-node Simple day/night heating Flows.
|
|
|
|
Does not replace `cargo test` or hardware verification.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
import tomllib
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
PROFILE_DIR = ROOT / "flows" / "simple-heating"
|
|
ROOMS = {
|
|
"sypialnia": ("9a0bb37d-0306-4f87-9b14-fabf3b4c6164", 20.5, 10),
|
|
"jan-pieczara": ("7feae66a-747e-4205-8cff-91cc0b7924f6", 20.8, 8),
|
|
"igor": ("dd451f3e-4fc0-4189-b1cf-0c814b65cefc", 20.8, 10),
|
|
}
|
|
TRANSLATIONS = [
|
|
"flow.node.simpleHeat", "flow.simpleHeatDay", "flow.simpleHeatNight",
|
|
"flow.simpleHeatMinimum", "flow.simpleHeatBoostMinutes",
|
|
"flow.simpleHeatOffMinutes", "flow.simpleHeatHint", "zones.flowDayOff", "zones.flowDayPendingOff",
|
|
"flow.constantDescription", "flow.constantHelp", "flow.simpleHeatDescription", "flow.simpleHeatHowItWorks",
|
|
"flow.simpleHeatHelpIntro", "flow.simpleHeatHelpNightTitle", "flow.simpleHeatHelpNightSleep",
|
|
"flow.simpleHeatHelpNightBrake", "flow.simpleHeatHelpNightGentle", "flow.simpleHeatHelpNightBoost",
|
|
"flow.simpleHeatHelpDayTitle", "flow.simpleHeatHelpDayOff", "flow.simpleHeatHelpDayHeat",
|
|
"flow.simpleHeatHelpDayStop", "flow.simpleHeatHelpSensors", "flow.simpleHeatHelpSchedule",
|
|
"flow.simpleHeatHelpControl", "flow.simpleHeatDayTip", "flow.simpleHeatNightTip",
|
|
"flow.simpleHeatMinimumTip", "flow.simpleHeatBoostTip", "flow.simpleHeatOffTip",
|
|
]
|
|
|
|
|
|
def main() -> None:
|
|
version = tomllib.loads((ROOT / "Cargo.toml").read_text())["package"]["version"]
|
|
for name, (zone_id, target, boost) in ROOMS.items():
|
|
path = PROFILE_DIR / f"{name}-simple-heat-v5.flow.json"
|
|
doc = json.loads(path.read_text())
|
|
assert doc["format"] == "gree-controller-flow" and doc["version"] == 1, name
|
|
graph = doc["flow"]
|
|
assert graph["enabled"] and not graph["draft"], name
|
|
assert len(graph["nodes"]) == 2 and len(graph["edges"]) == 1, name
|
|
conditions = [node for node in graph["nodes"] if node["kind"] == "constant"]
|
|
actions = [node for node in graph["nodes"] if node["kind"] == "zone_heat_cycle"]
|
|
assert len(conditions) == len(actions) == 1, name
|
|
assert conditions[0]["config"]["value"] is True, name
|
|
assert graph["edges"][0]["from"] == conditions[0]["id"], name
|
|
assert graph["edges"][0]["to"] == actions[0]["id"], name
|
|
conf = actions[0]["config"]
|
|
assert conf["zone_id"] == zone_id, name
|
|
assert conf["night_target"] == target, name
|
|
assert conf["day_target"] == 20 and conf["day_min"] == 19.7, name
|
|
assert conf["boost_after_minutes"] == boost, name
|
|
assert conf["day_off_after_minutes"] == 30, name
|
|
assert "Wymaga wersji aplikacji 0.16.2" in graph["description"], name
|
|
|
|
for lang in ("pl", "en"):
|
|
translations = json.loads((ROOT / "lang" / f"{lang}.json").read_text())["translations"]
|
|
assert all(translations.get(key) for key in TRANSLATIONS), lang
|
|
|
|
# Validate every distributable JSON input, not only the three new Flow exports.
|
|
json_files = list(ROOT.rglob("*.json"))
|
|
for path in json_files:
|
|
json.loads(path.read_text())
|
|
assert len(json_files) > 30, len(json_files)
|
|
|
|
js_files = list((ROOT / "web").rglob("*.js"))
|
|
for path in js_files:
|
|
subprocess.run(["node", "--check", str(path)], check=True, capture_output=True, text=True)
|
|
|
|
rust = (ROOT / "src" / "api" / "flows.rs").read_text()
|
|
engine = (ROOT / "src" / "engine" / "zone_control.rs").read_text()
|
|
auto = (ROOT / "src" / "engine" / "automations.rs").read_text()
|
|
status = (ROOT / "web" / "js-dynamic" / "entities.js").read_text()
|
|
ui = (ROOT / "web" / "js-dynamic" / "flows.js").read_text()
|
|
html = (ROOT / "web" / "index.html").read_text()
|
|
for value in ("zone_heat_cycle", "boost_after_minutes", "day_off_after_minutes"):
|
|
assert value in rust and value in ui, value
|
|
assert "simple_heat_step(" in engine and "action_zone_simple_heat" in auto
|
|
assert "zone.simple_heat" in engine and "active_flow_zone_action_ids" in engine
|
|
assert "automation.flow" in status and "zones.flowDayOff" in status
|
|
assert 'data-flow-add="zone_heat_cycle"' in html
|
|
assert "day_target_20_starts_at_21_then_boosts_to_22" in (ROOT / "src" / "engine" / "simple_heat.rs").read_text()
|
|
|
|
lock = (ROOT / "Cargo.lock").read_text()
|
|
assert f'name = "gree-controller"\nversion = "{version}"' in lock
|
|
for manifest in [
|
|
ROOT / "ha-addon" / "repository" / "gree-controller" / "config.yaml",
|
|
ROOT / "ha-addon" / "home-assistant" / "custom_components" / "gree_controller" / "manifest.json",
|
|
]:
|
|
assert version in manifest.read_text(), manifest
|
|
assert json.loads((ROOT / "docs" / "openapi.json").read_text())["info"]["version"] == version
|
|
|
|
print(f"OK: {len(ROOMS)} Flow imports, {len(json_files)} JSON files, {len(js_files)} JS scripts, translations, linked controller source, version {version}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
main()
|
|
except (AssertionError, subprocess.CalledProcessError) as exc:
|
|
print(f"FAIL: {exc}", file=sys.stderr)
|
|
raise
|