Tibber (Norway) websocket subscription

Hello, everybody. I have a problem, and I hope somebody please can help me.

Norwegian tibber uses graphQl subscription for live and accumulated power consumption-data. I would really like to get this data in to my IP-Symcon. To even out the peaks of powerconsumption in norway (typically on the morning and around dinnertime) the government and the power-companies charge extra based on maximum power consumption pr. hour. With these data I could regulate moveable consumption to minimize maximum hourly consumption. I’ve been trying to achieve this on and off for quite some time now, and I’ve gotten a few steps done. But now my (quite meagre) programming skills fall short of what I want to do.

I’ve managed to open a web socket, and connect to my smart meter, and I get valid response in the debug tab on the websocket.

The code I’ve use so far is this

$client = 34873; // the WS-client
$json = ‚{„type“:„connection_init“,„payload“:{„token“: „476c477d8a039529478ebd690d35ddd80e3308ffc49b59c65b142321aee963a4“}}‘;
WSC_SendMessage ($client, $json);

$query= ‚{„id“: „1“,„type“: „start“,„payload“: {„query“: „subscription{liveMeasurement(homeId:"c70dcbe5-4485-4821-933d-a8a86452737b"){timestamp power powerProduction accumulatedConsumption accumulatedCost}}“}}‘;
WSC_SendMessage ($client, $query);

The token and homeId are tibbers demo-tokens, not mine.

The response I get is:
05.08.2022, 15:35:48 | RECEIVED | {„type“:„connection_ack“}
05.08.2022, 15:35:51 | RECEIVED | {„type“:„data“,„id“:„1“,„payload“:{„data“:{„liveMeasurement“:{„timestamp“:„2022-08-05T15:35:52.500+02:00“,„power“:0,„powerProduction“:1386,„accumulatedConsumption“:15.955366,„accumulatedCost“:46.070754}}}}
05.08.2022, 15:35:54 | RECEIVED | {„type“:„data“,„id“:„1“,„payload“:{„data“:{„liveMeasurement“:{„timestamp“:„2022-08-05T15:35:55.000+02:00“,„power“:0,„powerProduction“:null,„accumulatedConsumption“:15.955366,„accumulatedCost“:46.070754}}}}
05.08.2022, 15:35:56 | RECEIVED | {„type“:„data“,„id“:„1“,„payload“:{„data“:{„liveMeasurement“:{„timestamp“:„2022-08-05T15:35:57.500+02:00“,„power“:0,„powerProduction“:null,„accumulatedConsumption“:15.955366,„accumulatedCost“:46.070754}}}}
etc.

What I’d like is to get access to the power powerProduction accumulatedConsumption accumulatedCost data, and get it into Symcon.

How can I do this?

You can connect a „Register Variable“ instance behind the WebSocket and it will receive all WebSocket messages. RegisterVariable — IP-Symcon :: Automation Software

parey

Thank you! An important step forward :slight_smile:

And I figured out that I had to empty the buffer at the end of the cycle. But I still don’t quite know how to get the variables :noob:

At the other (non subscription) request i get a json array(?) which I then parse (?) into current price etc, but when I try to adapt the subscription in the same way I get a blank.

This is an typical string I get in return:
{„type“:„data“,„id“:„1“,„payload“:{„data“:{„liveMeasurement“:{„timestamp“:„2022-08-05T16:44:10.000+02:00“,„power“:0,„powerProduction“:942,„accumulatedConsumption“:16.138631,„accumulatedCost“:46.609486}}}}

Which is in the $data variable from the example script in the documentation.

$accumul = $data->payload->data->liveMeasurment->accumulatedConsumption[0];
SetValue (13928, $accumul);

which then is blank. When I use
SetValue (13928, $data);

I get the whole data string in the variable.

Do you have any more pointers for me, please?

Andreas

1 „Gefällt mir“

I made it! The [0] wasn’t supposed to be there. Lots of trial, googeling and failing, before I achieved success. :sunglasses:

1 „Gefällt mir“

could you please write the solution ?

Hi, everybody!

I’ve gotten some questions as to how I did this, and I was planning to answer now, but the API at tibber has changed, and I jet again don’t know how to solve this. So my solution has stopped reporting the realtime, and accumulated consumption.

The change is the removed supprot for (the ended) graphql-ws protocol and the replacement with graphql-transport-ws protocol. I think I managed to get around that problem, by changing the protocol name, but there is now also a requirement to set user-agent header, which I don’t quite understand… As I’ve said before I have no background in programming, just what snippets I’ve managed to pick up for forums and stackexchange etc.

I’m in contact with tibber now on chat, and hopefully we can figure it out. :slight_smile:

