Home Posts Post Search Tag Search

Weather App 12 - Publishing Sensor Data 01
Published on: 2025-08-27 Tags: elixir, Generators , Blog, Side Project, Phoenix, Nerves, Weather App, Poncho

Chapter 4 Publishing Sensor Data (47)

Adding PostgreSQL to the Stack
    Starting off we need to create a new phoenix weather_tracker app we will use the command
        mix phx.new weather_tracker \
            --binary-id --no-webpack --no-html --no-gettext --no-dashboard

    With this being said you will need to have the right generator installed so if that doesn't work be sure to run this command within you linux console
        mix archive.install hex phx_new 1.5.8

    Once that is all setup we need to created the yml file and get into the proper location
        cd weather_tracker && touch docker-compose.yml

    Okay so now we can populate the file with
        version: '3.3'
            services:
                postgres:
                    image: timescale/timescaledb:2.1.0-pg13
                    ports:
                    - '5432:5432'
                    volumes:
                    - postgres-data:/var/lib/postgresql/data
                    environment:
                    POSTGRES_PASSWORD: postgres
                    POSTGRES_USER: postgres
            volumes:
                postgres-data: {}

    Starting the Docker Compose Stack
        docker-compose up

        With that we will have the docker container up and running we will need to do some work to it so leave it up if you need to close it you can just crtl+calculated

Creating the Phoenix Application
    We will need to run some migrations so that we can persist the data from the sensors
        mix ecto.gen.migration set_up_weather_data_table

    We can now setup the migrations for that data table with the following. It should have created a file with a time stamp and the file name appended to the time stamp.
        defmodule WeatherTracker.Repo.Migrations.SetUpWeatherDataTable do
            use Ecto.Migration

            def up do
                execute("CREATE EXTENSION IF NOT EXISTS timescaledb")

                create table(:weather_conditions, primary_key: false) do
                add :timestamp, :naive_datetime, null: false
                add :temperature_c, :decimal, null: false
                add :pressure_pa, :decimal, null: false
                add :humidity_rh, :decimal, null: false
                add :light_lumens, :decimal, null: false
                add :voc_index, :decimal, null: false
                add :uv_index, :decimal, null: false
                add :als_lux, :decimal, null: false
                end

                execute("SELECT create_hypertable('weather_conditions', 'timestamp')")
            end

            def down do
                drop table(:weather_conditions)
                execute("DROP EXTENSION IF EXISTS timescaledb")
            end
        end

    This will setup the data table with the correct :atoms as names and will say what to do when we rool forward or what happens when we want to roll the database backwards. This will also make sure that we are running the timescale DB as the data table type.
        mix ecto.setup

    This is where I ran into issues with the timescale not being installed so I went to this website and then followed all the instructions
        https://docs.tigerdata.com/self-hosted/latest/install/installation-linux/

Creating our Ecto Schema
    Now we need to set the way in which we add the correct values to the database. It will be in and called: 
        sensor_hub_poncho/weather_tracker/lib/weather_tracker/weather_conditions/weather_condition.ex

    defmodule WeatherTracker.WeatherConditions.WeatherCondition do
        use Ecto.Schema
        import Ecto.Changeset

        @allowed_fields [
            :temperature_c,
            :pressure_pa,
            :humidity_rh,
            :light_lumens,
            :voc_index,
            :uv_index,
            :als_lux
        ]

        @derive {Jason.Encoder, only: @allowed_fields}
        @primary_key false

        schema "weather_conditions" do
            field :timestamp, :naive_datetime
            field :temperature_c, :decimal
            field :pressure_pa, :decimal
            field :humidity_rh, :decimal
            field :light_lumens, :decimal
            field :voc_index, :decimal
            field :uv_index, :decimal
            field :als_lux, :decimal
        end

        def create_changset(weather_condition = %__MODULE__{}, attrs) do
            timestamp =
            NaiveDateTime.utc_now()
            |> NaiveDateTime.truncate(:second)

            weather_condition
            |> cast(attrs, @allowed_fields)
            |> validate_required(@allowed_fields)
            |> put_change(:timestamp, timestamp)
        end
    end

Creating the Phoenix Context Module
    Now we need the commands and code to instert any data to the database. This will be at: lib/weather_tracker/weather_conditions.ex and will be very simple as we don't need much from it. 
    defmodule WeatherTracker.WeatherConditions do
        alias WeatherTracker.{WeatherConditions.WeatherCondition, Repo}

        def create_weather_condition(attrs) do
            %WeatherCondition{}
            |> WeatherCondition.create_changeset(attrs)
            |> Repo.insert()
        end
    end