iCasa framework provides a device model that must be used to create new devices. In this model, each device has to publish a description based in a Java interface and a set of properties. Device interface is used to expose device functionality, on the other hand properties set is used to known device state.
To integrate a new device into the iCasa framework the device has to implement the fr.liglab.adele.icasa.device.GenericDevice interface. The GenericDevice interface defines methods to get the device unique identifier (getSerialNumber()), to access its state (getState()) and its fault situation (getFault()), and also a method to interrogate device to obtain its properties values (getPropertyValue(String propertyName)).
In addition, each device has the possibility of notify clients about changes in its state (when its properties' values have been changed) using the listener pattern. The device sends events to its listeners using a callback method (devicePropertyModified(GenericDevice device, String propertyName, Object oldValue, Object newValue)). Events sent must implement the Java interface fr.liglab.adele.icasa.device.DeviceEvent and listeners must implement fr.liglab.adele.icasa.device.DeviceListener interface.
iCasa framework provides an abstract implementation class for GenericDevice interface: fr.liglab.adele.icasa.device.util.AbstractDevice. This abstract class eases the development of new devices implementation by inheriting from it. There exists mainly two types of devices in iCasa: devices using a scope zone and localization (thermometer gets the temperature of a room by example) and devices that are independent of their localization i.e. a bathroom scale.
Testing and debugging pervasive applications is a difficult task because usually developers have not access to all physical devices. On the other hand is not easy to generate the adequate events in the right moment in order to produce conditions for testing applications in different execution scenarios. For this reason iCasa framework includes a simulation module allowing testing and debugging of pervasive applications. The iCasa simulation module provides a set of prebuilt simulated devices, it is extensible, allowing developers to build new types of simulated devices. Every simulated device is a full compliant iCasa device, but simulated device must implement fr.liglab.adele.icasa.simulator.SimulatedDevice interface to be recognized by the iCasa simulation framework.
In this section we will show how to extend the iCasa simulation module adding a new simulated device that uses a scope zone; the thermometer device. The section is divided in 4 subsections:
As show in the next code fragment, the description of the Thermometer device includes is made in a Java interface (fr.liglab.adele.icasa.device.temperature.Thermometer) and a set of properties (state, fault and currenttemperature). Usually the properties name are defined as static fields (constants) of the Java interface as THERMOMETERCURRENT_TEMPERATURE .
The Thermometer java interface is as follows
package fr.liglab.adele.icasa.device.temperature;
import fr.liglab.adele.icasa.device.GenericDevice;
public interface Thermometer extends GenericDevice {
static String THERMOMETER_CURRENT_TEMPERATURE = "current_temperature"; // Temperature Property
double getTemperature();
}
To access device current temperature the client has two options, call the method getTemperature() or ask for the current_temperature property.
Once the device interface defined a implementation class to this device must be provided.
package fr.liglab.adele.icasa.device.temperature.impl;
// Imports section omitted
@Component(name = "iCasa.Thermometer")
@Provides(properties = { @StaticServiceProperty(type = "java.lang.String", name = Constants.SERVICE_DESCRIPTION) })
public class SimulatedThermometerImpl extends AbstractDevice implements Thermometer, SimulatedDevice {
@ServiceProperty(name = Thermometer.DEVICE_SERIAL_NUMBER, mandatory = true)
private String m_serialNumber;
private volatile Zone m_zone;
private ZoneListener listener = new ThermometerZoneListener();
public SimulatedThermometerImpl() {
super();
// Property initialization
setPropertyValue(Thermometer.THERMOMETER_CURRENT_TEMPERATURE, 0.0);
}
@Override
public String getSerialNumber() {
return m_serialNumber;
}
@Override
public synchronized double getTemperature() {
return (Double) getPropertyValue(Thermometer.THERMOMETER_CURRENT_TEMPERATURE);
}
@Override
public void enterInZones(List<Zone> zones) {
if (!zones.isEmpty()) {
for (Zone zone : zones) {
if (zone.getVariableValue("Temperature") != null) {
m_zone = zone;
getTemperatureFromZone();
// Zone listener registration
m_zone.addListener(listener);
break;
}
}
}
}
@Override
public void leavingZones(List<Zone> zones) {
setPropertyValue(Thermometer.THERMOMETER_CURRENT_TEMPERATURE, null);
// Zone listener unregistration
if (m_zone != null)
m_zone.removeListener(listener);
}
private void getTemperatureFromZone() {
if (m_zone != null) {
Object currentTemperature = m_zone.getVariableValue("Temperature");
if (currentTemperature != null)
setPropertyValue(Thermometer.THERMOMETER_CURRENT_TEMPERATURE, currentTemperature);
}
}
// Zone listener implementation
class ThermometerZoneListener extends BaseZoneListener {
@Override
public void zoneVariableModified(Zone zone, String variableName, Object oldValue, Object newValue) {
if (m_zone == zone) {
if (!(getFault().equalsIgnoreCase("yes")))
if (variableName.equals("Temperature"))
getTemperatureFromZone();
}
}
}
}
The device's properties should be initialized for two reasons: - to establish the initial state of the device - to avoid any problem of associated to null pointers in the device implementation
Usually, this initialization should be done in the constructor as show in the class SimulatedThermometerImpl above.
As is shown in the SimulatedThermometerImpl class two methods have been implemented: enterInZones and leaveZones, these methods are callback methods defined in GenericDevice interface. iCasa platform call these methods when de device is placed in a new Zone to indicate its new scope zones and which are it leaving. In our example the device will try to find the first Zone that contains the Temperature variable. Then, the simulated device uses the Temperature variable to obtain the value used in to set its property current_temperature
The SimulatedThermometerImpl class defines an inner class ThermometerZoneListener to be notified when variables in its scope zone have been modified. Our listener is interested in changes in Temperature variable in the device's scope zone. This listener has to be subscribed to zone events as is done in enterInZones method ( mzone.addListener(listener)_ ), and in leavingZones method the listener is unregistered.
The iCasa platform has been built on top of the OSGi and iPOJO technologies. Each device to be incorporated to iCasa framework has to be built as an iPOJO component and deployed in the runtime as an OSGi Bundle. Some iPOJO annotations are defined in the previous implementation class, these annotations (@Component, @Provides) indicates the iPOJO manipulator tool and iPOJO runtime how to deal with the component. In our example we are indicating that the component is called "iCasa.Thermometer" and that it is providing all implemented interfaces (GenericDevice, SimulatedDevice and Thermometer) as OSGi services. To know more about the iPOJO compoenent model see this and for OSGI this.
You can use maven tool to build a device project. Two iCasa maven artifacts are necessary to build your project, the first one context.api defines the interfaces used in the Device model, the simulation.api provides the SimulatedDevice interface.
Artifacs :
Context API - Device interfaces
<groupId>fr.liglab.adele.icasa</groupId>
<artifactId>context.api</artifactId>
<version>1.0.0-SNAPSHOT</version>
Simulation API - Simulated Device interface
<groupId>fr.liglab.adele.icasa</groupId>
<artifactId>simulator.api</artifactId>
<version>1.2.3-SNAPSHOT</version>
Repositories :
<repositories>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>adele-central-snapshot</id>
<name>adele-repos</name>
<url>http://repository-icasa.forge.cloudbees.com/snapshot</url>
</repository>
<repository>
<snapshots />
<id>snapshots</id>
<name>adele-central-release</name>
<url>http://repository-icasa.forge.cloudbees.com/release</url>
</repository>
</repositories>
The pom file used in iCasa simulator module to build the simulated Thermometer is shown in the next fragment code. In addition to the two iCasa artifacts this pom includes other dependencies to iPojo and OSGi code. Finally, the pom defines the utilization of two plugins to iPOJO manipulation and OSGi bundle packaging.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- Project coordinates -->
<artifactId>device.temperature</artifactId>
<packaging>bundle</packaging>
<version>1.0.0-SNAPSHOT</version>
<!-- Project repositories -->
<repositories>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>adele-central-release</id>
<name>adele-repos</name>
<url>http://repository-icasa.forge.cloudbees.com/release</url>
</repository>
<repository>
<snapshots />
<id>snapshots</id>
<name>adele-central-snapshot</name>
<url>http://repository-icasa.forge.cloudbees.com/snapshot</url>
</repository>
</repositories>
<!-- Project dependencies -->
<dependencies>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.ipojo</artifactId>
<version>1.8.2</version>
</dependency>
<dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.ipojo.annotations</artifactId>
<version>1.8.2</version>
</dependency>
<dependency>
<groupId>fr.liglab.adele.icasa</groupId>
<artifactId>context.api</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>fr.liglab.adele.icasa</groupId>
<artifactId>simulator.api</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.3.7</version>
<configuration>
<instructions>
<Private-Package>fr.liglab.adele.icasa.device.temperature.impl</Private-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-ipojo-plugin</artifactId>
<version>1.8.6</version>
</plugin>
</plugins>
</build>