Content
Â
What are XML Definitions
An XML definition is a set of instructions (defined through XML syntax) that can be used to configure Snom phone buttons to achieve customized behavior.
In this tutorial we will cover the following functionality of XML definitions:
- customize actions to be taken when a button is pressed. For example, when a button is pressed display a text on the screen, or change/toggle a phone setting
- customized extension monitoring (BLF/presence). For example, when the Snom phone must interact with a SIP server that implements a customized BLF or presence functionality. You can instruct the phone to subscribe to a SIP resource and then parse the XML body received in the in-dialog NOTIFY. From the body of NOTIFY, you can extract information based on which you change variables, and the phone can take specific actions automatically when the state changes (such as turn led on/off, blink, etc)
- we will also see some more advanced examples with more complex use cases
How to configure an XML Definition
There are a few ways to configure and use XML Definitions, see a list here. In this tutorial, we will start with a very simple configuration on phone keys, and further below in the advanced section, we will also see other ways to configure XML Definitions.
Create the XML Definition
To start working with XML Definitions, we will need to write the XML code for it. This part will be explained starting with the next section below.
Via the phone web interface
Once you have written the XML code, you must add it to the phone. The easiest way to do this is to use a phone key. To configure a key with an XML Definition, click on Function Keys, choose a key and configure the following:
- Type: XML Definition
- Number: copy and paste the text of your XML definition, then press Apply and Save
Below are the most relevant parts of an XML definition. We will learn about them as we go through the different examples below.
- general - here you can give a name to the definition and configure a SIP identity that relates to
- initialization - used to initialize variables
- action - used to define the actions that should occur (for example when a button is pressed or when a NOTIFY is received)
- subscription - used to define a SIP subscription
- NotifyParsingRules - used to define parsing rules that will apply to the NOTIFY body
Customize actions to be taken when the button is pressed
Display simple text when a button is pressed
Here is our first XML definition example:
<general type="VerySimpleExample"/> <action> <url target="http://user:password@localhost/dummy.htm?settings=save&user_idle_text1=Button pressed!" when="on press"/> </action>
Here is what we defined here:
- The "general" section defines a name for our XML definition using the "type" parameter ("VerySimpleExample"). The name of the XML definition can be shown in the Phone User Interface (unless there is a label configured) or in some (more advanced) cases we can use it to refer to this XML definition code from another XML definition code.
- The "action" section has only one line:Â <url target="http://user:password@localhost/dummy.htm?settings=save&user_idle_text1=Button pressed!" when="on press"/> . The target attribute tells the phone which URL should be opened/executed, the when attribute says on which event the URL should be opened/executed. When this button is pressed, an URL will be called. The URL is a simple HTTP request which sets the user_idle_text on the phone to "Button pressed!" for identity 1.
Note: most  phone settings can be changed via HTTP requests. Details on how to set phone settings are outside the scope of this tutorial, for more information please see this link.
Let's try this example:
- In the above text, replace "user:password" with the HTTP username and password of your phone
- Use the steps provided under "How to configure an XML Definition" to enter the above XML definition text for a function key button of your choice:
The web interface will then look like this: - The entered text cannot be seen very well here, but generally, it is good to check that the XML text was parsed and accepted by the phone correctly. Click on Settings in the left menu (under Status), search in page for "VerySimpleExample" and then you can double-check that the configuration was saved on the phone:
You will notice that the phone did a few changes on its own:- we configured P3 but the XML definition appears under fkey2. This is because internally the phone keys are numbered starting with 0, so P1 is actually fkey0, P2 is fkey1, etc.
- the "initialization" part was added. The phone needs to know to which identity to apply this XML definition, so it will automatically take the currently active identity. This association is currently only used when setting up a SIP subscription, so we can ignore it for now.
- the "&" symbol was rewritten to it's "&". Because "&" is a reserved character, and the phone settings file is also in XML format just like the XML definition, the phone needs to encode it in order not to affect the XML structure of the settings file.
- Now that everything is set up, we can test the button. The phone will first look something like this: (showing your current extension)
Now press P3 and the phone will change the setting, which will show the configured text:
Toggle setting to on/off when a button is pressed
Some phone settings have on/off values, and in some cases, it is necessary to turn them on and off via buttons. Using the simple example above, you could easily define two phone buttons, one for changing the setting to on and the other one for changing the setting to off. However, usually, you would want to use fewer buttons because the number of buttons on the phone is limited. With XML Definitions, it is possible to add extra logic to the buttons to make it possible to use only one button for toggling a setting to on/off.
To achieve the setting toggling with one button only, we will use a special variable called "state". In this variable we will remember in which state we are, meaning if our setting has been turned on or off the last time we pressed this button. This way, we can check the state to decide whether pressing the button should turn the setting on or off. The first press of the button will turn the setting on, the next press of the button will set this setting back off, etc.
Another feature used here is that "state" is a special variable that automatically gets applied to the button LED (on or off). Using this variable we can decide when the button LED should turn on or off, and this will add a nice visual effect to our custom button.
For this example, we will use the setting show_clock, which can be set to off to hide the clock.
Initialization
The initialization element allows us to define an initial value for the state variable. We will give the state variable the value off. Which means that on bootup of the phone this button would be in the state off:
<initialization> <state value="off"/> </initialization>
Assign
To use the state variable, we need to add the logic to update it. For this purpose, we add two assign elements to our code. These elements are used to change the state of a button and/or assign values to variables or arrays.
An assign element has two sub-elements:
- source (value)
- destination (parameter name)
The following code snippet inside of an assign tag will set the state to on:
<source value="on"/>
<destination id="state"/>
Like the other action elements, assign has the when and states attributes to define on which event (when=...) and in which state of the button (states=...) should these assign action occur. Here is how a full assign will look like:
<assign when="on press" states="off"> <source value="on"/> <destination id="state"/> </assign>
For our definition, we need two such assign elements: one to set the state on if the button was pressed while the state was off, and the other one to set the state back off if the button was pressed while the state was on.
Action
Further, we add the actions which should happen when our button is pressed.
In the "action" section we will have two on-press actions, which will be applied depending on the current value of the state variable (the "state"/"states" attribute). If the state of the button is currently on, pressing the button will change the setting to off. If the state of the button is currently off, pressing the button will change the setting to on:
<url target="http://user:password@localhost/dummy.htm?settings=save&show_clock=on" when="on press" states="off"/> <url target="http://user:password@localhost/dummy.htm?settings=save&show_clock=off" when="on press" states="on"/>
Putting it all together
The full XML will look like this:
<general type="ToggleExample"/> <initialization> <state value="off"/> </initialization> <action> <url target="http://user:password@localhost/dummy.htm?settings=save&show_clock=on" when="on press" states="off"/> <url target="http://user:password@localhost/dummy.htm?settings=save&show_clock=off" when="on press" states="on"/> <assign when="on press" states="on"> <source value="off"/> <destination id="state"/> </assign> <assign when="on press" states="off"> <source value="on"/> <destination id="state"/> </assign> </action>
Let's try this example:
- In the above text, replace "user:password" with the HTTP username and password of your phone
- Use the steps provided under "How to configure an XML Definition" to enter the above XML definition text for a function key button of your choice.
- Now test the button. The phone will first look something like this: (showing the clock on the top of the screen)
Press the button. The default value of show_clock is on, so the first press does not change anything on the display. Only the LED for the configured button will be turned on because the state has been changed to on.
Press the button again. The phone will set show_clock to off, which you can see on the display (the clock disappeared):
Also, the LED for the configured button has been turned off.
Auto assignment
Auto assignment on reboot
By using the above example you might have noticed that when the phone has been rebooted the button will get in the state which we defined in the initialization tag. Even if the setting was already changed to on, if we reboot the phone, because of our initialization, the state of the button is off. This is incorrect. To solve this we are going to add an assign element which would be executed after-initialization.
<assign when="after initialization"> <source context="setting" id="show_clock"/> <destination context="this entity" id="state"/> </assign>
The element contains a new parameter called context:
- The context="setting" in our source element tells the phone to look for variables in the phone settings. Of course, we needed also to define for which setting exactly are we looking, that's why instead of the value attribute we used the id attribute which is the name of our setting
- The context="this entity" in our destination element means that the variable is part of this XML-entity. This is the default value of the context parameter, so if you don't specify a context it gets set to "this entity". There can be other values for the context parameter, but we will not use them for now. we put new elements named here: assign-action.
Using this assign, when you reboot the phone, the button will check the current (last set) value in the settings and update the state of our button.
Also, note that the URL that we have used to change the setting until now did not save the setting permanently. To save the setting permanently, we need to add " store_settings=save " to the URL target.
After adding the new assign element, our XML definition will look like this:
<general type="ToggleExample"/> <initialization> <state value="off"/> </initialization> <action> <url target="http://user:password@localhost/dummy.htm?settings=save&show_clock=on&store_settings=save" when="on press" states="off"/> <url target="http://user:password@localhost/dummy.htm?settings=save&show_clock=off&store_settings=save" when="on press" states="on"/> <assign when="after initialization"> <source context="setting" id="show_clock"/> <destination context="this entity" id="state"/> </assign> <assign when="on press" states="on"> <source value="off"/> <destination id="state"/> </assign> <assign when="on press" states="off"> <source value="on"/> <destination id="state"/> </assign> </action>
If you configure the XML definition again using the new code, you will notice that:
- the current value of show_clock will be reflected by the button's LED as soon as you have configured the button
- even after a reboot, the button's LED will still reflect the correct value
Dynamic labels
Dynamic labels allow us to show a text for our Button, for the function keys that have a corresponding dynamic display (for example, D385 phones).
A button's label is configured/defined by: XML Label (see below) and fkey_label (see also fkey_label_overrides_xml_label for priority) or in the absence of both by the label_default_text setting.
In this section we will show how to customize the XML Label via XML Definitions.
To our above example, we add the label variable in the initialization, which will define the text label for the button. This will set the button label to the initial text
"Clock":
<variable name="label" value="Clock"/>
We will additionally need to add the assignment code to dynamically change the label depending on the settings state:
When state is on and the button is pressed, change the label to "Clock off":
<
assign
when
=
"on press"
states
=
"on"
>
   Â
