How to optimize gas station prices processing (Tankerkoenig)

Correct, but we were referring to the binding!

Yes, thats true, but the binding uses from my point of view the second option (up to 10). It is written in the documentation that a second webservice is required if more than 10 stations should be queried. Possible modification for the binding are:

1.) Use the single station api and wait between two stations a to be defined amount of time. Drops the 10 station limit, but increases the server load

2.) Use the multi station api and wait between the item update a to be dfined amount of time. Thatā€™s implementation work and the 10 station limit is still there

3.) Create a new channel that triggers if all items are updated after data is polled. Again implementation work and this did not overcome the 10 station limit.

So from my point of view my current solution is suitable for me and i overcomes the 10 station limit.

Exactly the binding does use this second option and the limit is set to 10 stations (as agreed with tankerkoenig). My posted solution (using a webservice for each station) was showing a possible way with the current binding, not the suggested way! Yes that way the api-call for several stations would be used for a single one.

Iā€™d rather not change the binding for this one and if Iā€™d change I suggest the the binding would raise an event after each time the prices (plural) got polled,.

However thatā€™s all talks about what you do not need, since you got a good solution (I think).

Your are absolutly right. The solution is working and i am happy.

To finish this thread, please find below all related rules, items, sitemaps, etc.

Gas station things

Bridge tankerkoenig:webservice:xxxxxxxxxxx "Bridge_Tankerkoenig Tankerkƶnig Webservice" [ apikey="xxxxxxxxxxxxxxx", refresh= 15, modeOpeningTime =true ] {

    Thing station station01 "SL_IN_GasStation01 Nordoel (Elmshorn)" @ "Internet"[ locationid = "1ceea95b-d46e-4159-91f6-71f57d612bdb" ]
    Thing station station02 "SL_IN_GasStation02 Nordoel (Uetersen)" @ "Internet"[ locationid = "3d8c2dfb-4612-4aff-8ae7-96e3f88eafe6" ]
    Thing station station03 "SL_IN_GasStation03 HEM (Elmshorn)" @ "Internet"[ locationid = "e1a15081-24e2-9107-e040-0b0a3dfe563c" ]
    Thing station station04 "SL_IN_GasStation04 Star (Elmshorn)" @ "Internet"[ locationid = "005056ba-7cb6-1ed2-bceb-bc577a5e6d4e" ]
    Thing station station05 "SL_IN_GasStation05 HEM (Wedel)" @ "Internet"[ locationid = "e1a15081-254f-9107-e040-0b0a3dfe563c" ]
    Thing station station06 "SL_IN_GasStation06 SB (Wedel)" @ "Internet"[ locationid = "65c348cf-5ce7-4820-a24a-da0dbf816204" ]
}

and all the items that i use

/*---------------------------------------------------------------------------------------------------------------------
  
    Thing: All additional items to prcoess gas station data

---------------------------------------------------------------------------------------------------------------------*/
Group tSL_OH_GasStationItems "Gas station realted items" <scenegasstation> (gSL_openHAB)
    DateTime                    iSL_OH_GasStationItems_LastUpdate
                                "Gas station: Last update [%1$td.%1$tm.%1$ty %1$tH:%1$tM]"
                                <timeclock>
                                (tSL_OH_GasStationItems)

    Switch                      iSL_OH_GasStationItems_Timer
                                "Gas station: Timer [MAP(switch.map):%s]"
                                <timesandglass>
                                (tSL_OH_GasStationItems)
                                { expire="2m,state=OFF" }

    String                      iSL_OH_GasStationItems_Display01
                                "Gas station: Display 01 [%s]"
                                <scenegasstation>
                                (tSL_OH_GasStationItems)

    String                      iSL_OH_GasStationItems_Display02
                                "Gas station: Display 02 [%s]"
                                <scenegasstation>
                                (tSL_OH_GasStationItems)

    String                      iSL_OH_GasStationItems_Display03
                                "Gas station: Display 03 [%s]"
                                <scenegasstation>
                                (tSL_OH_GasStationItems)

    String                      iSL_OH_GasStationItems_Display04
                                "Gas station: Display 04 [%s]"
                                <scenegasstation>
                                (tSL_OH_GasStationItems)

    String                      iSL_OH_GasStationItems_Display05
                                "Gas station: Display 05 [%s]"
                                <scenegasstation>
                                (tSL_OH_GasStationItems)

    String                      iSL_OH_GasStationItems_Display06
                                "Gas station: Display 06 [%s]"
                                <scenegasstation>
                                (tSL_OH_GasStationItems)

