Search code examples
google-apiactions-on-googlegoogle-homegoogle-smart-home

Google Smart home report state error 403


I'm reporting states for devices using http post with a jwt generated using service account. Below is the payload for jwt

{
   "iss": "<service-account-email>",
   "scope": "https://www.googleapis.com/auth/homegraph",
   "aud": "https://accounts.google.com/o/oauth2/token",
   "iat": <current-time>,
   "exp": <current-time-plus-one-hour>
 }

after this I sign the jwt using the private key for my service account using the python library google.auth.crypt.RSASigner.from_service_account_file(path) and generate the jwt token. I am further using this token to obtain the access token from https://accounts.google.com/o/oauth/token, which is also successful. After obtaining the access token I am making a post request to https://homegraph.googleapis.com/v1/devices:reportStateAndNotification?key=api_key

with headers

{"Authorization": "Bearer <token>", "X-GFE-SSL": "yes", "Content-Type": "application/json"}

and json data

{ "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf", "agent_user_id": "1234", "payload": { "devices": { "states": { "1458765": { "on": true }, "4578964": { "on": true, "isLocked": true } } } } }

But this gives me

{'error': {'code': 403, 'message': 'The request is missing a valid API key.', 'status': 'PERMISSION_DENIED'}}

I followed the steps from https://developers.google.com/actions/smarthome/report-state is there anything i'm doing wrong? or am I missing any steps?

UPDATE: I added the api key to the uri now it gives me another error in response

{'error': {'code': 403, 'message': 'The caller does not have permission', 'status': 'PERMISSION_DENIED'}}

how do I resolve this issue?


Solution

  • In order to report the state to the Home Graph you have to:

    • Create a Service Account for the token creation from your SmartHomeApp project:
    • Go to APIs & Services => Click on Credentials Go to 'APIs & Services' => Click on 'Credentials'
    • Click on Create credentials => Click on Service account key Click on 'Create credentials' => Click on 'Service account key'
    • Fill with your data creating a new New service account => Select the role Service Accounts > Service Account Token Creator Fill with your data creating a new 'New service account' => Select the role 'Service Accounts > Service Account Token Creator'
    • Download the JSON file with your keys and certificate
    • Get a valid signed JWT token:

      credentials = service_account.Credentials.from_service_account_file(service_account_file, scopes="https://www.googleapis.com/auth/homegraph")
      
      now = int(time.time())
      expires = now + 3600  # One hour in seconds
      
      payload = {
          'iat': now,
          'exp': expires,
          'aud': "https://accounts.google.com/o/oauth2/token",
          'scope': SCOPE,
          'iss': credentials.service_account_email
      }
      
      signed_jwt = google.auth.jwt.encode(credentials.signer, payload)
      
    • Obtain a valid Access token:

      headers = {"Authorization": "Bearer {}".format(signed_jwt.decode("utf-8")), "Content-Type": "application/x-www-form-urlencoded"}
      data = {"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer", "assertion": signed_jwt}
      
      access_token = requests.post("https://accounts.google.com/o/oauth2/token", data=data, headers=headers).get("access_token")
      
    • Send the reported states:

      headers = {"Authorization": "Bearer {}".format(access_token), "X-GFE-SSL": "yes"}
      data = {"requestId": request_id, "agent_user_id": agent_user_id, "payload": {"devices": {"states": states}}}
      
      requests.post("https://homegraph.googleapis.com/v1/devices:reportStateAndNotification", data=json.dumps(data), headers=headers)
      

    NOTE: in order to work these snippets require to import google.auth.jwt, from google.oauth2 import service_accountand import requests from google-auth, google-auth-httplib2 and requests packages.