house-of-abbey / GarminHomeAssistant

Garmin application to provide a dashboard to control your Home Assistant
https://community.home-assistant.io/t/home-assistant-app-for-garmin/637348
MIT License
82 stars 12 forks source link

Transmit Battery: Authentication error #39

Closed teranex closed 8 months ago

teranex commented 8 months ago

I just updated the app on my venu 2 plus and enabled battery reporting. While watcing for the event in the HA developer tools, I didn't see any events appear. It occured to me however that I saw a few authentication errors:

Logger: homeassistant.components.http.ban
Source: components/http/ban.py:129
Integration: HTTP (documentation, issues)
First occurred: 14:37:30 (40 occurrences)
Last logged: 15:23:52

Login attempt or request with invalid authentication from localhost (127.0.0.1). Requested URL: '/api/events/garmin.battery_level'. (Mozilla/5.0 ( compatible ))

The rest of the app works without problems, including toggling of some of my lights and starting two scripts. My HA is exposed through Nabu Casa Cloud (which works without problems for the main functionality of the app, including serving the json-file).

Is there any additional step which needs to be done which might not be documented in https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/BatteryReporting.md ?

velaar commented 8 months ago

I have tested the battery reporting and it works fine in my configuration. Would suggest to try a fresh reinstall of the application. However I do not use the Nabu Casa.

philipabbey commented 8 months ago

@teranex,

The battery reporting function uses the same HTTP POST API request as the other API calls in the pre-existing app. Therefore if any are working, then all should be working.

The battery reporting has to fail silently or it will be annoying, just because your watch is out of range of your phone.

Thanks to @velaar for confirmation his installation is working, that's good to know given this is new functionality and I'm prepared for problems we've not yet encountered ourselves. I also appreciate the response "its working fine for us" is not so helpful!

Chatting with @JosephAbbey, we're guessing presently, but we're wondering what proxy services you have setup in your Home Assistant service. We're questioning (but uncertain) why your request came from localhost (127.0.0.1). I don't think we're expecting that, but presently we are unable to compare with the logs in our own installation. We can have a look at the weekend.

teranex commented 8 months ago

I reinstalled the app and added the configuration again, but I'm still seeing the same behaviour. Loading the menu.json works, toggling entities and tap-actions work. But battery reporting still gives the same error in the log. It is indeed weird that the request seems to come from 127.0.0.1, maybe it is something specific to how Nabu Casa cloud works (but then the other calls shouldn't work either if they use the same api request?

philipabbey commented 8 months ago

@teranex I don't think reinstalling and reconfiguring is going to help much. Einstein once said "Insanity is doing the same thing over and over and expecting different results." But then I suppose he had not used MS Windoze...

Anyway, we're back on decent IT now so can have a proper look. @JosephAbbey can we try to get the Garmin watch status reporting working on our free trial of Nabu Casa before it runs out?

philipabbey commented 8 months ago

@teranex, we can confirm that the battery level reporting works with Nabu Casa. We can even give you a script to check for yourself independent of the watch app.

send_battery.bash:

#!/bin/bash
#
#               battery% charging {0|1}
# ./send_battery.bash 19 0
#

API_KEY="<SECRET>"
URL=https://<Nabu Casa Account>.ui.nabu.casa/api

level=${1:-50}
is_charging=${2:-0}
echo "Battery Level = ${level}"
if [ ${is_charging} -eq 1 ]; then
  is_charging=true
else
  is_charging=false
fi
echo "Battery Charging? = ${is_charging}"
echo ""

curl -s -X POST \
  -H "Authorization: Bearer ${API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"level": '${level}', "is_charging": '${is_charging}',"device_id": "Bash Script"}' \
  ${URL}/events/garmin.battery_level \
  | jq .

You might want to omit the jq part if you don't have that installed, its just pretty prints the JSON response as seen below.

chmod +x send_battery.bash Execute this with:

