diff --git a/.gitignore b/.gitignore index 40d7c773..d40c0dac 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,4 @@ dump.rdb # this file isnt stored here but just in case. fwup-key.priv +.env diff --git a/config/hardware/rpi3/dev.exs b/config/hardware/rpi3/dev.exs index d96312e7..8c182b10 100644 --- a/config/hardware/rpi3/dev.exs +++ b/config/hardware/rpi3/dev.exs @@ -22,6 +22,10 @@ config :farmbot, path: "/state", config_file_name: "default_config_rpi3.json" +config :logger, :console, + format: "\n$time $metadata[$level] $levelpad$message\n", + metadata: [:module] + # In production, we want a cron job for checking for updates. config :quantum, cron: [ "5 1 * * *": {Farmbot.System.Updates, :do_update_check}] diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index eab7eb51..b2d9aa39 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -5,10 +5,6 @@ via shell environment variables. ## Firmware Signing We Produce signed releases in PROD environment. export `PRIV_KEY_FILE` to be the private key file. -## IO debugger -If you want more verbose logs you can export `DEBUG_LOG`. This will cause (a lot of) messages -to be displayed on the current tty. - ## Mix Environment you can set `MIX_ENV=prod` or `MIX_ENV=dev` (default) to change the environment of the farmbot application. @@ -24,7 +20,7 @@ into a static website that gets served by `Plug` Webpack is configured via a package called `ex_webpack`. Default behavior it to watch the web source files for changes and recompile. This adds extra time to the initial compile of the application and can be just generally annoying. to disable -this export `USE_WEBPACK=false` +this export `NO_WEBPACK=true` ## Configurator The Configurator app is started by default on port `5000`. diff --git a/hello.txt b/hello.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/help.txt b/help.txt deleted file mode 100644 index ab3fd7b9..00000000 --- a/help.txt +++ /dev/null @@ -1 +0,0 @@ -heyhey \ No newline at end of file diff --git a/lib/farmbot/celery_script/commands/data_update.ex b/lib/farmbot/celery_script/commands/data_update.ex index 3b165bc9..2ff77c7b 100644 --- a/lib/farmbot/celery_script/commands/data_update.ex +++ b/lib/farmbot/celery_script/commands/data_update.ex @@ -44,19 +44,20 @@ defmodule Farmbot.CeleryScript.Command.DataUpdate do end @type number_or_wildcard :: non_neg_integer | binary # "*" - @type syncable :: Farmbot.Database.syncable + @type syncable :: Farmbot.Database.syncable | nil @spec parse_syncable_str(binary) :: syncable - defp parse_syncable_str("regimens"), do: Regimen + defp parse_syncable_str("regimens"), do: Regimen defp parse_syncable_str("peripherals"), do: Peripheral - defp parse_syncable_str("sequences"), do: Sequence + defp parse_syncable_str("sequences"), do: Sequence defp parse_syncable_str("farm_events"), do: FarmEvent - defp parse_syncable_str("tools"), do: Tool - defp parse_syncable_str("points"), do: Point - defp parse_syncable_str("devices"), do: Device + defp parse_syncable_str("tools"), do: Tool + defp parse_syncable_str("points"), do: Point + defp parse_syncable_str("device"), do: Device defp parse_syncable_str(str) do debug_log "no such syncable: #{str}" + nil end diff --git a/lib/farmbot/database.ex b/lib/farmbot/database.ex index 8a4ac29e..1ddd165f 100644 --- a/lib/farmbot/database.ex +++ b/lib/farmbot/database.ex @@ -59,12 +59,18 @@ defmodule Farmbot.Database do @spec all_syncable_modules :: [syncable] def all_syncable_modules, do: unquote(syncable_modules) + defp set_syncing(ctx, msg) do + :ok = Farmbot.BotState.set_sync_msg(ctx, msg) + :ok + end + @doc """ Sync up with the API. """ + # TODO(Connor) this is slow. @spec sync(Context.t) :: :ok | no_return def sync(%Context{} = ctx) do - Farmbot.BotState.set_sync_msg(ctx, :syncing) + set_syncing(ctx, :syncing) try do for module_name <- all_syncable_modules() do if get_awaiting(ctx, module_name) do @@ -82,12 +88,12 @@ defmodule Farmbot.Database do end end - Farmbot.BotState.set_sync_msg(ctx, :synced) + set_syncing(ctx, :synced) :ok rescue e -> Logger.info ">> Encountered error syncing, #{inspect e}", type: :error - Farmbot.BotState.set_sync_msg(ctx, :sync_error) + set_syncing(ctx, :sync_error) end end diff --git a/lib/farmbot/http.ex b/lib/farmbot/http.ex index 4abefc48..c0776c83 100644 --- a/lib/farmbot/http.ex +++ b/lib/farmbot/http.ex @@ -36,7 +36,7 @@ defmodule Farmbot.HTTP do def request(context, method, url, body \\ "", headers \\ [], opts \\ []) def request(%Context{} = ctx, method, url, body, headers, opts) do - GenServer.call(ctx.http, {:request, method, url, body, headers, opts}, :infinity) + GenServer.call(ctx.http, {:request, method, url, body, headers, opts}, 30_000) end def request!(context, method, url, body \\ "", headers \\ [], opts \\ []) @@ -71,7 +71,10 @@ defmodule Farmbot.HTTP do def post(context, url, body \\ "", headers \\ [], opts \\ []) - def post(%Context{} = ctx, url, body, headers, opts), do: request(ctx, :post, url, body, headers, opts) + def post(%Context{} = ctx, url, body, headers, opts) do + debug_log "doing http post: \n" <> body + request(ctx, :post, url, body, headers, opts) + end @doc """ Downloads a file to the filesystem @@ -108,6 +111,7 @@ defmodule Farmbot.HTTP do end def init(ctx) do + Process.flag(:trap_exit, true) state = %{ context: %{ctx | http: self()}, requests: %{} @@ -126,7 +130,7 @@ defmodule Farmbot.HTTP do user_agent = {"User-Agent", "FarmbotOS/#{@version} (#{@target}) #{@target}"} headers = [user_agent | headers] try do - debug_log "Trying to create request" + debug_log "Trying to create request: #{inspect options}" %AsyncResponse{id: ref} = HTTPoison.request!(method, url, body, headers, options) empty_request = empty_request() populated = if save_to_file do @@ -283,6 +287,11 @@ defmodule Farmbot.HTTP do {:noreply, state} end end + def handle_info(e, state) do + require IEx + IEx.pry + {:noreply, state} + end end # defmodule Farmbot.HTTP do # @moduledoc """ diff --git a/lib/farmbot/image_watcher.ex b/lib/farmbot/image_watcher.ex index 476d2db3..35c28f6b 100644 --- a/lib/farmbot/image_watcher.ex +++ b/lib/farmbot/image_watcher.ex @@ -5,6 +5,7 @@ defmodule Farmbot.ImageWatcher do use GenServer require Logger alias Farmbot.Context + use Farmbot.DebugLog @images_path "/tmp/images" @type state :: [] @@ -22,6 +23,8 @@ defmodule Farmbot.ImageWatcher do def force_upload(%Context{} = ctx), do: do_checkup(ctx) def init(context) do + debug_log "Ensuring #{@images_path} exists." + File.mkdir_p! @images_path # TODO(Connor) kill :fs if this app dies. :fs_app.start(:normal, []) :fs.subscribe() diff --git a/lib/farmbot/serial/handler/open_tty.ex b/lib/farmbot/serial/handler/open_tty.ex index bb1b5231..10b016a3 100644 --- a/lib/farmbot/serial/handler/open_tty.ex +++ b/lib/farmbot/serial/handler/open_tty.ex @@ -8,20 +8,26 @@ defmodule Farmbot.Serial.Handler.OpenTTY do @baud 115_200 - defp ensure_supervisor(sup) when is_atom(sup) do + defp ensure_supervisor(sup, retries \\ 0) + + defp ensure_supervisor(sup, retries) when retries > 25 do + :error + end + + defp ensure_supervisor(sup, retries) when is_atom(sup) do case Process.whereis(sup) do - pid when is_pid(pid) -> ensure_supervisor(pid) - _ -> ensure_supervisor(sup) + pid when is_pid(pid) -> ensure_supervisor(pid, retries + 1) + _ -> ensure_supervisor(sup, retries + 1) end end - defp ensure_supervisor(sup) when is_pid(sup) do + defp ensure_supervisor(sup, retries) when is_pid(sup) do if Process.alive?(sup) do debug_log "Serial has a supervisor." :ok else debug_log "Waiting for serial to find a supervisor." - ensure_supervisor(sup) + ensure_supervisor(sup, retries + 1) end end diff --git a/lib/farmbot/system/targets/nerves_common/fs.exs b/lib/farmbot/system/targets/nerves_common/fs.exs index 4cac4223..43aaa191 100644 --- a/lib/farmbot/system/targets/nerves_common/fs.exs +++ b/lib/farmbot/system/targets/nerves_common/fs.exs @@ -65,14 +65,19 @@ defmodule Farmbot.System.NervesCommon.FileSystem do do: raise "error doing command(#{num}): #{inspect err}" defp format_state_part do + state_path = unquote(state_path) # Format partition System.cmd("mkfs.#{unquote(fs_type)}", ["#{unquote(block_device)}", "-F"]) # Mount it as read/write # NOTE(connor): is there a reason i did this in band? System.cmd("mount", ["-t", unquote(fs_type), "-o", "rw", - unquote(block_device), unquote(state_path)]) + unquote(block_device), state_path]) # Basically a flag that says the partition is formatted. File.write!("#{unquote(state_path)}/.formatted", "DONT CAT ME\n") + + File.mkdir_p! "#{state_path}/farmware" + File.mkdir_p! "#{state_path}/farmware/packages" + File.mkdir_p! "#{state_path}/farmware/repos" :ok end end diff --git a/lib/farmbot/transport/gen_mqtt/gen_mqtt.ex b/lib/farmbot/transport/gen_mqtt/gen_mqtt.ex index c3d95742..7f103440 100644 --- a/lib/farmbot/transport/gen_mqtt/gen_mqtt.ex +++ b/lib/farmbot/transport/gen_mqtt/gen_mqtt.ex @@ -67,11 +67,13 @@ defmodule Farmbot.Transport.GenMqtt do {:noreply, [], state} end - def handle_info(_e, state) do - # catch other messages if we don't have a token, or client or - # we just don't know how to handle this message. - {:noreply, [], state} - end + def handle_info(_, {nil, nil, _context} = state), do: {:noreply, [], state} + + # def handle_info(_e, state) do + # # catch other messages if we don't have a token, or client or + # # we just don't know how to handle this message. + # {:noreply, [], state} + # end @spec start_client(Context.t, Token.t) :: {:ok, pid} defp start_client(%Context{} = context, %Token{} = token) do diff --git a/lib/farmbot/transport/gen_mqtt/gen_mqtt_client.ex b/lib/farmbot/transport/gen_mqtt/gen_mqtt_client.ex index 2e99e963..380ff121 100644 --- a/lib/farmbot/transport/gen_mqtt/gen_mqtt_client.ex +++ b/lib/farmbot/transport/gen_mqtt/gen_mqtt_client.ex @@ -59,7 +59,10 @@ defmodule Farmbot.Transport.GenMqtt.Client do {:ok, {token, context}} end - def on_disconnect(_), do: :shutdown + def on_disconnect(disconnect) do + require IEx + IEx.pry + end def handle_cast({:status, %Ser{} = ser}, {%Token{} = token, %Context{} = con}) do json = Poison.encode!(ser) @@ -79,11 +82,15 @@ defmodule Farmbot.Transport.GenMqtt.Client do {:noreply, {token, context}} end - def terminate(_,_), do: :ok + def terminate(a, state) do + require IEx + IEx.pry + end @spec build_opts(Token.t) :: GenMQTT.option defp build_opts(%Token{} = token) do - [name: __MODULE__, + [ + # name: __MODULE__, host: token.unencoded.mqtt, timeout: 10_000, reconnect_timeout: 10_000, diff --git a/lib/farmbot/transport/supervisor.ex b/lib/farmbot/transport/supervisor.ex index 523a6b26..31d1221c 100644 --- a/lib/farmbot/transport/supervisor.ex +++ b/lib/farmbot/transport/supervisor.ex @@ -29,7 +29,7 @@ defmodule Farmbot.Transport.Supervisor do end defp default_transport(%Context{} = ctx) do - worker(Farmbot.Transport, [ctx, [name: Farmbot.Transport]]) + worker(Farmbot.Transport, [ctx, [name: Farmbot.Transport]], restart: :permanent) end @doc """ @@ -47,7 +47,7 @@ defmodule Farmbot.Transport.Supervisor do worker(module, [context, []], restart: :permanent) {module, opts} when is_atom(module) -> debug_log "starting transport: #{module} with opts: #{inspect opts}" - worker(module, [context, [opts]], restart: :permanent) + worker(module, [context, opts], restart: :permanent) end end) end diff --git a/lib/mix/tasks/upload.ex b/lib/mix/tasks/upload.ex index 697c2124..e2b255e1 100644 --- a/lib/mix/tasks/upload.ex +++ b/lib/mix/tasks/upload.ex @@ -23,6 +23,9 @@ defmodule Mix.Tasks.Farmbot.Upload do http_opts = [relaxed: true, autoredirect: true] opts = [] + + :ok = do_ping_bot(ip_address) + {:ok, file} = :file.read_file('#{file_name}') file = :binary.bin_to_list(file) url = 'http://#{ip_address}/api/upload_firmware' @@ -39,6 +42,27 @@ defmodule Mix.Tasks.Farmbot.Upload do |> response end + defp do_ping_bot(ip_address) do + url = 'http://#{ip_address}/api/ping' + headers = [] + :httpc.request(:get, {url, headers}, [], []) |> ping_response + end + + defp ping_response({:ok, {{_, 200, _}, _, _}}) do + Mix.shell.info "Connected to bot!" + :ok + end + + defp ping_response({:ok, {{_, status_code, _}, _, error}}) do + Mix.shell.info "\nCould not connect to bot! #{status_code} #{inspect error}" + {:error, status_code, error} + end + + defp ping_response({:error, error}) do + Mix.shell.info "\nCould not connect to bot! #{inspect error}" + {:error, error} + end + defp start_httpc() do Application.ensure_started(:inets) Application.ensure_started(:ssl) diff --git a/mix.exs b/mix.exs index bacdf089..c1c343a1 100644 --- a/mix.exs +++ b/mix.exs @@ -235,7 +235,8 @@ defmodule Farmbot.Mixfile do # {:nerves_firmware, "~> 0.3"}, # {:nerves_firmware, path: "../nerves_firmware", override: true}, - {:nerves_firmware, github: "nerves-project/nerves_firmware", tag: "0f558ad2402cbd5b36bd7a8a10bc2b53167de14e", override: true}, + {:nerves_firmware, github: "nerves-project/nerves_firmware", override: true}, + # {:nerves_firmware, github: "nerves-project/nerves_firmware", tag: "0f558ad2402cbd5b36bd7a8a10bc2b53167de14e", override: true}, {:nerves_ssdp_server, "~> 0.2.1"}, ], diff --git a/mix.lock b/mix.lock index 7408f0f8..b898a34b 100644 --- a/mix.lock +++ b/mix.lock @@ -1,71 +1,71 @@ %{"amnesia": {:git, "https://github.com/meh/amnesia.git", "9094607fe61dc65a77ba71ae75a994847eb1dba5", []}, - "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], []}, - "calendar": {:hex, :calendar, "0.17.1", "5c7dfffde2b68011c2d6832ff1a15496292de965a3b57b3fad32405f1176f024", [:mix], [{:tzdata, "~> 0.5.8 or ~> 0.1.201603", [hex: :tzdata, optional: false]}]}, - "certifi": {:hex, :certifi, "1.0.0", "1c787a85b1855ba354f0b8920392c19aa1d06b0ee1362f9141279620a5be2039", [:rebar3], []}, - "combine": {:hex, :combine, "0.9.6", "8d1034a127d4cbf6924c8a5010d3534d958085575fa4d9b878f200d79ac78335", [:mix], []}, - "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], []}, - "cors_plug": {:hex, :cors_plug, "1.2.1", "bbe1381a52e4a16e609cf3c4cbfde6884726a58b9a1a205db104dbdfc542f447", [:mix], [{:plug, "> 0.8.0", [hex: :plug, optional: false]}]}, - "cowboy": {:hex, :cowboy, "1.1.0", "d9637f0fe7f0727efc8d703a9cfc2cf9e67fd11c6f7a22d35be687e44441d734", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.2.0", [hex: :ranch, optional: false]}]}, - "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []}, - "credo": {:hex, :credo, "0.6.0-rc1", "3db898270ba651aa7d8dc8d844f2827e0111526a01474ab088c7f7f677e83778", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, optional: false]}]}, - "dialyxir": {:hex, :dialyxir, "0.4.3", "a4daeebd0107de10d3bbae2ccb6b8905e69544db1ed5fe9148ad27cd4cb2c0cd", [:mix], []}, - "distillery": {:hex, :distillery, "1.3.1", "211231af29ea55c79143d601a2caaf5936cc7b99e73bef25d78a0ff7f321b7fe", [:mix], []}, - "earmark": {:hex, :earmark, "1.0.3", "89bdbaf2aca8bbb5c97d8b3b55c5dd0cff517ecc78d417e87f1d0982e514557b", [:mix], []}, - "elixir_make": {:hex, :elixir_make, "0.4.0", "992f38fabe705bb45821a728f20914c554b276838433349d4f2341f7a687cddf", [:mix], []}, - "ex_doc": {:hex, :ex_doc, "0.14.5", "c0433c8117e948404d93ca69411dd575ec6be39b47802e81ca8d91017a0cf83c", [:mix], [{:earmark, "~> 1.0", [hex: :earmark, optional: false]}]}, - "ex_json_schema": {:hex, :ex_json_schema, "0.5.3", "f2db8eb71ca7a836607d67021d1b9c24f192dd5d1ec6b6b1c89bc934fec3bf5e", [:mix], []}, - "ex_rollbar": {:hex, :ex_rollbar, "0.1.1", "65e4c37e631a3884a9458ce4973b9e00d2ff6765a24bb33af589eba464b19131", [:mix], [{:poison, "~> 3.0", [hex: :poison, optional: false]}]}, - "ex_syslogger": {:hex, :ex_syslogger, "1.3.3", "914be2c8d759d60d853790815e6cf853e4cea9e24922c01b488c172ba0b7e105", [:mix], [{:poison, ">= 1.5.0", [hex: :poison, optional: true]}, {:syslog, "~> 1.0.2", [hex: :syslog, optional: false]}]}, - "ex_webpack": {:hex, :ex_webpack, "0.1.1", "d9deca1f9adfa1fa99ee2630f79756d741f17d4865bc808cd44577304fd053e3", [:mix], []}, - "exactor": {:hex, :exactor, "2.2.3", "a6972f43bb6160afeb73e1d8ab45ba604cd0ac8b5244c557093f6e92ce582786", [:mix], []}, - "excoveralls": {:hex, :excoveralls, "0.6.3", "894bf9254890a4aac1d1165da08145a72700ff42d8cb6ce8195a584cb2a4b374", [:mix], [{:exjsx, "~> 3.0", [hex: :exjsx, optional: false]}, {:hackney, ">= 0.12.0", [hex: :hackney, optional: false]}]}, - "exjsx": {:hex, :exjsx, "3.2.1", "1bc5bf1e4fd249104178f0885030bcd75a4526f4d2a1e976f4b428d347614f0f", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, optional: false]}]}, + "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"}, + "calendar": {:hex, :calendar, "0.17.1", "5c7dfffde2b68011c2d6832ff1a15496292de965a3b57b3fad32405f1176f024", [:mix], [{:tzdata, "~> 0.5.8 or ~> 0.1.201603", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"}, + "certifi": {:hex, :certifi, "1.0.0", "1c787a85b1855ba354f0b8920392c19aa1d06b0ee1362f9141279620a5be2039", [:rebar3], [], "hexpm"}, + "combine": {:hex, :combine, "0.9.6", "8d1034a127d4cbf6924c8a5010d3534d958085575fa4d9b878f200d79ac78335", [:mix], [], "hexpm"}, + "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"}, + "cors_plug": {:hex, :cors_plug, "1.2.1", "bbe1381a52e4a16e609cf3c4cbfde6884726a58b9a1a205db104dbdfc542f447", [:mix], [{:plug, "> 0.8.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, + "cowboy": {:hex, :cowboy, "1.1.0", "d9637f0fe7f0727efc8d703a9cfc2cf9e67fd11c6f7a22d35be687e44441d734", [:rebar3], [{:cowlib, "~> 1.0.2", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.2.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, + "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], [], "hexpm"}, + "credo": {:hex, :credo, "0.6.0-rc1", "3db898270ba651aa7d8dc8d844f2827e0111526a01474ab088c7f7f677e83778", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}], "hexpm"}, + "dialyxir": {:hex, :dialyxir, "0.4.3", "a4daeebd0107de10d3bbae2ccb6b8905e69544db1ed5fe9148ad27cd4cb2c0cd", [:mix], [], "hexpm"}, + "distillery": {:hex, :distillery, "1.3.1", "211231af29ea55c79143d601a2caaf5936cc7b99e73bef25d78a0ff7f321b7fe", [:mix], [], "hexpm"}, + "earmark": {:hex, :earmark, "1.0.3", "89bdbaf2aca8bbb5c97d8b3b55c5dd0cff517ecc78d417e87f1d0982e514557b", [:mix], [], "hexpm"}, + "elixir_make": {:hex, :elixir_make, "0.4.0", "992f38fabe705bb45821a728f20914c554b276838433349d4f2341f7a687cddf", [:mix], [], "hexpm"}, + "ex_doc": {:hex, :ex_doc, "0.14.5", "c0433c8117e948404d93ca69411dd575ec6be39b47802e81ca8d91017a0cf83c", [:mix], [{:earmark, "~> 1.0", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"}, + "ex_json_schema": {:hex, :ex_json_schema, "0.5.3", "f2db8eb71ca7a836607d67021d1b9c24f192dd5d1ec6b6b1c89bc934fec3bf5e", [:mix], [], "hexpm"}, + "ex_rollbar": {:hex, :ex_rollbar, "0.1.1", "65e4c37e631a3884a9458ce4973b9e00d2ff6765a24bb33af589eba464b19131", [:mix], [{:poison, "~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, + "ex_syslogger": {:hex, :ex_syslogger, "1.3.3", "914be2c8d759d60d853790815e6cf853e4cea9e24922c01b488c172ba0b7e105", [:mix], [{:poison, ">= 1.5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:syslog, "~> 1.0.2", [hex: :syslog, repo: "hexpm", optional: false]}], "hexpm"}, + "ex_webpack": {:hex, :ex_webpack, "0.1.1", "d9deca1f9adfa1fa99ee2630f79756d741f17d4865bc808cd44577304fd053e3", [:mix], [], "hexpm"}, + "exactor": {:hex, :exactor, "2.2.3", "a6972f43bb6160afeb73e1d8ab45ba604cd0ac8b5244c557093f6e92ce582786", [:mix], [], "hexpm"}, + "excoveralls": {:hex, :excoveralls, "0.6.3", "894bf9254890a4aac1d1165da08145a72700ff42d8cb6ce8195a584cb2a4b374", [:mix], [{:exjsx, "~> 3.0", [hex: :exjsx, repo: "hexpm", optional: false]}, {:hackney, ">= 0.12.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, + "exjsx": {:hex, :exjsx, "3.2.1", "1bc5bf1e4fd249104178f0885030bcd75a4526f4d2a1e976f4b428d347614f0f", [:mix], [{:jsx, "~> 2.8.0", [hex: :jsx, repo: "hexpm", optional: false]}], "hexpm"}, "exquisite": {:hex, :exquisite, "0.1.7", "4106503e976f409246731b168cd76eb54262bd04f4facc5cba82c2f53982aaf0", [:mix], []}, - "exvcr": {:hex, :exvcr, "0.8.7", "e76f33b10dfefbcf32afa6d6867140566d0d54797e352b47485eed0241dd7edf", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, optional: false]}, {:exjsx, "~> 3.2", [hex: :exjsx, optional: false]}, {:httpoison, "~> 0.8", [hex: :httpoison, optional: true]}, {:httpotion, "~> 3.0", [hex: :httpotion, optional: true]}, {:ibrowse, "~> 4.2.2", [hex: :ibrowse, optional: true]}, {:meck, "~> 0.8.3", [hex: :meck, optional: false]}]}, - "faker": {:hex, :faker, "0.7.0", "2c42deeac7be717173c78c77fb3edc749fb5d5e460e33d01fe592ae99acc2f0d", [:mix], []}, + "exvcr": {:hex, :exvcr, "0.8.7", "e76f33b10dfefbcf32afa6d6867140566d0d54797e352b47485eed0241dd7edf", [:mix], [{:exactor, "~> 2.2", [hex: :exactor, repo: "hexpm", optional: false]}, {:exjsx, "~> 3.2", [hex: :exjsx, repo: "hexpm", optional: false]}, {:httpoison, "~> 0.8", [hex: :httpoison, repo: "hexpm", optional: true]}, {:httpotion, "~> 3.0", [hex: :httpotion, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.2.2", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:meck, "~> 0.8.3", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"}, + "faker": {:hex, :faker, "0.7.0", "2c42deeac7be717173c78c77fb3edc749fb5d5e460e33d01fe592ae99acc2f0d", [:mix], [], "hexpm"}, "farmbot_simulator": {:git, "https://github.com/farmbot-labs/farmbot_simulator.git", "ee072d54e756d8548b687e85651e055f222d21fb", []}, - "fs": {:hex, :fs, "0.9.2", "ed17036c26c3f70ac49781ed9220a50c36775c6ca2cf8182d123b6566e49ec59", [:rebar], []}, - "gen_mqtt": {:hex, :gen_mqtt, "0.3.1", "6ce6af7c2bcb125d5b4125c67c5ab1f29bcec2638236509bcc6abf510a6661ed", [:mix], [{:vmq_commons, "1.0.0", [hex: :vmq_commons, optional: false]}]}, - "gen_stage": {:hex, :gen_stage, "0.11.0", "943bdfa85c75fa624e0a36a9d135baad20a523be040178f5a215444b45c66ea4", [:mix], []}, - "gettext": {:hex, :gettext, "0.13.0", "daafbddc5cda12738bb93b01d84105fe75b916a302f1c50ab9fb066b95ec9db4", [:mix], []}, - "hackney": {:hex, :hackney, "1.7.1", "e238c52c5df3c3b16ce613d3a51c7220a784d734879b1e231c9babd433ac1cb4", [:rebar3], [{:certifi, "1.0.0", [hex: :certifi, optional: false]}, {:idna, "4.0.0", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, optional: false]}]}, + "fs": {:hex, :fs, "0.9.2", "ed17036c26c3f70ac49781ed9220a50c36775c6ca2cf8182d123b6566e49ec59", [:rebar], [], "hexpm"}, + "gen_mqtt": {:hex, :gen_mqtt, "0.3.1", "6ce6af7c2bcb125d5b4125c67c5ab1f29bcec2638236509bcc6abf510a6661ed", [:mix], [{:vmq_commons, "1.0.0", [hex: :vmq_commons, repo: "hexpm", optional: false]}], "hexpm"}, + "gen_stage": {:hex, :gen_stage, "0.11.0", "943bdfa85c75fa624e0a36a9d135baad20a523be040178f5a215444b45c66ea4", [:mix], [], "hexpm"}, + "gettext": {:hex, :gettext, "0.13.0", "daafbddc5cda12738bb93b01d84105fe75b916a302f1c50ab9fb066b95ec9db4", [:mix], [], "hexpm"}, + "hackney": {:hex, :hackney, "1.7.1", "e238c52c5df3c3b16ce613d3a51c7220a784d734879b1e231c9babd433ac1cb4", [:rebar3], [{:certifi, "1.0.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "4.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"}, "httpoison": {:git, "https://github.com/edgurgel/httpoison.git", "d7633d7dc286aaac41970c7f2dd8d6f12da8f05d", []}, "ibrowse": {:hex, :ibrowse, "4.2.2", "b32b5bafcc77b7277eff030ed32e1acc3f610c64e9f6aea19822abcadf681b4b", [:rebar3], []}, - "idna": {:hex, :idna, "4.0.0", "10aaa9f79d0b12cf0def53038547855b91144f1bfcc0ec73494f38bb7b9c4961", [:rebar3], []}, - "jsx": {:hex, :jsx, "2.8.2", "7acc7d785b5abe8a6e9adbde926a24e481f29956dd8b4df49e3e4e7bcc92a018", [:mix, :rebar3], []}, - "meck": {:hex, :meck, "0.8.4", "59ca1cd971372aa223138efcf9b29475bde299e1953046a0c727184790ab1520", [:make, :rebar], []}, - "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []}, - "mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [:mix], []}, - "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []}, - "mock": {:hex, :mock, "0.2.1", "bfdba786903e77f9c18772dee472d020ceb8ef000783e737725a4c8f54ad28ec", [:mix], [{:meck, "~> 0.8.2", [hex: :meck, optional: false]}]}, - "mustache": {:hex, :mustache, "0.0.2", "870fbef411c47d17df06a57b8b3f69e26eb110cba0177c34e273db5d94369d9b", [:mix], []}, - "nerves": {:hex, :nerves, "0.5.1", "a55d73752e8f271c7aca5fbe47747f07d0e7760b5e858e7f8da17ab06287f49c", [:mix], [{:distillery, "~> 1.0", [hex: :distillery, optional: false]}]}, - "nerves_firmware": {:git, "https://github.com/nerves-project/nerves_firmware.git", "0f558ad2402cbd5b36bd7a8a10bc2b53167de14e", [tag: "0f558ad2402cbd5b36bd7a8a10bc2b53167de14e"]}, + "idna": {:hex, :idna, "4.0.0", "10aaa9f79d0b12cf0def53038547855b91144f1bfcc0ec73494f38bb7b9c4961", [:rebar3], [], "hexpm"}, + "jsx": {:hex, :jsx, "2.8.2", "7acc7d785b5abe8a6e9adbde926a24e481f29956dd8b4df49e3e4e7bcc92a018", [:mix, :rebar3], [], "hexpm"}, + "meck": {:hex, :meck, "0.8.4", "59ca1cd971372aa223138efcf9b29475bde299e1953046a0c727184790ab1520", [:make, :rebar], [], "hexpm"}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, + "mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [:mix], [], "hexpm"}, + "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], [], "hexpm"}, + "mock": {:hex, :mock, "0.2.1", "bfdba786903e77f9c18772dee472d020ceb8ef000783e737725a4c8f54ad28ec", [:mix], [{:meck, "~> 0.8.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"}, + "mustache": {:hex, :mustache, "0.0.2", "870fbef411c47d17df06a57b8b3f69e26eb110cba0177c34e273db5d94369d9b", [:mix], [], "hexpm"}, + "nerves": {:hex, :nerves, "0.5.1", "a55d73752e8f271c7aca5fbe47747f07d0e7760b5e858e7f8da17ab06287f49c", [:mix], [{:distillery, "~> 1.0", [hex: :distillery, repo: "hexpm", optional: false]}], "hexpm"}, + "nerves_firmware": {:git, "https://github.com/nerves-project/nerves_firmware.git", "421d343a987b6b324e1e2919c24ba4e8e90e34df", []}, "nerves_firmware_http": {:hex, :nerves_firmware_http, "0.3.1", "e6ed2f8aa216df424a18ee56b6160b2aa7df9c3783406c93a61d92efd94acfac", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, optional: false]}, {:exjsx, "~> 4.0", [hex: :exjsx, optional: false]}, {:nerves_firmware, "~> 0.3", [hex: :nerves_firmware, optional: false]}]}, "nerves_hal": {:git, "https://github.com/LeToteTeam/nerves_hal.git", "249139e985017fdf0c5839eefc59788f575c48dd", []}, "nerves_interim_wifi": {:git, "https://github.com/nerves-project/nerves_interim_wifi.git", "42a70b8773adbbf2918e68262b47241c6fb61947", []}, "nerves_lib": {:git, "https://github.com/nerves-project/nerves_lib.git", "aac351cb3e621831a317f2d2a078257161efa551", []}, - "nerves_network_interface": {:hex, :nerves_network_interface, "0.4.0", "a8e7662cd56fb4fe9060c891d35c43bbbff692ee6fd2d5efd538717da0cd96b8", [:make, :mix], [{:elixir_make, "~> 0.3", [hex: :elixir_make, optional: false]}]}, + "nerves_network_interface": {:hex, :nerves_network_interface, "0.4.0", "a8e7662cd56fb4fe9060c891d35c43bbbff692ee6fd2d5efd538717da0cd96b8", [:make, :mix], [{:elixir_make, "~> 0.3", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"}, "nerves_runtime": {:git, "https://github.com/nerves-project/nerves_runtime.git", "d5fe649b4ce5c5ac7a7ab89083ecaee0d9214b94", []}, - "nerves_ssdp_server": {:hex, :nerves_ssdp_server, "0.2.1", "2d010552023fc1a724e8cb5c92479a58552976e6f805b6dbf09babd31f923b8f", [:mix], []}, + "nerves_ssdp_server": {:hex, :nerves_ssdp_server, "0.2.1", "2d010552023fc1a724e8cb5c92479a58552976e6f805b6dbf09babd31f923b8f", [:mix], [], "hexpm"}, "nerves_system_br": {:hex, :nerves_system_br, "0.9.4", "5096a9dfec49d4663ccb94c4a4fe45885303fbf31108f7e9400369bdec94b5e7", [:mix], []}, - "nerves_toolchain_arm_unknown_linux_gnueabihf": {:hex, :nerves_toolchain_arm_unknown_linux_gnueabihf, "0.10.0", "18200c6cc3fcda1cbe263b7f7d50ff05db495b881ade9436cd1667b3e8e62429", [:mix], [{:nerves, "~> 0.4", [hex: :nerves, optional: false]}, {:nerves_toolchain_ctng, "~> 0.9", [hex: :nerves_toolchain_ctng, optional: false]}]}, + "nerves_toolchain_arm_unknown_linux_gnueabihf": {:hex, :nerves_toolchain_arm_unknown_linux_gnueabihf, "0.10.0", "18200c6cc3fcda1cbe263b7f7d50ff05db495b881ade9436cd1667b3e8e62429", [:mix], [{:nerves, "~> 0.4", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_toolchain_ctng, "~> 0.9", [hex: :nerves_toolchain_ctng, repo: "hexpm", optional: false]}], "hexpm"}, "nerves_toolchain_armv6_rpi_linux_gnueabi": {:hex, :nerves_toolchain_armv6_rpi_linux_gnueabi, "0.10.0", "a730667fb22710270d3e9fdab8ce7230381c424f65b0381feb693829bc460f80", [:mix], [{:nerves, "~> 0.4", [hex: :nerves, optional: false]}, {:nerves_toolchain_ctng, "~> 0.9", [hex: :nerves_toolchain_ctng, optional: false]}]}, - "nerves_toolchain_ctng": {:hex, :nerves_toolchain_ctng, "0.9.0", "825b2b5bbacc3ad20c8513baafd44978616d5b451e6e052cdb727be81e7cdcac", [:mix], []}, - "nerves_uart": {:hex, :nerves_uart, "0.1.2", "4310dbb1721a5a007b8e5c416cf81754415bde6b7e2c9aa65a059886b85e637c", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, optional: false]}]}, - "nerves_wpa_supplicant": {:hex, :nerves_wpa_supplicant, "0.3.0", "dfda748df2662e1e9e95df6662c3a512d371ef23359b2c090b9c8c884b236a3d", [:make, :mix], [{:elixir_make, "~> 0.3", [hex: :elixir_make, optional: false]}]}, - "plug": {:hex, :plug, "1.3.5", "7503bfcd7091df2a9761ef8cecea666d1f2cc454cbbaf0afa0b6e259203b7031", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, optional: true]}, {:mime, "~> 1.0", [hex: :mime, optional: false]}]}, - "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], []}, - "quantum": {:hex, :quantum, "1.8.1", "37c9ad0307cf47bd578507ce1ddda98746199b281e8afe91cbe44c21d56af983", [:mix], [{:calendar, "~> 0.16", [hex: :calendar, optional: false]}]}, - "ranch": {:hex, :ranch, "1.2.1", "a6fb992c10f2187b46ffd17ce398ddf8a54f691b81768f9ef5f461ea7e28c762", [:make], []}, - "redix": {:hex, :redix, "0.5.1", "2bf874a186cc759791b8defdd0bfaa752784716bd48241f6aa972a20e7f95745", [:mix], [{:connection, "~> 1.0", [hex: :connection, optional: false]}]}, + "nerves_toolchain_ctng": {:hex, :nerves_toolchain_ctng, "0.9.0", "825b2b5bbacc3ad20c8513baafd44978616d5b451e6e052cdb727be81e7cdcac", [:mix], [], "hexpm"}, + "nerves_uart": {:hex, :nerves_uart, "0.1.2", "4310dbb1721a5a007b8e5c416cf81754415bde6b7e2c9aa65a059886b85e637c", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"}, + "nerves_wpa_supplicant": {:hex, :nerves_wpa_supplicant, "0.3.0", "dfda748df2662e1e9e95df6662c3a512d371ef23359b2c090b9c8c884b236a3d", [:make, :mix], [{:elixir_make, "~> 0.3", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"}, + "plug": {:hex, :plug, "1.3.5", "7503bfcd7091df2a9761ef8cecea666d1f2cc454cbbaf0afa0b6e259203b7031", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"}, + "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, + "quantum": {:hex, :quantum, "1.8.1", "37c9ad0307cf47bd578507ce1ddda98746199b281e8afe91cbe44c21d56af983", [:mix], [{:calendar, "~> 0.16", [hex: :calendar, repo: "hexpm", optional: false]}], "hexpm"}, + "ranch": {:hex, :ranch, "1.2.1", "a6fb992c10f2187b46ffd17ce398ddf8a54f691b81768f9ef5f461ea7e28c762", [:make], [], "hexpm"}, + "redix": {:hex, :redix, "0.5.1", "2bf874a186cc759791b8defdd0bfaa752784716bd48241f6aa972a20e7f95745", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"}, "rollbax": {:hex, :rollbax, "0.8.2", "204a47a83fe32745def19ea0307b297395c00315fb85f30dfda04d5009b5ecb9", [:mix], [{:hackney, "~> 1.1", [hex: :hackney, optional: false]}, {:poison, "~> 1.4 or ~> 2.0 or ~> 3.0", [hex: :poison, optional: false]}]}, - "rsa": {:hex, :rsa, "0.0.1", "a63069f88ce342ffdf8448b7cdef4b39ba7dee3c1510644a39385c7e63ba246f", [:mix], []}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], []}, - "syslog": {:hex, :syslog, "1.0.2", "9cf72c0986675a170c03b210e49700845a0f7b61e96d302a3ba0df82963daf60", [:rebar], []}, + "rsa": {:hex, :rsa, "0.0.1", "a63069f88ce342ffdf8448b7cdef4b39ba7dee3c1510644a39385c7e63ba246f", [:mix], [], "hexpm"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], [], "hexpm"}, + "syslog": {:hex, :syslog, "1.0.2", "9cf72c0986675a170c03b210e49700845a0f7b61e96d302a3ba0df82963daf60", [:rebar], [], "hexpm"}, "system_registry": {:hex, :system_registry, "0.1.2", "b6dba36c575786f1848e18a1155a2b6bd82f607f0ae9dc88185d6b7dc3fcdf60", [:mix], []}, - "timex": {:hex, :timex, "3.1.7", "71f9c32e13ff4860e86a314303757cc02b3ead5db6e977579a2935225ce9a666", [:mix], [{:combine, "~> 0.7", [hex: :combine, optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, optional: false]}]}, - "tzdata": {:hex, :tzdata, "0.1.201605", "0c4184819b9d6adedcc02107b68321c45d8e853def7a32629b7961b9f2e95f33", [:mix], []}, - "vmq_commons": {:hex, :vmq_commons, "1.0.0", "5f5005c12db33f92f40e818a3617fb148972d59adcf99298c9d3808ef3582e34", [:rebar3], []}, - "websocket_client": {:hex, :websocket_client, "1.2.1", "a965ce0be5583c90347400bceca66629e4debd5feb9bd516107e2924bdf39dad", [:rebar3], []}, - "wobserver": {:hex, :wobserver, "0.1.7", "377b9a2903728b62e4e89d4e200ec17d60669ccdd3ed72b23a2ab3a2c079694d", [:mix], [{:cowboy, "~> 1.1", [hex: :cowboy, optional: false]}, {:httpoison, "~> 0.11", [hex: :httpoison, optional: false]}, {:plug, "~> 1.3", [hex: :plug, optional: false]}, {:poison, "~> 2.0 or ~> 3.0", [hex: :poison, optional: false]}, {:websocket_client, "~> 1.2", [hex: :websocket_client, optional: false]}]}} + "timex": {:hex, :timex, "3.1.7", "71f9c32e13ff4860e86a314303757cc02b3ead5db6e977579a2935225ce9a666", [:mix], [{:combine, "~> 0.7", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"}, + "tzdata": {:hex, :tzdata, "0.1.201605", "0c4184819b9d6adedcc02107b68321c45d8e853def7a32629b7961b9f2e95f33", [:mix], [], "hexpm"}, + "vmq_commons": {:hex, :vmq_commons, "1.0.0", "5f5005c12db33f92f40e818a3617fb148972d59adcf99298c9d3808ef3582e34", [:rebar3], [], "hexpm"}, + "websocket_client": {:hex, :websocket_client, "1.2.1", "a965ce0be5583c90347400bceca66629e4debd5feb9bd516107e2924bdf39dad", [:rebar3], [], "hexpm"}, + "wobserver": {:hex, :wobserver, "0.1.7", "377b9a2903728b62e4e89d4e200ec17d60669ccdd3ed72b23a2ab3a2c079694d", [:mix], [{:cowboy, "~> 1.1", [hex: :cowboy, repo: "hexpm", optional: false]}, {:httpoison, "~> 0.11", [hex: :httpoison, repo: "hexpm", optional: false]}, {:plug, "~> 1.3", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 2.0 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}, {:websocket_client, "~> 1.2", [hex: :websocket_client, repo: "hexpm", optional: false]}], "hexpm"}}