@homebridge/dbus-native 0.4.0

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.
package/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ This software is released under the MIT license:
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,124 @@
1
+ node-dbus
2
+ ===========
3
+
4
+ [![Greenkeeper badge](https://badges.greenkeeper.io/sidorares/dbus-native.svg)](https://greenkeeper.io/)
5
+ D-bus protocol client and server for node.js
6
+
7
+ [![Build Status](https://secure.travis-ci.org/sidorares/dbus-native.png)](http://travis-ci.org/sidorares/dbus-native)
8
+
9
+ Installation
10
+ ------------
11
+
12
+ ```shell
13
+ npm install dbus-native
14
+ ```
15
+ or
16
+
17
+ ```shell
18
+ git clone https://github.com/sidorares/node-dbus # clone the repo
19
+ cd node-dbus
20
+ npm install # install dependencies
21
+ sudo cp examples/com.github.sidorares.dbus.Example.conf /etc/dbus-1/system.d/ # if you want to test examples/service.js
22
+ ```
23
+
24
+ Usage
25
+ ------
26
+
27
+ Short example using desktop notifications service
28
+
29
+ ```js
30
+ var dbus = require('dbus-native');
31
+ var sessionBus = dbus.sessionBus();
32
+ sessionBus.getService('org.freedesktop.Notifications').getInterface(
33
+ '/org/freedesktop/Notifications',
34
+ 'org.freedesktop.Notifications', function(err, notifications) {
35
+
36
+ // dbus signals are EventEmitter events
37
+ notifications.on('ActionInvoked', function() {
38
+ console.log('ActionInvoked', arguments);
39
+ });
40
+ notifications.on('NotificationClosed', function() {
41
+ console.log('NotificationClosed', arguments);
42
+ });
43
+ notifications.Notify('exampl', 0, '', 'summary 3', 'new message text', ['xxx yyy', 'test2', 'test3', 'test4'], [], 5, function(err, id) {
44
+ //setTimeout(function() { n.CloseNotification(id, console.log); }, 4000);
45
+ });
46
+ });
47
+ ```
48
+
49
+ API
50
+ ---
51
+
52
+ ### Low level messaging: bus connection
53
+
54
+ `connection = dbus.createClient(options)`
55
+
56
+ options:
57
+ - socket - unix socket path
58
+ - port - TCP port
59
+ - host - TCP host
60
+ - busAddress - encoded bus address. Default is `DBUS_SESSION_BUS_ADDRESS` environment variable. See http://dbus.freedesktop.org/doc/dbus-specification.html#addresses
61
+ - authMethods - array of authentication methods, which are attempted in the order provided (default:['EXTERNAL', 'DBUS_COOKIE_SHA1', 'ANONYMOUS'])
62
+ - ayBuffer - boolean (default:true): if true 'ay' dbus fields are returned as buffers
63
+ - ReturnLongjs - boolean (default:false): if true 64 bit dbus fields (x/t) are read out as Long.js objects, otherwise they are converted to numbers (which should be good up to 53 bits)
64
+ - ( TODO: add/document option to use adress from X11 session )
65
+
66
+ connection has only one method, `message(msg)`
67
+
68
+ message fields:
69
+ - type - methodCall, methodReturn, error or signal
70
+ - path - object path
71
+ - interface
72
+ - destination
73
+ - sender
74
+ - member
75
+ - serial
76
+ - signature
77
+ - body
78
+ - errorName
79
+ - replySerial
80
+
81
+ connection signals:
82
+ - connect - emitted after successful authentication
83
+ - message
84
+ - error
85
+
86
+ example:
87
+
88
+ ```js
89
+ var dbus = require('dbus-native');
90
+ var conn = dbus.createConnection();
91
+ conn.message({
92
+ path:'/org/freedesktop/DBus',
93
+ destination: 'org.freedesktop.DBus',
94
+ 'interface': 'org.freedesktop.DBus',
95
+ member: 'Hello',
96
+ type: dbus.messageType.methodCall
97
+ });
98
+ conn.on('message', function(msg) { console.log(msg); });
99
+ ```
100
+
101
+ ### Note on INT64 'x' and UINT64 't'
102
+ Long.js is used for 64 Bit support. https://github.com/dcodeIO/long.js
103
+ The following javascript types can be marshalled into 64 bit dbus fields:
104
+ - typeof 'number' up to 53bits
105
+ - typeof 'string' (consisting of decimal digits with no separators or '0x' prefixed hexadecimal) up to full 64bit range
106
+ - Long.js objects (or object with compatible properties)
107
+
108
+ By default 64 bit dbus fields are unmarshalled into a 'number' (with precision loss beyond 53 bits). Use {ReturnLongjs:true} option to return the actual Long.js object and preserve the entire 64 bits.
109
+
110
+ ### Links
111
+ - http://cgit.freedesktop.org/dbus - freedesktop reference C library
112
+ - https://github.com/guelfey/go.dbus
113
+ - https://github.com/Shouqun/node-dbus - libdbus
114
+ - https://github.com/Motorola-Mobility/node-dbus - libdbus
115
+ - https://github.com/izaakschroeder/node-dbus - libdbus
116
+ - https://github.com/agnat/node_libdbus
117
+ - https://github.com/agnat/node_dbus - native js
118
+ - https://github.com/cocagne/txdbus - native python + twisted
119
+ - http://search.cpan.org/~danberr/Net-DBus-1.0.0/ (seems to be native, but requires libdbus?)
120
+ - https://github.com/mvidner/ruby-dbus (native, sync)
121
+ - http://www.ndesk.org/DBusSharp (C#/Mono)
122
+ - https://github.com/lizenn/erlang-dbus/ - erlang
123
+ - https://github.com/mspanc/dbux/ - elixir
124
+ - http://0pointer.net/blog/the-new-sd-bus-api-of-systemd.html - Blog post about sb-bus and D-Bus in general
package/bin/dbus2js.js ADDED
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const xml2js = require('xml2js');
5
+ const xml2js_opts = Object.assign({}, xml2js.defaults["0.1"], { explicitArray: true });
6
+ const dbus = require('../index');
7
+ const optimist = require('optimist');
8
+
9
+ var argv = optimist.boolean(['server', 'dump']).argv;
10
+
11
+ function die(err) {
12
+ console.log(err);
13
+ process.exit(-1);
14
+ }
15
+
16
+ var bus = argv.bus === 'system' ? dbus.systemBus() : dbus.sessionBus();
17
+
18
+ function getXML(callback) {
19
+ if (argv.xml) {
20
+ fs.readFile(argv.xml, 'ascii', callback);
21
+ } else {
22
+ bus.invoke(
23
+ {
24
+ destination: argv.service,
25
+ path: argv.path,
26
+ interface: 'org.freedesktop.DBus.Introspectable',
27
+ member: 'Introspect'
28
+ },
29
+ callback
30
+ );
31
+ }
32
+ }
33
+
34
+ if (argv.dump) {
35
+ getXML(function(err, xml) {
36
+ console.log(xml);
37
+ bus.connection.end();
38
+ });
39
+ }
40
+
41
+ if (!argv.server) {
42
+ getXML(function(err, xml) {
43
+ if (err) die(err);
44
+
45
+ var output = [];
46
+
47
+ var parser = new xml2js.Parser(xml2js_opts);
48
+ parser.parseString(xml, function(err, result) {
49
+ if (err) die(err);
50
+
51
+ var ifaceName, method, property, iface, arg, signature;
52
+ var ifaces = result['interface'];
53
+ for (var i = 0; i < ifaces.length; ++i) {
54
+ iface = ifaces[i];
55
+ ifaceName = iface['@'].name;
56
+
57
+ output.push(`module.exports['${ifaceName}'] = function(bus) {`);
58
+ output.push(
59
+ ' this.addListener = this.on = function(signame, callback) {'
60
+ );
61
+ //TODO: add path and interface to path
62
+ output.push(
63
+ " bus.addMatch('type=\\'signal\\',member=\\'' + signame + '\\'', function(err, result) {"
64
+ );
65
+ output.push(' if (err) throw new Error(err);');
66
+ output.push(' });');
67
+ output.push(
68
+ ` var signalFullName = bus.mangle('${argv.path}', '${
69
+ ifaceName
70
+ }', signame);`
71
+ );
72
+ output.push(
73
+ ' bus.signals.on(signalFullName, function(messageBody) {'
74
+ );
75
+ output.push(' callback.apply(null, messageBody);');
76
+ output.push(' });');
77
+ output.push(' };');
78
+
79
+ for (var m = 0; iface.method && m < iface.method.length; ++m) {
80
+ method = iface.method[m];
81
+ signature = '';
82
+ const methodName = method['@'].name;
83
+
84
+ var decl = ` this.${methodName} = function(`;
85
+ var params = [];
86
+ for (var a = 0; method.arg && a < method.arg.length; ++a) {
87
+ arg = method.arg[a]['@'];
88
+ if (arg.direction === 'in') {
89
+ decl += `${arg.name}, `;
90
+ params.push(arg.name);
91
+ signature += arg.type;
92
+ }
93
+ }
94
+ decl += 'callback) {';
95
+ output.push(decl);
96
+ output.push(' bus.invoke({');
97
+ output.push(` destination: '${argv.service}',`);
98
+ output.push(` path: '${argv.path}',`);
99
+ output.push(` interface: '${ifaceName}',`);
100
+ output.push(` member: '${methodName}',`);
101
+ if (params.length > 0) {
102
+ output.push(` body: [${params.join(', ')}], `);
103
+ output.push(` signature: '${signature}',`);
104
+ }
105
+ output.push(' }, callback);');
106
+ output.push(' };');
107
+ }
108
+ for (var p = 0; iface.property && p < iface.property.length; ++p) {
109
+ property = iface.property[p];
110
+ console.log(' property: \n', property);
111
+ }
112
+ output.push('}');
113
+ }
114
+ console.log(output.join('\n'));
115
+ bus.connection.end();
116
+ });
117
+ });
118
+ }
package/index.js ADDED
@@ -0,0 +1,155 @@
1
+ // dbus.freedesktop.org/doc/dbus-specification.html
2
+
3
+ const EventEmitter = require('events').EventEmitter;
4
+ const net = require('net');
5
+
6
+ const constants = require('./lib/constants');
7
+ const message = require('./lib/message');
8
+ const clientHandshake = require('./lib/handshake');
9
+ const serverHandshake = require('./lib/server-handshake');
10
+ const MessageBus = require('./lib/bus');
11
+ const server = require('./lib/server');
12
+
13
+ function createStream(opts) {
14
+ if (opts.stream) return opts.stream;
15
+ var host = opts.host;
16
+ var port = opts.port;
17
+ var socket = opts.socket;
18
+ if (socket) return net.createConnection(socket);
19
+ if (port) return net.createConnection(port, host);
20
+
21
+ var busAddress = opts.busAddress || process.env.DBUS_SESSION_BUS_ADDRESS;
22
+ if (!busAddress) throw new Error('unknown bus address');
23
+
24
+ var addresses = busAddress.split(';');
25
+ for (var i = 0; i < addresses.length; ++i) {
26
+ var address = addresses[i];
27
+ var familyParams = address.split(':');
28
+ var family = familyParams[0];
29
+ var params = {};
30
+ familyParams[1].split(',').map(function(p) {
31
+ var keyVal = p.split('=');
32
+ params[keyVal[0]] = keyVal[1];
33
+ });
34
+
35
+ try {
36
+ switch (family.toLowerCase()) {
37
+ case 'tcp':
38
+ host = params.host || 'localhost';
39
+ port = params.port;
40
+ return net.createConnection(port, host);
41
+ case 'unix':
42
+ if (params.socket) return net.createConnection(params.socket);
43
+ if (params.abstract) {
44
+ var abs = require('abstract-socket');
45
+ return abs.connect('\u0000' + params.abstract);
46
+ }
47
+ if (params.path) return net.createConnection(params.path);
48
+ throw new Error(
49
+ "not enough parameters for 'unix' connection - you need to specify 'socket' or 'abstract' or 'path' parameter"
50
+ );
51
+ case 'unixexec':
52
+ var eventStream = require('event-stream');
53
+ var spawn = require('child_process').spawn;
54
+ var args = [];
55
+ for (var n = 1; params['arg' + n]; n++) args.push(params['arg' + n]);
56
+ var child = spawn(params.path, args);
57
+
58
+ return eventStream.duplex(child.stdin, child.stdout);
59
+ default:
60
+ throw new Error('unknown address type:' + family);
61
+ }
62
+ } catch (e) {
63
+ if (i < addresses.length - 1) {
64
+ console.warn(e.message);
65
+ continue;
66
+ } else {
67
+ throw e;
68
+ }
69
+ }
70
+ }
71
+ }
72
+
73
+ function createConnection(opts) {
74
+ var self = new EventEmitter();
75
+ if (!opts) opts = {};
76
+ var stream = (self.stream = createStream(opts));
77
+ stream.setNoDelay();
78
+
79
+ stream.on('error', function(err) {
80
+ // forward network and stream errors
81
+ self.emit('error', err);
82
+ });
83
+
84
+ stream.on('end', function() {
85
+ self.emit('end');
86
+ self.message = function() {
87
+ console.warn("Didn't write bytes to closed stream");
88
+ };
89
+ });
90
+
91
+ self.end = function() {
92
+ stream.end();
93
+ return self;
94
+ };
95
+
96
+ var handshake = opts.server ? serverHandshake : clientHandshake;
97
+ handshake(stream, opts, function(error, guid) {
98
+ if (error) {
99
+ return self.emit('error', error);
100
+ }
101
+ self.guid = guid;
102
+ self.emit('connect');
103
+ message.unmarshalMessages(
104
+ stream,
105
+ function(message) {
106
+ self.emit('message', message);
107
+ },
108
+ opts
109
+ );
110
+ });
111
+
112
+ self._messages = [];
113
+
114
+ // pre-connect version, buffers all messages. replaced after connect
115
+ self.message = function(msg) {
116
+ self._messages.push(msg);
117
+ };
118
+
119
+ self.once('connect', function() {
120
+ self.state = 'connected';
121
+ for (var i = 0; i < self._messages.length; ++i) {
122
+ stream.write(message.marshall(self._messages[i]));
123
+ }
124
+ self._messages.length = 0;
125
+
126
+ // no need to buffer once connected
127
+ self.message = function(msg) {
128
+ stream.write(message.marshall(msg));
129
+ };
130
+ });
131
+
132
+ return self;
133
+ }
134
+
135
+ module.exports.createClient = function(params) {
136
+ var connection = createConnection(params || {});
137
+ return new MessageBus(connection, params || {});
138
+ };
139
+
140
+ module.exports.systemBus = function() {
141
+ return module.exports.createClient({
142
+ busAddress:
143
+ process.env.DBUS_SYSTEM_BUS_ADDRESS ||
144
+ 'unix:path=/var/run/dbus/system_bus_socket'
145
+ });
146
+ };
147
+
148
+ module.exports.sessionBus = function(opts) {
149
+ return module.exports.createClient(opts);
150
+ };
151
+
152
+ module.exports.messageType = constants.messageType;
153
+ module.exports.createConnection = createConnection;
154
+
155
+ module.exports.createServer = server.createServer;
@@ -0,0 +1,38 @@
1
+ // read dbus adress from window selection
2
+
3
+ const x11 = require('x11');
4
+ const fs = require('fs');
5
+ const os = require('os');
6
+
7
+ function getDbusAddress(callback) {
8
+ // read machine uuid
9
+ fs.readFile('/var/lib/dbus/machine-id', 'ascii', function(err, uuid) {
10
+ if (err) return callback(err);
11
+ var hostname = os.hostname().split('-')[0];
12
+ x11.createClient(function(err, display) {
13
+ var X = display.client;
14
+ var selectionName = `_DBUS_SESSION_BUS_SELECTION_${
15
+ hostname
16
+ }_${uuid.trim()}`;
17
+ X.InternAtom(false, selectionName, function(err, id) {
18
+ if (err) return callback(err);
19
+ X.GetSelectionOwner(id, function(err, win) {
20
+ if (err) return callback(err);
21
+ X.InternAtom(false, '_DBUS_SESSION_BUS_ADDRESS', function(
22
+ err,
23
+ propId
24
+ ) {
25
+ if (err) return callback(err);
26
+ win = display.screen[0].root;
27
+ X.GetProperty(0, win, propId, 0, 0, 10000000, function(err, val) {
28
+ if (err) return callback(err);
29
+ callback(null, val.data.toString());
30
+ });
31
+ });
32
+ });
33
+ });
34
+ });
35
+ });
36
+ }
37
+
38
+ module.exports = getDbusAddress;
package/lib/align.js ADDED
@@ -0,0 +1,12 @@
1
+ const Buffer = require('safe-buffer').Buffer;
2
+
3
+ function align(ps, n) {
4
+ var pad = n - ps._offset % n;
5
+ if (pad === 0 || pad === n) return;
6
+ // TODO: write8(0) in a loop (3 to 7 times here) could be more efficient
7
+ var padBuff = Buffer.alloc(pad);
8
+ ps.put(Buffer.from(padBuff));
9
+ ps._offset += pad;
10
+ }
11
+
12
+ exports.align = align;