Skip to content

Commit

Permalink
Add (partial) support for IoT XEPs
Browse files Browse the repository at this point in the history
That is XEP-0323, -0324, -0325, and -0347.

SMACK-727.
  • Loading branch information
Flowdalic committed Jul 21, 2016
1 parent d1fe5c2 commit b91978d
Show file tree
Hide file tree
Showing 110 changed files with 5,395 additions and 40 deletions.
2 changes: 2 additions & 0 deletions documentation/developer/integrationtest.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ debug=true
| accountOnePassword | Password of the first XMPP account |
| accountTwoUsername | Username of the second XMPP account |
| accountTwoPassword | Password of the second XMPP account |
| accountThreeUsername | Username of the third XMPP account |
| accountThreePassword | Password of the third XMPP account |
| debug | 'true' to enable debug output |
| enabledTests | List of enabled tests |
| disabledTests | List of disabled tests |
Expand Down
8 changes: 6 additions & 2 deletions documentation/extensions/index.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Smack Extensions User Manual
smackSmack Extensions User Manual
============================

The XMPP protocol includes a base protocol and many optional extensions
Expand Down Expand Up @@ -77,8 +77,12 @@ Experimental Smack Extensions and currently supported XEPs of smack-experimental
| Name | XEP | Description |
|---------------------------------------------|----------------------------------------------------------|----------------------------------------------------------------------------------------------------------|
| Message Carbons | [XEP-0280](http://xmpp.org/extensions/xep-0280.html) | Keep all IM clients for a user engaged in a conversation, by carbon-copy outbound messages to all interested resources.
| [HTTP over XMPP transport](hoxt.md) | [XEP-0332](http://xmpp.org/extensions/xep-0332.html) | Allows to transport HTTP communication over XMPP peer-to-peer networks. |
| [Internet of Things - Sensor Data](iot.md) | [XEP-0323](http://xmpp.org/extensions/xep-0323.html) | Sensor data interchange over XMPP. |
| [Internet of Things - Provisioning](iot.md) | [XEP-0324](http://xmpp.org/extensions/xep-0324.html) | Provisioning, access rights and user priviliges for the Internet of Things. |
| [Internet of Things - Control](iot.md) | [XEP-0325](http://xmpp.org/extensions/xep-0325.html) | Describes how to control devices or actuators in an XMPP-based sensor netowrk. |
| [HTTP over XMPP transport](hoxt.md) | [XEP-0332](http://xmpp.org/extensions/xep-0332.html) | Allows to transport HTTP communication over XMPP peer-to-peer networks. |
| JSON Containers | [XEP-0335](http://xmpp.org/extensions/xep-0335.html) | Encapsulation of JSON data within XMPP Stanzas. |
| [Internet of Things - Discovery](iot.md) | [XEP-0347](http://xmpp.org/extensions/xep-0347.html) | Describes how Things can be installed and discovered by their owners. |
| Google GCM JSON payload | n/a | Semantically the same as XEP-0335: JSON Containers |
| Client State Indication | [XEP-0352](http://xmpp.org/extensions/xep-0352.html) | A way for the client to indicate its active/inactive state. |

Expand Down
116 changes: 116 additions & 0 deletions documentation/extensions/iot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
Internet of Things (XEP-0323, -0324, -0325, -0347)
==================================================

The Internet of Things (IoT) XEPs are an experimental open standard how XMPP can be used for IoT. They currently consists of
- XEP-0323 Sensor Data
- XEP-0324 Provisioning
- XEP-0325 Control
- XEP-0326 Concentrators
- XEP-0347 Discovery

Smack only supports a subset of the functionality described by the XEPs!

Thing Builder
-------------

The `org.jivesoftware.smackx.iot.Thing` class acts as basic entity representing a single "Thing" which can used to retrieve data from or to send control commands to. `Things` are constructed using a builder API.


Reading data from things
------------------------

For example, we can build a Thing which provides the current temperature with

```java
Thing dataThing = Thing.builder().setKey(key).setSerialNumber(sn).setMomentaryReadOutRequestHandler(new ThingMomentaryReadOutRequest() {
@Override
public void momentaryReadOutRequest(ThingMomentaryReadOutResult callback) {
int temp = getCurrentTemperature();
IoTDataField.IntField field = new IntField("temperature", temp);
callback.momentaryReadOut(Collections.singletonList(field));
}
}).build();
```

While not strictly required, most things are identified via a key and serial number. We also build the thing with a "momentary read out request handler" which when triggered, retrieved the current temperature and reports it back to the requestor.

After the `Thing` is build, it needs to be made available so that other entities within the federated XMPP network can use it. Right now, we only intall the Thing in the `IoTDataManager`, which means the thing will act on read out requests but not be managed by a provisioning server.

```java
IoTDataManager iotDataManager = IoTDataManager.getInstanceFor(connection);
iotDataManager.installThing(thing);
```

The data can be read out also by using the `IoTDataManager`:

```java
FullJid jid =
List<IoTFieldsExtension> values = iotDataManager.requestMomentaryValuesReadOut(jid);
```

Now you have to unwrap the `IoTDataField` instances from the `IoTFieldsExtension`. Note that Smack currently only supports a subset of the specified data types.

Controlling a thing
-------------------

Things can also be controlled, e.g. to turn on a light. Let's create thing which can be used to turn the light on and off.

```java
Thing controlThing = Thing.builder().setKey(key).setSerialNumber(sn).setControlRequestHandler(new ThingControlRequest() {
@Override
public void processRequest(Jid from, Collection<SetData> setData) throws XMPPErrorException {
for (final SetData data : setData) {
if (!data.getName().equals("light")) continue;
if (!(data instanceof SetBoolData)) continue;
SetBoolData boolData = (SetBoolData) data;
setLight(boolData.getBooleanValue());
}
}
}).build();
```

No we have to install this thing into the `IoTControlManager`:

```java
IoTControlManager iotControlManager = IoTControlManager.getInstanceFor(connection);
iotControlManager.installThing(thing);
```

The `IoTControlManager` can also be used to control a thing:

```java
FullJid jid =
SetData setData = new SetBoolData("light", true);
iotControlManager.setUsingIq(jid, setData);
```

Smack currently only supports a subset of the possible data types for set data.

Discovery
---------

You may wondered how a full JIDs of things can be determined. One approach is using the discovery mechanisms specified in XEP-0347. Smack provides the `IoTDiscoveryManager` as API for this.

For example, instead of just installing the previous things in the `IoTDataManager` and/or `IoTControlManager`, we could also use the `IoTDiscoveryManger` to register the thing with a registry. Doing thing also installs the thing in the `IoTDataManager` and the `IoTControlManager`.

```java
IoTDiscoveryManager iotDiscoveryManager = IoTDiscoveryManager.getInstanceFor(connection);
iotDiscovyerManager.registerThing(thing);
```

The registry will now make the thing known to a broader audience, and available for a potential owner.

The `IoTDiscoveryManager` can also be used to claim, disown, remove and unregister a thing.

Provisioning
------------

Things can usually only be used by other things if they are friends. Since a thing normally can't decide on its own if an incoming friendship request should be granted or not, we can delegate this decission to a provisioning service. Smack provides the `IoTProvisinoManager` to deal with friendship and provisioning.

For example, if you want to befriend another thing:

```java
BareJid jid =
IoTProvisioningManager iotProvisioningManager = IoTProvisioningManager.getInstanceFor(connection);
iotProvisioningManager.sendFriendshipRequest(jid);
```
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,11 @@ public void run() {
// Makes a reconnection attempt
try {
if (isReconnectionPossible(connection)) {
connection.connect();
try {
connection.connect();
} catch (SmackException.AlreadyConnectedException e) {
LOGGER.log(Level.FINER, "Connection was already connected on reconnection attempt", e);
}
}
// TODO Starting with Smack 4.2, connect() will no
// longer login automatically. So change this and the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,7 @@ public EmptyResultIQ() {

public EmptyResultIQ(IQ request) {
this();
if (!(request.getType() == Type.get || request.getType() == Type.set)) {
throw new IllegalArgumentException(
"IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
}
setStanzaId(request.getStanzaId());
setFrom(request.getTo());
setTo(request.getFrom());
initialzeAsResultFor(request);
}

@Override
Expand Down
13 changes: 11 additions & 2 deletions smack-core/src/main/java/org/jivesoftware/smack/packet/IQ.java
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,17 @@ else if (childElementName != null) {
*/
protected abstract IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml);

protected final void initialzeAsResultFor(IQ request) {
if (!(request.getType() == Type.get || request.getType() == Type.set)) {
throw new IllegalArgumentException(
"IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());
}
setStanzaId(request.getStanzaId());
setFrom(request.getTo());
setTo(request.getFrom());
setType(Type.result);
}

/**
* Convenience method to create a new empty {@link Type#result IQ.Type.result}
* IQ based on a {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}
Expand Down Expand Up @@ -289,9 +300,7 @@ public static ErrorIQ createErrorResponse(final IQ request, final XMPPError.Cond
* @throws IllegalArgumentException if the IQ stanza(/packet) does not have a type of
* {@link Type#get IQ.Type.get} or {@link Type#set IQ.Type.set}.
* @return a new {@link Type#error IQ.Type.error} IQ based on the originating IQ.
* @deprecated use {@link #createErrorResponse(IQ, org.jivesoftware.smack.packet.XMPPError.Builder)} instead.
*/
@Deprecated
public static ErrorIQ createErrorResponse(final IQ request, final XMPPError error) {
return createErrorResponse(request, XMPPError.getBuilder(error));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ public final class Presence extends Stanza implements TypedCloneable<Presence> {
* @param type the type.
*/
public Presence(Type type) {
// Ensure that the stanza ID is set by calling super().
super();
setType(type);
}

Expand All @@ -85,6 +87,8 @@ public Presence(Type type) {
* @param mode the mode type for this presence update.
*/
public Presence(Type type, String status, int priority, Mode mode) {
// Ensure that the stanza ID is set by calling super().
super();
setType(type);
setStatus(status);
setPriority(priority);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.util.Date;
import java.util.Locale;

import org.jivesoftware.smack.SmackException;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
Expand Down Expand Up @@ -122,6 +123,14 @@ public static boolean getBooleanAttribute(XmlPullParser parser, String name,
}
}

public static int getIntegerAttributeOrThrow(XmlPullParser parser, String name, String throwMessage) throws SmackException {
Integer res = getIntegerAttribute(parser, name);
if (res == null) {
throw new SmackException(throwMessage);
}
return res;
}

public static Integer getIntegerAttribute(XmlPullParser parser, String name) {
String valueString = parser.getAttributeValue("", name);
if (valueString == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,10 @@ public XmlStringBuilder attribute(String name, String value) {
return this;
}

public XmlStringBuilder attribute(String name, boolean bool) {
return attribute(name, Boolean.toString(bool));
}

/**
* Add a new attribute to this builder, with the {@link java.util.Date} instance as its value,
* which will get formated with {@link XmppDateTime#formatXEP0082Date(Date)}.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.iot;

import org.jivesoftware.smack.SmackException;

public class IoTException extends SmackException {

/**
*
*/
private static final long serialVersionUID = 1L;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
*
* Copyright 2016 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.iot;

import java.util.Map;
import java.util.WeakHashMap;

import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.XMPPConnection;

public final class IoTManager extends Manager {

private static final Map<XMPPConnection, IoTManager> INSTANCES = new WeakHashMap<>();

public static synchronized IoTManager getInstanceFor(XMPPConnection connection) {
IoTManager manager = INSTANCES.get(connection);
if (manager == null) {
manager = new IoTManager(connection);
INSTANCES.put(connection, manager);
}
return manager;
}

private IoTManager(XMPPConnection connection) {
super(connection);
}

}
Loading

0 comments on commit b91978d

Please sign in to comment.