1
0
mirror of https://github.com/USA-RedDragon/badnest.git synced 2025-01-18 22:50:44 +00:00

Added Code for Google Auth Login

This commit is contained in:
SjoerdOegema 2019-10-17 09:17:14 +02:00 committed by Jacob McSwain
parent c686190a24
commit 5d74119c38
No known key found for this signature in database
GPG Key ID: 59957AD09B94EDF5
7 changed files with 123 additions and 19 deletions

1
.gitignore vendored
View File

@ -90,3 +90,4 @@ ENV/
.ropeproject
.vscode/
.DS_Store

View File

@ -3,14 +3,19 @@ import voluptuous as vol
from homeassistant.helpers import config_validation as cv
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
from .const import DOMAIN
from .const import DOMAIN, CONF_ISSUE_TOKEN, CONF_COOKIE, CONF_APIKEY
CONFIG_SCHEMA = vol.Schema(
{
DOMAIN: vol.Schema(
DOMAIN: vol.All(
{
vol.Required(CONF_EMAIL): cv.string,
vol.Required(CONF_PASSWORD): cv.string,
vol.Required(CONF_EMAIL, default=""): cv.string,
vol.Required(CONF_PASSWORD, default=""): cv.string,
},
{
vol.Required(CONF_ISSUE_TOKEN, default=""): cv.string,
vol.Required(CONF_COOKIE, default=""): cv.string,
vol.Required(CONF_APIKEY, default=""): cv.string
}
)
},
@ -23,10 +28,22 @@ def setup(hass, config):
if config.get(DOMAIN) is not None:
email = config[DOMAIN].get(CONF_EMAIL)
password = config[DOMAIN].get(CONF_PASSWORD)
issue_token = config[DOMAIN].get(CONF_ISSUE_TOKEN)
cookie = config[DOMAIN].get(CONF_COOKIE)
api_key = config[DOMAIN].get(CONF_APIKEY)
else:
email = None
password = None
issue_token = None
cookie = None
api_key = None
hass.data[DOMAIN] = {CONF_EMAIL: email, CONF_PASSWORD: password}
hass.data[DOMAIN] = {
CONF_EMAIL: email,
CONF_PASSWORD: password,
CONF_ISSUE_TOKEN: issue_token,
CONF_COOKIE: cookie,
CONF_APIKEY: api_key
}
return True

View File

@ -3,32 +3,71 @@ import requests
API_URL = "https://home.nest.com"
CAMERA_WEBAPI_BASE = "https://webapi.camera.home.nest.com"
CAMERA_URL = "https://nexusapi-us1.camera.home.nest.com"
USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) " \
"AppleWebKit/537.36 (KHTML, like Gecko) " \
"Chrome/75.0.3770.100 Safari/537.36"
URL_JWT = "https://nestauthproxyservice-pa.googleapis.com/v1/issue_jwt"
class NestAPI:
def __init__(self, email, password):
def __init__(self, email, password, issue_token, cookie, api_key):
self._user_id = None
self._access_token = None
self._session = requests.Session()
self._session.headers.update({"Referer": "https://home.nest.com/"})
self._device_id = None
self._login(email, password)
if email is not None and password is not None:
self._login_nest(email, password)
else:
self._login_google(issue_token, cookie, api_key)
self.update()
def _login(self, email, password):
def _login_nest(self, email, password):
r = self._session.post(
f"{API_URL}/session", json={"email": email, "password": password}
)
self._user_id = r.json()["userid"]
self._access_token = r.json()["access_token"]
def _login_google(self, issue_token, cookie, api_key):
headers = {
'Sec-Fetch-Mode': 'cors',
'User-Agent': USER_AGENT,
'X-Requested-With': 'XmlHttpRequest',
'Referer': 'https://accounts.google.com/o/oauth2/iframe',
'cookie': cookie
}
r = requests.get(url=issue_token, headers=headers)
access_token = r.json()['access_token']
headers = {
'Authorization': 'Bearer ' + access_token,
'User-Agent': USER_AGENT,
'x-goog-api-key': api_key,
'Referer': 'https://home.nest.com'
}
params = {
"embed_google_oauth_access_token": True,
"expire_after": '3600s',
"google_oauth_access_token": access_token,
"policy_id": 'authproxy-oauth-policy'
}
r = requests.post(url=URL_JWT, headers=headers, params=params)
self._user_id = r.json()['claims']['subject']['nestId']['id']
self._access_token = r.json()['jwt']
def update(self):
raise NotImplementedError()
class NestThermostatAPI(NestAPI):
def __init__(self, email, password):
super(NestThermostatAPI, self).__init__(email, password)
def __init__(self, email, password, issue_token, cookie, api_key):
super(NestThermostatAPI, self).__init__(
email,
password,
issue_token,
cookie,
api_key)
self._shared_id = None
self._czfe_url = None
self._compressor_lockout_enabled = None
@ -182,8 +221,13 @@ class NestThermostatAPI(NestAPI):
class NestCameraAPI(NestAPI):
def __init__(self, email, password):
super(NestCameraAPI, self).__init__(email, password)
def __init__(self, email, password, issue_token, cookie, api_key):
super(NestCameraAPI, self).__init__(
email,
password,
issue_token,
cookie,
api_key)
# log into dropcam
self._session.post(
f"{API_URL}/dropcam/api/login",

View File

@ -7,7 +7,7 @@ from homeassistant.components.camera import Camera, SUPPORT_ON_OFF
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
from .api import NestCameraAPI
from .const import DOMAIN
from .const import DOMAIN, CONF_ISSUE_TOKEN, CONF_COOKIE, CONF_APIKEY
_LOGGER = logging.getLogger(__name__)
@ -25,7 +25,10 @@ async def async_setup_platform(hass,
api = NestCameraAPI(
hass.data[DOMAIN][CONF_EMAIL],
hass.data[DOMAIN][CONF_PASSWORD]
hass.data[DOMAIN][CONF_PASSWORD],
hass.data[DOMAIN][CONF_ISSUE_TOKEN],
hass.data[DOMAIN][CONF_COOKIE],
hass.data[DOMAIN][CONF_APIKEY]
)
# cameras = await hass.async_add_executor_job(nest.get_cameras())

View File

@ -29,7 +29,7 @@ from homeassistant.const import (
)
from .api import NestThermostatAPI
from .const import DOMAIN
from .const import DOMAIN, CONF_ISSUE_TOKEN, CONF_COOKIE, CONF_APIKEY
NEST_MODE_HEAT_COOL = "range"
NEST_MODE_ECO = "eco"
@ -64,7 +64,10 @@ async def async_setup_platform(hass,
"""Set up the Nest climate device."""
nest = NestThermostatAPI(
hass.data[DOMAIN][CONF_EMAIL],
hass.data[DOMAIN][CONF_PASSWORD]
hass.data[DOMAIN][CONF_PASSWORD],
hass.data[DOMAIN][CONF_ISSUE_TOKEN],
hass.data[DOMAIN][CONF_COOKIE],
hass.data[DOMAIN][CONF_APIKEY]
)
async_add_entities([NestClimate(nest)])

View File

@ -1 +1,4 @@
DOMAIN = 'badnest'
CONF_ISSUE_TOKEN = 'issue_token'
CONF_COOKIE = 'cookie'
CONF_APIKEY = 'api_key'

37
info.md
View File

@ -13,20 +13,53 @@ This isn't an advertised or public API, it's still better than web scraping, but
- Tested with a single thermostat, I have no other devices to test with
- Camera integration is untested by me
- Nest could change their webapp api at any time, making this defunct
- Won't work with Google-linked accounts
- Presets don't work (Eco, Away)
## Example configuration.yaml
## Example configuration.yaml - When you're not using the Google Auth Login
```yaml
badnest:
email: email@domain.com
password: !secret nest_password
climate:
- platform: badnest
scan_interval: 10
camera:
- platform: badnest
```
## Example configuration.yaml - When you are using the Google Auth Login
```yaml
badnest:
issue_token: "https://accounts.google.com/o/oauth2/iframerpc....."
cookie: "OCAK=......"
api_key : "#YOURAPIKEYHERE#"
climate:
- platform: badnest
scan_interval: 10
camera:
- platform: badnest
```
Google Login support added with many thanks to: chrisjshull from <https://github.com/chrisjshull/homebridge-nest/>
The values of `"issue_token"`, `"cookie"` and `"api_key"` are specific to your Google Account. To get them, follow these steps (only needs to be done once, as long as you stay logged into your Google Account).
1. Open a Chrome browser tab in Incognito Mode (or clear your cache).
2. Open Developer Tools (View/Developer/Developer Tools).
3. Click on 'Network' tab. Make sure 'Preserve Log' is checked.
4. In the 'Filter' box, enter `issueToken`
5. Go to `home.nest.com`, and click 'Sign in with Google'. Log into your account.
6. One network call (beginning with `iframerpc`) will appear in the Dev Tools window. Click on it.
7. In the Headers tab, under General, copy the entire `Request URL` (beginning with `https://accounts.google.com`, ending with `nest.com`). This is your `"issue_token"` in `configuration.yaml`.
8. In the 'Filter' box, enter `oauth2/iframe`
9. Several network calls will appear in the Dev Tools window. Click on the last `iframe` call.
10. In the Headers tab, under Request Headers, copy the entire `cookie` (beginning `OCAK=...` - **include the whole string which is several lines long and has many field/value pairs** - do not include the `cookie:` name). This is your `"cookie"` in `configuration.yaml`.
11. In the 'Filter' box, enter `issue_jwt`
12. Click on the last `issue_jwt` call.
13. In the Headers tab, under Request Headers, copy the entire `x-goog-api-key` (do not include the `x-goog-api-key:` name). This is your `"api_key"` in `configuration.yaml`.