【Garmin】Connect IQ SDKでウォッチフェイスを作るⅠ ～適当なデジタル時計を作るための基礎編～

# 公式ドキュメント

https://developer.garmin.com/connect-iq/overview/

全部英語です。
以降この記事で貼る公式ドキュメントの記事は、どれも遡っていくと最終的には上記リンクに辿り着くはずです。

まずはMonkeyCの構文を見たいという人は下記をぱらぱら眺めるのがいいと思います。

https://developer.garmin.com/connect-iq/reference-guides/monkey-c-reference/

forどう書くのとかswitchあるんですかとか、基本的なステートメントは全てここにまとまってます(たぶん)。

上記の通りあれこれWebで公開されていますが、SDKをインストールするとローカルにも一通り配置されるっぽいです。

Windows環境だとここです。
> C:\Users\xxxx\AppData\Roaming\Garmin\ConnectIQ\Sdks\connectiq-sdk-win-xxxxxxxxxxx\

# 公式サンプルコード

ドキュメント同様、SDKのインストール先に一通り配置されるっぽいです。前述のディレクトリを覗くと「samples」配下になんかいっぱいあります。

![2025-04-02_18h43_17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164419/f0154777-4513-4be3-bbb6-e6aa53285ff9.png)



# 環境構築

ここ見て上手くいったので詳細は割愛します。

https://note.com/run_bike_other/n/n8f83fd0b24d0#f91e21d1-c02f-44fd-93bd-1385a6966329

公式ドキュメントで確認するならここ。

https://developer.garmin.com/connect-iq/connect-iq-basics/getting-started/#gettingstarted

# 自作したウォッチフェイスを実機で使う

ここ見て上手くいったので詳細は割愛します。

https://note.com/run_bike_other/n/n9569cd2d8806?magazine_key=mab35ddd423f5

上記が閲覧できなくなったときのためにざっと要所だけ書いときます。
- コマンドパレットから`Monkey C: Build for Device`でビルドする
- デバイスをPCに接続するとストレージとしてマウントできるので、ビルドして生成された.prgファイルを配置する
    - デバイス名\Internal Storage\GARMIN\Apps配下
- PCとの接続を切ると勝手にインストールしてくれる

# Tips

あれこれを片っ端から列挙していきます。

## コマンドパレットから新規プロジェクトを作成後、とりあえず編集することになるであろうファイル

### resource > layouts > layout.xml

WPF経験者は「XAML」と言えばすぐにピンとくるかなと思います。
XMLで書かれており、座標やフォント、色などビジュアル的なパラメータを指定します。表示内容そのものは後述のhogeView.mcで制御します。

### source > hogeView.mc

こちらがロジック側で、layout.xmlのノードをidで指定して操作します。

## 月表示

:::note warn
`import Toybox.Time.Gregorian;`が必要。
:::


`Gregorian.info()`で年月日情報を取得できる。渡す値によって取得できる年月日情報の形式が異なります。

``` C#:View.mc

function onUpdate(dc as Dc) as Void {
	var infoShort = Gregorian.info(Time.now(), Time.FORMAT_SHORT);
	var infoMedium = Gregorian.info(Time.now(), Time.FORMAT_MEDIUM );

	var view = View.findDrawableById("MonthLabel") as Text;
	view.setText(infoMedium.month + "   " + infoShort.month.format("%02d"));

	View.onUpdate(dc);
}

```

``` XML:layout.xml

<layout id="WatchFace">
    <label id="MonthLabel" x="center" y="center" font="Graphics.FONT_LARGE" justification="Graphics.TEXT_JUSTIFY_CENTER" color="Graphics.COLOR_BLUE" />  
</layout>

```

![2025-03-24_00h29_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164419/e09c8f8b-14de-4e76-a9fc-27f370d2ddc6.png)

`Time.FORMAT_MEDIUM`だとStringで取得するっぽいので、`format("%02d")`を試みるとエラーになります。

![2025-03-24_00h36_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164419/e8b493d3-2598-4cd5-9b3b-bff627bee15d.png)

数字で受け取りたい場合は`FORMAT_SHORT`で取得する必要があります。


細かいことは公式ドキュメントのここに書いてある(たぶん)。

https://developer.garmin.com/connect-iq/api-docs/Toybox/Time/Gregorian/Info.html

## 曜日表示

こいつもGregorian.info()で取得できて、`Time.FORMAT_MEDIUM`を渡すと`Sun`とか`Mon`とかで返してくれる。

``` C#:View.mc

function onUpdate(dc as Dc) as Void {
	var infoMedium = Gregorian.info(Time.now(), Time.FORMAT_MEDIUM );
    (View.findDrawableById("DayOfWeekLabel") as Text).setText(infoMedium.day_of_week);

	View.onUpdate(dc);
}

```

![2025-03-27_23h01_25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164419/b0d80725-ac8b-4e37-ba92-99ded5bf9a0d.png)

`Time.FORMAT_SHORT`だと日曜始まりで1～7を取得するので、`Mon`じゃなくて`Mo`にしたいとか思ったらこんな書き方ができます。

``` C#:View.mc

function onUpdate(dc as Dc) as Void {
	var infoShort = Gregorian.info(Time.now(), Time.FORMAT_SHORT);
    var dayLabels = ["", "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
    var dayOfWeekString = dayLabels[infoShort.day_of_week];
    (View.findDrawableById("DayOfWeekLabel") as Text).setText(dayOfWeekString);

	View.onUpdate(dc);
}

```

……なんで0始まりにしてくれないんですかね。

