Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

129 setting properties #130

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
143 changes: 143 additions & 0 deletions examples/client-properties.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
'use strict';

const dbus = require ('../index.js')

/*
This example shows how to query a DBus service for its properties (no methods nor signals here), get their value (
and display them) and then change them.
Since we're acting only as a client, there is not need to request a name: we don't need to register a service
against the bus (but we could!), so we only act as a client.

NOTE: this file is made to query the service that is exposed in the file 'server-properties.js', so be sure to
start it first (node server-properties.js in another terminal should be enough)
)
*/

// This is the DBus service we will query (server-properties.js)
const targetServiceName = 'com.dbus.native.properties'

// This is the service's interface we will query
const targetIfaceName = targetServiceName // note that is it equal to the service name, but this is not mandatory at all

// This is the service's DBus object path that we will query for the properties
const targetObjectPath = '/' + targetServiceName.replace (/\./g, '/')

// First, connect to the session bus (works the same on the system bus, it's just less permissive)
const sessionBus = dbus.sessionBus()

// Check the connection was successful
if (!sessionBus) {
throw new Error ('Could not connect to the DBus session bus.')
}

// First, we must query the bus for the desired DBus service:
let targetService = sessionBus.getService (targetServiceName)

// Then we must query it's interface, this is callback-based
targetService.getInterface (targetObjectPath, targetIfaceName, (e, iface) => {
// we need to check for error
if (e || !iface) {
console.error ('Could not query interface \'' + targetIfaceName + '\', the error was: ' + err ? err : '(no error)')
process.exit (1)
}

/*
Now, the service's object's interface is represented in 'iface'.
Properties are accessed via callback (so it can be a bit verbose)
*/
iface.SingleString ((e, propValue) => {
// Be careful not to check for `! propValue` because, what if propValue is a boolean whose value is false?!
if (e || typeof propValue === 'undefined') {
console.error ('Could not get propery \'SingleString\', the error was: ' + err ? err : '(no error)')
process.exit (1)
}

// now it's safe: we can display the value
console.log ('SingleString: ' + propValue)

/*
Move to the next example (you can comment the line if you want to go step-by-step)
Also, since this is all callback-based, I'm nesting the 'stepX' calls so that what is displayed on your
console is in the same order as the calls here. But in YOUR applications you can do otherwise of course.
*/
step1()
})

// Show how to get and change the value of 'SingleInt32'
function step1() {
iface.SingleInt32 ((e, propValue) => {
// Be careful not to check for `! propValue` because, what if propValue is a boolean whose value is false?!
if (e || typeof propValue === 'undefined') {
console.error ('Could not get propery \'SingleInt32\', the error was: ' + err ? err : '(no error)')
process.exit (1)
}

console.log ('SingleInt32 (before change): ' + propValue)

// Changing a property value is a simple matter of assignment:
iface.SingleInt32 = 33

/*
Let's display it again (you will notice that this callback-based accessor is verbose, I advise to make
a helper that automatically checks for error)
*/
iface.SingleInt32 ((e, propValue) => {
// Be careful not to check for `! propValue` because, what if propValue is a boolean whose value is false?!
if (e || typeof propValue === 'undefined') {
console.error ('Could not get propery \'SingleInt32\', the error was: ' + err ? err : '(no error)')
process.exit (1)
}

console.log ('SingleInt32 (after change): ' + propValue)

/*
Move to the next example (you can comment the line if you want to go step-by-step)
Also, since this is all callback-based, I'm nesting the 'stepX' calls so that what is displayed on your
console is in the same order as the calls here. But in YOUR applications you can do otherwise of course.
*/
step2()
})
})
}

function step2() {
iface.ArrayOfUint16 ((e, propValue) => {
// Be careful not to check for `! propValue` because, what if propValue is a boolean whose value is false?!
if (e || typeof propValue === 'undefined') {
console.error ('Could not get propery \'ArrayOfUint16\', the error was: ' + err ? err : '(no error)')
process.exit (1)
}

console.log ('ArrayOfUint16 (before change): ' + propValue)

/*
Remember our typing convention here: since an array is a "complex / container" type, it must be enclosed
in brackets; that's the first (outer) pair. Then, the second (inner) pair of brackets is the actual
array.
Please see comments in 'service-properties.js' for more information on this.
*/
iface.ArrayOfUint16 = [[20,21,21,22]]

/*
Let's display it again (you will notice that this callback-based accessor is verbose, I advise to make
a helper that automatically checks for error)
*/
iface.ArrayOfUint16 ((e, propValue) => {
// Be careful not to check for `! propValue` because, what if propValue is a boolean whose value is false?!
if (e || typeof propValue === 'undefined') {
console.error ('Could not get propery \'ArrayOfUint16\', the error was: ' + err ? err : '(no error)')
process.exit (1)
}

console.log ('ArrayOfUint16 (after change): ' + propValue)

/*
Move to the next example (you can comment the line if you want to go step-by-step)
Also, since this is all callback-based, I'm nesting the 'stepX' calls so that what is displayed on your
console is in the same order as the calls here. But in YOUR applications you can do otherwise of course.
*/

})
})
}
})
190 changes: 190 additions & 0 deletions examples/return-types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
'use strict';

