Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend the API for Grohe Blue Home #12

Closed
Sprocket02 opened this issue Jan 21, 2020 · 90 comments
Closed

Extend the API for Grohe Blue Home #12

Sprocket02 opened this issue Jan 21, 2020 · 90 comments

Comments

@Sprocket02
Copy link

Hello,

I have now bought a Grohe Home Blue and would like to integrate it into OpenHAB. The first step is to extend this API. I tried to read the network traffic between the Grohe Ondus app and the Grohe Blue Home. However, I was not able to do this because the connection is encrypted. Do you have a tip for me how to proceed? For example, if I could give you my access data to my Grohe account, you could look at the connection data yourself.

regards

Sebastian

@FlorianSW
Copy link
Owner

Yes, the connection data is encrypted, as it should be, so sniffing the functions from the network traffic is most likely not the way to go forward and will never work.

The way I started when doing the API for the GROHE Sense devices is by concentrating on the app and going through the de-compiled code of it. This is, however, only being reasonable, if you can also do the requests (like in cURL or Postman) and look at the result on the device (to see if it actually had an effect).

So, it would probably be possible to do that work if you give me your Grohe account data, however, I feel very uncomfortable asking or taking them from you, as it would allow me to control the device inside of your home. And generally, why should you trust your very private login credentials some stranger on the internet? :D

If I've some time, I'll probably take a look into the app to see what HTTP endpoint it actually talks to, provide some example requests and may come back to you to test them out.

However, please don't expect anything anytime soon, as I need to find the time to actually do that, and there's absolutely no guarantee, that this will work out at the end, as well.

What you, however, already can do is, to show me the model of the response for the appliances in your account. For that, please (if you still want to contribute here :P) obtain an access token as described here. Instead of taking the value of the refresh_token property, use the access_token one whenever I refer to "the token" later on here.

Secondly, use the HTTP request sending tool of your choice (I like to use postman for that) and do the following requests:

  1. GET https://idp2-apigw.cloud.grohe.com/v3/iot/locations
    The only header here is the "Authorization" header with the value "Bearer " (where "" needs to be replaces by the access_token (also remov the < and >).
    -> The response should contain a JSON object like:
[
    {
        "id": 100,
        "name": "Example",
        // ...
    }
]

Extract the value of id of the first element.
2. GET https://idp2-apigw.cloud.grohe.com/v3/iot/locations/100/rooms
Where 100 is the ID you retrieved/extracted from the first request. Also, include the very same Authorization header here.
-> The response should contain a JSON object like:

[
    {
        "id": 200,
        "name": "Example",
        "type": 0,
        "room_type": 1,
        "role": "owner"
    }
]

Extract the value id of the frist element.
3. GET https://idp2-apigw.cloud.grohe.com/v3/iot/locations/100/rooms/200/appliances
Where 100 is the ID of the first request, and 200 the ID of the second request.
-> Please post the whole response there. However, go through it and look, if there're any private data you DO NOT want to be publicly visible and replace them with example data of the same format (that's important!).

If you want to do that, thanks in advance. Please, if you've any problem while doing these steps, where I might be able to help, feel free to ask me :)

@Sprocket02
Copy link
Author

Sprocket02 commented Jan 22, 2020

Hello,

thanks for the great description. After the first step I get the following output:

  1. GET https://idp2-apigw.cloud.grohe.com/v3/iot/locations
[
    {
        "id": 119676,
        "name": "Mein Haus",
        "type": 2,
        "role": "owner",
        "timezone": "Europe/Berlin",
        "water_cost": -1,
        "energy_cost": -1,
        "heating_type": -1,
        "currency": "EUR",
        "default_water_cost": 0.004256,
        "default_energy_cost": 0.003977,
        "default_heating_type": 2,
        "emergency_shutdown_enable": true,
        "address": {
            "street": "",
            "city": "",
            "zipcode": "4xxxx",
            "housenumber": "",
            "country": "Deutschland",
            "country_code": "DE",
            "additionalInfo": ""
        }
    }
]
  1. GET https://idp2-apigw.cloud.grohe.com/v3/iot/locations/119676/rooms
[
    {
        "id": 118945,
        "name": "Küche",
        "type": 0,
        "room_type": 15,
        "role": "owner"
    }
]
  1. GET https://idp2-apigw.cloud.grohe.com/v3/iot/locations/119676/rooms/118945/appliances
