/*
* 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.threephaseelectricitymeter;
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.ThreePhaseElectricityMeter;
import it.polito.elite.dog.core.library.model.state.FrequencyMeasurementState;
import it.polito.elite.dog.core.library.model.state.PowerFactorMeasurementState;
import it.polito.elite.dog.core.library.model.state.SinglePhaseActiveEnergyState;
import it.polito.elite.dog.core.library.model.state.SinglePhaseReactiveEnergyState;
import it.polito.elite.dog.core.library.model.state.State;
import it.polito.elite.dog.core.library.model.state.ThreePhaseActiveEnergyState;
import it.polito.elite.dog.core.library.model.state.ThreePhaseActivePowerMeasurementState;
import it.polito.elite.dog.core.library.model.state.ThreePhaseApparentPowerMeasurementState;
import it.polito.elite.dog.core.library.model.state.ThreePhaseCurrentState;
import it.polito.elite.dog.core.library.model.state.ThreePhaseReactivePowerMeasurementState;
import it.polito.elite.dog.core.library.model.state.ThreePhaseVoltageState;
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.model.statevalue.StateValue;
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;
public class ZWaveThreePhaseElectricityMeterInstance extends
ZWaveDriverInstance implements ThreePhaseElectricityMeter
{
public ZWaveThreePhaseElectricityMeterInstance(ZWaveNetwork network,
ControllableDevice device, int deviceId, Set<Integer> instancesId,
int gatewayNodeId, int updateTimeMillis, BundleContext context)
{
super(network, device, deviceId, instancesId, gatewayNodeId,
updateTimeMillis, 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");
VAR.alternate("Var");
Unit<Power> VA = SI.WATT.alternate("VA");
// 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");
uf.label(SI.KILO(VA), "kVA");
String activeEnergyUOM = SI.WATT.times(NonSI.HOUR).toString();
String activePowerUOM = SI.WATT.toString();
// ------------ Three Phase Active Power
// --------------------------------
ActivePowerStateValue activePowerStateL1 = new ActivePowerStateValue();
activePowerStateL1.setFeature("phaseID", "L1");
activePowerStateL1.setValue(DecimalMeasure.valueOf("0 "
+ activePowerUOM));
ActivePowerStateValue activePowerStateL2 = new ActivePowerStateValue();
activePowerStateL2.setFeature("phaseID", "L2");
activePowerStateL2.setValue(DecimalMeasure.valueOf("0 "
+ activePowerUOM));
ActivePowerStateValue activePowerStateL3 = new ActivePowerStateValue();
activePowerStateL3.setFeature("phaseID", "L3");
activePowerStateL3.setValue(DecimalMeasure.valueOf("0 "
+ activePowerUOM));
this.currentState.setState(ThreePhaseActivePowerMeasurementState.class
.getSimpleName(), new ThreePhaseActivePowerMeasurementState(
activePowerStateL1, activePowerStateL2, activePowerStateL3));
// -------------- Three Phase Active Energy -------------------------
// ------------ Three Phase Active Power
// --------------------------------
ActiveEnergyStateValue activeEnergyStateL1 = new ActiveEnergyStateValue();
activeEnergyStateL1.setFeature("phaseID", "L1");
activeEnergyStateL1.setValue(DecimalMeasure.valueOf("0 "
+ activeEnergyUOM));
ActiveEnergyStateValue activeEnergyStateL2 = new ActiveEnergyStateValue();
activeEnergyStateL2.setFeature("phaseID", "L2");
activeEnergyStateL2.setValue(DecimalMeasure.valueOf("0 "
+ activeEnergyUOM));
ActiveEnergyStateValue activeEnergyStateL3 = new ActiveEnergyStateValue();
activeEnergyStateL3.setFeature("phaseID", "L3");
activeEnergyStateL3.setValue(DecimalMeasure.valueOf("0 "
+ activeEnergyUOM));
this.currentState.setState(ThreePhaseActiveEnergyState.class
.getSimpleName(), new ThreePhaseActiveEnergyState(
activeEnergyStateL1, activeEnergyStateL2, activeEnergyStateL3));
// 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;
// the state change flag
boolean energyUpdated = false;
boolean powerUpdated = false;
// Read the value associated with the right CommandClass
// meter values are in
// devices.X.instances.1.commandClasses.50.data.0 (KwH) and
// devices.X.instances.1.commandClasses.50.data.2 (W)
CommandClasses ccElectricityEntry = instanceNode.getCommandClasses()
.get(ZWaveAPI.COMMAND_CLASS_METER);
// tin pants...
if (ccElectricityEntry != null)
{
// prevent errors on first inclusion
DataElemObject data0 = ccElectricityEntry.get("0");
if (data0 != null)
{
Object data0Value = data0.getDataElemValue("val");
if (data0Value != null)
{
double activeEnergy = Double.valueOf(data0Value.toString());
String phaseID = "L" + instanceNode.getInstanceId();
DecimalMeasure<?> value = DecimalMeasure
.valueOf(activeEnergy + " "
+ SI.KILO(SI.WATT.times(NonSI.HOUR)));
this.updateThreePhaseStateValue(
ThreePhaseActiveEnergyState.class.getSimpleName(),
phaseID, value);
notifyNewActiveEnergyValue(phaseID, value);
// update the state change flag
energyUpdated = true;
}
}
// prevent errors on first inclusion
DataElemObject data2 = ccElectricityEntry.get("2");
if (data2 != null)
{
Object data2Value = data2.getDataElemValue("val");
if (data2Value != null)
{
double activePower = Double.valueOf(data2Value.toString());
// update the state....
// the phase id
String phaseID = "L" + instanceNode.getInstanceId();
DecimalMeasure<?> value = DecimalMeasure
.valueOf(activePower + " " + SI.WATT);
this.updateThreePhaseStateValue(
ThreePhaseActivePowerMeasurementState.class
.getSimpleName(), phaseID, value);
notifyNewActivePowerValue(phaseID, value);
// update the state change flag
powerUpdated = true;
}
}
}
if (energyUpdated || powerUpdated)
this.updateStatus();
}
@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
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 Measure<?, ?> getReactiveEnergyValue()
{
return (Measure<?, ?>) this.currentState.getState(
SinglePhaseReactiveEnergyState.class.getSimpleName())
.getCurrentStateValue()[0].getValue();
}
@Override
public Measure<?, ?> getReactivePower(String phaseID)
{
return this.getThreePhaseStateValue(
ThreePhaseReactivePowerMeasurementState.class.getSimpleName(),
phaseID);
}
@Override
public Measure<?, ?> getFrequency()
{
return (Measure<?, ?>) this.currentState.getState(
FrequencyMeasurementState.class.getSimpleName())
.getCurrentStateValue()[0].getValue();
}
@Override
public Measure<?, ?> getPowerFactor()
{
return (Measure<?, ?>) this.currentState.getState(
PowerFactorMeasurementState.class.getSimpleName())
.getCurrentStateValue()[0].getValue();
}
@Override
public Measure<?, ?> getActiveEnergyValue()
{
return (Measure<?, ?>) this.currentState.getState(
SinglePhaseActiveEnergyState.class.getSimpleName())
.getCurrentStateValue()[0].getValue();
}
@Override
public Measure<?, ?> getLNVoltageValue(String phaseID)
{
return this.getThreePhaseStateValue(
ThreePhaseVoltageState.class.getSimpleName(), phaseID);
}
@Override
public Measure<?, ?> getLLVoltageValue(String phaseID1, String phaseID2)
{
// TODO: fix this....
String phaseID = phaseID1 + phaseID2.substring(1);
return this.getThreePhaseStateValue(
ThreePhaseVoltageState.class.getSimpleName() + "LL", phaseID);
}
@Override
public Measure<?, ?> getElectricCurrentValue(String phaseID)
{
return this.getThreePhaseStateValue(
ThreePhaseCurrentState.class.getSimpleName(), phaseID);
}
@Override
public Measure<?, ?> getApparentPower(String phaseID)
{
return this.getThreePhaseStateValue(
ThreePhaseApparentPowerMeasurementState.class.getSimpleName(),
phaseID);
}
@Override
public Measure<?, ?> getActivePower(String phaseID)
{
return this.getThreePhaseStateValue(
ThreePhaseActivePowerMeasurementState.class.getSimpleName(),
phaseID);
}
@Override
public void notifyNewFrequencyValue(Measure<?, ?> frequency)
{
// notify the new measure
((ThreePhaseElectricityMeter) this.device)
.notifyNewFrequencyValue(frequency);
}
@Override
public void notifyNewReactivePowerValue(String phaseID, Measure<?, ?> value)
{
((ThreePhaseElectricityMeter) this.device).notifyNewReactivePowerValue(
phaseID, value);
}
@Override
public void notifyNewReactiveEnergyValue(Measure<?, ?> value)
{
// notify the new measure
((ThreePhaseElectricityMeter) this.device)
.notifyNewReactiveEnergyValue(value);
}
@Override
public void notifyNewActiveEnergyValue(Measure<?, ?> value)
{
// notify the new measure
((ThreePhaseElectricityMeter) this.device)
.notifyNewActiveEnergyValue(value);
}
public void notifyNewActiveEnergyValue(String phaseID,
DecimalMeasure<?> value)
{
((ThreePhaseElectricityMeter) this.device).notifyNewActivePowerValue(
phaseID, value);
}
@Override
public void notifyNewPhaseNeutralVoltageValue(String phaseID,
Measure<?, ?> value)
{
((ThreePhaseElectricityMeter) this.device)
.notifyNewPhaseNeutralVoltageValue(phaseID, value);
}
@Override
public void notifyNewPhasePhaseVoltageValue(String phaseID,
Measure<?, ?> value)
{
((ThreePhaseElectricityMeter) this.device)
.notifyNewPhasePhaseVoltageValue(phaseID, value);
}
@Override
public void notifyNewApparentPowerValue(String phaseID, Measure<?, ?> value)
{
((ThreePhaseElectricityMeter) this.device).notifyNewApparentPowerValue(
phaseID, value);
}
@Override
public void notifyNewPowerFactorValue(Measure<?, ?> powerFactor)
{
// notify the new measure
((ThreePhaseElectricityMeter) this.device)
.notifyNewPowerFactorValue(powerFactor);
}
@Override
public void notifyNewActivePowerValue(String phaseID, Measure<?, ?> value)
{
((ThreePhaseElectricityMeter) this.device).notifyNewActivePowerValue(
phaseID, value);
}
@Override
public void notifyNewCurrentValue(String phaseID, Measure<?, ?> value)
{
((ThreePhaseElectricityMeter) this.device).notifyNewCurrentValue(
phaseID, value);
}
@Override
public void updateStatus()
{
// update the monitor admin status snapshot
((Controllable) this.device).updateStatus();
}
private void updateThreePhaseStateValue(String stateClass, String phaseID,
Measure<?, ?> value)
{
// update the state....
StateValue currentStateValue[] = ((State) this.currentState
.getState(stateClass)).getCurrentStateValue();
for (int i = 0; i < currentStateValue.length; i++)
{
// find the matching state value
HashMap<String, Object> features = currentStateValue[i]
.getFeatures();
if ((features.containsKey("phaseID"))
&& (((String) features.get("phaseID"))
.equalsIgnoreCase(phaseID)))
{
// set the new value
currentStateValue[i].setValue(DecimalMeasure.valueOf(value
.toString()));
}
}
}
private Measure<?, ?> getThreePhaseStateValue(String stateClass,
String phaseID)
{
// the measure
Measure<?, ?> value = null;
// get the current state value
StateValue currentStateValue[] = ((State) this.currentState
.getState(stateClass)).getCurrentStateValue();
// check which state value matches the given phase ID
for (int i = 0; i < currentStateValue.length; i++)
{
// find the matching state value
HashMap<String, Object> features = currentStateValue[i]
.getFeatures();
// if matches, extract the power value and break the cycle
if ((features.containsKey("phaseID"))
&& (((String) features.get("phaseID"))
.equalsIgnoreCase(phaseID)))
{
// extract the power value
value = (Measure<?, ?>) currentStateValue[i].getValue();
// break
i = currentStateValue.length;
}
}
return value;
}
}