I should have known this one would take longer than I expected.
My Jawbone UP finally died, so I replaced it with a FitBit. I've really enjoyed the Jawbones, but I'm not confident in the future of the company's wearables department. I decided to take the plunge and dive in to the FitBit ecosystem instead. I got a new FitBit Charge 2, which among other things includes a heart rate monitor which I'm excited about. But step one was rewriting my sleep import script to pull sleep data from FitBit instead of Jawbone.

Registering for the FitBit API was pretty straightforward. I was also impressed that the FitBit API provides additional capabilities for your own account when you tell it you're creating a "Personal" application. You get a lot more data out of the API compared to applications accessing other peoples' accounts. For example, you get the minute-by-minute sleep data needed to build the image that I show for sleep posts.
One thing I was surprised about was that the API doesn't have any concept of absolute timestamps. All the times provided from the API lack a timezone and are local time. This means there are pieces of information such as "startTime": "2017-01-29T21:50:00.000" which doesn't actually correspond to an absolute point in time. This is a challenge when importing this to my website, since I want to pin that to an actual timestamp with a proper timezone offset. I realized this is the same problem I encountered with timestamps in Exif data in my photos. I even wrote a whole description of the problem and my solution. I had forgotten that at the end of that I turned the whole thing into an API so that I could reuse it later, so thanks past me!
Now that I'm able to take a sleep record that starts at "2017-01-29T21:50:00" and turn that into an absolute point in time, the next step was generating the sleep image from the raw data they provide. FitBit provides minute-by-minute data, so I just draw a bunch of skinny rectangles in the SVG and let the browser sort it out. This looks fine at 100% zoom, for example:

However, the number of vertical lines in the image is dependent on how long I was asleep for, so my images are all different widths. When I render this on my website, I just set the width to 100% and let the browser scale it. This unfortunately results in some interesting scaling artifacts that I'm not happy with:

I don't really know how to solve this, so I'm going to leave it alone for now. I always have the raw data in the post so I can go back and regenerate the images again later.