On the other hand. This problem is only with the current consumption, accumulated hourly consupstion data, which reqiures an subscription-connection. For the price-data I have another soulution that works just fine. It’s a script which sets three variables according to the current pricedata.

This then sets the color on the aeotec ss6 in the living-room, and on a colorbulb in the downstairs bathroom, si we always can check the pricelevel.

<?php
$pris = 12345;   //variabel for pris
$nivaa = 23456;  //variabel for prisnivå, 1-5 og 0 for error
$dyr = 34567;    //boolsk variabel for dyr strøm, sann eller usann

$json = '{"query":"{viewer {homes {currentSubscription {priceInfo {current {total level energy tax startsAt }}}}}}"}';

# Create a connection
$ch = curl_init('https://api.tibber.com/v1-beta/gql');
# Setting our options
curl_setopt($ch, CURLOPT_URL, 'https://api.tibber.com/v1-beta/gql');
curl_setopt($ch, CURLOPT_HTTPHEADER, 
   array('Content-Type: application/json',  
   'Authorization: Bearer /*insert your token here*/')); // token
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

# Get the response
$response = curl_exec($ch);
curl_close($ch);

$data = json_decode($response);
$current = $data->data->viewer->homes[0];
$level = $current->currentSubscription->priceInfo->current->level;
$total = $current->currentSubscription->priceInfo->current->total;
$energy = $current->currentSubscription->priceInfo->current->energy;
$tax = $current->currentSubscription->priceInfo->current->tax;
SetValue ($pris, $total);
switch ($level) {
    case 'VERY_CHEAP':
        SetValue ($nivaa, 1);
        SetValue ($dyr, 0);
        break;
    case 'CHEAP':
        SetValue ($nivaa, 2);
        SetValue ($dyr, 0);
        break;
    case 'NORMAL':
        SetValue ($nivaa, 3);
        SetValue ($dyr, 0);
        break;
    case 'EXPENSIVE':
        SetValue ($nivaa, 4);
        SetValue ($dyr, 1);
        break;
    case 'VERY_EXPENSIVE':
        SetValue ($nivaa, 5);
        SetValue ($dyr, 1);
        break;
    default:
        SetValue ($nivaa, 0);
        SetValue ($dyr, 0);
}?>

@paresy
Do you know if the problem is the graphql-transport-ws subprotocol? Does IPS suppoort this?

I made a workaround after trying out the websockets in postman. As I recieve data and connection, the protocol was never the problem :slight_smile:

This is how I now get data (throug the workaround)

Create a WS client under i/o instances:

  • The url is (at this moment) wss://websocket-api.tibber.com/v1-beta/gql/subscriptions
  • In first header: „Sec-WebSocket-Protocol“ is „graphql-transport-ws“
  • The second header: „User-Agent“ is „IP-Symcon/6.3 com.tibber/1.8.3“

Then I run a script (openWS) to open and initialize the connection:

{
$client = 34873; //objectID for the WS client

$json = '{"type":"connection_init","payload":{"token":"<access token here>"}}';
WSC_SendMessage ($client, $json);
sleep (5);
$query= '{"id": "1","type": "subscribe","payload": {"query": "subscription{liveMeasurement(homeId:\"<HomeID here>\"){timestamp power powerProduction accumulatedConsumptionLastHour accumulatedCost}}"}}';
WSC_SendMessage ($client, $query);
}

This results in resonse from the tibber-server feeding the requested data every few seconds.

I then have a registervariable connected to the tibber Websocket, and the registervariable runs a script (WSforbruk) which looks like this:

<?php
//ønsker: forbruk nå, (produksjon nå), akkumulert forbruk

$usage_now = 37938;
$acc_h_usage = 18071;
// wenn das Skript von einer RegisterVariable-Instanz aus aufgerufen worden ist
if ($_IPS['SENDER'] == "RegisterVariable")
{
    // bereits im Puffer der Instanz vorhandene Daten in $data kopieren
    $data  = RegVar_GetBuffer($_IPS['INSTANCE']);
    // neu empfangene Daten an $data anhängen
    $data .= $_IPS['VALUE'];
 
    // wenn das Trennzeichen ; in $data gefunden worden ist
    if (strpos($data, ';'))
    {
        // $data in durch ; separierte Datensätze zerlegen
        $datasets = explode(';', $data);
 
        // alle nicht durch ; terminierten Datensätze ausgeben
        for ($i = 0; $i < count($datasets) - 1; $i++)
        {
            echo "empfangener Datensatz: ".$datasets[$i]."\n";
        }
         // $data auf den Inhalt des letzten (unvollständigen) Datensatzes setzen
        $data = $datasets[count($datasets) - 1];
    }

$data2 = json_decode($data);

$accumul = $data2->payload->data->liveMeasurement->accumulatedConsumptionLastHour;
$consumption = $data2->payload->data->liveMeasurement->power;
$ts = $data2->payload->data->liveMeasurement->timestamp;
$sale = $data2->payload->data->liveMeasurement->powerProduction;
SetValue ($acc_h_usage, $accumul);

if ($consumption > 0) {
    SetValue ($usage_now, $consumption);
} else {
    if ($sale !=null){
        SetValue ($usage_now, 0-$sale);
    }
};
RegVar_SetBuffer($_IPS['INSTANCE'], null);
};