/*---------------------------------------------------------------------------------------------------------------------
  
    Thing: SL_IN_GasStation01 - Nordoel (Elmshorn)

---------------------------------------------------------------------------------------------------------------------*/
Group tSL_IN_GasStation01 "Gas station 01: Nordoel (Elmshorn)" <scenegasstation> (gSL_Internet)
    Number                      iSL_IN_GasStation01_Diesel
                                "Gas station 01: Diesel [%.3f ā‚¬]"
                                <scenegasstation>
                                (tSL_IN_GasStation01, gGasStationPrices, gStandardTimeline)
                                { channel="tankerkoenig:station:100720191523:station01:diesel" }

    Contact                     iSL_IN_GasStation01_Open
                                "Gas station 01: Status [MAP(contact.map):%s]"
                                <scenegasstation>
                                (tSL_IN_GasStation01)
                                { channel="tankerkoenig:station:100720191523:station01:station_open" }


/*---------------------------------------------------------------------------------------------------------------------
  
    Thing: SL_IN_GasStation02 - Nordoel (Uetersen)

---------------------------------------------------------------------------------------------------------------------*/
Group tSL_IN_GasStation02 "Gas station 02: Nordoel (Uetersen)" <scenegasstation> (gSL_Internet)
    Number                      iSL_IN_GasStation02_Diesel
                                "Gas station 02: Diesel [%.3f ā‚¬]"
                                <scenegasstation>
                                (tSL_IN_GasStation02, gGasStationPrices, gStandardTimeline)
                                { channel="tankerkoenig:station:100720191523:station02:diesel" }

    Contact                     iSL_IN_GasStation02_Open
                                "Gas station 02: Status [MAP(contact.map):%s]"
                                <scenegasstation>
                                (tSL_IN_GasStation02)
                                { channel="tankerkoenig:station:100720191523:station02:station_open" }


/*---------------------------------------------------------------------------------------------------------------------
  
    Thing: SL_IN_GasStation03 - HEM (Elmshorn)

---------------------------------------------------------------------------------------------------------------------*/
Group tSL_IN_GasStation03 "Gas station 03: HEM (Elmshorn)" <scenegasstation> (gSL_Internet)
    Number                      iSL_IN_GasStation03_Diesel
                                "Gas station 03: Diesel [%.3f ā‚¬]"
                                <scenegasstation>
                                (tSL_IN_GasStation03, gGasStationPrices, gStandardTimeline)
                                { channel="tankerkoenig:station:100720191523:station03:diesel" }

    Contact                     iSL_IN_GasStation03_Open
                                "Gas station 03: Status [MAP(contact.map):%s]"
                                <scenegasstation>
                                (tSL_IN_GasStation03)
                                { channel="tankerkoenig:station:100720191523:station03:station_open" }


/*---------------------------------------------------------------------------------------------------------------------
  
    Thing: SL_IN_GasStation04 - Star (Elmshorn)

---------------------------------------------------------------------------------------------------------------------*/
Group tSL_IN_GasStation04 "Gas station 04: Star (Elmshorn)" <scenegasstation> (gSL_Internet)
    Number                      iSL_IN_GasStation04_Diesel
                                "Gas station 04: Diesel [%.3f ā‚¬]"
                                <scenegasstation>
                                (tSL_IN_GasStation04, gGasStationPrices, gStandardTimeline)
                                { channel="tankerkoenig:station:100720191523:station04:diesel" }

    Contact                     iSL_IN_GasStation04_Open
                                "Gas station 04: Status [MAP(contact.map):%s]"
                                <scenegasstation>
                                (tSL_IN_GasStation04)
                                { channel="tankerkoenig:station:100720191523:station04:station_open" }


/*---------------------------------------------------------------------------------------------------------------------
  
    Thing: SL_IN_GasStation05 - HEM (Wedel)

---------------------------------------------------------------------------------------------------------------------*/
Group tSL_IN_GasStation05 "Gas station 05: HEM (Wedel)" <scenegasstation> (gSL_Internet)
    Number                      iSL_IN_GasStation05_Diesel
                                "Gas station 05: Diesel [%.3f ā‚¬]"
                                <scenegasstation>
                                (tSL_IN_GasStation05, gGasStationPrices, gStandardTimeline)
                                { channel="tankerkoenig:station:100720191523:station05:diesel" }

    Contact                     iSL_IN_GasStation05_Open
                                "Gas station 05: Status [MAP(contact.map):%s]"
                                <scenegasstation>
                                (tSL_IN_GasStation05)
                                { channel="tankerkoenig:station:100720191523:station05:station_open" }


