New sensor

To add support for new a new sensor it is required to create a class which bases on tigase.iot.framework.devices.AbstractSensor. This simple base class will take over of all required tasks and provide you with access to configuration and event bus.

Next you need to implement support for reading data/value from your sensor. When you have received new value, then wrap it in instance of class implementing interface tigase.iot.framework.devices.IValue and call updateValue() method from your class extending AbstractSensor. This method will then fire event with new value which will be delivered to every device which will be listening to state changes of your sensor.

Constructor of an AbstractSensor class requires in a parameter a type of a device - string value. This value is later on published in configuration of a device and used by UI to detect device type and use proper controls to display sensor and it’s state. Currently there is only a support for a few device types.

Note

After you have your class implemented, you need to compile it and add to classpath of Tigase IoT Framework project and add it to configuration as a @Bean

Example (support for a PIR sensor - HC SR501). 

/*
 * HC_SR501.java
 *
 * Tigase IoT Framework
 * Copyright (C) 2011-2017 "Tigase, Inc." <office@tigase.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. Look for COPYING file in the top folder.
 * If not, see http://www.gnu.org/licenses/.
 */

package tigase.iot.framework.rpi.sensors.pir;

import com.pi4j.io.gpio.*;
import com.pi4j.io.gpio.event.GpioPinDigitalStateChangeEvent;
import com.pi4j.io.gpio.event.GpioPinListenerDigital;
import tigase.iot.framework.devices.AbstractSensor;
import tigase.iot.framework.devices.IConfigurationAware;
import tigase.iot.framework.values.Movement;
import tigase.kernel.beans.Initializable;
import tigase.kernel.beans.Inject;
import tigase.kernel.beans.UnregisterAware;
import tigase.kernel.beans.config.ConfigField;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

/**
 * Implementation of a sensor responsible for reading data from HC-SR501 sensor.
 *
 * Created by andrzej on 23.10.2016.
 */
public class HC_SR501
		extends AbstractSensor<Movement>
		implements Initializable, UnregisterAware, GpioPinListenerDigital, IConfigurationAware {

	private static final Logger log = Logger.getLogger(HC_SR501.class.getCanonicalName());

	@ConfigField(desc = "WiringPi Pin number")
	private int pin = 21;         // equals to broadcom 5

	@ConfigField(desc = "Delay time in ms")
	private long timeout = 5 * 60 * 1000;

	@Inject
	private ScheduledExecutorService scheduledExecutorService;

	private GpioController gpio;
	private GpioPinDigitalInput input;
	private ScheduledFuture future;

	public HC_SR501() {
		super("movement-sensor", "Motion sensor", "HC-SR501");
	}

	@Override
	public void beforeUnregister() {
		if (input != null) {
			input.removeListener(this);
			gpio.unprovisionPin(input);
		}
	}

	@Override
	public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event) {
		if (event.getEdge() == PinEdge.RISING) {
			if (future != null) {
				future.cancel(false);
			}

			if (!isMovementDetected()) {
				updateValue(new Movement(true));
			}

			future = scheduledExecutorService.schedule(() -> updateValue(new Movement(false)), timeout,
													   TimeUnit.MILLISECONDS);
		}
	}

	public boolean isMovementDetected() {
		Movement val = getValue();

		if (val == null) {
			return false;
		}

		return val.getValue();
	}

	public void setPin(Integer pin) {
		this.pin = pin;
		if (gpio != null) {
			if (input != null) {
				input.removeListener(this);
				gpio.unprovisionPin(input);
				input = null;
			}
			initializeInput();
		}
	}

	@Override
	public void initialize() {
		super.initialize();
		try {
			gpio = GpioFactory.getInstance();
		} catch (Throwable ex) {
			throw new RuntimeException("Failed to retrieve instance of GpioFactory!", ex);
		}
		if (input == null) {
			initializeInput();
		}
	}

	private void initializeInput() {
		input = gpio.provisionDigitalInputPin(RaspiPin.getPinByAddress(pin), PinPullResistance.PULL_DOWN);
		input.setShutdownOptions(true);

		input.addListener(this);
	}
}