Tibber integration?

I tried both of these with the demo access token that they provide for developers.

driver: tibber-smartthings-app/tibber-price-device-handler.groovy at master · tibbercom/tibber-smartthings-app · GitHub
app: tibber-smartthings-app/tibber-price-example-smart-app.groovy at master · tibbercom/tibber-smartthings-app · GitHub

They're both a little bit rough around the edges, but they appear to install and work correctly on Hubitat.

The app demonstrates how you might use the price reporting to perform some other action like what @mario.fuchs describes. In the case of the demo, it turns a switch on when the energy price exceeds a certain threshold.



I did not test this additional app that provides integration with the Tibber app: tibber-smartthings-app/smartapp.groovy at master · tibbercom/tibber-smartthings-app · GitHub

It won't work as written due to URL differences between Hubitat and SmartThings, but it doesn't look like it would take much to fix it

1 Like

I tried with my access token and got this....

1 Like

@mailtomatte, are those values correct? I'm not sure what your conclusion was after trying it.

Yes I guess they are right... Here is a screen dump a couple of hours later when price has changed. But you need the last app to be able to get any use I guess...

1 Like

Ive played around and I think I got Tibber to work with Hubitat in Rule Machine.

I set up a rule for my dehumidifier who is a major powerswallows.

So if humidity is over 50% and Tibber price is under 35 (SEK öre) and lights in garage is off (dehumidifier is loud so only on when im not in the garage)... So I will test it for some days. @monsterdykaren @tomw

2 Likes

Yep, that's basically what I had in mind.

If I was developing the Tibber app myself, I'd add the Refresh capability so that it could be additionally refreshed on demand. It refreshes the values every two hours by default.

There's also a bug (or missing feature?) where the current energy consumption is incorrectly reported as the being the same as the current price.

I hacked out a modified version of the code that adds these two features. I can't test the energy reporting (it reports as null with the demo key), so one of you guys can hopefully give it a shot.

/**
 *  Copyright 2017 Tibber
 *
 *  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.
 *
 *  Tibber price
 *
 *  Author: tibberdev
 *
 *  Date: 2018-01-11
 *
 *  Revision notes: price is now given in currency/100. For NOK. the unit is now in Øre
 *  Revision notes: presenting price as energymeter, user can get price through energy attribute
 */
metadata {
	definition (name: "Tibber price device handler", namespace: "Tibber", author: "tibberdev") {
		capability "Sensor"
		capability "Energy Meter"
        capability "Refresh"
		attribute "price", "number"
		attribute "priceNextHour", "number"
		attribute "priceNextHourLabel", "string"
		attribute "pricePlus2Hour", "number"
		attribute "pricePlus2HourLabel", "string"
		attribute "priceMaxDay", "number"
		attribute "priceMaxDayLabel", "string"
		attribute "priceMinDay", "number"
		attribute "priceMinDayLabel", "string"
		attribute "currency", "string"
	}
	// tile definitions
	tiles(scale: 2) {
        multiAttributeTile(name:"valueTile", type:"generic", width:6, height:4, backgroundColor:"#00a0dc") {
            tileAttribute("device.price", key: "PRIMARY_CONTROL") {
                attributeState "price", label:'${currentValue}', defaultState: true,
                backgroundColors:backgroundColors()
            }
            tileAttribute("device.currency", key: "SECONDARY_CONTROL") {
                attributeState "currency", label:'${currentValue}', defaultState: true
            }
        }
        
        valueTile("priceNextHourLabelTile", "device.priceNextHourLabel", decoration: "flat", width: 4, height: 1) {
            state "priceNextHourLabel", label:'${currentValue}'
        }
        valueTile("priceNextHourTile", "device.priceNextHour", decoration: "flat", width: 2, height: 1) {
            state "priceNextHour", label:'${currentValue}',                        
            backgroundColors:backgroundColors()
        }
        valueTile("pricePlus2HourLabelTile", "device.pricePlus2HourLabel", decoration: "flat", width: 4, height: 1) {
            state "pricePlus2HourLabel", label:'${currentValue}'
        }
        valueTile("pricePlus2HourTile", "device.pricePlus2Hour", decoration: "flat", width: 2, height: 1) {
            state "pricePlus2Hour", label:'${currentValue}',
            backgroundColors:backgroundColors()
        }
        valueTile("priceMaxDayLabelTile", "device.priceMaxDayLabel", decoration: "flat", width: 4, height: 1) {
            state "priceMaxDayLabel", label:'${currentValue}'
        }
        valueTile("priceMaxDayTile", "device.priceMaxDay", decoration: "flat", width: 2, height: 1) {
            state "priceMaxDay", label:'${currentValue}',
            backgroundColors:backgroundColors()
        }
        valueTile("priceMinDayLabelTile", "device.priceMinDayLabel", decoration: "flat", width: 4, height: 1) {
            state "priceMinDayLabel", label:'${currentValue}'
        }        
        valueTile("priceMinDayTile", "device.priceMinDay", decoration: "flat", width: 2, height: 1) {
            state "priceMinDay", label:'${currentValue}',
            backgroundColors:backgroundColors()
        }
        main (["valueTile"])
        details(["valueTile", "PriceEnergyTile", "priceNextHourLabelTile", "priceNextHourTile", "pricePlus2HourLabelTile", "pricePlus2HourTile","priceMaxDayLabelTile", "priceMaxDayTile","priceMinDayLabelTile", "priceMinDayTile"])
	}

    preferences {
        input (
            type: "paragraph",
            element: "paragraph",
            title: "Tibber API key",
            description: "You'll find the API key at https://developer.tibber.com/settings/accesstoken"
        )
        input (
            name: "tibber_apikey",
            type: "password",
            title: "API Key",
            description: "Enter the Tibber API key",
            required: true,
            displayDuringSetup: true
        )
    }
}

