Phev2mqtt — Full Install & Setup (Ubuntu Server)

Comprehensive step-by-step guide to install buxtronix/phev2mqtt on Ubuntu Server. Uses sudo nano <path> for file edits so you can paste and save easily.

Requirements

Raspberry PI, or NAS running Docker, or any machine with Linux and Wi-Fi Home Assistant & MQTT running (Can be installed on Home Assistant - this does npot include instructions to do this, but Google s your friend here.) You will need the mac address of your WiFi details when you have used the app to connect to your car. When connected, open your WiFi settings of your phone and write them down. Once Ubuntu is installed, you will also need the mac address of the WiFi card.

1. Download the Raspberry PI imager here
2. Select the correct PI you have from the main screen when you launch it. I chose "Other General Purpose OS" and then Ubuntu Server 25.10.
3. When configuring, make sure you select the option for SSH. And remeber the username and password you enter.
4. Boot uo the PI once finsished ensuring its connected to your network.
5. Download Putty to SSH into the PI here for the version of the machine you will use to SSH into the PI
6. Obtain the IP address of the PI (normally can be found on your routers web page).
7. Open putty and enter the IP to connect to, you will be asked your username and password from step 3.

Assumptions

You have Home Assistant installed - If not check it out https://www.home-assistant.io/ MQTT (Mosquitto broker) installed - Once Home Assistant is installed, go to Add Ons and install and configure it.

0 — Quick checklist before starting

Download the Raspberry PI imager here

1 — Install updates & base packages

Install required system packages.

sudo apt update
sudo apt upgrade

Install tools:

sudo apt install -y network-manager
If you plan to use netplan + systemd-networkd (recommended for headless servers), do not let NetworkManager manage the same interfaces. Pick one manager for each interface.

2 — Install Go & pcap

sudo apt install -y golang
sudo apt install -y libpcap-dev

3 — Clone and build phev2mqtt

Clone repo and build the binary.

git clone https://github.com/Roysteroonie/phev2mqtt.git

Move into the phev2mqtt directory.

cd phev2mqtt

Build it.

go build

Test its working.

phev2mqtt --help
The output should look like this:-
"See below for subcommands. For further information
on this tool, see https://github.com/Roysteroonie/phev2mqtt.

Usage:
phev2mqtt [command]
Available Commands:
client Client to connect and interact with the vehicle
completion generate the autocompletion script for the specified shell
decode Commands to decode Phev messages
emulator Emulate a car, to enable app testing
help Help about any command

Flags:
--config string config file (default is $HOME/.phev2mqtt.yaml)
-h, --help help for phev2mqtt
-s, --log_syslog plain logging to syslog instead of console
-t, --log_timestamps coloured logging with timestamps
-v, --verbosity string logging level to use (default "info")

Use "phev2mqtt [command] --help" for more information about a command."

Update the script for Phev2MQTT - Replace all details in white with your details.

Where there are speech marks, they should be included.
sudo nano /etc/systemd/system/phev2mqtt.service
[Unit]
Description=phev2mqtt service script
StartLimitIntervalSec=5
After=syslog.target network.target

[Service]
Type=exec
ExecStart=/usr/local/bin/phev2mqtt --config=/dev/null client mqtt --mqtt_server tcp://MQTT_SERVER_IP:1883/ --mqtt_username USERNAME --mqtt_password PASSWORD -v=debug

# Restart script if stopped
Restart=always
# Wait 30s before restart
RestartSec=30s

# Tag things in the log
# View with: sudo journalctl -f -u phev2mqtt -o cat
SyslogIdentifier=phev2mqtt

StandardOutput=syslog
StandardError=syslog

[Install]
WantedBy=multi-user.target

Install the binary system-wide:

sudo cp phev2mqtt /usr/local/bin/
sudo chmod 755 /usr/local/bin/phev2mqtt

Test connection to MQTT - Update the sections as per your configuration.

Change details between the speach marks
phev2mqtt client mqtt --mqtt_server tcp://MQTT_SERVER_IP:1883/ --mqtt_username USERNAME --mqtt_password PASSWORD

Cursor will drop to the line below if the connection works. Use CTRL+C to stop

4 — Network scripts

sudo nano /etc/systemd/network/00-default.link

Paste in the following (using your details)

[Match]
# This should be the 'real' (default) mac address of the Pi's wireless interface.
# To obtain this use "ip a" 
MACAddress=Your local mac address
	
[Link]
# This should be the MAC address to use to connect to the car, per above.
MACAddress=Your mac address from your mobile wifi for the car
NamePolicy=kernel database onboard slot path
If you want NetworkManager instead, change the netplan renderer to NetworkManager and use nmcli, but don't let both manage wlan0 simultaneously.

Paste in the following (using your details)

sudo nano /etc/wpa_supplicant/wpa_supplicant.conf
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=GB # Change this to suit your local

network={
	ssid="YOUR CARS SSID"
	scan_ssid=1
	psk="WIFI PASSWORD"
}

