dbus2mqtt 0.1.2__py3-none-any.whl → 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of dbus2mqtt might be problematic. Click here for more details.
- dbus2mqtt/{config.py → config/__init__.py} +18 -15
- dbus2mqtt/config/jsonarparse.py +31 -0
- dbus2mqtt/dbus/dbus_client.py +62 -38
- dbus2mqtt/dbus/dbus_types.py +2 -2
- dbus2mqtt/dbus/dbus_util.py +2 -2
- dbus2mqtt/dbus/introspection_patches/mpris_playerctl.py +151 -0
- dbus2mqtt/dbus/introspection_patches/mpris_vlc.py +122 -0
- dbus2mqtt/event_broker.py +1 -1
- dbus2mqtt/flow/__init__.py +22 -3
- dbus2mqtt/flow/actions/context_set.py +2 -0
- dbus2mqtt/flow/actions/mqtt_publish.py +24 -6
- dbus2mqtt/flow/flow_processor.py +28 -26
- dbus2mqtt/main.py +12 -18
- dbus2mqtt/mqtt/mqtt_client.py +40 -24
- dbus2mqtt/template/dbus_template_functions.py +2 -2
- dbus2mqtt/template/templating.py +57 -80
- {dbus2mqtt-0.1.2.dist-info → dbus2mqtt-0.3.0.dist-info}/METADATA +9 -12
- dbus2mqtt-0.3.0.dist-info/RECORD +23 -0
- dbus2mqtt-0.1.2.dist-info/RECORD +0 -20
- {dbus2mqtt-0.1.2.dist-info → dbus2mqtt-0.3.0.dist-info}/WHEEL +0 -0
- {dbus2mqtt-0.1.2.dist-info → dbus2mqtt-0.3.0.dist-info}/entry_points.txt +0 -0
- {dbus2mqtt-0.1.2.dist-info → dbus2mqtt-0.3.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -15,8 +15,9 @@ class SignalConfig:
|
|
|
15
15
|
filter: str | None = None
|
|
16
16
|
|
|
17
17
|
def matches_filter(self, template_engine: TemplateEngine, *args) -> bool:
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
if self.filter:
|
|
19
|
+
return template_engine.render_template(self.filter, bool, { "args": args })
|
|
20
|
+
return True
|
|
20
21
|
|
|
21
22
|
@dataclass
|
|
22
23
|
class MethodConfig:
|
|
@@ -29,13 +30,15 @@ class PropertyConfig:
|
|
|
29
30
|
@dataclass
|
|
30
31
|
class InterfaceConfig:
|
|
31
32
|
interface: str
|
|
32
|
-
|
|
33
|
+
mqtt_command_topic: str | None = None
|
|
33
34
|
signals: list[SignalConfig] = field(default_factory=list)
|
|
34
35
|
methods: list[MethodConfig] = field(default_factory=list)
|
|
35
36
|
properties: list[PropertyConfig] = field(default_factory=list)
|
|
36
37
|
|
|
37
|
-
def
|
|
38
|
-
|
|
38
|
+
def render_mqtt_command_topic(self, template_engine: TemplateEngine, context: dict[str, Any]) -> Any:
|
|
39
|
+
if self.mqtt_command_topic:
|
|
40
|
+
return template_engine.render_template(self.mqtt_command_topic, str, context)
|
|
41
|
+
return None
|
|
39
42
|
|
|
40
43
|
@dataclass
|
|
41
44
|
class FlowTriggerMqttConfig:
|
|
@@ -47,8 +50,8 @@ class FlowTriggerMqttConfig:
|
|
|
47
50
|
class FlowTriggerScheduleConfig:
|
|
48
51
|
type: Literal["schedule"] = "schedule"
|
|
49
52
|
id: str = field(default_factory=lambda: uuid.uuid4().hex)
|
|
50
|
-
cron: dict[str,
|
|
51
|
-
interval: dict[str,
|
|
53
|
+
cron: dict[str, object] | None = None
|
|
54
|
+
interval: dict[str, object] | None = None
|
|
52
55
|
|
|
53
56
|
@dataclass
|
|
54
57
|
class FlowTriggerDbusSignalConfig:
|
|
@@ -57,17 +60,17 @@ class FlowTriggerDbusSignalConfig:
|
|
|
57
60
|
type: Literal["dbus_signal"] = "dbus_signal"
|
|
58
61
|
bus_name: str | None = None
|
|
59
62
|
path: str | None = None
|
|
60
|
-
filter: str | None = None
|
|
63
|
+
# filter: str | None = None
|
|
61
64
|
|
|
62
65
|
@dataclass
|
|
63
66
|
class FlowTriggerBusNameAddedConfig:
|
|
64
67
|
type: Literal["bus_name_added"] = "bus_name_added"
|
|
65
|
-
filter: str | None = None
|
|
68
|
+
# filter: str | None = None
|
|
66
69
|
|
|
67
70
|
@dataclass
|
|
68
71
|
class FlowTriggerBusNameRemovedConfig:
|
|
69
72
|
type: Literal["bus_name_removed"] = "bus_name_removed"
|
|
70
|
-
filter: str | None = None
|
|
73
|
+
# filter: str | None = None
|
|
71
74
|
|
|
72
75
|
FlowTriggerConfig = Annotated[
|
|
73
76
|
FlowTriggerMqttConfig | FlowTriggerScheduleConfig | FlowTriggerDbusSignalConfig | FlowTriggerBusNameAddedConfig | FlowTriggerBusNameRemovedConfig,
|
|
@@ -77,17 +80,17 @@ FlowTriggerConfig = Annotated[
|
|
|
77
80
|
@dataclass
|
|
78
81
|
class FlowActionContextSetConfig:
|
|
79
82
|
type: Literal["context_set"] = "context_set"
|
|
80
|
-
context: dict[str,
|
|
81
|
-
|
|
83
|
+
context: dict[str, object] | None = None
|
|
84
|
+
"""Per flow execution context"""
|
|
85
|
+
global_context: dict[str, object] | None = None
|
|
86
|
+
"""Global context, shared between multiple flow executions, over all subscriptions"""
|
|
82
87
|
|
|
83
88
|
@dataclass
|
|
84
89
|
class FlowActionMqttPublishConfig:
|
|
85
90
|
topic: str
|
|
86
91
|
payload_template: str | dict[str, Any]
|
|
87
|
-
"""should be a dict if payload_type is json/yaml
|
|
88
|
-
or a string if payload_type is text"""
|
|
89
92
|
type: Literal["mqtt_publish"] = "mqtt_publish"
|
|
90
|
-
payload_type: Literal["json", "yaml", "text"] = "json"
|
|
93
|
+
payload_type: Literal["json", "yaml", "text", "binary"] = "json"
|
|
91
94
|
|
|
92
95
|
FlowActionConfig = Annotated[
|
|
93
96
|
FlowActionMqttPublishConfig | FlowActionContextSetConfig,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import jsonargparse
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def _custom_yaml_load(stream):
|
|
5
|
+
if isinstance(stream, str):
|
|
6
|
+
v = stream.strip()
|
|
7
|
+
|
|
8
|
+
# jsonargparse tries to parse yaml 1.1 boolean like values
|
|
9
|
+
# Without this, str:"{'PlaybackStatus': 'Off'}" would become dict:{'PlaybackStatus': False}
|
|
10
|
+
if v in ['on', 'On', 'off', 'Off', 'TRUE', 'FALSE', 'True', 'False']:
|
|
11
|
+
return stream
|
|
12
|
+
|
|
13
|
+
# Anoyingly, values starting with {{ and ending with }} are working with the default yaml_loader
|
|
14
|
+
# from jsonargparse. Somehow its not when we use the custom yaml loader.
|
|
15
|
+
# This fixes it
|
|
16
|
+
if v.startswith("{{") or v.startswith("{%"):
|
|
17
|
+
return stream
|
|
18
|
+
|
|
19
|
+
# Delegate to default yaml loader from jsonargparse
|
|
20
|
+
yaml_loader = jsonargparse.get_loader("yaml")
|
|
21
|
+
return yaml_loader(stream)
|
|
22
|
+
|
|
23
|
+
def new_argument_parser() -> jsonargparse.ArgumentParser:
|
|
24
|
+
|
|
25
|
+
# register out custom yaml loader for jsonargparse
|
|
26
|
+
jsonargparse.set_loader("yaml_custom", _custom_yaml_load)
|
|
27
|
+
|
|
28
|
+
# unless specified otherwise, load config from config.yaml
|
|
29
|
+
parser = jsonargparse.ArgumentParser(default_config_files=["config.yaml"], default_env=True, env_prefix=False, parser_mode="yaml_custom")
|
|
30
|
+
|
|
31
|
+
return parser
|
dbus2mqtt/dbus/dbus_client.py
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
|
-
import re
|
|
4
3
|
|
|
5
4
|
from datetime import datetime
|
|
6
5
|
from typing import Any
|
|
7
6
|
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import dbus_next.introspection as dbus_introspection
|
|
7
|
+
import dbus_fast.aio as dbus_aio
|
|
8
|
+
import dbus_fast.introspection as dbus_introspection
|
|
11
9
|
|
|
12
10
|
from dbus2mqtt import AppContext
|
|
13
11
|
from dbus2mqtt.config import InterfaceConfig, SubscriptionConfig
|
|
@@ -17,17 +15,15 @@ from dbus2mqtt.dbus.dbus_util import (
|
|
|
17
15
|
unwrap_dbus_object,
|
|
18
16
|
unwrap_dbus_objects,
|
|
19
17
|
)
|
|
18
|
+
from dbus2mqtt.dbus.introspection_patches.mpris_playerctl import (
|
|
19
|
+
mpris_introspection_playerctl,
|
|
20
|
+
)
|
|
21
|
+
from dbus2mqtt.dbus.introspection_patches.mpris_vlc import mpris_introspection_vlc
|
|
20
22
|
from dbus2mqtt.event_broker import DbusSignalWithState, MqttMessage
|
|
21
23
|
from dbus2mqtt.flow.flow_processor import FlowScheduler, FlowTriggerMessage
|
|
22
24
|
|
|
23
25
|
logger = logging.getLogger(__name__)
|
|
24
26
|
|
|
25
|
-
# dbus_next path to support https://docs.flatpak.org/en/latest/portal-api-reference.html
|
|
26
|
-
# Although not correct, flatpak exposes properties with names containing '-'
|
|
27
|
-
# This is failing assertions in dbus_next
|
|
28
|
-
# original: r'^[A-Za-z_][A-Za-z0-9_]*$'
|
|
29
|
-
# patched: r'^[A-Za-z_][A-Za-z0-9_-]*$'
|
|
30
|
-
dbus_next.validators._element_re = re.compile(r'^[A-Za-z_][A-Za-z0-9_-]*$')
|
|
31
27
|
|
|
32
28
|
class DbusClient:
|
|
33
29
|
|
|
@@ -90,23 +86,23 @@ class DbusClient:
|
|
|
90
86
|
|
|
91
87
|
return proxy_object, bus_name_subscriptions
|
|
92
88
|
|
|
93
|
-
def
|
|
89
|
+
def _dbus_fast_signal_publisher(self, dbus_signal_state: dict[str, Any], *args):
|
|
94
90
|
unwrapped_args = unwrap_dbus_objects(args)
|
|
95
91
|
self.event_broker.on_dbus_signal(
|
|
96
92
|
DbusSignalWithState(**dbus_signal_state, args=unwrapped_args)
|
|
97
93
|
)
|
|
98
94
|
|
|
99
|
-
def
|
|
95
|
+
def _dbus_fast_signal_handler(self, signal: dbus_introspection.Signal, state: dict[str, Any]) -> Any:
|
|
100
96
|
expected_args = len(signal.args)
|
|
101
97
|
|
|
102
98
|
if expected_args == 1:
|
|
103
|
-
return lambda a: self.
|
|
99
|
+
return lambda a: self._dbus_fast_signal_publisher(state, a)
|
|
104
100
|
elif expected_args == 2:
|
|
105
|
-
return lambda a, b: self.
|
|
101
|
+
return lambda a, b: self._dbus_fast_signal_publisher(state, a, b)
|
|
106
102
|
elif expected_args == 3:
|
|
107
|
-
return lambda a, b, c: self.
|
|
103
|
+
return lambda a, b, c: self._dbus_fast_signal_publisher(state, a, b, c)
|
|
108
104
|
elif expected_args == 4:
|
|
109
|
-
return lambda a, b, c, d: self.
|
|
105
|
+
return lambda a, b, c, d: self._dbus_fast_signal_publisher(state, a, b, c, d)
|
|
110
106
|
raise ValueError("Unsupported nr of arguments")
|
|
111
107
|
|
|
112
108
|
async def _subscribe_interface(self, bus_name: str, path: str, introspection: dbus_introspection.Node, interface: dbus_introspection.Interface, subscription_config: SubscriptionConfig, si: InterfaceConfig) -> SubscribedInterface:
|
|
@@ -131,7 +127,7 @@ class DbusClient:
|
|
|
131
127
|
"signal_config": signal_config,
|
|
132
128
|
}
|
|
133
129
|
|
|
134
|
-
handler = self.
|
|
130
|
+
handler = self._dbus_fast_signal_handler(interface_signal, dbus_signal_state)
|
|
135
131
|
obj_interface.__getattribute__(on_signal_method_name)(handler)
|
|
136
132
|
logger.info(f"subscribed with signal_handler: signal={signal_config.signal}, bus_name={bus_name}, path={path}, interface={interface.name}")
|
|
137
133
|
|
|
@@ -162,12 +158,28 @@ class DbusClient:
|
|
|
162
158
|
|
|
163
159
|
return new_subscriptions
|
|
164
160
|
|
|
161
|
+
async def _introspect(self, bus_name: str, path: str) -> dbus_introspection.Node:
|
|
162
|
+
|
|
163
|
+
if path == "/org/mpris/MediaPlayer2" and bus_name.startswith("org.mpris.MediaPlayer2.vlc"):
|
|
164
|
+
# vlc 3.x branch contains an incomplete dbus introspection
|
|
165
|
+
# https://github.com/videolan/vlc/commit/48e593f164d2bf09b0ca096d88c86d78ec1a2ca0
|
|
166
|
+
# Until vlc 4.x is out we use the official specification instead
|
|
167
|
+
introspection = mpris_introspection_vlc
|
|
168
|
+
else:
|
|
169
|
+
introspection = await self.bus.introspect(bus_name, path)
|
|
170
|
+
|
|
171
|
+
# MPRIS: If no introspection data is available, load a default
|
|
172
|
+
if path == "/org/mpris/MediaPlayer2" and bus_name.startswith("org.mpris.MediaPlayer2.") and len(introspection.interfaces) == 0:
|
|
173
|
+
introspection = mpris_introspection_playerctl
|
|
174
|
+
|
|
175
|
+
return introspection
|
|
176
|
+
|
|
165
177
|
async def _visit_bus_name_path(self, bus_name: str, path: str) -> list[SubscribedInterface]:
|
|
166
178
|
|
|
167
179
|
new_subscriptions: list[SubscribedInterface] = []
|
|
168
180
|
|
|
169
181
|
try:
|
|
170
|
-
introspection = await self.
|
|
182
|
+
introspection = await self._introspect(bus_name, path)
|
|
171
183
|
except TypeError as e:
|
|
172
184
|
logger.warning(f"bus.introspect failed, bus_name={bus_name}, path={path}: {e}")
|
|
173
185
|
return new_subscriptions
|
|
@@ -223,21 +235,27 @@ class DbusClient:
|
|
|
223
235
|
self.flow_scheduler.start_flow_set(subscription_config.flows)
|
|
224
236
|
|
|
225
237
|
# Trigger flows that have a bus_name_added trigger configured
|
|
226
|
-
await self._trigger_bus_name_added(subscription_config, subscribed_interface.bus_name)
|
|
238
|
+
await self._trigger_bus_name_added(subscription_config, subscribed_interface.bus_name, subscribed_interface.path)
|
|
227
239
|
|
|
228
240
|
processed_new_subscriptions.add(subscription_config.id)
|
|
229
241
|
|
|
230
242
|
return new_subscriped_interfaces
|
|
231
243
|
|
|
232
|
-
async def _trigger_bus_name_added(self, subscription_config: SubscriptionConfig, bus_name: str):
|
|
244
|
+
async def _trigger_bus_name_added(self, subscription_config: SubscriptionConfig, bus_name: str, path: str):
|
|
233
245
|
|
|
234
246
|
for flow in subscription_config.flows:
|
|
235
247
|
for trigger in flow.triggers:
|
|
236
248
|
if trigger.type == "bus_name_added":
|
|
237
|
-
|
|
249
|
+
trigger_context = {
|
|
238
250
|
"bus_name": bus_name,
|
|
251
|
+
"path": path
|
|
239
252
|
}
|
|
240
|
-
trigger_message = FlowTriggerMessage(
|
|
253
|
+
trigger_message = FlowTriggerMessage(
|
|
254
|
+
flow,
|
|
255
|
+
trigger,
|
|
256
|
+
datetime.now(),
|
|
257
|
+
trigger_context=trigger_context
|
|
258
|
+
)
|
|
241
259
|
await self.event_broker.flow_trigger_queue.async_q.put(trigger_message)
|
|
242
260
|
|
|
243
261
|
async def _handle_bus_name_removed(self, bus_name: str):
|
|
@@ -251,7 +269,7 @@ class DbusClient:
|
|
|
251
269
|
for subscription_config in subscription_configs:
|
|
252
270
|
|
|
253
271
|
# Trigger flows that have a bus_name_added trigger configured
|
|
254
|
-
await self._trigger_bus_name_removed(subscription_config, bus_name)
|
|
272
|
+
await self._trigger_bus_name_removed(subscription_config, bus_name, path)
|
|
255
273
|
|
|
256
274
|
# Stop schedule triggers
|
|
257
275
|
self.flow_scheduler.stop_flow_set(subscription_config.flows)
|
|
@@ -275,16 +293,22 @@ class DbusClient:
|
|
|
275
293
|
|
|
276
294
|
del self.subscriptions[bus_name]
|
|
277
295
|
|
|
278
|
-
async def _trigger_bus_name_removed(self, subscription_config: SubscriptionConfig, bus_name: str):
|
|
296
|
+
async def _trigger_bus_name_removed(self, subscription_config: SubscriptionConfig, bus_name: str, path: str):
|
|
279
297
|
|
|
280
298
|
# Trigger flows that have a bus_name_removed trigger configured
|
|
281
299
|
for flow in subscription_config.flows:
|
|
282
300
|
for trigger in flow.triggers:
|
|
283
301
|
if trigger.type == "bus_name_removed":
|
|
284
|
-
|
|
302
|
+
trigger_context = {
|
|
285
303
|
"bus_name": bus_name,
|
|
304
|
+
"path": path
|
|
286
305
|
}
|
|
287
|
-
trigger_message = FlowTriggerMessage(
|
|
306
|
+
trigger_message = FlowTriggerMessage(
|
|
307
|
+
flow,
|
|
308
|
+
trigger,
|
|
309
|
+
datetime.now(),
|
|
310
|
+
trigger_context=trigger_context
|
|
311
|
+
)
|
|
288
312
|
await self.event_broker.flow_trigger_queue.async_q.put(trigger_message)
|
|
289
313
|
|
|
290
314
|
async def _dbus_name_owner_changed_callback(self, name, old_owner, new_owner):
|
|
@@ -310,7 +334,7 @@ class DbusClient:
|
|
|
310
334
|
|
|
311
335
|
return res
|
|
312
336
|
|
|
313
|
-
async def get_dbus_interface_property(self, interface: dbus_aio.proxy_object.ProxyInterface, property: str):
|
|
337
|
+
async def get_dbus_interface_property(self, interface: dbus_aio.proxy_object.ProxyInterface, property: str) -> Any:
|
|
314
338
|
|
|
315
339
|
call_method_name = "get_" + camel_to_snake(property)
|
|
316
340
|
res = await interface.__getattribute__(call_method_name)()
|
|
@@ -322,17 +346,12 @@ class DbusClient:
|
|
|
322
346
|
|
|
323
347
|
return res
|
|
324
348
|
|
|
325
|
-
async def set_dbus_interface_property(self, interface: dbus_aio.proxy_object.ProxyInterface, property: str, value):
|
|
349
|
+
async def set_dbus_interface_property(self, interface: dbus_aio.proxy_object.ProxyInterface, property: str, value: Any) -> None:
|
|
326
350
|
|
|
327
351
|
call_method_name = "set_" + camel_to_snake(property)
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if res:
|
|
331
|
-
res = unwrap_dbus_object(res)
|
|
332
|
-
|
|
333
|
-
logger.info(f"set_dbus_interface_property: bus_name={interface.bus_name}, interface={interface.introspection.name}, property={property}, res={res}")
|
|
352
|
+
await interface.__getattribute__(call_method_name)(value)
|
|
334
353
|
|
|
335
|
-
|
|
354
|
+
logger.info(f"set_dbus_interface_property: bus_name={interface.bus_name}, interface={interface.introspection.name}, property={property}, value={value}")
|
|
336
355
|
|
|
337
356
|
async def mqtt_receive_queue_processor_task(self):
|
|
338
357
|
"""Continuously processes messages from the async queue."""
|
|
@@ -367,13 +386,18 @@ class DbusClient:
|
|
|
367
386
|
matches_filter = signal.signal_config.matches_filter(self.app_context.templating, *signal.args)
|
|
368
387
|
|
|
369
388
|
if matches_filter:
|
|
370
|
-
|
|
389
|
+
trigger_context = {
|
|
371
390
|
"bus_name": signal.bus_name,
|
|
372
391
|
"path": signal.path,
|
|
373
392
|
"interface": signal.interface_name,
|
|
374
393
|
"args": signal.args
|
|
375
394
|
}
|
|
376
|
-
trigger_message = FlowTriggerMessage(
|
|
395
|
+
trigger_message = FlowTriggerMessage(
|
|
396
|
+
flow,
|
|
397
|
+
trigger,
|
|
398
|
+
datetime.now(),
|
|
399
|
+
trigger_context=trigger_context
|
|
400
|
+
)
|
|
377
401
|
|
|
378
402
|
await self.event_broker.flow_trigger_queue.async_q.put(trigger_message)
|
|
379
403
|
except Exception as e:
|
|
@@ -389,7 +413,7 @@ class DbusClient:
|
|
|
389
413
|
for subscription_configs in self.config.subscriptions:
|
|
390
414
|
for interface_config in subscription_configs.interfaces:
|
|
391
415
|
# TODO, performance improvement
|
|
392
|
-
mqtt_topic = interface_config.
|
|
416
|
+
mqtt_topic = interface_config.render_mqtt_command_topic(self.templating, {})
|
|
393
417
|
found_matching_topic |= mqtt_topic == msg.topic
|
|
394
418
|
|
|
395
419
|
if not found_matching_topic:
|
dbus2mqtt/dbus/dbus_types.py
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import dbus_fast.aio as dbus_aio
|
|
6
6
|
|
|
7
7
|
from dbus2mqtt.config import InterfaceConfig, SubscriptionConfig
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class BusNameSubscriptions:
|
|
11
11
|
|
|
12
|
-
def __init__(self,
|
|
12
|
+
def __init__(self, bus_name: str):
|
|
13
13
|
self.bus_name = bus_name
|
|
14
14
|
self.path_objects: dict[str, dbus_aio.proxy_object.ProxyObject] = {}
|
|
15
15
|
|
dbus2mqtt/dbus/dbus_util.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import re
|
|
3
3
|
|
|
4
|
-
import
|
|
4
|
+
import dbus_fast.signature as dbus_signature
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
def _variant_serializer(obj):
|
|
@@ -10,7 +10,7 @@ def _variant_serializer(obj):
|
|
|
10
10
|
return obj
|
|
11
11
|
|
|
12
12
|
def unwrap_dbus_object(o):
|
|
13
|
-
# an easy way to get rid of
|
|
13
|
+
# an easy way to get rid of dbus_fast.signature.Variant types
|
|
14
14
|
res = json.dumps(o, default=_variant_serializer)
|
|
15
15
|
json_obj = json.loads(res)
|
|
16
16
|
return json_obj
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import dbus_fast.introspection as dbus_introspection
|
|
2
|
+
|
|
3
|
+
# taken from https://github.com/altdesktop/playerctl/blob/b19a71cb9dba635df68d271bd2b3f6a99336a223/playerctl/playerctl-daemon.c#L578
|
|
4
|
+
mpris_introspection_playerctl = dbus_introspection.Node.parse("""\
|
|
5
|
+
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
|
6
|
+
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
|
7
|
+
<node>
|
|
8
|
+
<interface name="org.freedesktop.DBus.Introspectable">
|
|
9
|
+
<method name="Introspect">
|
|
10
|
+
<arg name="data" direction="out" type="s"/>
|
|
11
|
+
</method>
|
|
12
|
+
</interface>
|
|
13
|
+
|
|
14
|
+
<interface name="org.freedesktop.DBus.Properties">
|
|
15
|
+
<method name="Get">
|
|
16
|
+
<arg direction="in" type="s"/>
|
|
17
|
+
<arg direction="in" type="s"/>
|
|
18
|
+
<arg direction="out" type="v"/>
|
|
19
|
+
</method>
|
|
20
|
+
<method name="Set">
|
|
21
|
+
<arg direction="in" type="s"/>
|
|
22
|
+
<arg direction="in" type="s"/>
|
|
23
|
+
<arg direction="in" type="v"/>
|
|
24
|
+
</method>
|
|
25
|
+
<method name="GetAll">
|
|
26
|
+
<arg direction="in" type="s"/>
|
|
27
|
+
<arg direction="out" type="a{sv}"/>
|
|
28
|
+
</method>
|
|
29
|
+
<signal name="PropertiesChanged">
|
|
30
|
+
<arg type="s"/>
|
|
31
|
+
<arg type="a{sv}"/>
|
|
32
|
+
<arg type="as"/>
|
|
33
|
+
</signal>
|
|
34
|
+
</interface>
|
|
35
|
+
|
|
36
|
+
<interface name="org.mpris.MediaPlayer2">
|
|
37
|
+
<property name="CanQuit" type="b" access="read"/>
|
|
38
|
+
<property name="Fullscreen" type="b" access="readwrite"/>
|
|
39
|
+
<property name="CanSetFullscreen" type="b" access="read"/>
|
|
40
|
+
<property name="CanRaise" type="b" access="read"/>
|
|
41
|
+
<property name="HasTrackList" type="b" access="read"/>
|
|
42
|
+
<property name="Identity" type="s" access="read"/>
|
|
43
|
+
<property name="DesktopEntry" type="s" access="read"/>
|
|
44
|
+
<property name="SupportedUriSchemes" type="as" access="read"/>
|
|
45
|
+
<property name="SupportedMimeTypes" type="as" access="read"/>
|
|
46
|
+
<method name="Raise"/>
|
|
47
|
+
<method name="Quit"/>
|
|
48
|
+
</interface>
|
|
49
|
+
|
|
50
|
+
<interface name="org.mpris.MediaPlayer2.Player">
|
|
51
|
+
<property name="PlaybackStatus" type="s" access="read"/>
|
|
52
|
+
<property name="LoopStatus" type="s" access="readwrite"/>
|
|
53
|
+
<property name="Rate" type="d" access="readwrite"/>
|
|
54
|
+
<property name="Shuffle" type="b" access="readwrite"/>
|
|
55
|
+
<property name="Metadata" type="a{sv}" access="read"/>
|
|
56
|
+
<property name="Volume" type="d" access="readwrite"/>
|
|
57
|
+
<property name="Position" type="x" access="read"/>
|
|
58
|
+
<property name="MinimumRate" type="d" access="read"/>
|
|
59
|
+
<property name="MaximumRate" type="d" access="read"/>
|
|
60
|
+
<property name="CanGoNext" type="b" access="read"/>
|
|
61
|
+
<property name="CanGoPrevious" type="b" access="read"/>
|
|
62
|
+
<property name="CanPlay" type="b" access="read"/>
|
|
63
|
+
<property name="CanPause" type="b" access="read"/>
|
|
64
|
+
<property name="CanSeek" type="b" access="read"/>
|
|
65
|
+
<property name="CanControl" type="b" access="read"/>
|
|
66
|
+
<method name="Next"/>
|
|
67
|
+
<method name="Previous"/>
|
|
68
|
+
<method name="Pause"/>
|
|
69
|
+
<method name="PlayPause"/>
|
|
70
|
+
<method name="Stop"/>
|
|
71
|
+
<method name="Play"/>
|
|
72
|
+
<method name="Seek">
|
|
73
|
+
<arg type="x" name="Offset" direction="in"/>
|
|
74
|
+
</method>
|
|
75
|
+
<method name="SetPosition">
|
|
76
|
+
<arg type="o" name="TrackId" direction="in"/>
|
|
77
|
+
<arg type="x" name="Offset" direction="in"/>
|
|
78
|
+
</method>
|
|
79
|
+
<method name="OpenUri">
|
|
80
|
+
<arg type="s" name="Uri" direction="in"/>
|
|
81
|
+
</method>
|
|
82
|
+
<signal name="Seeked">
|
|
83
|
+
<arg type="x" name="Position" direction="out"/>
|
|
84
|
+
</signal>
|
|
85
|
+
</interface>
|
|
86
|
+
|
|
87
|
+
<interface name="org.mpris.MediaPlayer2.TrackList">
|
|
88
|
+
<property name="Tracks" type="ao" access="read">
|
|
89
|
+
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="invalidates"/>
|
|
90
|
+
</property>
|
|
91
|
+
<property name="CanEditTracks" type="b" access="read">
|
|
92
|
+
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
|
|
93
|
+
</property>
|
|
94
|
+
<method name="GetTracksMetadata">
|
|
95
|
+
<arg direction="in" name="TrackIds" type="ao"/>
|
|
96
|
+
<arg direction="out" name="Metadata" type="aa{sv}"/>
|
|
97
|
+
</method>
|
|
98
|
+
<method name="AddTrack">
|
|
99
|
+
<arg direction="in" name="Uri" type="s"/>
|
|
100
|
+
<arg direction="in" name="AfterTrack" type="o"/>
|
|
101
|
+
<arg direction="in" name="SetAsCurrent" type="b"/>
|
|
102
|
+
</method>
|
|
103
|
+
<method name="RemoveTrack">
|
|
104
|
+
<arg direction="in" name="TrackId" type="o"/>
|
|
105
|
+
</method>
|
|
106
|
+
<method name="GoTo">
|
|
107
|
+
<arg direction="in" name="TrackId" type="o"/>
|
|
108
|
+
</method>
|
|
109
|
+
<signal name="TrackListReplaced">
|
|
110
|
+
<arg name="Tracks" type="ao"/>
|
|
111
|
+
<arg name="CurrentTrack" type="o"/>
|
|
112
|
+
</signal>
|
|
113
|
+
<signal name="TrackAdded">
|
|
114
|
+
<arg name="Metadata" type="a{sv}"/>
|
|
115
|
+
<arg name="AfterTrack" type="o"/>
|
|
116
|
+
</signal>
|
|
117
|
+
<signal name="TrackRemoved">
|
|
118
|
+
<arg name="TrackId" type="o"/>
|
|
119
|
+
</signal>
|
|
120
|
+
<signal name="TrackMetadataChanged">
|
|
121
|
+
<arg name="TrackId" type="o"/>
|
|
122
|
+
<arg name="Metadata" type="a{sv}"/>
|
|
123
|
+
</signal>
|
|
124
|
+
</interface>
|
|
125
|
+
|
|
126
|
+
<interface name="org.mpris.MediaPlayer2.Playlists">
|
|
127
|
+
<method name="ActivatePlaylist">
|
|
128
|
+
<arg direction="in" name="PlaylistId" type="o"/>
|
|
129
|
+
</method>
|
|
130
|
+
<method name="GetPlaylists">
|
|
131
|
+
<arg direction="in" name="Index" type="u"/>
|
|
132
|
+
<arg direction="in" name="MaxCount" type="u"/>
|
|
133
|
+
<arg direction="in" name="Order" type="s"/>
|
|
134
|
+
<arg direction="in" name="ReverseOrder" type="b"/>
|
|
135
|
+
<arg direction="out" name="Playlists" type="a(oss)"/>
|
|
136
|
+
</method>
|
|
137
|
+
<property name="PlaylistCount" type="u" access="read">
|
|
138
|
+
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
|
|
139
|
+
</property>
|
|
140
|
+
<property name="Orderings" type="as" access="read">
|
|
141
|
+
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
|
|
142
|
+
</property>
|
|
143
|
+
<property name="ActivePlaylist" type="(b(oss))" access="read">
|
|
144
|
+
<annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="true"/>
|
|
145
|
+
</property>
|
|
146
|
+
<signal name="PlaylistChanged">
|
|
147
|
+
<arg name="Playlist" type="(oss)"/>
|
|
148
|
+
</signal>
|
|
149
|
+
</interface>
|
|
150
|
+
</node>
|
|
151
|
+
""")
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import dbus_fast.introspection as dbus_introspection
|
|
2
|
+
|
|
3
|
+
# taken from https://code.videolan.org/videolan/vlc/-/blob/master/modules/control/dbus/dbus_introspect.h
|
|
4
|
+
mpris_introspection_vlc = dbus_introspection.Node.parse("""\
|
|
5
|
+
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
|
6
|
+
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
|
7
|
+
<node>
|
|
8
|
+
<interface name="org.freedesktop.DBus.Introspectable">
|
|
9
|
+
<method name="Introspect">
|
|
10
|
+
<arg name="data" direction="out" type="s"/>
|
|
11
|
+
</method>
|
|
12
|
+
</interface>
|
|
13
|
+
|
|
14
|
+
<interface name="org.freedesktop.DBus.Properties">
|
|
15
|
+
<method name="Get">
|
|
16
|
+
<arg direction="in" type="s"/>
|
|
17
|
+
<arg direction="in" type="s"/>
|
|
18
|
+
<arg direction="out" type="v"/>
|
|
19
|
+
</method>
|
|
20
|
+
<method name="Set">
|
|
21
|
+
<arg direction="in" type="s"/>
|
|
22
|
+
<arg direction="in" type="s"/>
|
|
23
|
+
<arg direction="in" type="v"/>
|
|
24
|
+
</method>
|
|
25
|
+
<method name="GetAll">
|
|
26
|
+
<arg direction="in" type="s"/>
|
|
27
|
+
<arg direction="out" type="a{sv}"/>
|
|
28
|
+
</method>
|
|
29
|
+
<signal name="PropertiesChanged">
|
|
30
|
+
<arg type="s"/>
|
|
31
|
+
<arg type="a{sv}"/>
|
|
32
|
+
<arg type="as"/>
|
|
33
|
+
</signal>
|
|
34
|
+
</interface>
|
|
35
|
+
|
|
36
|
+
<interface name="org.mpris.MediaPlayer2">
|
|
37
|
+
<property name="CanQuit" type="b" access="read"/>
|
|
38
|
+
<property name="Fullscreen" type="b" access="readwrite"/>
|
|
39
|
+
<property name="CanSetFullscreen" type="b" access="read"/>
|
|
40
|
+
<property name="CanRaise" type="b" access="read"/>
|
|
41
|
+
<property name="HasTrackList" type="b" access="read"/>
|
|
42
|
+
<property name="Identity" type="s" access="read"/>
|
|
43
|
+
<property name="DesktopEntry" type="s" access="read"/>
|
|
44
|
+
<property name="SupportedUriSchemes" type="as" access="read"/>
|
|
45
|
+
<property name="SupportedMimeTypes" type="as" access="read"/>
|
|
46
|
+
<method name="Raise"/>
|
|
47
|
+
<method name="Quit"/>
|
|
48
|
+
</interface>
|
|
49
|
+
|
|
50
|
+
<interface name="org.mpris.MediaPlayer2.Player">
|
|
51
|
+
<property name="PlaybackStatus" type="s" access="read"/>
|
|
52
|
+
<property name="LoopStatus" type="s" access="readwrite"/>
|
|
53
|
+
<property name="Rate" type="d" access="readwrite"/>
|
|
54
|
+
<property name="Shuffle" type="b" access="readwrite"/>
|
|
55
|
+
<property name="Metadata" type="a{sv}" access="read"/>
|
|
56
|
+
<property name="Volume" type="d" access="readwrite"/>
|
|
57
|
+
<property name="Position" type="x" access="read"/>
|
|
58
|
+
<property name="MinimumRate" type="d" access="read"/>
|
|
59
|
+
<property name="MaximumRate" type="d" access="read"/>
|
|
60
|
+
<property name="CanGoNext" type="b" access="read"/>
|
|
61
|
+
<property name="CanGoPrevious" type="b" access="read"/>
|
|
62
|
+
<property name="CanPlay" type="b" access="read"/>
|
|
63
|
+
<property name="CanPause" type="b" access="read"/>
|
|
64
|
+
<property name="CanSeek" type="b" access="read"/>
|
|
65
|
+
<property name="CanControl" type="b" access="read"/>
|
|
66
|
+
<method name="Next"/>
|
|
67
|
+
<method name="Previous"/>
|
|
68
|
+
<method name="Pause"/>
|
|
69
|
+
<method name="PlayPause"/>
|
|
70
|
+
<method name="Stop"/>
|
|
71
|
+
<method name="Play"/>
|
|
72
|
+
<method name="Seek">
|
|
73
|
+
<arg type="x" direction="in"/>
|
|
74
|
+
</method>
|
|
75
|
+
<method name="SetPosition">
|
|
76
|
+
<arg type="o" direction="in"/>
|
|
77
|
+
<arg type="x" direction="in"/>
|
|
78
|
+
</method>
|
|
79
|
+
<method name="OpenUri">
|
|
80
|
+
<arg type="s" direction="in"/>
|
|
81
|
+
</method>
|
|
82
|
+
<signal name="Seeked">
|
|
83
|
+
<arg type="x"/>
|
|
84
|
+
</signal>
|
|
85
|
+
</interface>
|
|
86
|
+
|
|
87
|
+
<interface name="org.mpris.MediaPlayer2.TrackList">
|
|
88
|
+
<property name="Tracks" type="ao" access="read"/>
|
|
89
|
+
<property name="CanEditTracks" type="b" access="read"/>
|
|
90
|
+
<method name="GetTracksMetadata">
|
|
91
|
+
<arg type="ao" direction="in"/>
|
|
92
|
+
<arg type="aa{sv}" direction="out"/>
|
|
93
|
+
</method>
|
|
94
|
+
<method name="AddTrack">
|
|
95
|
+
<arg type="s" direction="in"/>
|
|
96
|
+
<arg type="o" direction="in"/>
|
|
97
|
+
<arg type="b" direction="in"/>
|
|
98
|
+
</method>
|
|
99
|
+
<method name="RemoveTrack">
|
|
100
|
+
<arg type="o" direction="in"/>
|
|
101
|
+
</method>
|
|
102
|
+
<method name="GoTo">
|
|
103
|
+
<arg type="o" direction="in"/>
|
|
104
|
+
</method>
|
|
105
|
+
<signal name="TrackListReplaced">
|
|
106
|
+
<arg type="ao"/>
|
|
107
|
+
<arg type="o"/>
|
|
108
|
+
</signal>
|
|
109
|
+
<signal name="TrackAdded">
|
|
110
|
+
<arg type="a{sv}"/>
|
|
111
|
+
<arg type="o"/>
|
|
112
|
+
</signal>
|
|
113
|
+
<signal name="TrackRemoved">
|
|
114
|
+
<arg type="o"/>
|
|
115
|
+
</signal>
|
|
116
|
+
<signal name="TrackMetadataChanged">
|
|
117
|
+
<arg type="o"/>
|
|
118
|
+
<arg type="a{sv}"/>
|
|
119
|
+
</signal>
|
|
120
|
+
</interface>
|
|
121
|
+
</node>
|
|
122
|
+
""")
|