/*---------------------------------------------------------------------------------------------------------------------
  
    Thing: SL_IN_GasStation06 - SB (Wedel)

---------------------------------------------------------------------------------------------------------------------*/
Group tSL_IN_GasStation06 "Gas station 06: SB (Wedel)" <scenegasstation> (gSL_Internet)
    Number                      iSL_IN_GasStation06_Diesel
                                "Gas station 06: Diesel [%.3f ā‚¬]"
                                <scenegasstation>
                                (tSL_IN_GasStation06, gGasStationPrices, gStandardTimeline)
                                { channel="tankerkoenig:station:100720191523:station06:diesel" }

    Contact                     iSL_IN_GasStation06_Open
                                "Gas station 06: Status [MAP(contact.map):%s]"
                                <scenegasstation>
                                (tSL_IN_GasStation06)
                                { channel="tankerkoenig:station:100720191523:station06:station_open" }

and the rules to sort the prices

/*---------------------------------------------------------------------------------------------------------------------
  
    Imports

---------------------------------------------------------------------------------------------------------------------*/
import org.eclipse.smarthome.model.script.ScriptServiceUtil


/*---------------------------------------------------------------------------------------------------------------------
  
    Global variables and constants

---------------------------------------------------------------------------------------------------------------------*/
val String ruleIdentification = "user.gasstationrule."


/*---------------------------------------------------------------------------------------------------------------------
  
    Rule: Gas Station Price Change Trigger

    Description:    This rule triggers the trigger item which is connected to the expire binding. After 2 minutes the
                    switch is reset to Off and the price sort rule is executed.

---------------------------------------------------------------------------------------------------------------------*/
rule "Gas Station Price Changed Trigger"
when
    
    Member of gGasStationPrices changed

then
    
    val String ruleIdentifier = ruleIdentification+"GasStationPriceChangedTrigger"

    logInfo(ruleIdentifier, "Gas station prices have changed. Trigger is {}.", transform("MAP", "i18n_gasstation.map", triggeringItem.name.substring(1, triggeringItem.name.lastIndexOf('_'))))
    iSL_OH_GasStationItems_Timer.postUpdate(ON)

end


/*---------------------------------------------------------------------------------------------------------------------
  
    Rule: Process Gas Station Prices

    Description: Sort gas station prices and setup the diaplay items in the right order.

---------------------------------------------------------------------------------------------------------------------*/
rule "Gas Station Price Sorter"
when

    Item iSL_OH_GasStationItems_Timer changed to OFF     

then
    
    val String ruleIdentifier = ruleIdentification+"GasStationPriceSorter"

    var Double actualPrice
    var Double previousPrice
    var String indicator

    logInfo(ruleIdentifier, "Gas station price sorting has been triggered.")

    gGasStationPrices.members.filter[ price | (price != UNDEF) && (price != NULL) ].sortBy[ state as DecimalType ].forEach[ gasPrice, index |
        
        val stationOpen = ScriptServiceUtil.getItemRegistry.getItem("i"+gasPrice.name.substring(1, gasPrice.name.lastIndexOf('_'))+"_Open") as ContactItem
        val stationDisplay = ScriptServiceUtil.getItemRegistry.getItem("iSL_OH_GasStationItems_Display"+String::format("%02d", index+1)) as StringItem

        stationDisplay.setLabel(transform("MAP", "i18n_gasstation.map", gasPrice.name.substring(1, gasPrice.name.lastIndexOf('_'))))

        if (stationOpen.state == OPEN) {
        
            actualPrice = (gasPrice.state as DecimalType).doubleValue()
            previousPrice = (gasPrice.previousState(true).state as DecimalType).doubleValue()
            indicator = if (actualPrice > previousPrice) "ā†‘" else if (actualPrice < previousPrice) "ā†“" else "ā†’"
            
            stationDisplay.postUpdate(String::format("%s %.3f ā‚¬", indicator, actualPrice))
        
        } else {
        
            stationDisplay.postUpdate(transform("MAP", "i18n_contact.map", stationOpen.state.toString))
        
        }
    
    ]

    iSL_OH_GasStationItems_LastUpdate.postUpdate(now.toString)
    logInfo(ruleIdentifier, "Gas station prices are sorted now")

end

and last but not least the sitemap to display the items