[
    {
        "appliance_id": "ae9fd2e3-4285-48a0-9b8a-692745702d83",
        "installation_date": "2020-01-14T18:43:56.000+00:00",
        "name": "MEIN C-SPOUT",
        "serial_number": "305a31303031323931333242433830304339303030333031",
        "type": 104,
        "version": "01.00.Z10.0300.0104",
        "tdt": "2020-01-22T19:19:06.000+01:00",
        "timezone": 60,
        "config": {
            "co2_type": 1,
            "hose_length": 110,
            "co2_consumption_medium": 48,
            "co2_consumption_carbonated": 65,
            "guest_mode_active": false,
            "auto_flush_active": true,
            "flush_confirmed": false,
            "f_parameter": 7,
            "l_parameter": 0,
            "flow_rate_still": 20,
            "flow_rate_medium": 18,
            "flow_rate_carbonated": 16
        },
        "role": "owner",
        "registration_complete": true,
        "presharedkey": "8PRu8pXVuMEsykdtyCDb4C+6RVFP0AuUjsEJqXwNURU=",
        "params": {
            "water_hardness": 0,
            "carbon_hardness": 15,
            "filter_type": 1,
            "variant": 2,
            "auto_flush_reminder_notif": true,
            "consumables_low_notif": true,
            "product_information_notif": true
        },
        "error": {
            "errors_1": false,
            "errors_2": false,
            "errors_3": false,
            "errors_4": false,
            "errors_5": false,
            "errors_6": false,
            "errors_7": false,
            "errors_8": false,
            "errors_9": false,
            "errors_10": false,
            "errors_11": false,
            "errors_12": false,
            "errors_13": false,
            "errors_14": false,
            "errors_15": false,
            "errors_16": false,
            "error1_counter": 256,
            "error2_counter": 512,
            "error3_counter": 256,
            "error4_counter": 0,
            "error5_counter": 0,
            "error6_counter": 0,
            "error7_counter": 0,
            "error8_counter": 0,
            "error9_counter": 0,
            "error10_counter": 0,
            "error11_counter": 0,
            "error12_counter": 4096,
            "error13_counter": 0,
            "error14_counter": 0,
            "error15_counter": 0,
            "error16_counter": 0
        },
        "state": {
            "start_time": 1579644441,
            "APPLIANCE_SUCCESSFUL_CONFIGURED": false,
            "co2_empty": false,
            "co2_20l_reached": false,
            "filter_empty": false,
            "filter_20l_reached": false,
            "cleaning_mode_active": false,
            "cleaning_needed": false,
            "flush_confirmation_required": true,
            "System_error_bitfield": 0
        }
    }
]

regards

Sebastian

@FlorianSW
Copy link
Owner

Interesting, that should not be the case :/ You can, alternatively, use the following request to get the whole response (locations, rooms and appliances from one request):

GET https://idp2-apigw.cloud.grohe.com/v3/iot/dashboard

You also need to put the access_token as the Authorization header (please retrieve a new, fresh one).

@Sprocket02
Copy link
Author

It works now, see the last thread.

@Sprocket02
Copy link
Author

Sprocket02 commented Jan 22, 2020

GET https://idp2-apigw.cloud.grohe.com/v3/iot/dashboard

