/*
* Dog - Z-Wave
*
* Copyright 2013 Davide Aimone and Dario Bonino
*
* 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 it.polito.elite.dog.drivers.zwave.temperatureandhumiditysensor;
import it.polito.elite.dog.core.library.model.ControllableDevice;
import it.polito.elite.dog.core.library.model.DeviceStatus;
import it.polito.elite.dog.core.library.model.devicecategory.Controllable;
import it.polito.elite.dog.core.library.model.devicecategory.QuadSensor;
import it.polito.elite.dog.core.library.model.devicecategory.TemperatureAndHumiditySensor;
import it.polito.elite.dog.core.library.model.state.HumidityMeasurementState;
import it.polito.elite.dog.core.library.model.state.TemperatureState;
import it.polito.elite.dog.core.library.model.statevalue.HumidityStateValue;
import it.polito.elite.dog.core.library.model.statevalue.TemperatureStateValue;
import it.polito.elite.dog.core.library.util.LogHelper;
import it.polito.elite.dog.drivers.zwave.ZWaveAPI;
import it.polito.elite.dog.drivers.zwave.model.SensorType;
import it.polito.elite.dog.drivers.zwave.model.zway.json.CommandClasses;
import it.polito.elite.dog.drivers.zwave.model.zway.json.CommandClassesData;
import it.polito.elite.dog.drivers.zwave.model.zway.json.Controller;
import it.polito.elite.dog.drivers.zwave.model.zway.json.DataElemObject;
import it.polito.elite.dog.drivers.zwave.model.zway.json.Device;
import it.polito.elite.dog.drivers.zwave.model.zway.json.Instance;
import it.polito.elite.dog.drivers.zwave.network.ZWaveDriverInstance;
import it.polito.elite.dog.drivers.zwave.network.info.ZWaveNodeInfo;
import it.polito.elite.dog.drivers.zwave.network.interfaces.ZWaveNetwork;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.measure.DecimalMeasure;
import javax.measure.Measure;
import javax.measure.unit.NonSI;
import javax.measure.unit.SI;
import javax.measure.unit.Unit;
import javax.measure.unit.UnitFormat;
import org.osgi.framework.BundleContext;
import org.osgi.service.log.LogService;
public class ZWaveTemperatureAndHumiditySensorDriverInstance extends
ZWaveDriverInstance implements TemperatureAndHumiditySensor
{
// the class logger
private LogHelper logger;
// sensor-level update times
private long temperatureUpdateTime = 0;
private long humidityUpdateTime = 0;
// the group set
private HashSet<Integer> groups;
public ZWaveTemperatureAndHumiditySensorDriverInstance(
ZWaveNetwork network, ControllableDevice device, int deviceId,
Set<Integer> instancesId, int gatewayNodeId, int updateTimeMillis,
BundleContext context)
{
super(network, device, deviceId, instancesId, gatewayNodeId,
updateTimeMillis, context);
// build inner data structures
this.groups = new HashSet<Integer>();
// create a logger
logger = new LogHelper(context);
// initialize states
this.initializeStates();
}
/**
* Initializes the state asynchronously as required by OSGi
*/
private void initializeStates()
{
// set up unit of measures
Unit.ONE.alternate("%");
UnitFormat uf = UnitFormat.getInstance();
uf.label(SI.CELSIUS, "C");
uf.alias(SI.CELSIUS, "C");
// initialize the state
this.currentState.setState(TemperatureState.class.getSimpleName(),
new TemperatureState(new TemperatureStateValue()));
this.currentState.setState(
HumidityMeasurementState.class.getSimpleName(),
new HumidityMeasurementState(new HumidityStateValue()));
// get the initial state of the device
Runnable worker = new Runnable()
{
public void run()
{
network.read(nodeInfo, true);
}
};
Thread workerThread = new Thread(worker);
workerThread.start();
}
@Override
public void newMessageFromHouse(Device deviceNode, Instance instanceNode,
Controller controllerNode, String sValue)
{
// update deviceNode
this.deviceNode = deviceNode;
// the state change flag
boolean measureUpdated = false;
// Read the value for temperature or humidity.
CommandClasses ccInst = instanceNode
.getCommandClass(ZWaveAPI.COMMAND_CLASS_SENSOR_MULTILEVEL);
// Check if it is a real new value or if it is an old one
long globalUpdateTime = ccInst.getValUpdateTime();
// check if the instance contains only one value
if (globalUpdateTime > 0)
{
// check if the values are up-to-date
if (this.lastUpdateTime < globalUpdateTime)
{
// update last update time
lastUpdateTime = ccInst.getValUpdateTime();
nFailedUpdate = 0;
// Reads values and sensorType
double measure = ccInst.getVal();
String sensorType = ccInst.getSensorType();
// parse unit of measure
String unitOfMeasure = (String) ccInst.getCommandClassesData()
.getDataElemValue(CommandClassesData.FIELD_SCALESTRING);
// forward to the right method
measureUpdated = this.forwardMeasure(measure, unitOfMeasure,
sensorType, this.lastUpdateTime);
}
}
else if (ccInst.getValUpdateTime() < 0)
{
// handle the case in which instances are more complex than usual
// (e.g. in the ST814 case) and data is hidden in numeric keys.
// TODO: check if this is the best way (I have some doubt on it)
// iterate over numeric keys
Map<String, DataElemObject> cmdClassData = ccInst
.getCommandClassesData().getAllData();
for (String key : cmdClassData.keySet())
{
// check for numeric key
try
{
// check if the key is a number, otherwise an exception will
// be thrown and caught
Integer.valueOf(key);
// get the element data associated to the key
DataElemObject sensorData = cmdClassData.get(key);
// check the last update time
long updateTime = sensorData.getUpdateTime();
// Read value
double measure = Double.valueOf(sensorData
.getDataElemValue(CommandClassesData.FIELD_VAL)
.toString());
// Read sensorType
String sensorType = (String) sensorData
.getDataElemValue(CommandClassesData.FIELD_SENSORTYPE);
// parse unit of measure
String unitOfMeasure = (String) sensorData
.getDataElemValue(CommandClassesData.FIELD_SCALESTRING);
// forward to the right method
measureUpdated = this.forwardMeasure(measure,
unitOfMeasure, sensorType, updateTime);
}
catch (NumberFormatException ne)
{
// not a number, simply ignore it
}
}
}
if (measureUpdated)
this.updateStatus();
}
private void changeTemperatureState(double measure, String unitOfMeasure)
{
// build the temperature measure
DecimalMeasure<?> temperatureValue = DecimalMeasure.valueOf(measure
+ " "
+ (unitOfMeasure.contains("C") ? SI.CELSIUS.toString()
: NonSI.FAHRENHEIT.toString()));
// if the given temperature is null, than the network-level
// value is not up-to-date
if (temperatureValue != null)
{
// update the state
TemperatureStateValue pValue = new TemperatureStateValue();
pValue.setValue(temperatureValue);
currentState.setState(TemperatureState.class.getSimpleName(),
new TemperatureState(pValue));
}
// debug
logger.log(LogService.LOG_DEBUG, "Device " + device.getDeviceId()
+ " temperature " + temperatureValue.toString());
this.notifyNewTemperatureValue(temperatureValue);
}
private void changeHumidityState(double measure, String unitOfMeasure)
{
// build the humidity measure
DecimalMeasure<?> relativeHumidity = DecimalMeasure.valueOf(measure
+ " " + unitOfMeasure);
// if the given temperature is null, than the network-level
// value is not up-to-date
if (relativeHumidity != null)
{
// update the state
HumidityStateValue pValue = new HumidityStateValue();
pValue.setValue(relativeHumidity);
currentState.setState(
HumidityMeasurementState.class.getSimpleName(),
new HumidityMeasurementState(pValue));
this.notifyChangedRelativeHumidity(relativeHumidity);
// debug
logger.log(LogService.LOG_DEBUG, "Device " + device.getDeviceId()
+ " humidity " + relativeHumidity.toString());
}
}
@Override
protected void specificConfiguration()
{
// prepare the device state map
currentState = new DeviceStatus(device.getDeviceId());
}
@Override
protected void addToNetworkDriver(ZWaveNodeInfo nodeInfo)
{
network.addDriver(nodeInfo, updateTimeMillis, this);
}
@Override
protected boolean isController()
{
return false;
}
@Override
public void deleteGroup(Integer groupID)
{
// remove the given group id
this.groups.remove(groupID);
// notify
this.notifyLeftGroup(groupID);
}
@Override
public void storeGroup(Integer groupID)
{
// Store the given group id
this.groups.add(groupID);
this.notifyJoinedGroup(groupID);
}
@Override
public DeviceStatus getState()
{
return currentState;
}
@Override
public Measure<?, ?> getRelativeHumidity()
{
return (Measure<?, ?>) currentState.getState(
HumidityMeasurementState.class.getSimpleName())
.getCurrentStateValue()[0].getValue();
}
@Override
public Measure<?, ?> getTemperature()
{
return (Measure<?, ?>) currentState.getState(
TemperatureState.class.getSimpleName()).getCurrentStateValue()[0]
.getValue();
}
@Override
public void notifyNewTemperatureValue(Measure<?, ?> temperatureValue)
{
// notify the new measure
((TemperatureAndHumiditySensor) device)
.notifyNewTemperatureValue(temperatureValue);
}
@Override
public void notifyChangedRelativeHumidity(Measure<?, ?> relativeHumidity)
{
// notify the new measure
((TemperatureAndHumiditySensor) device)
.notifyChangedRelativeHumidity(relativeHumidity);
}
@Override
public void notifyJoinedGroup(Integer groupNumber)
{
// send the joined group notification
((QuadSensor) this.device).notifyJoinedGroup(groupNumber);
}
@Override
public void notifyLeftGroup(Integer groupNumber)
{
// send the left group notification
((QuadSensor) this.device).notifyLeftGroup(groupNumber);
}
@Override
public void updateStatus()
{
// update the monitor admin status snapshot
((Controllable) this.device).updateStatus();
}
@Override
protected ZWaveNodeInfo createNodeInfo(int deviceId,
Set<Integer> instancesId, boolean isController)
{
HashMap<Integer, Set<Integer>> instanceCommand = new HashMap<Integer, Set<Integer>>();
// for this device the right Get command class is
// COMMAND_CLASS_SENSOR_MULTILEVEL for each instance.
HashSet<Integer> ccSet = new HashSet<Integer>();
ccSet.add(ZWaveAPI.COMMAND_CLASS_SENSOR_MULTILEVEL);
for (Integer instanceId : instancesId)
{
instanceCommand.put(instanceId, ccSet);
}
ZWaveNodeInfo nodeInfo = new ZWaveNodeInfo(deviceId, instanceCommand,
isController);
return nodeInfo;
}
private boolean forwardMeasure(double measure, String unitOfMeasure,
String sensorType, long updateTime)
{
boolean stateChanged = false;
// check which value has been read
if (sensorType.equals(SensorType.SENSORTYPE_TEMPERATURE))
{
// check if and how manage the update time
if (this.temperatureUpdateTime < updateTime)
{
this.temperatureUpdateTime = updateTime;
// change the state
this.changeTemperatureState(measure, unitOfMeasure);
// changed
stateChanged = true;
}
}
else if (sensorType.equals(SensorType.SENSORTYPE_HUMIDITY))
{
// check if and how manage the update time
if (this.humidityUpdateTime < updateTime)
{
this.humidityUpdateTime = updateTime;
this.changeHumidityState(measure, unitOfMeasure);
// changed
stateChanged = true;
}
}
return stateChanged;
}
}