{
  "id": "tqAc1cxtCunO54Ob",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Real-time ISS Overhead Alert with Weather Check and Multi-Channel Notifications",
  "tags": [],
  "nodes": [
    {
      "id": "a7f18dd5-9055-4ce8-bf03-1bd6e24a5c11",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -3952,
        1808
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 10
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "1cac3a05-08ed-40eb-8cff-8a459db832fe",
      "name": "Get ISS Position",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -3744,
        1808
      ],
      "parameters": {
        "url": "http://api.open-notify.org/iss-now.json",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "c1167a6f-01c9-4f1b-bb08-3baf4ae496f3",
      "name": "Calculate Distance and Direction",
      "type": "n8n-nodes-base.code",
      "position": [
        -3520,
        1808
      ],
      "parameters": {
        "jsCode": "// YOUR_AWS_SECRET_KEY_HERE===\n// USER CONFIGURATION - Edit these values\n// YOUR_AWS_SECRET_KEY_HERE===\nconst USER_LAT = 35.6762;      // Your latitude\nconst USER_LON = 139.6503;     // Your longitude  \nconst USER_LOCATION_NAME = \"Tokyo\";  // Your location name\nconst VISIBILITY_RADIUS_KM = 800;    // Alert radius in km\n\n// YOUR_AWS_SECRET_KEY_HERE===\n// ISS Position from previous node\n// YOUR_AWS_SECRET_KEY_HERE===\nconst issLat = parseFloat($input.first().json.iss_position.latitude);\nconst issLon = parseFloat($input.first().json.iss_position.longitude);\nconst timestamp = $input.first().json.timestamp;\n\n// YOUR_AWS_SECRET_KEY_HERE===\n// Haversine formula for distance calculation\n// YOUR_AWS_SECRET_KEY_HERE===\nfunction haversineDistance(lat1, lon1, lat2, lon2) {\n  const R = 6371;\n  const dLat = (lat2 - lat1) * Math.PI / 180;\n  const dLon = (lon2 - lon1) * Math.PI / 180;\n  const a = \n    Math.sin(dLat/2) * Math.sin(dLat/2) +\n    Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * \n    Math.sin(dLon/2) * Math.sin(dLon/2);\n  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));\n  return R * c;\n}\n\n// YOUR_AWS_SECRET_KEY_HERE===\n// Calculate bearing (compass direction)\n// YOUR_AWS_SECRET_KEY_HERE===\nfunction calculateBearing(lat1, lon1, lat2, lon2) {\n  const \u03c61 = lat1 * Math.PI / 180;\n  const \u03c62 = lat2 * Math.PI / 180;\n  const \u0394\u03bb = (lon2 - lon1) * Math.PI / 180;\n  const y = Math.sin(\u0394\u03bb) * Math.cos(\u03c62);\n  const x = Math.cos(\u03c61) * Math.sin(\u03c62) - Math.sin(\u03c61) * Math.cos(\u03c62) * Math.cos(\u0394\u03bb);\n  const \u03b8 = Math.atan2(y, x);\n  return (\u03b8 * 180 / Math.PI + 360) % 360;\n}\n\n// YOUR_AWS_SECRET_KEY_HERE===\n// Convert bearing to compass direction\n// YOUR_AWS_SECRET_KEY_HERE===\nfunction bearingToDirection(bearing) {\n  const directions = [\n    { min: 337.5, max: 360, name: \"North\" },\n    { min: 0, max: 22.5, name: \"North\" },\n    { min: 22.5, max: 67.5, name: \"Northeast\" },\n    { min: 67.5, max: 112.5, name: \"East\" },\n    { min: 112.5, max: 157.5, name: \"Southeast\" },\n    { min: 157.5, max: 202.5, name: \"South\" },\n    { min: 202.5, max: 247.5, name: \"Southwest\" },\n    { min: 247.5, max: 292.5, name: \"West\" },\n    { min: 292.5, max: 337.5, name: \"Northwest\" }\n  ];\n  for (const dir of directions) {\n    if (bearing >= dir.min && bearing < dir.max) return dir.name;\n  }\n  return \"North\";\n}\n\n// YOUR_AWS_SECRET_KEY_HERE===\n// Estimate elevation angle\n// YOUR_AWS_SECRET_KEY_HERE===\nfunction estimateElevation(distanceKm) {\n  const ISS_ALTITUDE = 408;\n  const elevation = Math.atan2(ISS_ALTITUDE, distanceKm) * 180 / Math.PI;\n  return Math.max(0, Math.min(90, elevation));\n}\n\n// YOUR_AWS_SECRET_KEY_HERE===\n// Execute calculations\n// YOUR_AWS_SECRET_KEY_HERE===\nconst distance = haversineDistance(USER_LAT, USER_LON, issLat, issLon);\nconst bearing = calculateBearing(USER_LAT, USER_LON, issLat, issLon);\nconst direction = bearingToDirection(bearing);\nconst elevation = estimateElevation(distance);\nconst isNearby = distance <= VISIBILITY_RADIUS_KM;\n\nconst now = new Date(timestamp * 1000);\nconst timeStr = now.toISOString();\n\nreturn [{\n  json: {\n    isNearby,\n    distance: Math.round(distance),\n    bearing: Math.round(bearing),\n    direction,\n    elevation: Math.round(elevation),\n    issLatitude: issLat,\n    issLongitude: issLon,\n    userLatitude: USER_LAT,\n    userLongitude: USER_LON,\n    userLocation: USER_LOCATION_NAME,\n    timestamp,\n    timeStr,\n    visibilityRadius: VISIBILITY_RADIUS_KM,\n    googleEarthUrl: `https://earth.google.com/web/search/${issLat},${issLon}`\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "7156152e-3f07-4c49-b30f-a77cbe678303",
      "name": "Is ISS Overhead?",
      "type": "n8n-nodes-base.if",
      "position": [
        -3296,
        1808
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "nearby-check",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.isNearby }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "4a92bf3a-2898-44e2-b9d9-8f6a052f7786",
      "name": "Get Local Weather",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -3072,
        1712
      ],
      "parameters": {
        "url": "=https://api.openweathermap.org/data/2.5/weather?lat={{ $json.userLatitude }}&lon={{ $json.userLongitude }}&appid={{$credentials.openWeatherMapApi.apiKey}}&units=metric&lang=en",
        "options": {},
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "openWeatherMapApi"
      },
      "typeVersion": 4.2
    },
    {
      "id": "b0d340a7-9e7f-4d32-8930-26d0f539c678",
      "name": "Analyze Observation Conditions",
      "type": "n8n-nodes-base.code",
      "position": [
        -2864,
        1712
      ],
      "parameters": {
        "jsCode": "const positionData = $('Calculate Distance and Direction').first().json;\nconst weatherData = $input.first().json;\n\nconst weatherMain = weatherData.weather?.[0]?.main || 'Unknown';\nconst weatherDescription = weatherData.weather?.[0]?.description || 'unknown';\nconst cloudiness = weatherData.clouds?.all || 0;\nconst visibility = weatherData.visibility || 10000;\n\nlet observationCondition = 'good';\nlet observationAdvice = '';\n\nif (['Rain', 'Snow', 'Thunderstorm', 'Drizzle'].includes(weatherMain)) {\n  observationCondition = 'poor';\n  observationAdvice = '\ud83c\udf27\ufe0f Unfortunately, weather conditions are poor. ISS is not visible. Try next time!';\n} else if (cloudiness > 80) {\n  observationCondition = 'poor';\n  observationAdvice = '\u2601\ufe0f Heavy cloud cover. Observation will be difficult. Look for gaps in the clouds.';\n} else if (cloudiness > 50) {\n  observationCondition = 'fair';\n  observationAdvice = '\ud83c\udf25\ufe0f Partly cloudy. You might see it through cloud breaks.';\n} else {\n  observationCondition = 'good';\n  observationAdvice = '\u2728 Great viewing conditions! Look up now!';\n}\n\nconst now = new Date();\nconst hour = now.getUTCHours();\n\nlet dayNightAdvice = '';\nif (hour >= 6 && hour < 18) {\n  dayNightAdvice = '\u2600\ufe0f Daytime - ISS is harder to see, but it\\'s passing overhead!';\n} else if ((hour >= 18 && hour < 21) || (hour >= 4 && hour < 6)) {\n  dayNightAdvice = '\ud83c\udf05 Twilight - Best time! ISS reflects sunlight and appears very bright.';\n} else {\n  dayNightAdvice = '\ud83c\udf19 Nighttime - If conditions are good, ISS will be clearly visible.';\n}\n\nreturn [{\n  json: {\n    ...positionData,\n    weather: {\n      main: weatherMain,\n      description: weatherDescription,\n      cloudiness,\n      visibility\n    },\n    observationCondition,\n    observationAdvice,\n    dayNightAdvice,\n    canObserve: observationCondition !== 'poor'\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "9ce4126b-7b3c-4934-a5fe-215f661e9c4a",
      "name": "Can Observe?",
      "type": "n8n-nodes-base.if",
      "position": [
        -2640,
        1712
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "can-observe",
              "operator": {
                "type": "boolean",
                "operation": "equals"
              },
              "leftValue": "={{ $json.canObserve }}",
              "rightValue": true
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "2018e63c-4adf-43ba-957c-d6aaf6407251",
      "name": "Format Alert (Good Conditions)",
      "type": "n8n-nodes-base.code",
      "position": [
        -2416,
        1616
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json;\n\nconst message = `\ud83d\udef0\ufe0f **ISS OVERHEAD ALERT**\n\nThe International Space Station is passing over your location!\n\n\ud83d\udccd **Your Location:** ${data.userLocation}\n\ud83d\udccf **Distance:** ~${data.distance} km\n\ud83e\udded **Direction:** ${data.direction} (${data.bearing}\u00b0)\n\ud83d\udcd0 **Elevation:** ~${data.elevation}\u00b0 above horizon\n\n${data.dayNightAdvice}\n${data.observationAdvice}\n\n\ud83c\udf24\ufe0f **Current Weather:** ${data.weather.description} (${data.weather.cloudiness}% clouds)\n\n\ud83d\udd2d **How to Spot ISS:**\n\u2022 Look ${data.direction}, about ${data.elevation}\u00b0 above the horizon\n\u2022 ISS appears as a bright, steady light (no blinking)\n\u2022 It moves faster than airplanes across the sky\n\n\ud83c\udf0d **View ISS on 3D Map:**\n${data.googleEarthUrl}\n\n\u23f0 **Detected at:** ${data.timeStr}`;\n\nreturn [{\n  json: {\n    ...data,\n    notificationMessage: message,\n    shortMessage: `\ud83d\udef0\ufe0f ISS Alert! Look ${data.direction}, ${data.elevation}\u00b0 up. Distance: ${data.distance}km`,\n    notificationType: 'observation_possible'\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "7f0df4b6-df12-4bea-aac2-31a40037a137",
      "name": "Format Alert (Poor Conditions)",
      "type": "n8n-nodes-base.code",
      "position": [
        -2416,
        1808
      ],
      "parameters": {
        "jsCode": "const data = $input.first().json;\n\nconst message = `\ud83d\udef0\ufe0f **ISS PASSING BY**\n\nThe International Space Station is near your location, but...\n\n${data.observationAdvice}\n\n\ud83d\udccd **Your Location:** ${data.userLocation}\n\ud83d\udccf **Distance:** ~${data.distance} km\n\ud83e\udded **Direction:** ${data.direction} (${data.bearing}\u00b0)\n\n\ud83c\udf24\ufe0f **Current Weather:** ${data.weather.description} (${data.weather.cloudiness}% clouds)\n\n\ud83c\udf0d **View ISS Location on 3D Map:**\n${data.googleEarthUrl}\n\nBetter luck next time!\n\n\u23f0 **Detected at:** ${data.timeStr}`;\n\nreturn [{\n  json: {\n    ...data,\n    notificationMessage: message,\n    shortMessage: `\ud83d\udef0\ufe0f ISS nearby but not visible (${data.weather.description})`,\n    notificationType: 'observation_difficult'\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "2b0ff92c-8997-4b8a-a70f-00dff8b670a7",
      "name": "Merge Messages",
      "type": "n8n-nodes-base.merge",
      "position": [
        -2192,
        1712
      ],
      "parameters": {
        "mode": "combine",
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "8d6c327e-0892-47cc-b72f-9b2a66ca87fd",
      "name": "Get ISS Crew Info",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -1984,
        1712
      ],
      "parameters": {
        "url": "http://api.open-notify.org/astros.json",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "b683efdd-63f9-4eff-9b6d-e407c659ea1d",
      "name": "Add Crew Information",
      "type": "n8n-nodes-base.code",
      "position": [
        -1760,
        1712
      ],
      "parameters": {
        "jsCode": "const mainData = $('Merge Messages').first().json;\nconst astronautData = $input.first().json;\n\nconst issAstronauts = astronautData.people?.filter(p => p.craft === 'ISS') || [];\n\nlet crewInfo = '';\nif (issAstronauts.length > 0) {\n  crewInfo = `\\n\\n\ud83d\udc68\u200d\ud83d\ude80 **Currently aboard ISS (${issAstronauts.length} astronauts):**\\n`;\n  issAstronauts.forEach((astronaut, index) => {\n    crewInfo += `  ${index + 1}. ${astronaut.name}\\n`;\n  });\n}\n\nreturn [{\n  json: {\n    ...mainData,\n    issAstronauts,\n    astronautCount: issAstronauts.length,\n    enrichedMessage: mainData.notificationMessage + crewInfo\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "7ed820ad-105b-4d39-bf5f-e0c8142de7fc",
      "name": "Send to Discord",
      "type": "n8n-nodes-base.discord",
      "position": [
        -1536,
        1616
      ],
      "parameters": {
        "content": "={{ $json.enrichedMessage }}",
        "options": {},
        "authentication": "webhook"
      },
      "typeVersion": 2
    },
    {
      "id": "06bddb7a-9442-4ea2-959d-d2310b26aa87",
      "name": "Send to Telegram",
      "type": "n8n-nodes-base.telegram",
      "position": [
        -1536,
        1808
      ],
      "parameters": {
        "text": "={{ $json.enrichedMessage }}",
        "chatId": "YOUR_CHAT_ID",
        "additionalFields": {}
      },
      "typeVersion": 1.2
    },
    {
      "id": "d67a038b-b204-42a9-8458-0807fb92804f",
      "name": "Send Email via Gmail",
      "type": "n8n-nodes-base.gmail",
      "position": [
        -1536,
        2016
      ],
      "parameters": {
        "sendTo": "user@example.com",
        "message": "={{ $json.enrichedMessage }}",
        "options": {},
        "subject": "=\ud83d\udef0\ufe0f ISS Alert - {{ $json.userLocation }}"
      },
      "typeVersion": 2.1
    },
    {
      "id": "6dbf1a79-4d39-491b-ace8-9c36a4beac5d",
      "name": "ISS Not Overhead",
      "type": "n8n-nodes-base.set",
      "position": [
        -3072,
        1920
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3.4
    },
    {
      "id": "6d9f8542-eec4-4908-ba2f-742bd753a95d",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4464,
        1520
      ],
      "parameters": {
        "width": 420,
        "height": 820,
        "content": "## \ud83d\udef0\ufe0f Real-time ISS Overhead Alert\n### with Weather Check & Multi-Channel Notifications\n\n**What this workflow does:**\nAutomatically detects when the International Space Station (ISS) passes over your location and sends alerts to Discord, Telegram, and Gmail - but only when weather conditions allow for actual observation.\n\n**Key Features:**\n- \ud83d\udce1 Real-time ISS position tracking (Open Notify API)\n- \ud83c\udf24\ufe0f Weather condition check (OpenWeatherMap)\n- \ud83e\udded Compass direction & elevation angle calculation\n- \ud83d\udc68\u200d\ud83d\ude80 Current ISS crew information\n- \ud83d\udd14 Multi-channel notifications (Discord, Telegram, Gmail)\n- \ud83c\udf0d Google Earth link to view ISS position\n\n**Who is this for:**\n- Space enthusiasts and astronomy hobbyists\n- Parents who want to share science moments with kids\n- Remote workers looking for a fun break reminder\n- Anyone who wants to spot the ISS with their own eyes\n\n**How to set up:**\n1. Configure your location in \"Calculate Distance and Direction\" node\n2. Add OpenWeatherMap API credentials\n3. Set up at least one notification channel (Discord/Telegram/Gmail)\n4. Activate the workflow!\n\n**APIs Used:**\n- Open Notify (free, no key required)\n- OpenWeatherMap (free tier available)"
      },
      "typeVersion": 1
    },
    {
      "id": "69d24c1c-709d-42b7-928b-6c0d67cc7c03",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -4000,
        1968
      ],
      "parameters": {
        "color": 7,
        "width": 440,
        "content": "### Step 1: Schedule & Get ISS Position\nRuns every 10 minutes to check ISS location.\nISS orbits Earth every ~90 minutes, so 10-min intervals ensure we don't miss a pass."
      },
      "typeVersion": 1
    },
    {
      "id": "dc3d41d2-ec03-4a93-a93a-f0470ced18a0",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3520,
        1968
      ],
      "parameters": {
        "color": 7,
        "width": 320,
        "height": 212,
        "content": "### Step 2: Calculate Distance & Direction\n\u26a0\ufe0f **CONFIGURE HERE:**\n- `USER_LAT` - Your latitude\n- `USER_LON` - Your longitude\n- `USER_LOCATION_NAME` - Your city name\n- `VISIBILITY_RADIUS_KM` - Alert radius (default: 800km)"
      },
      "typeVersion": 1
    },
    {
      "id": "1c8eee45-e6eb-4b16-8f77-e61f8301a719",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -3072,
        1488
      ],
      "parameters": {
        "color": 7,
        "width": 300,
        "height": 208,
        "content": "### Step 3: Weather Check\nOnly alerts you when ISS is actually visible.\nChecks cloud cover, rain, and time of day.\n\n**Requires:** OpenWeatherMap API key (free)"
      },
      "typeVersion": 1
    },
    {
      "id": "6f48e813-ca77-4959-b96b-ce2fd55dfd4a",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1536,
        1408
      ],
      "parameters": {
        "color": 7,
        "width": 300,
        "height": 180,
        "content": "### Step 4: Multi-Channel Notifications\nConfigure the channels you want to use:\n\n**Discord:** Add webhook URL\n**Telegram:** Add bot token & chat ID\n**Gmail:** Connect Google account\n\nDisable unused channels."
      },
      "typeVersion": 1
    }
  ],
  "active": true,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "c580ee61-c475-4fbf-86bb-ebb24c370fb2",
  "connections": {
    "Can Observe?": {
      "main": [
        [
          {
            "node": "Format Alert (Good Conditions)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Format Alert (Poor Conditions)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Messages": {
      "main": [
        [
          {
            "node": "Get ISS Crew Info",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get ISS Position": {
      "main": [
        [
          {
            "node": "Calculate Distance and Direction",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is ISS Overhead?": {
      "main": [
        [
          {
            "node": "Get Local Weather",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "ISS Not Overhead",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get ISS Position",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get ISS Crew Info": {
      "main": [
        [
          {
            "node": "Add Crew Information",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Local Weather": {
      "main": [
        [
          {
            "node": "Analyze Observation Conditions",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add Crew Information": {
      "main": [
        [
          {
            "node": "Send to Discord",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send to Telegram",
            "type": "main",
            "index": 0
          },
          {
            "node": "Send Email via Gmail",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze Observation Conditions": {
      "main": [
        [
          {
            "node": "Can Observe?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Alert (Good Conditions)": {
      "main": [
        [
          {
            "node": "Merge Messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Format Alert (Poor Conditions)": {
      "main": [
        [
          {
            "node": "Merge Messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Calculate Distance and Direction": {
      "main": [
        [
          {
            "node": "Is ISS Overhead?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}