<
source
v
alue
=
"Clock off"
/>
   Â
<
destination
id
=
"label"
/>
</
assign
>
When state is off and the button is pressed, change the label to "Clock on":
<
assign
when
=
"on press"
states
=
"off"
>
     Â
<
source
value
=
"Clock on"
/>
     Â
<
destination
id
=
"label"
/>
</
assign
>
Here is the resulting XML definition with simple dynamic labeling:
<general type="ToggleExample"/> <initialization> <state value="off"/> <variable name="label" value="Clock"/> </initialization> <action> <url target="http://user:password@localhost/dummy.htm?settings=save&show_clock=on&store_settings=save" when="on press" states="off"/> <url target="http://user:password@localhost/dummy.htm?settings=save&show_clock=off&store_settings=save" when="on press" states="on"/> <assign when="after initialization"> <source context="setting" id="show_clock"/> <destination context="this entity" id="state"/> </assign> <assign when="on press" states="on"> <source value="off"/> <destination id="state"/> </assign> <assign when="on press" states="off"> <source value="on"/> <destination id="state"/> </assign> <assign when="on press" states="on"> <source value="Clock off"/> <destination id="label"/> </assign> <assign when="on press" states="off"> <source value="Clock on"/> <destination id="label"/> </assign> </action>
When trying this XML definition on a D385 phone you will see the label changing when pressing the button:
When the clock is on the label will look like this:
When the clock if off the label will look like this:
Language support for labels
It is possible to use a translation for labels to display the labels in different local languages in multi-language environments. To do this, instead of using a static text as a source, you can use the language context and extract the label translation from the Snom Phone User Interface language file.
A complete list of translations can be found under Firmware Update Center :
- Open the Firmware Update Center
- Click on the version that your phone is running
- Download the Customization File for your model
- Extract the archive
- Browse to the languages directory
- Open one of the gui_lang files, either gui_lang_EN.xml or the one for your language you prefer
- Search in the file for the text that you wish to replace
- Take the value under n="...", and remove the "lang_" part at the beginning
- For example, the label for the text "on" and "off" for the German language:
Here is the translation for "off" in our XML definition:
<assign when="on press" states="on"> <source context="language" id="off"/> <destination id="label"/> </assign>
And here is the translation for "on":
<assign when="on press" states="off"> <source context="language" id="on"/> <destination id="label"/> </assign>
Here is the resulting XML definition with dynamic labeling and translation:
<general type="ToggleExample"/> <initialization> <state value="off"/> <variable name="label" value="Clock on"/> </initialization> <action> <url target="http://user:password@localhost/dummy.htm?settings=save&show_clock=on" when="on press" states="off"/> <url target="http://user:password@localhost/dummy.htm?settings=save&show_clock=off" when="on press" states="on"/> <assign when="after initialization"> <source context="setting" id="show_clock"/> <destination context="this entity" id="state"/> </assign> <assign when="on press" states="on"> <source value="off"/> <destination id="state"/> </assign> <assign when="on press" states="off"> <source value="on"/> <destination id="state"/> </assign> <assign when="on press" states="on"> <source context="language" id="off"/> <destination id="label"/> </assign> <assign when="on press" states="off"> <source context="language" id="on"/> <destination id="label"/> </assign> </action>
And when using this XML definition, the label will be translated to the current Phone User Interface language set, as in the following example for German:
       Â
          Â
