JS Transformations in item channel definition

Hi,
I am moving all my bindings to V2 in order to migrate to OH3.

So far I encountered one issue that I haven’t managed to solve yet:
I want to transform a state received from a binding.

This is my Items definition:

Number APC_TIMELEFT (gAPC) {channel="networkupstools:ups:ups:batteryRuntime" [profile="transform:JS", function="sec2min.js", sourceFormat="%.1f"]}

This is my JS transformation (sec2min.js)

(function(i) {return i / 60; } ) (input)

And this is the error message:

2021-01-29 09:13:47.407 [WARN ] [iles.JavascriptTransformationProfile] - Could not transform state '8308.0 s' with function 'sec2min.js' and format '%.1f'

I tried without sourceFormat and with sourceFormat="%d" or "%.0f" or "%.1f s" but get the same type of error.

(I know I could transform the seconds to minutes using the Time unit of measure, but I want a more general way using the transformation service for future use. So plese help find the error in the above syntax.)

Seems like a version of this bug

This comes from the binding supplied defaults and is misapplied to transform output.

The value input into the transformation seems to be ‘8308.0 s’, i.e with the unit attached. I think you need to handle that properly in your javascript. Try changing to

(function(i) {return parseFloat(i) / 60; } ) (input)

and see if that helps.

So I should not use sourceFormat in transformations?

Tried the suggestion without sourceFormat but I receive no error message but the state = NULL

I’m actually not sure if the format specifier works, it seems that the incoming state is converted to a String before the format specifier is applied, and the ‘f’ format specifier is only applicable for floats/doubles. Try removing it from the item definition.

Edit: saw now you already tried that and it didn’t work. Not sure how to do then…

Can’t test ATM, so use the following with caution:
IMHO your js function expects an input which can be parsed to a number, however it gets a number with a dimension (“s”). I’d try to split that dimension from the number, like:

var temp= i.split (" ");
return temp[0]/ 60;

If that isn’t according your more generell solution, try to remove any non-member character using a reflex or the like.

SourceFormat is about formatting raw data from binding as it gets given to the transform. In almost every case you can let that default (%s) and transform deals with a string.
As an aside, remember that any format produces a string - the transform input is always a string.

Your problem is the other end of the transform, the output to Item, and it is beyond your control. It’s a bug.

OK, a few things to mention here to be complete.

The Channel is returning a Number:Time value so if you make your Item be a Number:Time too then you don’t need to do any transformations at all. You can just change the State Description to use %.2f min and it will convert it to minutes for you.

You can also use the DateTime formatting to show Hours:Minutes:Seconds I think. If you use %s it will display the value as a DateTime (with 1970-01-01 as the date).

In your rules you can use the units too. For example, to see if it’s less than 2 hours

if(APC_TIMELEFT.state < 2 |h) {

As for the transform, i is getting the String “8308.0 s”. You need to strip off the units and then you should be able to parse it into a float you can do math.

var num = parseFloat(i.split(" ")[0]);

NOTE: all of this is assuming that everything is working correctly and not related to the bug rossko57 linked to.

1 Like

I understand this solution but I don’t understand why it doesn’t work in my situation. I try to detect all UNDEF values and want to convert them to 0 (because it’s used in a group SUM aggregation which shows wrong values when UNDEF is the value of at least one of it’s members).

item definition:


Group:Number:Power:SUM gForecastSolarPower “Group ForecastSolarPower [%.3f %unit%]”

Number:Power ForecastSolarHome_Power_String_1_north “ForecastSolar Power String 1 north [JS(UNDEF_to_0.js):%.3f %unit%]” (gForecastSolarPower) {channel=“solarforecast:fs-plane:myhome:ForecastSolar_PV_String_1_north:actual-power” }

UNDEF_to_0.js file

(function(i) {
if (i = “UNDEF”) return 0;
return parseFloat(i.split(" ")[0]);
})(input)

For each new value, I receive the following error message:

Failed transforming the state ‘14.837 kWh’ on item ‘ForecastSolarHome_Today_String_1_north’ with pattern ‘JS(UNDEF_to_0.js):%.3f %unit%’: Cannot format state ‘14.837 kWh’ to format ‘%.3f %unit%’

I’m not sure what I am doing wrong?

Your Group is a Number:Power

Your Item is a Number:Power.

Your label state pattern is expecting units. That’s what the %unit% is there for.

Your transformation is returning a number without units.

Therefore when it tries to apply %unit% it fails because the transformed value doesn’t have units.

In addition, as configured, this only changes the the way that the Item appears in a sitemap. It does not change the state of the Item and therefore does not address your original problem.

You have two choices.

  1. Keep the units by changing your transformation to include them.
(function(i) {
  if (i = “UNDEF”) return "0 kWh";
  return i;
})(input)
  1. Drop the units everywhere by changing all the Number:Power in your Item definitions to Number and remove the %unit% from your state pattern in the label.

In both cases, you need to apply the transformation either on the Thing’s Channel or use a transform Profile on the Link. Having it on the label does not change the state of the Item and therefore won’t address your original problem.

Notes:

  • Please use code fences

    code goes here
    
  • Please don’t reopen an ancient thread even if you think you have the same problem as discussed on that thread. A lot has changed in two years. When in doubt, open a new thread.

Thanks a lot for clarification and your really fast and comprehensive response!

I will pay attention about your notes in the future!