def initialize() {
	state.price = 100;
	log.debug("init")
    getPrice()
    schedule("0 2 * * * ?", getPrice)
}

def installed() {
	log.debug "Installed"
    initialize()
}

def updated() {
	log.debug "Updated"
    initialize()
}

def refresh()
{
    unschedule(getPrice)
    getPrice()
    schedule("0 2 * * * ?", getPrice)    
}

def getPrice() {
	log.debug("getPrice")
    if(tibber_apikey == null){
        log.error("API key is not set. Please set it in the settings.")
    } else {
        def params = [
            uri: "https://api.tibber.com/v1-beta/gql",
            headers: ["Content-Type": "application/json;charset=UTF-8" , "Authorization": "Bearer $tibber_apikey"],
            body: graphQLApiQuery()
        ]
        try {
            httpPostJson(params) { resp ->
                if(resp.status == 200){
                    log.debug "${resp.data}"
                    def today = resp.data.data.viewer.homes[0].currentSubscription.priceInfo.today
                    def tomorrow = resp.data.data.viewer.homes[0].currentSubscription.priceInfo.tomorrow
                    
                    def consumption = resp.data.data.viewer.homes[0].consumption.nodes[0].consumption?.toFloat()
                    def consumptionUnit = resp.data.data.viewer.homes[0].consumption.nodes[0].consumptionUnit?.toString()
                    def price = Math.round(resp.data.data.viewer.homes[0].currentSubscription.priceInfo.current.total * 100)
                    def priceMaxDay = Math.round(MaxValue(today) *100)
                    def priceMaxDayLabel = "Max price @ ${MaxValueTimestamp(today)}"
                    def priceMinDay = Math.round(MinValue(today) *100)
                    def priceMinDayLabel = "Min price @ ${MinValueTimestamp(today)}"

                    def priceList = today
                    tomorrow.each{
                        priceList << it
                    }
                    def priceNextHours = PriceNextHours(priceList)
                    def priceNextHour = Math.round(priceNextHours[0] *100)
                    def priceNextHourLabel = "@ ${priceNextHours[2]}"
                    def pricePlus2Hour = Math.round(priceNextHours[1] *100)
                    def pricePlus2HourLabel = "@ ${priceNextHours[3]}"
                    def currency = resp.data.data.viewer.homes[0].currentSubscription.priceInfo.current.currency

                    currency = "${currency}: ${currencyToMinor(currency)}/kWh"

                    state.currency = currency
                    state.price = price
                    state.priceNextHour = priceNextHour
                    state.priceNextHourLabel = priceNextHourLabel
                    state.pricePlus2Hour = pricePlus2Hour
                    state.pricePlus2HourLabel = pricePlus2HourLabel
                    state.priceMaxDay = priceMaxDay
                    state.priceMaxDayLabel = priceMaxDayLabel
                    state.priceMinDay = priceMinDay
                    state.priceMinDayLabel = priceMinDayLabel

                    sendEvent(name: "energy", value: consumption, unit: consumptionUnit)
                    sendEvent(name: "price", value: state.price, unit: currency)
                    sendEvent(name: "priceNextHour", value: state.priceNextHour, unit: currency)
                    sendEvent(name: "pricePlus2Hour", value: state.pricePlus2Hour, unit: currency)
                    sendEvent(name: "priceMaxDay", value: state.priceMaxDay, unit: currency)
                    sendEvent(name: "priceMinDay", value: state.priceMinDay, unit: currency)

                    sendEvent(name: "priceNextHourLabel", value: state.priceNextHourLabel)
                    sendEvent(name: "pricePlus2HourLabel", value: state.pricePlus2HourLabel)
                    sendEvent(name: "priceMaxDayLabel", value: state.priceMaxDayLabel)
                    sendEvent(name: "priceMinDayLabel", value: state.priceMinDayLabel)

                    sendEvent(name: "currency", value: state.currency)
                }
            }
        } catch (e) {
            log.debug "something went wrong: $e"
        }
    }
}
def parse(String description) {
    log.debug "parse description: ${description}"
    def eventMap = [
        createEvent(name: "energy", value: state.price, unit: state.currency)
        ,createEvent(name: "price", value: state.price, unit: state.currency)
        ,createEvent(name: "priceNextHour", value: state.priceNextHour, unit: state.currency)
        ,createEvent(name: "pricePlus2Hour", value: state.pricePlus2Hour, unit: state.currency)
        ,createEvent(name: "priceMaxDay", value: state.priceMaxDay, unit: state.currency)
        ,createEvent(name: "priceMinDay", value: state.priceMinDay, unit: state.currency)
        ,createEvent(name: "priceNextHourLabel", value: state.priceNextHourLabel)
        ,createEvent(name: "pricePlus2HourLabel", value: state.pricePlus2HourLabel)
        ,createEvent(name: "priceMaxDayLabel", value: state.priceMaxDayLabel)
        ,createEvent(name: "priceMinDayLabel", value: state.priceMinDayLabel)    
        ,createEvent(name: "currencyLabel", value: state.currency, unit: state.currency)   
    ]
    log.debug "Parse returned ${description}"
    return eventMap
}

