upower.js 5.2 KB

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