Window-Heating-Control Example Application

Last modified by David Nestle on 2018/06/14 17:56

In this tutorial app we want to present how an application with some real application logic is realized. We want to create an application that turns down the setpoint of the heating thermostat in a room when the window is opened. Opening the window shall be detected by a window sensor. This could also be done by sensing a temperature drop at the thermostat, but for this first simple app we will not evaluate this. On the other hand, the app will take into account the possibility to have multiple thermostats and/or window sensors in one room right from the beginning. In a second step we add an option that assumes a battery is installed in the building. When the app also finds a battery system and the battery is fully loaded the heating is not turned down even when the window is opened as we assume that energy (e.g. from PV) is available abundently. This is a very much simplied assumption, of course, but we want to demonstrate here how such interaction can be coded in OGEMA. It is up to you to further develop this app to be really useful if you can test it in a real environment.

The source code of the sample app is available on Github.

Contents

Setup

Create a new app: File -> New -> Project... -> Ogema -> App, and name it window-heat-control. Select the type App without GUI. Click Finish -> the new project is created. Wait for the build process to finish, so that the errors disappear, if they don't, update the Maven dependencies with Alt+F5.

First of all we have to identify devices we want to access. In the first step we are interested in thermostats and window sensors. To find the respective data models, you may

  • Use the OGEMA search (open with Ctrl+H in Eclipse)
  • Browse the documentation, in particular the org.ogema.model... packages
  • Create the resources with a suitable hardware driver based on real devices (not really practical for now), or
  • Create simulated objects and take a look into the Resource View to find out the models and their structure (this will be explained in the next step of the tutorial)

You will soon find out that we are looking for the model Thermostat and for the model DoorWindowSensor. To add the pattern use the same steps as shown for the sensor-app, i.e. right-click the project, choose OGEMA -> Add OGEMA pattern..., and create a pattern for each of the two device types as shown below (change the pattern class names to WindowSensorPattern and ThermostatPattern):

WindowSensorPatternCreateDialog.png

For the Thermostat you can use a standard pattern, containing all standard values of the model offered by different drivers:

ThermostatPatternCreateDialog.png

In the next step we declare the elements to be accessed in the pattern. The most important decision is whether an element is required (default, if no annotation is present) or optional. The AccessMode can declare read-only, read-write-shared (default) or read-write-exclusive access. Exclusive access should be registered when parallel write access by another application shall be prevented, but we do not need this here. The pattern members should look like this in the first step (both patterns need to be modified, and the missing imports resolved):

public class WindowSensorPattern extends ResourcePattern<DoorWindowSensor> {

   public final BooleanResource open = model.reading();

   public final Room room = model.location().room();

   /**
     * Constructor for the access pattern. This constructor is invoked by the framework. Must be public.
     */

   public WindowSensorPattern(Resource device) {
       super(device);
   }
}
public class ThermostatPattern extends ResourcePattern<Thermostat> {

   /**Providing drivers: Homematic*/
   @Existence(required = CreateMode.MUST_EXIST)
   private final TemperatureSensor tempSens = model.temperatureSensor();

   /**Providing drivers: Homematic*/
   @Existence(required = CreateMode.MUST_EXIST)
   @Access(mode = AccessMode.SHARED)
   public final TemperatureResource setpoint = tempSens.settings().setpoint();

   @Existence(required = CreateMode.OPTIONAL)
   @Access(mode = AccessMode.READ_ONLY)
   private final TemperatureResource setpointFeedback = tempSens.deviceFeedback().setpoint();

   @Existence(required = CreateMode.OPTIONAL)
   public final TemperatureResource reading = tempSens.reading();

   @Existence(required = CreateMode.MUST_EXIST)
   public final Room room = model.location().room();

   /**
     * Constructor for the access pattern. This constructor is invoked by the framework.
     */

   public ThermostatPattern(Resource device) {
       super(device);
    }
}

Setup the rooms management

