Skip to content

Instantly share code, notes, and snippets.

@craigjmidwinter
Last active June 14, 2020 17:55
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save craigjmidwinter/f4456186759302d75d3619d642999e5e to your computer and use it in GitHub Desktop.
Save craigjmidwinter/f4456186759302d75d3619d642999e5e to your computer and use it in GitHub Desktop.
googlefit steps component for home assistant
import json
import logging
import os
import time
from datetime import datetime, timedelta
import httplib2
import requests
import voluptuous as vol
from apiclient.discovery import build
from oauth2client.client import OAuth2WebServerFlow
from oauth2client.file import Storage
import homeassistant.helpers.config_validation as cv
from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import (CONF_MONITORED_VARIABLES, CONF_NAME,
CONF_PASSWORD, CONF_USERNAME, TEMP_CELSIUS)
from homeassistant.helpers.entity import Entity
from homeassistant.helpers.event import track_time_change
from homeassistant.util import Throttle, convert, dt
DOMAIN = 'google_fit'
# Change these values
CLIENT_ID = 'XXXXXXXXXXXXXXXXXX.apps.googleusercontent.com'
CLIENT_SECRET = 'XXXXXXXXXXXXXXXXXXXXXXX'
OAUTH_SCOPE = 'https://www.googleapis.com/auth/fitness.activity.read'
DATA_SOURCE = "derived:com.google.step_count.delta:com.google.android.gms:estimated_steps"
NOTIFICATION_ID = 'google_fit_notification'
NOTIFICATION_TITLE = 'Google Fit Setup'
TODAY = datetime.today().date()
NOW = datetime.today()
START = int(time.mktime(TODAY.timetuple())*1000000000)
END = int(time.mktime(NOW.timetuple())*1000000000)
DATA_SET = "%s-%s" % (START, END)
REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'
TOKEN_FILE = '.{}.token'.format(DOMAIN)
_LOGGER = logging.getLogger(__name__)
def nanoseconds(nanotime):
"""
Convert epoch time with nanoseconds to human-readable.
"""
dt = datetime.fromtimestamp(nanotime // 1000000000)
return dt.strftime('%Y-%m-%d %H:%M:%S')
if __name__ == "__main__":
# Point of entry in execution mode:
dataset = retrieve_data()
with open('dataset.txt', 'w') as outfile:
json.dump(dataset, outfile)
starts = []
ends = []
values = []
for point in dataset["point"]:
if int(point["startTimeNanos"]) > START:
starts.append(int(point["startTimeNanos"]))
ends.append(int(point["endTimeNanos"]))
values.append(point['value'][0]['intVal'])
def setup_platform(hass, config, add_devices, discovery_info=None):
"""Set up the GoogleFitSteps sensor."""
# Create a data fetcher to support all of the configured sensors. Then make
# the first call to init the data.
token_file = hass.config.path(TOKEN_FILE)
if not os.path.isfile(token_file):
do_authentication(hass, config)
else:
fit_service = GoogleFitSteps(hass.config.path(TOKEN_FILE))
add_devices([fit_service], True)
class GoogleFitSteps(Entity):
"""Implementation of a GoogleFitSteps sensor."""
def __init__(self, token_file):
"""Init the Google Fit service."""
self.token_file = token_file
self._name = 'steps'
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def state(self):
"""Return the state of the sensor."""
return self._state
def update(self):
data = self.retrieve_data()
steps = 0
for point in data['point']:
steps = steps + point['value'][0]['intVal']
self._state = steps
def retrieve_data(self):
credentials = Storage(self.token_file).get()
http = credentials.authorize(httplib2.Http())
fitness_service = build('fitness', 'v1', http=http, cache_discovery=False)
return fitness_service.users().dataSources(). \
datasets(). \
get(userId='me', dataSourceId=DATA_SOURCE, datasetId=DATA_SET). \
execute()
def do_authentication(hass, config):
"""Notify user of actions and authenticate.
Notify user of user_code and verification_url then poll
until we have an access token.
"""
from oauth2client.client import (
OAuth2WebServerFlow,
OAuth2DeviceCodeError,
FlowExchangeError
)
oauth = OAuth2WebServerFlow(
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
scope='https://www.googleapis.com/auth/fitness.activity.read',
redirect_uri='Home-Assistant.io',
)
try:
dev_flow = oauth.step1_get_device_and_user_codes()
except OAuth2DeviceCodeError as err:
hass.components.persistent_notification.create(
'Error: {}<br />You will need to restart hass after fixing.'
''.format(err),
title=NOTIFICATION_TITLE,
notification_id=NOTIFICATION_ID)
return False
hass.components.persistent_notification.create(
'In order to authorize Home-Assistant to view your steps '
'you must visit: <a href="{}" target="_blank">{}</a> and enter '
'code: {}'.format(dev_flow.verification_url,
dev_flow.verification_url,
dev_flow.user_code),
title=NOTIFICATION_TITLE, notification_id=NOTIFICATION_ID
)
def step2_exchange(now):
"""Keep trying to validate the user_code until it expires."""
if now >= dt.as_local(dev_flow.user_code_expiry):
hass.components.persistent_notification.create(
'Authenication code expired, please restart '
'Home-Assistant and try again',
title=NOTIFICATION_TITLE,
notification_id=NOTIFICATION_ID)
listener()
try:
credentials = oauth.step2_exchange(device_flow_info=dev_flow)
except FlowExchangeError:
# not ready yet, call again
return
storage = Storage(hass.config.path(TOKEN_FILE))
storage.put(credentials)
listener()
listener = track_time_change(hass, step2_exchange,
second=range(0, 60, dev_flow.interval))
return True
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment