Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
# Changelog

## vag_vehicle 0.2.1

Show only a VIN's last four characters in log lines (`****1234`). Logs get pasted into public issues, and a full VIN reached srcfl/device-drivers#143 that way. Every log call goes through one masking function, so API errors that quote a URL with the VIN are masked too. The driver still uses the full VIN for API calls and as its serial.

## tesla_vehicle 0.2.4

Show only a VIN's last four characters in log lines (`****1234`). Logs get pasted into public issues, and a full VIN reached srcfl/device-drivers#143 that way. Every log call goes through one masking function, so API errors that quote a URL with the VIN are masked too. The driver still uses the full VIN for API calls and as its serial.

## tesla_cloud 0.1.1

Show only a VIN's last four characters in log lines (`****1234`). Logs get pasted into public issues, and a full VIN reached srcfl/device-drivers#143 that way. Every log call goes through one masking function, so API errors that quote a URL with the VIN are masked too. The driver still uses the full VIN for API calls and as its serial.

## teslamate_vehicle 0.1.1

Show only a VIN's last four characters in log lines (`****1234`). Logs get pasted into public issues, and a full VIN reached srcfl/device-drivers#143 that way. Every log call goes through one masking function, so API errors that quote a URL with the VIN are masked too. The driver still uses the full VIN for API calls and as its serial.

## myuplink 1.2.3

Record NIBE F750 as hardware-tested with the existing read-only MyUplink Cloud REST API v2 driver. OAuth connection and live telemetry were verified on an F750 in FTW; observed points included BT1 average outdoor temperature, BT12 condenser temperature and more than 100 additional heat-pump telemetry points. No control or write path is added.
Expand Down
16 changes: 8 additions & 8 deletions SUPPORT_STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,18 +158,18 @@ Catalog source is not proof that a target can install or run a driver.
| sonnen | 2.0.3 | blixt-l1 | not_assessed | — | not_recorded | not_assessed |
| sungrow | 1.5.10 | ftw-core | not_assessed | — | not_recorded | not_assessed |
| sungrow | 1.5.10 | blixt-l1 | not_assessed | — | not_recorded | not_assessed |
| tesla_cloud | 0.1.0 | ftw-core | not_assessed | — | not_recorded | not_assessed |
| tesla_cloud | 0.1.0 | blixt-l1 | not_assessed | — | not_recorded | not_assessed |
| tesla_vehicle | 0.2.3 | ftw-core | not_assessed | — | not_recorded | not_assessed |
| tesla_vehicle | 0.2.3 | blixt-l1 | not_assessed | — | not_recorded | not_assessed |
| tesla_cloud | 0.1.1 | ftw-core | not_assessed | — | not_recorded | not_assessed |
| tesla_cloud | 0.1.1 | blixt-l1 | not_assessed | — | not_recorded | not_assessed |
| tesla_vehicle | 0.2.4 | ftw-core | not_assessed | — | not_recorded | not_assessed |
| tesla_vehicle | 0.2.4 | blixt-l1 | not_assessed | — | not_recorded | not_assessed |
| tesla_wall_connector | 0.1.1 | ftw-core | not_assessed | — | not_recorded | not_assessed |
| tesla_wall_connector | 0.1.1 | blixt-l1 | not_assessed | — | not_recorded | not_assessed |
| teslamate_vehicle | 0.1.0 | ftw-core | not_assessed | — | not_recorded | not_assessed |
| teslamate_vehicle | 0.1.0 | blixt-l1 | not_assessed | — | not_recorded | not_assessed |
| teslamate_vehicle | 0.1.1 | ftw-core | not_assessed | — | not_recorded | not_assessed |
| teslamate_vehicle | 0.1.1 | blixt-l1 | not_assessed | — | not_recorded | not_assessed |
| tibber | 1.1.2 | ftw-core | not_assessed | — | not_recorded | not_assessed |
| tibber | 1.1.2 | blixt-l1 | not_assessed | — | not_recorded | not_assessed |
| vag_vehicle | 0.2.0 | ftw-core | not_assessed | — | not_recorded | not_assessed |
| vag_vehicle | 0.2.0 | blixt-l1 | not_assessed | — | not_recorded | not_assessed |
| vag_vehicle | 0.2.1 | ftw-core | not_assessed | — | not_recorded | not_assessed |
| vag_vehicle | 0.2.1 | blixt-l1 | not_assessed | — | not_recorded | not_assessed |
| varta | 1.1.1 | ftw-core | not_assessed | — | not_recorded | not_assessed |
| varta | 1.1.1 | blixt-l1 | not_assessed | — | not_recorded | not_assessed |
| victron | 2.1.2 | ftw-core | not_assessed | — | not_recorded | not_assessed |
Expand Down
8 changes: 4 additions & 4 deletions devices.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1806,7 +1806,7 @@ manufacturers:
protocols:
- protocol: http
driver: "tesla_vehicle"
version: "0.2.3"
version: "0.2.4"
ders: [vehicle]
control: true
firmware_versions: ""
Expand All @@ -1817,7 +1817,7 @@ manufacturers:
protocols:
- protocol: http
driver: "tesla_cloud"
version: "0.1.0"
version: "0.1.1"
ders: [vehicle]
control: false
firmware_versions: ""
Expand All @@ -1828,7 +1828,7 @@ manufacturers:
protocols:
- protocol: mqtt
driver: "teslamate_vehicle"
version: "0.1.0"
version: "0.1.1"
ders: [vehicle]
control: false
firmware_versions: ""
Expand Down Expand Up @@ -2064,7 +2064,7 @@ manufacturers:
protocols:
- protocol: http
driver: "vag_vehicle"
version: "0.2.0"
version: "0.2.1"
ders: [vehicle]
control: false
firmware_versions: ""
Expand Down
47 changes: 29 additions & 18 deletions drivers/lua/tesla_cloud.lua
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ DRIVER = {
id = "tesla_cloud",
name = "Tesla Vehicle (Fleet API)",
manufacturer = "Tesla",
version = "0.1.0",
version = "0.1.1",
protocols = { "http" },
capabilities = { "vehicle" },
read_only = true,
Expand Down Expand Up @@ -123,6 +123,17 @@ local next_request_ms = 0
-- checked yet since start).
local was_online = nil

