diff --git a/README.md b/README.md
index f7f2071..e719716 100644
--- a/README.md
+++ b/README.md
@@ -155,6 +155,26 @@ org.freedesktop.tuhi1.Device
The physical dimensions (width, height) in µm
Read-only
+ Property: BatteryPercent (u)
+ The last known battery charge level in percent. This charge level is
+ only valid when the BatteryState is other than Unknown.
+ Read-only
+
+ Property: BatteryState (u)
+ An enum describing the battery state. Permitted enum values are
+
+ 0: Unknown
+ 1: Charging
+ 2: Discharging
+
+ 'Unknown' may refer to a state that could not be read, a state
+ that has not yet been updated, or a state that has not updated within
+ a daemon-internal time period. Thus, a device that is connected but
+ does not regularly send battery updates may eventually switch to
+ 'Unknown'.
+
+ Read-only
+
Property: DrawingsAvailable (at)
An array of timestamps of the available drawings. The timestamp of
each drawing can be used as argument to GetJSONData(). Timestamps are
diff --git a/tools/tuhi-kete.py b/tools/tuhi-kete.py
index eeb35cb..2489c7b 100755
--- a/tools/tuhi-kete.py
+++ b/tools/tuhi-kete.py
@@ -163,6 +163,14 @@ class TuhiKeteDevice(_DBusObject):
def drawings_available(self):
return self.property('DrawingsAvailable')
+ @GObject.Property
+ def battery_percent(self):
+ return self.property('BatteryPercent')
+
+ @GObject.Property
+ def battery_state(self):
+ return self.property('BatteryState')
+
def pair(self):
logger.debug(f'{self}: Pairing')
# FIXME: Pair() doesn't return anything useful yet, so we wait until
@@ -199,6 +207,10 @@ class TuhiKeteDevice(_DBusObject):
self.notify('drawings-available')
elif 'Listening' in changed_props:
self.notify('listening')
+ elif 'BatteryPercent' in changed_props:
+ self.notify('battery-percent')
+ elif 'BatteryState' in changed_props:
+ self.notify('battery-state')
def __repr__(self):
return f'{self.address} - {self.name}'
@@ -923,6 +935,16 @@ class TuhiKeteShell(cmd.Cmd):
for device in self._manager.devices:
if parsed_args.address is None or parsed_args.address == device.address:
print(device)
+ charge_strs = {
+ 0: 'unknown',
+ 1: 'charging',
+ 2: 'discharging'
+ }
+ try:
+ charge_str = charge_strs[device.battery_state]
+ except KeyError:
+ charge_str = 'invalid'
+ print(f'\tBattery level: {device.battery_percent}%, {charge_str}')
print('\tAvailable drawings:')
for d in device.drawings_available:
t = time.localtime(d)
diff --git a/tuhi/base.py b/tuhi/base.py
index bd9afef..29600e7 100644
--- a/tuhi/base.py
+++ b/tuhi/base.py
@@ -12,6 +12,7 @@
#
import argparse
+import enum
import logging
import sys
from gi.repository import GObject
@@ -34,6 +35,12 @@ class TuhiDevice(GObject.Object):
real device) with the frontend DBusServer object that exports the device
over Tuhi's DBus interface
'''
+
+ class BatteryState(enum.Enum):
+ UNKNOWN = 0
+ CHARGING = 1
+ DISCHARGING = 2
+
__gsignals__ = {
# Signal sent when an error occurs on the device itself.
# Argument is a Wacom*Exception
@@ -49,6 +56,8 @@ class TuhiDevice(GObject.Object):
assert uuid is not None or paired is False
self.paired = paired
self._uuid = uuid
+ self._battery_state = TuhiDevice.BatteryState.UNKNOWN
+ self._battery_percent = 0
bluez_device.connect('connected', self._on_bluez_device_connected)
bluez_device.connect('disconnected', self._on_bluez_device_disconnected)
@@ -93,6 +102,23 @@ class TuhiDevice(GObject.Object):
def listening(self):
return self._tuhi_dbus_device.listening
+ @GObject.Property
+ def battery_percent(self):
+ return self._battery_percent
+
+ @battery_percent.setter
+ def battery_percent(self, value):
+ self._battery_percent = value
+
+ @GObject.Property
+ def battery_state(self):
+ return self._battery_state
+
+ @battery_state.setter
+ def battery_state(self, value):
+ print('setting battery state on tuhidevice')
+ self._battery_state = value
+
def connect_device(self):
self._bluez_device.connect_device()
@@ -104,6 +130,7 @@ class TuhiDevice(GObject.Object):
self._wacom_device.connect('done', self._on_fetching_finished, bluez_device)
self._wacom_device.connect('button-press-required', self._on_button_press_required)
self._wacom_device.connect('notify::uuid', self._on_uuid_updated, bluez_device)
+ self._wacom_device.connect('battery-status', self._on_battery_status, bluez_device)
self._wacom_device.start(not self.paired)
self.pairing_mode = False
@@ -138,6 +165,13 @@ class TuhiDevice(GObject.Object):
def _on_listening_updated(self, dbus_device, pspec):
self.notify('listening')
+ def _on_battery_status(self, wacom_device, percent, is_charging, bluez_device):
+ if is_charging:
+ self.battery_state = TuhiDevice.BatteryState.CHARGING
+ else:
+ self.battery_state = TuhiDevice.BatteryState.DISCHARGING
+ self.battery_percent = percent
+
class Tuhi(GObject.Object):
__gsignals__ = {
diff --git a/tuhi/dbusserver.py b/tuhi/dbusserver.py
index 7b4038f..7ae2df6 100755
--- a/tuhi/dbusserver.py
+++ b/tuhi/dbusserver.py
@@ -53,6 +53,12 @@ INTROSPECTION_XML = '''
+
+
+
+
+
+
@@ -146,7 +152,11 @@ class TuhiDBusDevice(_TuhiDBus):
self._listening = False
self._listening_client = None
self._dbusid = self._register_object(connection)
+ self._battery_percent = 0
+ self._battery_state = device.battery_state
device.connect('notify::paired', self._on_device_paired)
+ device.connect('notify::battery-percent', self._on_battery_percent)
+ device.connect('notify::battery-state', self._on_battery_state)
device.connect('device-error', self._on_device_error)
@GObject.Property
@@ -169,6 +179,30 @@ class TuhiDBusDevice(_TuhiDBus):
def paired(self, paired):
self._paired = paired
+ @GObject.Property
+ def battery_percent(self):
+ return self._battery_percent
+
+ @battery_percent.setter
+ def battery_percent(self, value):
+ if self._battery_percent == value:
+ return
+
+ self._battery_percent = value
+ self.properties_changed({'BatteryPercent': GLib.Variant.new_uint32(value)})
+
+ @GObject.Property
+ def battery_state(self):
+ return self._battery_state
+
+ @battery_state.setter
+ def battery_state(self, value):
+ if self._battery_state == value:
+ return
+
+ self._battery_state = value
+ self.properties_changed({'BatteryState': GLib.Variant.new_uint32(value.value)})
+
def remove(self):
self.connection.unregister_object(self._dbusid)
self._dbusid = None
@@ -221,6 +255,10 @@ class TuhiDBusDevice(_TuhiDBus):
return ts
elif propname == 'Listening':
return GLib.Variant.new_boolean(self.listening)
+ elif propname == 'BatteryPercent':
+ return GLib.Variant.new_uint32(self.battery_percent)
+ elif propname == 'BatteryState':
+ return GLib.Variant.new_uint32(self.battery_state.value)
return None
@@ -235,6 +273,12 @@ class TuhiDBusDevice(_TuhiDBus):
return
self.paired = device.paired
+ def _on_battery_percent(self, device, pspec):
+ self.battery_percent = device.battery_percent
+
+ def _on_battery_state(self, device, pspec):
+ self.battery_state = device.battery_state
+
def _on_device_error(self, device, exception):
logger.info('An error occured while synching the device')
if self.listening:
diff --git a/tuhi/wacom.py b/tuhi/wacom.py
index 1e45870..68107bc 100644
--- a/tuhi/wacom.py
+++ b/tuhi/wacom.py
@@ -106,6 +106,9 @@ class WacomDevice(GObject.Object):
(GObject.SIGNAL_RUN_FIRST, None, (GObject.TYPE_PYOBJECT, )),
'button-press-required':
(GObject.SIGNAL_RUN_FIRST, None, ()),
+ # battery level in %, boolean for is-charging
+ "battery-status":
+ (GObject.SIGNAL_RUN_FIRST, None, (GObject.TYPE_INT, GObject.TYPE_BOOLEAN)),
}
def __init__(self, device, uuid=None):
@@ -544,6 +547,7 @@ class WacomDevice(GObject.Object):
logger.debug(f'device is plugged in and charged at {battery}%')
else:
logger.debug(f'device is discharging: {battery}%')
+ self.emit('battery-status', battery, charging)
if self.is_slate():
self.width = w = self.get_dimensions('width')
self.height = h = self.get_dimensions('height')