Customized extension monitoring
Snom phones already support BLF and presence without the need for XML Definition (you can simply define a function key of type BLF). However, some server implementations require customized behavior, which you can achieve using XML Definitions.
Note: for this section of the article it is required to be familiar with the SIP protocol and the extension monitoring (BLF) functionality.
Simple BLF
To start with, let's learn by using an example of a simple XML Definition key which provides simplified BLF functionality (monitor a remote extension and depending on its state change the phone's LED with different colors). Please note that to keep this first example simple we did not implement the full BLF functionality. See the next sections for more information.
Initialization
In the beginning, we will need to initialize our variables. You already know about the state variable from the previous examples. We add two more variables:
- the identity variable ties this button to an identity. For this example, we will use identity 2. You can also leave this line out when configuring the definition from the web interface because as we could see in the previous example the phone will automatically configure the identity to the currently active identity. Another way to configure the identity is by adding it as a parameter in the general section, like this: <general type="BusyLampField" identity="2"/>
- the subscr_uri variable is the SIP URI of the remotely monitored extension. For this example, it will be "sip:13011@ser.berlin.snom.com"
This is the initialization section of our definition:
<initialization> <state value="off"/> <identity value="2"/> <variable name="subscr_uri" value="sip:13011@ser.berlin.snom.com"/> </initialization>
Subscription
Here is how our subscription element will look like:
<subscription type="dialog" to="<$(subscr_uri)>" for="$(subscr_uri)"/>
This element is new. Using the subscription section we tell the phone to send a SIP SUBSCRIBE message. To use our previously defined variable subscr_uri we put its name into $(...).
Depending on the PBX on which you want to subscribe there are a few different types that can be used, for this example, we will use dialog. See more about types here: Syntax-subscription
Note that values like "<" and ">" need to be encoded like "<
" and ">
". See more about XML syntax here.
NotifyParsingRules
Next, we add Parsing Rules to our XML Definition Code, which take the SIP NOTIFY messages we receive on the phone and extract the relevant data.
Applies rules
This rule will check if a SIP NOTIFY message belongs to this particular button (because you can have different NOTIFY messages for different buttons). In the NotifyParsingRules element container, we put new elements named level. This (level) elements are condition elements: if the condition inside the tags (/dialog-info[@entity="$(subscr_uri)"]) is matched, the applies element get the value (OK) which is defined in the translates_to attribute.
The rules are written in XPATH format, you can find more information about it here: Xpath
<NotifyParsingRules type="applies"> <level1 translates_to='OK'>/dialog-info[@entity="$(subscr_uri)"]</level1> </NotifyParsingRules>
Below is how an example SIP NOTIFY body would look like to understand the matching better:
<?xml version="1.0"?> <dialog-info xmlns="urn:ietf:params:xml:ns:dialog-info" version="9" state="full" entity="sip:13011@ser.berlin.snom.com"> <dialog id="c8a91cae3e73da5c6bd7defba62664c5" > <state>trying</state> </dialog> </dialog-info>
Only the second line of this example is important for the NotifyParsingRule of type "applies". The rule looks for the parameters of the <dialog-info> tag, finds the entity parameter, and compares it to the given value. For this example, if the content of variable $(subscr_uri)) is "sip:13011@ser.berlin.snom.com", it means that this NOTIFY applies to our button so we can go ahead and process it (using the NotifyParsingRule of type "state").
State rules
The state NotifyParsingRules set the button state based on the body of the SIP NOTIFY. Here we use XPATH to match conditions and if the match is successful we assign the value to the state. The different levels determine when to try to match the condition. To understand all the details how the condition matching works, see this link.
In the below example, the search is started with level1, if level1 matches the search is stopped (because level1 does not have any sublevels, see section "Rule with sublevels" below). If level1 doesn't match, level2 is checked, and so on. The level5 rule is a "catch-all" rule: because it does not have any condition, it applies to all cases where none of the other rules matched:
<NotifyParsingRules type="state"> <level1 translates_to="ringing">/dialog-info/dialog/state[.="early"]</level1> <level2 translates_to="ringing">/dialog-info/dialog/state[.="proceeding"]</level2> <level3 translates_to="offhook">/dialog-info/dialog/state[.="trying"]</level3> <level4 translates_to="in_a_call">/dialog-info/dialog/state[.="confirmed"]</level4> <level5 translates_to="free"/> </NotifyParsingRules>
Below is the example SIP NOTIFY body again to understand the matching better:
<?xml version="1.0"?> <dialog-info xmlns="urn:ietf:params:xml:ns:dialog-info" version="9" state="full" entity="sip:13011@ser.berlin.snom.com"> <dialog id="c8a91cae3e73da5c6bd7defba62664c5" > <state>trying</state> </dialog> </dialog-info>
The phone looks under the <dialog-info> tag, then under the <dialog> tag, and inside it it looks to match the <state> tag. In this example the text inside the <state> tag is "trying" so our NotifyParsingRule would match at level3 ("<level3 translates_to="offhook">/dialog-info/dialog/state[.="trying"]</level3>"). As a result, the state will be set to "trying".
How the state applies to button behavior
Now that we have processed the NOTIFY and assigned values to the "state" variable, the phone will change the button LED behavior automatically. This part is not configured in the XML Definition, but it works by default because the keywords "ringing", "offhook" etc. are configured on the phone within special settings that define LED behavior. These too are customizable, and we will look at this customization later in the section Customized LED control.
Putting it all together
Here is how the full XML definition will look like:
<general type="BusyLampField"/> <initialization> <state value="off"/> <identity value="2"/> <variable name="subscr_uri" value="sip:13011@ser.berlin.snom.com"/> </initialization> <subscription type="dialog" to="<$(subscr_uri)>" for="$(subscr_uri)"/> <NotifyParsingRules type="applies"> <level1 translates_to='OK'>/dialog-info[@entity="$(subscr_uri)"]</level1> </NotifyParsingRules> <NotifyParsingRules type="state"> <level1 translates_to="ringing">/dialog-info/dialog/state[.="early"]</level1> <level2 translates_to="ringing">/dialog-info/dialog/state[.="proceeding"]</level2> <level3 translates_to="offhook">/dialog-info/dialog/state[.="trying"]</level3> <level4 translates_to="in_a_call">/dialog-info/dialog/state[.="confirmed"]</level4> <level5 translates_to="free"/> </NotifyParsingRules>
Let's try this example:
- Make sure you have a SIP server that supports BLF
- In the above text:
- replace "13011" with the extension number that you would like to monitor
- replace "ser.berlin.snom.com" with the IP or hostname of your SIP server
- replace the identity value ("2") with the identity index to which you want to apply this too. If you have only one identity, use "1" here.
- Check under State → Subscriptions and make sure that there is a working subscription. You can recognize a working subscription by the fact that there is a numeric value under Expires:
for
A not working subscription could look as below:
- Use the steps provided under "How to configure an XML Definition" to enter the above XML definition text for a function key button of your choice on your Snom phone
From another phone, call the monitored extension. Below is an example NOTIFY message that will be sent to your test phone. You don't need to understand everything in it, focus on the bold text:
NOTIFY sip:13012@10.245.0.72:52262;line=q55rh8at SIP/2.0 From: <sip:13011@ser.berlin.snom.com>;tag=gknqb59wlv To: <sip:13012@ser.berlin.snom.com>;tag=3o40fhfg32 ...some more headers that are irrelevant... Content-Length: 659 <?xml version="1.0"?> <dialog-info xmlns="urn:ietf:params:xml:ns:dialog-info" version="3" state="full" entity="sip:13011@ser.berlin.snom.com"><dialog id="0887e37839c4a2648b05a7b6c877f8d4" direction='recipient' call-id='313630303130333336303339383436-5expwpdr9avv' local-tag="w3qgzyi6oz" remote-tag="zfx6jztlfc"><state> early </state><local><identity>sip:13011@ser.berlin.snom.com;user=phone</identity><target uri="sip:13011@10.245.0.72:60663;line=g7imogfr"><param pname="x-line-id" pval="1" /></target></local><remote><identity>sip:13013@ser.berlin.snom.com</identity><target uri="sip:13013@192.168.137.132:5060;line=m62k1dzl"/></remote></dialog></dialog-info>
At this point, the phone will do the following:
- check that the
entity
parameter in the<dialog-info
> tag of the NOTIFY body matches the value defined in the XML definition under <initialization> for variable subscr_uri. Only if this matches, continue processing this NOTIFY - check the text under the
<dialog-info
> →<dialog>
 →<state>
tag of the NOTIFY body and populate the "state" variable according to the NotifyParsingRules of type "state". As a result, for the above example, state will be assigned the value "early
" - because the phone is configured to blink for the state "
early
", the LED corresponding button on which we configured this XML definition will start blinking
6. Answer the call from the monitored extension. A similar NOTIFY will be received with the state "confirmed", which will translate to "in_a_call" and as a result, the LED will turn solid on.
7. Hang up the call. Another NOTIFY should be received with the state "terminated", which will not match any of the level1 to level4 rules, and as a result, it will use the level5 rule and translate to "free". This will make the LED turn off.
Rule with sublevels
If you are familiar with BLF you might notice a small problem with the above BLF example. The LED will blink when the monitored extension is ringing, but also when the monitored extension calls another number and the remote number is ringing. This is because our XML Definition does not check the direction of the call. But standard BLF functionality would be to see the LED steady on, not blinking, in case the monitored extension is the initiator of the call. Thus we need another state for this case. To add this, we must add another level to our existing "ringing" levels. Currently, we had:
<level1 translates_to="ringing">/dialog-info/dialog/state[.="early"]</level1> <level2 translates_to="ringing">/dialog-info/dialog/state[.="proceeding"]</level2>
These two cases mean that the monitored extension is involved in a call that is currently ringing. But to have correct BLF functionality we need to know which of the two cases we are in:
- either the monitored extension is the initiator of the call, in which case we want the LED to be turned on
- or the monitored extension is the receiver / recipient of the call, in which case we want the LED to be blinking
The NOTIFY body contains this information inside the direction parameter. Here is an example NOTIFY body for the ringing case:
<?xml version="1.0"?> <dialog-info xmlns="urn:ietf:params:xml:ns:dialog-info" version="1" state="full" entity="sip:13011@ser.berlin.snom.com"> <dialog id="092c45453b4e9f606489369676776dc5" direction='recipient' call-id='313630303138383238353430363632-6viw2mbsfjxw' local-tag="yy7oj6ahdr" remote-tag="vuodb8s5w0"> <state>early</state> ...
The direction parameter contains the information whether the monitored extension is the recipient of the call or the initiator of the call. So to our two level1 and level2 rules we will add sub-rules, level1-1 and level 2-1, like this:
<level1 translates_to = "ringing">/dialog-info/dialog/state[.="early"]</level1> <level1-1 translates_to = "calling">/dialog-info/dialog[@direction="initiator"]</level1-1> <level2 translates_to = "ringing">/dialog-info/dialog/state[.="proceeding"]</level2> <level2-1 translates_to = "calling">/dialog-info/dialog[@direction="initiator"]</level2-1>
The rule level1-1 will only be checked when the rule level1 was true. Similarly, level2-1 will only be checked when level2 was true. This will make sure that the state changes from "ringing" to "calling" in case the monitored extension is the initiator of the call (so that the LED will be steady on instead of blinking).
Below is how the full XML definition will now look like. Remember that to use it you must replace the value of variable subscr_uri and identity:
<general type="BusyLampField"/> <initialization> <state value="off"/> <identity value="2"/> <variable name="subscr_uri" value="sip:13011@ser.berlin.snom.com"/> </initialization> <subscription type="dialog" to="<$(subscr_uri)>" for="$(subscr_uri)"/> <NotifyParsingRules type="applies"> <level1 translates_to='OK'>/dialog-info[@entity="$(subscr_uri)"]</level1> </NotifyParsingRules> <NotifyParsingRules type="state"> <level1 translates_to="ringing">/dialog-info/dialog/state[.="early"]</level1> <level1-1 translates_to="calling">/dialog-info/dialog[@direction="initiator"]</level1-1> <level2 translates_to="ringing">/dialog-info/dialog/state[.="proceeding"]</level2> <level2-1 translates_to="calling">/dialog-info/dialog[@direction="initiator"]</level2-1> <level3 translates_to="offhook">/dialog-info/dialog/state[.="trying"]</level3> <level4 translates_to="in_a_call">/dialog-info/dialog/state[.="confirmed"]</level4> <level5 translates_to="free"/> </NotifyParsingRules>
Action on button press
For this example, we would like to add some functionality to the button:
1-if the monitored extension is idle, pressing the button should call the extension
2-if the monitored extension is currently ringing, pressing the button should pick up the current incoming call
The first functionality is quite simple. We just need to add an invite action:
   <invite target="$(subscr_uri)" when="on press" states="calling, in_a_call, offhook, free"/>
