farmbot discovery
parent
67bcbf41fd
commit
20122cdfc6
|
@ -9,11 +9,13 @@ defmodule Farmbot.System.NervesCommon.Network do
|
|||
target = Keyword.get(opts, :target)
|
||||
quote do
|
||||
@behaviour Farmbot.System.Network
|
||||
use GenServer
|
||||
use GenServer
|
||||
require Logger
|
||||
alias Nerves.InterimWiFi, as: NervesWifi
|
||||
alias Farmbot.System.Network.Hostapd
|
||||
alias Farmbot.Context
|
||||
alias Nerves.InterimWiFi, as: NervesWifi
|
||||
alias Nerves.{NetworkInterface, Udhcpc, WpaSupplicant, SSDPServer}
|
||||
alias Farmbot.System.Network.Hostapd
|
||||
alias Farmbot.Context
|
||||
use Farmbot.DebugLog
|
||||
|
||||
def start_link(%Context{} = ctx),
|
||||
do: GenServer.start_link(__MODULE__, ctx, name: __MODULE__)
|
||||
|
@ -133,7 +135,7 @@ defmodule Farmbot.System.NervesCommon.Network do
|
|||
end
|
||||
end
|
||||
|
||||
def enumerate(%Context{} = _ctx), do: Nerves.NetworkInterface.interfaces -- ["lo"]
|
||||
def enumerate(%Context{} = _ctx), do: NetworkInterface.interfaces -- ["lo"]
|
||||
|
||||
defp clean_ssid(hc) do
|
||||
hc
|
||||
|
@ -141,14 +143,13 @@ defmodule Farmbot.System.NervesCommon.Network do
|
|||
|> String.replace("\\x00", "")
|
||||
|> String.split("\n")
|
||||
|> Enum.filter(fn(s) -> String.contains?(s, "SSID: ") end)
|
||||
|> Enum.map(fn(z) -> String.replace(z, "SSID: ", "") end)
|
||||
|> Enum.map(fn(z) -> String.replace(z, "SSID: ", "") end)
|
||||
|> Enum.filter(fn(z) -> String.length(z) != 0 end)
|
||||
end
|
||||
|
||||
# GENSERVER STUFF
|
||||
|
||||
def handle_call(:logged_in, _from, state) do
|
||||
# Tear down hostapd here
|
||||
{:reply, :ok, %{state | logging_in: false}}
|
||||
end
|
||||
|
||||
|
@ -161,9 +162,9 @@ defmodule Farmbot.System.NervesCommon.Network do
|
|||
end
|
||||
{:reply, :ok, Map.delete(state, interface)}
|
||||
else
|
||||
:ok = Registry.unregister(Nerves.NetworkInterface, interface)
|
||||
:ok = Registry.unregister(Nerves.Udhcpc, interface)
|
||||
:ok = Registry.unregister(Nerves.WpaSupplicant, interface)
|
||||
:ok = Registry.unregister(NetworkInterface, interface)
|
||||
:ok = Registry.unregister(Udhcpc, interface)
|
||||
:ok = Registry.unregister(WpaSupplicant, interface)
|
||||
Logger.warn ">> cant stop: #{interface}"
|
||||
{:reply, {:error, :not_implemented}, state}
|
||||
end
|
||||
|
@ -172,33 +173,50 @@ defmodule Farmbot.System.NervesCommon.Network do
|
|||
end
|
||||
|
||||
def handle_cast({:start_interface, interface, settings, pid}, state) do
|
||||
{:ok, _} = Registry.register(Nerves.NetworkInterface, interface, [])
|
||||
{:ok, _} = Registry.register(Nerves.Udhcpc, interface, [])
|
||||
{:ok, _} = Registry.register(Nerves.WpaSupplicant, interface, [])
|
||||
{:ok, _} = Registry.register(NetworkInterface, interface, [])
|
||||
{:ok, _} = Registry.register(Udhcpc, interface, [])
|
||||
{:ok, _} = Registry.register(WpaSupplicant, interface, [])
|
||||
{:noreply, Map.put(state, interface, {settings, pid})}
|
||||
end
|
||||
|
||||
def handle_info({Nerves.Udhcpc, :bound, %{ifname: interface, ipv4_address: ip}}, state) do
|
||||
if state.logging_in do
|
||||
{:noreply, state}
|
||||
else
|
||||
that = self()
|
||||
spawn fn() ->
|
||||
def handle_info({:ssdp_timer, ip, uuid} = msg, state) do
|
||||
fields = ssdp_fields(ip)
|
||||
{:ok, _} = SSDPServer.publish "uuid:#{uuid}", "nerves:farmbot", fields
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
Farmbot.System.Network.on_connect(state.context, fn() ->
|
||||
def handle_info(
|
||||
{Udhcpc, :bound, %{ifname: interface, ipv4_address: ip}},
|
||||
%{logging_in: true} = state
|
||||
) do
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info(
|
||||
{Udhcpc, :bound, %{ifname: interface, ipv4_address: ip}},
|
||||
state
|
||||
) do
|
||||
that = self()
|
||||
spawn fn() ->
|
||||
Farmbot.System.Network.on_connect(state.context,
|
||||
# before callback
|
||||
fn() ->
|
||||
try do
|
||||
{_, 0} = System.cmd("epmd", ["-daemon"])
|
||||
:net_kernel.start(['farmbot@#{ip}'])
|
||||
rescue
|
||||
_ ->
|
||||
Logger.warn "could not start epmd or net_kernel"
|
||||
debug_log "could not start epmd or net_kernel"
|
||||
:ok
|
||||
end
|
||||
Logger.info ">> is waiting for linux and network and what not."
|
||||
Process.sleep(5000) # ye old race linux condidtion
|
||||
uuid = Nerves.Lib.UUID.generate()
|
||||
send(that, {:ssdp_timer, ip, uuid})
|
||||
GenServer.call(that, :logged_in)
|
||||
end,
|
||||
|
||||
# after callback
|
||||
fn(token) ->
|
||||
for {key, value} <- state do
|
||||
if match?({%{"default" => "hostapd"}, _}, value) do
|
||||
|
@ -208,12 +226,11 @@ defmodule Farmbot.System.NervesCommon.Network do
|
|||
end
|
||||
end)
|
||||
|
||||
end
|
||||
{:noreply, %{state | logging_in: true}}
|
||||
end
|
||||
{:noreply, %{state | logging_in: true}}
|
||||
end
|
||||
|
||||
def handle_info({Nerves.WpaSupplicant, {:error, :psk, :FAIL}, %{ifname: _iface}}, state) do
|
||||
def handle_info({WpaSupplicant, {:error, :psk, :FAIL}, %{ifname: _iface}}, state) do
|
||||
Farmbot.System.factory_reset("""
|
||||
I could not authenticate with the access point. This could be a bad
|
||||
password, or an unsupported network type.
|
||||
|
@ -221,7 +238,10 @@ defmodule Farmbot.System.NervesCommon.Network do
|
|||
{:stop, :factory_reset, state}
|
||||
end
|
||||
|
||||
def handle_info({Nerves.WpaSupplicant, _event, %{ifname: _iface}}, %{retries: retries} = state) when retries > 5 do
|
||||
def handle_info(
|
||||
{WpaSupplicant, _event, %{ifname: _iface}},
|
||||
%{retries: retries} = state
|
||||
) when retries > 5 do
|
||||
Farmbot.System.factory_reset("""
|
||||
I could not find the wifi access point. Check that it was inputted correctly.
|
||||
I tried #{retries} times and still found nothing. Maybe I'm not close enough to the access point?
|
||||
|
@ -229,7 +249,10 @@ defmodule Farmbot.System.NervesCommon.Network do
|
|||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_info({Nerves.WpaSupplicant, event, %{ifname: _iface}}, state) when is_atom(event) do
|
||||
def handle_info(
|
||||
{WpaSupplicant, event, %{ifname: _iface}},
|
||||
state
|
||||
) when is_atom(event) do
|
||||
event = event |> Atom.to_string
|
||||
wrong_key? = event |> String.contains?("reason=WRONG_KEY")
|
||||
not_found? = event |> String.contains?("CTRL-EVENT-NETWORK-NOT-FOUND")
|
||||
|
@ -254,6 +277,15 @@ defmodule Farmbot.System.NervesCommon.Network do
|
|||
:ok
|
||||
end
|
||||
|
||||
defp ssdp_fields(ip) do
|
||||
[
|
||||
location: "http://#{ip}/ssdp",
|
||||
server: "Farmbot",
|
||||
node: node(),
|
||||
"cache-control": "max-age=1800"
|
||||
]
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
defmodule Mix.Tasks.Farmbot.Discover do
|
||||
@moduledoc """
|
||||
Usage: mix farmbot.discover [OPTS]
|
||||
## OPTS
|
||||
* `--print` - print the output. (Optional)
|
||||
* `--format=FORMAT` - format to print. (Optional, ignored if not printing.)
|
||||
|
||||
## FORMAT
|
||||
* `json` - print as json.
|
||||
* `human` - print in human readable form. (default)
|
||||
|
||||
## Examples
|
||||
```
|
||||
$ mix farmbot.discover --print
|
||||
%{"cache-control": "max-age=1800", host: "192.168.29.103",
|
||||
location: "http://192.168.29.103/ssdp", node: "farmbot@nerves-7e4b",
|
||||
server: "Farmbot", service_name: "uuid:4d6ac078-0e4a-4a7a-b6cf-e951e5c959c5",
|
||||
st: "nerves:farmbot"}
|
||||
```
|
||||
|
||||
```
|
||||
$ mix farmbot.discover --print --format json
|
||||
[{"st":"nerves:farmbot","service_name":"uuid:4d6ac078-0e4a-4a7a-b6cf-e951e5c959c5","server":"Farmbot","node":"farmbot@nerves-7e4b","location":"http://192.168.29.103/ssdp","host":"192.168.29.103","cache-control":"max-age=1800"}]
|
||||
```
|
||||
"""
|
||||
use Mix.Task
|
||||
@shortdoc """
|
||||
Discovers farmbots on the network via ssdp.
|
||||
"""
|
||||
|
||||
def run(opts) do
|
||||
devices = Nerves.SSDPClient.discover()
|
||||
|> Enum.filter(fn({_, device}) -> device.st == "nerves:farmbot" end)
|
||||
|> Enum.map(fn({sn, device}) -> Map.put(device, :service_name, sn) end)
|
||||
|
||||
switches = [print: :boolean, format: :string]
|
||||
{kws, _, _} = OptionParser.parse(opts, switches: switches)
|
||||
should_print? = Keyword.get(kws, :print, false)
|
||||
if should_print? do
|
||||
format = Keyword.get(kws, :format, "human")
|
||||
do_print(devices, format)
|
||||
end
|
||||
|
||||
devices
|
||||
end
|
||||
|
||||
defp do_print(devices, "json") do
|
||||
IO.puts Poison.encode!(devices)
|
||||
end
|
||||
|
||||
defp do_print([device | rest], format) do
|
||||
print_device(device)
|
||||
do_print(rest, format)
|
||||
end
|
||||
|
||||
defp do_print([], _), do: :ok
|
||||
|
||||
defp print_device(device) do
|
||||
IO.inspect device
|
||||
end
|
||||
end
|
|
@ -7,8 +7,11 @@ defmodule Mix.Tasks.Farmbot.Upload do
|
|||
otp_app = Mix.Project.config[:app]
|
||||
target = Mix.Project.config[:target]
|
||||
|
||||
{keywords, [ip_address], _} =
|
||||
opts |> OptionParser.parse(switches: [signed: :boolean])
|
||||
{keywords, ip_address} =
|
||||
case opts |> OptionParser.parse(switches: [signed: :boolean]) do
|
||||
{keywords, [ip_address], _} -> {keywords, ip_address}
|
||||
{keywords, [], _} -> {keywords, try_discover()}
|
||||
end
|
||||
|
||||
signed_bool = Keyword.get(keywords, :signed, false)
|
||||
file_name = Path.join(["images", "#{Mix.env()}", "#{target}", find_file_name(otp_app, signed_bool)])
|
||||
|
@ -51,4 +54,17 @@ defmodule Mix.Tasks.Farmbot.Upload do
|
|||
|
||||
defp find_file_name(otp_app, true), do: "#{otp_app}-signed.fw"
|
||||
defp find_file_name(otp_app, false), do: "#{otp_app}.fw"
|
||||
|
||||
defp try_discover do
|
||||
devices = Mix.Tasks.Farmbot.Discover.run([])
|
||||
case devices do
|
||||
[device] -> device.host
|
||||
[_device | _more] -> do_raise("detected more than one farmbot.")
|
||||
[] -> do_raise("could not detect farmbot.")
|
||||
end
|
||||
end
|
||||
|
||||
defp do_raise(msg) do
|
||||
Mix.raise "#{msg} Please supply the ip address of your bot."
|
||||
end
|
||||
end
|
|
@ -1,74 +0,0 @@
|
|||
defmodule Mix.Tasks.Cs.New do
|
||||
@moduledoc false
|
||||
use Mix.Task
|
||||
@shortdoc "Creates a new celery script command"
|
||||
def run([new_cs]) do
|
||||
IO.puts "Defining new Celery Script command: #{new_cs}"
|
||||
module_string =
|
||||
new_cs
|
||||
|> Macro.camelize
|
||||
|> build_module
|
||||
|
||||
module_test_string =
|
||||
new_cs
|
||||
|> Macro.camelize
|
||||
|> build_test_module
|
||||
|
||||
new_cs_path = "lib/farmbot/celery_script/commands/#{new_cs}.ex"
|
||||
new_cs_test_path = "test/farmbot/celery_script/commands/#{new_cs}_test.exs"
|
||||
if File.exists?(new_cs_path) do
|
||||
Mix.raise("#{new_cs} already exists!!!!")
|
||||
end
|
||||
:ok = File.write new_cs_path, module_string
|
||||
:ok = File.write new_cs_test_path, module_test_string
|
||||
end
|
||||
|
||||
def run(_), do: Mix.raise("Unexpected args!")
|
||||
|
||||
defp build_test_module(camelized_kind) do
|
||||
"""
|
||||
defmodule Farmbot.CeleryScript.Command.#{camelized_kind}Test do
|
||||
use ExUnit.Case
|
||||
alias Farmbot.CeleryScript.Ast
|
||||
alias Farmbot.CeleryScript.Command
|
||||
|
||||
test "the truth" do
|
||||
# TODO(Connor) fix the truth in #{camelized_kind}
|
||||
assert true == false
|
||||
end
|
||||
end
|
||||
"""
|
||||
end
|
||||
|
||||
defp build_module(camelized_kind) do
|
||||
"""
|
||||
defmodule Farmbot.CeleryScript.Command.#{camelized_kind} do
|
||||
#{build_module_doc(camelized_kind)}
|
||||
|
||||
alias Farmbot.CeleryScript.Ast
|
||||
alias Farmbot.CeleryScript.Command
|
||||
require Logger
|
||||
@behaviour Command
|
||||
|
||||
#{build_run_doc(camelized_kind) |> String.trim}
|
||||
@spec run(%{}, []) :: no_return
|
||||
def run(%{}, []) do
|
||||
#TODO Finish #{camelized_kind}
|
||||
end
|
||||
|
||||
end
|
||||
"""
|
||||
end
|
||||
|
||||
defp build_run_doc(camelized_kind) do
|
||||
~s(@doc ~s"""
|
||||
#{camelized_kind}
|
||||
args: %{},
|
||||
body: []\n """)
|
||||
end
|
||||
|
||||
defp build_module_doc(camelized_kind) do
|
||||
~s(@moduledoc """
|
||||
#{camelized_kind}\n """)
|
||||
end
|
||||
end
|
33
mix.exs
33
mix.exs
|
@ -69,14 +69,15 @@ defmodule Farmbot.Mixfile do
|
|||
end
|
||||
|
||||
def application do
|
||||
[mod: {Farmbot, []},
|
||||
[mod: {Farmbot, []},
|
||||
applications: applications() ++ target_applications(@target) ++ env_applications(Mix.env()),
|
||||
included_applications: [
|
||||
:gen_mqtt,
|
||||
:nerves_ssdp_client,
|
||||
:ex_json_schema,
|
||||
:fs,
|
||||
:ex_rollbar,
|
||||
:ssh
|
||||
:gen_mqtt,
|
||||
:ssh,
|
||||
:fs,
|
||||
] ++ included_apps(Mix.env)]
|
||||
end
|
||||
|
||||
|
@ -90,6 +91,7 @@ defmodule Farmbot.Mixfile do
|
|||
:nerves_uart,
|
||||
:nerves_hal,
|
||||
:nerves_runtime,
|
||||
:nerves_ssdp_server,
|
||||
:poison,
|
||||
:rsa,
|
||||
:nerves_lib,
|
||||
|
@ -105,16 +107,13 @@ defmodule Farmbot.Mixfile do
|
|||
:inets,
|
||||
:redix,
|
||||
:eex,
|
||||
# :system_registry
|
||||
]
|
||||
end
|
||||
|
||||
defp target_applications("host"), do: []
|
||||
defp target_applications(_system), do: [
|
||||
:nerves_interim_wifi,
|
||||
# :nerves_firmware_http,
|
||||
:nerves_firmware,
|
||||
:nerves_ssdp_server
|
||||
]
|
||||
|
||||
defp env_applications(:prod), do: []
|
||||
|
@ -127,7 +126,6 @@ defmodule Farmbot.Mixfile do
|
|||
[
|
||||
|
||||
{:nerves, "0.5.1"},
|
||||
# {:nerves_runtime, "~> 0.1.1"},
|
||||
{:nerves_runtime, github: "nerves-project/nerves_runtime", override: true},
|
||||
{:nerves_hal, github: "LeToteTeam/nerves_hal"},
|
||||
|
||||
|
@ -158,11 +156,11 @@ defmodule Farmbot.Mixfile do
|
|||
# Log to syslog
|
||||
{:ex_syslogger, "~> 1.3.3", only: :prod},
|
||||
{:ex_rollbar, "0.1.2"},
|
||||
# {:rollbax, "~> 0.6"},
|
||||
# {:ex_rollbar, path: "../../ex_rollbar"},
|
||||
|
||||
# Other stuff
|
||||
{:gen_stage, "0.11.0"},
|
||||
{:gen_stage, "0.11.0" },
|
||||
{:nerves_ssdp_server, "~> 0.2.2"},
|
||||
{:nerves_ssdp_client, "~> 0.1.0"},
|
||||
|
||||
# Test/Dev only
|
||||
{:credo, "~> 0.8", only: [:dev, :test], runtime: false},
|
||||
|
@ -180,13 +178,10 @@ defmodule Farmbot.Mixfile do
|
|||
{:cowboy, "~> 1.1"},
|
||||
{:ex_webpack, "~> 0.1.1", runtime: false, warn_missing: false},
|
||||
|
||||
# {:farmbot_simulator, "~> 0.1.3", only: [:test, :dev]},
|
||||
# {:farmbot_simulator, path: "../farmbot_simulator", only: [:test, :dev]},
|
||||
{:farmbot_simulator, github: "farmbot-labs/farmbot_simulator", only: [:test, :dev]},
|
||||
|
||||
{:tzdata, "~> 0.1.201601", override: true},
|
||||
{:fs, "~> 0.9.1"},
|
||||
# {:system_registry, "~> 0.1"}
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -232,18 +227,8 @@ defmodule Farmbot.Mixfile do
|
|||
if File.exists?("nerves/nerves_system_#{sys}"),
|
||||
do: [
|
||||
{:"nerves_system_#{sys}", warn_missing: false, path: "nerves/nerves_system_#{sys}"},
|
||||
# {:nerves_interim_wifi, "~> 0.2.0"},
|
||||
# {:nerves_interim_wifi, path: "../nerves_interim_wifi"},
|
||||
{:nerves_interim_wifi, github: "nerves-project/nerves_interim_wifi"},
|
||||
|
||||
# {:nerves_firmware_http, "~> 0.3.1"},
|
||||
|
||||
# {:nerves_firmware, "~> 0.3"},
|
||||
# {:nerves_firmware, path: "../nerves_firmware", 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"},
|
||||
],
|
||||
else: Mix.raise("There is no existing system package for #{sys}")
|
||||
end
|
||||
|
|
3
mix.lock
3
mix.lock
|
@ -47,7 +47,8 @@
|
|||
"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, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"nerves_runtime": {:git, "https://github.com/nerves-project/nerves_runtime.git", "06da20aed5e391f0c885d69d2155126ecc4ca527", []},
|
||||
"nerves_ssdp_server": {:hex, :nerves_ssdp_server, "0.2.1", "2d010552023fc1a724e8cb5c92479a58552976e6f805b6dbf09babd31f923b8f", [:mix], [], "hexpm"},
|
||||
"nerves_ssdp_client": {:hex, :nerves_ssdp_client, "0.1.3", "b09dc7433b2536399885a5f0fcd1fb58283115b075f9485f86fa713547d404dc", [:mix], [], "hexpm"},
|
||||
"nerves_ssdp_server": {:hex, :nerves_ssdp_server, "0.2.2", "30988caf00a175285a238fff83feccb1f662b9ac4fcc68805b2e9cc01dcf1ad8", [:mix], [], "hexpm"},
|
||||
"nerves_system_br": {:hex, :nerves_system_br, "0.9.4", "5096a9dfec49d4663ccb94c4a4fe45885303fbf31108f7e9400369bdec94b5e7", [:mix], [], "hexpm"},
|
||||
"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]}]},
|
||||
|
|
Loading…
Reference in New Issue