https://developer.garmin.com/connect-iq/api-docs/Toybox/Time/Gregorian.html

土曜始まりや月曜始まりにも設定できるっぽい。

![2025-03-29_15h47_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164419/c75ccfa1-6b03-4e26-a851-6b1361d93270.png)

### 日本語での曜日表示

言語設定を日本語にしたら`火`とか`水`が表示されるのかなと思って試してみたら、豆腐になっちゃいました。デフォで使えるフォントは後述の通りデバイスによって変わるので、場合によってはカスタムフォントが必要だったりするかも。

シミュレータ上でしか試してないので実機でどうなるかは不明。

## フォント

layout.xml内の`font="Graphics.FONT_LARGE"`を書き換える。
具体的な値については公式のデバイスリファレンスに一覧があります。

![2025-03-24_02h52_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164419/2756677e-8c13-480f-8c93-e88b228366de.png)

https://developer.garmin.com/connect-iq/reference-guides/devices-reference/#devicereference

XMLに設定する値は「Font Symbol」で、一覧上で対応する書体とサイズでデバイスに表示されます。
一覧はデバイスによって、というか実質的にはたぶん液晶のスペックによって個別に作られており、同じFont Symbolであっても表示される書体やフォントサイズが常に同じとは限らないです。開発者が「こんなサイズ感で」と作ったものを、勝手に他のデバイスでもいい感じで表示できる仕組みになってるっぽい。

![2025-03-24_03h05_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164419/35e38417-ef49-48f2-9a2e-3ec7d8a3b447.png)

![2025-03-24_03h04_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164419/9cffdb08-4348-47e9-a8ec-cc7a6680a166.png)

### 手持ちのデバイスだけ見たい
上記リンク、全デバイスなのかやたらとデカいし見にくい。
シリーズ別でわけられたページも実は個別に設けられてまして、たとえば下記にはFenixシリーズと他の似たようなデバイスだけでまとまってます。

https://developer.garmin.com/connect-iq/user-experience-guidelines/fenix-2022/

フォントサンプルも一緒に載っていて嬉しい。

## フォントカラー

layout.xml内の`font="Graphics.COLOR_BLUE"`を書き換えます。

表示可能な色は液晶のスペック次第なのでデバイスによって違います。デバイスリファレンスに記載があったり無かったりする。

![2025-03-24_03h31_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164419/7d3215b4-b5e2-4c11-8e9c-7bc589713173.png)

https://developer.garmin.com/connect-iq/reference-guides/devices-reference/#devicereference

網羅は出来ていないけど何色か試してみました。

![2025-03-24_03h26_29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164419/ec163510-9f9d-4d92-be2b-1f2978ef53a9.png)

![2025-03-24_03h26_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164419/8bb2cfda-320b-4c3f-9712-f527cb509830.png)

定数で指定できる色はここに一覧があります。

https://developer.garmin.com/connect-iq/api-docs/Toybox/Graphics.html

## バッテリー情報
`System.getSystemStats()`で取得できるStatsが持ってます。

``` C#:View.mc

function onUpdate(dc as Dc) as Void {
	var stats = System.getSystemStats();

    // 残量(%)
    var batteryString = Lang.format("$1$%", [stats.battery.format("%2d")]);
    
    // 残量(日数)
    var batteryInDaysString = Lang.format("$1$days", [stats.battery.format("%2d")]);

    // 充電状況 ※満充電状態でも充電器が接続されていればTrue
    var batteryIsCharging = stats.charging ? "charging" : "not charging";

    // ソーラー強度
    var batterySolarIntensity = Lang.format("$1$%", [stats.solarIntensity.format("%2d")]);

    // --------------------------------------------
        
    (View.findDrawableById("BatteryLabel") as Text).setText(
        batteryString + " / " + batteryInDaysString + " / " + batteryIsCharging + " / " + batterySolarIntensity 
    );

	View.onUpdate(dc);
}

```

![2025-03-28_02h30_28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164419/646b5ff7-3b7e-48e5-acd1-d8fd454a7db5.png)

シミュレータ上でのバッテリー情報はSettingsのSet Battery Satusから設定できます。

![2025-03-28_02h36_36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164419/b738c059-225a-4235-994f-89e0aa2cfae2.png)

https://developer.garmin.com/connect-iq/api-docs/Toybox/System/Stats.html

## 通知

`System.getDeviceSettings()`で取得できるDeviceSettingsが持ってます。

``` C#:View.mc

function onUpdate(dc as Dc) as Void {
    (View.findDrawableById("NotificationCountLabel") as Text).setText(
        Lang.format("$1$",[System.getDeviceSettings().notificationCount])
    );

	View.onUpdate(dc);
}

```

シミュレータ上での通知数は、SettingsのSet Phone Notificationsから設定できます。

![2025-03-29_16h42_34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164419/4a28235b-f8b7-4d1e-9cdb-64cd9898fcd3.png)

![2025-03-29_16h44_17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164419/f6bc0265-a0b9-44ff-b76b-e4fe1b58fdcd.png)



# おまけその1

環境構築はVSCodeの使用が前提になっていますが、この記事はHaystackEditorで開発しながら書いてます。

https://haystackeditor.com/

VSCodeからのフォークらしいので拡張機能に互換性があるようで、今のところMonkeyC拡張機能も問題無く動いています。

# おまけその2

使わずに済むならvarを使いたくない派ですが、どうもvarでしか宣言できないっぽいです。

小数型であることを明示したいときは`1`ではなく`1.0`を代入しましょうとかそういう感じらしい。