Volvo API playground
- 34 Devlogs
- 119 Total hours
This projects aims to provide dynamic alternative to official volvo API sandbox giving developers ways to test every possibility. /readme Backup: frog01.mikr.us:21185
This projects aims to provide dynamic alternative to official volvo API sandbox giving developers ways to test every possibility. /readme Backup: frog01.mikr.us:21185
Read till the end PLEASE 🥺
A lot of changes from the last devlog only 5 hrs
Mostly bc I add stuff on top of but I will suffer in the future
(See dev branch for these changes)
Change all connectivity API to start with /connected-vehicle/v2/ to make room for different APIs
I added a location API with a ONE endpoint
location:read If I remember correctly Fixed that operationID could be a constant
Added a config option to use longer API keys
About here I Fixed the url for location api because it used the connectivity scheme
Started working on energy API
Oauth2 update:
There consist of three parts header payload and signature
The two first is important for my project the 3 isn’t .
They store info about issuer, when token is active, scopes…
A lot of useless data for this project or useful? We will see
Bc of school I didn’t have a lot of time to write devlog (and write code) but I try to do my best before the end of stardance
The next ship is planned for 28 September
TODAY (16 September)
Im celebrating my birthday 😄
If you write me a few good words It would be motivating for me.
If you didn’t follow from start this branch contains the experimental scope support
which is mostly done but I can guarantee stability that’s why it isn’t pushed to main in fist ship
The school started aaand I didn’t have time I had low motivation
The only thing to do before first ship is docs but I could find time to finish them.
Buut I back now. AND I promise to ship it before 7 of September.
If you are voting on this project PLEASE do not skip.
I have my birthday coming and if the shipwrights doesn’t have a long list it could mean this project payout could be my birthday gift.
If I ofc ship it 😅
A long way from last devlog
I think I forgot to say something in last devlog
Untracked
I created a small modified volvo example app to show that my app works like the real one
\033[31m\033[0m- between errorsI started working on scopes.
What are they? They are really permissions (you can know them from discord bots or if you login thru GitHub etc.)
I don’t want to put them in this first ship but I needed to work on something (while I work on wiki) and I CAN’T guarantee every thing works after adding them.
You can see it in the scopes branch.
when I add everything to them I will finally try making a ship
Bonus photos from vacation on baltic sea
There was a small (or big) problem in my playground.
If an error was returned in the response, you could only see it if your app handled it or by printing the response.
That’s why I added an error logger.
It can show errors in two ways:
There were two ways of doing it (from what I learned):
I use two handlers:
@app.exception_handler(RequestValidationError)@app.exception_handler(HTTPException)It was a horrible time searching for why it didn’t work.
At first, I worked in a separate file, but I couldn’t get the (fastapi) app variable into those functions.
So I copied them into main file to make them work first.
I had to change some JSON responses to HTTPException to make the second handler work.
After I captured the first request and printed it, I worked on one function that writes a log and another that prints to the terminal. I also created a class to store useful data.
It works like this:
Exception handler->
->Put data into class ->
->Create response ->
->Return response
->Create a background task that logs the error
I create a background task so the response doesn’t wait or even worse fails
In the end I found that I can wrap these func with this simple func to make them work in different file
def setup_error_logging(app: FastAPI):
Added a config option:
I returned an exception instead of raising it.
I spent a good 15 minutes wondering why the response had status code 200 with a JSON body saying status code 400.
I also checked how the authorization errors look and added two new ones:
Authorization header.vcc-api-key header.I focus on making a GitHub wiki (new docs)
After ending them and the error logger.
Its time for the first ship !!!!!!!
I made a backup server : https://frog01-21185.wykr.es/
Its not ideal one, bc some asian countries are blocked but its shouldn’t have any downtime (I hope so)
If an Admin read it why I must copy enter from different text input 😭
inputText because they are really the same; the only difference is the HTML type.header is the same size (they weren’t).main branch.configparser library.docs.md into a GitHub Wiki with up to date information.I had some problems with catching the fastapi automatic response but I will leave it for next devlog
I forgot to add that I worked on the tests in the test branch, not the main one.
""), it was accepted as "".And I merged it into main.
Then I started cleaning up the Tailwind CSS.
NOTE: they are in new branch dashboard/cleanup
Why? Because I used the Play CDN in the past, so I couldn’t create utility classes in a CSS file.
Soooo I didn’t. Andddd there were a lot of Tailwind classes that didn’t fit on my screen.
Now that I started precompiling it, I could add them.
I added 5 unique utility classes. All of them combined are used around ~120 times across all HTMLs.
The longest one:
@utility inputSelection {
@apply w-full px-4 py-2.5 bg-zinc-950 border border-zinc-800 rounded-xl text-zinc-100 focus:outline-none focus:ring-2 focus:ring-blue-300 focus:border-transparent transition-all appearance-none text-center [text-align-last:center] cursor-pointer;
}
I also minified the CSS file. If I understand it, it removes anything the browser doesn’t need to understand the CSS (new lines, unnecessary spaces…). It’s better because those characters are still sent over the network, and fewer bytes mean less traffic.
While using the new utility classes, I tried to preserve the layout, so everything should look the same as before.
Working hard on pytest tests.
I learn how assert works and how to name .
I never used or created test in any language so I think they aren’t perfect (not like they should),but they work😄.
I looked like AI made few and how other people and read some stuff online
Around 25 of them
The tests helped me find and clean up several issues:
../horn but should be /honk.odometer, fuel) are int, just like in the OpenAPI specification, not as described in the official docs.oillevel to oilLevelcoolantLever to coolantLevel
I need to test engine and climate endpoints but I don’t have an access to a car with it. Soooo if you can contact me on slack
Devlog
Tracking headers implemented :
There are two
vcc-api-operationId - UUID
traceparent - W3C traceparent (for now only sends it back)
dashboard should look good on small screen (its very long but I don’t want to redesign it now)
This is the reason why I there is much time in this devlog. It was a lot of try and error before something looked good both on mobile and desktop and I liked it . But at least it works know 🙃.
some small cleanup of comments
I worked again on internal updates endpoint (hopefully works now)
started working on pytests (but I will talk about in next devlog (I gave my self 2 days for it)
I really want to get the first ship out, so it isn’t going to be perfect. I still have a lot of features and improvements I’d like to add.
I had a problem when I wanted to rewrite the scenario system. It only changed the local version of the Car (It means it really didn’t update anything).
I wanted to update a backup version first, and only if everything was correct, replace the original with the backup.
In the end, I used an already created function to first test and then apply to the car.
int.Then I think it’s finally ready for the first ship.
There are still a lot of things I want to add, but I can’t work indefinitely.
Content-Type fix enabled (like the official API).NOTE: I will add here more screenshots as link later
Also thinking about adding it frictionless mission but I’m not sure.
Content-Type fix from the official API).The Content-Type problem is SOLVED……
What was the problem? (My reading skills? ) there are two headers with application/json:
Content-Type (POST)Accept (GET)Accept header isn’t required ( can be */*, application/* or application/json).Content-Type mystery is mostly FIXED but I still need to check with a real Volvo car if Content-Type is actually required. (I’ll do it tomorrow.)Accept is now handled by my API like in the official API.Content-Type checks (like the docs say, meaning it’s required).PKCE is written as PCKE in one internal endpoint. (Not fixed yet.)I attached screenshots showing it (you can also see snapshot button but its not implemented YET)
Comparison app: https://compareplay.kls.hackclub.app/
I also updated the main app with the new dashboard
design: https://playground.kls.hackclub.app/internal/welcome
Remember to update your banner to feature your app (see #stardance on Slack). Otherwise, your ship will be rejected.
EDIT: A lot of bots try to connect to my apps do you think I should make a honeypot?
I found another way to hide the fuel stat when it isn’t useful for a particular type of car: the HTML hidden attribute.
At first, I thought I should add or remove the element hidden, but that turned out to be more complicated than I expected. I think using hidden is still a cleaner solution but I still using templates files.
While working on the dashboard, I wanted to add units like km, L, and % to some values. Instead of handling that in Python, I added another div with some margins and, in a few places, used flex so everything stays on the same line without adding more backend logic.
HX-Reswap and the HX-Retarget response header (when creating new car as proof of concept).(Mostly a checklist for myself 😅)
I wanted to add a video but MacOS made me a half gig and I don’t think stardance would handle
Most of my time was spent working on the dashboard with Tailwind CSS. It took a lot of experimenting to find a design I actually like.
I also changed how HTMX updates the dashboard through WebSockets.
Before:
Now:
That said, in the future it won’t always be just text. For example, if the fuel type changes from hybrid to electric, the fuel level should disappear from the dashboard instead of just changing its value.
While working on this, I found a bug caused by HTMX sending boolean values as strings. This caused climate settings to be marked as invalid. I need to distinguish between an invalid value and an invalid attribute instead of always treating the entire attribute as invalid.
Aaand that’s all. Making websites is painfully slow.
I don’t know if I mentioned it before, but for now the Content-Type checks are disabled. I need to learn more about them and implement better validation.
( The new OAuth2 settings page and the car selection screen. For next develop I think I will make a video of how everything works)
I said it was finished for now, but I forgot to add one more thing (the one on the left in the screenshots): a dashboard redirect.
Content-Type problem. I verified that the official Volvo API behaves correctly, so I’m working on matching that behavior.Content-Type checks.Let me know which design you like more: the new one (center) or the old one (right).
I’m mainly focused on finishing the companion app.
I started working on the CSS. Most of my time was spent learning Tailwind CSS (so I didn’t track it). I think it’s a better fit for me than writing plain CSS.
I think it done now but I will come back to it before first ship
While working on the companion app, I found another issue.
The Content-Type: application/json behavior doesn’t match the official API. I verified that the official API works as expected, checks for it mine doesn’t (I think)
Before pushing, I forgot to fix one thing:
{"error":"Failed to retrieve data from unofficial API.","detail":{"detail":[{"type":"dict_type","loc":["body"],"msg":"Input should be a valid dictionary","input":"{ \"runtimeMinutes\": 10}"}]}}
The fix was simple: I forgot to use json.loads().
I pushed a quick fix commit right after.
The photo shows the manual comparison page. I’m still working on the comparison logic, which is why it doesn’t have any CSS yet.
Sorry for not adding Volvo API keys so you can see the differences between both APIs, but that would have taken too much time today.
Its also the reason why Im creating this app
I should probably test everything before making a devlog or committing, but I also don’t want my devlogs to have to much time.
The project is finally available online!
https://playground.kls.hackclub.app/internal/welcome
Please don’t be the bad guy and fight with other people over access. ❤️
That’s all I wanted to say because this is a really big milestone for me.
There will also be another commit today to make sure everyone can run their own instance of the project.
I found a few issues that only appeared outside of my local environment:
Those should be fixed in today’s commit.
If you find any errors please comment or contact me on slack
Also thanks to nest hackclub for hosting
Authorization was my mistake. It should have been No it was good before I was sleepy I think when I did thataccessToken, not Authorization. Why I thought that? I have no idea.
The snapshots system now works.
Why it didn’t work as expected:
I forgot to use copy(deep=True).
One of the small dictionaries was written incorrectly.
Added saving and loading snapshots from a file.
The scenarios system now works and has been tested.
The main app is mostly ready. Before the first release, I still need to:
api_key_p was None because one endpoint redirected using api_key instead.PS: A photo of why the api_key_p was none
Writing documentation is a good way to check whether the code you wrote actually works. I already found four bugs while documenting the project.
For example, I forgot to pass the redirect URI in a function in main.py, even though it was already implemented in internal.py.
I also fixed a bug where notifications could sometimes contain an older value.
The dashboard has been moved into a separate file.
Snapshots currently have a problem. They are very similar to the Car class, so I’ll debug it and explain what the issue was in the next devlog.
The companion app is meant to showcase the differences between my implementation and the official Volvo Demo Car Sandbox. It also makes testing easier, since you don’t have to write your own application just to try the API.
I had a problem with the built-in LibreSSL because one of the dependencies didn’t support it. I had to reinstall Python so that uv would detect the new OpenSSL installation.
After that, I was finally able to start the app and get it working.
Auto-login and automatic creation of a test car are now working.
There’s is no css but I will do it after logic works
One more thing: the reason the devlogs have been quieter recently is that I’ve been dealing with some administrative paperwork. I’ll get back to posting them more regularly once that’s sorted out.
How I don’t like web development… did I already say that? I’ll say it again.
I’m working on the OAuth2 settings, and I had a problem where one of the query parameters wasn’t reaching the backend. I still don’t know what actually fixed it.
Then I had another problem. I have a function that changes values in the backend, but only one button worked while everything else didn’t.
It turned out to be because I debugged the first problem with AI. Always trust your own mind before trusting AI. The problem was hx-trigger.
I also had problems applying CSS through HTMX. In the end, I solved it by using hx-redirect.
But in the end everything works (you can see it one the phtoto)
I also worked on the scenario system, which loads scenarios from a JSON file. It isn’t tested yet, and I still need to work on state snapshots. The idea is that you’ll be able to save a moment in time and restore it later, so you can test the same scenario as many times as you want without setting up all the data again.
I also started working on a companion app to showcase the differences between my implementation and the official demo car sandbox.
I’ll write more about it in the next devlog.
A small error from the last devlog: it’s PKCE, not PCKE. I had to rename a lot of variables because of that.
{ "data": {} }.Working:
How does it work?
It normally protects from having the tokens stolen
OAuth2 without PKCE.
I started working on PKCE, but I thought I should write a devlog before adding it.
I’ll write more about it once it’s added to the project. In short, PKCE protects the authorization flow by ensuring that only the application that started the login process can exchange the authorization code for tokens.
The application first generates a random secret (called a code verifier). Before the login request is sent, a hashed version of that secret (the code challenge) is sent to the server. Later, when exchanging the authorization code for tokens, the application sends the original secret. The server verifies that it matches the previously received hash before issuing any tokens.
I’ll explain the complete flow in the next devlog.
This devlog will be a short one because I’ve been focusing on implementing OAuth 2.0. I’ll cover that in more detail in the next update but I will add photo of a “login” screen that I created for it (it looks bad on purpose).
main still contains the public API endpoints.internal contains the dashboard logic and internal API endpoints.database will contain multiple predefined car scenarios.statistics and warning. The statistics endpoint mostly returns data, so there isn’t much to test. The warning endpoint has a very large response that would require a lot of new code, and for now I don’t want to implement all of it.STOP commands now respond correctly. In the last invoice update, they incorrectly returned RUNNING.UNAVAILABLE reason invoice logic mostly something like this:
NO_INTERNET, the invoice becomes CONNECTION_FAILURE.POWER_SAVING_MODE, the invoice becomes VEHICLE_IN_SLEEP.A funny story: I built the invoice system but didn’t always assign values correctly, so the values got mixed up. I spent a long time debugging before realizing that was the cause.
{
"error": {
"message": "VALUE_ERROR",
"description": "\"Car\" object has no field \"123\""
}
}
I also spent a lot of time on CSS. Most of that time went into learning:
The reason it took so long since the last devlog is CSS. As I said before, I don’t really like web development, so my motivation was pretty low.
I used some AI, but I worked hard to remove most of the generated code. At this point, most of the site’s design looks the way I want it to and will probably stay that way until the end of the project.
Made invoices have some logic instead of always sending the same value.
That means:
COMPLETED or RUNNING depending on whether the engine or climate is running.RUNNING invoice status.PS: Always remember to stop the server with Ctrl+C, not Ctrl+Z, because Ctrl+Z can leave the process running and keep the port blocked.
It has been a long time since my last devlog, but I was more focused on learning than adding new stuff.
centralLock is called carLocked in the docs. The docs seem to be outdated. I think the OpenAPI specification is more accurate and contains fewer errors.datetime function from pythonI had a 2 days off because I was on school trip but I back. Next thing on my list is an updated dashboard
Devlog
You can edit data through the dashboard now. It’s not the best solution, and I want to change how it works (probably start from scratch), but not now.
Diagnostics endpoints.
I had a very good time with them today. Why? Because of the “/diagnostics” docs. I attached a screenshot showing an example from the official website and data from a real car (left: real API, right: docs).
If you look at them, you can see two errors and one very small issue that you would only notice if you read the whole documentation:
In serviceWarning, you can see the value is NORMAL, but NORMAL is not a valid value for it. It should be NO_WARNING. This is the small one.
In engineHoursToService, the API response contains a unit, but the docs don’t mention it at all.
And the worst one (and the only real error): there is no information about washerFluidLevelWarning in the docs, but it appears in the data from the car.
What do you think I should do? Should I write to them about it? I think you can comment on the post, so let me know what you think.
Devlog
Funny thing is that Volvo sends in what unit the data is, but the docs say that only metric units are valid, so it really sends always the same unit. Most likely they wanted to add imperial later, or they left it like this for simplicity.
For now, it’s using polling every 5 seconds (I have planned to change it to WebSocket). It only displays some information and can’t edit anything.
Also, I wrote some HTML and JavaScript. I never liked web development, so I don’t know a lot (I’m learning while I code 😅). It could be that it will look very rough till the end because I don’t have any patience for HTML and CSS.
I “will” do it when I have a very good day, or AI will do it? I would like not to use AI in this way, but I will see.
Edit: Forgot to push the updated version to GitHub I will do it tomorrow because it’s late (it’s tomorrow )
Devlog
It was also a frustrating day because I repeatedly forgot to save my code before testing (my bad).
A photo of the error that cost me way too much time today. (I changed it to “/vehicles/{VIN}/engine-status”, but I didn’t save the file.)
I setup my codespace and started working on first few endpoints. Additionally I looked how most of Volvos apis looks that gave me direction to go. That was all time I had today. Also I could add screenshot here because of macOS :(