I don’t quite understand all of this script, but it gives me my current usage og sale from the solar panels and the accumulated usage for the current hour put into the two variables defined at the top.

The problems I have now:

  • the WS-connection is a bit fragile. I have to wait for „‚connection_ack‘“ before I can reguest the data, or I’ll get an error message. Hence the sleep (5); which seem to work.
  • If I lose the connection with the tibber WS-server I have to wait for the WS to be „OPEN“ (in the message coloumn in debug) or I won’t get the connection_ack and I get no data. Prior to the change the made this winter I just checked if the data in „forbruk nå“-varable was more than 6 minutes old, and if it was, just reinitialize the connection. But now if I don’t time the reinitialization to the „opened“ message I dont get a new connection.

Is there a way to either monitor the „opened“ message and the run the connection inizialization, og through a script deactivate and reactivate the WS-client? Or to make an if 12345 is erroneous then (…)?

And of course: Is this an acceptable solution, or should it be reworked?

Where do you put this?

I get :Interface is an error state. Please check message log for more information.
in debug websocket client I get: ERROR permision denied

This is how my WebSocket is configured now. Tibber support-chat suggested the changes you see. Is this how your’s is configured?

No
I run ver 5.5, mine looks like this:

There was a change in the websocket config in 6.2 or 6.3, I think. I don’t think it’ll work woithout it…

Then i have upgraded to 6.3

But websocket connects and reconnects all the time. Do i need to get an personal „user-Agent“?
The two scripts are excecuted in a time-interval?

i get code:4408, Reason:Connection initialisation timeout

How do you get the registervariabel to run the script :see_no_evil:

Thats fron a debug on the websocket? You have to time the script to when it’s „opened“, then you’ll end up with the „connection_ack“ message, and the requst for the subscription can be sent.

I changed my openWS script to work around this issue:

// Sjekke om det er mer enn 6 min siden TibberWS er oppdatert
$tib_forbruk = 37938;
$ws=34873;

$time = date(IPS_GetVariable($tib_forbruk)['VariableUpdated']);
$time = time() - $time; // to get the time since that moment
if ($time > 360)

// slå av og på WS, slik at den kommer i "opened"
{
    sleep (2);
    IPS_SetProperty ($ws, 'Active', 0);
    IPS_ApplyChanges($ws);
    sleep (2);
    IPS_SetProperty ($ws, 'Active', 1);
    IPS_ApplyChanges($ws);
    sleep (2);
// autentisitere og starte subscription
    $json = '{"type":"connection_init","payload":{"token":"<insert your token here>"}}';
    WSC_SendMessage ($ws, $json);
    sleep (5);
    $query= '{"id": "1","type": "subscribe","payload": {"query": "subscription{liveMeasurement(homeId:\"<insert your homeId here>\"){timestamp power powerProduction accumulatedConsumptionLastHour accumulatedCost}}"}}';
    WSC_SendMessage ($ws, $query);
}

It might not be up to best practice in PHP (feel free to prettyfy, anybody), but I think it does the trick. Try it out, and please report back wheter it worked or not.

As this script checks wheter the data from tibber is quite recent, I think it’s safe to run it every 4 minutes, to check. If the data’s updated it just closes, right?

regards Andreas

Dubble tap the regvar, and choose the script as target. Sorry, I can’t screenshot right now.

Alright… at least i got something different now. I have tried several different commands but they all result in tis response:

Do you have a Tibber-dude i can ask?
I feel a bit stupid right now :grinning_face_with_smiling_eyes:

There seems to be something wrong with the query string. I think you missed the slashes in the query.

$query= '{"id": "1","type": "subscribe","payload": {"query": "subscription{liveMeasurement(homeId:\"<insert your homeId here>\"){timestamp power powerProduction accumulatedConsumptionLastHour accumulatedCost}}"}}';

I’ve had that stupid-feeling quite a few times :wink: Still ooften do, actually…

A