{
    "locations": [
        {
            "id": 119676,
            "name": "Mein Haus",
            "type": 2,
            "role": "owner",
            "timezone": "Europe/Berlin",
            "water_cost": -1,
            "energy_cost": -1,
            "heating_type": -1,
            "currency": "EUR",
            "default_water_cost": 0.004256,
            "default_energy_cost": 0.003977,
            "default_heating_type": 2,
            "emergency_shutdown_enable": true,
            "address": {
                "street": "",
                "city": "",
                "zipcode": "4xxxx",
                "housenumber": "",
                "country": "Deutschland",
                "country_code": "DE",
                "additionalInfo": ""
            },
            "rooms": [
                {
                    "id": 118945,
                    "name": "Küche",
                    "type": 0,
                    "room_type": 15,
                    "role": "owner",
                    "appliances": [
                        {
                            "appliance_id": "ae9fd2e3-4285-48a0-9b8a-692745702d83",
                            "installation_date": "2020-01-14T18:43:56.000+00:00",
                            "name": "MEIN C-SPOUT",
                            "serial_number": "XXX",
                            "type": 104,
                            "version": "01.00.Z10.0300.0104",
                            "tdt": "2020-01-22T19:19:06.000+01:00",
                            "timezone": 60,
                            "config": {
                                "co2_type": 1,
                                "hose_length": 110,
                                "co2_consumption_medium": 48,
                                "co2_consumption_carbonated": 65,
                                "guest_mode_active": false,
                                "auto_flush_active": true,
                                "flush_confirmed": false,
                                "f_parameter": 7,
                                "l_parameter": 0,
                                "flow_rate_still": 20,
                                "flow_rate_medium": 18,
                                "flow_rate_carbonated": 16
                            },
                            "role": "owner",
                            "registration_complete": true,
                            "presharedkey": "XXX",
                            "params": {
                                "water_hardness": 0,
                                "carbon_hardness": 15,
                                "filter_type": 1,
                                "variant": 2,
                                "auto_flush_reminder_notif": true,
                                "consumables_low_notif": true,
                                "product_information_notif": true
                            },
                            "error": {
                                "errors_1": false,
                                "errors_2": false,
                                "errors_3": false,
                                "errors_4": false,
                                "errors_5": false,
                                "errors_6": false,
                                "errors_7": false,
                                "errors_8": false,
                                "errors_9": false,
                                "errors_10": false,
                                "errors_11": false,
                                "errors_12": false,
                                "errors_13": false,
                                "errors_14": false,
                                "errors_15": false,
                                "errors_16": false,
                                "error1_counter": 256,
                                "error2_counter": 512,
                                "error3_counter": 256,
                                "error4_counter": 0,
                                "error5_counter": 0,
                                "error6_counter": 0,
                                "error7_counter": 0,
                                "error8_counter": 0,
                                "error9_counter": 0,
                                "error10_counter": 0,
                                "error11_counter": 0,
                                "error12_counter": 4096,
                                "error13_counter": 0,
                                "error14_counter": 0,
                                "error15_counter": 0,
                                "error16_counter": 0
                            },
                            "state": {
                                "start_time": 1579644441,
                                "APPLIANCE_SUCCESSFUL_CONFIGURED": false,
                                "co2_empty": false,
                                "co2_20l_reached": false,
                                "filter_empty": false,
                                "filter_20l_reached": false,
                                "cleaning_mode_active": false,
                                "cleaning_needed": false,
                                "flush_confirmation_required": true,
                                "System_error_bitfield": 0
                            },
                            "notifications": [],
                            "status": [
                                {
                                    "type": "update_available",
                                    "value": 0
                                },
                                {
                                    "type": "connection",
                                    "value": 1
                                }
                            ],
                            "data_latest": {
                                "measurement": {
                                    "open_close_cycles_carbonated": 92,
                                    "pump_running_time": 30,
                                    "timeoffset": 60,
                                    "water_running_time_carbonated": 27,
                                    "time_since_restart": 536887329,
                                    "remaining_co2": 55,
                                    "open_close_cycles_still": 114,
                                    "power_cut_count": 22,
                                    "time_since_last_withdrawal": 22,
                                    "date_of_co2_replacement": "2019-12-28T19:25:46.000+01:00",
                                    "operating_time": 171,
                                    "remaining_filter": 95,
                                    "date_of_filter_replacement": "2019-12-28T19:25:46.000+01:00",
                                    "date_of_cleaning": "2019-12-28T19:25:04.000+01:00",
                                    "cleaning_count": 0,
                                    "max_idle_time": 756,
                                    "pump_count": 156,
                                    "water_running_time_medium": 3,
                                    "timestamp": "2020-01-22T19:19:06.000+01:00",
                                    "water_running_time_still": 10,
                                    "filter_change_count": 0,
                                    "remaining_filter_liters": 756,
                                    "remaining_co2_liters": 31
                                }
                            }
                        }
                    ]
                }
            ],
            "emergency_contacts": []
        }
    ]
}

@FlorianSW
Copy link
Owner

Just out of curiosity: Did you obfuscate the value of the presharedkey field? If not, I would recommend to remove the BLUE from your account and set it up again. I'm not sure, if the value changes afterwards or for what it is used, however, the name indicates it's something you really should not post on the public internet...

I would also do the same, if I were you, for the serial number, that's nothing we need here to see if we can implement the stuff :)

Additionally:
Can you please do the following request as well?

GET https://idp2-apigw.cloud.grohe.com/v3/iot/locations/LOCATION_ID/rooms/ROOM_ID/appliances/APPLIANCE_ID/command
and post the result?

@Sprocket02
Copy link
Author

GET https://idp2-apigw.cloud.grohe.com/v3/iot/locations/LOCATION_ID/rooms/ROOM_ID/appliances/APPLIANCE_ID/command

{
    "appliance_id": "ae9fd2e3-4285-48a0-9b8a-692745702d83",
    "type": 104,
    "command": {
        "co2_status_reset": false,
        "tap_type": 0,
        "cleaning_mode": false,
        "filter_status_reset": false,
        "get_current_measurement": false,
        "tap_amount": 0,
        "factory_reset": false,
        "revoke_flush_confirmation": true,
        "exec_auto_flush": false
    },
    "commandb64": "IAAAAA==",
    "timestamp": "2020-01-18T09:41:46.984Z"
}

@FlorianSW
Copy link
Owner

Alright, that looks straight forward as of now. Now the question is: What are the relevant actions you can do in the app (which you also would like to have exposed in the API as well)? Would it be possible to show relevant screenshots of the app?

@Sprocket02
Copy link
Author

Yes, first I removed the system from the app and added it again:

GET https://idp2-apigw.cloud.grohe.com/v3/iot/dashboard

