电池状态 API

W3C 工作草案

关于此文档的更多细节
此版本
https://w3org.cn/TR/2024/WD-battery-status-20241024/
最新发布版本
https://w3org.cn/TR/battery-status/
最新编辑草案
https://w3c.github.io/battery/
历史
https://w3org.cn/standards/history/battery-status/
提交历史
测试套件
https://wpt.live/battery-status/
实现报告
https://wpt.fyi/results/battery-status
编辑
Anssi Kostiainen (英特尔公司)
前任编辑
Raphael Kubo da Costa (英特尔公司)
Mounir Lamouri (Google 公司)(此前在 Mozilla)
反馈
GitHub w3c/batterypull request新建 issue已打开的 issue
public-device-apis@w3.org(主题行 [battery-status] … message topic …),邮件归档

摘要

本规范定义了一个 API,用于提供宿主设备的电池状态信息。

本文档状态

本节描述本文档在发布时的状态。当前的 W3C 出版物列表以及本技术报告的最新修订版可在 https://w3org.cn/TR/ 的 W3C 技术报告索引 中找到。

设备与传感器工作组将在本规范上进行编辑现代化工作,对 API 的安全性与隐私方面进行一次自审与修订后,再请求横向审查。现有的安全与隐私问题已列出。

本文件由设备与传感器工作组作为工作草案发布,采用推荐轨道

发布为工作草案并不意味着 W3C 及其成员的认可。

这是一份草案文档,可能会随时被其他文档更新、替换或废弃。将其作为非进行中工作引用是不恰当的。

本文件由遵循W3C 专利政策的组织编写。W3C维护一份针对本工作组交付物的专利披露公开列表;该页面还包含披露专利的说明。任何实际了解某专利且认为该专利包含必要权利要求的个人,必须依据《W3C 专利政策》第 6 节进行披露。

本文档受 2023 年 11 月 3 日 W3C 流程文档 管辖。

1. 简介

本节是非规范性的。

Battery Status API 规范定义了一种方式,使 Web 开发者能够以编程方式获取宿主设备的电池状态。如果不了解设备的电池状态,开发者只能假设设备有足够的电量来完成当前任务,这会导致设备的电池耗尽速度快于预期,因为开发者无法基于电池状态作出决策。若能获取电池信息,开发者就可以编写更节能的 Web 内容与应用,从而提升用户体验。但需要注意,若该 API 被天真地实现,可能会对电池寿命产生负面影响。

当设备未充电或电量不足时,Battery Status API 可用于推迟或缩减工作量。比如,一个高级 Web 邮件客户端在设备充电时可以每隔几秒向服务器检查新邮件,而在设备未充电或电量低时则降低检查频率。再如,基于 Web 的文字处理器可以监控电池电量,在电池耗尽前自动保存更改,以防止数据丢失。

2. 一致性

除了标记为非规范性的章节外,本规范中的所有创作指南、图表、示例和注释均为非规范性内容。本规范中的其他所有内容均为规范性内容。

本文档中的关键字 MAY(可以)、MUST(必须)和 SHOULD(应该)应仅在全大写时,按照 BCP 14 [RFC2119] [RFC8174] 中的描述进行解释。

本规范定义了适用于单一产品的一致性标准:实现其所包含接口的 用户代理 (user agent)

3. 安全性与隐私注意事项

本规范中定义的 API 用于判断宿主设备的电池状态。

用户代理SHOULD不应暴露电池状态的高精度读数,因为这可能成为新的指纹识别向量。

用户代理MAY请求用户授权访问电池状态信息,或在其隐私浏览模式下强制执行用户权限要求。

用户代理SHOULD以不显眼的方式告知用户脚本正在使用该 API,以提升透明度并允许用户撤销该 API 的访问权限。

用户代理MAY对暴露的值进行混淆,使作者无法直接判断宿主设备是否无电池、是否在充电或是否提供了伪造值。

4. 概念

本规范中提及的任务来源任务电池状态任务来源

5. Navigator 接口的扩展

WebIDL[SecureContext]
partial interface Navigator {
  Promise<BatteryManager> getBattery();
};

此方法在PR #51之前可在不安全的上下文中使用。

5.1 内部槽位

内部槽位 初始值 描述
[[BatteryPromise]] null Promise 的返回值,来源于对 getBattery() 的调用。
[[BatteryManager]] null 在通过 getBattery() 创建后,关联到给定NavigatorBatteryManager 实例。

5.2 getBattery() 方法