5 — Setup connectivity to the car

Wake the car AP if needed (app or lock/unlock) then disconnect it:

nmcli

Expected: wlan0: disconnected "Broadcom BCM43438 combo and Bluetooth Low Energy" wifi (brcmfmac), "should be your phones mac address", hw, mtu 1500 when ECU is awake.

Create the WiFi connection to the car:

sudo nmcli connection add \
type wifi \
ifname wlan0 \
con-name Your Car SSID \
ssid Your Car SSID

Modify the WiFi connection to the car including mac address

sudo nmcli connection modify CAR WIFI SSID \
wifi-sec.key-mgmt wpa-psk \
wifi-sec.psk CAR WIFI PASSWORD \
802-11-wireless.cloned-mac-address MAC ADRESS USED ABOVE

Bring the connection up

sudo nmcli connection up Your Car SSID

If its worked, run "ip a" in the window, you should see this kind of output.

3: wlan0: mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether "Your cloned mac address" brd ff:ff:ff:ff:ff:ff permaddr b8:27:eb:40:b5:64
inet 192.168.8.47/24 brd 192.168.8.255 scope global dynamic noprefixroute wlan0
valid_lft 86385sec preferred_lft 86385sec
inet6 fe80::f51c:8587:db3a:4143/64 scope link noprefixroute

At this point, give the PI a reboot

sudo reboot

6 — Register client with the car

You can run ip a to make sure the wlan0 is connected to the car showing UP:

phev2mqtt client register --address 192.168.8.46:8080 -v=debug

Successful registration shows logs ending with Success!. If it fails, ensure no other app is connected and the car is in registration mode.

level=debug msg="%PHEV_TCP_READER_CLOSE%"
level=error msg="Connection closed."
level=info msg="Success!"

7 — Set the phev2mqtt to auto start

sudo systemctl daemon-reload
sudo systemctl enable phev2mqtt.service
sudo systemctl start phev2mqtt.service

Lets check the logs:

sudo journalctl -u phev2mqtt.service -f

Example output:

Feb 02 17:02:29 phev2mqtt phev2mqtt[9863]: level=debug msg="%PHEV_TCP_SEND_MSG%: [57] PING REQ (id f)"
Feb 02 17:02:29 phev2mqtt phev2mqtt[9863]: level=debug msg="%PHEV_TCP_RECV_MSG%: [57] PING RESP (id f)"
Feb 02 17:02:30 phev2mqtt phev2mqtt[9863]: level=debug msg="%PHEV_TCP_SEND_MSG%: [57] PING REQ (id 10)"
Feb 02 17:02:30 phev2mqtt phev2mqtt[9863]: level=debug msg="%PHEV_TCP_RECV_MSG%: [57] PING RESP (id 10)"
Feb 02 17:02:30 phev2mqtt phev2mqtt[9863]: level=debug msg="%PHEV_TCP_SEND_MSG%: [57] PING REQ (id 11)"
Feb 02 17:02:30 phev2mqtt phev2mqtt[9863]: level=debug msg="%PHEV_TCP_RECV_MSG%: [57] PING RESP (id 11)"
Feb 02 17:02:31 phev2mqtt phev2mqtt[9863]: level=debug msg="%PHEV_TCP_SEND_MSG%: [57] PING REQ (id 12)"
Feb 02 17:02:31 phev2mqtt phev2mqtt[9863]: level=debug msg="%PHEV_TCP_RECV_MSG%: [57] PING RESP (id 12)"
Feb 02 17:02:31 phev2mqtt phev2mqtt[9863]: level=debug msg="%PHEV_TCP_SEND_MSG%: [57] PING REQ (id 13)"
Feb 02 17:02:31 phev2mqtt phev2mqtt[9863]: level=debug msg="%PHEV_TCP_RECV_MSG%: [57] PING RESP (id 13)"
Feb 02 17:02:32 phev2mqtt phev2mqtt[9863]: level=debug msg="%PHEV_TCP_SEND_MSG%: [57] PING REQ (id 14)"
Feb 02 17:02:32 phev2mqtt phev2mqtt[9863]: level=debug msg="%PHEV_TCP_RECV_MSG%: [57] PING RESP (id 14)"
Feb 02 17:02:33 phev2mqtt phev2mqtt[9863]: level=debug msg="%PHEV_TCP_SEND_MSG%: [57] PING REQ (id 15)"
Feb 02 17:02:33 phev2mqtt phev2mqtt[9863]: level=debug msg="%PHEV_TCP_RECV_MSG%: [57] PING RESP (id 15)"
Feb 02 17:02:33 phev2mqtt phev2mqtt[9863]: level=debug msg="%PHEV_TCP_SEND_MSG%: [57] PING REQ (id 16)"
Feb 02 17:02:33 phev2mqtt phev2mqtt[9863]: level=debug msg="%PHEV_TCP_RECV_MSG%: [57] PING RESP (id 16)"
Feb 02 17:02:34 phev2mqtt phev2mqtt[9863]: level=debug msg="%PHEV_TCP_SEND_MSG%: [57] PING REQ (id 17)"
Feb 02 17:02:34 phev2mqtt phev2mqtt[9863]: level=debug msg="%PHEV_TCP_RECV_MSG%: [57] PING RESP (id 17)"
Feb 02 17:02:35 phev2mqtt phev2mqtt[9863]: level=debug msg="%PHEV_TCP_SEND_MSG%: [57] PING REQ (id 18)"
Feb 02 17:02:35 phev2mqtt phev2mqtt[9863]: level=debug msg="%PHEV_TCP_RECV_MSG%: [57] PING RESP (id 18)"

