The Daily Parker

Politics, Weather, Photography, and the Dog

Taking a minute

A friend I met just last November died on Friday night at 32. She owned Heirloom Books in Chicago's Edgewater community, one of the last independent used-book shops in the neighborhood. (Always a "shop;" never a "store.") She'd only recently adopted a kitten, Pilar, who I met a few weeks ago.

Her father posted on Facebook that she died "peacefully but unexpectedly...from complications arising from a long-standing illness which she fought valiantly against over many years, but which few people were aware of." I was aware of it, and I can say without hesitation it was not inherently fatal. But the endlessness and isolation of the pandemic, exacerbated by a president who's too stupid and too narcissistic to have the least comprehension or compassion a human being needs to call himself one, surely contributed. So did the difficulty of getting affordable health care here.

I'm sad a friend died, and I'm angry that in almost any other country she wouldn't have.

My heart goes out to her parents and her sister.

As the pipeline builds...

I'm waiting for a build to finish so I can sign off work for the day, so I've queued up a few things to read later:

Looks like the build is done, and all the tests passed. (I love green pipelines.)

How to pass secrets into your custom log target

Today I finally solved a problem that has nagged me for months: Given a .NET Core 3.1 Web API, NLog, and a custom log target for NLog, how could I pass secrets into the log target using Azure Key Vault?