getBattery() 方法的步骤如下:

  1. 如果 this.[[BatteryPromise]]null,则在 this相关域中创建一个 新 Promise 并赋给它。
  2. 如果 this相关全局对象关联 Document 不允许使用 “battery策略控制特性,则以 NotAllowedErrorDOMException 拒绝 this.[[BatteryPromise]]
  3. 否则
    1. 如果 this.[[BatteryManager]]null,则在 this相关域中创建一个新的 BatteryManager 实例并赋给它。
    2. Resolve this.[[BatteryPromise]]this.[[BatteryManager]]
  4. 返回 this.[[BatteryPromise]]

6. BatteryManager 接口

WebIDL[SecureContext, Exposed=Window]
interface BatteryManager : EventTarget {
    readonly        attribute boolean             charging;
    readonly        attribute unrestricted double chargingTime;
    readonly        attribute unrestricted double dischargingTime;
    readonly        attribute double              level;
                    attribute EventHandler        onchargingchange;
                    attribute EventHandler        onchargingtimechange;
                    attribute EventHandler        ondischargingtimechange;
                    attribute EventHandler        onlevelchange;
};

BatteryManager 接口表示宿主设备的当前电池状态信息

如果用户代理 无法报告电池状态信息,则它无法为任意属性报告数值,例如因用户或系统偏好、设置或限制而导致的情况。

6.1 内部槽位

BatteryManager 实例在创建时拥有以下内部槽位

内部槽位 初始值
[[Charging]] true
[[ChargingTime]] 0
[[DischargingTime]] 正无穷
[[Level]] 1.0

6.1.1 [[Charging]] 内部槽位

[[Charging]] 内部槽位表示系统电池的充电状态。若电池在放电则**MUST**设为 false,若电池在充电、实现无法报告状态、或系统未连接电池等情况下则**MUST**设为 true

当系统电池的充电状态发生变化时,用户代理必须以 truefalse(取决于电池是充电还是放电)以及 “chargingchange” 为参数,运行更新电池状态并通知算法。

6.1.2 [[ChargingTime]] 内部槽位

[[ChargingTime]] 内部槽位表示系统电池完全充满所剩余的秒数。若电池已满或系统未连接电池,则**MUST**设为 0;若电池在放电、实现无法报告剩余充电时间或其它情况,则**MUST**设为正无穷。

当电池的充电时间更新时,用户代理必须以新的充电时间(秒)以及 “chargingtimechange” 为参数,运行更新电池状态并通知算法。

6.1.3 [[DischargingTime]] 内部槽位

[[DischargingTime]] 属性表示系统电池完全放电并导致系统即将挂起的剩余秒数。若电池在充电、实现无法报告剩余放电时间、系统未连接电池或其它情况,则**MUST**设为正无穷。

当电池的放电时间更新时,用户代理必须以新的放电时间(秒)以及 “dischargingtimechange” 为参数,运行更新电池状态并通知算法。

6.1.4 [[Level]] 内部槽位

[[Level]] 内部槽位表示系统电池的电量水平。若系统电池电量耗尽且系统将被挂起,则**MUST**设为 0;若电池已满、实现无法报告电量或系统未连接电池,则**MUST**设为 1.0。

当电池电量水平更新时,用户代理必须以新的电量水平以及 “levelchange” 为参数,运行更新电池状态并通知算法。

关于何时触发 “chargingtimechange”、 “dischargingtimechange” 与 “levelchange” 事件的触发频率留给实现自行决定。

6.2 charging 属性

charging 的 getter 步骤为返回 this.[[Charging]]

6.3 chargingTime 属性

chargingTime 的 getter 步骤为返回 this.[[ChargingTime]]

6.4 dischargingTime 属性

dischargingTime 的 getter 步骤为返回 this.[[DischargingTime]]

6.5 level 属性

level 的 getter 步骤为返回 this.[[Level]]

6.6 事件处理程序

以下是事件处理程序(及对应的事件类型),MUST 作为属性由BatteryManager 对象支持:

事件处理器 事件处理器事件类型
onchargingchange chargingchange
onchargingtimechange chargingtimechange
ondischargingtimechange dischargingtimechange
onlevelchange levelchange

6.7 算法

更新电池状态并通知(给定内部槽位 slot、值 value 与事件名 eventName),执行以下步骤:

  1. global当前全局对象
  2. 如果 global 不是Window,则终止本步骤。
  3. navigatorglobal 的关联Navigator
  4. batteryManagernavigator[[BatteryManager]] 的值。
  5. 如果 batteryManagernull,则终止本步骤。
  6. 全局任务队列电池状态任务来源上,以 global 为上下文,执行以下步骤:
    1. batteryManager.slot 设为 value
    2. batteryManager触发一个事件,其名称为 eventName

6.8 多电池情况

如果宿主设备包含多个电池,BatteryManager SHOULD 提供统一的电池视图。

内部槽位 [[Charging]] MUST 为 true,只要至少有一个电池的充电状态为 true;否则 MUST 为 false。

