1350 lines
46 KiB
JSON
1350 lines
46 KiB
JSON
{
|
|
"updatedAt": "2026-04-03T16:02:46.397Z",
|
|
"createdAt": "2026-04-03T13:37:06.873Z",
|
|
"id": "D5vTBspoT8Ag5q67",
|
|
"name": "Muscle Building Analyzer",
|
|
"active": true,
|
|
"isArchived": false,
|
|
"nodes": [
|
|
{
|
|
"parameters": {},
|
|
"id": "manual-id-001",
|
|
"name": "ManualTrigger",
|
|
"position": [
|
|
0,
|
|
144
|
|
],
|
|
"type": "n8n-nodes-base.manualTrigger",
|
|
"typeVersion": 1
|
|
},
|
|
{
|
|
"parameters": {
|
|
"values": {
|
|
"string": [
|
|
{
|
|
"name": "folder_id",
|
|
"value": "18Bhudb1KaJdJnIw-z0US2p8ScJjiliTo"
|
|
},
|
|
{
|
|
"name": "ha_weight_entity",
|
|
"value": "sensor.prasanna_weight_correct"
|
|
}
|
|
]
|
|
},
|
|
"options": {}
|
|
},
|
|
"id": "config-id-001",
|
|
"name": "Config",
|
|
"position": [
|
|
208,
|
|
144
|
|
],
|
|
"type": "n8n-nodes-base.set",
|
|
"typeVersion": 1
|
|
},
|
|
{
|
|
"parameters": {
|
|
"rule": {
|
|
"interval": [
|
|
{
|
|
"field": "weeks",
|
|
"triggerAtDay": [
|
|
1
|
|
],
|
|
"triggerAtHour": 9
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"id": "node-schedule-001",
|
|
"name": "Every Monday Morning",
|
|
"type": "n8n-nodes-base.scheduleTrigger",
|
|
"typeVersion": 1.3,
|
|
"position": [
|
|
0,
|
|
0
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"authentication": "serviceAccount",
|
|
"resource": "fileFolder",
|
|
"queryString": "strong",
|
|
"filter": {},
|
|
"options": {}
|
|
},
|
|
"id": "node-drive-list-001",
|
|
"name": "Sync: List CSVs",
|
|
"type": "n8n-nodes-base.googleDrive",
|
|
"typeVersion": 3,
|
|
"position": [
|
|
400,
|
|
-208
|
|
],
|
|
"credentials": {
|
|
"googleApi": {
|
|
"id": "BHZe7DHLZJ4mRYHz",
|
|
"name": "Google Service Account account"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"operation": "executeQuery",
|
|
"query": "SELECT file_id FROM processed_files WHERE file_id = $1",
|
|
"options": {
|
|
"queryReplacement": "={{ [$json.id] }}"
|
|
}
|
|
},
|
|
"id": "node-pg-check-001",
|
|
"name": "Check if Processed",
|
|
"type": "n8n-nodes-base.postgres",
|
|
"typeVersion": 2.6,
|
|
"position": [
|
|
608,
|
|
-208
|
|
],
|
|
"alwaysOutputData": true,
|
|
"credentials": {
|
|
"postgres": {
|
|
"id": "VyZORXHG4fXznguO",
|
|
"name": "Postgres account"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"conditions": {
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": "",
|
|
"typeValidation": "strict",
|
|
"version": 3
|
|
},
|
|
"conditions": [
|
|
{
|
|
"id": "condition-file-id-empty",
|
|
"leftValue": "={{ $json.file_id }}",
|
|
"operator": {
|
|
"operation": "empty",
|
|
"type": "string",
|
|
"singleValue": true
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and"
|
|
},
|
|
"options": {}
|
|
},
|
|
"id": "node-if-new-001",
|
|
"name": "Is New File?",
|
|
"type": "n8n-nodes-base.if",
|
|
"typeVersion": 2.3,
|
|
"position": [
|
|
800,
|
|
-208
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"authentication": "serviceAccount",
|
|
"operation": "download",
|
|
"fileId": "={{ $node[\"Sync: List CSVs\"].json[\"id\"] }}",
|
|
"options": {}
|
|
},
|
|
"id": "node-drive-download-001",
|
|
"name": "Download File",
|
|
"type": "n8n-nodes-base.googleDrive",
|
|
"typeVersion": 3,
|
|
"position": [
|
|
1008,
|
|
-304
|
|
],
|
|
"credentials": {
|
|
"googleApi": {
|
|
"id": "BHZe7DHLZJ4mRYHz",
|
|
"name": "Google Service Account account"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"fileFormat": "csv",
|
|
"options": {
|
|
"delimiter": ";",
|
|
"readAsString": true
|
|
}
|
|
},
|
|
"id": "node-spreadsheet-001",
|
|
"name": "CSV to JSON",
|
|
"type": "n8n-nodes-base.spreadsheetFile",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
1200,
|
|
-304
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"values": {
|
|
"number": [
|
|
{
|
|
"name": "set_order",
|
|
"value": "={{ parseInt($json[\"Set Order\"]) || 0 }}"
|
|
},
|
|
{
|
|
"name": "weight",
|
|
"value": "={{ parseFloat($json[\"Weight\"]) || 0 }}"
|
|
},
|
|
{
|
|
"name": "reps",
|
|
"value": "={{ parseInt($json[\"Reps\"]) || 0 }}"
|
|
}
|
|
],
|
|
"string": [
|
|
{
|
|
"name": "workout_date",
|
|
"value": "={{ $json[\"Date\"] }}"
|
|
},
|
|
{
|
|
"name": "exercise_name",
|
|
"value": "={{ $json[\"Exercise Name\"] }}"
|
|
},
|
|
{
|
|
"name": "duration",
|
|
"value": "={{ $json[\"Workout Duration\"] }}"
|
|
}
|
|
]
|
|
},
|
|
"options": {}
|
|
},
|
|
"id": "node-set-format-001",
|
|
"name": "Format Rows",
|
|
"type": "n8n-nodes-base.set",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
1408,
|
|
-304
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"operation": "executeQuery",
|
|
"query": "INSERT INTO gym_results (workout_date, exercise_name, set_order, weight, reps, duration) VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (workout_date, exercise_name, set_order) DO UPDATE SET weight = EXCLUDED.weight, reps = EXCLUDED.reps",
|
|
"options": {
|
|
"queryBatching": "independently",
|
|
"queryReplacement": "={{ [$json.workout_date, $json.exercise_name, $json.set_order, $json.weight, $json.reps, $json.duration] }}"
|
|
}
|
|
},
|
|
"id": "node-pg-upsert-001",
|
|
"name": "Upsert Results",
|
|
"type": "n8n-nodes-base.postgres",
|
|
"typeVersion": 2.6,
|
|
"position": [
|
|
1600,
|
|
-304
|
|
],
|
|
"credentials": {
|
|
"postgres": {
|
|
"id": "VyZORXHG4fXznguO",
|
|
"name": "Postgres account"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"operation": "executeQuery",
|
|
"query": "INSERT INTO processed_files (file_id, file_name) VALUES ($1, $2) ON CONFLICT (file_id) DO NOTHING",
|
|
"options": {
|
|
"queryReplacement": "={{ [$json.id, $json.name] }}"
|
|
}
|
|
},
|
|
"id": "node-pg-mark-001",
|
|
"name": "Mark Processed",
|
|
"type": "n8n-nodes-base.postgres",
|
|
"typeVersion": 2.6,
|
|
"position": [
|
|
1808,
|
|
-304
|
|
],
|
|
"credentials": {
|
|
"postgres": {
|
|
"id": "VyZORXHG4fXznguO",
|
|
"name": "Postgres account"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"operation": "executeQuery",
|
|
"query": "SELECT * FROM gym_results WHERE workout_date >= (CURRENT_DATE - INTERVAL '14 days') ORDER BY workout_date DESC;",
|
|
"additionalFields": {}
|
|
},
|
|
"id": "node-pg-fetch-001",
|
|
"name": "Fetch 14d Workouts",
|
|
"type": "n8n-nodes-base.postgres",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
400,
|
|
208
|
|
],
|
|
"credentials": {
|
|
"postgres": {
|
|
"id": "VyZORXHG4fXznguO",
|
|
"name": "Postgres account"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"authentication": "genericCredentialType",
|
|
"genericAuthType": "httpHeaderAuth",
|
|
"jsonBody": "={{ JSON.stringify({ entity_id: 'input_text.n8n_summary', value: $json.content.parts[0].text.slice(0, 255) }) }}",
|
|
"method": "POST",
|
|
"options": {},
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"url": "http://192.168.178.111:8123/api/services/input_text/set_value"
|
|
},
|
|
"id": "node-ha-notify-001",
|
|
"name": "HA Set Summary",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
1152,
|
|
304
|
|
],
|
|
"credentials": {
|
|
"httpHeaderAuth": {
|
|
"id": "OBwOm2Ixqr6biMwc",
|
|
"name": "homeAssistant"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"messages": {
|
|
"values": [
|
|
{
|
|
"content": "=You are a fitness coach. Analyze the last 14 days.\n\nWORKOUT DATA:\n{{ JSON.stringify($json.workouts) }}\n\nWEIGHT (kg, chronological):\n{{ JSON.stringify($json.weights) }}\n\nRespond with EXACTLY 3 lines, each starting with a bullet (•). Total must be under 255 characters.\nLine 1: Weight trend + body composition estimate (muscle/fat gain or fat loss).\nLine 2: Top training focus for next weeks.\nLine 3: Top nutrition focus for next weeks.\nNo markdown, no bold, no extra text."
|
|
}
|
|
]
|
|
},
|
|
"modelId": {
|
|
"__rl": true,
|
|
"cachedResultName": "Gemini 2.5 Flash (Guideline)",
|
|
"mode": "list",
|
|
"value": "models/gemini-2.5-flash"
|
|
}
|
|
},
|
|
"type": "@n8n/n8n-nodes-langchain.googleGemini",
|
|
"typeVersion": 1.1,
|
|
"position": [
|
|
864,
|
|
304
|
|
],
|
|
"id": "f8489e65-7ea1-4fea-b1d3-0df0824479de",
|
|
"name": "Message a model",
|
|
"credentials": {
|
|
"googlePalmApi": {
|
|
"id": "EWlC0h6CFPDYaxlJ",
|
|
"name": "Google Gemini(PaLM) Api account"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const workouts = $items(\"Fetch 14d Workouts\");\nconst weightRaw = $items(\"Get Weight History\");\n\n// HA history API returns [[{state, last_changed, ...}, ...]]\nlet weights = [];\ntry {\n if (weightRaw.length > 0) {\n const firstJson = weightRaw[0].json;\n if (Array.isArray(firstJson) && firstJson.length > 0 && Array.isArray(firstJson[0])) {\n weights = firstJson[0].map(item => ({\n state: item.state,\n last_changed: item.last_changed\n })).filter(w => w.state !== 'unavailable' && w.state !== 'unknown');\n } else if (Array.isArray(firstJson)) {\n weights = firstJson.map(item => ({\n state: item.state,\n last_changed: item.last_changed\n })).filter(w => w.state !== 'unavailable' && w.state !== 'unknown');\n }\n }\n} catch(e) {\n weights = [];\n}\n\nreturn [{\n json: {\n workouts: workouts.map(i => i.json),\n weights: weights\n }\n}];"
|
|
},
|
|
"id": "code-agg-001",
|
|
"name": "Prepare Analysis Data",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
704,
|
|
304
|
|
],
|
|
"executeOnce": true
|
|
},
|
|
{
|
|
"parameters": {
|
|
"path": "trigger-muscle-now",
|
|
"options": {}
|
|
},
|
|
"id": "547435b3-0c02-4efe-89ba-c65ea8359cf5",
|
|
"name": "TriggerWebhook",
|
|
"type": "n8n-nodes-base.webhook",
|
|
"typeVersion": 2.1,
|
|
"position": [
|
|
0,
|
|
288
|
|
],
|
|
"webhookId": "c376190f-52c2-4586-ad8e-31501d60aa5b"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"mode": "chooseBranch"
|
|
},
|
|
"id": "merge-wait-001",
|
|
"name": "Wait for Upsert",
|
|
"type": "n8n-nodes-base.merge",
|
|
"typeVersion": 3.2,
|
|
"position": [
|
|
1712,
|
|
-144
|
|
]
|
|
},
|
|
{
|
|
"parameters": {},
|
|
"id": "merge-analysis-001",
|
|
"name": "Analysis Merge",
|
|
"type": "n8n-nodes-base.merge",
|
|
"typeVersion": 3.2,
|
|
"position": [
|
|
560,
|
|
304
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"authentication": "genericCredentialType",
|
|
"genericAuthType": "httpHeaderAuth",
|
|
"options": {
|
|
"response": {
|
|
"response": {
|
|
"neverError": true
|
|
}
|
|
}
|
|
},
|
|
"queryParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "filter_entity_id",
|
|
"value": "sensor.prasanna_weight_correct"
|
|
},
|
|
{
|
|
"name": "minimal_response",
|
|
"value": "true"
|
|
},
|
|
{
|
|
"name": "end_time",
|
|
"value": "={{ $now.toISO() }}"
|
|
}
|
|
]
|
|
},
|
|
"sendQuery": true,
|
|
"url": "={{ 'http://192.168.178.111:8123/api/history/period/' + $now.minus({days: 14}).toISO() }}"
|
|
},
|
|
"id": "node-ha-history-001",
|
|
"name": "Get Weight History",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
400,
|
|
352
|
|
],
|
|
"credentials": {
|
|
"httpHeaderAuth": {
|
|
"id": "OBwOm2Ixqr6biMwc",
|
|
"name": "homeAssistant"
|
|
}
|
|
}
|
|
}
|
|
],
|
|
"connections": {
|
|
"ManualTrigger": {
|
|
"main": [
|
|
[
|
|
{
|
|
"index": 0,
|
|
"node": "Config",
|
|
"type": "main"
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Every Monday Morning": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Config",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Config": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Sync: List CSVs",
|
|
"type": "main",
|
|
"index": 0
|
|
},
|
|
{
|
|
"node": "Fetch 14d Workouts",
|
|
"type": "main",
|
|
"index": 0
|
|
},
|
|
{
|
|
"node": "Get Weight History",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Sync: List CSVs": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Check if Processed",
|
|
"type": "main",
|
|
"index": 0
|
|
},
|
|
{
|
|
"node": "Wait for Upsert",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Check if Processed": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Is New File?",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Is New File?": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Download File",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Download File": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "CSV to JSON",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"CSV to JSON": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Format Rows",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Format Rows": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Upsert Results",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Message a model": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "HA Set Summary",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Prepare Analysis Data": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Message a model",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"TriggerWebhook": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Config",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Upsert Results": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Wait for Upsert",
|
|
"type": "main",
|
|
"index": 1
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Wait for Upsert": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Mark Processed",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Fetch 14d Workouts": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Analysis Merge",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Analysis Merge": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Prepare Analysis Data",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Get Weight History": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Analysis Merge",
|
|
"type": "main",
|
|
"index": 1
|
|
}
|
|
]
|
|
]
|
|
}
|
|
},
|
|
"settings": {
|
|
"executionOrder": "v1",
|
|
"saveDataErrorExecution": "all",
|
|
"saveDataSuccessExecution": "all",
|
|
"saveManualExecutions": true,
|
|
"saveExecutionProgress": true,
|
|
"callerPolicy": "workflowsFromSameOwner",
|
|
"availableInMCP": false,
|
|
"binaryMode": "separate"
|
|
},
|
|
"staticData": {
|
|
"node:Every Monday Morning": {
|
|
"recurrenceRules": []
|
|
}
|
|
},
|
|
"meta": {
|
|
"templateCredsSetupCompleted": true
|
|
},
|
|
"pinData": {},
|
|
"versionId": "9c73e87e-c0f8-4073-ae2e-cf3484bcc550",
|
|
"activeVersionId": "9c73e87e-c0f8-4073-ae2e-cf3484bcc550",
|
|
"triggerCount": 2,
|
|
"shared": [
|
|
{
|
|
"updatedAt": "2026-04-03T13:37:06.876Z",
|
|
"createdAt": "2026-04-03T13:37:06.876Z",
|
|
"role": "workflow:owner",
|
|
"workflowId": "D5vTBspoT8Ag5q67",
|
|
"projectId": "cSAaWNJydnksD5EB"
|
|
}
|
|
],
|
|
"activeVersion": {
|
|
"updatedAt": "2026-04-03T16:02:46.398Z",
|
|
"createdAt": "2026-04-03T16:02:46.398Z",
|
|
"versionId": "9c73e87e-c0f8-4073-ae2e-cf3484bcc550",
|
|
"workflowId": "D5vTBspoT8Ag5q67",
|
|
"nodes": [
|
|
{
|
|
"parameters": {},
|
|
"id": "manual-id-001",
|
|
"name": "ManualTrigger",
|
|
"position": [
|
|
0,
|
|
144
|
|
],
|
|
"type": "n8n-nodes-base.manualTrigger",
|
|
"typeVersion": 1
|
|
},
|
|
{
|
|
"parameters": {
|
|
"values": {
|
|
"string": [
|
|
{
|
|
"name": "folder_id",
|
|
"value": "18Bhudb1KaJdJnIw-z0US2p8ScJjiliTo"
|
|
},
|
|
{
|
|
"name": "ha_weight_entity",
|
|
"value": "sensor.prasanna_weight_correct"
|
|
}
|
|
]
|
|
},
|
|
"options": {}
|
|
},
|
|
"id": "config-id-001",
|
|
"name": "Config",
|
|
"position": [
|
|
208,
|
|
144
|
|
],
|
|
"type": "n8n-nodes-base.set",
|
|
"typeVersion": 1
|
|
},
|
|
{
|
|
"parameters": {
|
|
"rule": {
|
|
"interval": [
|
|
{
|
|
"field": "weeks",
|
|
"triggerAtDay": [
|
|
1
|
|
],
|
|
"triggerAtHour": 9
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"id": "node-schedule-001",
|
|
"name": "Every Monday Morning",
|
|
"type": "n8n-nodes-base.scheduleTrigger",
|
|
"typeVersion": 1.3,
|
|
"position": [
|
|
0,
|
|
0
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"authentication": "serviceAccount",
|
|
"resource": "fileFolder",
|
|
"queryString": "strong",
|
|
"filter": {},
|
|
"options": {}
|
|
},
|
|
"id": "node-drive-list-001",
|
|
"name": "Sync: List CSVs",
|
|
"type": "n8n-nodes-base.googleDrive",
|
|
"typeVersion": 3,
|
|
"position": [
|
|
400,
|
|
-208
|
|
],
|
|
"credentials": {
|
|
"googleApi": {
|
|
"id": "BHZe7DHLZJ4mRYHz",
|
|
"name": "Google Service Account account"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"operation": "executeQuery",
|
|
"query": "SELECT file_id FROM processed_files WHERE file_id = $1",
|
|
"options": {
|
|
"queryReplacement": "={{ [$json.id] }}"
|
|
}
|
|
},
|
|
"id": "node-pg-check-001",
|
|
"name": "Check if Processed",
|
|
"type": "n8n-nodes-base.postgres",
|
|
"typeVersion": 2.6,
|
|
"position": [
|
|
608,
|
|
-208
|
|
],
|
|
"alwaysOutputData": true,
|
|
"credentials": {
|
|
"postgres": {
|
|
"id": "VyZORXHG4fXznguO",
|
|
"name": "Postgres account"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"conditions": {
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"leftValue": "",
|
|
"typeValidation": "strict",
|
|
"version": 3
|
|
},
|
|
"conditions": [
|
|
{
|
|
"id": "condition-file-id-empty",
|
|
"leftValue": "={{ $json.file_id }}",
|
|
"operator": {
|
|
"operation": "empty",
|
|
"type": "string",
|
|
"singleValue": true
|
|
}
|
|
}
|
|
],
|
|
"combinator": "and"
|
|
},
|
|
"options": {}
|
|
},
|
|
"id": "node-if-new-001",
|
|
"name": "Is New File?",
|
|
"type": "n8n-nodes-base.if",
|
|
"typeVersion": 2.3,
|
|
"position": [
|
|
800,
|
|
-208
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"authentication": "serviceAccount",
|
|
"operation": "download",
|
|
"fileId": "={{ $node[\"Sync: List CSVs\"].json[\"id\"] }}",
|
|
"options": {}
|
|
},
|
|
"id": "node-drive-download-001",
|
|
"name": "Download File",
|
|
"type": "n8n-nodes-base.googleDrive",
|
|
"typeVersion": 3,
|
|
"position": [
|
|
1008,
|
|
-304
|
|
],
|
|
"credentials": {
|
|
"googleApi": {
|
|
"id": "BHZe7DHLZJ4mRYHz",
|
|
"name": "Google Service Account account"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"fileFormat": "csv",
|
|
"options": {
|
|
"delimiter": ";",
|
|
"readAsString": true
|
|
}
|
|
},
|
|
"id": "node-spreadsheet-001",
|
|
"name": "CSV to JSON",
|
|
"type": "n8n-nodes-base.spreadsheetFile",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
1200,
|
|
-304
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"values": {
|
|
"number": [
|
|
{
|
|
"name": "set_order",
|
|
"value": "={{ parseInt($json[\"Set Order\"]) || 0 }}"
|
|
},
|
|
{
|
|
"name": "weight",
|
|
"value": "={{ parseFloat($json[\"Weight\"]) || 0 }}"
|
|
},
|
|
{
|
|
"name": "reps",
|
|
"value": "={{ parseInt($json[\"Reps\"]) || 0 }}"
|
|
}
|
|
],
|
|
"string": [
|
|
{
|
|
"name": "workout_date",
|
|
"value": "={{ $json[\"Date\"] }}"
|
|
},
|
|
{
|
|
"name": "exercise_name",
|
|
"value": "={{ $json[\"Exercise Name\"] }}"
|
|
},
|
|
{
|
|
"name": "duration",
|
|
"value": "={{ $json[\"Workout Duration\"] }}"
|
|
}
|
|
]
|
|
},
|
|
"options": {}
|
|
},
|
|
"id": "node-set-format-001",
|
|
"name": "Format Rows",
|
|
"type": "n8n-nodes-base.set",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
1408,
|
|
-304
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"operation": "executeQuery",
|
|
"query": "INSERT INTO gym_results (workout_date, exercise_name, set_order, weight, reps, duration) VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (workout_date, exercise_name, set_order) DO UPDATE SET weight = EXCLUDED.weight, reps = EXCLUDED.reps",
|
|
"options": {
|
|
"queryBatching": "independently",
|
|
"queryReplacement": "={{ [$json.workout_date, $json.exercise_name, $json.set_order, $json.weight, $json.reps, $json.duration] }}"
|
|
}
|
|
},
|
|
"id": "node-pg-upsert-001",
|
|
"name": "Upsert Results",
|
|
"type": "n8n-nodes-base.postgres",
|
|
"typeVersion": 2.6,
|
|
"position": [
|
|
1600,
|
|
-304
|
|
],
|
|
"credentials": {
|
|
"postgres": {
|
|
"id": "VyZORXHG4fXznguO",
|
|
"name": "Postgres account"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"operation": "executeQuery",
|
|
"query": "INSERT INTO processed_files (file_id, file_name) VALUES ($1, $2) ON CONFLICT (file_id) DO NOTHING",
|
|
"options": {
|
|
"queryReplacement": "={{ [$json.id, $json.name] }}"
|
|
}
|
|
},
|
|
"id": "node-pg-mark-001",
|
|
"name": "Mark Processed",
|
|
"type": "n8n-nodes-base.postgres",
|
|
"typeVersion": 2.6,
|
|
"position": [
|
|
1808,
|
|
-304
|
|
],
|
|
"credentials": {
|
|
"postgres": {
|
|
"id": "VyZORXHG4fXznguO",
|
|
"name": "Postgres account"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"operation": "executeQuery",
|
|
"query": "SELECT * FROM gym_results WHERE workout_date >= (CURRENT_DATE - INTERVAL '14 days') ORDER BY workout_date DESC;",
|
|
"additionalFields": {}
|
|
},
|
|
"id": "node-pg-fetch-001",
|
|
"name": "Fetch 14d Workouts",
|
|
"type": "n8n-nodes-base.postgres",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
400,
|
|
208
|
|
],
|
|
"credentials": {
|
|
"postgres": {
|
|
"id": "VyZORXHG4fXznguO",
|
|
"name": "Postgres account"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"authentication": "genericCredentialType",
|
|
"genericAuthType": "httpHeaderAuth",
|
|
"jsonBody": "={{ JSON.stringify({ entity_id: 'input_text.n8n_summary', value: $json.content.parts[0].text.slice(0, 255) }) }}",
|
|
"method": "POST",
|
|
"options": {},
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"url": "http://192.168.178.111:8123/api/services/input_text/set_value"
|
|
},
|
|
"id": "node-ha-notify-001",
|
|
"name": "HA Set Summary",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
1152,
|
|
304
|
|
],
|
|
"credentials": {
|
|
"httpHeaderAuth": {
|
|
"id": "OBwOm2Ixqr6biMwc",
|
|
"name": "homeAssistant"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"messages": {
|
|
"values": [
|
|
{
|
|
"content": "=You are a fitness coach. Analyze the last 14 days.\n\nWORKOUT DATA:\n{{ JSON.stringify($json.workouts) }}\n\nWEIGHT (kg, chronological):\n{{ JSON.stringify($json.weights) }}\n\nRespond with EXACTLY 3 lines, each starting with a bullet (•). Total must be under 255 characters.\nLine 1: Weight trend + body composition estimate (muscle/fat gain or fat loss).\nLine 2: Top training focus for next weeks.\nLine 3: Top nutrition focus for next weeks.\nNo markdown, no bold, no extra text."
|
|
}
|
|
]
|
|
},
|
|
"modelId": {
|
|
"__rl": true,
|
|
"cachedResultName": "Gemini 2.5 Flash (Guideline)",
|
|
"mode": "list",
|
|
"value": "models/gemini-2.5-flash"
|
|
}
|
|
},
|
|
"type": "@n8n/n8n-nodes-langchain.googleGemini",
|
|
"typeVersion": 1.1,
|
|
"position": [
|
|
864,
|
|
304
|
|
],
|
|
"id": "f8489e65-7ea1-4fea-b1d3-0df0824479de",
|
|
"name": "Message a model",
|
|
"credentials": {
|
|
"googlePalmApi": {
|
|
"id": "EWlC0h6CFPDYaxlJ",
|
|
"name": "Google Gemini(PaLM) Api account"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const workouts = $items(\"Fetch 14d Workouts\");\nconst weightRaw = $items(\"Get Weight History\");\n\n// HA history API returns [[{state, last_changed, ...}, ...]]\nlet weights = [];\ntry {\n if (weightRaw.length > 0) {\n const firstJson = weightRaw[0].json;\n if (Array.isArray(firstJson) && firstJson.length > 0 && Array.isArray(firstJson[0])) {\n weights = firstJson[0].map(item => ({\n state: item.state,\n last_changed: item.last_changed\n })).filter(w => w.state !== 'unavailable' && w.state !== 'unknown');\n } else if (Array.isArray(firstJson)) {\n weights = firstJson.map(item => ({\n state: item.state,\n last_changed: item.last_changed\n })).filter(w => w.state !== 'unavailable' && w.state !== 'unknown');\n }\n }\n} catch(e) {\n weights = [];\n}\n\nreturn [{\n json: {\n workouts: workouts.map(i => i.json),\n weights: weights\n }\n}];"
|
|
},
|
|
"id": "code-agg-001",
|
|
"name": "Prepare Analysis Data",
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
704,
|
|
304
|
|
],
|
|
"executeOnce": true
|
|
},
|
|
{
|
|
"parameters": {
|
|
"path": "trigger-muscle-now",
|
|
"options": {}
|
|
},
|
|
"id": "547435b3-0c02-4efe-89ba-c65ea8359cf5",
|
|
"name": "TriggerWebhook",
|
|
"type": "n8n-nodes-base.webhook",
|
|
"typeVersion": 2.1,
|
|
"position": [
|
|
0,
|
|
288
|
|
],
|
|
"webhookId": "c376190f-52c2-4586-ad8e-31501d60aa5b"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"mode": "chooseBranch"
|
|
},
|
|
"id": "merge-wait-001",
|
|
"name": "Wait for Upsert",
|
|
"type": "n8n-nodes-base.merge",
|
|
"typeVersion": 3.2,
|
|
"position": [
|
|
1712,
|
|
-144
|
|
]
|
|
},
|
|
{
|
|
"parameters": {},
|
|
"id": "merge-analysis-001",
|
|
"name": "Analysis Merge",
|
|
"type": "n8n-nodes-base.merge",
|
|
"typeVersion": 3.2,
|
|
"position": [
|
|
560,
|
|
304
|
|
]
|
|
},
|
|
{
|
|
"parameters": {
|
|
"authentication": "genericCredentialType",
|
|
"genericAuthType": "httpHeaderAuth",
|
|
"options": {
|
|
"response": {
|
|
"response": {
|
|
"neverError": true
|
|
}
|
|
}
|
|
},
|
|
"queryParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "filter_entity_id",
|
|
"value": "sensor.prasanna_weight_correct"
|
|
},
|
|
{
|
|
"name": "minimal_response",
|
|
"value": "true"
|
|
},
|
|
{
|
|
"name": "end_time",
|
|
"value": "={{ $now.toISO() }}"
|
|
}
|
|
]
|
|
},
|
|
"sendQuery": true,
|
|
"url": "={{ 'http://192.168.178.111:8123/api/history/period/' + $now.minus({days: 14}).toISO() }}"
|
|
},
|
|
"id": "node-ha-history-001",
|
|
"name": "Get Weight History",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.2,
|
|
"position": [
|
|
400,
|
|
352
|
|
],
|
|
"credentials": {
|
|
"httpHeaderAuth": {
|
|
"id": "OBwOm2Ixqr6biMwc",
|
|
"name": "homeAssistant"
|
|
}
|
|
}
|
|
}
|
|
],
|
|
"connections": {
|
|
"ManualTrigger": {
|
|
"main": [
|
|
[
|
|
{
|
|
"index": 0,
|
|
"node": "Config",
|
|
"type": "main"
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Every Monday Morning": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Config",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Config": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Sync: List CSVs",
|
|
"type": "main",
|
|
"index": 0
|
|
},
|
|
{
|
|
"node": "Fetch 14d Workouts",
|
|
"type": "main",
|
|
"index": 0
|
|
},
|
|
{
|
|
"node": "Get Weight History",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Sync: List CSVs": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Check if Processed",
|
|
"type": "main",
|
|
"index": 0
|
|
},
|
|
{
|
|
"node": "Wait for Upsert",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Check if Processed": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Is New File?",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Is New File?": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Download File",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Download File": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "CSV to JSON",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"CSV to JSON": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Format Rows",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Format Rows": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Upsert Results",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Message a model": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "HA Set Summary",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Prepare Analysis Data": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Message a model",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"TriggerWebhook": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Config",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Upsert Results": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Wait for Upsert",
|
|
"type": "main",
|
|
"index": 1
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Wait for Upsert": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Mark Processed",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Fetch 14d Workouts": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Analysis Merge",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Analysis Merge": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Prepare Analysis Data",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Get Weight History": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Analysis Merge",
|
|
"type": "main",
|
|
"index": 1
|
|
}
|
|
]
|
|
]
|
|
}
|
|
},
|
|
"authors": "Prasanna Ranjan",
|
|
"name": null,
|
|
"description": null,
|
|
"autosaved": false
|
|
},
|
|
"tags": []
|
|
} |