The last bit required me to add Azure Key Vault support to the InnerDrive.Logging NuGet package, which I did back in February. The KeyVaultSecretProvider class allows you to retrieve secrets from a specified Azure Key Vault. (I'm in the process of updating it to handle keys as well.) Before that, you'd have to put your secrets in configuration files, which anyone on your development team or who has access to your Git repository can see. For example, to use our SendGridTarget class, you would have to put this in your nLog.config file:

<extensions>
	<add assembly="NLog.Web.AspNetCore"/>
	<add assembly="InnerDrive.Logging"/>
</extensions>

<targets async="false">
	<target xsi:type="SendGridTarget"
	        name="sendgrid"
		apiKey="{supposedly secret key}"
		applicationName="My Cool App"
	        from="service@contoso.org"
	        to="admin@contoso.org"
	/>
</targets>

That is...suboptimal. Instead, you want to use a class that implements ISecretProvider and inject it into the SendGridTarget class through this constructor:

/// <summary>
/// Creates a new instance of <see cref="SendGridTarget"/> with
/// a specified <see cref="ISendGridSender"/> implementation.
/// </summary>
/// <param name="sender">The <see cref="ISendGridSender"/> to use</param>
/// <param name="secretProvider">The <see cref="IConfiguration"/> to use</param>
/// <param name="apiKey">The API key to use</param>
public SendGridTarget(
	ISendGridSender sender, 
	ISecretProvider secretProvider = null, 
	string apiKey = null)
{
	IncludeEventProperties = true;
	if (null != secretProvider) Configuration = secretProvider;
	if (!string.IsNullOrWhiteSpace(apiKey)) ApiKey = apiKey;
	_sender = sender;
}

And now I'm going to save you about 30 hours of research and frustration. To get NLog to inject the secret provider into the target, I added this method to Startup.cs:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IComponentContext container)
{
	// Setup code elided
	ConfigureLogging(container);
}

public static void ConfigureLogging(IComponentContext container)
{
	var defaultConstructor = ConfigurationItemFactory.Default.CreateInstance;
	var secretProvider = container.Resolve<ISecretProvider>();
	ConfigurationItemFactory.Default.CreateInstance = type =>
	{
		if (type == typeof(SendGridTarget))
		{
			var sendGridSender = container.Resolve<ISendGridSender>();
			return new SendGridTarget(sendGridSender, secretProvider);
		}

		return defaultConstructor(type);
	};
	LogManager.Configuration = LogManager.Configuration.Reload();
	NLogBuilder.ConfigureNLog(LogManager.Configuration);
}

(Note that the ISecretProvider and ISendGridSender classes need to be registered with your dependency-injection framework.)

Today was a good day.

Garmin offline

Four days after I switched from Fitbit to Garmin, all of Garmin's online services have gone offline:

The problems with those services also mean that a range of features can't be used on Garmin's own devices: it is not possible to create new routes to go running or cycling, for instance, or to share those activities on services like Strava once they are completed.

The devices themselves continue to work as normal with the data they do have, however, meaning that any data collected during the outage will be safe.

Garmin wrote on its official Twitter pages that the problems were also affecting its call centres, leaving users unable to get in touch through calls or online messages.

"We are currently experiencing an outage that affects Garmin Connect, and as a result, the Garmin Connect website and mobile app are down at this time," it wrote.

"This outage also affects our call centres, and we are currently unable to receive any calls, emails or online chats. We are working to resolve this issue as quickly as possible and apologise for this inconvenience."

This is not what Garmin customers want to see:

The error message on the Garmin Connect website suggests the problem is with their Cloudflare equipment:

Update: Based on Garmin employee's social-media posts, ZDNet now reports that the company experienced a catastrophic ransomware attack, most likely a new strain of WastedLocker. Fortunately, my Venu can hold 200 hours of data. So as long as they get it back up within a week or so, I shouldn't lose anything—unless the ransomware attack already destroyed my data from this past week.

Making reservations for beer gardens

A friend and I plan to go to a local beer garden this weekend—one on the Brews and Choos list, in fact—so we had to make a reservation that included a $7.50-per-person deposit. Things are weird, man. And if you read the news today, oh boy, the weirdness is all over:

Finally, closer to home, 4,400 restaurants in Chicago have closed because of the pandemic, 2,400 permanently. The Chicago Tribune has a list of the more notable closures. 

Garmin v Fitbit: Full day comparison

I wore both my old Fitbit Ionic and new Garmin Venu for about 42 hours straight. Yesterday they overlapped for the entire day. And they came in with similar, but not quite the same, numbers.

I thought that my Fitbit would record fewer steps overall, because it recorded about 450 (about 7%) fewer on my walk yesterday. For the whole day, though, the Fitbit counted 14,190 to the Garmin's 13,250—7% more. But I wore the Fitbit on my right (dominant) wrist, so it may have just had more activity in general.

In other basic measures:

  • The Fitbit recorded 13.3 km to the Garmin's 10.6 km;
  • The Fitbit estimated my resting heart rate as 64 to the Garmin's 65;
  • Fitbit counted 82 "active" minutes to the Garmin's 359 "moderate" and 369 "vigorous";
  • Fitbit estimated my calorie burn at 3,100 to Garmin's 2,862.

I have no way to know which tracker was more accurate, but I might bet a dollar on the Garmin. I think the Garmin used actual distance to the Fitbit's estimate based on my usual stride length, which doesn't account for all the difference.

The Garmin's app presentation is so far beyond Fitbit's I wonder whether Fitbit even has software developers. Here's Fitbit:

Here's Garmin's:

And that's not even all of the Garmin data.

I walked halfway home after work today, and once again, the Garmin tracked my workout better than the Fitbit has done in months.

I'm glad I switched.

Some observations about my walk just now

Before I get to the technical bits comparing the Garmin Venu (now on my left wrist) to the Fitbit Ionic, let me just list some "learnings" today:

  • Both trackers are waterproof as advertised, as is my phone.
  • I am glad that I keep a towel by my back door.
  • I am glad that my washing machine—and, let's face it, my dryer—is by my back door.
  • There comes a point where one's clothes have absorbed so much water that it really doesn't matter how much more water they will encounter.

I have no one to blame but myself. This is the radar picture 10 minutes into the walk:

And 40 minutes in:

Result:

But enough about me. This post is really about fitness trackers.

In sum, the main difference between the Garmin and the Fitbit remains the Fitbit's total GPS failure, and the paucity of data Fitbit provides on its app compared with Garmin.

Here's the Fitbit data:

And the Garmin data:

I am pleased, however, that both trackers got almost exactly the same distance, given that the Garmin tracked distance using actual data and the Fitbit guessed based on my stride length. They don't agree on how many calories I burned, how many steps I took: the Garmin said 6,556, while the Fitbit said 6,109. So far today, my Garmin says 9,127 to Fitbit's 9,047, which adds data to my hypothesis that my Fitbit has always under-counted.

So, other than the rain, I thought this test went well.

Garmin v Fitbit: sleep metrics

Despite the Garmin Venu handing the Fitbit Ionic its ass in my first test of exercise tracing, the Fitbit didn't fail completely in sleep tracking.

Based on my self-perception of how well I slept, including my (and Parker's) acute awareness of the squall line that pushed through around 6:30, I think the Fitbit might have recorded my awake time more accurately. The Garmin, however, also recorded pulse oxygen, respiration, and can display movement on the UI.

Here's the Fitbit results:

And Garmin:

Also, yesterday my Fitbit counted 18,206 steps to the Garmin's 12,142—but I put the Garmin on my wrist at 1pm. Adding the 6,963 the Fitbit recorded before 1pm, that means the Garmin and Fitbit differed by (19,105 – 18,206 = ) 899, or 4.7% for the 11 hours between 1pm and midnight. Today I'm using both of them, though I switched wrists (yesterday the Garmin was on my dominant side; today it's not).

The thunderstorms from earlier have pushed on through the area, so once I digest my omelet and my Garmin fully charges, I'll do a longer test of exercise tracking.

Garmin Venu vs Fitbit Ionic short walk head-to-head

Yeah, the Garmin wins, hands down.

After realizing that my first head-to-head test pitted an Ionic whose GPS was failing against a treadmill exercise, I went out for a quick loop around the block with both trackers correctly set to "Walk."

The Garmin found a GPS signal in about 20 seconds. The Fitbit never did.

After the walk, the Garmin produced this delightful map, complete with weather report and options for different maps:

Right on the activity view, I've got a gear icon with these options:

Fitbit only exports TCX files. Or you can export your entire account archive, become a programmer like me, parse your archive, and extract the relevant item.

But the map and export options just scratch the surface. Look what the Fitbit Web app gives me for this walk (since it didn't have GPS):

And here's the Garmin:

I mean, that's not even fair. Garmin even gave me the weather report, fer gassake. (It did not give me the step count for the activity, though.

Yeah. Fitbit, you were great, but I've grown; you haven't. You fell so far behind Garmin that I don't know how you're going to catch up.

Tonight, I'll see how differently they track sleep. And I hope that I can re-import today's Fitbit steps, else I'll lose the 7,000 I had before I set up the Garmin. Also, Garmin only imports step counts, intensity times, and body mass from Fitbit, not sleep data, so I'll have to find a different solution for that.

More annals of eclectic musical interests

Back in May I started listening to every CD I own, in the order that I bought them, starting with Eugen Jochum conducting Mozart's Mass in C-Major, K317 (purchased in May 1988). I'm up to July 1989 now, and as I write this, I'm playing The Mama's and the Papa's [sic] If You Can Believe Your Eyes and Ears (1967). This follows The Beatles' With The Beatles (1963) and Paul McCartney's Pipes of Peace (1983).

And then it goes sideways.

Next up: Haydn's Piano Concerto #11 (1781), and Josquin's "Missa L'Homme Armé" (ca. 1500). I bought those five CDs on 7 July 1989.

Three days later I acquired a batch of six, including a collection of English madrigals sung by the King's Singers, Oscar Levant playing Gershwin, and the soundtrack from The Breakfast Club.

There are stretches of classical and stretches of modern throughout this list, but right now I'm in summer break after my first year of college when I was expanding both sides of my collection as fast as I could afford to.

I just did some math: at the rate I'm going, I'll be out of my university years around November 17th, in the 21st Century around the beginning of April, and through all of them in the fall of 2021. (That's a moving target for obvious reasons.)

It's a little trippy. I haven't heard some of these in a long, long time.