2019-03-05 12:35:09 -07:00
|
|
|
defmodule FarmbotOS.Platform.Target.NervesHubClient do
|
2018-11-21 17:20:11 -07:00
|
|
|
@moduledoc """
|
|
|
|
Client that decides when an update should be done.
|
|
|
|
"""
|
|
|
|
|
|
|
|
use GenServer
|
2019-03-05 12:35:09 -07:00
|
|
|
require FarmbotCore.Logger
|
|
|
|
alias FarmbotCore.{Asset, BotState, BotState.JobProgress.Percent, Project}
|
2018-12-05 11:30:36 -07:00
|
|
|
|
2018-11-21 17:20:11 -07:00
|
|
|
@behaviour NervesHub.Client
|
2019-03-05 12:35:09 -07:00
|
|
|
@behaviour FarmbotOS.NervesHub
|
2018-12-14 11:05:29 -07:00
|
|
|
|
2019-03-05 12:35:09 -07:00
|
|
|
@current_version Project.version()
|
|
|
|
@data_path FarmbotOS.FileSystem.data_path()
|
2018-12-14 11:05:29 -07:00
|
|
|
@data_path || Mix.raise("Please configure data_path in application env")
|
|
|
|
|
2019-03-07 14:30:19 -07:00
|
|
|
def serial_number(:rpi0), do: serial_number("rpi")
|
|
|
|
def serial_number(:rpi3), do: serial_number("rpi")
|
2018-11-21 17:20:11 -07:00
|
|
|
|
|
|
|
def serial_number(plat) do
|
2018-11-23 11:25:14 -07:00
|
|
|
:os.cmd(
|
|
|
|
'/usr/bin/boardid -b uboot_env -u nerves_serial_number -b uboot_env -u serial_number -b #{
|
|
|
|
plat
|
|
|
|
}'
|
|
|
|
)
|
2018-11-21 17:20:11 -07:00
|
|
|
|> to_string()
|
|
|
|
|> String.trim()
|
|
|
|
end
|
|
|
|
|
2018-12-14 13:55:03 -07:00
|
|
|
def uuid, do: Nerves.Runtime.KV.get_active("nerves_fw_uuid")
|
|
|
|
|
2019-03-05 12:35:09 -07:00
|
|
|
def serial_number, do: serial_number(Project.target())
|
2018-11-21 17:20:11 -07:00
|
|
|
|
|
|
|
def connect do
|
2019-04-18 13:55:49 -06:00
|
|
|
config = config()
|
2018-11-23 11:25:14 -07:00
|
|
|
|
2019-04-18 13:55:49 -06:00
|
|
|
if nil in config do
|
|
|
|
{:error, "No OTA Certs: #{inspect(config)}"}
|
|
|
|
else
|
|
|
|
FarmbotCore.Logger.debug(3, "Starting OTA Service")
|
|
|
|
# NervesHub replaces it's own env on startup. Reset it.
|
2018-12-17 10:53:39 -07:00
|
|
|
|
2019-04-18 13:55:49 -06:00
|
|
|
supervisor = FarmbotOS
|
|
|
|
# Stop Nerves Hub if it is running.
|
|
|
|
_ = Supervisor.terminate_child(supervisor, NervesHub.Supervisor)
|
|
|
|
_ = Supervisor.delete_child(supervisor, NervesHub.Supervisor)
|
2018-12-17 10:53:39 -07:00
|
|
|
|
2019-04-18 13:55:49 -06:00
|
|
|
# Cause NervesRuntime.KV to restart.
|
|
|
|
_ = GenServer.stop(Nerves.Runtime.KV)
|
2018-12-17 10:53:39 -07:00
|
|
|
|
2019-04-18 13:55:49 -06:00
|
|
|
# Wait for a few seconds for good luck.
|
|
|
|
Process.sleep(1000)
|
|
|
|
|
|
|
|
# Start the connection again.
|
|
|
|
{:ok, _pid} = Supervisor.start_child(supervisor, NervesHub.Supervisor)
|
|
|
|
FarmbotCore.Logger.debug(3, "OTA Service started")
|
|
|
|
:ok
|
|
|
|
end
|
2018-11-21 17:20:11 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
def provision(serial) do
|
|
|
|
Nerves.Runtime.KV.UBootEnv.put("nerves_serial_number", serial)
|
|
|
|
Nerves.Runtime.KV.UBootEnv.put("nerves_fw_serial_number", serial)
|
|
|
|
end
|
|
|
|
|
|
|
|
def configure_certs(cert, key) do
|
2019-04-18 13:55:49 -06:00
|
|
|
Nerves.Runtime.KV.UBootEnv.put("nerves_hub_cert", cert) |> IO.inspect()
|
|
|
|
Nerves.Runtime.KV.UBootEnv.put("nerves_hub_key", key) |> IO.inspect()
|
2018-11-21 17:20:11 -07:00
|
|
|
:ok
|
|
|
|
end
|
|
|
|
|
|
|
|
def deconfigure() do
|
|
|
|
Nerves.Runtime.KV.UBootEnv.put("nerves_hub_cert", "")
|
|
|
|
Nerves.Runtime.KV.UBootEnv.put("nerves_hub_key", "")
|
|
|
|
Nerves.Runtime.KV.UBootEnv.put("nerves_serial_number", "")
|
|
|
|
Nerves.Runtime.KV.UBootEnv.put("nerves_fw_serial_number", "")
|
|
|
|
:ok
|
|
|
|
end
|
|
|
|
|
|
|
|
def config() do
|
|
|
|
[
|
|
|
|
Nerves.Runtime.KV.get("nerves_fw_serial_number"),
|
|
|
|
Nerves.Runtime.KV.get("nerves_hub_cert"),
|
2018-11-23 11:25:14 -07:00
|
|
|
Nerves.Runtime.KV.get("nerves_hub_key")
|
2018-11-21 17:20:11 -07:00
|
|
|
]
|
2018-11-23 11:25:14 -07:00
|
|
|
|> Enum.map(fn val ->
|
2018-12-04 12:01:33 -07:00
|
|
|
if val == "", do: nil, else: val
|
|
|
|
end)
|
2018-11-21 17:20:11 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
def check_update do
|
|
|
|
case GenServer.call(__MODULE__, :check_update) do
|
|
|
|
# If updates were disabled, and an update is queued
|
2018-12-05 11:30:36 -07:00
|
|
|
{:ignore, _url} ->
|
2019-03-05 12:35:09 -07:00
|
|
|
FarmbotCore.Logger.info(1, "Applying OTA update")
|
2018-12-05 11:30:36 -07:00
|
|
|
NervesHub.update()
|
2018-11-23 11:25:14 -07:00
|
|
|
|
2018-12-05 11:30:36 -07:00
|
|
|
_ ->
|
2019-03-05 12:35:09 -07:00
|
|
|
FarmbotCore.Logger.debug(1, "No update cached. Checking for tag changes.")
|
2018-11-23 11:25:14 -07:00
|
|
|
|
2018-12-05 11:30:36 -07:00
|
|
|
case NervesHub.HTTPClient.update() do
|
|
|
|
{:ok, %{"data" => %{"update_available" => false}}} ->
|
2019-01-08 09:48:15 -07:00
|
|
|
nil
|
2018-12-10 10:35:47 -07:00
|
|
|
|
2018-12-05 11:30:36 -07:00
|
|
|
_ ->
|
2019-03-05 12:35:09 -07:00
|
|
|
FarmbotCore.Logger.info(1, "Applying OTA update")
|
2018-12-05 11:30:36 -07:00
|
|
|
NervesHub.update()
|
|
|
|
end
|
2018-11-21 17:20:11 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Callback for NervesHub.Client
|
|
|
|
def update_available(args) do
|
|
|
|
GenServer.call(__MODULE__, {:update_available, args}, :infinity)
|
|
|
|
end
|
|
|
|
|
2018-12-05 11:30:36 -07:00
|
|
|
def handle_error(args) do
|
2019-03-05 12:35:09 -07:00
|
|
|
FarmbotCore.Logger.error(1, "OTA failed to download: #{inspect(args)}")
|
2019-04-17 12:46:53 -06:00
|
|
|
prog = %Percent{status: "error"}
|
2018-11-23 11:25:14 -07:00
|
|
|
|
2019-03-05 12:35:09 -07:00
|
|
|
if Process.whereis(BotState) do
|
|
|
|
BotState.set_job_progress("FBOS_OTA", prog)
|
2018-12-05 11:30:36 -07:00
|
|
|
end
|
2018-11-23 11:25:14 -07:00
|
|
|
|
2018-12-05 11:30:36 -07:00
|
|
|
:ok
|
|
|
|
end
|
|
|
|
|
2018-12-06 11:50:19 -07:00
|
|
|
def handle_fwup_message({:ok, _, _info}) do
|
2019-03-05 12:35:09 -07:00
|
|
|
FarmbotCore.Logger.success(1, "OTA Complete Going down for reboot")
|
2019-04-17 12:46:53 -06:00
|
|
|
prog = %Percent{percent: 100, status: "complete"}
|
2018-11-23 11:25:14 -07:00
|
|
|
|
2019-03-05 12:35:09 -07:00
|
|
|
if Process.whereis(BotState) do
|
|
|
|
BotState.set_job_progress("FBOS_OTA", prog)
|
2018-12-05 11:30:36 -07:00
|
|
|
end
|
2018-11-23 11:25:14 -07:00
|
|
|
|
2018-12-05 11:30:36 -07:00
|
|
|
:ok
|
|
|
|
end
|
|
|
|
|
|
|
|
def handle_fwup_message({:progress, 100}) do
|
2019-03-05 12:35:09 -07:00
|
|
|
FarmbotCore.Logger.success(1, "OTA Complete. Going down for reboot")
|
2019-04-17 12:46:53 -06:00
|
|
|
prog = %Percent{percent: 100, status: "complete"}
|
2018-11-23 11:25:14 -07:00
|
|
|
|
2019-03-05 12:35:09 -07:00
|
|
|
if Process.whereis(BotState) do
|
|
|
|
BotState.set_job_progress("FBOS_OTA", prog)
|
2018-12-05 11:30:36 -07:00
|
|
|
end
|
2018-11-23 11:25:14 -07:00
|
|
|
|
2018-12-05 11:30:36 -07:00
|
|
|
:ok
|
|
|
|
end
|
|
|
|
|
2018-11-28 15:42:38 -07:00
|
|
|
def handle_fwup_message({:progress, percent}) when rem(percent, 5) == 0 do
|
2019-03-05 12:35:09 -07:00
|
|
|
prog = %Percent{percent: percent}
|
2018-11-23 11:25:14 -07:00
|
|
|
|
2019-03-05 12:35:09 -07:00
|
|
|
if Process.whereis(BotState) do
|
|
|
|
BotState.set_job_progress("FBOS_OTA", prog)
|
2018-11-21 17:20:11 -07:00
|
|
|
end
|
2018-11-23 11:25:14 -07:00
|
|
|
|
2018-11-21 17:20:11 -07:00
|
|
|
:ok
|
|
|
|
end
|
|
|
|
|
|
|
|
def handle_fwup_message({:error, _, reason}) do
|
2019-03-05 12:35:09 -07:00
|
|
|
FarmbotCore.Logger.error(1, "OTA failed to apply: #{inspect(reason)}")
|
|
|
|
prog = %Percent{status: :error}
|
2018-11-23 11:25:14 -07:00
|
|
|
|
2019-03-05 12:35:09 -07:00
|
|
|
if Process.whereis(BotState) do
|
|
|
|
BotState.set_job_progress("FBOS_OTA", prog)
|
2018-12-05 11:30:36 -07:00
|
|
|
end
|
2018-11-23 11:25:14 -07:00
|
|
|
|
2018-11-21 17:20:11 -07:00
|
|
|
:ok
|
|
|
|
end
|
|
|
|
|
|
|
|
def handle_fwup_message(_) do
|
|
|
|
:ok
|
|
|
|
end
|
|
|
|
|
2019-04-17 12:19:28 -06:00
|
|
|
def start_link(args) do
|
|
|
|
GenServer.start_link(__MODULE__, args, name: __MODULE__)
|
2018-11-21 17:20:11 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
def init([]) do
|
2018-12-14 11:05:29 -07:00
|
|
|
maybe_post_update()
|
2018-11-21 17:20:11 -07:00
|
|
|
{:ok, nil}
|
|
|
|
end
|
|
|
|
|
|
|
|
def handle_call({:update_available, %{"firmware_url" => url}}, _, _state) do
|
2019-03-05 12:35:09 -07:00
|
|
|
if Process.whereis(BotState) do
|
|
|
|
BotState.set_update_available(true)
|
2018-11-21 17:20:11 -07:00
|
|
|
end
|
2018-11-23 11:25:14 -07:00
|
|
|
|
2019-03-05 12:35:09 -07:00
|
|
|
case Asset.fbos_config(:os_auto_update) do
|
2018-12-05 11:30:36 -07:00
|
|
|
true ->
|
2019-03-05 12:35:09 -07:00
|
|
|
FarmbotCore.Logger.success(1, "Applying OTA update")
|
2018-12-05 11:30:36 -07:00
|
|
|
{:reply, :apply, {:apply, url}}
|
2018-11-23 11:25:14 -07:00
|
|
|
|
2018-12-05 11:30:36 -07:00
|
|
|
false ->
|
2019-03-05 12:35:09 -07:00
|
|
|
FarmbotCore.Logger.info(1, "New Farmbot OS is available!")
|
2018-12-05 11:30:36 -07:00
|
|
|
{:reply, :ignore, {:ignore, url}}
|
2018-11-21 17:20:11 -07:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def handle_call(:check_update, _from, state) do
|
|
|
|
{:reply, state, state}
|
|
|
|
end
|
2018-12-14 11:05:29 -07:00
|
|
|
|
|
|
|
defp maybe_post_update do
|
|
|
|
case File.read(update_file()) do
|
2018-11-23 11:25:14 -07:00
|
|
|
{:ok, @current_version} ->
|
|
|
|
:ok
|
|
|
|
|
2018-12-14 11:05:29 -07:00
|
|
|
{:ok, old_version} ->
|
2019-03-05 12:35:09 -07:00
|
|
|
FarmbotCore.Logger.info(
|
|
|
|
1,
|
|
|
|
"Updating FarmbotOS from #{old_version} to #{@current_version}"
|
|
|
|
)
|
|
|
|
|
2018-12-14 11:05:29 -07:00
|
|
|
do_post_update()
|
|
|
|
|
|
|
|
{:error, :enoent} ->
|
2019-03-05 12:35:09 -07:00
|
|
|
FarmbotCore.Logger.info(1, "Setting up FarmbotOS #{@current_version}")
|
2018-11-23 11:25:14 -07:00
|
|
|
|
|
|
|
{:error, err} ->
|
|
|
|
raise err
|
2018-12-14 11:05:29 -07:00
|
|
|
end
|
2018-11-23 11:25:14 -07:00
|
|
|
|
2018-12-14 11:05:29 -07:00
|
|
|
before_update()
|
|
|
|
end
|
|
|
|
|
|
|
|
defp do_post_update do
|
2019-04-17 12:19:28 -06:00
|
|
|
IO.warn("flash firmware at this point i guess?")
|
|
|
|
File.rm(update_file())
|
2018-12-14 11:05:29 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
defp before_update, do: File.write!(update_file(), @current_version)
|
|
|
|
|
|
|
|
defp update_file, do: Path.join(@data_path, "update")
|
2018-11-21 17:20:11 -07:00
|
|
|
end
|