[SOLVED] Using Javascript transformation to calculate energy cost with additional item state

Hi everybody,

I am struggling with a JavaScript transformation.

What I’ve got so far:

1. MQTT sensor data from power outlet

I am using a Gosund SP1 Outlet flashed with Tasmota to read energy data from my washing machine. The data is transferred to openHAB through MQTT. The MQTT state topic of the Gosund device is subscribed to in the Gosund thing. The topic is called:

sonoff-washer/tele/SENSOR

And will yield the following string when subscribed to:

{"Time":"2022-12-06T14:11:03","ENERGY":{"TotalStartTime":"2022-11-29T22:57:37","Total":2.316,"Yesterday":0.000,"Today":0.000,"Period":0,"Power":0,"ApparentPower":0,"ReactivePower":0,"Factor":0.00,"Voltage":230,"Current":0.000}}

2. JavaScript Transformation to get TotalPower consumed and calculate cost

Then I set up a JS transformation to retrieve the MQTT topic string as input, perform a RegEx match to get the Total Power consumed value and multiply that value by my current electricity price. The item page looks like this, with the linked channel and JS transformation file:

The JS transformation file looks like this (be gentle I am no coder, forum posts combined with trial and error :smiley: ):

processed = input.replace(/"/g, ''); //removes all quotes (") from the input string. Otherwise JS won't work

(function(i) {
    var regex = /Total:\d*.\d*/g; //RegEx extracting the string "Total:" + the following number
    var found = i.match(regex); //performing match with the RegEx above
	strip = JSON.stringify(found).replace('["Total:', '').replace('"]', ''); //output of above is object, therefore converting back to string and removing all string components except the desired number
	count = Number(strip); //converting the string from above to a number
	output = count * 0.22; //multiplying the extracted total power consumption with current electricity price
    return output; //returning the result of the calculation > this will be the linked item's state.
})(processed)

In the End, I have an item which directly converts the total power consumption measured by the Gosund SP1 to a price value. (Gosund > MQTT > JS Transformation > items with price as number)

Problem: As you might have noticed, the current energy price is “hard coded” into the JS transformation file. This leads me to What I want to have: I want the current energy price to be passed to the JS transformation as an additional parameter, which is taken from a corresponding item.

I read on the JS transformation page [Link] that additional parameters can be passed to a JS transformation script. I changed the whole thing around a bit. The page of my Test26 item now looks like this. Notice that I am using a different JS file and added ?price=0.22 behind the JS filename:

The slightly altered JS file looks like this. It mostly has the same functionality with the addition that the value I state behind the JS filename is now passed into the transformation (see also comments):

