Airaw Widget API
The Airaw Widget API is a JavaScript bridge that allows HTML widgets displayed on CarPlay to retrieve real-time iOS system data — battery, network, music, weather, and more.
Airaw Widget API は、CarPlay 上に表示される HTML ウィジェットが iOS システム情報(バッテリー・ネットワーク・音楽・天気など)を リアルタイムで取得するための JavaScript ブリッジです。
Communication is bidirectional. The widget requests data via
WKScriptMessageHandler (airawAction) — or the injected helper airawRequest(method) —
which avoids touching window.location. The legacy arapi: URL scheme is still supported for compatibility.
Airaw responds by injecting global variables and calling mainUpdate(type).
通信は双方向です。データ要求は WKScriptMessageHandler(airawAction)、または注入済みの airawRequest(method) を推奨します(window.location を汚しません)。
互換のため従来の arapi: URL スキームも利用できます。
Airaw がグローバル変数を注入した上で mainUpdate(type) を呼び出します。
iOS 14 / 15 / 16 / 17. Only active when CarPlay is connected.
Designed for rootless jailbreak environments (Dopamine / palera1n).
iOS 14 / 15 / 16 / 17 対応(CarPlay 接続時のみ動作)。
Rootless jailbreak(Dopamine / palera1n)環境下での動作を想定しています。
Architectureアーキテクチャ
The data flow between the widget and Airaw is shown below.
Widget ↔ Airaw 間のデータフローを示します。
airawRequest('arcall_Battery') ──→mainUpdate('battery') ────Communication Model通信モデル
| Direction方向 | Mechanism手段 | Description説明 |
|---|---|---|
| Widget → Airaw | postMessage / airawRequest |
Recommended. window.webkit.messageHandlers.airawAction.postMessage({ action: 'requestData', method: 'arcall_Battery' }), or airawRequest('arcall_Battery') injected by Airaw (falls back to arapi: only when the handler is unavailable).
推奨。 window.webkit.messageHandlers.airawAction.postMessage({ action: 'requestData', method: 'arcall_Battery' })、または Airaw が注入する airawRequest('arcall_Battery')(ハンドラが無い場合のみ arapi: にフォールバック)。
|
| Widget → Airaw | arapi: URL scheme |
Legacy. Assign window.location = 'arapi:arcall_Battery'; Airaw intercepts navigation. Prefer postMessage to avoid history / SPA side-effects.
レガシー。 window.location = 'arapi:…' でナビゲーションをインターセプト。履歴や SPA への影響を避けるなら postMessage を推奨。
|
| Airaw → Widget | Direct JS evaluationJS 文字列の直接評価 |
Declares global variables with var xxx = ..., then calls mainUpdate(type).
グローバル変数を var xxx = ... で宣言後、mainUpdate(type) を呼び出します。
|
Quick Startクイックスタート
Minimal widget implementation example.
最小構成のウィジェット実装例です。
<!-- Request all data on page load -->
<script>
window.addEventListener('load', function() {
// Recommended: injected helper (uses postMessage internally)
airawRequest('arcall_all');
// Legacy: window.location = 'arapi:arcall_all';
});
// Airaw calls this after injecting variables
function mainUpdate(type) {
if (type === 'battery') {
document.getElementById('bat').textContent = batteryPercent + '%';
}
if (type === 'weather') {
document.getElementById('temp').textContent = weather.temperature + '°C';
}
}
</script>
mainUpdate関数名を変更しないことThe function name mainUpdate is hardcoded in Airaw. It cannot be renamed.
mainUpdate という関数名は Airaw が固定で呼び出します。この名前は変更できません。
Widget Options (airaw-widget-schema.json)ウィジェットオプション(airaw-widget-schema.json)
Place a schema file named airaw-widget-schema.json in the same folder as your widget HTML.
Airaw reads it to build a settings UI and injects the merged values into the WebView as window.AIRAW_WIDGET_OPTIONS.
User overrides are persisted under /var/jb/Library/Airaw/WidgetOptions/ on rootless setups (path may vary by jailbreak).
ウィジェットの HTML と同じフォルダに airaw-widget-schema.json を置きます。
Airaw がスキーマを読み取り設定画面を生成し、マージ結果を WebView に window.AIRAW_WIDGET_OPTIONS として注入します。
ユーザーが変更した値は rootless 環境では /var/jb/Library/Airaw/WidgetOptions/ 以下に保存されます(JB により実パスは異なる場合があります)。
Schema shapeスキーマの形
Top-level keys such as widgetId / version are optional metadata for authors.
Airaw uses the properties object to generate prefs cells and defaults.
ルートの widgetId や version は作者向けメタデータとして任意です。
設定 UI と既定値には properties オブジェクトが使われます。
| Field (per property)各プロパティのフィールド | Description説明 |
|---|---|
type |
string, boolean, number, or integer. Unknown types are ignored.
string / boolean / number / integer。それ以外は無視されます。
|
default |
Fallback when no user override exists. ユーザー未設定時に使われる既定値。 |
label |
Shown in Settings; falls back to the property key. 設定画面のラベル。省略時はプロパティ名。 |
minimum / maximum |
Clamp for number and integer types.
number / integer の値をこの範囲に収めます。
|
{
"widgetId": "com.example.mywidget",
"version": 1,
"properties": {
"accentColor": {
"type": "string",
"default": "#4fc3f7",
"label": "Accent color (#hex)"
},
"compactCards": {
"type": "boolean",
"default": false,
"label": "Compact cards"
}
}
}
Storage identifier (slug)保存キー(スラッグ)
Overrides are stored under /Library/Airaw/WidgetOptions/ (rootless prefix applied by the jailbreak).
The plist basename starts from the HTML path’s folder names:
xenhtml.<parent>.<folder> (segments lowercased). Example:
…/MyPack/widget/index.html → xenhtml.mypack.widget.plist.
If your schema defines a non-empty widgetId, Airaw appends a sanitized segment so different widgets under the same folder path do not collide:
xenhtml.mypack.widget.<sanitized-widgetId>.plist.
When you add widgetId later, an existing legacy plist at the base-only filename is moved to the new name on first access so user settings are preserved.
保存先は(rootless ではプレフィックス付きの)/Library/Airaw/WidgetOptions/ です。
ベース名は HTML のフォルダから xenhtml.<親>.<フォルダ>(小文字)。例:
…/MyPack/widget/index.html → xenhtml.mypack.widget.plist。
スキーマに空でない widgetId がある場合は衝突回避のため
xenhtml.mypack.widget.<sanitized-widgetId>.plist のように続けます。
後から widgetId を追加した場合、既存のレガシー plist は初回アクセス時に新ファイル名へ移動され、設定は引き継がれます。
JavaScript APIJavaScript からの利用
| Mechanism手段 | Description説明 |
|---|---|
window.AIRAW_WIDGET_OPTIONS |
Plain object of merged defaults + user overrides. Updated on load and when settings change. 既定値とユーザー値をマージしたオブジェクト。読み込み時および設定変更時に更新されます。 |
airawwidgetoptions |
window CustomEvent; event.detail is the options object (empty object when no keys).
window の CustomEvent。detail にオプションオブジェクト(キーが無い場合は空オブジェクト)。
|
window.onAirawWidgetOptions |
If defined as a function, Airaw calls it with the options object after each injection. 関数として定義されていれば、注入のたびにその関数がオプションオブジェクトで呼ばれます。 |
function applyOptions(o) {
if (!o) return;
document.documentElement.style.setProperty('--accent', o.accentColor || '#4fc3f7');
document.body.classList.toggle('compact', !!o.compactCards);
}
window.addEventListener('airawwidgetoptions', function(e) {
applyOptions(e.detail);
});
window.onAirawWidgetOptions = applyOptions;
// Initial paint if already injected before listeners ran
if (typeof window.AIRAW_WIDGET_OPTIONS !== 'undefined') {
applyOptions(window.AIRAW_WIDGET_OPTIONS);
}
Settings UI設定アプリでの操作
Open Airaw’s screen where you configure CarPlay widget placement for your HTML widget. When the schema file is present, a group titled HTML widget options lists switches, sliders, or text fields per property. Tap Apply to push changes to CarPlay / refresh the WebView.
Airaw のCarPlay 上のウィジェット配置を編集する設定画面で、その HTML ウィジェットを開きます。 スキーマがある場合、HTMLウィジェットのオプションというグループにスイッチ・スライダー・テキストなどが現れます。 変更を反映するには Apply をタップしてください。
See Samples/airaw-widget-schema.json and Samples/airaw-sample-widget.html for a complete working example
(CSS variables, compact layout, and option listeners).
実装例はリポジトリの Samples/airaw-widget-schema.json と Samples/airaw-sample-widget.html を参照してください(CSS 変数・コンパクト表示・オプション連動)。
Trigger APIsトリガー API
Call Airaw from JavaScript to refresh widget data. Prefer airawRequest(method) or postMessage so the webview does not navigate.
The arapi: URL form remains supported as a legacy fallback.
JavaScript から Airaw にデータ取得を依頼します。airawRequest(method) または postMessage を推奨します(WebView がナビゲーションしないため)。
arapi: 形式はレガシー互換として残ります。
Recommended: airawRequest('arcall_Battery') (injected at document start), or explicitly:
window.webkit.messageHandlers.airawAction.postMessage({ action: 'requestData', method: 'arcall_Battery' }).
The method string is always the same token that follows arapi: (e.g. arcall_all, action_launchApp_com.apple.Music).
推奨: ドキュメント開始時に注入される airawRequest('arcall_Battery')、または明示的に
window.webkit.messageHandlers.airawAction.postMessage({ action: 'requestData', method: 'arcall_Battery' })。
method は常に arapi: の後ろと同じ識別子です(例: arcall_all、action_launchApp_com.apple.Music)。
Legacy: window.location = 'arapi:' + method; — Airaw cancels navigation and runs the same pipeline.
レガシー: window.location = 'arapi:' + method; — Airaw がナビゲーションをキャンセルし、同じ処理へ渡します。
airawRequest('arcall_Battery');
// postMessage: window.webkit.messageHandlers.airawAction.postMessage({ action: 'requestData', method: 'arcall_Battery' });
// legacy: window.location = 'arapi:arcall_Battery';
Airaw collects battery info and calls mainUpdate('battery').
Airaw がバッテリー情報を収集し mainUpdate('battery') を実行します。
airawRequest('arcall_Statusbar');
// postMessage … method: 'arcall_Statusbar'
// legacy: window.location = 'arapi:arcall_Statusbar';
airawRequest('arcall_Music');
// postMessage … method: 'arcall_Music'
// legacy: window.location = 'arapi:arcall_Music';
Music data is fetched asynchronously via the MediaRemote framework, so it may arrive
later than other categories. Airaw also fires mainUpdate('music') automatically whenever
the track changes — no explicit request needed.
音楽情報は MediaRemote フレームワーク経由で非同期取得されます。そのため他のカテゴリより
遅延が生じる場合があります。また、楽曲が変わると Airaw が自動的に mainUpdate('music')
を呼び出します(要求不要)。
airawRequest('arcall_Weather');
// postMessage … method: 'arcall_Weather'
// legacy: window.location = 'arapi:arcall_Weather';
Data is sourced from the iOS Weather framework. The "Weather API" option must be enabled in Airaw settings.
iOS 標準の Weather フレームワークから取得します。 Airaw の設定で「Weather API」が有効になっている必要があります。
airawRequest('arcall_System');
// postMessage … method: 'arcall_System'
// legacy: window.location = 'arapi:arcall_System';
airawRequest('arcall_all');
// postMessage … method: 'arcall_all'
// legacy: window.location = 'arapi:arcall_all';
Requests Battery, Statusbar, Music, Weather, and System together.
Recommended for use in window.onload.
Battery / Statusbar / Music / Weather / System をまとめて要求します。
初期化時(window.onload)での使用を推奨します。
Callbacks — mainUpdate(type)コールバック — mainUpdate(type)
After collecting data, Airaw injects global variables and calls mainUpdate(type).
Use type to identify the category and read the corresponding variables.
Airaw はデータ収集後、グローバル変数を注入してから mainUpdate(type) を呼び出します。
type で対象カテゴリを判別し、各変数を読み取ります。
Injected variables注入される変数
| Variable変数名 | Type型 | Description説明 | Example例 |
|---|---|---|---|
| batteryPercent | number | Battery level (0–100)バッテリー残量(0〜100) | 87 |
| batteryCharging | number | Charging flag (1 = charging, 0 = not)充電中フラグ(1=充電中, 0=未充電) | 1 |
| batteryLowPowerMode | number | Low Power Mode (1 = ON)省電力モード(1=ON, 0=OFF) | 0 |
| batteryCycles | number | Charge cycle count充放電サイクル数 | 342 |
| batteryHealth | number | Battery health (0.00–100.00)バッテリー健全度(0.00〜100.00) | 94.32 |
| batteryVoltage | number | Voltage (mV)電圧(mV) | 4023 |
| batteryCelsius | number | Battery temperature (°C)バッテリー温度(℃) | 29.45 |
| bateryInfo | object | Connected device battery info (AirPods, etc.). Contains a devices array.接続デバイス(AirPods 等)のバッテリー情報。devices 配列を含む。 |
{ devices: [...] } |
bateryInfo (one "t" missing) is a typo in the source code,
preserved for backward compatibility.
bateryInfo("t" が1つ)はソースコード上のタイポですが、
互換性のためそのまま維持されています。
bateryInfo.devices[] structurebateryInfo.devices[] の構造
devices: [{
deviceName: string// "AirPods Pro"
batteryPercent: number// 0–100
machineName: string// display name
allmachineName: string// glyph asset name
deviceIdentifier: string// device identifier
deviceProductIdentifier: number// product identifier
}]
}
Example実装例
function mainUpdate(type) {
if (type === 'battery') {
const pct = typeof batteryPercent !== 'undefined' ? batteryPercent : 0;
document.getElementById('pct').textContent = pct + '%';
document.getElementById('icon').style.display = batteryCharging ? 'block' : 'none';
// Connected devices (AirPods, etc.)
if (bateryInfo?.devices?.length) {
bateryInfo.devices.forEach(d => console.log(d.deviceName, d.batteryPercent));
}
}
}
Injected variables注入される変数
| Variable変数名 | Type型 | Description説明 | Example例 |
|---|---|---|---|
| signalBars | string | Cellular signal strength ('0'–'4')セルラー電波強度('0'〜'4') | '3' |
| signalName | string | Carrier nameキャリア名 | 'Softbank' |
| wifiBars | string | Wi-Fi signal strength ('0'–'3')Wi-Fi 電波強度('0'〜'3') | '2' |
| wifiName | string | Connected SSID接続中の SSID | 'HomeNetwork_5G' |
| signalNetworkType | string | Data network typeデータ通信規格 | '5G' | 'LTE' | '4G' | '3G' | 'E' |
Airaw automatically fires this callback whenever the track or playback state changes.
You can also request it manually with arcall_Music.
楽曲変更・再生状態変化時に Airaw が自動で呼び出します。
arcall_Music で手動要求することも可能です。
Injected variables注入される変数
| Variable変数名 | Type型 | Description説明 | Example例 |
|---|---|---|---|
| title | string | Track title楽曲タイトル | 'Blinding Lights' |
| artist | string | Artist nameアーティスト名 | 'The Weeknd' |
| album | string | Album nameアルバム名 | 'After Hours' |
| currentDuration | string | Total track duration (seconds)楽曲の総再生時間(秒) | '200' |
| currentElapsedTime | string | Current playback position (seconds)現在の再生位置(秒) | '73' |
| PlayingApplication | string | Bundle ID of the playing app再生アプリのバンドル ID | 'com.apple.Music' |
| PlayingApplicationName | string | Localized display name of the playing app再生アプリの表示名 | 'ミュージック' |
| isplaying | boolean / number | Whether playback is active (1/0)再生中かどうか(1/0) |
1 |
| artworkUrl | string | iOS 16+ HTTP URL for album art (recommended for CarPlay widgets). See Artwork. iOS 16+ アルバムアートの HTTP URL(CarPlay ウィジェット推奨)。Artwork 参照。 | 'http://localhost:18765/AirawMedia/MusicArt.png?t=…' |
| playingAppIconUrl | string | iOS 16+ HTTP URL for the playing app’s icon (same HTTP daemon as artwork). iOS 16+ 再生アプリのアイコン HTTP URL(アートと同じ HTTP デーモン)。 | 'http://localhost:18765/AirawMedia/PlayingAppIcon.png?t=…' |
Example実装例
function mainUpdate(type) {
if (type === 'music') {
const playing = typeof isplaying !== 'undefined' ? !!isplaying : (title !== '');
const dur = parseFloat(currentDuration) || 0;
const elapsed = parseFloat(currentElapsedTime) || 0;
const pct = dur > 0 ? (elapsed / dur) * 100 : 0;
document.getElementById('title').textContent = playing ? title : 'Not Playing';
document.getElementById('artist').textContent = playing ? artist : '';
document.getElementById('bar').style.width = pct + '%';
// App name + icon (iOS 16+)
const appLabel = typeof PlayingApplicationName !== 'undefined' && PlayingApplicationName
? PlayingApplicationName
: (PlayingApplication || '');
const appNameEl = document.getElementById('appName');
if (appNameEl) appNameEl.textContent = appLabel;
const appIcon = document.getElementById('appIcon');
if (appIcon) {
if (typeof playingAppIconUrl !== 'undefined' && playingAppIconUrl) {
appIcon.src = playingAppIconUrl;
appIcon.style.display = 'block';
} else {
appIcon.style.display = 'none';
}
}
// iOS 16+: use artworkUrl (HTTP). Do not rely on Documents path.
const img = document.getElementById('artwork');
if (img) {
if (playing && typeof artworkUrl !== 'undefined' && artworkUrl) {
img.src = artworkUrl;
img.style.display = 'block';
} else {
img.style.display = 'none';
}
}
}
}
Weather data is provided as a single weather object,
unlike other categories which inject flat variables.
天気データは単一の weather オブジェクトとして提供されます。
他のカテゴリとは異なりフラットな変数ではありません。
weather object structureweather オブジェクト構造
city: string// "Tokyo"
countryISOCode: string// "JP"
temperature: number// current temp (unit follows
celsius)feelsLike: number// feels-like temperature
celsius: string// "C" or "F" (follows iOS setting)
condition: string// "Partly Cloudy"
conditionCode: number// see Condition Codes table
humidity: number// humidity (%)
windSpeed: number// wind speed (km/h)
windDirection: number// degrees
windChill: number// wind chill index
dewPoint: number// dew point
uvIndex: number// UV index (0–11+)
visibility: number// visibility (km)
pressure: number// pressure (hPa)
heatIndex: number// heat index
precipitationPast24Hours: number// 24h rainfall (mm)
chanceOfRain: number// precipitation probability (%)
moonPhase: number// moon phase (0.0–1.0)
high: number// today's high
low: number// today's low
sunrise: string// "6:24" (H:MM)
sunset: string// "18:45" (HH:MM)
updateTimeString: string// last update time
latlong: string// "35.6762,139.6503"
dayForecasts: DayForecast[]// up to 7 days
hourlyForecasts: HourlyForecast[]// hourly data
}
DayForecast[]
dayNumber: number// sequential day number
dayOfWeek: number// 0=Sun, 1=Mon … 6=Sat
icon: number// condition code
high: number// high temp
low: number// low temp
}
HourlyForecast[]
time: string// "14:00"
hourIndex: number// hour index
conditionCode: number// condition code
temperature: number// temperature
percentPrecipitation: number// precipitation probability (%)
detail: string// description text
}
Example実装例
function mainUpdate(type) {
if (type === 'weather') {
if (typeof weather === 'undefined') return;
const unit = weather.celsius === 'C' ? '°C' : '°F';
document.getElementById('temp').textContent = weather.temperature + unit;
document.getElementById('city').textContent = weather.city;
// 7-day forecast
const days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
weather.dayForecasts?.slice(0, 7).forEach(f => {
console.log(days[f.dayOfWeek], f.high + unit, f.low + unit);
});
}
}
Injected variables注入される変数
| Variable変数名 | Type型 | Description説明 | Example例 |
|---|---|---|---|
| deviceName | string | Device display name (Settings → General → About)デバイスの表示名(設定 → 一般 → 情報 と同じ) | 'iPhone' |
| deviceType | string | Platform / model identifier機種識別子(プラットフォーム名) | 'iPhone16,1' |
| systemVersion | string | iOS versioniOS バージョン | '17.4.1' |
| ipAddress | string | Wi-Fi IPv4 addressWi-Fi の IPv4 アドレス | '192.168.1.5' |
| ramFree | string | Free RAM (MB)空き RAM(MB) | '1243' |
| ramUsed | string | Used RAM (MB)使用中 RAM(MB) | '2853' |
| ramTotal | string | Total installed RAM (MB)搭載 RAM 合計(MB) | '6144' |
RAM values are provided as string type. Use parseFloat() before arithmetic operations.
RAM 系の値は文字列型(string)で提供されます。数値演算前に parseFloat() で変換してください。
Called automatically when the user navigates between CarPlay dashboard pages. Useful for controlling widget animations per page.
ユーザーが CarPlay のダッシュボードページを切り替えた際に自動で呼び出されます。 ウィジェットのアニメーション切り替えなどに利用できます。
Injected variables注入される変数
| Variable変数名 | Type型 | Description説明 | Example例 |
|---|---|---|---|
| pageIndex | number | Current page index (0-based). -1 = widget not visible. 現在表示中のページインデックス(0 始まり)。-1 = 非表示状態。 | 0 |
Example実装例
function mainUpdate(type) {
if (type === 'CarPlay') {
// Animate only when on page 0
document.body.style.opacity = (pageIndex === 0) ? '1' : '0.4';
}
}
Condition Codes天気コード(conditionCode)一覧
Numeric codes used by weather.conditionCode,
dayForecasts[].icon, and hourlyForecasts[].conditionCode.
Follows the Apple Weather framework (formerly WeatherData) definition.
weather.conditionCode / dayForecasts[].icon /
hourlyForecasts[].conditionCode で使用される数値コードです。
Apple Weather フレームワーク(旧 WeatherData)の定義に従います。
Emoji mapping function絵文字マッピング関数の実装例
function weatherEmoji(code) {
const c = parseInt(code, 10);
if (c <= 1) return '🌪';
if (c <= 3) return '⛈';
if (c <= 12) return '🌧';
if (c <= 17) return '⛈';
if (c === 19) return '🌫';
if (c <= 22) return '💨';
if (c <= 30) return '🌨';
if (c === 31 || c === 33) return '🌙';
if (c === 32 || c === 34) return '☀️';
if (c === 36) return '🌡';
if (c <= 40) return '🌦';
if (c <= 43) return '🌩';
return '⛅';
}
Artwork Imageアートワーク画像の取得
iOS 16+ (recommended) — artworkUrliOS 16+(推奨)— artworkUrl
On iOS 16+, CarPlay widgets often run inside CarPlayWallpaper, which cannot read
/var/mobile/Documents/ (sandbox). Airaw therefore serves artwork over a local HTTP server
on SpringBoard and injects an HTTP URL as artworkUrl when calling
mainUpdate('music').
iOS 16 以降、CarPlay ウィジェットは多くの場合 CarPlayWallpaper 上で動作し、
サンドボックスのため /var/mobile/Documents/ を直接読めません。
Airaw は SpringBoard 側のローカル HTTP でアートを配信し、
mainUpdate('music') 時に変数 artworkUrl として注入します。
// Injected example
artworkUrl = 'http://localhost:18765/AirawMedia/MusicArt.png?t=1710000000000';
| Item項目 | Value値 |
|---|---|
| Variable | artworkUrl |
| URL form | http://localhost:18765/AirawMedia/MusicArt.png?t=<version> |
| File on device | /var/mobile/Library/Airaw/Media/MusicArt.png (rootless-aware) |
| App icon file | /var/mobile/Library/Airaw/Media/PlayingAppIcon.png → playingAppIconUrl |
t= query |
Changes only when artwork bytes change (cache-friendly). アートのバイト列が変わったときだけ更新(キャッシュしやすい)。 |
// Recommended (iOS 16+ CarPlay widget)
function mainUpdate(type) {
if (type !== 'music') return;
const img = document.getElementById('artwork');
if (!img) return;
if (typeof artworkUrl !== 'undefined' && artworkUrl) {
img.onerror = function() { this.style.display = 'none'; };
img.onload = function() { this.style.display = 'block'; };
img.src = artworkUrl; // already includes ?t=
} else {
img.style.display = 'none';
}
}
Requires Airaw’s SpringBoard HTTP daemon (same localhost server used for SBHTML widgets).
If artworkUrl is empty, artwork data was unavailable for the current track.
SpringBoard 上の Airaw HTTP デーモンが必要です(SBHTML 配信と同じ localhost)。
artworkUrl が空の場合は、その曲のアートデータが取得できていません。
Legacy path (not for iOS 16+ CarPlayWallpaper)レガシー経路(iOS 16+ CarPlayWallpaper では非推奨)
Airaw still writes a copy to the Documents path for older environments / Prefs preview. Do not rely on this path inside iOS 16+ CarPlayWallpaper widgets — the file is not readable there.
互換のため Documents にもコピーしますが、 iOS 16+ の CarPlayWallpaper ウィジェットではこのパスに依存しないでください(読めません)。
/var/mobile/Documents/MusicArt.png
// Legacy only (Prefs preview / older hosts that can read Documents)
const img = document.getElementById('artwork');
img.src = '/var/mobile/Documents/MusicArt.png?t=' + Date.now();
img.onerror = function() { this.style.display = 'none'; };
On rootless jailbreaks (Dopamine, etc.), /var/mobile/Documents/
may map to /var/jb/var/mobile/Documents/.
Prefer artworkUrl so you do not need to resolve jbroot yourself.
Rootless 環境(Dopamine 等)では /var/mobile/Documents/ のパスが
/var/jb/var/mobile/Documents/ にマップされる場合があります。
artworkUrl を使えば jbroot を意識する必要はありません。
Best Practices実装のヒント
1. Safe variable access1. 安全な変数アクセス
Airaw always injects variables before calling mainUpdate, but use
typeof checks for safety when opening the HTML file directly during development.
Airaw は変数を注入してから mainUpdate を呼びますが、開発・デバッグ時に
直接 HTML を開く場合に備え、typeof チェックを推奨します。
// Recommended pattern
const pct = typeof batteryPercent !== 'undefined' ? batteryPercent : 0;
// Nullish coalescing (modern browsers)
const temp = weather?.temperature ?? '--';
2. Periodic polling2. 定期更新の実装
Airaw notifies automatically on changes, but you can also poll at a fixed interval.
Airaw は変化があった際に自動で通知しますが、一定間隔でポーリングすることもできます。
// Refresh all data every 30 seconds
setInterval(function() {
if (typeof airawRequest === 'function') {
airawRequest('arcall_all');
} else {
window.location = 'arapi:arcall_all';
}
}, 30000);
3. mainUpdate template3. mainUpdate のテンプレート
function mainUpdate(type) {
switch (type) {
case 'battery':
/* batteryPercent, batteryCharging, batteryLowPowerMode,
batteryCycles, batteryHealth, batteryVoltage,
batteryCelsius, bateryInfo */
break;
case 'statusbar':
/* signalBars, signalName, wifiBars, wifiName,
signalNetworkType */
break;
case 'music':
/* title, artist, album, currentDuration,
currentElapsedTime, PlayingApplication, PlayingApplicationName,
isplaying, artworkUrl, playingAppIconUrl (iOS 16+ HTTP) */
break;
case 'weather':
/* weather.{ temperature, city, condition, humidity,
windSpeed, uvIndex, high, low, celsius,
dayForecasts[], hourlyForecasts[] ... } */
break;
case 'system':
/* deviceName, deviceType, systemVersion, ipAddress,
ramFree, ramUsed, ramTotal */
break;
case 'CarPlay':
/* pageIndex */
break;
}
}
window.addEventListener('load', function() {
if (typeof airawRequest === 'function') {
airawRequest('arcall_all');
} else {
window.location = 'arapi:arcall_all';
}
});
4. Demo data for development4. デバッグ用デモデータ
To develop the UI without a real device, refer to the fillRandom() function
in airaw-sample-widget.html. It injects demo values into all fields without
making any API calls.
実デバイスなしで UI 開発を進める場合は、airaw-sample-widget.html の
fillRandom() 関数を参考にしてください。
API 呼び出しを一切せずにすべてのフィールドにデモ値を注入できます。
5. Widget options alongside API data5. API データと並行するウィジェットオプション
User-configurable widget options (schema file + window.AIRAW_WIDGET_OPTIONS) are independent of
data triggers (requestData / airawRequest / legacy arapi:) and mainUpdate. You can read options inside mainUpdate or react via
airawwidgetoptions — see the Widget Options section.
スキーマに基づくユーザー設定(window.AIRAW_WIDGET_OPTIONS)は、データ取得(requestData / airawRequest / レガシー arapi:)や mainUpdate とは別経路です。
mainUpdate 内で参照してもよいし、airawwidgetoptions で UI を更新しても構いません。詳細はウィジェットオプションを参照してください。