{
    "locations": [
        {
            "id": 119676,
            "name": "Mein Haus",
            "type": 2,
            "role": "owner",
            "timezone": "Europe/Berlin",
            "water_cost": -1,
            "energy_cost": -1,
            "heating_type": -1,
            "currency": "EUR",
            "default_water_cost": 0.004256,
            "default_energy_cost": 0.003977,
            "default_heating_type": 2,
            "emergency_shutdown_enable": true,
            "address": {
                "street": "",
                "city": "",
                "zipcode": "4xxxx",
                "housenumber": "",
                "country": "Deutschland",
                "country_code": "DE",
                "additionalInfo": ""
            },
            "rooms": [
                {
                    "id": 118945,
                    "name": "Küche",
                    "type": 0,
                    "room_type": 15,
                    "role": "owner",
                    "appliances": [
                        {
                            "appliance_id": "a5e45219-e859-47b6-a19f-1fc89ad8dd71",
                            "installation_date": "2020-01-22T19:10:58.000+00:00",
                            "name": "MEIN C-SPOUT",
                            "serial_number": "XXX",
                            "type": 104,
                            "version": "01.00.Z10.0300.0104",
                            "tdt": "2020-01-22T20:15:36.000+01:00",
                            "timezone": 60,
                            "config": {
                                "co2_type": 1,
                                "hose_length": 110,
                                "co2_consumption_medium": 48,
                                "co2_consumption_carbonated": 65,
                                "guest_mode_active": false,
                                "auto_flush_active": false,
                                "flush_confirmed": false,
                                "f_parameter": 7,
                                "l_parameter": 0,
                                "flow_rate_still": 20,
                                "flow_rate_medium": 18,
                                "flow_rate_carbonated": 16
                            },
                            "role": "owner",
                            "registration_complete": true,
                            "presharedkey": "IMfyn+nrj9tFQs1eGhB5YN2N1fzTikzZILyIMCCeGHo=",
                            "params": {
                                "water_hardness": 0,
                                "carbon_hardness": 15,
                                "filter_type": 1,
                                "variant": 2,
                                "auto_flush_reminder_notif": false,
                                "consumables_low_notif": true,
                                "product_information_notif": true
                            },
                            "error": {
                                "errors_1": false,
                                "errors_2": false,
                                "errors_3": false,
                                "errors_4": false,
                                "errors_5": false,
                                "errors_6": false,
                                "errors_7": false,
                                "errors_8": false,
                                "errors_9": false,
                                "errors_10": false,
                                "errors_11": false,
                                "errors_12": false,
                                "errors_13": false,
                                "errors_14": false,
                                "errors_15": false,
                                "errors_16": false,
                                "error1_counter": 256,
                                "error2_counter": 512,
                                "error3_counter": 256,
                                "error4_counter": 0,
                                "error5_counter": 0,
                                "error6_counter": 0,
                                "error7_counter": 0,
                                "error8_counter": 0,
                                "error9_counter": 0,
                                "error10_counter": 0,
                                "error11_counter": 0,
                                "error12_counter": 4096,
                                "error13_counter": 0,
                                "error14_counter": 0,
                                "error15_counter": 0,
                                "error16_counter": 0
                            },
                            "state": {
                                "start_time": 1579720514,
                                "APPLIANCE_SUCCESSFUL_CONFIGURED": false,
                                "co2_empty": false,
                                "co2_20l_reached": false,
                                "filter_empty": false,
                                "filter_20l_reached": false,
                                "cleaning_mode_active": false,
                                "cleaning_needed": false,
                                "flush_confirmation_required": true,
                                "System_error_bitfield": 0
                            },
                            "notifications": [],
                            "data_latest": {
                                "measurement": {
                                    "open_close_cycles_carbonated": 92,
                                    "pump_running_time": 30,
                                    "timeoffset": 60,
                                    "water_running_time_carbonated": 27,
                                    "time_since_restart": 536887329,
                                    "remaining_co2": 55,
                                    "open_close_cycles_still": 114,
                                    "power_cut_count": 22,
                                    "time_since_last_withdrawal": 78,
                                    "date_of_co2_replacement": "2019-12-28T19:25:46.000+01:00",
                                    "operating_time": 172,
                                    "remaining_filter": 95,
                                    "date_of_filter_replacement": "2019-12-28T19:25:46.000+01:00",
                                    "date_of_cleaning": "2019-12-28T19:25:04.000+01:00",
                                    "cleaning_count": 0,
                                    "max_idle_time": 756,
                                    "pump_count": 156,
                                    "water_running_time_medium": 3,
                                    "timestamp": "2020-01-22T20:15:23.000+01:00",
                                    "water_running_time_still": 10,
                                    "filter_change_count": 0,
                                    "remaining_filter_liters": 756,
                                    "remaining_co2_liters": 31
                                }
                            },
                            "status": [
                                {
                                    "type": "update_available",
                                    "value": 0
                                },
                                {
                                    "type": "connection",
                                    "value": 1
                                }
                            ]
                        }
                    ]
                }
            ],
            "emergency_contacts": []
        }
    ]
}

@Sprocket02
Copy link
Author

App Screenshots

@Sprocket02
Copy link
Author

I marked the values that are interesting from my point of view with -->.