processed = input.replace(/"/g, '');

(function(i, p) { //p is added to the function
    var regex = /Total:\d*.\d*/g;
    var found = i.match(regex);
	strip = JSON.stringify(found).replace('["Total:', '').replace('"]', '');
	count = Number(strip);
	output = count * p; //instead of a fixed value the added parameter value is used
    return output;
})(processed, price) //price is imported as a additional parameter

So far, so good. Now I have the energy price no longer in the JS transformation file but hardcoded into the item settings in Main UI. And this is where I am stuck!

Question: Can I somehow reference an item state in my item’s profile configuration?

I have tried the following lines (energy_electricity_price is a number item with value 0.22):

gosund_costtotal_price.js?price=energy_electricity_price.state
gosund_costtotal_price.js?price=items.energy_electricity_price.state
gosund_costtotal_price.js?price=http://192.168.178.23:16680/rest/items/energy_electricity_price/state
gosund_costtotal_price.js?price=items.getItem(“energy_electricity_price”).state

In all above cases, no item state is transferred as an additional parameter to the JS transformation. Instead the string after the “=” sign is transferred. E.g. for the first case the string “energy_electricity_price.state”.

I am completely clueless how to call an item state within the item’s profile configuration, or whether this is at all possible? Maybe the item state could be called within the JS transformation file?

Any help is greatly appreciated :slight_smile:

wow! :wink: It’s waaaay more easy than that! :wink:
Have a look at your MQTT-Thing:

  - id: EnergyTotal
    channelTypeUID: mqtt:number
    label: Gesamtverbrauch
    description: null
    configuration:
      stateTopic: sonoff-washer/tele/SENSOR
      transformationPattern: JSONPATH:$.ENERGY.Total

That’s all you need, to have the channel “EnergyTotal”, which will get you total consumption from the JSON-payload, the /SENSOR topic provides.

That’s really all: but be sure, to have installed “JSONPATH” in OH3 via “settings” - “AddOns” - “Other”. $.ENERGY.Total will simply fetch the value of the JSON attribut “Total” in ENERGY of the JSON-Payload.

With that, you have two main “OH-standard” option for calculating your “EnergyTotal”-pricing:

option1: Rule

  1. add a “EnergyTotalPrice”-item (or whatever you’d like to call it)
  2. add a rule
  3. whenever your “EnergyTotal”-item updates => multiply your item with your Cents

option2: your JS-transformation

  1. add a “EnergyTotalPrice”-item (or whatever you’d like to call it) and link it to the same channel
  2. add a much more simplified JS in the Profile-section:
(function(i) {
    return parseFloat(i)* 0.22;
})(input)
  1. whenever your “EnergyTotal”-channel updates => the price-item gehts JS-transformed
2 Likes

Okay, I see your point about adding the transformationPattern to the MQTT-Thing. I was not even aware that the transformation can be directly applied to the channel. But now I found the “show advanced” option under configuration :smiley: . Thanks for the hint!

Somehow a solution involving RegEx is always the most complicated and the one I go for :stuck_out_tongue:

Using option 2 I would have to go into the JS and alter the price as soon as my provider decides to adjust. Which I do not like, because I will forget how to do that in a couple of months, and then the searching begins.

Using option 1 I can use a item.state ( of my electricity price item) and only need to adjust that one item in case my provider adjust the price, and it will change all calculations. So that seems to be the better (in terms of maintenance) solution for me.

Out of curiosity, is it at all possible to call the energy_electricity_price.state within the JS file? To have something like:

(function(i) {
    price = energy_electricity_price.state //I know the syntax is baloney. I hope you get what I mean :-)
    return parseFloat(i)* price;
})(input)

Thanks again! Learned a lot already :slight_smile:

1 Like

I think the answer is no. IIRC the context that gets passed into a rule does not get passed into a JS transformation so you don’t have access to OH stuff like the states of Items and Actions.

So use a Rule instead.

You learn only while doing! :wink:

yes, if that’s your goal - go for it! Perhaps your provider has an online API of sorts for your tariff, that you can automagically update your item’s state with! :wink:

Nope, I don’t think so. (but as I type this, I saw Rich will answer soon, also, he’s surely got some more profund answer to this) :grin:

Thanks @binderth and @rlkoshak, I will go for the rule solution!

Somehow automatically upgrading the tariff sounds like a tempting project :thinking: I see my next RegEx desaster coming :smiley:

@binderth I will mark your first post as solution and the thread as solved.

Thanks again!

1 Like

on a serious note: there’s some providers who provide tariff information via an API (mostly REST-based JSON-format), this would be a no-brainer then.
With others/most you’ll have to scrape the Website (hoping the URL won’t change over time), and parse the body with … RegEx. openHAB is not the best solution for this, I’d recommend going with either a python-script for this or use other lowCode/noCode frameworks like NodeRed for that. The result can be sent to openHAB via MQTT or REST-API then.

…or: have a look on github.com - perhaps, there’s already someone similar crazy and they have already done the heavy lifting with your provider already.

on the other hand: there shouldn’t be frequent changes in tariffs and you can easily just adjust a tariff-item for that, except you have a smart/dynamic tariff, but then there’s definately an API for that.

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.