const dbus = require ('../index.js')

/*
This test file's purpose is to show example of possible return types for functions.
In order to do that, we connect to the session bus and create a DBus service exposing
a certain number of function calls (no signals nor properties) that you can call with
any DBus-speaking software.

For instance you can use `gdbus` to introspect a service and make function calls.
- introspect: `gdbus introspect -e -d com.dbus.native.return.types -o /com/dbus/native/return/types`
- make a method call: `gdbus introspect -e -d com.dbus.native.return.types -o /com/dbus/native/return/types -m com.dbus.native.return.types.FunctionName`
*/

const serviceName = 'com.dbus.native.return.types' // our DBus service name
/*
The interface under which we will expose our functions (chose to be the same as the service name, but we can
choose whatever name we want, provided it respects the rules, see DBus naming documentation)
*/
const interfaceName = serviceName
/*
The object pat hthat we want to expose on the bus. Here we chose to have the same path as the service (and
interface) name, with the dots replaced by slashes (because objects path must be on the form of UNIX paths)
But again, we could chose anything. This is just a demo here.
*/
const objectPath = '/' + serviceName.replace (/\./g, '/')

// First, connect to the session bus (works the same on the system bus, it's just less permissive)
const sessionBus = dbus.sessionBus()

// Check the connection was successful
if (!sessionBus) {
throw new Error ('Could not connect to the DBus session bus.')
}

/*
Then request our service name to the bus.
The 0x4 flag means that we don't want to be queued if the service name we are requesting is already
owned by another service ;we want to fail instead.
*/
sessionBus.requestName (serviceName, 0x4, (e, retCode) => {
// If there was an error, warn user and fail
if (e) {
throw new Error (`Could not request service name ${serviceName}, the error was: ${e}.`)
}

// Return code 0x1 means we successfully had the name
if (retCode === 1) {
console.log (`Successfully requested service name "${serviceName}"!`)
proceed()
}
/* Other return codes means various errors, check here
(https://dbus.freedesktop.org/doc/api/html/group__DBusShared.html#ga37a9bc7c6eb11d212bf8d5e5ff3b50f9) for more
information
*/
else {
throw new Error (`Failed to request service name "${serviceName}". Check what return code "${retCode}" means.`)
}
})

