openhab / openhab-addons

Add-ons for openHAB
https://www.openhab.org/
Eclipse Public License 2.0
1.88k stars 3.59k forks source link

[windcentrale] State not updated due to breaking API changes #13625

Closed D-Byte closed 1 year ago

D-Byte commented 2 years ago

The windcentrale binding is not working any more because of an api change

The api url of windcentrale.nl has chanced from https://zep-api.windcentrale.nl/production/41/live to https://mijn.windcentrale.nl/api/v0/livedata?projects=WND-RZ . The windmill numbers chanced to an abbreviation, so De Ranke Zwaan = 41 changed to WND-RZ (WiND-RankeZwaan). Also there is an authorization header needed to get the data, This you get when you log in to https://mijn.windcentrale.nl. The received data: { "WND-RZ": { "power": "-8", "wind_direction": "Z", "year_production": "2080319", "rpm": "1.7", "total_runtime": "19530", "year_runtime": "6376.405556", "diameter": "0", "pulsating": "0", "power_percentage": "0", "power_per_share": "-1", "id": "41", "timestamp": "1667142330", "wind_power": "3" } }

If you leave the ?projects away (https://mijn.windcentrale.nl/api/v0/livedata) you get data of all the windmills { "WND-BR": { "power": "110", "wind_direction": "ZW", "year_production": "2027236", "rpm": "18.5", "total_runtime": "25925", "year_runtime": "-68070618.108889", "diameter": "24", "pulsating": "0", "power_percentage": "13", "power_per_share": "20", "id": "131", "timestamp": "1667142420", "wind_power": "4" }, "WND-RH": { "power": "-7", "wind_direction": "Z", "year_production": "2545504", "rpm": "0.1", "total_runtime": "101449", "year_runtime": "6958.595556", "diameter": "0", "pulsating": "0", "power_percentage": "0", "power_per_share": "-1", "id": "31", "timestamp": "1667142420", "wind_power": "3" }, "WND-WJ": { "power": "-11", "wind_direction": "Z", "year_production": "2392540", "rpm": "1.9", "total_runtime": "107577", "year_runtime": "6969.475556", "diameter": "0", "pulsating": "0", "power_percentage": "0", "power_per_share": "-1", "id": "51", "timestamp": "1667142420", "wind_power": "3" }, "WND-BZ": { "power": "-1", "wind_direction": "ZO", "year_production": "1115796", "rpm": "11.4", "total_runtime": "52110", "year_runtime": "6385.000000", "diameter": "0", "pulsating": "0", "power_percentage": "0", "power_per_share": "0", "id": "201", "timestamp": "1667142420", "wind_power": "2" }, "WND-VW": { "power": "106", "wind_direction": "ZW", "year_production": "2010808", "rpm": "18.0", "total_runtime": "115425", "year_runtime": "-389620446.915556", "diameter": "24", "pulsating": "0", "power_percentage": "13", "power_per_share": "20", "id": "141", "timestamp": "1667142420", "wind_power": "4" }, "WND-GG": { "power": "0", "wind_direction": "ZO", "year_production": "3181747", "rpm": "6.2", "total_runtime": "118698", "year_runtime": "6872.000000", "diameter": "0", "pulsating": "0", "power_percentage": "0", "power_per_share": "0", "id": "1", "timestamp": "1667142420", "wind_power": "2" }, "WND-TW": { "power": "109", "wind_direction": "ZW", "year_production": "2006148", "rpm": "18.2", "total_runtime": "113377", "year_runtime": "-382517214.204444", "diameter": "24", "pulsating": "0", "power_percentage": "13", "power_per_share": "20", "id": "121", "timestamp": "1667142420", "wind_power": "4" }, "WND-BH": { "power": "107", "wind_direction": "ZW", "year_production": "1963944", "rpm": "17.8", "total_runtime": "113549", "year_runtime": "-383872242.524444", "diameter": "24", "pulsating": "0", "power_percentage": "13", "power_per_share": "20", "id": "111", "timestamp": "1667142420", "wind_power": "4" }, "WND-VH": { "power": "3", "wind_direction": "Z", "year_production": "3492183", "rpm": "5.9", "total_runtime": "0", "year_runtime": "0", "diameter": "10", "pulsating": "0", "power_percentage": "1", "power_per_share": "1", "id": "211", "timestamp": "1667142420", "wind_power": "3" }, "WND-JH": { "power": "0", "wind_direction": "ZO", "year_production": "3356991", "rpm": "6.1", "total_runtime": "118885", "year_runtime": "6912.000000", "diameter": "0", "pulsating": "0", "power_percentage": "0", "power_per_share": "0", "id": "2", "timestamp": "1667142420", "wind_power": "2" }, "WND-RZ": { "power": "-8", "wind_direction": "Z", "year_production": "2080319", "rpm": "1.7", "total_runtime": "19530", "year_runtime": "6376.405556", "diameter": "0", "pulsating": "0", "power_percentage": "0", "power_per_share": "-1", "id": "41", "timestamp": "1667142420", "wind_power": "3" } }

There is also more data via https://mijn.windcentrale.nl/api/v0/sustainable/projects info about the user and the windmill https://mijn.windcentrale.nl/api/v0/sustainable/notices messages https://mijn.windcentrale.nl/api/v0/sustainable/production/WND-RZ?timeframe_type=WEEK4_WEEKS&timeframe_offset=0&view_type=TOTAL_PROJECT here you can get historical data https://mijn.windcentrale.nl/api/v0/weather weather data for windmill locations

Hopefully the binding developer can cook something up with this info

Running OpenHAB 3.3.0 Linux/4.18.0-348.2.1.el8_5.x86_64 (amd64), Java Runtime: Eclipse Adoptium 11.0.15 (Temurin-11.0.15+10)

wborn commented 2 years ago

I also noticed the API changed and AWS OAuth tokens are used for access. Do you still use the Windcentrale binding @marcelrv? If so, do you have plans to look into this?

marcelrv commented 2 years ago

Let me indeed take a look at this and see if I can figure the new logic out

marcelrv commented 2 years ago

FYI. Progressed little so far.

in python we can login / get token for windcentrale site with:

    def __get_tokens(self):
        boto3_client = boto3.client('cognito-idp', region_name='eu-west-1')
        aws = AWSSRP(username=' usename', password='password', pool_id='eu-west-1_U7eYBPrBd', client_id='715j3r0trk7o8dqg3md57il7q0', client=boto3_client)
        return aws.authenticate_user()

(values can be these values can be extracted from request to : https://mijn.windcentrale.nl/api/v0/labels/key?domain=mijn.windcentrale.nl )

but have not yet been successful is doing the same in java. Did find some pointer on how to here: https://stackoverflow.com/questions/67528443/cognito-srp-using-aws-java-sdk-v2-x

marcelrv commented 1 year ago

Tried the examples from stackoverflow, but so-far without success.

From the official library there seems there is no easy way. https://github.com/aws/aws-sdk-java-v2/issues/2851

marcelrv commented 1 year ago

As temporary workaround you could run below script that publishes the mill data to mqtt

https://github.com/marcelrv/windcentrale

copied below the initial version

#!/usr/bin/env python
# coding: utf-8

from warrant.aws_srp import AWSSRP
import boto3
import paho.mqtt.client as paho
import json
import time
from urllib.request import urlopen, Request

##########################################################
# Small script to get your mill data and post to mqtt for further processing
# 2022 Marcel Verpaalen
# 
#########################################################

# configuration
username = ""
password = ""
mqtt_server = "192.168.3.17"
mqtt_port = 1883

mqtt_topic = "/openhab/winddata"
molen = "WND-BR"  # None will give data for all mills. Alternatively find the code for your mill, e.g. WND-BR
refresh_interval = 30

############################################################
client_id = "715j3r0trk7o8dqg3md57il7q0"
user_pool_id = "eu-west-1_U7eYBPrBd"
region = "eu-west-1"

authentication_details = json.loads(
    urlopen("https://mijn.windcentrale.nl/api/v0/labels/key?domain=mijn.windcentrale.nl").read().decode("utf-8")
)
print("Logon details:")
print("client_id:    {}".format(authentication_details["client_id"]))
print("region:       {}".format(authentication_details["region"]))
print("user_pool_id: {}".format(authentication_details["user_pool_id"]))

def get_authorization():
    try:
        boto3_client = boto3.client("cognito-idp", region_name=region)
        aws = AWSSRP(
            username=username, password=password, pool_id=user_pool_id, client_id=client_id, client=boto3_client
        )
        tokens = aws.authenticate_user()
        #        print("tokens: ", tokens)
        token_type = tokens["AuthenticationResult"]["TokenType"]
        id_token = tokens["AuthenticationResult"]["IdToken"]
        authorization_header = {"Authorization": token_type + " " + id_token}
        return authorization_header
    except Exception as ex:
        print("invalid_user_credentails", ex)
        return None

url = "https://mijn.windcentrale.nl/api/v0/livedata"
if molen is not None:
    url += "?projects=" + molen

client = paho.Client("windcentrale")
client.connect(mqtt_server, mqtt_port)
authorization = get_authorization()

req = Request("https://mijn.windcentrale.nl/api/v0/sustainable/projects")
req.add_header("Authorization", authorization["Authorization"])
print("Mill data", urlopen(req).read().decode("utf-8"))

while True:
    try:
        if authorization is None:
            authorization = get_authorization()
        req = Request(url)
        req.add_header("Authorization", authorization["Authorization"])
        data = json.loads(urlopen(req).read().decode("utf-8"))
        #        print(data)
        client.publish(mqtt_topic, json.dumps(data, indent=4))
    except Exception as ex:
        print("Error while running", ex)
        authorization = None
    time.sleep(refresh_interval)
wborn commented 1 year ago

Did find some pointer on how to here: https://stackoverflow.com/questions/67528443/cognito-srp-using-aws-java-sdk-v2-x

I finally had a look at it this evening and was able to write some Java code which obtains a valid JWT token based on username/password. It was also possible to use the token with curl to query the API. :slightly_smiling_face: So it should be possible to use this for reworking the binding to use an account and query the API using tokens.

wborn commented 1 year ago

There is now #14770 to adapt the binding to use the new API. I've added some links to JARs that can be used for testing.