[Python] Automatically Record Sleep Time to Toggl

Table of Contents
Create a program using Python to automatically write sleep time to Toggl.
Overview
To squeeze out free time or relaxation, I use several task management tools. As my main tool, I use TickTick to track completed tasks. However, I noticed it's hard to see how much time I spent on each one.
In terms of input visibility, Toggl allows me to easily measure time like a stopwatch and quickly record the work done. It also has an API for automatically accumulating data. Additionally, with its CSV export function, I can easily store and analyze data.
As a starting point, I'll write a process to automatically register sleep time from my smartwatch to Toggl and gradually get familiar with the API.
Input Data
I use Garmin's Venu 2 as my smartwatch.
Garmin does not have an official API, but there is a library that can access the API. I'll use this to obtain sleep logs and convert the data for the Toggl API. Although I'm using a Garmin watch this time, Fitbit has an official API as well.
Since it's a third-party product, the returns from the API will differ, but I can still obtain sleep records. As long as I can get the data, other watches should work fine for recording sleep time.
Additionally, by modifying the data, I can input various things beyond sleep, such as exercise time from other APIs, or record human activities, or even use it for non-human time tracking purposes, which could be interesting.
Coding
The repository for the code I wrote is at the following URL:
https://github.com/rmc8/Tool-for-transcribing-sleep-records-to-toggl
Main
The main processing is written in main.py. It pulls settings from a YAML file or command line and executes the program.
The command line is structured as follows.
python main.py {number of days to go back}
If you input 1
for the number of days to go back via the command line, it will try to retrieve sleep records from the previous day up to the day of program execution. If you input 30
, it will retrieve records from 30 days prior. If you omit the argument, it will only try to get data for the current day. It's a bit inconvenient not being able to specify dates, but I've simplified it since I want to set it up for automatic registration with a task scheduler.
The YAML file is as follows.
time_zone: 9
toggl:
token: # Required
time_entry:
workspace_id:
project_id:
task_id:
billable: False # Not required
tags:
- 睡眠
garmin:
email: # Required
password: # Required
YAML loading is implemented in util.py using PyYAML, a convenient library for reading YAML files into dictionary format. All contents of this configuration file are values to be passed to the API. The time_zone is the offset from UTC. The rest will be explained later.
After that, it retrieves sleep time from the Garmin API, converts the data for Toggl, and registers the sleep time to Toggl via the API. Finally, it outputs the response from the Toggl API as a CSV file in the ./output
directory.
Garmin API
https://github.com/rmc8/Tool-for-transcribing-sleep-records-to-toggl/blob/main/pkg/garmin.py
Data acquisition from Garmin uses the unofficial library as mentioned earlier. Therefore, there are no clues for data acquisition other than the repository's README, source code, and developer tools analysis. While reading the source code reveals endpoints and POST contents, I'll omit that here.
API Connection
class GarminAPI:
def __init__(self, email: str, password: str, time_deff: int = 9):
self.client = Garmin(email, password)
self.client.login()
self.time_deff: int = time_deff
The GarminAPI class connects to the API upon initialization. Authentication uses the same user information as Garmin Connect. Enter the email address and password in config.yml
. Since returns from the Garmin API are in UTC (GMT), I added time_deff
for time difference adjustment.
Sleep Log Acquisition
Use the get_sleep_logs
method to retrieve sleep information in bulk. Although encapsulated, the get_sleep_data
method from the garminconnect library quickly obtains a JSON including sleep time.
The dailySleepDTO
value in that JSON contains bedtime and wake-up time in epoch seconds (GMT). The following processes convert these to Python's datetime format and adjust for the time difference.
@staticmethod def epoc2date(epoc: int, time_deff: int): dt = datetime.fromtimestamp(epoc / 1000) return dt + timedelta(hours=time_deff)
start = self.epoc2date(display[“sleepStartTimestampGMT”], self.time_deff) end = self.epoc2date(display[“sleepEndTimestampGMT”], self.time_deff)
After that, the data is converted for Toggl. The dictionary contents are as follows.
Key | Value | Description |
---|---|---|
date | Date slept | Not used, but for reference |
description | Description | Here, it describes when you went to sleep. |
start | Start time | This corresponds to bedtime. |
stop | End time | This corresponds to wake-up time. |
duration | Duration | Time elapsed in seconds. |
duration
is explained in a complicated way in Toggl's official documentation, but it's used for the display in the yellow-marked section in the figure below.
It mentions that negatives are possible, which is unclear, but using it effectively could allow subtracting time spent awake during sleep. The others aren't particularly tricky, so I'll omit them for now.
Once the data for all dates is compiled with sleep times, it's converted to a Pandas DataFrame and returned to main.py
.
Toggl API
https://github.com/rmc8/Tool-for-transcribing-sleep-records-to-toggl/blob/main/pkg/toggl.py
The Toggl API is summarized on GitHub. There's a Python client, but since I couldn't get it from PyPI and the usage wasn't clear without reading the source code, I wrote only the necessary processes based on the documentation.
Authentication for the API uses a token. It's located at the bottom of the Profile page. Enter this in config.yml
under toggl > token
. The other three IDs in toggl > time_entry
are for recording in specific locations. billable
is basically False since sleep doesn't directly generate profit. Additionally, tags
can be set as a list for multiple tags. For now, I've set just one: - 睡眠
.
Recording to Toggl
Use the create_time_entry
method to record sleep time to Toggl. It takes the required values as arguments and uses keyword arguments and unpacking for others as needed.stop
isn't mandatory, but since duration can be tricky, including it ensures the end time is accurate.
Then, it generates a JSON payload based on the documentation and sends it via the _request
method to register the sleep times in bulk through the API.
Log Output
Output the API results as a CSV file in the ./output
directory. A sample is as follows.
id,wid,billable,start,stop,duration,description,tags,duronly,at,uid
2xxxxxxxxx,5xxxxxxxx,False,2021-09-25T23:46:00.000+09:00,2021-09-26T06:56:00.000+09:00,25800,Sleep time on 2021-09-25,['睡眠'],False,2021-09-26T08:59:03+00:00,7xxxxxxxx
Future Applications
I was able to automatically register sleep time. Similarly, for anything that can be automatically registered using smartwatches or IoT, I want to proceed with automatic registration first.
For TickTick, I'm using GAS or IFTTT for integration, and manually registering important tasks to organize what to do. If I can get this via API and select the tasks being done to control the stopwatch (start/stop) and (complete/incomplete), managing input and output would become easier.
Additionally, for parts without API, the app's functions are very simple, so using them as-is would make task management and analysis much easier.
Besides just using the Toggl app normally, there's still a lot of coding needed, but by reusing what's available and integrating via API, and since it's cloud-based for information sharing across all devices, it's very convenient. For personal use, it's free and has more than enough features, so I'm grateful.
Summary
Toggl is a great platform for managing and analyzing time. Although Japanese support is still limited, if you're not allergic to English, it's a highly useful and extensible tool. Please try combining it with various IoT devices or APIs for fun.