{
    "locations": [
        {
            "id": 119676,
            "name": "Mein Haus",
            "type": 2,
            "role": "owner",
            "timezone": "Europe/Berlin",
            "water_cost": -1,
            "energy_cost": -1,
            "heating_type": -1,
            "currency": "EUR",
            "default_water_cost": 0.004256,
            "default_energy_cost": 0.003977,
            "default_heating_type": 2,
            "emergency_shutdown_enable": true,
            "address": {
                "street": "",
                "city": "",
                "zipcode": "4xxxx",
                "housenumber": "",
                "country": "Deutschland",
                "country_code": "DE",
                "additionalInfo": ""
            },
            "rooms": [
                {
                    "id": 118945,
                    "name": "Küche",
                    "type": 0,
                    "room_type": 15,
                    "role": "owner",
                    "appliances": [
                        {
                            "appliance_id": "a5e45219-e859-47b6-a19f-1fc89ad8dd71",
                            "installation_date": "2020-01-22T19:10:58.000+00:00",
                            "name": "MEIN C-SPOUT",
                            "serial_number": "XXX",
                            "type": 104,
                            "version": "01.00.Z10.0300.0104",
                            "tdt": "2020-01-22T20:15:36.000+01:00",
                            "timezone": 60,
                            "config": {
                                -->"co2_type": 1,
                                -->"hose_length": 110,
                                -->"co2_consumption_medium": 48,
                                -->"co2_consumption_carbonated": 65,
                                -->"guest_mode_active": false,
                                -->"auto_flush_active": false,
                                -->"flush_confirmed": false,
                                -->"f_parameter": 7,
                                -->"l_parameter": 0,
                                -->"flow_rate_still": 20,
                                -->"flow_rate_medium": 18,
                                -->"flow_rate_carbonated": 16
                            },
                            "role": "owner",
                            "registration_complete": true,
                            "presharedkey": "IMfyn+nrj9tFQs1eGhB5YN2N1fzTikzZILyIMCCeGHo=",
                            "params": {
                                -->"water_hardness": 0,
                                -->"carbon_hardness": 15,
                                -->"filter_type": 1,
                                -->"variant": 2,
                                -->"auto_flush_reminder_notif": false,
                                -->"consumables_low_notif": true,
                                -->"product_information_notif": true
                            },
                            "error": {
                                "errors_1": false,
                                "errors_2": false,
                                "errors_3": false,
                                "errors_4": false,
                                "errors_5": false,
                                "errors_6": false,
                                "errors_7": false,
                                "errors_8": false,
                                "errors_9": false,
                                "errors_10": false,
                                "errors_11": false,
                                "errors_12": false,
                                "errors_13": false,
                                "errors_14": false,
                                "errors_15": false,
                                "errors_16": false,
                                "error1_counter": 256,
                                "error2_counter": 512,
                                "error3_counter": 256,
                                "error4_counter": 0,
                                "error5_counter": 0,
                                "error6_counter": 0,
                                "error7_counter": 0,
                                "error8_counter": 0,
                                "error9_counter": 0,
                                "error10_counter": 0,
                                "error11_counter": 0,
                                "error12_counter": 4096,
                                "error13_counter": 0,
                                "error14_counter": 0,
                                "error15_counter": 0,
                                "error16_counter": 0
                            },
                            "state": {
                                "start_time": 1579720514,
                                "APPLIANCE_SUCCESSFUL_CONFIGURED": false,
                                -->"co2_empty": false,
                                -->"co2_20l_reached": false,
                                -->"filter_empty": false,
                                -->"filter_20l_reached": false,
                                -->"cleaning_mode_active": false,
                                -->"cleaning_needed": false,
                                -->"flush_confirmation_required": true,
                                -->"System_error_bitfield": 0
                            },
                            "notifications": [],
                            "data_latest": {
                                "measurement": {
                                    -->"open_close_cycles_carbonated": 92,
                                    -->"pump_running_time": 30,
                                    -->"timeoffset": 60,
                                    -->"water_running_time_carbonated": 27,
                                    -->"time_since_restart": 536887329,
                                    -->"remaining_co2": 55,
                                    -->"open_close_cycles_still": 114,
                                    -->"power_cut_count": 22,
                                    -->"time_since_last_withdrawal": 78,
                                    -->"date_of_co2_replacement": "2019-12-28T19:25:46.000+01:00",
                                    -->"operating_time": 172,
                                    -->"remaining_filter": 95,
                                    -->"date_of_filter_replacement": "2019-12-28T19:25:46.000+01:00",
                                    -->"date_of_cleaning": "2019-12-28T19:25:04.000+01:00",
                                    -->"cleaning_count": 0,
                                    -->"max_idle_time": 756,
                                    -->"pump_count": 156,
                                    -->"water_running_time_medium": 3,
                                    -->"timestamp": "2020-01-22T20:15:23.000+01:00",
                                    -->"water_running_time_still": 10,
                                    -->"filter_change_count": 0,
                                    -->"remaining_filter_liters": 756,
                                    -->"remaining_co2_liters": 31
                                }
                            },
                            "status": [
                                {
                                    "type": "update_available",
                                    "value": 0
                                },
                                {
                                    "type": "connection",
                                    "value": 1
                                }
                            ]
                        }
                    ]
                }
            ],
            "emergency_contacts": []
        }
    ]
}