def currencyToMinor(String currency){
	def currencyUnit = "";
	switch(currency){
    	case "NOK":currencyUnit = "Øre";break;
        case "SEK":currencyUnit = "Øre";break;
        case "USD":currencyUnit = "Penny";break;
        default: currencyUnit = "";break;
    }
    return currencyUnit;
    
}
def backgroundColors(){
    return [
		[value: 20, color: "#02A701"],
      	[value: 39, color: "#6CCD00"],
      	[value: 59, color: "#ECD400"],
      	[value: 74, color: "#FD6700"],
      	[value: 95, color: "#FE3500"]
    ]
}

def graphQLApiQuery(){
    return '{"query": "{viewer {homes {currentSubscription {priceInfo { current {total currency} today{ total startsAt } tomorrow{ total startsAt }}} consumption(resolution: HOURLY, last: 1) { nodes { from to cost unitPrice unitPriceVAT consumption consumptionUnit }}}}}", "variables": null, "operationName": null}';
}

def MaxValueTimestamp(List values){
	def max = 0
    def maxTimestamp = ""
	values.each{
    	def timestamp = it.startsAt
        def total = it.total
        if(total>max){
        	max = it.total
            maxTimestamp = timestamp
        }
    }
    return maxTimestamp.substring(11,13)
}
def MaxValue(List values){
	def max = 0
    def maxTimestamp = ""
	values.each{
    	def timestamp = it.startsAt
        def total = it.total
        if(total>max){
        	max = it.total
            maxTimestamp = timestamp
        }
    }
    return max
}

def MinValueTimestamp(List values){
	def min = 1000
    def minTimestamp = ""
	values.each{
    	def timestamp = it.startsAt
        def total = it.total   
        if(it.total<min){
        	min = it.total
            minTimestamp = timestamp
        }
    }
    return minTimestamp.substring(11,13)
}
def MinValue(List values){
	def min = 1000
    def minTimestamp = ""
	values.each{
    	def timestamp = it.startsAt
        def total = it.total   
        if(it.total<min){
        	min = it.total
            minTimestamp = timestamp
        }
    }
    return min
}


