auto-sync: 2026-05-31 02:20:01
This commit is contained in:
6527
teplitsa_export.json
Normal file
6527
teplitsa_export.json
Normal file
File diff suppressed because it is too large
Load Diff
132
teplitsa_export.py
Normal file
132
teplitsa_export.py
Normal file
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Экспорт данных по теплице из Home Assistant за указанный период (stdlib only)."""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
# ============ НАСТРОЙКИ (подставлены из .env и parameters.yaml) ============
|
||||
HA_URL = "https://ha.homenet542.keenetic.pro"
|
||||
TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJmOTkyNzMxNmNlZTI0MjYzOWU4NjRhMGZlOGI2OTExZSIsImlhdCI6MTc3NTIzOTM1OCwiZXhwIjoyMDkwNTk5MzU4fQ.eumM2Vhk68uZZTvA4uIjKDqzlwBPKhBV6JeVRmSAJos"
|
||||
DAYS = 7
|
||||
ENTITIES = [
|
||||
"sensor.ulitsa_teplitsa_klimat_temperature",
|
||||
"sensor.ulitsa_teplitsa_klimat_humidity",
|
||||
"switch.ulitsa_teplitsa_vytiazhka",
|
||||
"switch.ulitsa_teplitsa_obogrev",
|
||||
"sun.sun",
|
||||
]
|
||||
OUTPUT_FILE = "/home/node/.openclaw/workspace/teplitsa_export.json"
|
||||
# ====================================
|
||||
|
||||
|
||||
def api_get(path, params=None):
|
||||
headers = {
|
||||
"Authorization": f"Bearer {TOKEN}",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
url = f"{HA_URL}{path}"
|
||||
if params:
|
||||
url = f"{url}?{urllib.parse.urlencode(params)}"
|
||||
req = urllib.request.Request(url, headers=headers, method="GET")
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
return json.loads(resp.read().decode("utf-8"))
|
||||
|
||||
|
||||
def main():
|
||||
start = datetime.now(timezone.utc) - timedelta(days=DAYS)
|
||||
start_iso = start.isoformat()
|
||||
|
||||
print(f"📊 Сбор данных за {DAYS} дней с {start_iso[:10]}...")
|
||||
|
||||
try:
|
||||
# История сенсоров и переключателей
|
||||
params = {
|
||||
"filter_entity_id": ",".join(ENTITIES),
|
||||
"minimal_response": "true",
|
||||
}
|
||||
history_raw = api_get(f"/api/history/period/{start_iso}", params)
|
||||
# Фильтруем unavailable/unknown
|
||||
history = []
|
||||
for series in history_raw:
|
||||
filtered = [s for s in series if s.get("state") not in ("unavailable", "unknown", None, "")]
|
||||
history.append(filtered)
|
||||
total = sum(len(s) for s in history)
|
||||
print(f"✅ История: {total} записей по {len(history)} сущностям")
|
||||
|
||||
# Логбук
|
||||
logbook_raw = api_get(f"/api/logbook/{start_iso}")
|
||||
teplitsa_log = [
|
||||
e for e in logbook_raw
|
||||
if e.get("entity_id") in ENTITIES
|
||||
or "теплиц" in (e.get("name", "") or "").lower()
|
||||
]
|
||||
print(f"✅ Логбук: {len(teplitsa_log)} релевантных событий")
|
||||
|
||||
# Текущие состояния
|
||||
states = []
|
||||
for eid in ENTITIES:
|
||||
try:
|
||||
states.append(api_get(f"/api/states/{eid}"))
|
||||
except Exception as e:
|
||||
print(f"⚠️ Не удалось получить {eid}: {e}")
|
||||
|
||||
except urllib.error.HTTPError as e:
|
||||
print(f"❌ HTTP ошибка {e.code}: {e.reason}")
|
||||
sys.exit(1)
|
||||
except urllib.error.URLError as e:
|
||||
print(f"❌ Ошибка соединения: {e.reason}")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
output = {
|
||||
"exported_at": datetime.now().isoformat(),
|
||||
"period_days": DAYS,
|
||||
"entities": ENTITIES,
|
||||
"current_states": states,
|
||||
"history": history,
|
||||
"logbook": teplitsa_log,
|
||||
}
|
||||
|
||||
with open(OUTPUT_FILE, "w", encoding="utf-8") as f:
|
||||
json.dump(output, f, ensure_ascii=False, indent=2, default=str)
|
||||
|
||||
print(f"\n💾 Сохранено: {OUTPUT_FILE}")
|
||||
|
||||
# Краткая сводка
|
||||
print("\n" + "=" * 50)
|
||||
print("📈 КРАТКАЯ СВОДКА")
|
||||
print("=" * 50)
|
||||
|
||||
for series in history:
|
||||
if not series:
|
||||
continue
|
||||
eid = series[0].get("entity_id", "?")
|
||||
if eid.startswith("sensor."):
|
||||
try:
|
||||
values = [float(s["state"]) for s in series]
|
||||
if values:
|
||||
print(f"\n{eid}")
|
||||
print(f" Мин/Макс/Среднее: {min(values):.1f} / {max(values):.1f} / {sum(values)/len(values):.1f}")
|
||||
except (ValueError, KeyError):
|
||||
pass
|
||||
elif eid.startswith("switch."):
|
||||
transitions = sum(
|
||||
1 for i in range(1, len(series))
|
||||
if series[i]["state"] != series[i - 1]["state"]
|
||||
)
|
||||
on_count = sum(1 for s in series if s["state"] == "on")
|
||||
off_count = sum(1 for s in series if s["state"] == "off")
|
||||
print(f"\n{eid}")
|
||||
print(f" Состояний 'on': {on_count}, 'off': {off_count}")
|
||||
print(f" Переключений: {transitions}")
|
||||
|
||||
print("\n📤 Загрузи файл teplitsa_export.json в чат для анализа.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
131
teplitsa_export.sh
Executable file
131
teplitsa_export.sh
Executable file
@@ -0,0 +1,131 @@
|
||||
#!/bin/bash
|
||||
# Экспорт данных по теплице из Home Assistant за указанный период (через curl + jq)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ============ НАСТРОЙКИ ============
|
||||
HA_URL="https://ha.homenet542.keenetic.pro"
|
||||
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJmOTkyNzMxNmNlZTI0MjYzOWU4NjRhMGZlOGI2OTExZSIsImlhdCI6MTc3NTIzOTM1OCwiZXhwIjoyMDkwNTk5MzU4fQ.eumM2Vhk68uZZTvA4uIjKDqzlwBPKhBV6JeVRmSAJos"
|
||||
DAYS=7
|
||||
ENTITIES=(
|
||||
"sensor.ulitsa_teplitsa_klimat_temperature"
|
||||
"sensor.ulitsa_teplitsa_klimat_humidity"
|
||||
"switch.ulitsa_teplitsa_vytiazhka"
|
||||
"switch.ulitsa_teplitsa_obogrev"
|
||||
"sun.sun"
|
||||
)
|
||||
OUTPUT_FILE="/home/node/.openclaw/workspace/teplitsa_export.json"
|
||||
# ====================================
|
||||
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
echo "❌ jq не найден. Установи: apt-get install -y jq"
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v curl >/dev/null 2>&1; then
|
||||
echo "❌ curl не найден"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
START_ISO=$(date -u -d "$DAYS days ago" +"%Y-%m-%dT%H:%M:%S+00:00" 2>/dev/null || date -u -v-${DAYS}d +"%Y-%m-%dT%H:%M:%S+00:00" 2>/dev/null || echo "")
|
||||
if [ -z "$START_ISO" ]; then
|
||||
# Fallback: python для даты
|
||||
START_ISO=$(python3 -c "
|
||||
from datetime import datetime, timedelta, timezone
|
||||
print((datetime.now(timezone.utc) - timedelta(days=$DAYS)).isoformat())
|
||||
" )
|
||||
fi
|
||||
|
||||
echo "📊 Сбор данных за $DAYS дней с ${START_ISO:0:10}..."
|
||||
|
||||
# История
|
||||
ENTITIES_CSV=$(IFS=,; echo "${ENTITIES[*]}")
|
||||
HISTORY=$(curl -sS -H "Authorization: Bearer $TOKEN" \
|
||||
"$HA_URL/api/history/period/$START_ISO?filter_entity_id=$ENTITIES_CSV&minimal_response=true" \
|
||||
| jq 'map(map(select(.state != "unavailable" and .state != "unknown" and .state != null)))')
|
||||
|
||||
TOTAL=$(echo "$HISTORY" | jq '[.[] | length] | add')
|
||||
COUNT=$(echo "$HISTORY" | jq 'length')
|
||||
echo "✅ История: $TOTAL записей по $COUNT сущностям"
|
||||
|
||||
# Логбук
|
||||
LOGBOOK=$(curl -sS -H "Authorization: Bearer $TOKEN" \
|
||||
"$HA_URL/api/logbook/$START_ISO" \
|
||||
| jq --argjson ents "$(printf '%s\n' "${ENTITIES[@]}" | jq -R . | jq -s .)" '
|
||||
map(select(
|
||||
(.entity_id as $eid | $ents | index($eid)) != null
|
||||
or (.name // "" | ascii_downcase | contains("теплиц"))
|
||||
))
|
||||
')
|
||||
|
||||
LOGBOOK_COUNT=$(echo "$LOGBOOK" | jq 'length')
|
||||
echo "✅ Логбук: $LOGBOOK_COUNT релевантных событий"
|
||||
|
||||
# Текущие состояния
|
||||
STATES_JSON="[]"
|
||||
for eid in "${ENTITIES[@]}"; do
|
||||
STATE=$(curl -sS -H "Authorization: Bearer $TOKEN" "$HA_URL/api/states/$eid" || true)
|
||||
if echo "$STATE" | jq -e '.entity_id' >/dev/null 2>&1; then
|
||||
STATES_JSON=$(echo "$STATES_JSON" | jq --argjson s "$STATE" '. + [$s]')
|
||||
else
|
||||
echo "⚠️ Не удалось получить $eid"
|
||||
fi
|
||||
done
|
||||
|
||||
# Собираем результат
|
||||
jq -n \
|
||||
--arg exported_at "$(date -Iseconds)" \
|
||||
--argjson days "$DAYS" \
|
||||
--argjson entities "$(printf '%s\n' "${ENTITIES[@]}" | jq -R . | jq -s .)" \
|
||||
--argjson states "$STATES_JSON" \
|
||||
--argjson history "$HISTORY" \
|
||||
--argjson logbook "$LOGBOOK" '
|
||||
{
|
||||
exported_at: $exported_at,
|
||||
period_days: $days,
|
||||
entities: $entities,
|
||||
current_states: $states,
|
||||
history: $history,
|
||||
logbook: $logbook
|
||||
}' > "$OUTPUT_FILE"
|
||||
|
||||
echo ""
|
||||
echo "💾 Сохранено: $OUTPUT_FILE"
|
||||
echo ""
|
||||
echo "=================================================="
|
||||
echo "📈 КРАТКАЯ СВОДКА"
|
||||
echo "=================================================="
|
||||
|
||||
for eid in "${ENTITIES[@]}"; do
|
||||
if [[ $eid == sensor.* ]]; then
|
||||
VALUES=$(echo "$HISTORY" | jq -r --arg eid "$eid" '
|
||||
.[] | select(.[0].entity_id == $eid) | .[].state | tonumber
|
||||
' 2>/dev/null || true)
|
||||
if [ -n "$VALUES" ]; then
|
||||
MIN=$(echo "$VALUES" | sort -n | head -1)
|
||||
MAX=$(echo "$VALUES" | sort -n | tail -1)
|
||||
AVG=$(echo "$VALUES" | awk '{s+=$1; c++} END {printf "%.1f", s/c}')
|
||||
echo ""
|
||||
echo "$eid"
|
||||
echo " Мин/Макс/Среднее: $MIN / $MAX / $AVG"
|
||||
fi
|
||||
elif [[ $eid == switch.* ]]; then
|
||||
ON=$(echo "$HISTORY" | jq -r --arg eid "$eid" '
|
||||
.[] | select(.[0].entity_id == $eid) | .[] | select(.state == "on") | .state
|
||||
' | wc -l)
|
||||
OFF=$(echo "$HISTORY" | jq -r --arg eid "$eid" '
|
||||
.[] | select(.[0].entity_id == $eid) | .[] | select(.state == "off") | .state
|
||||
' | wc -l)
|
||||
TRANS=$(echo "$HISTORY" | jq -r --arg eid "$eid" '
|
||||
.[] | select(.[0].entity_id == $eid) as $h |
|
||||
range(1; ($h | length)) |
|
||||
select( ($h[.].state) != ($h[.-1].state) )
|
||||
' | wc -l)
|
||||
echo ""
|
||||
echo "$eid"
|
||||
echo " Состояний 'on': $ON, 'off': $OFF"
|
||||
echo " Переключений: $TRANS"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "📤 Загрузи файл teplitsa_export.json в чат для анализа."
|
||||
Reference in New Issue
Block a user