-- Logs get pasted into public issues, and a VIN identifies the owner's
-- car. Every log line therefore shows only the VIN's last four characters.
local function log(level, message)
message = tostring(message)
if type(vin) == "string" and #vin > 4 then
local escaped = vin:gsub("%p", "%%%0")
message = message:gsub(escaped, "****" .. vin:sub(-4))
end
host.log(level, message)
end

-- Last vendor observation. seen_ms is host.millis() of the last successful
-- charge_state read. Age is measured on that clock; Tesla's unix ms cannot
-- be compared to host.millis().
Expand Down Expand Up @@ -200,17 +211,17 @@ local function persist_refresh(new_token)
if not host.persist_secret then return end
local ok, perr = host.persist_secret("refresh_token", refresh_token)
if not ok then
host.log("warn", "tesla_cloud: could not persist rotated refresh_token: " .. tostring(perr))
log("warn", "tesla_cloud: could not persist rotated refresh_token: " .. tostring(perr))
end
end

local function fetch_token()
if not refresh_token or refresh_token == "" then
host.log("warn", "tesla_cloud: not connected — set refresh_token from a Tesla Fleet API auth-code exchange")
log("warn", "tesla_cloud: not connected — set refresh_token from a Tesla Fleet API auth-code exchange")
return false
end
if not client_id or client_id == "" then
host.log("error", "tesla_cloud: client_id required")
log("error", "tesla_cloud: client_id required")
return false
end
local body = "grant_type=refresh_token"
Expand All @@ -224,12 +235,12 @@ local function fetch_token()
Accept = "application/json",
})
if err then
host.log("error", "tesla_cloud: token refresh failed: " .. redact_http_err(err))
log("error", "tesla_cloud: token refresh failed: " .. redact_http_err(err))
return false
end
local data, derr = decode_json(resp)
if derr or type(data) ~= "table" or not data.access_token then
host.log("error", "tesla_cloud: no access_token in refresh response")
log("error", "tesla_cloud: no access_token in refresh response")
return false
end
access_token = data.access_token
Expand Down Expand Up @@ -401,7 +412,7 @@ function driver_init(config)
local region = tostring(config.region or "eu"):lower()
base_url = REGION_URL[region]
if not base_url then
host.log("warn", "tesla_cloud: unknown region " .. region .. ", using eu")
log("warn", "tesla_cloud: unknown region " .. region .. ", using eu")
region = "eu"
base_url = REGION_URL.eu
end
Expand All @@ -412,9 +423,9 @@ function driver_init(config)
-- First poll soon; driver_poll sets POLL_MS on every path after that.
host.set_poll_interval(500)
if not client_id or client_id == "" or not refresh_token or refresh_token == "" then
host.log("error", "tesla_cloud: client_id and refresh_token required (Fleet API auth-code exchange)")
log("error", "tesla_cloud: client_id and refresh_token required (Fleet API auth-code exchange)")
end
host.log("info", "tesla_cloud: init region=" .. region ..
log("info", "tesla_cloud: init region=" .. region ..
" vin=" .. tostring(vin or "(discover)") ..
" telemetry-only")
end
Expand Down Expand Up @@ -443,15 +454,15 @@ function driver_poll()
end
end
if err or type(row) ~= "table" then
host.log("warn", "tesla_cloud: vehicle status: " .. redact_http_err(err))
log("warn", "tesla_cloud: vehicle status: " .. redact_http_err(err))
wait(ERROR_RETRY_MS)
emit_vehicle(false)
return POLL_MS
end

bind_identity(row.vin)
if not vin or vin == "" then
host.log("warn", "tesla_cloud: no VIN on account")
log("warn", "tesla_cloud: no VIN on account")
wait(ERROR_RETRY_MS)
return POLL_MS
end
Expand All @@ -461,7 +472,7 @@ function driver_poll()
local woke_just_now = online and was_online == false
was_online = online
if not online then
host.log("debug", "tesla_cloud: " .. vin .. " is " .. (state ~= "" and state or "unknown") ..
log("debug", "tesla_cloud: " .. vin .. " is " .. (state ~= "" and state or "unknown") ..
" — not calling vehicle_data")
wait(ASLEEP_EVERY_MS)
emit_vehicle(false)
Expand All @@ -470,7 +481,7 @@ function driver_poll()
if woke_just_now then
-- The car wakes briefly on its own. A live call now would keep it
-- awake; if it is still online at the next check, it is in use.
host.log("debug", "tesla_cloud: " .. vin .. " just woke — reading at the next check")
log("debug", "tesla_cloud: " .. vin .. " just woke — reading at the next check")
wait(ASLEEP_EVERY_MS)
emit_vehicle(false)
return POLL_MS
Expand All @@ -486,20 +497,20 @@ function driver_poll()
if ferr then
local es = tostring(ferr)
if es:match("HTTP 408") or es:match("vehicle unavailable") then
host.log("debug", "tesla_cloud: vehicle_data unavailable (asleep)")
log("debug", "tesla_cloud: vehicle_data unavailable (asleep)")
wait(ASLEEP_EVERY_MS)
elseif es:match("HTTP 429") then
host.log("warn", "tesla_cloud: rate limited")
log("warn", "tesla_cloud: rate limited")
wait(BACKOFF_MS)
else
host.log("warn", "tesla_cloud: vehicle_data: " .. redact_http_err(ferr))
log("warn", "tesla_cloud: vehicle_data: " .. redact_http_err(ferr))
wait(ERROR_RETRY_MS)
end
emit_vehicle(false)
return POLL_MS
end
if type(cs) ~= "table" or not remember(cs) then
host.log("debug", "tesla_cloud: no battery_level in charge_state")
log("debug", "tesla_cloud: no battery_level in charge_state")
wait(IDLE_EVERY_MS)
emit_vehicle(false)
return POLL_MS
Expand All @@ -510,7 +521,7 @@ function driver_poll()
else
wait(IDLE_EVERY_MS)
end
host.log("info", "tesla_cloud: emit soc=" .. tostring(last.soc) ..
log("info", "tesla_cloud: emit soc=" .. tostring(last.soc) ..
" limit=" .. tostring(last.charge_limit) ..
" state=" .. tostring(last.charging_state))
emit_vehicle(true)
Expand Down
Loading
Loading