1<?xml version="1.0"?>
2<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
3
4<chapter id="server-tutorial" xmlns:xi="http://www.w3.org/2001/XInclude">
5  <title>Writing a UPnP Service</title>
6
7  <simplesect>
8    <title>Introduction</title>
9    <para>
10      This chapter explains how to implement a UPnP service using GUPnP. For
11      this example we will create a virtual UPnP-enabled light bulb.
12    </para>
13    <para>
14      Before any code can be written, the device and services that it implement
15      need to be described in XML.  Although this can be frustrating, if you are
16      implementing a standardised service (see <ulink
17      url="http://upnp.org/sdcps-and-certification/standards/sdcps/"/> for the
18      list of standard devices and services) then the service description is
19      already written for you and the device description is trivial.  UPnP has
20      standardised <ulink url="http://upnp.org/specs/ha/lighting/">Lighting
21      Controls</ulink>, so we'll be using the device and service types defined
22      there.
23    </para>
24  </simplesect>
25
26  <simplesect>
27    <title>Defining the Device</title>
28    <para>
29      The first step is to write the <firstterm>device description</firstterm>
30      file.  This is a short XML document which describes the device and what
31      services it provides (for more details see the <ulink
32      url="http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.0.pdf">UPnP
33      Device Architecture</ulink> specification, section 2.1).  We'll be using
34      the <literal>BinaryLight1</literal> device type, but if none of the
35      existing device types are suitable a custom device type can be created.
36    </para>
37    <programlisting><xi:include href="../examples/BinaryLight1.xml" parse="text"><xi:fallback /></xi:include></programlisting>
38    <para>
39      The <sgmltag>specVersion</sgmltag> tag defines what version of the UPnP
40      Device Architecture the document conforms to.  At the time of writing the
41      only version is 1.0.
42    </para>
43    <para>
44      Next there is the root <sgmltag>device</sgmltag> tag.  This contains
45      metadata about the device, lists the services it provides and any
46      sub-devices present (there are none in this example).  The
47      <sgmltag>deviceType</sgmltag> tag specifies the type of the device.
48    </para>
49    <para>
50      Next we have <sgmltag>friendlyName</sgmltag>,
51      <sgmltag>manufacturer</sgmltag> and <sgmltag>modelName</sgmltag>.  The
52      friendly name is a human-readable name for the device, the manufacturer
53      and model name are self-explanatory.
54    </para>
55    <para>
56      Next there is the UDN, or <firstterm>Unique Device Name</firstterm>.  This
57      is an identifier which is unique for each device but persistent for each
58      particular device.  Although it has to start with <literal>uuid:</literal>
59      note that it doesn't have to be an UUID.  There are several alternatives
60      here: for example it could be computed at built-time if the software will
61      only be used on a single machine, or it could be calculated using the
62      device's serial number or MAC address.
63    </para>
64    <para>
65      Finally we have the <sgmltag>serviceList</sgmltag> which describes the
66      services this device provides.  Each service has a service type (again
67      there are types defined for standardised services or you can create your
68      own), service identifier, and three URLs.  As a service type we're using
69      the standard <literal>SwitchPower1</literal> service.  The
70      <sgmltag>SCPDURL</sgmltag> field specifies where the <firstterm>Service
71      Control Protocol Document</firstterm> can be found, this describes the
72      service in more detail and will be covered next.  Finally there are the
73      control and event URLs, which need to be unique on the device and will be
74      managed by GUPnP.
75    </para>
76  </simplesect>
77
78  <simplesect>
79    <title>Defining Services</title>
80    <para>
81      Because we are using a standard service we can use the service description
82      from the specification.  This is the <literal>SwitchPower1</literal>
83      service description file:
84    </para>
85    <programlisting><xi:include href="../examples/SwitchPower1.xml" parse="text"><xi:fallback /></xi:include></programlisting>
86    <para>
87      Again, the <sgmltag>specVersion</sgmltag> tag defines the UPnP version
88      that is being used.  The rest of the document consists of an
89      <sgmltag>actionList</sgmltag> defining the <glossterm
90      linkend="action">actions</glossterm> available and a
91      <sgmltag>serviceStateTable</sgmltag> defining the <glossterm
92      linkend="state-variable">state variables</glossterm>.
93    </para>
94    <para>
95      Every <sgmltag>action</sgmltag> has a <sgmltag>name</sgmltag> and a list
96      of <sgmltag>argument</sgmltag>s.  Arguments also have a name, a direction
97      (<literal>in</literal> or <literal>out</literal> for input or output
98      arguments) and a related state variable.  The state variable is used to
99      determine the type of the argument, and as such is a required element.
100      This can lead to the creation of otherwise unused state variables to
101      define the type for an argument (the <literal>WANIPConnection</literal>
102      service is a good example of this), thanks to the legacy behind UPnP.
103    </para>
104    <para>
105      <sgmltag>stateVariable</sgmltag>s need to specify their
106      <sgmltag>name</sgmltag> and <sgmltag>dataType</sgmltag>.  State variables
107      by default send notifications when they change, to specify that a variable
108      doesn't do this set the <sgmltag>sendEvents</sgmltag> attribute to
109      <literal>no</literal>.  Finally there are optional
110      <sgmltag>defaultValue</sgmltag>, <sgmltag>allowedValueList</sgmltag> and
111      <sgmltag>allowedValueRange</sgmltag> elements which specify what the
112      default and valid values for the variable.
113    </para>
114    <para>
115      For the full specification of the service definition file, including a
116      complete list of valid <sgmltag>dataType</sgmltag>s, see section 2.3 of
117      the <ulink
118      url="http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.0.pdf">UPnP
119      Device Architecture</ulink>.
120    </para>
121  </simplesect>
122
123  <simplesect>
124    <title>Implementing the Device</title>
125    <para>
126      Before starting to implement the device, some boilerplate code is needed
127      to initialise GUPnP.  GLib types and threading needs to be initialised,
128      and then a GUPnP context can be created using gupnp_context_new().
129    </para>
130    <programlisting>GUPnPContext *context;
131/* Initialize required subsystems */
132#if !GLIB_CHECK_VERSION(2,35,0)
133  g_type_init ();
134#endif
135/* Create the GUPnP context with default host and port */
136context = gupnp_context_new (NULL, NULL, 0, NULL);</programlisting>
137    <para>
138      Next the root device can be created. The name of the device description
139      file can be passed as an absolute file path or a relative path to the
140      second parameter of gupnp_root_device_new(). The service description
141      files referenced in the device description are expected to be at the path
142      given there as well.
143    </para>
144    <programlisting>GUPnPRootDevice *dev;
145/* Create the root device object */
146dev = gupnp_root_device_new (context, "BinaryLight1.xml", ".");
147/* Activate the root device, so that it announces itself */
148gupnp_root_device_set_available (dev, TRUE);</programlisting>
149    <para>
150      GUPnP scans the device description and any service description files it
151      refers to, so if the main loop was entered now the device and service
152      would be available on the network, albeit with no functionality.  The
153      remaining task is to implement the services.
154    </para>
155  </simplesect>
156
157  <simplesect>
158    <title>Implementing a Service</title>
159    <para>
160      To implement a service we first fetch the #GUPnPService from the root
161      device using gupnp_device_info_get_service() (#GUPnPRootDevice is a
162      subclass of #GUPnPDevice, which implements #GUPnPDeviceInfo).  This
163      returns a #GUPnPServiceInfo which again is an interface, implemented by
164      #GUPnPService (on the server) and #GUPnPServiceProxy (on the client).
165    </para>
166    <programlisting>GUPnPServiceInfo *service;
167service = gupnp_device_info_get_service
168  (GUPNP_DEVICE_INFO (dev), "urn:schemas-upnp-org:service:SwitchPower:1");</programlisting>
169    <para>
170      #GUPnPService handles interacting with the network itself, leaving the
171      implementation of the service itself to signal handlers that we need to
172      connect.  There are two signals: #GUPnPService::action-invoked and
173      #GUPnPService::query-variable.  #GUPnPService::action-invoked is emitted
174      when a client invokes an action: the handler is passed a
175      #GUPnPServiceAction object that identifies which action was invoked, and
176      is used to return values using gupnp_service_action_set().
177      #GUPnPService::query-variable is emitted for evented variables when a
178      control point subscribes to the service (to announce the initial value),
179      or whenever a client queries the value of a state variable (note that this
180      is now deprecated behaviour for UPnP control points): the handler is
181      passed the variable name and a #GValue which should be set to the current
182      value of the variable.
183    </para>
184    <para>
185      Handlers should be targetted at specific actions or variables by using
186      the <firstterm>signal detail</firstterm> when connecting. For example,
187      this causes <function>on_get_status_action</function> to be called when
188      the <function>GetStatus</function> action is invoked:
189    </para>
190    <programlisting>static void on_get_status_action (GUPnPService *service, GUPnPServiceAction *action, gpointer user_data);
191&hellip;
192g_signal_connect (service, "action-invoked::GetStatus", G_CALLBACK (on_get_status_action), NULL);</programlisting>
193    <para>
194      The implementation of action handlers is quite simple.  The handler is
195      passed a #GUPnPServiceAction object which represents the in-progress
196      action.  If required it can be queried using
197      gupnp_service_action_get_name() to identify the action (this isn't
198      required if detailed signals were connected).  Any
199      <firstterm>in</firstterm> arguments can be retrieving using
200      gupnp_service_action_get(), and then return values can be set using
201      gupnp_service_action_set().  Once the action has been performed, either
202      gupnp_service_action_return() or gupnp_service_action_return_error()
203      should be called to either return successfully or return an error code.
204      If any evented state variables were modified during the action then a
205      notification should be emitted using gupnp_service_notify().  This is an
206      example implementation of <function>GetStatus</function> and
207      <function>SetTarget</function>:
208    </para>
209    <programlisting>static gboolean status;
210
211static void
212get_status_cb (GUPnPService *service, GUPnPServiceAction *action, gpointer user_data)
213{
214  gupnp_service_action_set (action,
215                            "ResultStatus", G_TYPE_BOOLEAN, status,
216                            NULL);
217  gupnp_service_action_return (action);
218}
219
220void
221set_target_cb (GUPnPService *service, GUPnPServiceAction *action, gpointer user_data)
222{
223  gupnp_service_action_get (action,
224                            "NewTargetValue", G_TYPE_BOOLEAN, &amp;status,
225                            NULL);
226  gupnp_service_action_return (action);
227  gupnp_service_notify (service, "Status", G_TYPE_STRING, status, NULL);
228}
229&hellip;
230g_signal_connect (service, "action-invoked::GetStatus", G_CALLBACK (get_status_cb), NULL);
231g_signal_connect (service, "action-invoked::SetTarget", G_CALLBACK (set_target_cb), NULL);</programlisting>
232    <para>
233      State variable query handlers are called with the name of the variable and
234      a #GValue.  This value should be initialized with the relevant type and
235      then set to the current value.  Again signal detail can be used to connect
236      handlers to specific state variable callbacks.
237    </para>
238    <programlisting>static gboolean status;
239
240static void
241query_status_cb (GUPnPService *service, char *variable, GValue *value, gpointer user_data)
242{
243  g_value_init (value, G_TYPE_BOOLEAN);
244  g_value_set_boolean (value, status);
245}
246&hellip;
247g_signal_connect (service, "query-variable::Status", G_CALLBACK (query_status_cb), NULL);</programlisting>
248    <para>
249      The service is now fully implemented.  To complete it, enter a GLib main
250      loop and wait for a client to connect.  The complete source code for this
251      example is available as <filename>examples/light-server.c</filename> in
252      the GUPnP sources.
253    </para>
254    <para>
255      For services which have many actions and variables there is a convenience
256      method gupnp_service_signals_autoconnect() which will automatically
257      connect specially named handlers to signals.  See the documentation for
258      full details on how it works.
259    </para>
260  </simplesect>
261  <simplesect>
262    <title>Generating Service-specific Wrappers</title>
263    <para>
264      Using service-specific wrappers can simplify the implementation of a service.
265      Wrappers can be generated with <xref linkend="gupnp-binding-tool"/>
266      using the option <literal>--mode server</literal>.
267    </para>
268    <para>
269      In the following examples the wrapper has been created with
270      <literal>--mode server --prefix switch</literal>. Please note that the callback handlers
271      (<literal>get_status_cb</literal> and <literal>set_target_cb</literal>) are not automatically
272      generated by <xref linkend="gupnp-binding-tool"/> for you.
273    </para>
274    <programlisting>static gboolean status;
275
276static void
277get_status_cb (GUPnPService *service,
278               GUPnPServiceAction *action,
279               gpointer user_data)
280{
281  switch_get_status_action_set (action, status);
282
283  gupnp_service_action_return (action);
284}
285
286static void
287set_target_cb (GUPnPService *service,
288               GUPnPServiceAction *action,
289               gpointer user_data)
290{
291  switch_set_target_action_get (action, &amp;status);
292  switch_status_variable_notify (service, status);
293
294  gupnp_service_action_return (action);
295}
296
297&hellip;
298
299switch_get_status_action_connect (service, G_CALLBACK(get_status_cb), NULL);
300switch_set_target_action_connect (service, G_CALLBACK(set_target_cb), NULL);</programlisting>
301    <para>
302      Note how many possible problem situations that were run-time errors are
303      actually compile-time errors when wrappers are used: Action names,
304      argument names and argument types are easier to get correct (and available
305      in editor autocompletion).
306    </para>
307    <para>
308      State variable query handlers are implemented in a similar manner, but
309      they are even simpler as the return value of the handler is the state
310      variable value.
311    </para>
312    <programlisting>static gboolean
313query_status_cb (GUPnPService *service,
314                 gpointer user_data)
315{
316  return status;
317}
318
319&hellip;
320
321switch_status_query_connect (service, query_status_cb, NULL);</programlisting>
322  </simplesect>
323</chapter>
324