weewx-proxy/lib/weewx_proxy/http/ecowitt.ex

123 lines
4.1 KiB
Elixir

defmodule WeewxProxy.HTTP.Ecowitt do
require Logger
use Plug.Router
alias WeewxProxy.{Publisher, Utils}
alias WeewxProxy.Sdr.Ecowitt, as: Sdr
@type parsed_body :: %{required(String.t()) => String.t()}
plug Plug.Logger, log: :debug
plug Plug.Parsers, parsers: [:urlencoded]
plug :match
plug :dispatch
post "/update" do
body = conn.body_params
_ = Logger.debug("Incoming request body: #{inspect(body)}")
data = transform_data(body)
:ok =
if valid_data?(data) do
sdr_keys = Sdr.recently_uploaded_keys(data.dateTime)
_ = Logger.debug("Removing keys: `#{inspect(sdr_keys)}'")
partial_data = Map.drop(data, sdr_keys)
Publisher.publish("weewx/ingest_us", partial_data)
else
_ = Logger.error("Not publishing record because data appears invalid: #{inspect(data)}")
:ok
end
tz = System.get_env("TZ", "Europe/Berlin")
utc_offset = Utils.utc_offset_string(tz)
response = ~s({"errcode":"0","errmsg":"ok","UTC_offset":"#{utc_offset}"})
send_resp(conn, 200, response)
end
match _ do
send_resp(conn, 404, "Not Found")
end
# Private
@spec transform_data(parsed_body()) :: Publisher.data()
defp transform_data(data) do
# Fields with totals:
# - rain
# - lightning_strike_count
%{
dateTime: format_date_time(data),
# Outdoor
outTemp: Utils.parse_float(data["tempf"]),
outHumidity: Utils.parse_float(data["humidity"]),
pressure: Utils.parse_float(data["baromabsin"]),
windSpeed: Utils.parse_float(data["windspeedmph"]),
windGust: Utils.parse_float(data["windgustmph"]),
windDir: Utils.parse_float(data["winddir"]),
rain: Utils.parse_float(data["yearlyrainin"]),
rainRate: Utils.parse_float(data["rainratein"]),
UV: Utils.parse_float(data["uv"]),
radiation: Utils.parse_float(data["solarradiation"]),
soilMoist1: Utils.parse_float(data["soilmoisture1"]),
soilTemp1: Utils.parse_float(data["tf_ch1"]),
lightning_strike_count: calculate_lightning_strike_count(data),
lightning_last_det_time: Utils.parse_integer(data["lightning_time"]),
lightning_distance: calculate_lightning_distance(data),
# Indoor
inTemp: Utils.parse_float(data["tempinf"]),
inHumidity: Utils.parse_float(data["humidityin"]),
# Battery Status: Outdoor sensor array
batteryStatus1: Utils.parse_integer(data["wh65batt"]),
# Battery Status: Lightning sensor
batteryStatus2: Utils.parse_integer(data["wh57batt"]),
# Battery Status: Indoor sensor
inTempBatteryStatus: Utils.parse_integer(data["wh25batt"]),
# Battery Voltage: Soil moisture sensor
batteryStatus3: Utils.parse_float(data["soilbatt1"]),
# Battery Voltage: Soil temperature sensor
batteryStatus4: Utils.parse_float(data["tf_batt1"])
}
end
@spec format_date_time(parsed_body()) :: non_neg_integer()
defp format_date_time(data) do
{:ok, dt, 0} =
data |> Map.get("dateutc") |> String.replace("+", "T") |> Utils.append_string("Z") |> DateTime.from_iso8601()
DateTime.to_unix(dt)
end
@spec calculate_lightning_strike_count(parsed_body()) :: float() | nil
defp calculate_lightning_strike_count(data) do
if Map.has_key?(data, "lightning_num") do
value = Utils.parse_float(data["lightning_num"])
if is_nil(value), do: 0.0, else: value
else
nil
end
end
@spec calculate_lightning_distance(parsed_body()) :: float() | nil
defp calculate_lightning_distance(data) do
distance_km = data["lightning"]
strikes = calculate_lightning_strike_count(data)
current_time = Utils.utc_timestamp()
lightning_time = Utils.parse_integer(data["lightning_time"])
time_diff = current_time - lightning_time
if is_binary(distance_km) and is_number(strikes) and byte_size(distance_km) > 0 and strikes > 0 and time_diff < 1200 do
0.62137119 * Utils.parse_float(distance_km)
else
nil
end
end
@spec valid_data?(Publisher.data()) :: boolean()
defp valid_data?(data) do
Map.has_key?(data, :outTemp) and is_number(data.outTemp)
end
end