If using mqtt explorer at this point you should see all the entries against phev.

On Home Assistant, go to Settings--> Devices & Services --> MQTT You should now see another devices named PHEV All done :o)

8 — Code for adding to a Dashboard

On Home Assistant, create or add to an existing dashboard. If New edit the dash, click the 3 dots top right and select Raw configuration editor, and paste the contents in replacing "YOUR CAR VIN" with yours. Then save

views:
  - title: Home
    cards:
      - type: horizontal-stack
        cards:
          - type: picture-elements
            elements:
              - type: state-icon
                entity: light.phev_"YOUR CAR VIN"_phev_park_lights
                style:
                  top: 95%
                  left: 30%
                icon: mdi:car-parking-lights
              - type: state-icon
                entity: light.phev_"YOUR CAR VIN"_phev_head_lights
                style:
                  top: 5%
                  left: 50%
                icon: mdi:car-light-high
              - type: state-icon
                entity: binary_sensor.phev_"YOUR CAR VIN"_phev_boot
                style:
                  top: 90%
                  left: 50%
                icon: mdi:car-connected
              - type: state-icon
                entity: binary_sensor.phev_"YOUR CAR VIN"_phev_bonnet
                style:
                  top: 17%
                  left: 50%
                icon: mdi:car
              - type: state-icon
                entity: binary_sensor.phev_"YOUR CAR VIN"_phev_front_passenger_door
                icon: mdi:car-door
                style:
                  top: 50%
                  left: 7%
              - type: state-icon
                entity: binary_sensor.phev_"YOUR CAR VIN"_phev_driver_door
                icon: mdi:car-door
                style:
                  top: 50%
                  left: 90%
              - type: state-icon
                entity: binary_sensor.phev_"YOUR CAR VIN"_phev_rear_left_door
                icon: mdi:car-door
                style:
                  top: 65%
                  left: 7%
              - type: state-icon
                entity: binary_sensor.phev_"YOUR CAR VIN"_phev_rear_right_door
                icon: mdi:car-door
                style:
                  top: 65%
                  left: 90%
              - type: state-icon
                entity: binary_sensor.phev_"YOUR CAR VIN"_phev_charger_connected
                style:
                  top: 80%
                  left: 90%
              - type: state-icon
                entity: binary_sensor.phev_"YOUR CAR VIN"_phev_locked
                style:
                  top: 60%
                  left: 50%
            image: https://ha.cactii.net/local/car-top-1.png
          - type: gauge
            tap_action:
              action: toggle
            entity: sensor.phev_"YOUR CAR VIN"_phev_battery
            unit: '%'
          - type: custom:button-card
            entity: binary_sensor.phev_"YOUR CAR VIN"_phev_charger_connected
            name: Charger
            aspect_ratio: 1/1.7
            tap_action:
              action: more-info
              entity: switch.phev_"YOUR CAR VIN"_phev_disable_charge_timer
            styles:
              grid:
                - position: relative
              custom_fields:
                notification:
                  - background-color: |
                      [[[
                        if (states['binary_sensor.phev_"YOUR CAR VIN"_phev_charging'].state == "on")
                          return "green";
                        return "red";
                      ]]]
                  - border-radius: 50%
                  - position: absolute
                  - left: 60%
                  - top: 10%
                  - height: 40px
                  - width: 40px
                  - font-size: 14px
                  - line-height: 40px
            custom_fields:
              notification: >
                [[[ return
                Math.floor(states['sensor.phev_"YOUR CAR VIN"_phev_charge_remaining'].state)
                +'m']]]
            state:
              - value: 'on'
                icon: mdi:power-plug
                label: Plugged In
              - value: 'off'
                icon: mdi:power-plug-off
                label: Unplugged
      - type: horizontal-stack
        cards:
          - show_name: true
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: switch.phev_"YOUR CAR VIN"_phev_cool
          - show_name: true
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: switch.phev_"YOUR CAR VIN"_phev_heat
          - show_name: true
            show_icon: true
            type: button
            tap_action:
              action: toggle
            entity: switch.phev_"YOUR CAR VIN"_phev_windscreen

If you find any mistakes or bad edits, please pop me a message and let me know, I