@FlorianSW
Copy link
Owner

Ok, so: With the last commits I made today and the days before, there's a basic support for reading data from the grohe blue device, based on the API responses you gave here in the thread.

Next step would be to find out what actions in the app we can do with which API request. For that, I would ask you to do the following:

  1. Get the API response from both, the appliance and the appliance action curl requests, save them somewhere
  2. Do an action in the app, which you want to be able to do from openHAB as well.
  3. Get the API responses from step one again and save them somewhere else
  4. Make a diff of both responses you got from step 1 and 3. If you do not know how to do that, please give both responses in this thread (with a proper name, so I do not get confused :P).

That should help us find out, what we need to provide in this library to do actions :)

Thanks in advance!

@Sprocket02
Copy link
Author

Cool! I'll take care of it tomorrow evening.

@Sprocket02
Copy link
Author

I have one question regarding the first point:

appliance request is https://idp2-apigw.cloud.grohe.com/v3/iot/locations/idxxx/rooms/idyyy/appliances

But what exactly does the appliance action curl requests mean or what does it looks like?

@FlorianSW
Copy link
Owner

Oha, sorry, that's confusing, yes :D To be honest, I'm not entirely sure anymore, what I meant with the action API requests, too :P From looking at what I probably had in my mind, I can only think of the appliance command request, so https://idp2-apigw.cloud.grohe.com/v3/iot/locations/idxxx/rooms/idyyy/appliances/idzzz/command

@Sprocket02
Copy link
Author

I tried to create a list of values (with a corresponding description) that would be interesting for the OpenHAB integration.

Grohe Blue Home Specification.xlsx

@FlorianSW
Copy link
Owner

@Sprocket02 Ui, that's very detailed, thanks for providing this, that will make the implementation of the features much easier!

Please give me some time to find time to go through this list. I would also most likely ask you again to execute some requests on your side, to verify, that they work to change the values in the device, however, I would come back to you here in the thread then :)

Like I said, it could take some time, very busy days :(

@FlorianSW
Copy link
Owner

Ok, I added some more values, which can be read from the API. Next step is updating values. For that, can you please check, if you can execute the following API commands and give me a hint what the return values are?

First, get the current values of the appliance (as before) with:
GET https://idp2-apigw.cloud.grohe.com/v3/iot/locations/LOCATION_ID/rooms/ROOM_ID/appliances/APPLIANCE_ID

Then, change one of the params values, e.g. carbon_hardness, to another valid one. Then use this JSON string as a body for the next request:
PUT https://idp2-apigw.cloud.grohe.com/v3/iot/locations/LOCATION_ID/rooms/ROOM_ID/appliances/APPLIANCE_ID

If you then do the GET requet from above: Did the value (e.g. carbon_hardness) change? Could you also check the app?

Please repeat the same procedure, however, change one value of the config object, e.g. hose_length.

Then, let's check, if we can change the tap_typevalue. For that, do a GET request for the command first:
GET https://idp2-apigw.cloud.grohe.com/v3/iot/locations/LOCATION_ID/rooms/ROOM_ID/appliances/APPLIANCE_ID/command

Change the value of tap_type to another integer (out of the three valid values 1, 2 and 3). Then, try to change the value by using this changed JSON string as a body for the following PUT request:
PUT https://idp2-apigw.cloud.grohe.com/v3/iot/locations/LOCATION_ID/rooms/ROOM_ID/appliances/APPLIANCE_ID/command

Now, do the GET request again and check, if the value tap_type changed (also check the app please, if the value is represented there as well).

Of course, you need to retrieve a valid access_token from the API again, and put this one into the Authorization header of each of the above requests as well.

Thanks again for your help here, please give me a hint, if you need help here somewhere :)

To the hint in the excel with the App source code: Unfortunately, the app does not seem to reveal much about the Blue Home appliance at all, I couldn't find, e.g., the code which changes the values in the command endpoint. I could only find the values for the GROHE SENSE Guard. However, from looking at the API, the way of changing the values should work as on the other devices. The values itself are mostly something we need to guess, that was the same procedure in the other appliances as well. We don't have the apps source code, just the decompiled classes, and meaningful names are not really present there :)

@Sprocket02
Copy link
Author

Sorry, but how can I PUT the request in Postman? I can't edit the GET request directly.

@FlorianSW
Copy link
Owner

Next to the URL (left side) there is a drop down box, where you can select the method, like GET, POST, DELETE or PUT :)

@Sprocket02
Copy link
Author

Yes, but where can I put the parameters?. Postman PUT

@Sprocket02
Copy link
Author

I tested the PUT in the screenshot and now I can't send an GET request anymore.