When a fitting resource is found (this may be a top-level resource or a sub resource) the patternAvailable callback within the respective ResourcePatternListener is called. You can remove the processInterdependies() calls there, also in the patternUnavailable callback.

The next task for our app is to interlink thermostats and window sensors via the rooms in which they are installed. This is a very common task. As this needs information from more than one listener class it has to be organized in the controller. Add LinkingResourceManagement members to the controller (class WindowHeatControlController), which sort the resources found according to their rooms, for thermostats and window sensors (note: the appConfigData, thermostatListener and windowSensorListener fields should already exist in the class):

public WindowHeatControl2Config appConfigData;
// keeps track of window sensor -> room associations
private final LinkingResourceManagement<Room, WindowSensorPattern> windowSensors;
// keeps track of thermostat -> room associations
private final LinkingResourceManagement<Room, ThermostatPattern> thermostats;

public WindowHeatControl2Controller(ApplicationManager appMan) {
       this.appMan = appMan;
       this.log = appMan.getLogger();
       this.advAcc = appMan.getResourcePatternAccess();
 
        initConfigurationResource();
       this.windowSensors = new LinkingResourceManagement<>();
       this.thermostats = new LinkingResourceManagement<>();
        initDemands();
}

Next we introduce a RoomLogic class (in the main package: right-click the package com.example.app.windowheatcontrol -> New -> Class -> enter class name RoomLogic) - we will create one instance per room in the system, which will manage the devices in the room:

public class RoomLogic {

   private final Room room;
   // provides access to the thermostats in this room, which may change dynamically
   private final LinkingManagementAccess<Room, ThermostatPattern> thermostats;
   // provides access to the window sensors in this room, which may change dynamically
   private final LinkingManagementAccess<Room, WindowSensorPattern> windowSensors;

   public RoomLogic(Room room,
                          LinkingManagementAccess<Room, ThermostatPattern> thermostats,
                          LinkingManagementAccess<Room, WindowSensorPattern> windowSensors) {
       this.room = room;
       this.thermostats = thermostats;
       this.windowSensors = windowSensors;
   }

   // new thermostat has been added, or old one removed
   public void settingsChanged() {
       // TODO
   }

   public void addWindowSensor(WindowSensorPattern pattern) {
       // TODO
   }

   public void removeWindowSensor(WindowSensorPattern sensor) {
       // TODO
   }

}

We do not pass the LinkingResourceManagement itself to the room controller, but the LinkingManagementAccess, so that the room controller for a given room only gets access to the devices in its own room. Later on, we will see how this works. We do not create any instances of the room controller yet; they will be created as soon as a matching device for a room is available. Add the following line to the variable declarations in WindowHeatControlController:

private final Map<Room, RoomLogic> roomControllers = new HashMap<>();

and a new method 

public RoomLogic getController(Room room) {
    room = room.getLocationResource();
    RoomLogic controller = roomControllers.get(room);
   if (controller == null) {
        controller = new RoomLogic(room,
                                        thermostats.getSingleResourceManagement(room),
                                        windowSensors.getSingleResourceManagement(room));
        roomControllers.put(room, controller);
    }
    return controller;
}

Take actions when new devices appear: Pattern listeners and Management Logic

See Take actions when new devices appear:Pattern listeners, Management Logic and Running the App

Adding battery access

See Adding battery access

Excursion: Resource locations and paths

See Resource locations and paths

Excursion: Room access in patterns

See Room Access in Patterns and Using the Pattern Debugger

Next: Testing the Application

In order to properly test the application, we need a setting where at least one room with thermostats and window sensors is available, and optionally a battery.
Usually such devices are provided by drivers. As we do not have real drivers available in our SDK sample development space, and it would be cumbersome if we could only test with real deviecs, we use simulations instead. For our Window-Heat control we need a simulated thermostat and a door-window sensor. Create these elements as described in the simulation tutorial (we will need the Battery mentioned there later on also, so you can create this device already now if you want).

Tags:
Created by Christoph Nölle on 2017/02/03 01:13