sitemap smarthome label="Smart Home" {

    Text label="Information" icon="messageinfo" {

        Frame label="Dieselpreise" {
            Text item=iSL_OH_GasStationItems_LastUpdate label="Letzte Abfrage [%1$tH:%1$tM]" icon="timeclock"
            Text item=iSL_OH_GasStationItems_Display01
            Text item=iSL_OH_GasStationItems_Display02
            Text item=iSL_OH_GasStationItems_Display03
            Text item=iSL_OH_GasStationItems_Display04
            Text item=iSL_OH_GasStationItems_Display05
            Text item=iSL_OH_GasStationItems_Display06
        }

    }

}

and my first try with habpanel based on the matrix theme.

<div class="section">

  <div class="sectionIconContainer">
    <div class="sectionIcon"><svg viewBox="0 0 361 361"><use xlink:href="/static/matrix-theme/knx-uf_icons.svg#scenegasstation"></use></svg></div>
  </div>

  <div class="title">Dieselpreise {{itemValue('iSL_OH_GasStationItems_LastUpdate') | date:'HH:mm'}}</div>

	<div class="controls">

		<div class="widget">
			<div class="icon off"><svg viewBox="0 0 361 361"><use xlink:href="/static/matrix-theme/knx-uf_icons.svg#scenegasstation"></use></svg></div>
			<div class="name">1. {{getItem('iSL_OH_GasStationItems_Display01').label}}</div>
			<div class="valueGroup"><div class="value">{{itemValue('iSL_OH_GasStationItems_Display01')}}</div></div>			
		</div>

		<div class="widget">
			<div class="icon off"><svg viewBox="0 0 361 361"><use xlink:href="/static/matrix-theme/knx-uf_icons.svg#scenegasstation"></use></svg></div>
			<div class="name">2. {{getItem('iSL_OH_GasStationItems_Display02').label}}</div>
			<div class="valueGroup"><div class="value">{{itemValue('iSL_OH_GasStationItems_Display02')}}</div></div>			
		</div>

		<div class="widget">
			<div class="icon off"><svg viewBox="0 0 361 361"><use xlink:href="/static/matrix-theme/knx-uf_icons.svg#scenegasstation"></use></svg></div>
			<div class="name">3. {{getItem('iSL_OH_GasStationItems_Display03').label}}</div>
			<div class="valueGroup"><div class="value">{{itemValue('iSL_OH_GasStationItems_Display03')}}</div></div>			
		</div>

		<div class="widget">
			<div class="icon off"><svg viewBox="0 0 361 361"><use xlink:href="/static/matrix-theme/knx-uf_icons.svg#scenegasstation"></use></svg></div>
			<div class="name">4. {{getItem('iSL_OH_GasStationItems_Display04').label}}</div>
			<div class="valueGroup"><div class="value">{{itemValue('iSL_OH_GasStationItems_Display04')}}</div></div>			
		</div>

		<div class="widget">
			<div class="icon off"><svg viewBox="0 0 361 361"><use xlink:href="/static/matrix-theme/knx-uf_icons.svg#scenegasstation"></use></svg></div>
			<div class="name">5. {{getItem('iSL_OH_GasStationItems_Display05').label}}</div>
			<div class="valueGroup"><div class="value">{{itemValue('iSL_OH_GasStationItems_Display05')}}</div></div>			
		</div>

    <div class="widget">
			<div class="icon off"><svg viewBox="0 0 361 361"><use xlink:href="/static/matrix-theme/knx-uf_icons.svg#scenegasstation"></use></svg></div>
			<div class="name">6. {{getItem('iSL_OH_GasStationItems_Display06').label}}</div>
			<div class="valueGroup"><div class="value">{{itemValue('iSL_OH_GasStationItems_Display06')}}</div></div>			
		</div>

	</div>
  </div>
3 Likes

Hello,

being new I try to adapt your work to learn something, but have a really hard time as the MAPs are missing - namely

  • switch.map
  • contact.map
  • i18n_gasstation.map
  • i18n_contact.map.

Would you please be so kind to publish them here for completeness?

Thank you very much!

switch.map and contact.map simply translate the ON/OFF OPEN/Close to their german representations

gaststation.map translates the itemnanames to the station name to set the label

OL_IN_FuelStation01=Tanklager Baack (Kiebitzreiher Chaussee, 25358 Horst)
...

could you just upload the files here, this would make it easy

Seconded! Also the given map explanation of @Dibbler42 doesnā€˜t relate to the rest of the scripts. The item name OL_IN_ā€¦ has been never seen before. iā€˜m even more confusedā€¦

The point is that i have changed my rules over the last six month and the names of items and files has changed.