{
    "message": "The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.\n\nThe Canonical String for this request should have been\n'GET\n/v3/locations/119676/rooms/118945/appliances\n\ncontent-type:application/json\nhost:7r0z8xr3fg.execute-api.eu-central-1.amazonaws.com\nx-amz-date:20200202T134519Z\nx-authorization:Bearer TOKEN\n\ncontent-type;host;x-amz-date;x-authorization\ne3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'\n\nThe String-to-Sign should have been\n'AWS4-HMAC-SHA256\n20200202T134519Z\n20200202/eu-central-1/execute-api/aws4_request\n1c8f1c31b2021a67f7fdae11021107856f9538878d5756dea3974aa8e5dee703'\n"
}

@FlorianSW
Copy link
Owner

Alright, you found the body for the PUT, however, you need to specify the applianceId after the /appliances/ part, so the request should look like:
/v3/locations/119676/rooms/118945/appliances/bacb...

I truncated the appliance ID, please use the value of the JSON :)

@Sprocket02
Copy link
Author

In your description, the applianceId is in front of the appliance. I don't understand it somehow.

/v3/locations/119676/rooms/118945/appliances/bacb...

And what do you mean by:

I truncated the appliance ID, please use the value of the JSON :)

@FlorianSW
Copy link
Owner

I mean, that you need to fill in the full UUID instead of the abbreviated one I used :) Look at the value of the appliance_id key in your JSON :)

@Sprocket02
Copy link
Author

Sorry for the slow start, but now I test the GET and the PUT command as you mentioned.

Unfortunately, the value (e.g. carbon_hardness) was not changed.

GET
PUT

@FlorianSW
Copy link
Owner

The param wasn't changed, as there was an error (please always provide the status code (top right side of the response body section) for better understanding.

In this case, one question: Did you strip (remove) parts from the body of the PUT request (which you retrieved from the GET request)? If so, this would most likely be the problem. A PUT request overwrites the representation of the object you PUT to as a whole, so it needs to contain all the required information. That's why you should do a GET request first, take the body of the response and use it as a body in the PUT request and only change values in it (e.g. carbon_hardness). If my assumption is correct, could you please try it again?

Also, could you please try the following requests as well and check, if they return values or what the status code is?

GET https://idp2-apigw.cloud.grohe.com/v3/iot/locations/LOCATION_ID/rooms/ROOM_ID/appliances/APPLIANCE_ID/config
GET https://idp2-apigw.cloud.grohe.com/v3/iot/locations/LOCATION_ID/rooms/ROOM_ID/appliances/APPLIANCE_ID/params

Thanks in advance!

@Sprocket02
Copy link
Author

Sprocket02 commented Feb 3, 2020

PUT

But when I send a GET request, the value of carbon_hardness is still 15.

@Sprocket02
Copy link
Author

Sprocket02 commented Feb 3, 2020

@FlorianSW
Copy link
Owner

Ok, next try: Could you issue the same PUT request again, but this time use POST instead?

@Sprocket02
Copy link
Author

Hi, how do I initialize the ApplianceCommand object?

@FlorianSW
Copy link
Owner

Oh damn... I see... I implemented a method to retrieve the current command (was there before already), which is OnduService#applianceCommand (this can also be used to "instantiate" such a command). But I did not provide a possibility to do certain actions on it :( That's something I need to add before you can actually try the commands part of the Blue, sorry for the inconvenience here :( I'm not sure, if I can get this done today or tomorrow, however, I let you know once you can check it out.

Sorry again for my stupidness here, I just forgot this part :D

@FlorianSW
Copy link
Owner

With the last commit I added a way to change the tap_type value of the Blue appliance. Could you please take a look? Thanks in advance! :)

@Sprocket02
Copy link
Author

Sprocket02 commented Mar 5, 2020

Doesn't that have to be the blue type?

Unbenannt

@FlorianSW
Copy link
Owner

Embarrassing :( You're absolutely right. It's fixed!

@Sprocket02
Copy link
Author

I'm not sure if the calls are all correct, but at this point the app crashes.

Unbenannt2

The command object is null.

Unbenannt

@FlorianSW
Copy link
Owner

Yeah, you do not need to create the ApplianceCommand by yourself. You should request the current ApplianceCommand from the API with OndusService#applianceCommand and then update this one. Maybe this code helps you on the basic ordering of the commands:
https://github.com/FlorianSW/grohe-ondus-api-java/blob/master/src/test/java/org/grohe/ondus/api/BlueIntegrationTest.java#L93-L101
:)

@Sprocket02
Copy link
Author

Sprocket02 commented Mar 6, 2020

Great, it works. The water runs like magic out of the tap according to the tap_type setting. But it never stops. I think we have to send out the setting of the tap_amount at the same time.

@FlorianSW
Copy link
Owner

Oha, ok, that's interesting, I asked myself already what this parameter does :D I'll add a way to update this setting as well. Does it make sense to change tap_type without setting tap_amount ? So, letting it flow indefinite?

