/*
* 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.singlephaseelectricitymeter;
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.SinglePhaseActivePowerMeter;
import it.polito.elite.dog.core.library.model.devicecategory.SinglePhaseElectricityMeter;
import it.polito.elite.dog.core.library.model.devicecategory.SinglePhaseEnergyMeter;
import it.polito.elite.dog.core.library.model.state.SinglePhaseActiveEnergyState;
import it.polito.elite.dog.core.library.model.state.SinglePhaseActivePowerMeasurementState;
import it.polito.elite.dog.core.library.model.statevalue.ActiveEnergyStateValue;
import it.polito.elite.dog.core.library.model.statevalue.ActivePowerStateValue;
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.zway.json.CommandClasses;
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.Set;
import javax.measure.DecimalMeasure;
import javax.measure.Measure;
import javax.measure.quantity.Power;
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 ZWaveSinglePhaseElectricityMeterInstance extends
ZWaveDriverInstance implements SinglePhaseElectricityMeter
{
// the class logger
private LogHelper logger;
public ZWaveSinglePhaseElectricityMeterInstance(ZWaveNetwork network,
ControllableDevice device, int deviceId, Set<Integer> instancesId,
int gatewayNodeId, int updateTimeMillis, BundleContext context)
{
super(network, device, deviceId, instancesId, gatewayNodeId,
updateTimeMillis, context);
// create a logger
logger = new LogHelper(context);
// initialize states
this.initializeStates();
}
/**
* Initializes the state asynchronously as required by OSGi
*/
private void initializeStates()
{
// create the var and va units
Unit<Power> VAR = SI.WATT.alternate("var");
// add unit of measure aliases (to fix notation problems...)
UnitFormat uf = UnitFormat.getInstance();
uf.alias(SI.WATT.times(NonSI.HOUR), "Wh");
uf.label(SI.KILO(SI.WATT.times(NonSI.HOUR)), "kWh");
uf.alias(VAR.times(NonSI.HOUR), "Varh");
uf.label(SI.KILO(VAR.times(NonSI.HOUR)), "kVarh");
// initialize the state
this.currentState.setState(
SinglePhaseActiveEnergyState.class.getSimpleName(),
new SinglePhaseActiveEnergyState(new ActiveEnergyStateValue()));
this.currentState.setState(SinglePhaseActivePowerMeasurementState.class
.getSimpleName(), new SinglePhaseActivePowerMeasurementState(
new ActivePowerStateValue()));
// 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 DeviceStatus getState()
{
return currentState;
}
@Override
public void newMessageFromHouse(Device deviceNode, Instance instanceNode,
Controller controllerNode, String sValue)
{
this.deviceNode = deviceNode;
// Read the value associated with the right CommandClass
// meter values are in
// devices.X.instances.x.commandClasses.50.data.0 (KwH) and
// devices.X.instances.x.commandClasses.50.data.2 (W)
CommandClasses ccElectricityEntry = instanceNode.getCommandClasses()
.get(ZWaveAPI.COMMAND_CLASS_METER);
//
if (ccElectricityEntry != null)
{
// the energy updated flag
boolean energyUpdated = false;
// the power updated flag
boolean powerUpdated = false;
// Check if it is a real new value or if it is an old one. We can
// use
// one of the cc available
DataElemObject instance0 = ccElectricityEntry.get("0");
if (instance0 != null)
{
long updateTime = instance0.getDataElem("val").getUpdateTime();
// first time we only save update time, no more
if (lastUpdateTime == 0)
lastUpdateTime = updateTime;
else if (lastUpdateTime < updateTime)
{
// update last update time
lastUpdateTime = updateTime;
nFailedUpdate = 0;
// handle energy data if available
DataElemObject energyEntry = ccElectricityEntry.get("0");
if (energyEntry != null)
{
this.changeActiveEnergyState(Double.valueOf(energyEntry
.getDataElemValue("val").toString()));
energyUpdated = true;
}
// handle power data if available
DataElemObject powerEntry = ccElectricityEntry.get("2");
if (powerEntry != null)
{
this.changeActivePowerState(Double.valueOf(powerEntry
.getDataElemValue("val").toString()));
powerUpdated = true;
}
}
if (energyUpdated || powerUpdated)
this.updateStatus();
}
}
}
/**
* Manages the power state update (only updates if the current state value
* is different from the given one)
*
* @param activeEnergy
* @return
*/
private void changeActiveEnergyState(double activeEnergy)
{
// build the energy measure
DecimalMeasure<?> value = DecimalMeasure.valueOf(activeEnergy + " "
+ SI.KILO(SI.WATT.times(NonSI.HOUR)).toString());
// update the state
ActiveEnergyStateValue pValue = new ActiveEnergyStateValue();
pValue.setValue(value);
currentState.setState(
SinglePhaseActiveEnergyState.class.getSimpleName(),
new SinglePhaseActiveEnergyState(pValue));
// debug
logger.log(LogService.LOG_DEBUG, "Device " + device.getDeviceId()
+ " active energy " + value.toString());
// notify energy change
this.notifyNewActiveEnergyValue(value);
}
/**
* Manages the energy state update (only updates if the current state value
* is different from the given one)
*
* @param activePower
* @return
*/
private void changeActivePowerState(double activePower)
{
// build the power measure
DecimalMeasure<?> powerValue = DecimalMeasure.valueOf(activePower + " "
+ SI.WATT.toString());
// update the state
ActivePowerStateValue pValue = new ActivePowerStateValue();
pValue.setValue(powerValue);
currentState.setState(
SinglePhaseActivePowerMeasurementState.class.getSimpleName(),
new SinglePhaseActivePowerMeasurementState(pValue));
// debug
logger.log(LogService.LOG_DEBUG, "Device " + device.getDeviceId()
+ " active power " + powerValue.toString());
// notify the state change
this.notifyNewActivePowerValue(powerValue);
}
@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 Measure<?, ?> getActiveEnergyValue()
{
return (Measure<?, ?>) currentState.getState(
SinglePhaseActiveEnergyState.class.getSimpleName())
.getCurrentStateValue()[0].getValue();
}
@Override
public Measure<?, ?> getActivePower()
{
return (Measure<?, ?>) currentState.getState(
SinglePhaseActivePowerMeasurementState.class.getSimpleName())
.getCurrentStateValue()[0].getValue();
}
@Override
public void notifyNewActiveEnergyValue(Measure<?, ?> value)
{
// notify the new measure
((SinglePhaseEnergyMeter) device).notifyNewActiveEnergyValue(value);
}
@Override
public void notifyNewActivePowerValue(Measure<?, ?> powerValue)
{
// notify the new measure
((SinglePhaseActivePowerMeter) device)
.notifyNewActivePowerValue(powerValue);
}
@Override
protected ZWaveNodeInfo createNodeInfo(int deviceId,
Set<Integer> instancesId, boolean isController)
{
HashMap<Integer, Set<Integer>> instanceCommand = new HashMap<Integer, Set<Integer>>();
// meter information are in instance 1 with commandclasses =
// COMMAND_CLASS_METER (kwH) and COMMAND_CLASS_SENSOR_MULTILEVEL (W)
for (Integer instanceId : instancesId)
{
HashSet<Integer> ccSet = new HashSet<Integer>();
ccSet.add(ZWaveAPI.COMMAND_CLASS_METER);
ccSet.add(ZWaveAPI.COMMAND_CLASS_SENSOR_MULTILEVEL);
instanceCommand.put(instanceId, ccSet);
}
ZWaveNodeInfo nodeInfo = new ZWaveNodeInfo(deviceId, instanceCommand,
isController);
return nodeInfo;
}
@Override
public void notifyNewPowerFactorValue(Measure<?, ?> powerFactor)
{
// Nothing to do: not supported by device...
}
@Override
public void notifyNewReactiveEnergyValue(Measure<?, ?> value)
{
// Nothing to do: not supported by device...
}
@Override
public Measure<?, ?> getReactiveEnergyValue()
{
// Nothing to do: not supported by device...
return null;
}
@Override
public Measure<?, ?> getPowerFactor()
{
// Nothing to do: not supported by device...
return null;
}
@Override
public Measure<?, ?> getReactivePower()
{
// Nothing to do: not supported by device...
return null;
}
@Override
public Measure<?, ?> getFrequency()
{
// Nothing to do: not supported by device...
return null;
}
@Override
public Measure<?, ?> getPhaseNeutralVoltageValue()
{
// Nothing to do: not supported by device...
return null;
}
@Override
public Measure<?, ?> getElectricCurrentValue()
{
// Nothing to do: not supported by device...
return null;
}
@Override
public Measure<?, ?> getApparentPower()
{
// Nothing to do: not supported by device...
return null;
}
@Override
public void notifyNewFrequencyValue(Measure<?, ?> frequency)
{
// Nothing to do: not supported by device...
}
@Override
public void notifyNewReactivePowerValue(Measure<?, ?> powerValue)
{
// Nothing to do: not supported by device...
}
@Override
public void notifyNewApparentPowerValue(Measure<?, ?> powerValue)
{
// Nothing to do: not supported by device...
}
@Override
public void notifyNewPhaseNeutralVoltageValue(Measure<?, ?> lnVoltage)
{
// Nothing to do: not supported by device...
}
@Override
public void notifyNewCurrentValue(Measure<?, ?> value)
{
// Nothing to do: not supported by device...
}
@Override
public void updateStatus()
{
// update the monitor admin status snapshot
((Controllable) this.device).updateStatus();
}
}