// Function called when we have successfully got the service name we wanted
function proceed() {
let ifaceDesc
let iface

// First, we need to create our interface description (here we will only expose method calls)
ifaceDesc = {
name: interfaceName,
methods: {
// Simple types
SayHello: ['', 's', [], ['hello_sentence']], // Takes no input and returns a single string
GetInt16: ['', 'n', [], ['Int16_number']], // Takes no input and returns an int16 integers
GetUInt16: ['', 'q', [], ['UInt16_number']], // Takes no input and returns an uint16 integers
GetInt32: ['', 'i', [], ['Int32_number']], // Takes no input, returns an int32 integer
GetUInt32: ['', 'u', [], ['UInt32_number']], // Takes no input, returns an uint32 integer
// 64 numbers being not handled natively in Javascript, they are not yet handled by this library (WIP)
//GetInt64: ['', 'x', [], ['Int32_number']], // Takes no input, returns an int64 integer
//GetUInt64: ['', 't', [], ['UInt32_number']], // Takes no input, returns an uint64 integer
GetBool: ['', 'b', [], ['Bool_value']], // Takes no input, returns a boolean
GetDouble: ['', 'd', [], ['Double_value']], // Takes no input, returns a double
GetByte: ['', 'y', [], ['Byte_value']], // Takes no input, returns a byte

// Complex-types
GetArrayOfStrings: ['y', 'as', ['nb_elems'], ['strings']], // Take a number and return an array of N strings
// Takes no input, returns a structure with a string, an int32 and a bool
GetCustomStruct: ['', '(sib)', [], ['struct']],
// Takes no input, returns a dictionary (hash-table) whose keys are strings and values int32
GetDictEntry: ['', 'a{si}', [], ['dict_entry']],
},
// No signals nor properties for this example
signals: {},
properties: {}
}

// Then we need to create the interface implementation (with actual functions)
iface = {
SayHello: function() {
return 'Hello, world!' // This is how to return a single string
},
GetInt16: function() {
let min = -0x7FFF-1
let max = 0x7FFF
return Math.round (Math.random() * (max - min) + min)
},
GetUInt16: function() {
let min = 0
let max = 0xFFFF
return Math.round (Math.random() * (max - min) + min)
},
GetInt32: function() {
let min = -0x7FFFFFFF-1
let max = 0x7FFFFFFF
return Math.round (Math.random() * (max - min) + min)
},
GetUInt32: function() {
let min = 0
let max = 0xFFFFFFFF
return Math.round (Math.random() * (max - min) + min)
},
GetBool: function() {
return Math.random() >= 0.5 ? true : false
},
GetDouble: function() {
/*
We are only returning a number between 0 and 1 here, but this is just for the test.
Javascript can handle number between Number.MIN_VALUE and Number.MAX_VALUE, which are 5e-234 and 1.7976931348623157e+308 respectively.
There would be no point in returing such big numbers for this demo, but this is perfectly okay with DBus.
*/
return Math.random()
},
GetByte: function() {
let min = 0x00
let max = 0xFF
return Math.round (Math.random() * (max - min) + min)
},
GetArrayOfStrings: function (n) {
let ret = []

// Check that we requested a positive number of elements, and not a too big one
if (n < 0 || n > 255) {
// Return a DBus error to indicate a problem (shows how to send DBus errors)
return new Error ('Incorrect number of elements supplied (0 < n < 256)!')
}

while (n--) {
ret.unshift ('String #' + n)
}

return ret // 'ret' is an array, to return an array, we simply return it
},
GetCustomStruct: function () {
let min = -0x7FFFFFFF-1
let max = 0x7FFFFFFF
let string = 'I\m sorry, my responses are limited, you must ask the right question.'
let int32 = Math.round (Math.random() * (max - min) + min)
let bool = Math.random() >= 0.5 ? true : false

/*
Important note here: for the DBus type STRUCT, you need to return a Javascript ARRAY, with the field in
the right order for the declared struct.
*/
return [string, int32, bool]
},
GetDictEntry: function () {
let min = -0x7FFFFFFF-1
let max = 0x7FFFFFFF
let key1 = 'str1'
let key2 = 'str2'
let key3 = 'str3'
let i1 = Math.round (Math.random() * (max - min) + min)
let i2 = Math.round (Math.random() * (max - min) + min)
let i3 = Math.round (Math.random() * (max - min) + min)

/*
This is how DICT_ENTRIES are returned: in JS side, it's an array of arrays.
Each of the arrays must have TWO values, the first being the key (here a string ; keys
MUST be single types, so string, integers, double, booleans, etc.) and the second being
the value (here, an int32 ; keys can be any type, including complex one: struct, etc.)
*/
return [[key1, i1], [key2, i2], [key3, i3]]
}
}

// Now we need to actually export our interface on our object
sessionBus.exportInterface (iface, objectPath, ifaceDesc)

// Say our service is ready to receive function calls (you can use `gdbus call` to make function calls)
console.log ('Interface exposed to DBus, ready to receive function calls!')
}
Loading