内部槽位 [[ChargingTime]] 可设为各电池并行充电时的最大充电时间,或在串行充电时设为各电池充电时间之和。

内部槽位 [[DischargingTime]] 可设为各电池并行放电时的最大放电时间,或在串行放电时设为各电池放电时间之和。

内部槽位 [[Level]] 可设为同等容量电池的平均电量,或在容量不同的电池之间使用加权平均。

7. 权限策略集成

Battery Status API 是一个策略控制特性,其标识字符串为 “battery”。其默认允许列表'self'

8. 示例

本节是非规范性的。

以下示例在每次电量变化时把电池电量写入控制台:

// We get the initial value when the promise resolves ...
navigator.getBattery().then(function(battery) {
  console.log(battery.level);
  // ... and any subsequent updates.
  battery.onlevelchange = function() {
    console.log(this.level);
  };
});

同样的效果也可以使用 addEventListener() 方法实现:

navigator.getBattery().then(function(battery) {
  console.log(battery.level);
  battery.addEventListener('levelchange', function() {
    console.log(this.level);
  });
});

下面的示例在指示灯上显示充电状态、电量水平以及剩余时间(单位:分钟):

<!DOCTYPE html>
<html>
<head>
  <title>Battery Status API Example</title>
  <script>
    window.onload = function () {
      function updateBatteryStatus(battery) {
        document.querySelector('#charging').textContent = battery.charging ? 'charging' : 'not charging';
        document.querySelector('#level').textContent = battery.level;
        document.querySelector('#dischargingTime').textContent = battery.dischargingTime / 60;
      }

      navigator.getBattery().then(function(battery) {
        // Update the battery status initially when the promise resolves ...
        updateBatteryStatus(battery);

        // .. and for any subsequent updates.
        battery.onchargingchange = function () {
          updateBatteryStatus(battery);
        };

        battery.onlevelchange = function () {
          updateBatteryStatus(battery);
        };

        battery.ondischargingtimechange = function () {
          updateBatteryStatus(battery);
        };
      });
    };
  </script>
</head>
<body>
  <div id="charging">(charging state unknown)</div>
  <div id="level">(battery level unknown)</div>
  <div id="dischargingTime">(discharging time unknown)</div>
</body>
</html>

A. 索引

A.1 本规范定义的术语

A.2 通过引用定义的术语

B. IDL 索引

WebIDL[SecureContext]
partial interface Navigator {
  Promise<BatteryManager> getBattery();
};

[SecureContext, Exposed=Window]
interface BatteryManager : EventTarget {
    readonly        attribute boolean             charging;
    readonly        attribute unrestricted double chargingTime;
    readonly        attribute unrestricted double dischargingTime;
    readonly        attribute double              level;
                    attribute EventHandler        onchargingchange;
                    attribute EventHandler        onchargingtimechange;
                    attribute EventHandler        ondischargingtimechange;
                    attribute EventHandler        onlevelchange;
};

C. 致谢

工作组深深感谢 Mounir Lamouri、Jonas Sicking 以及整个 Mozilla WebAPI 团队对原型实现提供的宝贵反馈。也感谢 System Information API 与 Device Orientation Event 规范的作者提供的初始灵感。以及为我们带来 Page Visibility 规范的同事们,使本规范的引言章节能够讨论同样适用于本规范的真实高价值使用案例。特别感谢 Device APIs Working Group 的所有参与者以及其他提供了大量反馈和评论的朋友们,让 Web 变得更加美好。最后,感谢 Lukasz Olejnik、Gunes Acar、Claude Castelluccia 与 Claudia Diaz 对 API 的隐私分析。

D. 参考文献

D.1 规范性参考文献

[dom]
DOM Standard. Anne van Kesteren. WHATWG. Living Standard. URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript 语言规范. Ecma International. URL: https://tc39.es/ecma262/multipage/
[html]
HTML 标准. Anne van Kesteren; Domenic Denicola; Dominic Farolino; Ian Hickson; Philip Jägenstedt; Simon Pieters. WHATWG. 活标准. URL: https://html.whatwg.cn/multipage/
[permissions-policy]
Permissions Policy. Ian Clelland. W3C. 25 September 2024. W3C Working Draft. URL: https://w3org.cn/TR/permissions-policy-1/
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. 1997年3月. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc2119
[RFC8174]
RFC 2119 关键词中大小写的歧义. B. Leiba. IETF. 2017年5月. 最佳当前实践. URL: https://www.rfc-editor.org/rfc/rfc8174
[WEBIDL]
Web IDL Standard. Edgar Chen; Timothy Gu. WHATWG. Living Standard. URL: https://webidl.spec.whatwg.org/