def PriceNextHours(List values){
	def priceNowTimestamp = 0
    def priceNextHour = -1;
    def priceNextNextHour = -1;
    def i=0
    values.each{
        Calendar cal=Calendar.getInstance();
        def hourNowUtc = cal.get(Calendar.HOUR_OF_DAY) + 1
        def dayNowUtc = cal.get(Calendar.DAY_OF_MONTH)    
        def startsAt = it.startsAt
        def total = it.total        
        int hourNow = startsAt.substring(11,13) as int
        int dayNow = startsAt.substring(8,10) as int
        int hourOffset = startsAt.substring(20,22) as int
        def timeZoneOperator = startsAt.substring(19,20)
        if(timeZoneOperator=="+"){
            hourNowUtc = hourNowUtc + hourOffset
        }
        if(timeZoneOperator=="-"){
            hourNowUtc = hourNowUtc - hourOffset
        }
        if(hourNowUtc<0){
        	hourNowUtc = hourNowUtc+24 //wrap
            dayNowUtc = dayNowUtc-1
        }
        if(hourNowUtc>23){
        	hourNowUtc = hourNowUtc-24 //wrap
            dayNowUtc = dayNowUtc+1
        }
        if(hourNow == hourNowUtc && dayNow == dayNowUtc ){
        	priceNextHour = it.total
            priceNextNextHour = values[i+1].total   
            priceNowTimestamp = hourNowUtc
        }
    	i++

    }
    
    def priceNextTimestamp = 0
    if(priceNowTimestamp<23)
    	priceNextTimestamp = priceNowTimestamp + 1
    return [priceNextHour, priceNextNextHour, fromToTimestamp(priceNowTimestamp), fromToTimestamp(priceNextTimestamp)]
}
def fromToTimestamp(def timestamp){
	def from = timestamp
    def to = timestamp + 1
    if(to>23){
    	to = 0
    }
    return "${formatTimestamp(from)} - ${formatTimestamp(to)} "
}
def formatTimestamp(def timestamp){
	if(timestamp < 9)
    	return "0${timestamp}"
    return timestamp
}
1 Like

Nice! Tested it.. Null here on energy also but refresh button works good!...

1 Like

Cool! I'll poke around more in the API to see if I can find a way to get an energy usage reading. The API itself is in some sort of beta state, so some things did not look fully supported. It could explain why they didn't report energy in the example driver... :wink:

2 Likes

@monsterdykaren, I worked with @mailtomatte for a while trying to get live measurements to work. We were not able to get it to work, because real-time reporting requires a Tibber Pulse or Watty device, which neither of us have. Do you have either of those?

Sadly not. :confused:

Sorry to poke an old thread, but I have a Tibber Pulse device :slight_smile:

1 Like

Cool! Try the code in my post here: Tibber integration? - #18 by tomw

It's a very slightly modified version of the code that Tibber shared for use with SmartThings.

Also FYI: @Carl

1 Like
  • currency : NOK: Øre/kWh
  • energy : 2.597
  • price : 62
  • priceMaxDay : 65
  • priceMaxDayLabel : Max price @ 09
  • priceMinDay : 52
  • priceMinDayLabel : Min price @ 06
  • priceNextHour : 62
  • priceNextHourLabel : @ 19 - 20
  • pricePlus2Hour : 62
  • pricePlus2HourLabel : @ 20 - 21

Trying to refresh makes the energy "null". The values are otherwise correct.

Thank you for the debug prints.

It looks like the duration of this energy measurement is 1 hour (20:00-21:00). This would mean that your consumption was .395 kWh in that hour. Does the app tell you the time period for the averaged power measurement? The daily total that you mentioned seems too low if the average usage throughout the day was 372 W. But if that was the average usage for the past hour, perhaps it makes sense.

That is odd. Which step causes it to again show a value in energy, if refresh marks it to null? Is there anything mentioned in the debug log in Hubitat?