For the second functionality, we must add a more complex invite action:
   <invite target="$(remote_uri)" when="on press" state="ringing" request_uri="$(remote_uri)" replaces="$(call_id);to-tag=$(remote_tag);from-tag=$(local_tag)"/>
This means: if the state is ringing (state="ringing"
), when pressing the button, send an INVITE to "$(remote_uri)"
. The rest of the rule refers to the SIP headers which will not be covered as they are outside the scope of this training.
To use the variables which we need for the call pickup, we must fetch them first from the NOTIFY body. To do this, we add NotifyParsingRules of the type variable, which can be used to populate variables with values from the NOTIFY body.
These rules will look like this:
<NotifyParsingRules type="variable" id="call_id" state="ringing"> <level1 fetch_attribute="call-id">/dialog-info/dialog[@call-id]</level1> </NotifyParsingRules> ...and similar for the other parameters that are needed for the pick-up INVITE: remote-tag, local-tag and uri...
This means: if the state is ringing, take the parameter call-id and put it into the variable "call_id". Below is the example NOTIFY message that we already saw above, which will be sent to your test phone for state ringing. You don't need to understand everything in it, focus on the bold text. You can see for example where the call-id parameter is found:
NOTIFY sip:13012@10.245.0.72:52262;line=q55rh8at SIP/2.0 From: <sip:13011@ser.berlin.snom.com>;tag=gknqb59wlv To: <sip:13012@ser.berlin.snom.com>;tag=3o40fhfg32 ...some more headers that are irrelevant... Content-Length: 659 <?xml version="1.0"?> <dialog-info xmlns="urn:ietf:params:xml:ns:dialog-info" version="3" state="full" entity="sip:13011@ser.berlin.snom.com"><dialog id="0887e37839c4a2648b05a7b6c877f8d4" direction='recipient' call-id='313630303130333336303339383436-5expwpdr9avv' local-tag="w3qgzyi6oz" remote-tag="zfx6jztlfc"><state>early</state><local><identity>sip:13011@ser.berlin.snom.com;user=phone</identity><target uri="sip:13011@10.245.0.72:60663;line=g7imogfr"><param pname="x-line-id" pval="1" /></target></local><remote><identity>sip:13013@ser.berlin.snom.com</identity><target uri="sip:13013@192.168.137.132:5060;line=m62k1dzl"/></remote></dialog></dialog-info>
Below is how the full XML definition will now look like. Remember that to use it you must replace the value of variable subscr_uri and identity.
<general type="BusyLampField"/> <initialization> <state value="off"/> <identity value="2"/> <variable name="subscr_uri" value="sip:104@192.168.137.1:5070"/> </initialization> <subscription type="dialog" to="<$(subscr_uri)>" for="$(subscr_uri)"/> <NotifyParsingRules type="applies"> <level1 translates_to='OK'>/dialog-info[@entity="$(subscr_uri)"]</level1> </NotifyParsingRules> <NotifyParsingRules type="state"> <level1 translates_to="ringing">/dialog-info/dialog/state[.="early"]</level1> <level1-1 translates_to="calling">/dialog-info/dialog[@direction="initiator"]</level1-1> <level2 translates_to="ringing">/dialog-info/dialog/state[.="proceeding"]</level2> <level2-1 translates_to="calling">/dialog-info/dialog[@direction="initiator"]</level2-1> <level3 translates_to="offhook">/dialog-info/dialog/state[.="trying"]</level3> <level4 translates_to="in_a_call">/dialog-info/dialog/state[.="confirmed"]</level4> <level5 translates_to="free"/> </NotifyParsingRules> <NotifyParsingRules type="variable" id="call_id" state="ringing"> <level1 fetch_attribute="call-id">/dialog-info/dialog[@call-id]</level1> </NotifyParsingRules> <NotifyParsingRules type="variable" id="remote_tag" state="ringing"> <level1 fetch_attribute="remote-tag">/dialog-info/dialog[@remote-tag]</level1> </NotifyParsingRules> <NotifyParsingRules type="variable" id="local_tag" state="ringing"> <level1 fetch_attribute="local-tag">/dialog-info/dialog[@local-tag]</level1> </NotifyParsingRules> <NotifyParsingRules type="variable" id="remote_uri" state="ringing"> <level1 fetch_attribute="uri">/dialog-info/dialog/remote/target[@uri]</level1> </NotifyParsingRules> <action> <invite target="$(subscr_uri)" when="on press" states="calling, in_a_call, offhook, free"/> <invite target="$(remote_uri)" when="on press" state="ringing" request_uri="$(remote_uri)" replaces="$(call_id);to-tag=$(remote_tag);from-tag=$(local_tag)"/> </action>
Customized LED control
Sometimes we would like to change the default LED behavior and color depending on the state of our remote monitored extension. To do so, we use the translate_to attribute values (e.g. offhook, free, etc.) which we defined in the NotifyParsingRules element container for the type=state and add them to the appropriate phone settings like led_on, led_blink_slow, led_blink_medium, led_red, led_green , etc. Below is an example of the default values for these settings (on a snomD735 with version 10.1.42.14):
led_on=ON IN_A_CALL CALLING IN_A_MEETING URGENT_INTERRUPTIONS_ONLY BUSY I-Am-Busy DND_ALL DND_SELF ACTIVE INACTIVE BE_RIGHT_BACK SEIZED CONNECTED ON_HOLD OFFHOOK RINGBACK I-Am-Ready AWAY AVAILABLE AVAILABLE_ON_MOBILE AVAILABLE_AT_DESK PhoneHasCall PhoneHasMissedCalls CurrentIdentityHasVoiceMessages PhoneHasVoiceMessages seized_local seized_remote active_local active_remote led_blink_slow=PARKED HOLDING I-Am-Almost-Ready PhoneHasCallInStateHolding held_local held_remote KeyConfigActive led_blink_medium=RECORDING MESSAGE DateOngoing DateReminding led_blink_fast=RINGING PICKUP PhoneHasCallInStateRinging alerting_local alerting_remote led_red=BUSY DND_ALL DND_SELF I-Am-Busy IN_A_CALL IN_A_MEETING URGENT_INTERRUPTIONS_ONLY UNAVAILABLE seized_remote alerting_remote active_remote held_remote led_green=AVAILABLE AVAILABLE_ON_MOBILE AVAILABLE_AT_DESK I-Am-Ready I-Am-Almost-Ready seized_local alerting_local active_local held_local led_orange=AWAY INACTIVE BE_RIGHT_BACK KeyConfigActive
You can ignore the states that are not related to your XML Definition, they are there for other features.
There are several ways to change these settings. For example, you can open the Settings page from the web interface and right-click the setting, send an HTTP request to the phone or use Auto-provisioning. Recommended is to take the default values for your version and then add or remove the states that are needed for your configuration.
Let's try the following changes:
Custom LED example 1: set green light on for state "free"
Since by default Snom phones don't have the state free defined, we can just add it to led_green and to led_on:
led_green=AVAILABLE AVAILABLE_ON_MOBILE AVAILABLE_AT_DESK I-Am-Ready I-Am-Almost-Ready seized_local alerting_local active_local held_local free
led_on=ON IN_A_CALL CALLING IN_A_MEETING URGENT_INTERRUPTIONS_ONLY BUSY I-Am-Busy DND_ALL DND_SELF ACTIVE INACTIVE BE_RIGHT_BACK SEIZED CONNECTED ON_HOLD OFFHOOK RINGBACK I-Am-Ready AWAY AVAILABLE AVAILABLE_ON_MOBILE AVAILABLE_AT_DESK PhoneHasCall PhoneHasMissedCalls CurrentIdentityHasVoiceMessages PhoneHasVoiceMessages seized_local seized_remote active_local active_remote free
Custom LED example 2: set orange light blinking slowly for state "offhook"
By default, the offhook state is present in the led_on setting, so the LED will be turned on if the button is in this state. Since the color of the button is not configured (because the word "offhook" is not presend in any of the led_red, led_green or led_orange settings), it will be red by default.
In order to change the color, the state "offhook" must be added to led_orange.
In order to have the led blink slowly instead of being on the state "offhook'" has to removed from led_on and added to led_blink_slow. The resulting settings would look similar to the ones below:
led_orange=AWAY INACTIVE BE_RIGHT_BACK KeyConfigActive offhook led_blink_slow=PARKED HOLDING I-Am-Almost-Ready PhoneHasCallInStateHolding held_local held_remote KeyConfigActive offhook led_on=ON IN_A_CALL CALLING IN_A_MEETING URGENT_INTERRUPTIONS_ONLY BUSY I-Am-Busy DND_ALL DND_SELF ACTIVE INACTIVE BE_RIGHT_BACK SEIZED CONNECTED ON_HOLD RINGBACK I-Am-Ready AWAY AVAILABLE AVAILABLE_ON_MOBILE AVAILABLE_AT_DESK PhoneHasCall PhoneHasMissedCalls CurrentIdentityHasVoiceMessages PhoneHasVoiceMessages seized_local seized_remote active_local active_remote free
Controlling the states for NOTIFYs with two dialogs
The goal of this example is to solve the following problem that sometimes occurs with some BLF implementation: if the NOTIFY message contains two dialogs, the phone takes into consideration only the first dialog to determine the led state.
Assuming the server sends the following NOTIFY (see that there are two dialogs inside the body):
NOTIFY sip:1234@10.10.10.24:33127;line=t6kyhaap SIP/2.0 ..(some irrelevant headers deleted).. Event: dialog Subscription-State: active;expires=2135 Content-Type: application/dialog-info+xml Content-Length: ... <?xml version="1.0"?> <dialog-info xmlns="urn:ietf:params:xml:ns:dialog-info" version="13" state="full" entity="sip:300@pbx.local"> <dialog id="52" call-id="2803be77-7734-1237-bd9e-1418776478d4" direction="recipient"> <state>confirmed</state> <local><identity display="Gp 2">sip:+3331234567@pbx.local</identity></local> <remote><identity display="test">sip:+333123456300@pbx.local</identity><target uri="sip:*87300@pbx.local"/></remote> </dialog> <dialog id="53" call-id="452345fd-sdfgss-1418744444478d45555" direction="recipient"> <state>early</state> <local><identity display="Gp 2">sip:+4446512345@pbx.local</identity></local> <remote><identity display="test">sip:+333123456300@pbx.local</identity><target uri="sip:*87300@pbx.local"/></remote> </dialog> </dialog-info>
With standard BLF buttons and as well with our previous XML definition code, the corresponding led would turn solid red, because the first dialog in the NOTIFY has state set to "confirmed". However, in some cases such as call pickup scenarios, users might want to take the state from the second dialog instead of the first dialog, and see the led blinking for this case. To do this, you can create an additional check for the "confirmed" rule:
<level4 translates_to="in_a_call">/dialog-info/dialog/state[.="confirmed"]</level4> <level4-1 translates_to="ringing">/dialog-info/dialog[last()]/state[.="early"]</level4-1>
This rule means: if the "confirmed" state is found in the NOTIFY, check as well the state of the last dialog (
"dialog[last()]
"). If the state of the last dialog is "early", then translate the led state to "ringing" (blinking led).
Below is how this rule would look like inside a simple XML Definition. Note also that here we added the identity inside the general section instead of the initialization section:
<general type="BusyLampField" identity="1"/> <initialization> <state value="off"/> <variable name="subscr_uri" value="sip:300@pbx.local"/> </initialization> <subscription type="dialog" to="<$(subscr_uri)>" for="$(subscr_uri)"/> <NotifyParsingRules type="applies"> <level1 translates_to='OK'>/dialog-info[@entity="$(subscr_uri)"]</level1> </NotifyParsingRules> <NotifyParsingRules type="state"> <level1 translates_to="ringing">/dialog-info/dialog/state[.="early"]</level1> <level2 translates_to="ringing">/dialog-info/dialog/state[.="proceeding"]</level2> <level3 translates_to="offhook">/dialog-info/dialog/state[.="trying"]</level3> <level4 translates_to="in_a_call">/dialog-info/dialog/state[.="confirmed"]</level4> <level4-1 translates_to="ringing">/dialog-info/dialog[last()]/state[.="early"]</level4-1> <level5 translates_to="free"/> </NotifyParsingRules> <action> <invite target="sip:*87300@pbx.local" when="on press" /> </action>
Advanced XML Definitions
Accessing information about ongoing calls
The goal for this example: if we are currently in an ongoing call, we would like to have a button that transfers a call to a chosen number. But in case we are not in a call, the same button should call another number.
In the initialization section, we define our two numbers:
<general type="Transfer-or-call"/> <initialization> <state value="off"/> <variable name='transfer_destination' value='435'/> <variable name='call_destination' value='436'/> </initialization>
In the action section, we will first define a new variable which we call "transfer_id". This variable will fetch the call ID of the current call in case our phone is currently in a call.
First, we will initialize this variable to "" (empty):
<assign when="on press"> <source context="this entity" value=""/> <destination context="this entity" id="transfer_id"/> </assign>
Then, we use the call context to fetch the internal ID of the current call into variable transfer_id, only if our own state is connected:
<assign when="on press"> <source context="call" id="id" require="${state}==connected"/> <destination context="this entity" id="transfer_id"/> </assign>
Next, check if the transfer_id is set (and not empty), in which case change our button state to in_a_call. We will use this state in the next step as a condition to do the transfer.
<assign when="on press"> <source context="this entity" value="in_a_call"/> <destination context="this entity" id="state" require="$(transfer_id)!= "/> </assign>
If the transfer_id is empty, then change our button state to idle.
<assign when="on press"> <source context="this entity" value="idle"/> <destination context="this entity" id="state" require="$(transfer_id)=="/> </assign>
Based on the button state which we defined above, we can do either a REFER or an INVITE. Note that we use the "on release" value for the when parameter. The reason for this is that the "on release" event comes after the "on press" event and we want to use this in order to:
- first assign the value for the state (via assign "on press")
- then, depending on the state, do either an INVITE or a REFER
It is important to understand the order in which the actions are executed and how the actions work in relation to the when parameter. Changing the state during an event (for example the "on press" event) does not reflect on the when condition for the same event. For details regarding order of execution, see Order of execution for XML Definition actions.
<refer target="$(transfer_destination)" source="$(transfer_id)" when="on release" states="in_a_call"/> <invite target="$(call_destination)" when="on release" states="idle"/>
Here is the full definition for this example:
<general type="Transfer-or-call"/> <initialization> <state value="off"/> <variable name='transfer_destination' value='435'/> <variable name='call_destination' value='436'/> </initialization> <action> <assign when="on press"> <source context="this entity" value=""/> <destination context="this entity" id="transfer_id"/> </assign> <assign when="on press"> <source context="call" id="id" require="${state}==connected"/> <destination context="this entity" id="transfer_id"/> </assign> <assign when="on press"> <source context="this entity" value="in_a_call"/> <destination context="this entity" id="state" require="$(transfer_id)!= "/> </assign> <assign when="on press"> <source context="this entity" value="idle"/> <destination context="this entity" id="state" require="$(transfer_id)=="/> </assign> <refer target="$(transfer_destination)" source="$(transfer_id)" when="on release" states="in_a_call"/> <invite target="$(call_destination)" when="on release" states="idle"/> </action>
Accessing information about the state of my own phone
The goal for this example: use this button to monitor an extension as we did in the BLF example (including button press actions), but also, only if our phone is currently in a call, transfer the ongoing connected call to the remotely monitored extension. Here is the detailed goal:
- if the monitored extension is idle, pressing the button should call the extension
- if the monitored extension is currently ringing, pressing the button should pick up the current incoming call
- if the own phone is currently in a call, transfer the ongoing connected call to the remotely monitored extension
This example also uses the call context as in the previous example, but also, uses the button's state as well to monitor a remote extension. This means that we are using the "state" variable with 2 different purposes: for the standard BLF state, and for temporarily storing the own extension state. By making use of the two when parameters ("on press" and "on release"), we can temporarily reassign the button's state at "on press" to the own extension's state and then restore its initial state at "on release".
We need to use the "state" variable this way to use the states="" condition when pressing the button.
Note: Before trying this example, make sure you set fkey_longpress for the tested function key to off. Only after turning the Longpress feature off can we use the "on release" functionality properly. For more details, see Order of execution for XML Definition actions.
To complete the goal, we will take the BLF XML Definition code and add a few changes.
We will use the "transfer_id" variable as in the previous example. This variable will fetch the call ID of the current call in case our phone is currently in a call.
First, we will initialize this variable to "":
<assign when="on press"> <source context="this entity" value=""/> <destination context="this entity" id="transfer_id"/> </assign>
Then, we use the call context to fetch the internal ID of the current call into variable transfer_id, only if our own state is connected:
<assign when="on press"> <source context="call" id="id" require="${state}==connected"/> <destination context="this entity" id="transfer_id"/> </assign>
We also need to back up the current state of the remote extension into a new variable state_save, which we will restore at the end of the code:
<assign when="on press"> <source context="this entity" id="state"/> <destination context="this entity" id="state_save"/> </assign>
Next, check if the transfer_id is set (and not empty), in which case change our button state to transferring. We will use this state in the next step as a condition to do the transfer.
<assign when="on press"> <source context="this entity" value="transferring"/> <destination context="this entity" id="state" require="$(transfer_id)!= "/> </assign>
We add a refer element which will set a SIP REFER message to our PBX to transfer our connected call if the state is set to transferring:
<refer target="$(subscr_uri)" source="$(transfer_id)" when="on release" states="transferring"/>
As mentioned before, at the end we must restore our saved (backup) state back to the button:
<assign when="on release"> <source context="this entity" id="state_save"/> <destination context="this entity" id="state"/> </assign>
Also, we change the when attributes on our invite elements from "on press" to "on release" to eliminate unexpected calls being done.
Here is the full definition for this example:
<general type="BusyLampField"/> <initialization> <state value="off"/> <variable name="subscr_uri" value="sip:435@10.110.22.35"/> </initialization> <subscription type="dialog" to="<$(subscr_uri)>" for="$(subscr_uri)"/> <NotifyParsingRules type="applies"> <level1 translates_to='OK'>/dialog-info[@entity="$(subscr_uri)"]</level1> </NotifyParsingRules> <NotifyParsingRules type="state"> <level1 translates_to="ringing">/dialog-info/dialog/state[.="early"]</level1> <level1-1 translates_to="calling">/dialog-info/dialog[@direction="initiator"]</level1-1> <level2 translates_to="ringing">/dialog-info/dialog/state[.="proceeding"]</level2> <level2-1 translates_to="calling">/dialog-info/dialog[@direction="initiator"]</level2-1> <level3 translates_to="offhook">/dialog-info/dialog/state[.="trying"]</level3> <level4 translates_to="in_a_call">/dialog-info/dialog/state[.="confirmed"]</level4> <level5 translates_to="free"/> </NotifyParsingRules> <NotifyParsingRules type="variable" id="call_id" state="ringing"> <level1 fetch_attribute="call-id">/dialog-info/dialog[@call-id]</level1> </NotifyParsingRules> <NotifyParsingRules type="variable" id="remote_tag" state="ringing"> <level1 fetch_attribute="remote-tag">/dialog-info/dialog[@remote-tag]</level1> </NotifyParsingRules> <NotifyParsingRules type="variable" id="local_tag" state="ringing"> <level1 fetch_attribute="local-tag">/dialog-info/dialog[@local-tag]</level1> </NotifyParsingRules> <NotifyParsingRules type="variable" id="remote_uri" state="ringing"> <level1 fetch_attribute="uri">/dialog-info/dialog/remote/target[@uri]</level1> </NotifyParsingRules> <action> <assign when="on press"> <source context="this entity" value=""/> <destination context="this entity" id="transfer_id"/> </assign> <assign when="on press"> <source context="call" id="id" require="${state}==connected"/> <destination context="this entity" id="transfer_id"/> </assign> <assign when="on press"> <source context="this entity" id="state"/> <destination context="this entity" id="state_save"/> </assign> <assign when="on press"> <source context="this entity" value="transferring"/> <destination context="this entity" id="state" require="$(transfer_id)!= "/> </assign> <refer target="$(subscr_uri)" source="$(transfer_id)" when="on release" states="transferring"/> <invite target="$(subscr_uri)" when="on release" states="calling, in_a_call, offhook, free"/> <invite target="$(remote_name)<$(remote_uri)>" when="on release" state="ringing" request_uri="$(remote_uri)" replaces="$(call_id);to-tag=$(remote_tag);from-tag=$(local_tag)"/> <assign when="on release"> <source context="this entity" id="state_save"/> <destination context="this entity" id="state"/> </assign> </action>
Referencing an XML Definition from another XML Definition
Sometimes it is helpful within an XML Definition code to be able to fetch information from another XML Definition Code. Let's try this in the following example.
The goal of this example is the following:
- monitor two extensions A and B, using 2 buttons
- also use a third button called C. When pressing button C, call A only if A is free. Otherwise, call B
For A and B we write two simple XML Definitions to monitor their state:
Button for A:
<general type='A'/> <initialization> <variable name='subscr_uri' value='sip:434@10.110.22.35'/> <state value='off'/> </initialization> <subscription type='dialog' to='<$(subscr_uri)>' for='$(subscr_uri)'/> <NotifyParsingRules type='applies'> <level1 translates_to='OK'>/dialog-info[@entity='$(subscr_uri)']</level1> </NotifyParsingRules> <NotifyParsingRules type="state"> <level1 translates_to="ringing">/dialog-info/dialog/state[.="early"]</level1> <level1-1 translates_to="calling">/dialog-info/dialog[@direction="initiator"]</level1-1> <level2 translates_to="ringing">/dialog-info/dialog/state[.="proceeding"]</level2> <level2-1 translates_to="calling">/dialog-info/dialog[@direction="initiator"]</level2-1> <level3 translates_to="offhook">/dialog-info/dialog/state[.="trying"]</level3> <level4 translates_to="in_a_call">/dialog-info/dialog/state[.="confirmed"]</level4> <level5 translates_to="free"/> </NotifyParsingRules>
Button for B:
<general type='B'/> <initialization> <variable name='subscr_uri' value='sip:435@10.110.22.35'/> <state value='off'/> </initialization> <subscription type='dialog' to='<$(subscr_uri)>' for='$(subscr_uri)'/> <NotifyParsingRules type='applies'> <level1 translates_to='OK'>/dialog-info[@entity='$(subscr_uri)']</level1> </NotifyParsingRules> <NotifyParsingRules type="state"> <level1 translates_to="ringing">/dialog-info/dialog/state[.="early"]</level1> <level1-1 translates_to="calling">/dialog-info/dialog[@direction="initiator"]</level1-1> <level2 translates_to="ringing">/dialog-info/dialog/state[.="proceeding"]</level2> <level2-1 translates_to="calling">/dialog-info/dialog[@direction="initiator"]</level2-1> <level3 translates_to="offhook">/dialog-info/dialog/state[.="trying"]</level3> <level4 translates_to="in_a_call">/dialog-info/dialog/state[.="confirmed"]</level4> <level5 translates_to="free"/> </NotifyParsingRules>
For the C button, first, we add the URIs for A and B:
<general type="Call"/> <initialization> <state value="off"/> <variable name='A_uri' value='sip:434@10.110.22.35'/> <variable name='B_uri' value='sip:435@10.110.22.35'/> </initialization>
Then in the actions section, we initialize the state with "idle":
<assign when='on press'> <source value='idle'/> <destination context='this entity' id='state'/> </assign>
Next, we use a special context called 'all xml entities' to fetch the state from the A's XML Definition. To tell the phone from which XML definition to fetch values we use require1='${type}==A'. We also use require2='${state}==in_a_call' to change the current state only if A's state is 'in_a_call':
<assign when='on press'> <source context='all xml entities' id='state' require1='${type}==A' require2='${state}==in_a_call'/> <destination context='this entity' id='state'/> </assign>
Based on the state we add the invite actions to call either A or B:
<invite target='$(A_uri)' when='on release' states='idle'/> <invite target="$(B_uri)" when="on release" states="in_a_call"/>
Here is the full definition for C:
<general type="Call"/> <initialization> <state value="off"/> <variable name='A_uri' value='sip:434@10.110.22.35'/> <variable name='B_uri' value='sip:435@10.110.22.35'/> </initialization> <action> <assign when='on press'> <source value='idle'/> <destination context='this entity' id='state'/> </assign> <assign when='on press'> <source context='all xml entities' id='state' require1='${type}==A' require2='${state}==in_a_call'/> <destination context='this entity' id='state'/> </assign> <invite target='$(A_uri)' when='on release' states='idle'/> <invite target="$(B_uri)" when="on release" states="in_a_call"/> </action>
Find and replace into a new variable
The next example assign will take the A_uri value and extract the number from it into new variable A_num, thanks to the RegEx we write in the value_match attribute and the value_replace attribute where we defined which part of the RegEx we want to be saved into the A_num variable:
<assign when='on press'> <source context='this entity' id='A_uri' value_match='(sip:)?(.*)@.*' value_replace='$2'/> <destination context='this entity' id='A_num'/> </assign>
Concatenate variables using the join function
Sometimes it is useful to combine two values into one and to do so we are going to use the join function. For example, for button C in section Referencing an XML Definition from another XML Definition it would make sense to set the label of the button based on some existing variables:
<assign when='on release' states='idle'> <source context="this entity" id="A_num"/> <function name="join" pattern="Calling A: $(src0)"/> <destination context="this entity" id="label"/> </assign> <assign when='on release' states='in_a_call'> <source context="this entity" id="A_num"/> <source context="this entity" id="B_num"/> <function name="join" pattern="$(src0) busy, calling $(src1)"/> <destination context="this entity" id="label"/> </assign>
The attribute pattern allows us to define how to combine the values we want. You can have more sources which you would like to combine, the indexing of the (src) starts always from the top one and the first index is 0. For example, if you have 4 sources the last one would be "src3".
XML Definitions without attached button (general_purpose_xml_descriptions)
In the previous example Referencing an XML Definition from another XML Definition we have seen that we can fetch information from another XML Definition. In some cases, we prefer to not attach the XML Definition which we want to use to a button, because we are not visually interested to see its state or because the phone does not have enough buttons.
In the example Referencing an XML Definition from another XML Definition, it can be that we don't want to use up two buttons to fetch A's and B's states, but we only want a button for the XML Definition C. So for A and B, instead of XML definitions attached to buttons, we can use general_purpose_xml_descriptions.
We can use general_purpose_xml_descriptions0 for A, and general_purpose_xml_descriptions1 for B. Here are the most common options to configure them:
Configuring general_purpose definitions via the phone web interface
- Make sure you are logged in as admin
- Click on Settings, then search in page for general_purpose_xml_descriptions or scroll down
- Right-click on general_purpose_xml_descriptions0, you will see the following menu:
- Enter the value of the XML definition starting with <general .. until </NotifyParsingRules>, then press OK:
- Under "Some settings are not yet stored permanently.", press "View Changes" to review your changes:
- Then press Save
Configure general_purpose definitions via
Accessing information about the state of my own extension
Our goal for the current example is to configure a button which fetches a remote URL, but only if our extension is currently in a call. Otherwise, pressing the button should have the standard BLF functions (call monitored extension or pick up a ringing call). Here is the detailed goal:
1-if the monitored extension is idle, pressing the button should call the extension
2-if the monitored extension is currently ringing, pressing the button should pick up the current incoming call
3-if the own extension is currently in a call, pressing the button should fetch a remote URL.
In a previous example, we showed how to use the call context to get the current state of our phone. There is one limitation here: the call context is local for the current phone, but in some server implementations it can be that our own extension is registered on more than one phone, so our extension could be in a call but not on our local phone. For this reason, we sometimes need a different way to figure out if the current extension is in a call or not. In Asterisk-like environments, it is possible to SUBSCRIBE to your own extension state to know if it is in a call or not. In this example, we will try out this functionality.
In the first part, we will create an XML Definition Code for monitoring our own state and put it into a general_purpose_xml_descriptions setting. Then in the second part, we will refer to the own extension state (which we take from the general_purpose_xml_description defined in Part 1).
Note: Before trying this example, make sure you set fkey_longpress for the tested function key to off. Only after turning the Longpress feature off can we use the "on release" functionality properly. For more details, see Order of execution for XML Definition actions.
Part 1: SUBSCRIBE to own Extension
The XML Definition Code for our own extension state looks similar to the code that we already learned in the basic BLF example. We need to do only a few changes:
- We change the name of our XML Definition code in the general element to "PhoneState" because we are going to use this name later on for referencing to this code
- We add a new variable element called current_state which will contain our own extension's state.
- We change the values of the translate_to attributes to contain me_ in front. That will help us, later on, to decide which action we are going to perform. The re-naming of the attributes is not necessary but it helps to avoid confusion.
- In the action section, we need only an assign element which will be executed on notify, when a SIP NOTIFY message arrives and puts the state of our monitored extension (our own extension) into the new variable current_state
Here is how the definition will look like:
<general type='PhoneState'/> <initialization> <variable name='current_state' value=''/> <variable name='own_subscr_uri' value='sip:433@10.110.22.35'/> <state value='off'/> </initialization> <subscription type='dialog' to='<$(own_subscr_uri)>' for='$(own_subscr_uri)'/> <NotifyParsingRules type='applies'> <level1 translates_to='OK'>/dialog-info[@entity='$(own_subscr_uri)']</level1> </NotifyParsingRules> <NotifyParsingRules type="state"> <level1 translates_to="me_ringing">/dialog-info/dialog/state[.="early"]</level1> <level1-1 translates_to="me_calling">/dialog-info/dialog[@direction="initiator"]</level1-1> <level2 translates_to="me_ringing">/dialog-info/dialog/state[.="proceeding"]</level2> <level2-1 translates_to="me_calling">/dialog-info/dialog[@direction="initiator"]</level2-1> <level3 translates_to="me_offhook">/dialog-info/dialog/state[.="trying"]</level3> <level4 translates_to="me_in_a_call">/dialog-info/dialog/state[.="confirmed"]</level4> <level5 translates_to="me_free"/> </NotifyParsingRules> <action> <assign when='on notify'> <source context='local' id='state'/> <destination context='local' id='current_state'/> </assign> </action>
Part 2: Use the state from Part 1 in another XML Definition
Now our next step is to use the BLF XML Definition Code and adapt it to refer to the above (own extension's state) XML Definition Code.
In the initialization section, we add two new variable elements, which we are going to use to save the remote extension's number and state:
<variable name='subscr_state' value='initial'/> <variable name='subscr_num' value='' />
In the actions section, we fill our new variables whenever the user presses the button.
The first assign will take the subscr_uri value and extract the number from it, thanks to the RegEx we wrote in the value_match attribute and the value_replace attribute where we defined which part of the RegEx would be saved into the subscr_num variable:
<assign when='on press'> <source context='local' id='subscr_uri' value_match='(sip:)?(.*)@.*' value_replace='$2'/> <destination context='local' id='subscr_num'/> </assign>
Also, we copy the current state value into our new subscr_state variable to restore it at the end:
<assign when='on press'> <source context='this entity' id='state'/> <destination context='this entity' id='subscr_state'/> </assign>
We also need to add an assign element to fetch our own phone state from the XML definition which we defined in Part 1. When this button gets pressed, get all XML Definition Codes where the type is PhoneState and, if in that Code the current_state has the value of me_in_a_call, assign current_state value to our state variable.
<assign when='on press'> <source context='all xml entities' id='current_state' require1='${type}==PhoneState' require2='${current_state}==me_in_a_call'/> <destination context='this entity' id='state'/> </assign>
Another assign is needed to revert the remotely monitored extension state back when we are done with our actions. The assign will be executed on the release of the button:
<assign when='on release'> <source context='this entity' id='subscr_state'/> <destination context='this entity' id='state'/> </assign>
Our last step would be to add an Action if we are in a call. We will use a URL action, but this action will occur only if we (our extension) is in a call:
<url target='http://my-remote-uri' when='on release' states='me_in_a_call'/>
Also, we change the when attributes on our invite elements from "on press" to "on release" to eliminate unexpected calls being done.
Here is how these additions would look like inside an XML Definition:
<general type="BusyLampField"/> <initialization> <state value="off"/> <variable name="subscr_uri" value="sip:435@10.110.22.35"/> <variable name='subscr_state' value='initial'/> <variable name='subscr_num' value='' /> </initialization> <subscription type="dialog" to="<$(subscr_uri)>" for="$(subscr_uri)"/> <NotifyParsingRules type="applies"> <level1 translates_to='OK'>/dialog-info[@entity="$(subscr_uri)"]</level1> </NotifyParsingRules> <NotifyParsingRules type="state"> <level1 translates_to="ringing">/dialog-info/dialog/state[.="early"]</level1> <level1-1 translates_to="calling">/dialog-info/dialog[@direction="initiator"]</level1-1> <level2 translates_to="ringing">/dialog-info/dialog/state[.="proceeding"]</level2> <level2-1 translates_to="calling">/dialog-info/dialog[@direction="initiator"]</level2-1> <level3 translates_to="offhook">/dialog-info/dialog/state[.="trying"]</level3> <level4 translates_to="in_a_call">/dialog-info/dialog/state[.="confirmed"]</level4> <level5 translates_to="free"/> </NotifyParsingRules> <NotifyParsingRules type="variable" id="call_id" state="ringing"> <level1 fetch_attribute="call-id">/dialog-info/dialog[@call-id]</level1> </NotifyParsingRules> <NotifyParsingRules type="variable" id="remote_tag" state="ringing"> <level1 fetch_attribute="remote-tag">/dialog-info/dialog[@remote-tag]</level1> </NotifyParsingRules> <NotifyParsingRules type="variable" id="local_tag" state="ringing"> <level1 fetch_attribute="local-tag">/dialog-info/dialog[@local-tag]</level1> </NotifyParsingRules> <NotifyParsingRules type="variable" id="remote_uri" state="ringing"> <level1 fetch_attribute="uri">/dialog-info/dialog/remote/target[@uri]</level1> </NotifyParsingRules> <action> <assign when='on press'> <source context='local' id='subscr_uri' value_match='(sip:)?(.*)@.*' value_replace='$2'/> <destination context='local' id='subscr_num'/> </assign> <assign when='on press'> <source context='this entity' id='state'/> <destination context='this entity' id='subscr_state'/> </assign> <assign when='on press'> <source context='all xml entities' id='current_state' require1='${type}==PhoneState' require2='${current_state}==me_in_a_call'/> <destination context='this entity' id='state'/> </assign> <url target='http://my-remote-uri' when='on release' states='me_in_a_call'/> <invite target="$(subscr_uri)" when="on release" states="calling, in_a_call, offhook, free"/> <invite target="$(remote_name)<$(remote_uri)>" when="on release" state="ringing" request_uri="$(remote_uri)" replaces="$(call_id);to-tag=$(remote_tag);from-tag=$(local_tag)"/> <assign when='on release'> <source context='this entity' id='subscr_state'/> <destination context='this entity' id='state'/> </assign> </action>
Other actions (DTMF, Access phone modules)
Some other actions are possible aside from invite, URL, and assign.
It is possible to send a DTMF code with a * (asterisk) and the number of the remotely monitored extension when pressing this button:
<dtmf target='*$(subscr_num)' when='on press'/>
It is also possible to use the Request action to access information and functions of other phone modules (CallInterface, BusyLampField, PUI, etc).
See the XML Definition syntax for detailed information regarding actions.
Re-using the same XMLÂ Definition for more buttons
It can be useful to use the same XML definition code for more buttons so that we don't have to write the same code repeatedly. Also, sometimes we would like to allow phone users to configure their own buttons based on our custom XML but without them having to know XML to make the configuration. For these reasons, Snom phones allow replacement_plan elements. These allow you to change the default functionality of a function key button to your own (designed with XML Definition Code). Once you have provisioned the replacement (and rebooted the phone) there will be a new value (its name is according to the key id from the XML definition code) in the Function Keys page under Type:
We need only a few small changes to write a simple replacement plan for our BLF XML definition from section Customized extension monitoring:
- Add the XML code into a ReplacementPlan section
- Inside the ReplacementPlan section, we add a subsection called key: <key id="CustomBLF"> . This defines the name of the entry in the Function Keys page under Type
- We put the XML definition code inside the key subsection
- Instead of writing the extension name inside the initialization section, we add only the server IP there and for the extension number, we will take the text that the user enters in the web interface. This text will be available to us within the XML code as variable $(ui_argument)
Here is the resulting ReplacementPlan example:
<ReplacementPlan> <key id="CustomBLF"> <general type="SampleCustomBLF" /> <initialization> <variable name="subscr_proxy" value="10.110.22.35"/> <state value="off"/> </initialization> <subscription type="dialog" to="<sip:$(ui_argument)@$(subscr_proxy)>" for="$(ui_argument)@$(subscr_proxy)"/> <NotifyParsingRules type="applies"> <level1 translates_to='OK'>/dialog-info[@entity="sip:$(ui_argument)@$(subscr_proxy)"]</level1> </NotifyParsingRules> <NotifyParsingRules type="state"> <level1 translates_to="ringing">/dialog-info/dialog/state[.="early"]</level1> <level1-1 translates_to="calling">/dialog-info/dialog[@direction="initiator"]</level1-1> <level2 translates_to="ringing">/dialog-info/dialog/state[.="proceeding"]</level2> <level2-1 translates_to="calling">/dialog-info/dialog[@direction="initiator"]</level2-1> <level3 translates_to="offhook">/dialog-info/dialog/state[.="trying"]</level3> <level4 translates_to="in_a_call">/dialog-info/dialog/state[.="confirmed"]</level4> <level5 translates_to="free"/> </NotifyParsingRules> <NotifyParsingRules type="variable" id="call_id" state="ringing"> <level1 fetch_attribute="call-id">/dialog-info/dialog[@call-id]</level1> </NotifyParsingRules> <NotifyParsingRules type="variable" id="remote_tag" state="ringing"> <level1 fetch_attribute="remote-tag">/dialog-info/dialog[@remote-tag]</level1> </NotifyParsingRules> <NotifyParsingRules type="variable" id="local_tag" state="ringing"> <level1 fetch_attribute="local-tag">/dialog-info/dialog[@local-tag]</level1> </NotifyParsingRules> <NotifyParsingRules type="variable" id="remote_uri" state="ringing"> <level1 fetch_attribute="uri">/dialog-info/dialog/remote/target[@uri]</level1> </NotifyParsingRules> <action> <invite target="$(ui_argument)" when="on press" states="calling, in_a_call, offhook, free"/> <invite target="$(remote_name)<$(remote_uri)>" when="on press" state="ringing" request_uri="$(remote_uri)" replaces="$(call_id);to-tag=$(remote_tag);from-tag=$(local_tag)"/> </action> </key> </ReplacementPlan>
This code needs to be uploaded to the phone with Auto-Provisioning, either inside the <settings> tag or as an individual XML file which is being listed in the <setting-files> -> <file> tag.
- Make sure you are logged in as an administrator on the phone
-
<fkey idx="2" context="active" perm="">!!§[::]!!§[replacement-key:CustomBLF,ui_argument=436,ui_label=John 436]</fkey>
Another example of using a replacement plan (for the Broadsoft environment) can be found here.
Troubleshooting
Syntax check XML Definition Code
The parts of an XML Definition Code where a syntax error exists will not be saved on the phone. For this reason, it is important to check the syntax of your XML definition code in case you have any issues.
There are various XML syntax check tools online which you can use. Another way to check if your XML Definition Code has Syntax issues is by downloading the phone settings after the Code has been inserted onto the phone.
Check the current values of the variables
It might be useful to know which value or what variables have been used/set in the code. To do that we are going to open the following URL in our Browsers: http://<PHONE_IP>/xml_entities.htm
There we will find all the xml_entities split into categories of button and setting types:
- LineKey are the free programmable function keys
- ContextKey are the 4 keys below the display
- NaviKey the navigation keys
- HardKey are the hardcoded keys like dnd, directory, menu, transfer, etc
- GeneralPurposeXml are the general_purpose_xml_descriptions settings on the phone
- XmlsForEventListUri are the user_event_list_uri settings on the phone
By selecting a category you will get a list of indexes for that button or setting, each of them can contain an XML Definition Code.
XML definitions with a button
- Make sure you are logged in as an administrator on the phone
- Open your browser at the following address: http://<PHONE_IP>/xml_entities.htm
- Click on LineKey
- You will see a page similar to this:
- The left numbers match the function key index, with one note: internally the phone keys are indexed starting with 0, so for P1 you must click on "0", for P2 click on "1" etc.
For example let's click the BLF example, in the above image that would be P6. The phone will show something like this:
When the state value changes you can refresh this page to see the new value. For example for ringing: - It is also possible to access the values for this XML definition directly by opening the browser directly at address http://<PHONE_IP>/xml_entities.htm?type=LineKey&index=<INDEX>
XML definitions without a button (general_purpose)
- Make sure you are logged in as an administrator on the phone
- Open your browser at the following address: http://<PHONE_IP>/xml_entities.htm
- Click on GeneralPurposeXml
- Now you can see the values in a similar way to the XML definitions with a button above.
Phone log
Logs when receiving NOTIFY
When receiving a NOTIFY for a subscription, you can find detailed information in the Phone's Logs from Level 9 DEBUG2. Here is an example for state ringing (monitored extension is 434):
Sep 30 16:10:02.489 [DEBUG1] SIP: recv NOTIFY (107: 313630313437343936323131373639-qr8mtzbo7neh) <- Udp:10.110.22.35:5060 Sep 30 16:10:02.490 [INFO ] SIP: ClearRefreshNotifyTimeout 313630313437343936323131373639-qr8mtzbo7neh Sep 30 16:10:02.492 [INFO ] PHN: Xpath applies: /dialog-info[@entity="sip:434@10.110.22.35"] Sep 30 16:10:02.492 [INFO ] PHN: Xpath applies: @entity="sip:434@10.110.22.35"] Sep 30 16:10:02.492 [DEBUG2] PHN: Xpath matched element node dialog-info Sep 30 16:10:02.492 [INFO ] PHN: Xpath applies: /dialog-info[@entity="sip:434@10.110.22.35"] Sep 30 16:10:02.492 [INFO ] PHN: Xpath applies: @entity="sip:434@10.110.22.35"] Sep 30 16:10:02.492 [DEBUG2] PHN: Xpath matched element node dialog-info Sep 30 16:10:02.493 [DEBUG1] TOOLS: NotifyParsingRule returns match-translation which is 'OK' <--------- for the <NotifyParsingRules type="applies"> rule ... Sep 30 16:10:02.493 [DEBUG1] TOOLS: NotifyParsingRule returns match-translation which is 'ringing' <--------- for the <NotifyParsingRules type="state"> rule
Logs for assign elements
You can find the assign elements in the Phone's Logs from Level 9 DEBUG2. Below an excerpt of the log with level 9, for the setting toggle XML Definition Code:
Sep 28 13:12:22.133 [DEBUG2] PHN: AssignActionOperands::Assign: Determined source-values "on" for XmlEntity.LineKey.006 <assign when="on press" states="off"> <source value="on"/> <destination context="this entity" id="state"/> </assign> Sep 28 13:12:22.133 [DEBUG2] PHN: gui_keys: on key event (P7, 0), from_hardware 0, is_longpressed 0, State Idle (27), time 554553 ... Sep 28 13:12:22.262 [INFO ] PHN: WEB: Request 49/341/0: GET /dummy.htm?settings=save&show_clock=on HTTP/1.1 ...
Further Information
Related articles