const { _isItem, _isQuantity } = require('./helpers');
const QuantityType = Java.type('org.openhab.core.library.types.QuantityType');
/**
* @type {JavaBigDecimal}
* @private
*/
const BigDecimal = Java.type('java.math.BigDecimal');
/**
* @typedef {import('./items/items').Item} Item
* @private
*/
/**
* Takes either an {@link Item}, a `string`, a `number` or a {@link Quantity} and converts it to a {@link QuantityType} or {@link BigDecimal}.
* When the Item state is a DecimalType, it is converted to a {@link BigDecimal}, otherwise to a {@link QuantityType}.
* @param {Item|string|number|Quantity} value
* @returns {BigDecimal|QuantityType}
* @throws {TypeError} when parameter has the wrong type
* @throws {QuantityError} when {@link BigDecimal} creation failed
* @private
*/
function _toBigDecimalOrQtyType (value) {
if (_isItem(value) && value.rawState.getClass().getSimpleName() === 'DecimalType') {
try {
value = value.rawState.toBigDecimal();
} catch (e) {
throw new QuantityError(`Failed to create BigDecimal from DecimalType Item state ${value.state}: ${e}`);
}
} else if (typeof value === 'number') {
try {
value = BigDecimal.valueOf(value);
} catch (e) {
throw new QuantityError(`Failed to create BigDecimal from ${value}: ${e}`);
}
} else {
value = _toQtyType(value, 'Argument of wrong type provided, required Item, number, string or Quantity.');
}
return value;
}
/**
* Takes either a {@link Quantity} or a `string` and converts it to a {@link QuantityType}.
* @param {Item|string|Quantity} value
* @param {string} [errorMsg] error message to throw if parameter has wrong type
* @returns {QuantityType}
* @throws {TypeError} when parameter has the wrong type
* @throws {QuantityError} when {@link QuantityType} creation failed
* @private
*/
function _toQtyType (value, errorMsg = 'Argument of wrong type provided, required Item, string or Quantity.') {
if (_isItem(value)) {
if (value.rawState.getClass().getSimpleName() === 'QuantityType') {
value = value.rawState;
} else {
try {
value = QuantityType.valueOf(value.state);
} catch (e) {
throw new QuantityError(`Failed to create QuantityType from Item state ${value.state}: ${e}`);
}
}
} else if (typeof value === 'string') {
try {
value = QuantityType.valueOf(value);
} catch (e) {
throw new QuantityError(`Failed to create QuantityType from ${value}: ${e}`);
}
} else if (_isQuantity(value)) {
value = QuantityType.valueOf(value.rawQtyType.toString()); // Avoid referencing the same underlying QuantityType, so "clone" it
} else {
throw new TypeError(errorMsg);
}
return value;
}
/**
* QuantityError is thrown when {@link Quantity} creation or operation fails.
* It is used to wrap the underlying Java Exceptions and add some additional information and a JS stacktrace to it.
*/
class QuantityError extends Error {
/**
* @param {string} message
*/
constructor (message) {
super(message);
super.name = 'QuantityError';
}
}
/**
* Class allowing easy Units of Measurement/Quantity handling by wrapping the openHAB {@link QuantityType}.
*
* Throws {@link QuantityError} when Quantity creation or operation failed due to wrong quantity or unit.
* Throws {@link TypeError} when wrong argument type is provided.
*
* @hideconstructor
*/
class Quantity {
/**
* @param {Item|string|Quantity|QuantityType} value
*/
constructor (value) {
if (value instanceof QuantityType) {
/**
* @type {QuantityType}
* @private
*/
this.rawQtyType = value;
} else {
/**
* @type {QuantityType}
* @private
*/
this.rawQtyType = _toQtyType(value);
}
}
/**
* Dimension of this Quantity, e.g. `[L]` for metres or `[L]²` for cubic-metres
* @type {string}
*/
get dimension () {
return this.rawQtyType.getDimension().toString();
}
/**
* Unit of this Quantity, e.g. `Metre`, or `null` if not available
* @type {string|null}
*/
get unit () {
const unit = this.rawQtyType.getUnit().getName();
return (unit === null) ? null : unit.toString();
}
/**
* Unit symbol of this Quantity, e.g. `m`, or `null` if not available
* @type {string|null}
*/
get symbol () {
const str = this.rawQtyType.toString();
const i = str.indexOf(' ');
return (i !== -1) ? str.substring(i + 1) : null;
}
/**
* Float (decimal number) value of this Quantity
* @type {number}
*/
get float () {
return parseFloat(this.rawQtyType.doubleValue());
}
/**
* Integer (non-decimal number) value of this Quantity
* @type {number}
*/
get int () {
return parseInt(this.rawQtyType.longValue());
}
/**
* Add the given value to this Quantity.
*
* @param {Item|string|Quantity} value Quantity-compatible {@link Item}, `string` consisting of amount and unit or a {@link Quantity}
* @returns {Quantity} result as new Quantity
*/
add (value) {
value = _toQtyType(value);
return new Quantity(this.rawQtyType.add(value));
}
/**
* Divide this Quantity by the given value.
*
* @example
* Quantity('20 W').divide(4); // is 5 W
* Quantity('20 W').divide('4 W') // is 5
*
* @param {Item|number|string|Quantity} value usually a number; may also be an {@link Item} which is either Quantity-compatible or holds a number, a `string` consisting of amount and unit or a {@link Quantity}, but be careful: 1 W / 5 W = 0.2 which might not be what you want
* @returns {Quantity} result as new Quantity
*/
divide (value) {
value = _toBigDecimalOrQtyType(value);
return new Quantity(this.rawQtyType.divide(value));
}
/**
* Multiply this Quantity by the given value.
*
* @example
* Quantity('20 W').multiply(4); // is 80 W
* Quantity('20 W').multiply('4 W') // is 80 W^2
*
* @param {Item|number|string|Quantity} value usually a number; may also be an {@link Item} which is either Quantity-compatible or holds a number, a `string` consisting of amount and unit or a {@link Quantity}, but be careful: 1 W * 5 W = 5 W^2 which might not be what you want
* @returns {Quantity} result as new Quantity
*/
multiply (value) {
value = _toBigDecimalOrQtyType(value);
return new Quantity(this.rawQtyType.multiply(value));
}
/**
* Subtract the given value from this Quantity.
*
* @param {Item|string|Quantity} value Quantity-compatible {@link Item}, `string` consisting of amount and unit or a {@link Quantity}
* @returns {Quantity} result as new Quantity
*/
subtract (value) {
value = _toQtyType(value);
return new Quantity(this.rawQtyType.subtract(value));
}
/**
* Convert this Quantity to the given unit.
*
* @param {string} unit
* @returns {Quantity|null} a new Quantity with the given unit or `null` if conversion to this unit is not possible
* @throws {QuantityError} when unit cannot be parsed because it is invalid
*/
toUnit (unit) {
let qtyType;
try {
qtyType = (this.rawQtyType.toUnit(unit));
} catch (e) {
throw new QuantityError(`Failed to parse unit ${unit}: ${e}`);
}
if (qtyType === null) {
console.warn(`Failed to convert ${this.rawQtyType.toString()} to unit ${unit}.`);
return null;
}
return new Quantity(qtyType);
}
/**
* Checks whether this Quantity is equal to the passed in value.
*
* @param {Item|string|Quantity} value Quantity-compatible {@link Item}, `string` consisting of amount and unit or a {@link Quantity}
* @returns {boolean}
*/
equal (value) {
value = _toQtyType(value);
return this.rawQtyType.compareTo(value) === 0;
}
/**
* Checks whether this Quantity is larger than the passed in value.
*
* @param {Item|string|Quantity} value Quantity-compatible {@link Item}, `string` consisting of amount and unit or a {@link Quantity}
* @returns {boolean}
*/
greaterThan (value) {
value = _toQtyType(value);
return this.rawQtyType.compareTo(value) > 0;
}
/**
* Checks whether this Quantity is larger than or equal to the passed in value.
*
* @param {Item|string|Quantity} value Quantity-compatible {@link Item}, `string` consisting of amount and unit or a {@link Quantity}
* @returns {boolean}
*/
greaterThanOrEqual (value) {
value = _toQtyType(value);
return this.rawQtyType.compareTo(value) >= 0;
}
/**
* Checks whether this Quantity is smaller than the passed in value.
*
* @param {Item|string|Quantity} value Quantity-compatible {@link Item}, `string` consisting of amount and unit or a {@link Quantity}
* @returns {boolean}
*/
lessThan (value) {
value = _toQtyType(value);
return this.rawQtyType.compareTo(value) < 0;
}
/**
* Checks whether this Quantity is smaller than or equal to the passed in value.
*
* @param {Item|string|Quantity} value Quantity-compatible {@link Item}, `string` consisting of amount and unit or a {@link Quantity}
* @returns {boolean}
*/
lessThanOrEqual (value) {
value = _toQtyType(value);
return this.rawQtyType.compareTo(value) <= 0;
}
toString () {
return this.rawQtyType.toString();
}
}
/**
* The Quantity allows easy Units of Measurement/Quantity handling by wrapping the openHAB {@link QuantityType}.
*
* @private
* @param {Item|string|Quantity|QuantityType} value either a Quantity-compatible {@link Item}, a string consisting of a numeric value and a dimension, e.g. `5.5 m`, a {@link Quantity} or a {@link QuantityType}
* @returns {Quantity}
* @throws {QuantityError} if Quantity creation or operation failed
* @throws {TypeError} if wrong argument type is provided
*/
function getQuantity (value) {
return new Quantity(value);
}
module.exports = {
getQuantity,
Quantity,
QuantityError,
_toQtyType,
_toBigDecimalOrQtyType
};