You can search for log.debug in the code and comment the uninteresting lines out (add // to beginning of line) or delete them completely.

1 Like

Hi

Could someone please help port this updated Smartthings version of the tibber DH?

https://raw.githubusercontent.com/cscheiene/SmartThings-cscheiene/master/devicetypes/cscheiene/tibber-price.src/tibber-price.groovy

1 Like

Looks like, outside of some capabilities that don’t map, that the code should pretty much work as is. Try this code as a device driver:

Ported Code

/**
 *  Copyright 2017 Tibber
 *
 *  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.
 *
 *  Tibber price
 *
 *  Author: tibberdev,cscheiene
 */
metadata {
	definition (name: "Tibber Price", namespace: "cscheiene", author: "tibberdev, cscheiene", mnmn: "SmartThingsCommunity", vid: "560e33c3-94b5-3b05-970c-de5af9ae5ae9", ocfDeviceType: "x.com.st.d.energymeter") {
		capability "Sensor"
        capability "Refresh"
/*		capability "islandtravel33177.tibberPriceNextHour"
        capability "islandtravel33177.tibberPrice"
        capability "islandtravel33177.tibberPriceMaxDayLabel"
        capability "islandtravel33177.tibberPriceMaxDay"
        capability "islandtravel33177.tibberPriceMinDay"
        capability "islandtravel33177.tibberPriceMinLabel"
        capability "islandtravel33177.tibberPricePlusTwoHour"
        capability "islandtravel33177.tibberPriceLevel"
        capability "islandtravel33177.tibberConsumptionPrevHour"
        capability "islandtravel33177.tibberPriceNextHourDisplay"
        capability "islandtravel33177.tibberPricePlusTwoHourDisplay"
        capability "islandtravel33177.tibberPriceMinDayDisplay"
        capability "islandtravel33177.tibberPriceMaxDayDisplay"
        capability "islandtravel33177.tibberAddress" */
		capability "Energy Meter" //workaround for Actions tiles etc
		
		attribute "currency", "string"
	}

    preferences {
        input (
            name: "tibber_apikey",
            type: "password",
            title: "API Key",
            description: "Enter the Tibber API key",
            required: true,
            displayDuringSetup: true
        )
        input (
            name: "apkeylink",
            type: "paragraph",
            title: "API Key can be found here:",
            description: "https://developer.tibber.com/settings/accesstoken"
        )
		input (
            name: "home",
            type: "number",
            title: "Home",
            description: "Enter the home you want to display, default is 0",
            required: true,
            displayDuringSetup: true,
        )
		input (
            name: "NORMAL",
            type: "paragraph",
            title: "NORMAL",
            description: "The price is greater than 90 % and smaller than 115 % compared to average price."
        )
        input (
            name: "CHEAP",
            type: "paragraph",
            title: "CHEAP",
            description: "The price is greater than 60 % and smaller or equal to 90 % compared to average price."
        )
        input (
            name: "VERY CHEAP",
            type: "paragraph",
            title: "VERY CHEAP",
            description: "The price is smaller or equal to 60 % compared to average price."
        )
        input (
            name: "EXPENSIVE",
            type: "paragraph",
            title: "EXPENSIVE",
            description: "The price is greater or equal to 115 % and smaller than 140 % compared to average price."
        )
        input (
            name: "VERY EXPENSIVE",
            type: "paragraph",
            title: "VERY EXPENSIVE",
            description: "The price is greater or equal to 140 % compared to average price."
        )
        input (
            name: "VERSION",
            type: "paragraph",
            title: "Version number",
            description: "140421"
        )
    }
}

def homeNumber() {
		if(settings.home == null){
        return 0
        } else {
        return settings.home}
}        


def initialize() {
	state.price = 100;
	log.debug("init")
    getPrice()
    schedule("0 1,5,10,15,20,25,30 * * * ?", getPrice)
}

def installed() {
	log.debug "Installed"
    initialize()
}

def ping() {
    refresh()
}

def updated() {
	log.debug "Updated"
    initialize()
}

def refresh() {
    initialize()
}

def getPrice() {
	log.debug("getPrice")
    if(tibber_apikey == null){
        log.error("API key is not set. Please set it in the settings.")
    } else {
        def params = [
            uri: "https://api.tibber.com/v1-beta/gql",
            headers: ["Content-Type": "application/json;charset=UTF-8" , "Authorization": "Bearer $tibber_apikey"],
            body: graphQLApiQuery()
        ]
        try {
            httpPostJson(params) { resp ->
                if(resp.status == 200){
                    def today = resp.data.data.viewer.homes[homeNumber()].currentSubscription.priceInfo.today
                    def tomorrow = resp.data.data.viewer.homes[homeNumber()].currentSubscription.priceInfo.tomorrow

                    
                    def price = Math.round(resp.data.data.viewer.homes[homeNumber()].currentSubscription.priceInfo.current.total * 100)
                    def priceMaxDay = Math.round(MaxValue(today) *100)
                    def priceMaxDayLabel = "${MaxValueTimestamp(today)}:00"
                    def priceMinDay = Math.round(MinValue(today) *100)
                    def priceMinDayLabel = "${MinValueTimestamp(today)}:00"

                    def priceList = today
                    tomorrow.each{
                        priceList << it
                    }
                    def priceNextHours = PriceNextHours(priceList)
                    def priceNextHour = Math.round(priceNextHours[0] *100)
                    def priceNextHourLabel = "@ ${priceNextHours[2]}"
                    def pricePlusTwoHour = Math.round(priceNextHours[1] *100)
                    def pricePlusTwoHourLabel = "@ ${priceNextHours[3]}"
                    def currency = resp.data.data.viewer.homes[homeNumber()].currentSubscription.priceInfo.current.currency
                    def level = resp.data.data.viewer.homes[homeNumber()].currentSubscription.priceInfo.current.level
                    def consumptionPrevHour = resp.data.data.viewer.homes[homeNumber()].consumption.nodes[0].consumption
                    def address = resp.data.data.viewer.homes[homeNumber()].address.address1
                    
                    
                    

                    currency = "${currency}: ${currencyToMinor(currency)}/kWh"

                    state.currency = currency
                    state.level = level
                    state.price = price
                    state.priceNextHour = priceNextHour
                    state.priceNextHourLabel = priceNextHourLabel
                    state.pricePlusTwoHour = pricePlusTwoHour
                    state.pricePlusTwoHourLabel = pricePlusTwoHourLabel
                    state.priceMaxDay = priceMaxDay
                    state.priceMaxDayLabel = priceMaxDayLabel
                    state.priceMinDay = priceMinDay
                    state.priceMinDayLabel = priceMinDayLabel

                    sendEvent(name: "consumptionPrevHour", value: consumptionPrevHour, unit: "kWh")
                    sendEvent(name: "address", value: address)
                    sendEvent(name: "energy", value: price, unit: currency)
                    sendEvent(name: "price", value: state.price, unit: currency)
                    sendEvent(name: "priceNextHour", value: state.priceNextHour, unit: currency)
                    sendEvent(name: "pricePlusTwoHour", value: state.pricePlusTwoHour, unit: currency)
                    sendEvent(name: "priceMaxDay", value: state.priceMaxDay, unit: currency)
                    sendEvent(name: "priceMinDay", value: state.priceMinDay, unit: currency)
                    sendEvent(name: "priceMinDayDisplay", value: "$priceMinDay $currency @ $priceMinDayLabel")
                    sendEvent(name: "priceMaxDayDisplay", value: "$priceMaxDay $currency @ $priceMaxDayLabel")

                    sendEvent(name: "priceNextHourDisplay", value: "$priceNextHour $currency $priceNextHourLabel")
                    sendEvent(name: "pricePlusTwoHourDisplay", value: "$pricePlusTwoHour $currency $pricePlusTwoHourLabel")
                    sendEvent(name: "priceMaxDayLabel", value: state.priceMaxDayLabel)
                    sendEvent(name: "priceMinDayLabel", value: state.priceMinDayLabel)

                    sendEvent(name: "currency", value: state.currency)
                    sendEvent(name: "level", value: state.level)
                    
                    
                    log.debug "$address"
                }
            }
        } catch (e) {
            log.debug "something went wrong: $e"
        }
    }
}
def parse(String description) {
    log.debug "parse description: ${description}"
    def eventMap = [
        createEvent(name: "energy", value: state.price, unit: state.currency)
        ,createEvent(name: "level", value: state.level)
        ,createEvent(name: "price", value: state.price, unit: state.currency)
        ,createEvent(name: "priceNextHour", value: state.priceNextHour, unit: state.currency)
        ,createEvent(name: "pricePlusTwoHour", value: state.pricePlusTwoHour, unit: state.currency)
        ,createEvent(name: "priceMaxDay", value: state.priceMaxDay, unit: state.currency)
        ,createEvent(name: "priceMinDay", value: state.priceMinDay, unit: state.currency)
        ,createEvent(name: "priceNextHourLabel", value: state.priceNextHourLabel)
        ,createEvent(name: "pricePlus2HourLabel", value: state.pricePlus2HourLabel)
        ,createEvent(name: "priceMaxDayLabel", value: state.priceMaxDayLabel)
        ,createEvent(name: "priceMinDayLabel", value: state.priceMinDayLabel)    
        ,createEvent(name: "currencyLabel", value: state.currency, unit: state.currency)   
    ]
    log.debug "Parse returned ${description}"
    return eventMap
}

def currencyToMinor(String currency){
	def currencyUnit = "";
	switch(currency){
    	case "NOK":currencyUnit = "Øre";break;
        case "SEK":currencyUnit = "Øre";break;
        case "USD":currencyUnit = "Penny";break;
        default: currencyUnit = "";break;
    }
    return currencyUnit;
    
}

def graphQLApiQuery(){
    return '{"query": "{viewer {homes {address{address1} consumption(resolution: HOURLY, last: 1) {nodes {consumption consumptionUnit}} currentSubscription {priceInfo { current {total currency level} today {total startsAt} tomorrow{ total startsAt }}}}}}", "variables": null, "operationName": null}';
}

def MaxValueTimestamp(List values){
	def max = 0
    def maxTimestamp = ""
	values.each{
    	def timestamp = it.startsAt
        def total = it.total
        if(total>max){
        	max = it.total
            maxTimestamp = timestamp
        }
    }
    return maxTimestamp.substring(11,13)
}
def MaxValue(List values){
	def max = 0
    def maxTimestamp = ""
	values.each{
    	def timestamp = it.startsAt
        def total = it.total
        if(total>max){
        	max = it.total
            maxTimestamp = timestamp
        }
    }
    return max
}

def MinValueTimestamp(List values){
	def min = 1000
    def minTimestamp = ""
	values.each{
    	def timestamp = it.startsAt
        def total = it.total   
        if(it.total<min){
        	min = it.total
            minTimestamp = timestamp
        }
    }
    return minTimestamp.substring(11,13)
}
def MinValue(List values){
	def min = 1000
    def minTimestamp = ""
	values.each{
    	def timestamp = it.startsAt
        def total = it.total   
        if(it.total<min){
        	min = it.total
            minTimestamp = timestamp
        }
    }
    return min
}


def PriceNextHours(List values){
	def priceNowTimestamp = 0
    def priceNextHour = -1;
    def priceNextNextHour = -1;
    def i=0
    values.each{
        Calendar cal=Calendar.getInstance();
        def hourNowUtc = cal.get(Calendar.HOUR_OF_DAY) + 1
        def dayNowUtc = cal.get(Calendar.DAY_OF_MONTH)    
        def startsAt = it.startsAt
        def total = it.total        
        int hourNow = startsAt.substring(11,13) as int
        int dayNow = startsAt.substring(8,10) as int
        int hourOffset = startsAt.substring(20,22) as int
        def timeZoneOperator = startsAt.substring(19,20)
        if(timeZoneOperator=="+"){
            hourNowUtc = hourNowUtc + hourOffset
        }
        if(timeZoneOperator=="-"){
            hourNowUtc = hourNowUtc - hourOffset
        }
        if(hourNowUtc<0){
        	hourNowUtc = hourNowUtc+24 //wrap
            dayNowUtc = dayNowUtc-1
        }
        if(hourNowUtc>23){
        	hourNowUtc = hourNowUtc-24 //wrap
            dayNowUtc = dayNowUtc+1
        }
        if(hourNow == hourNowUtc && dayNow == dayNowUtc ){
        	priceNextHour = it.total
            priceNextNextHour = values[i+1].total   
            priceNowTimestamp = hourNowUtc
        }
    	i++

    }
    
    def priceNextTimestamp = 0
    if(priceNowTimestamp<23)
    	priceNextTimestamp = priceNowTimestamp + 1
    return [priceNextHour, priceNextNextHour, fromToTimestamp(priceNowTimestamp), fromToTimestamp(priceNextTimestamp)]
}
def fromToTimestamp(def timestamp){
	def from = timestamp
    def to = timestamp + 1
    if(to>23){
    	to = 0
    }
    return "${formatTimestamp(from)} - ${formatTimestamp(to)} "
}
def formatTimestamp(def timestamp){
	if(timestamp < 9)
    	return "0${timestamp}"
    return timestamp
}
1 Like

Thanks for the quick response and help with this.
The driver installs fine.
But I get this error

Was wondering if the non-standard capabilities had external references. Looks like they do, but a quick search through the public repositories didn't turn up anything I can use to resolve.