The corresponding name to OL_IN_FuelStation01 should be iSL_IN_GasStation01 and so on.

Unfortunately it is now allowed to upload map files. If you could describe your problem a little bit more, i am sure that we could solve the problem

could you upload the complete set of files to a repo? That makes it easy to avoid inconsistencies.

Guys at the moment i have not the time to setup a repo with all required tasks.

Just describe your problem and other may help as well

Please correct me, but you didnā€™t posted those files

  • switch.map
  • contact.map
  • i18n_gasstation.map
  • i18n_contact.map.

So, Iā€™m not able to get it running using the above code sniplets.

You are right i didnā€™t post there files, but i gave the information what is in the files. From my point of view you need to adapt these files for your system. In addition the code is understandable and it should be no problem to modify things to your needs.

If you are not able to get these snippets running you should describe the concrete problem and the community will assist, but i think you could/should not expect an out of the shelf solution.

Just an example:

I do not understand this line. Could someone help? i Understand that this is a logline, bute the transformation statement ist not clear to me

logInfo(ruleIdentifier, "Gas station prices have changed. Trigger is {}.", transform("MAP", "i18n_gasstation.map", triggeringItem.name.substring(1, triggeringItem.name.lastIndexOf('_'))))

The answer would likely be.
To understand transformations just use the documentation. But in short the transformation consist of thre parts.

Part1: Type of transformation -> Here a map tranformation
Part2: Map to use -> Here the file i18n_gasstation.map
Part3: The value to transform -> OK thats a little bit tricky

TriggerinItem is a predefined variable in alle rule that contains the item that triggered the rule. Let it be ā€œiSL_IN_GasStation01_Dieselā€.

triggeringItem.name.substring(1, triggeringItem.name.lastIndexOf('_'))

The above code creates a substring from the item nae starting from the first character up to the last underscore. This leads to iSL_IN_GasStation01. So the map file should map iSL_IN_GasStation01 to something more readable like ā€œMeine Essotankstelle um die Eckeā€

1 Like

Hi
thanks for sharing this part. I got it running, the station name also gets updates in sitemap, the prices are picked up an get sorted. My last issue is this line:

I believe, the gasPrice.previousState(true) needs to be persistand, am I right? Right now, the rule fails to continue on this stage, since there is no previous state avalable.
However, I installed influxDB and persited the previousPrice, but the rule still fails.
Maybe you can give me a hint on this?
Thanks for your effort
Marcus

You are right the gas prices need to be persited. The used statement rely on the default persistence Service defined inthe system setting. If this is not set to influx it will noct work. Another solution is to add the influx in the previousState statement (Just check the documentation on this)

In my system the indicator is currently not active. I just changed the correspondig if clause.

1 Like

ok, managed it, it has to be

previousPrice = if (previousPrice == null) previousPrice = actualPrice else (gasPrice.previousState(true, "influxdb") as DecimalType)
1 Like

Thank you @Dibbler42 @alkaline
I also got it to work.

One last question remains - I have currently one gas station which is closed and therefore retuns no gas prices, but ā€œUNDEFā€ instead.

According to the rules this shouldnā€™t be a problem because of this boolean expression:

logInfo(ruleIdentifier, "Gas station price sorting has been triggered.")
gGasStationPrices.members.filter[ price | (price != UNDEF) && (price != NULL) ].sortBy[ state as DecimalType ].forEach[ gasPrice, index |

Hereā€™s the current array of gGasStationPrices:

Item gGasStationPrices | NULL
Members:
Item iSL_IN_GasStation01_Diesel | UNDEF
Item iSL_IN_GasStation02_Diesel | 1.239
Item iSL_IN_GasStation03_Diesel | 1.219
Item iSL_IN_GasStation04_Diesel | 1.269
Item iSL_IN_GasStation05_Diesel | 1.209
Item iSL_IN_GasStation06_Diesel | 1.209

Strangely the rule processing breaks with the following message logged and I donā€™t know why.

[INFO ] [gasstationrule.GasStationPriceSorter] - Gas station price sorting has been triggered.
[ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Gas Station Price Sorter': Could not cast UNDEF to org.eclipse.smarthome.core.library.types.DecimalType; line 62, column 93, length 20

Any help would be appreciated.

Once in a while I do have similar messages, but it wonā€™t break the rule. If I take a look at your posting the prices are still available, correct? My log shows this kind of message late in the evening (10 PM)
As soon as the gasstation will open the rule will work.

Currently i have no time to look into this, but it should be easy to check the open/Closed switches of the gasstations and create some if clause