upower.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. // SPDX-FileCopyrightText: GSConnect Developers https://github.com/GSConnect
  2. //
  3. // SPDX-License-Identifier: GPL-2.0-or-later
  4. import Gio from 'gi://Gio';
  5. import GLib from 'gi://GLib';
  6. import GObject from 'gi://GObject';
  7. /**
  8. * The warning level of a battery.
  9. *
  10. * @readonly
  11. * @enum {number}
  12. */
  13. const DeviceLevel = {
  14. UNKNOWN: 0,
  15. NONE: 1,
  16. DISCHARGING: 2,
  17. LOW: 3,
  18. CRITICAL: 4,
  19. ACTION: 5,
  20. NORMAL: 6,
  21. HIGH: 7,
  22. FULL: 8,
  23. LAST: 9,
  24. };
  25. /**
  26. * The device state.
  27. *
  28. * @readonly
  29. * @enum {number}
  30. */
  31. const DeviceState = {
  32. UNKNOWN: 0,
  33. CHARGING: 1,
  34. DISCHARGING: 2,
  35. EMPTY: 3,
  36. FULLY_CHARGED: 4,
  37. PENDING_CHARGE: 5,
  38. PENDING_DISCHARGE: 6,
  39. LAST: 7,
  40. };
  41. /**
  42. * A class representing the system battery.
  43. */
  44. const Battery = GObject.registerClass({
  45. GTypeName: 'GSConnectSystemBattery',
  46. Signals: {
  47. 'changed': {
  48. flags: GObject.SignalFlags.RUN_FIRST,
  49. },
  50. },
  51. Properties: {
  52. 'charging': GObject.ParamSpec.boolean(
  53. 'charging',
  54. 'Charging',
  55. 'The current charging state.',
  56. GObject.ParamFlags.READABLE,
  57. false
  58. ),
  59. 'level': GObject.ParamSpec.int(
  60. 'level',
  61. 'Level',
  62. 'The current power level.',
  63. GObject.ParamFlags.READABLE,
  64. -1, 100,
  65. -1
  66. ),
  67. 'threshold': GObject.ParamSpec.uint(
  68. 'threshold',
  69. 'Threshold',
  70. 'The current threshold state.',
  71. GObject.ParamFlags.READABLE,
  72. 0, 1,
  73. 0
  74. ),
  75. },
  76. }, class Battery extends GObject.Object {
  77. _init() {
  78. super._init();
  79. this._cancellable = new Gio.Cancellable();
  80. this._proxy = null;
  81. this._propertiesChangedId = 0;
  82. this._loadUPower();
  83. }
  84. async _loadUPower() {
  85. try {
  86. this._proxy = new Gio.DBusProxy({
  87. g_bus_type: Gio.BusType.SYSTEM,
  88. g_name: 'org.freedesktop.UPower',
  89. g_object_path: '/org/freedesktop/UPower/devices/DisplayDevice',
  90. g_interface_name: 'org.freedesktop.UPower.Device',
  91. g_flags: Gio.DBusProxyFlags.DO_NOT_AUTO_START,
  92. });
  93. await this._proxy.init_async(GLib.PRIORITY_DEFAULT,
  94. this._cancellable);
  95. this._propertiesChangedId = this._proxy.connect(
  96. 'g-properties-changed', this._onPropertiesChanged.bind(this));
  97. this._initProperties(this._proxy);
  98. } catch (e) {
  99. if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
  100. const service = Gio.Application.get_default();
  101. if (service !== null)
  102. service.notify_error(e);
  103. else
  104. logError(e);
  105. }
  106. this._proxy = null;
  107. }
  108. }
  109. _initProperties(proxy) {
  110. if (proxy.g_name_owner === null)
  111. return;
  112. const percentage = proxy.get_cached_property('Percentage').unpack();
  113. const state = proxy.get_cached_property('State').unpack();
  114. const level = proxy.get_cached_property('WarningLevel').unpack();
  115. this._level = Math.floor(percentage);
  116. this._charging = (state !== DeviceState.DISCHARGING);
  117. this._threshold = (!this.charging && level >= DeviceLevel.LOW);
  118. this.emit('changed');
  119. }
  120. _onPropertiesChanged(proxy, changed, invalidated) {
  121. let emitChanged = false;
  122. const properties = changed.deepUnpack();
  123. if (properties.hasOwnProperty('Percentage')) {
  124. emitChanged = true;
  125. const value = proxy.get_cached_property('Percentage').unpack();
  126. this._level = Math.floor(value);
  127. this.notify('level');
  128. }
  129. if (properties.hasOwnProperty('State')) {
  130. emitChanged = true;
  131. const value = proxy.get_cached_property('State').unpack();
  132. this._charging = (value !== DeviceState.DISCHARGING);
  133. this.notify('charging');
  134. }
  135. if (properties.hasOwnProperty('WarningLevel')) {
  136. emitChanged = true;
  137. const value = proxy.get_cached_property('WarningLevel').unpack();
  138. this._threshold = (!this.charging && value >= DeviceLevel.LOW);
  139. this.notify('threshold');
  140. }
  141. if (emitChanged)
  142. this.emit('changed');
  143. }
  144. get charging() {
  145. if (this._charging === undefined)
  146. this._charging = false;
  147. return this._charging;
  148. }
  149. get is_present() {
  150. return (this._proxy && this._proxy.g_name_owner);
  151. }
  152. get level() {
  153. if (this._level === undefined)
  154. this._level = -1;
  155. return this._level;
  156. }
  157. get threshold() {
  158. if (this._threshold === undefined)
  159. this._threshold = 0;
  160. return this._threshold;
  161. }
  162. destroy() {
  163. if (this._cancellable.is_cancelled())
  164. return;
  165. this._cancellable.cancel();
  166. if (this._proxy && this._propertiesChangedId > 0) {
  167. this._proxy.disconnect(this._propertiesChangedId);
  168. this._propertiesChangedId = 0;
  169. }
  170. }
  171. });
  172. /**
  173. * The service class for this component
  174. */
  175. export default Battery;