Having a weather station is good, having a dashboard and a history of meteorological data is better. So, I decided to implement a web application.
The challenge was to see how fast the development could be using only the bundles and commands provided by the Symfony framework.
The project includes the following:
- A Dashboard to see the current weather data and some graphs to display the evolution over time.
- An API to allow the weather station to send data
- An admin area to manage the weather data
Here, the source code of the project.
The setup
Preparing everything to be ready to start implementing anything is always the most annoying part, at least for me.
Luckily I have a MariaDB and an Apache server running on my PC with, more or less, the same configuration as my "production" server, a Raspberry Pi 5 that I use to self-host my private cloud.
Thanks to this, I only had to create the database for the project and download the basic Symfony project using the amazing Symfony CLI tool.
$> symfony new weatherdashboard --webapp
Once done I installed the Symfony apache-pack for better integration of the project with the Apache web server:
$> composer require symfony/apache-pack
I also installed the Doctrine Fixtures bundle to easily create some initial data, like a default user for the admin area.
$> composer require orm-fixtures --dev
The last setup step was to configure the database connection in the Symfony .env file.
Everything was ready to start working on the project!
The Meteorological Data Admin
I wanted to manage the weather data received from the station. Why? Simply to be able to delete some weird data that can be received and also for testing purposes.
The first step was to create the meteorological data entity to store the weather information.
The MeteoData entity should have the following attributes:
- deviceTemperature: float
- temperature: float
- humidity: float
- relativeHumidity: float
- dewpoint: float
- lux: float
- pressure: float
Creating the entity was as easy as executing the following command:
$> bin/console make:entity
The entity is nothing if the database table it maps doesn't exist. Creating the table in the database is as simple as this:
$> bin/console make:migration
$> bin/console doctrine:migrations:migrate
The last step was to create all the code to manage the meteorological data with this "magic spell":
$> bin/console make:crud
Without typing any code, just with a few commands, I had the Meteorological Data Admin complete and ready to use.
Securing Access to the Meteorological Data Admin
An Admin area needs to be private, so I needed to secure access to the Meteorological Data Admin.
The first thing to do was to create the User entity:
$> bin/console make:user
Then, as before, I ran again the migration commands to create the table in the database.
To create the admin user I decided to use the Doctrine Fixtures bundle, a useful tool to insert default or initial needed data into the database.
To do this I had to implement the UserFixtures class manually to create the admin user. There is no command, or at least I don't know it, to "automagically" create a default admin user.
Once done, using the following command, the admin user was loaded into the database.
$> bin/console doctrine:fixtures:load --append --group=UserFixtures
To access the admin area, a login form is required. Creating it is really easy:
$> bin/console make:auth
The last step was manual again but simple. I just added the following to the "access_control" section of the "config/packages/security.yaml".
- { path: ^/meteo, roles: ROLE_USER }
The "meteo" path was secured while the dashboard on the home page remained public.
Creating the Dashboard HTML template
Everything in this Weather Station project was implemented without AI. But, as I'm not an expert in web design, I made an exception here and asked ChatGPT to create an HTML dashboard to display the current weather data.
With this static HTML, I was able to create the Twig base template of the project and the dashboard page.
Implementing the API endpoint
To connect the Weather Station with this web application, an API was required. To do this I installed the Symfony API Platform bundle.
$> composer require api-platform/core
The magic spell ends by adding this annotation in the MeteoData entity:
#[ApiResource]
What? That was all? Yes, that was all it took to have an API ready to receive data from the Weather Station. As simple as that.
Securing the Access to the API
This webapp and the weather station both run in my local home network. But I wanted to see how two different ways of authentication, user session and stateless JWT token, could coexist. Also to have some fun, of course :-)
I installed this bundle to secure the API using a JWT token.
$> composer require lexik/jwt-authentication-bundle
Then I generated the SSL keys required to create valid JWT tokens:
$> bin/console lexik:jwt:generate-keypair
A manual step was required to configure the station user. As the station is a machine and not a regular user I decided to use a simple approach creating a in-memory configuration.
api_user_provider:
memory:
users:
station: { password: '%env(METEO_PASSWORD)%', roles: [ 'ROLE_API' ] }
Now, I just needed to generate the JWT token for the station user to see if it worked:
bin/console lexik:jwt:generate-token station --user-class=Symfony\\Component\\Security\\Core\\User\\InMemoryUser
But then I got stuck... When trying to use the API it returned the following error:
ERROR 500: Session was used while the request was declared stateless.
I read the Symfony documentation about how to have two types of authentication in the same project, I googled it, I searched forums, I tried everything to avoid the use of AI.
My "spider sense" was warning me about what could be the problem, but I decided to ignore it. You know, sometimes you ignore this little voice in your head that is telling you the right thing. And then I gave in, I didn't want to, but I ended up asking the AI...
What was the AI response? Exactly what my intuition was telling me :-D. It was the order of the security firewalls configuration. I had first the user authentication, that requires a session, and then the API.
Changing the order and putting the API authentication first fixed the issue. Problem solved with a huge dumb face xD.
Conclusion
The purpose of a framework is to allow developers to focus on the real problem, the third party libraries or bundles exist to avoid reinventing the wheel.
This is not a surprise, of course, but in these times where AI seems the answer to developing fast, I wanted to prove to myself that it is possible to do it as fast as without AI.
And I think that I achieved the goal, in about 16 hours of work I was able to finish the Dashboard, the admin area and the API.
In the next article I will try to explain how I replicated the whole Weather Station System using AI.

Top comments (0)