Skip to main content
Glama
alexa-dynamic.js25.1 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (g && (g = 0, op[0] && (_ = 0)), _) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getAccountInfo = getAccountInfo; exports.getAlexaEndpoints = getAlexaEndpoints; exports.getAlexaDevices = getAlexaDevices; exports.getPrimaryEchoDevice = getPrimaryEchoDevice; exports.getPrimaryMediaDevice = getPrimaryMediaDevice; exports.getSmartHomeFavorites = getSmartHomeFavorites; exports.getSmartHomeEntities = getSmartHomeEntities; exports.getPrimaryLight = getPrimaryLight; exports.getAllDeviceEntityIds = getAllDeviceEntityIds; exports.getCustomerSmartHomeEndpoints = getCustomerSmartHomeEndpoints; exports.getEchoDeviceEntityId = getEchoDeviceEntityId; exports.getLightApplianceId = getLightApplianceId; exports.extractEntityId = extractEntityId; exports.buildEndpointId = buildEndpointId; var alexa_1 = require("@/schemas/alexa"); var alexa_2 = require("@/utils/alexa"); // Cache for dynamic values to avoid repeated API calls var cache = new Map(); var CACHE_TTL = 5 * 60 * 1000; // 5 minutes function getCached(key) { var cached = cache.get(key); if (cached && Date.now() - cached.timestamp < CACHE_TTL) { return cached.value; } return null; } function setCache(key, value) { cache.set(key, { value: value, timestamp: Date.now() }); } // Fetch account information dynamically function getAccountInfo(env) { return __awaiter(this, void 0, void 0, function () { var cacheKey, cached, response, data, validatedData, primaryAccount, accountInfo; return __generator(this, function (_a) { switch (_a.label) { case 0: cacheKey = "account_info"; cached = getCached(cacheKey); if (cached) return [2 /*return*/, cached]; return [4 /*yield*/, fetch("https://alexa-comms-mobile-service.amazon.com/accounts", { method: "GET", headers: (0, alexa_2.buildAlexaHeaders)(env), })]; case 1: response = _a.sent(); if (!response.ok) { throw new Error("Failed to fetch account info: ".concat(response.status)); } return [4 /*yield*/, response.json()]; case 2: data = _a.sent(); validatedData = alexa_1.AccountInfoSchema.parse(data); primaryAccount = validatedData.find(function (account) { return account.signedInUser; }) || validatedData[0]; if (!primaryAccount) { throw new Error("No account found in response"); } accountInfo = { customerId: primaryAccount.directedId, profiles: [], // Not available in this simpler endpoint }; setCache(cacheKey, accountInfo); return [2 /*return*/, accountInfo]; } }); }); } // Fetch all Alexa devices/endpoints function getAlexaEndpoints(env) { return __awaiter(this, void 0, void 0, function () { var cacheKey, cached, response, data, validatedData; return __generator(this, function (_a) { switch (_a.label) { case 0: cacheKey = "alexa_endpoints"; cached = getCached(cacheKey); if (cached) return [2 /*return*/, cached]; return [4 /*yield*/, fetch("https://alexa.amazon.com/api/smarthome/v2/endpoints", { method: "POST", headers: (0, alexa_2.buildAlexaHeaders)(env, { "Content-Type": "application/json; charset=utf-8", "Cache-Control": "no-cache", }), body: JSON.stringify({ endpointContexts: ["GROUP"] }), })]; case 1: response = _a.sent(); if (!response.ok) { throw new Error("Failed to fetch endpoints: ".concat(response.status)); } return [4 /*yield*/, response.json()]; case 2: data = _a.sent(); validatedData = alexa_1.EndpointsDiscoverySchema.parse(data); setCache(cacheKey, validatedData.endpoints); return [2 /*return*/, validatedData.endpoints]; } }); }); } // Fetch registered devices function getAlexaDevices(env) { return __awaiter(this, void 0, void 0, function () { var cacheKey, cached, response, data, validatedData; return __generator(this, function (_a) { switch (_a.label) { case 0: cacheKey = "alexa_devices"; cached = getCached(cacheKey); if (cached) return [2 /*return*/, cached]; return [4 /*yield*/, fetch("https://alexa.amazon.com/api/devices-v2/device?cached=true", { method: "GET", headers: (0, alexa_2.buildAlexaHeaders)(env, { Accept: "application/json; charset=utf-8", "Cache-Control": "no-cache", }), })]; case 1: response = _a.sent(); if (!response.ok) { throw new Error("Failed to fetch devices: ".concat(response.status)); } return [4 /*yield*/, response.json()]; case 2: data = _a.sent(); validatedData = alexa_1.PhoenixDevicesSchema.parse(data); setCache(cacheKey, validatedData.devices); return [2 /*return*/, validatedData.devices]; } }); }); } // Get primary Echo device for announcements function getPrimaryEchoDevice(env) { return __awaiter(this, void 0, void 0, function () { var devices, echoDevices; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, getAlexaDevices(env)]; case 1: devices = _a.sent(); echoDevices = devices.filter(function (device) { return device.deviceFamily === "ECHO" && device.online; }); if (echoDevices.length === 0) { throw new Error("No online Echo devices found"); } // Return the first available Echo device return [2 /*return*/, echoDevices[0]]; } }); }); } // Get primary smart home device for media queries function getPrimaryMediaDevice(env) { return __awaiter(this, void 0, void 0, function () { var devices, mediaDevices, onlineDevices; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, getAlexaDevices(env)]; case 1: devices = _a.sent(); mediaDevices = devices.filter(function (device) { var _a; return ((_a = device.capabilities) === null || _a === void 0 ? void 0 : _a.includes("AUDIO_PLAYER")) && device.online; }); if (mediaDevices.length === 0) { onlineDevices = devices.filter(function (device) { return device.online; }); if (onlineDevices.length === 0) { throw new Error("No online devices found"); } return [2 /*return*/, onlineDevices[0]]; } return [2 /*return*/, mediaDevices[0]]; } }); }); } // Get favorites (main smart home devices) - more reliable than endpoints function getSmartHomeFavorites(env) { return __awaiter(this, void 0, void 0, function () { var cacheKey, cached, query, response, data, validatedData, favorites; return __generator(this, function (_a) { switch (_a.label) { case 0: cacheKey = "smart_home_favorites"; cached = getCached(cacheKey); if (cached) return [2 /*return*/, cached]; query = "\n fragment FavoriteMetadata on Favorite {\n resource {\n id\n __typename\n }\n favoriteFriendlyName\n displayInfo {\n displayCategories {\n primary {\n isCustomerSpecified\n isDiscovered\n value\n sources\n __typename\n }\n all {\n isCustomerSpecified\n isDiscovered\n value\n sources\n __typename\n }\n __typename\n }\n __typename\n }\n alternateIdentifiers {\n legacyIdentifiers {\n chrsIdentifier {\n entityId\n __typename\n }\n dmsIdentifier {\n deviceSerialNumber {\n type\n value {\n text\n __typename\n }\n __typename\n }\n deviceType {\n type\n value {\n text\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n type\n rank\n active\n variant\n __typename\n }\n\n query ListFavoritesForHomeChannel($requestedTypes: [String!]) {\n favorites(listFavoritesInput: {requestedTypes: $requestedTypes}) {\n favorites {\n ...FavoriteMetadata\n __typename\n }\n __typename\n }\n }\n "; return [4 /*yield*/, fetch("https://alexa.amazon.com/nexus/v1/graphql", { method: "POST", headers: (0, alexa_2.buildAlexaHeaders)(env, { "Content-Type": "application/json", "X-Amzn-Marketplace-Id": "ATVPDKIKX0DER", "X-Amzn-Client": "AlexaApp", "X-Amzn-Os-Name": "android", }), body: JSON.stringify({ operationName: "ListFavoritesForHomeChannel", variables: { requestedTypes: [ "AEA", "ALEXA_LIST", "AWAY_LIGHTING", "DEVICE_SHORTCUT", "DTG", "ENDPOINT", "SHORTCUT", "STATIC_ENTERTAINMENT", ], }, query: query, }), })]; case 1: response = _a.sent(); if (!response.ok) { throw new Error("Failed to fetch favorites: ".concat(response.status)); } return [4 /*yield*/, response.json()]; case 2: data = _a.sent(); validatedData = alexa_1.SmartHomeFavoritesSchema.parse(data); favorites = validatedData.data.favorites.favorites; setCache(cacheKey, favorites); return [2 /*return*/, favorites]; } }); }); } // Find smart home entities (lights, sensors, etc.) using favorites API function getSmartHomeEntities(env) { return __awaiter(this, void 0, void 0, function () { var favorites, smartHomeDevices; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, getSmartHomeFavorites(env)]; case 1: favorites = _a.sent(); smartHomeDevices = favorites.filter(function (favorite) { return favorite.active && favorite.type === "ENDPOINT"; }); return [2 /*return*/, smartHomeDevices]; } }); }); } // Get the primary light entity dynamically (first available light) function getPrimaryLight(env) { return __awaiter(this, void 0, void 0, function () { var smartHomeDevices, lightDevices; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, getSmartHomeEntities(env)]; case 1: smartHomeDevices = _a.sent(); lightDevices = smartHomeDevices.filter(function (device) { var _a, _b, _c; var primaryCategory = (_c = (_b = (_a = device.displayInfo) === null || _a === void 0 ? void 0 : _a.displayCategories) === null || _b === void 0 ? void 0 : _b.primary) === null || _c === void 0 ? void 0 : _c.value; return primaryCategory === "LIGHT"; }); if (lightDevices.length === 0) { throw new Error("No smart home light devices found"); } // Return the first light device return [2 /*return*/, lightDevices[0]]; } }); }); } // Get all device entity IDs for state requests function getAllDeviceEntityIds(env) { return __awaiter(this, void 0, void 0, function () { var _a, endpoints, devices, entityIds; return __generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, Promise.all([getAlexaEndpoints(env), getAlexaDevices(env)])]; case 1: _a = _b.sent(), endpoints = _a[0], devices = _a[1]; entityIds = []; // Add Echo devices for temperature/illuminance sensors devices.forEach(function (device) { if (device.online) { // Add bridge entities for Echo devices entityIds.push({ entityId: "AlexaBridge_".concat(device.serialNumber, "@").concat(device.deviceType, "_").concat(device.serialNumber), entityType: "APPLIANCE", }); } }); // Add smart home endpoints endpoints.forEach(function (endpoint) { var _a; var entityId = ((_a = endpoint.identifier) === null || _a === void 0 ? void 0 : _a.entityId) || endpoint.serialNumber; if (entityId) { entityIds.push({ entityId: entityId, entityType: "APPLIANCE", }); } }); return [2 /*return*/, entityIds]; } }); }); } // Get customer smart home endpoints using GraphQL function getCustomerSmartHomeEndpoints(env) { return __awaiter(this, void 0, void 0, function () { var cacheKey, cached, query, response, data, endpoints; var _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: cacheKey = "customer_smart_home_endpoints"; cached = getCached(cacheKey); if (cached) return [2 /*return*/, cached]; query = "\n\t\tquery CustomerSmartHome {\n\t\t\tendpoints(\n\t\t\t\tendpointsQueryParams: { paginationParams: { disablePagination: true } }\n\t\t\t) {\n\t\t\t\titems {\n\t\t\t\t\tendpointId\n\t\t\t\t\tid\n\t\t\t\t\tfriendlyName\n\t\t\t\t\tdisplayCategories {\n\t\t\t\t\t\tall {\n\t\t\t\t\t\t\tvalue\n\t\t\t\t\t\t}\n\t\t\t\t\t\tprimary {\n\t\t\t\t\t\t\tvalue\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tlegacyIdentifiers {\n\t\t\t\t\t\tchrsIdentifier {\n\t\t\t\t\t\t\tentityId\n\t\t\t\t\t\t}\n\t\t\t\t\t\tdmsIdentifier {\n\t\t\t\t\t\t\tdeviceType {\n\t\t\t\t\t\t\t\ttype\n\t\t\t\t\t\t\t\tvalue {\n\t\t\t\t\t\t\t\t\ttext\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tdeviceSerialNumber {\n\t\t\t\t\t\t\t\ttype\n\t\t\t\t\t\t\t\tvalue {\n\t\t\t\t\t\t\t\t\ttext\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tlegacyAppliance {\n\t\t\t\t\t\tapplianceId\n\t\t\t\t\t\tapplianceTypes\n\t\t\t\t\t\tfriendlyName\n\t\t\t\t\t\tentityId\n\t\t\t\t\t\tmergedApplianceIds\n\t\t\t\t\t\tcapabilities\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t"; return [4 /*yield*/, fetch("https://alexa.amazon.com/nexus/v1/graphql", { method: "POST", headers: (0, alexa_2.buildAlexaHeaders)(env, { "Content-Type": "application/json", "X-Amzn-Marketplace-Id": "ATVPDKIKX0DER", "X-Amzn-Client": "AlexaApp", "X-Amzn-Os-Name": "android", }), body: JSON.stringify({ query: query }), })]; case 1: response = _c.sent(); if (!response.ok) { throw new Error("Failed to fetch customer smart home endpoints: ".concat(response.status)); } return [4 /*yield*/, response.json()]; case 2: data = (_c.sent()); endpoints = ((_b = (_a = data.data) === null || _a === void 0 ? void 0 : _a.endpoints) === null || _b === void 0 ? void 0 : _b.items) || []; setCache(cacheKey, endpoints); return [2 /*return*/, endpoints]; } }); }); } // Get Echo device entity ID for sensors (temperature, illuminance) function getEchoDeviceEntityId(env) { return __awaiter(this, void 0, void 0, function () { var endpoints, echoDevice; var _a, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: return [4 /*yield*/, getCustomerSmartHomeEndpoints(env)]; case 1: endpoints = _c.sent(); echoDevice = endpoints.find(function (endpoint) { var _a, _b; var primaryCategory = (_b = (_a = endpoint.displayCategories) === null || _a === void 0 ? void 0 : _a.primary) === null || _b === void 0 ? void 0 : _b.value; return primaryCategory === "ALEXA_VOICE_ENABLED"; }); if (!echoDevice) { throw new Error("No Echo device found for sensors"); } // For sensor data, we need the entity ID, not appliance ID return [2 /*return*/, ((_b = (_a = echoDevice.legacyIdentifiers) === null || _a === void 0 ? void 0 : _a.chrsIdentifier) === null || _b === void 0 ? void 0 : _b.entityId) || echoDevice.entityId]; } }); }); } // Get light appliance ID for state requests (different from entity ID) function getLightApplianceId(env) { return __awaiter(this, void 0, void 0, function () { var endpoints, lightDevice; var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, getCustomerSmartHomeEndpoints(env)]; case 1: endpoints = _b.sent(); lightDevice = endpoints.find(function (endpoint) { var _a, _b; var primaryCategory = (_b = (_a = endpoint.displayCategories) === null || _a === void 0 ? void 0 : _a.primary) === null || _b === void 0 ? void 0 : _b.value; return primaryCategory === "LIGHT"; }); if (!lightDevice) { throw new Error("No light device found"); } // Return the appliance ID from legacyAppliance return [2 /*return*/, (_a = lightDevice.legacyAppliance) === null || _a === void 0 ? void 0 : _a.applianceId]; } }); }); } // Helper to extract entity ID from smart home device function extractEntityId(device) { var _a, _b, _c, _d, _e, _f, _g; // For favorites API response, entity ID is in alternateIdentifiers if ((_c = (_b = (_a = device.alternateIdentifiers) === null || _a === void 0 ? void 0 : _a.legacyIdentifiers) === null || _b === void 0 ? void 0 : _b.chrsIdentifier) === null || _c === void 0 ? void 0 : _c.entityId) { return device.alternateIdentifiers.legacyIdentifiers.chrsIdentifier.entityId; } // For other API responses, check identifier if ((_d = device.identifier) === null || _d === void 0 ? void 0 : _d.entityId) { return device.identifier.entityId; } // Extract from resource.id if available (favorites format) if ((_f = (_e = device.resource) === null || _e === void 0 ? void 0 : _e.id) === null || _f === void 0 ? void 0 : _f.includes("endpoint.")) { return device.resource.id.replace("amzn1.alexa.endpoint.", ""); } // Fallback to serial number return device.serialNumber || ((_g = device.resource) === null || _g === void 0 ? void 0 : _g.id); } // Helper to build endpoint ID from entity ID function buildEndpointId(entityId) { if (entityId.startsWith("amzn1.alexa.endpoint.")) { return entityId; } return "amzn1.alexa.endpoint.".concat(entityId); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/guitarbeat/alexa-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server