pi@melrose:~/Code/home_assistant $ ./send_battery.bash 
Battery Level = 50
Battery Charging? = false

{
  "message": "Event garmin.battery_level fired."
}

Open your Home Assistant instance and show your event developer tools.

Our event logs for garmin.battery_level looks like this.

event_type: garmin.battery_level
data:
  level: 50
  is_charging: false
  device_id: Bash Script
origin: REMOTE
time_fired: "2024-01-06T19:19:57.693364+00:00"
context:
  id: 01HKG3FEFXMQTGMVMD9QSWX526
  parent_id: null
  user_id: 35e0e5a7e4bc49e9a328743697c58b90

If you create this script, then you can easily perform a debug on your events under your own control. The Bash script could also be a Windows batch file, something like the following UNTESTED code:

set API_KEY="<SECRET>"
set URL=https://<Nabu Casa Account>.ui.nabu.casa/api

curl -s -X POST ^
  -H "Authorization: Bearer %API_KEY%" ^
  -H "Content-Type: application/json" ^
  -d '{"level": 55, "is_charging": 0, "device_id": "Batch File"}' ^
  %URL%/events/garmin.battery_level

I'll leave you to figure that out more carefully if you prefer it.

philipabbey commented 8 months ago

Okay, based off the draft above, we've now created some additional troubleshooting guides at https://github.com/house-of-abbey/GarminHomeAssistant/blob/main/Troubleshooting.md#watch-battery-level-reporting. There's a working Windows batch file.

teranex commented 8 months ago

@philipabbey I found the problem and it has nothing to do with Nabu Casa Cloud :)

When I run the script (I merged them together) from my local Linux I see that the API is running, but the second call fails:

❯ ./api-test.sh
{"message":"API running."}
Battery Level = 50
Battery Charging? = false

401: Unauthorized%    

But then it struck me the API key I use belongs to a user on my HA which does not have admin rights. After I changed the user to be an administrator:

❯ ./api-test.sh
{"message":"API running."}Battery Level = 50
Battery Charging? = false

{"message":"Event garmin.battery_level fired."}%                      

So it seems only adminstrator users are allowed to do POST-calls to the /event endpoint

philipabbey commented 8 months ago

@teranex> So it seems only adminstrator users are allowed to do POST-calls to the /event endpoint

Youch! That's great feedback. Thank you. We'll consider the consequences of this.

teranex commented 8 months ago

Just to make sure, I also tested the script with my local url, http://homeassistant.local:8123/api, and I see the exact same behaviour. Only works when the user is administrator

JosephAbbey commented 8 months ago

The above authentication error can be solved by using the webhook API which does not require authentication as admin. No changes on the server side are required.

The new request URL is:

https://<homeassistant>/api/webhook/<webhook-id>

NB. The webhook-id can be anything, it just needs to not collide with other ids so something unique like GarminHomeAssistant.

The body is:

{
  "type": "fire_event",
  "data": {
    "event_type": "garmin.battery_level",
    "event_data": {"level": 19, "is_charging": true, "device_id": "<device-id>"}
  }
}

This is a POST request still.

philipabbey commented 8 months ago

Struggling to get an event via the webhook. Are you sure that your JSON is correctly formed? See https://developers.home-assistant.io/docs/api/websocket#fire-an-event

philipabbey commented 8 months ago

Webhook ID best practices: https://www.home-assistant.io/docs/automation/trigger/#webhook-security

philipabbey commented 8 months ago

Okay, found this which matches your JSON: https://developers.home-assistant.io/docs/api/native-app-integration/sending-data/#fire-an-event

philipabbey commented 8 months ago

We've hit a road block, the plan didn't go quite as well as... planned. We do not have a working webhook implementation because the server needs a registered ID to start with. @JosephAbbey has played with get arounds and so far we have not come up with anything suitable, it all gets slightly complicated and user unfriendly. We are goinmg to have to say this function is for administrators only until we can find a better solution.