WeatherflowTempest.Protocol (Weatherflow Tempest v1.0.1)

The Weatherflow Protocol has a lot of magic fields. This parses and converts them to make the returned objects more intelligible.

Byte-effecient arrays are unpacked into named fields based on the protocol docs published by Weatherflow.

The following field standardizations are made to all event types:

  • "type" fields are removed. (Use the whole {:type, %{}} tuple)

  • "evt" fields containing the raw un-parsed event data are removed, and the data from those fields is flattened into a single event map.

  • "uptime" fields containing seconds-as-integers are given human-readable string representations such as "1 week, 4 days, 3 hours, 16 minutes", which are placed in an :uptime_string field

  • "timestamp" field containing the epoch time are converted to DateTime

  • All fields are converted to use atoms as keys. The only string keys used will be device serial numbers, i.e.:

      device_statuses: %{
          "AR-00000001" => %{
              firmware_revision: 23
          }
      }
  • :hub_sn key is added to the hub_status message type, allowing easy pattern matching for all events from a given hub.

Notes:

  • The hub_status event returns a firmware_revision as a string rather than an integer, according to the API examples. We do not convert that integer to a string since it's unclear what Weatherflow's intent here is. Perhaps there will be a firmware revision "37beta2"?
  • The actual data types of the fields is not given by the Weatherflow UDP API documentation. We infer which values are floats and which are ints based on their examples only. There is no guarantee that this is correct. Did they show "0" for an example but may actually return "0.1"? Because of this we will not attempt to coerce any values or parse anything that we aren't absolutely certain of.

Summary

Functions

Accepts the result tuple of Jason.decode() If the JSON could not be decoded bubble the error up to be handled by the WeatherflowTempest.Client, otherwise parse the event types defined by the Weatherflow spec.

Functions

Link to this function

handle_json(error_tuple)

@spec handle_json({atom(), map()}) :: map()

Accepts the result tuple of Jason.decode() If the JSON could not be decoded bubble the error up to be handled by the WeatherflowTempest.Client, otherwise parse the event types defined by the Weatherflow spec.

Returns a tuple containing an atom matching the event "type" field, followed by the parsed object as a map. (Because it is cleaner to pattern match the against the tuple than against a key in the map, and makes returning ar error tuple the easy way to bubble up any errors.) Some liberties are taken with renaming fields and altering the resulting structure for clarity. The "type" key is removed from all result maps, as the intention is to hold on to the entire tuple if you need to pass it around.

For evt_precip, evt_strike, and rapid_wind messages we flatten the data into a single map for convenience.

For example, a rapid_wind event from the UDP API looks like this:

  {
    "serial_number": "AR-00004049",
    "type":"rapid_wind",
    "hub_sn": "HB-00000001",
    "ob":[1493322445,2.3,128]
  }

and we will parse that into:

  {:rapid_wind,
   %{
    serial_number: "AR-00004049",
    hub_sn: "HB-00000001",
    timestamp: ~U[2017-04-27 19:47:25Z],
    wind_speed_mps: 2.3,
    wind_direction_degrees: 128
   }
  }

However, the obs_air, obs_sky, and obs_st event types can return a list of observations, although they most often return just a single event. Because of this those observations are not flattened into a single map, and are instead returned as a list, sorted by ascending timestamp. They are returned under the :observations key, rather than the "obs" used by the API, for the sake of clarity.

The API doesn't cearly define what would cause the devices to return a list of observations rather than a single one, so we can't make any assumptions here about de-duplication or repeated events unfortunately, and will simply parse and return whatever the API provides. Do note however that the logic in WeatherflowTempest.Client strips out all but the most recent event, so if you'd like to actually deal with a device that is sending lists of observations you'll need to use WeatherflowTempest.Protocol directly.