From 4ddf47c2a8922fd59780b1669cc6fa387bdf9f9b Mon Sep 17 00:00:00 2001 From: awilliams <645192+awilliams@users.noreply.github.com> Date: Sat, 21 Oct 2023 21:18:30 -0600 Subject: [PATCH] Change entity name to not include device name This seems to fix the new HA error of: > MQTT entity name starts with the device name in your config Also: The Homeassistant setup needs updating for newer version. This allows for a basic instance of HASS to be run via docker compose, and then for wifi-presence to send events to it via MQTT. Fixes #18 --- .gitignore | 1 + cmd/wifi-presence/main.go | 6 +-- internal/hass/messages.go | 14 +++---- internal/hass/mqtt.go | 2 +- internal/presence/daemon.go | 2 +- test/Makefile | 44 +++++++++++++++++++-- test/create-ha-account.bash | 76 +++++++++++++++++++++++++++++++++++++ test/docker-compose.yml | 4 +- test/ha.yaml | 5 +-- 9 files changed, 133 insertions(+), 21 deletions(-) create mode 100755 test/create-ha-account.bash diff --git a/.gitignore b/.gitignore index 0d4742e..c851e81 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ build/bin/* cmd/wifi-presence/wifi-presence notes.md Makefile +out/* diff --git a/cmd/wifi-presence/main.go b/cmd/wifi-presence/main.go index f126c1a..3108b7b 100644 --- a/cmd/wifi-presence/main.go +++ b/cmd/wifi-presence/main.go @@ -217,13 +217,13 @@ func run(ctx context.Context, appName string) error { statusCtx, statusCancel := context.WithTimeout(ctx, 2*time.Second) defer statusCancel() - if err := mqtt.StatusOnline(statusCtx); err != nil { + if err = mqtt.StatusOnline(statusCtx); err != nil { return err } defer func() { // Cannot use main context since it may have already been cancelled. - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - _ = mqtt.StatusOffline(ctx) + mqttCloseCtx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + _ = mqtt.StatusOffline(mqttCloseCtx) cancel() mqtt.Close() diff --git a/internal/hass/messages.go b/internal/hass/messages.go index 046c379..bf11747 100644 --- a/internal/hass/messages.go +++ b/internal/hass/messages.go @@ -13,8 +13,8 @@ type Configuration struct { // TrackConfig describes a single Wifi station/device to monitor for state changes. type TrackConfig struct { - Name string `json:"name"` MAC string `json:"mac"` + Name string `json:"name"` } // DeviceTracker is used to configure HomeAssistant to track a device. @@ -38,21 +38,21 @@ type DeviceTracker struct { // Device is part of the DeviceTracker configuration. type Device struct { Connections [][2]string `json:"connections"` // A list of connections of the device to the outside world as a list of tuples [connection_type, connection_identifier]. For example the MAC address of a network interface: 'connections': ['mac', '02:5b:26:a8:dc:12']. - Name string `json:"name,omitempty"` // The name of the device. - ViaDevice string `json:"via_device,omitempty"` // The name of the device. Manufacturer string `json:"manufacturer,omitempty"` // The manufacturer of the device. + Name string `json:"name,omitempty"` // The name of the device. + ViaDevice string `json:"via_device,omitempty"` // Identifier of a device that routes messages between this device and Home Assistant. Examples of such devices are hubs, or parent devices of a sub-device. This is used to show device topology in Home Assistant. } // Attrs are a device's attributes. type Attrs struct { - Name string `json:"name"` - MAC string `json:"mac_address"` - IsConnected bool `json:"is_connected"` APName string `json:"ap_name"` - SSID string `json:"ssid"` BSSID string `json:"bssid"` ConnectedAt *time.Time `json:"connected_at,omitempty"` ConnectedFor int `json:"connected_for,omitempty"` DisconnectedAt *time.Time `json:"disconnected_at,omitempty"` DisconnectedFor int `json:"disconnected_for,omitempty"` + IsConnected bool `json:"is_connected"` + MAC string `json:"mac_address"` + Name string `json:"name"` + SSID string `json:"ssid"` } diff --git a/internal/hass/mqtt.go b/internal/hass/mqtt.go index 42bb85e..5966120 100644 --- a/internal/hass/mqtt.go +++ b/internal/hass/mqtt.go @@ -186,7 +186,7 @@ func (m *MQTT) RegisterDeviceTracker(ctx context.Context, dsc Discovery) error { }, Icon: icon, JSONAttributesTopic: m.topics.DeviceJSONAttrs(dsc.MAC), - Name: fmt.Sprintf("%s %s", dsc.Name, m.apName), + Name: fmt.Sprintf("%s %s", dsc.MAC, m.apName), // This cannot start with 'dsc.Name' ObjectID: deviceID, PayloadAvailable: StatusOnline, PayloadNotAvailable: StatusOffline, diff --git a/internal/presence/daemon.go b/internal/presence/daemon.go index d2d9ae2..e7d2a77 100644 --- a/internal/presence/daemon.go +++ b/internal/presence/daemon.go @@ -201,7 +201,7 @@ func (s staChange) String() string { case staUpdated: return "updated" default: - return "?" + return fmt.Sprintf("?:%d", s) } } diff --git a/test/Makefile b/test/Makefile index 573b565..0f448e3 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,4 +1,42 @@ -.PHONY: +.PHONY: test test: - go test -race ../... - go test . ../internal/presence ../internal/hass -race -v -mqttAddr "tcp://localhost:1883" + go test \ + -race \ + ../... + go test \ + . \ + ../internal/presence \ + ../internal/hass \ + -race \ + -v \ + -mqttAddr "tcp://localhost:1883" + +.PHONY: wifi-presence +wifi-presence: + go run ../cmd/wifi-presence \ + -apName="test-AP" \ + -debounce="2s" \ + -hass.autodiscovery=true \ + -mqtt.addr="tcp://localhost:1883" \ + -hostapd.socks="./hostapd.sock" \ + -verbose + +.PHONY: hostap-ctrl +hostap-ctrl: + go run ./hostap-ctrl + +.PHONY: send-cfg +send-cfg: + echo '{"devices":[{"name":"test-phone","mac":"BE:EF:00:00:FA:CE"}]}' \ + | mosquitto_pub \ + -h 'localhost' \ + -t 'wifi-presence/config' \ + -l + +.PHONY: send-empty-cfg +send-empty-cfg: + echo '{"devices":[]}' \ + | mosquitto_pub \ + -h 'localhost' \ + -t 'wifi-presence/config' \ + -l diff --git a/test/create-ha-account.bash b/test/create-ha-account.bash new file mode 100755 index 0000000..e48b2fd --- /dev/null +++ b/test/create-ha-account.bash @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# +# This script configures a fresh instance of HASS running +# as part of the associated Docker Compose environment. + +set -e + +HAURL="http://localhost:8123" + +HANAME="admin" +HAUSER="${HANAME}" +HAPASSWORD="${HANAME}" + +# Create temporary directory which is deleted on exit. +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +TMPDIR=`mktemp -d -p "$DIR"` +function cleanup { + rm -rf "${TMPDIR}" +} +trap cleanup EXIT + +curl "${HAURL}/api/onboarding/users" \ + --silent \ + -H 'Content-Type: text/plain;charset=UTF-8' \ + --data-binary "{\"client_id\":\"${HAURL}\",\"name\":\"${HANAME}\",\"username\":\"${HAUSER}\",\"password\":\"${HAPASSWORD}\",\"language\":\"en\"}" \ + --output - \ + | jq -r '.auth_code' > "${TMPDIR}/auth_code" + +curl "${HAURL}/auth/token" \ + --silent \ + -X 'POST' \ + -F "client_id=${HAURL}" \ + -F "code=$(cat "${TMPDIR}/auth_code")" \ + -F 'grant_type=authorization_code' \ + --output - \ + | jq -r '.access_token' > "${TMPDIR}/access_token" + +curl "${HAURL}/api/onboarding/core_config" \ + --silent \ + -X 'POST' \ + -H "Authorization: Bearer $(cat "${TMPDIR}/access_token")" \ + -o /dev/null + +curl "${HAURL}/api/onboarding/analytics" \ + --silent \ + -X 'POST' \ + -H "Authorization: Bearer $(cat "${TMPDIR}/access_token")" \ + -o /dev/null + +curl "${HAURL}/api/onboarding/integration" \ + --silent \ + -H 'Content-Type: application/json;charset=UTF-8' \ + -H "Authorization: Bearer $(cat "${TMPDIR}/access_token")" \ + --data-binary "{\"client_id\":\"${HAURL}\",\"redirect_uri\":\"${HAURL}/?auth_callback=1\"}" \ + -o /dev/null + +# Configure MQTT integration. + +curl "${HAURL}/api/config/config_entries/flow" \ + --silent \ + -X 'POST' \ + -H 'Content-Type: application/json;charset=UTF-8' \ + -H "Authorization: Bearer $(cat "${TMPDIR}/access_token")" \ + --data-binary '{"handler":"mqtt","show_advanced_options":false}' \ + --output - \ + | jq -r '.flow_id' > "${TMPDIR}/flow_id" + +curl "${HAURL}/api/config/config_entries/flow/$(cat "${TMPDIR}/flow_id")" \ + --silent \ + -X 'POST' \ + -H 'Content-Type: application/json;charset=UTF-8' \ + -H "Authorization: Bearer $(cat "${TMPDIR}/access_token")" \ + --data-binary '{"broker":"mosquitto","port":1883}' \ + -o /dev/null + +echo "Created user '${HAUSER}'" diff --git a/test/docker-compose.yml b/test/docker-compose.yml index 5dec39b..71414a6 100644 --- a/test/docker-compose.yml +++ b/test/docker-compose.yml @@ -1,7 +1,7 @@ services: homeassistant: container_name: 'ha' - image: 'homeassistant/home-assistant:2022.2.9' + image: 'homeassistant/home-assistant:2023.10.4' volumes: - './ha.yaml:/config/configuration.yaml' ports: @@ -11,7 +11,7 @@ services: mosquitto: container_name: mosquitto - image: eclipse-mosquitto + image: eclipse-mosquitto:2.0.17 volumes: - './mosquitto.conf:/mosquitto/config/mosquitto.conf:ro' ports: diff --git a/test/ha.yaml b/test/ha.yaml index 8448750..84df852 100644 --- a/test/ha.yaml +++ b/test/ha.yaml @@ -5,11 +5,8 @@ homeassistant: name: Wifi Presence Test auth_providers: - type: trusted_networks + allow_bypass_login: true trusted_networks: - 172.0.0.0/8 - 192.0.0.0/8 - allow_bypass_login: true - type: homeassistant - -mqtt: - broker: "mosquitto"