Do you think that sending a 0 as a tap_type would turn off the flow of water? Would you be able to check that through postman?

@Sprocket02
Copy link
Author

The tap cannot be switched off with tab_type = 0. No, it make no sense to change tap_type without setting the tap_amount. The goal should be, e.g. telling Alexa to pour a liter of water into a carafe. So, letting it flow indefinite make no sense.

Unbenannt

@FlorianSW
Copy link
Owner

Ok, however, it's very hard for me to understand the unit behind the "amount" switch in the screenshot :/ Do you know what it means? Is it based on liters, is it based on time? What happens if you start when selecting 100? Is it on for 100 seconds?

@Sprocket02
Copy link
Author

That's milliliters. I set the type of water, then the amount and press start.

@FlorianSW
Copy link
Owner

Alright, thanks for the info :) Let me change the API :) I'll answer here, when I uploaded the change, so it would be great if you could take one more look than.

@FlorianSW
Copy link
Owner

Ok, I uploaded the change :) Can you please check again? You can see the changes in the test file I linked above :)

@Sprocket02
Copy link
Author

It works. It is the same as in the app! But I saw that my CO2 filter was also reset. Maybe the value co2_status_reset is true when the command is sent?

@FlorianSW
Copy link
Owner

Hmm, that's very unlikely, the command should contain all the information as they were before (that's why you should request the ApplianceCommand from the API). Could you check, what the current status is by evaluating the following code:

command.getCommand().isCo2StatusReset()

And let me know what the value is?

@Sprocket02
Copy link
Author

Yes, the value is true. Even if I make a get with Postman. No idea why the value always is true, but it must not be true when sending!

Unbenannt

@FlorianSW
Copy link
Owner

GET https://idp2-apigw.cloud.grohe.com/v3/iot/locations/LOCATION_ID/rooms/ROOM_ID/appliances/APPLIANCE_ID/command

{
    "appliance_id": "ae9fd2e3-4285-48a0-9b8a-692745702d83",
    "type": 104,
    "command": {
        "co2_status_reset": false,
        "tap_type": 0,
        "cleaning_mode": false,
        "filter_status_reset": false,
        "get_current_measurement": false,
        "tap_amount": 0,
        "factory_reset": false,
        "revoke_flush_confirmation": true,
        "exec_auto_flush": false
    },
    "commandb64": "IAAAAA==",
    "timestamp": "2020-01-18T09:41:46.984Z"
}

Hmm, here it was false, so maybe that is now set to true always because of the ApplianceCommand send :/ Weird...

The problem is, this needs to be done by the API client in this case, as the returned ApplianceCommand is supposed to have the current value set, so I can not just overwrite it. Could you test it out by calling the sendCommand again but with a changed command like this:

command.getCommand().setCo2StatusReset(false);
?

@Sprocket02
Copy link
Author

That works, now the value is false when I make a GET with Postman.

@FlorianSW
Copy link
Owner

What happens if you open the tap with the Java API again? Is the CO2 status reset true again?

@Sprocket02
Copy link
Author

No

@FlorianSW
Copy link
Owner

Hmm, strange, then I don't know why this reset was triggered in the first place, and I doubt that it has to do with the "open tap" through the Java API :/ I would keep it like it is now and if it happens more often, we certainly need to take a look, but as of now, I don't see how this could be related :(

Is there anything else missing then? Any feature for the ApplianceCommand or the config/params part?

@Sprocket02
Copy link
Author

No, I think everything is ready for the next step.

@FlorianSW
Copy link
Owner

@MortenVinding
Copy link

MortenVinding commented Jan 15, 2021

Hi @FlorianSW and @Sprocket02,

I have looked in to this since I recently acquired a Grohe Blue Home.
I'm not using OpenHAB and is not a Java programmer, but I'm fairly accustomed to shell scripting, so I have implemented the basic functionality in a shell script.

However I'm still missing two things:

  1. How do I refresh the OIDC access token? I have used oauth2 before, so I guess I just needs a URL to refresh the token using the refresh token?

  2. I can't change any parameters on the https://idp2-apigw.cloud.grohe.com/v3/iot/locations/LOCATION_ID/rooms/ROOM_ID/appliances/APPLIANCE_ID URL

I can get the config and status from it by sending a GET request, but if I resend the JSON (sans the brackets and .params.water_hardness) it is replying with a 404 response.

I can however send commands to the https://idp2-apigw.cloud.grohe.com/v3/iot/locations/LOCATION_ID/rooms/ROOM_ID/appliances/APPLIANCE_ID/command URL with a JSON like this:

{
  "command": {
    "co2_status_reset": false,
    "tap_type": 3,
    "cleaning_mode": false,
    "filter_status_reset": false,
    "get_current_measurement": true,
    "tap_amount": 150,
    "factory_reset": false,
    "revoke_flush_confirmation": false,
    "exec_auto_flush": false
  }
}

To get 150ml sparkling water.

Hope you have time to point me in the right direction 😊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants