Spaces:
Sleeping
Sleeping
File size: 6,411 Bytes
3aa60e0 4d0931b e3273bb 3aa60e0 4d0931b ff4e8e9 e9b0471 3aa60e0 5894fd7 3aa60e0 5894fd7 3aa60e0 4d0931b 9d3b2ec 4d0931b 3aa60e0 5894fd7 e3273bb 5894fd7 e3273bb 5894fd7 3aa60e0 5894fd7 43e56c9 3aa60e0 43e56c9 3aa60e0 43e56c9 3aa60e0 43e56c9 e9b0471 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | from fastapi import FastAPI, Request, HTTPException
import requests
import json
import uuid
from datetime import datetime, timezone
import os # Import the 'os' module
app = FastAPI()
# --- CONFIGURATION ---
# Use Hugging Face Secrets for sensitive data
CLIENT_ID = "amzn1.application-oa2-client.bc4475f5b85148fa8c1329e4bb6da5e9" # Your *NEW* ALEXA Client ID (from Custom Skill)
CLIENT_SECRET = os.environ.get("CLIENT_SECRET") # Get *ALEXA* Client Secret from environment variable
ALEXA_EVENT_GATEWAY = "https://api.amazonalexa.com/v3/events"
# --- DEVICE MAPPING ---
TRIGGERS = {
"Trigger1": {"endpointId": "device-001", "description": "Webhook Trigger 1", "state": "NOT_DETECTED"},
"Trigger2": {"endpointId": "device-002", "description": "Webhook Trigger 2", "state": "NOT_DETECTED"},
"Trigger3": {"endpointId": "device-003", "description": "Webhook Trigger 3", "state": "NOT_DETECTED"},
"Trigger4": {"endpointId": "device-004", "description": "Webhook Trigger 4", "state": "NOT_DETECTED"},
"Trigger5": {"endpointId": "device-005", "description": "Webhook Trigger 5", "state": "NOT_DETECTED"},
"Trigger6": {"endpointId": "device-006", "description": "Webhook Trigger 6", "state": "NOT_DETECTED"},
"Trigger7": {"endpointId": "device-007", "description": "Webhook Trigger 7", "state": "NOT_DETECTED"},
"Trigger8": {"endpointId": "device-008", "description": "Webhook Trigger 8", "state": "NOT_DETECTED"},
"Trigger9": {"endpointId": "device-009", "description": "Webhook Trigger 9", "state": "NOT_DETECTED"},
"Trigger10": {"endpointId": "device-010", "description": "Webhook Trigger 10", "state": "NOT_DETECTED"},
}
# --- TOKEN MANAGEMENT (Simple - In-Memory for Now) ---
TOKEN_DATA = {
"access_token": None,
"expires_at": None, # Timestamp when the token expires
}
# --- HELPER FUNCTION: Get LWA Access Token ---
async def get_lwa_access_token():
"""Fetches or refreshes the LWA access token."""
global TOKEN_DATA
# Check if we have a token and if it's still valid
if TOKEN_DATA["access_token"] and TOKEN_DATA["expires_at"] and datetime.now(timezone.utc).timestamp() < TOKEN_DATA["expires_at"]:
return TOKEN_DATA["access_token"]
# Get a new token
token_url = "https://api.amazon.com/auth/o2/token"
data = {
"grant_type": "client_credentials",
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"scope": "alexa::proactive_events",
}
# --- DEBUGGING LOGS ---
print("----- LWA Token Request Data -----")
print(data)
print("---------------------------------")
print(f"----- CLIENT_SECRET Value: {CLIENT_SECRET} -----")
# --- END OF DEBUGGING LOGS ---
try:
response = requests.post(token_url, data=data)
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
token_response = response.json()
TOKEN_DATA["access_token"] = token_response["access_token"]
# Calculate expiry time (subtract a small buffer to be safe)
TOKEN_DATA["expires_at"] = datetime.now(timezone.utc).timestamp() + token_response["expires_in"] - 60
return TOKEN_DATA["access_token"]
except requests.exceptions.RequestException as e:
print(f"Error getting LWA token: {e}")
raise HTTPException(status_code=500, detail="Failed to get LWA access token")
# --- HELPER FUNCTION: Send ChangeReport to Alexa ---
async def send_change_report(endpoint_id, property_name, property_value):
"""Sends a ChangeReport event to Alexa."""
try:
access_token = await get_lwa_access_token()
except HTTPException: # Re-raise exception from get_lwa_access_token
raise
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
}
change_report = {
"event": {
"header": {
"namespace": "Alexa",
"name": "ChangeReport",
"messageId": str(uuid.uuid4()),
"payloadVersion": "3",
},
"endpoint": {
"endpointId": endpoint_id,
},
"payload": {
"change": {
"cause": {"type": "PHYSICAL_INTERACTION"},
"properties": [
{
"namespace": "Alexa.ContactSensor",
"name": property_name,
"value": property_value,
"timeOfSample": datetime.now(timezone.utc).isoformat(),
"uncertaintyInMilliseconds": 0,
}
],
}
},
}
}
try:
response = requests.post(
ALEXA_EVENT_GATEWAY, headers=headers, data=json.dumps(change_report)
)
response.raise_for_status()
print(f"ChangeReport sent for {endpoint_id}. Response: {response.status_code} - {response.text}") # Log full response
except requests.exceptions.RequestException as e:
print(f"Error sending ChangeReport: {e}")
raise HTTPException(status_code=500, detail=f"Failed to send ChangeReport: {e}")
# --- WEBHOOK ENDPOINT ---
@app.post("/{trigger_name}")
async def alexa_webhook(trigger_name: str, request: Request):
data = await request.json()
print(f"Received request for {trigger_name}: {data}")
if trigger_name in TRIGGERS:
endpoint_id = TRIGGERS[trigger_name]["endpointId"]
# Toggle the state
current_state = TRIGGERS[trigger_name]["state"]
new_state = "NOT_DETECTED" if current_state == "DETECTED" else "DETECTED"
TRIGGERS[trigger_name]["state"] = new_state
try:
await send_change_report(endpoint_id, "detectionState", new_state)
except HTTPException as e:
return e # Return the HTTPException directly
return {
"event": {
"header": {
"namespace": "Alexa",
"name": "Response",
"payloadVersion": "3",
"messageId": str(uuid.uuid4())
},
"payload": {"message": f"{trigger_name} activated! New state: {new_state}"}
}
}
else:
raise HTTPException(status_code=404, detail=f"Unknown trigger: {trigger_name}") |