1
0
mirror of https://github.com/home-assistant/core.git synced 2026-02-15 07:36:16 +00:00

Use API token authentiation in traccar_server (#155297)

This commit is contained in:
Jordan Harvey
2025-10-29 14:10:47 +00:00
committed by GitHub
parent dbfa0aa22c
commit dff8e5221b
9 changed files with 52 additions and 55 deletions

View File

@@ -7,5 +7,5 @@
"documentation": "https://www.home-assistant.io/integrations/traccar",
"iot_class": "cloud_push",
"loggers": ["pytraccar"],
"requirements": ["pytraccar==2.1.1", "stringcase==1.2.0"]
"requirements": ["pytraccar==3.0.0", "stringcase==1.2.0"]
}

View File

@@ -9,6 +9,7 @@ from pytraccar import ApiClient
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
CONF_API_TOKEN,
CONF_HOST,
CONF_PASSWORD,
CONF_PORT,
@@ -18,6 +19,7 @@ from homeassistant.const import (
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.aiohttp_client import async_create_clientsession
from homeassistant.helpers.event import async_track_time_interval
@@ -33,6 +35,11 @@ PLATFORMS: list[Platform] = [
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Traccar Server from a config entry."""
if CONF_API_TOKEN not in entry.data:
raise ConfigEntryAuthFailed(
translation_domain=DOMAIN,
translation_key="migrate_to_api_token",
)
client_session = async_create_clientsession(
hass,
cookie_jar=CookieJar(
@@ -46,8 +53,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
client_session=client_session,
host=entry.data[CONF_HOST],
port=entry.data[CONF_PORT],
username=entry.data[CONF_USERNAME],
password=entry.data[CONF_PASSWORD],
token=entry.data[CONF_API_TOKEN],
ssl=entry.data[CONF_SSL],
verify_ssl=entry.data[CONF_VERIFY_SSL],
),
@@ -90,3 +96,15 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
"""Handle an options update."""
await hass.config_entries.async_reload(entry.entry_id)
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Migrate old entry."""
if entry.version < 2:
# Version 2: Remove username and password, only keep API token
data = dict(entry.data)
data.pop(CONF_USERNAME, None)
data.pop(CONF_PASSWORD, None)
hass.config_entries.async_update_entry(entry, data=data, version=2)
return True

View File

@@ -16,11 +16,10 @@ import voluptuous as vol
from homeassistant import config_entries
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import (
CONF_API_TOKEN,
CONF_HOST,
CONF_PASSWORD,
CONF_PORT,
CONF_SSL,
CONF_USERNAME,
CONF_VERIFY_SSL,
)
from homeassistant.core import callback
@@ -61,10 +60,7 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
vol.Optional(CONF_PORT, default="8082"): TextSelector(
TextSelectorConfig(type=TextSelectorType.TEXT)
),
vol.Required(CONF_USERNAME): TextSelector(
TextSelectorConfig(type=TextSelectorType.EMAIL)
),
vol.Required(CONF_PASSWORD): TextSelector(
vol.Required(CONF_API_TOKEN): TextSelector(
TextSelectorConfig(type=TextSelectorType.PASSWORD)
),
vol.Optional(CONF_SSL, default=False): BooleanSelector(BooleanSelectorConfig()),
@@ -120,16 +116,17 @@ OPTIONS_FLOW = {
class TraccarServerConfigFlow(ConfigFlow, domain=DOMAIN):
"""Handle a config flow for Traccar Server."""
VERSION = 2
async def _get_server_info(self, user_input: dict[str, Any]) -> ServerModel:
"""Get server info."""
client = ApiClient(
client_session=async_get_clientsession(self.hass),
host=user_input[CONF_HOST],
port=user_input[CONF_PORT],
username=user_input[CONF_USERNAME],
password=user_input[CONF_PASSWORD],
ssl=user_input[CONF_SSL],
verify_ssl=user_input[CONF_VERIFY_SSL],
token=user_input[CONF_API_TOKEN],
)
return await client.get_server()
@@ -201,19 +198,11 @@ class TraccarServerConfigFlow(ConfigFlow, domain=DOMAIN):
reauth_entry,
data_updates=user_input,
)
username = (
user_input[CONF_USERNAME]
if user_input
else reauth_entry.data[CONF_USERNAME]
)
return self.async_show_form(
step_id="reauth_confirm",
data_schema=vol.Schema(
{
vol.Required(CONF_USERNAME, default=username): TextSelector(
TextSelectorConfig(type=TextSelectorType.EMAIL)
),
vol.Required(CONF_PASSWORD): TextSelector(
vol.Required(CONF_API_TOKEN): TextSelector(
TextSelectorConfig(type=TextSelectorType.PASSWORD)
),
}

View File

@@ -5,5 +5,5 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/traccar_server",
"iot_class": "local_push",
"requirements": ["pytraccar==2.1.1"]
"requirements": ["pytraccar==3.0.0"]
}

View File

@@ -12,23 +12,21 @@
"step": {
"reauth_confirm": {
"data": {
"password": "[%key:common::config_flow::data::password%]",
"username": "[%key:common::config_flow::data::username%]"
"api_token": "[%key:common::config_flow::data::api_token%]"
},
"description": "The authentication credentials for {host}:{port} need to be updated."
},
"user": {
"data": {
"api_token": "[%key:common::config_flow::data::api_token%]",
"host": "[%key:common::config_flow::data::host%]",
"password": "[%key:common::config_flow::data::password%]",
"port": "[%key:common::config_flow::data::port%]",
"ssl": "[%key:common::config_flow::data::ssl%]",
"username": "[%key:common::config_flow::data::username%]",
"verify_ssl": "[%key:common::config_flow::data::verify_ssl%]"
},
"data_description": {
"host": "The hostname or IP address of your Traccar Server",
"username": "The username (email) you use to log in to your Traccar Server"
"api_token": "The API token generated from your account on your Traccar Server",
"host": "The hostname or IP address of your Traccar Server"
}
}
}
@@ -62,6 +60,11 @@
}
}
},
"exceptions": {
"migrate_to_api_token": {
"message": "To continue using Traccar Server, you need to migrate to API token based authentication."
}
},
"options": {
"step": {
"init": {

2
requirements_all.txt generated
View File

@@ -2605,7 +2605,7 @@ pytouchlinesl==0.5.0
# homeassistant.components.traccar
# homeassistant.components.traccar_server
pytraccar==2.1.1
pytraccar==3.0.0
# homeassistant.components.tradfri
pytradfri[async]==9.0.1

View File

@@ -2166,7 +2166,7 @@ pytouchlinesl==0.5.0
# homeassistant.components.traccar
# homeassistant.components.traccar_server
pytraccar==2.1.1
pytraccar==3.0.0
# homeassistant.components.tradfri
pytradfri[async]==9.0.1

View File

@@ -14,11 +14,10 @@ from homeassistant.components.traccar_server.const import (
DOMAIN,
)
from homeassistant.const import (
CONF_API_TOKEN,
CONF_HOST,
CONF_PASSWORD,
CONF_PORT,
CONF_SSL,
CONF_USERNAME,
CONF_VERIFY_SSL,
)
@@ -74,8 +73,7 @@ def mock_config_entry() -> MockConfigEntry:
data={
CONF_HOST: "1.1.1.1",
CONF_PORT: "8082",
CONF_USERNAME: "test@example.org",
CONF_PASSWORD: "ThisIsNotThePasswordYouAreL00kingFor",
CONF_API_TOKEN: "ThisIsNotThePasswordYouAreL00kingFor",
CONF_SSL: False,
CONF_VERIFY_SSL: True,
},

View File

@@ -16,11 +16,10 @@ from homeassistant.components.traccar_server.const import (
)
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import (
CONF_API_TOKEN,
CONF_HOST,
CONF_PASSWORD,
CONF_PORT,
CONF_SSL,
CONF_USERNAME,
CONF_VERIFY_SSL,
)
from homeassistant.core import HomeAssistant
@@ -44,8 +43,7 @@ async def test_form(
result["flow_id"],
{
CONF_HOST: "1.1.1.1",
CONF_USERNAME: "test-username",
CONF_PASSWORD: "test-password",
CONF_API_TOKEN: "test-token",
},
)
await hass.async_block_till_done()
@@ -55,8 +53,7 @@ async def test_form(
assert result["data"] == {
CONF_HOST: "1.1.1.1",
CONF_PORT: "8082",
CONF_USERNAME: "test-username",
CONF_PASSWORD: "test-password",
CONF_API_TOKEN: "test-token",
CONF_SSL: False,
CONF_VERIFY_SSL: True,
}
@@ -87,8 +84,7 @@ async def test_form_cannot_connect(
result["flow_id"],
{
CONF_HOST: "1.1.1.1",
CONF_USERNAME: "test-username",
CONF_PASSWORD: "test-password",
CONF_API_TOKEN: "test-token",
},
)
@@ -101,8 +97,7 @@ async def test_form_cannot_connect(
result["flow_id"],
{
CONF_HOST: "1.1.1.1",
CONF_USERNAME: "test-username",
CONF_PASSWORD: "test-password",
CONF_API_TOKEN: "test-token",
},
)
await hass.async_block_till_done()
@@ -112,8 +107,7 @@ async def test_form_cannot_connect(
assert result["data"] == {
CONF_HOST: "1.1.1.1",
CONF_PORT: "8082",
CONF_USERNAME: "test-username",
CONF_PASSWORD: "test-password",
CONF_API_TOKEN: "test-token",
CONF_SSL: False,
CONF_VERIFY_SSL: True,
}
@@ -168,8 +162,7 @@ async def test_abort_already_configured(
{
CONF_HOST: "1.1.1.1",
CONF_PORT: "8082",
CONF_USERNAME: "test-username",
CONF_PASSWORD: "test-password",
CONF_API_TOKEN: "test-token",
},
)
@@ -201,8 +194,7 @@ async def test_reauth_flow(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_USERNAME: "new-username",
CONF_PASSWORD: "new-password",
CONF_API_TOKEN: "new-token",
},
)
await hass.async_block_till_done()
@@ -211,8 +203,7 @@ async def test_reauth_flow(
assert result["reason"] == "reauth_successful"
# Verify the config entry was updated
assert mock_config_entry.data[CONF_USERNAME] == "new-username"
assert mock_config_entry.data[CONF_PASSWORD] == "new-password"
assert mock_config_entry.data[CONF_API_TOKEN] == "new-token"
@pytest.mark.parametrize(
@@ -248,8 +239,7 @@ async def test_reauth_flow_errors(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_USERNAME: "new-username",
CONF_PASSWORD: "new-password",
CONF_API_TOKEN: "new-token",
},
)
@@ -262,8 +252,7 @@ async def test_reauth_flow_errors(
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
{
CONF_USERNAME: "new-username",
CONF_PASSWORD: "new-password",
CONF_API_TOKEN: "new-token",
},
)
await hass.async_block_till_done()