Skip to content

Commit a259c49

Browse files
authored
Merge pull request #49 from grisp/sylane/system-info
Add more information and rename partition_state request to system_info
2 parents b911f50 + 39be342 commit a259c49

File tree

5 files changed

+255
-116
lines changed

5 files changed

+255
-116
lines changed

docs/grisp_connect_api.md

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,42 +20,41 @@ We use [jsonrpc](https://www.jsonrpc.org) 2.0 between frontend and backend.
2020

2121
</p>
2222
</details>
23-
<details><summary><i>Get - partition_state</i></summary>
23+
<details><summary><i>Get - system_info</i></summary>
2424
<p>
2525

26-
Retrieves the current state of the system’s partition, indicating whether the
27-
system requires a reboot, needs validation, or is running an old partition
28-
with no updates pending. This can be used to check if the system is running a
29-
valid system, or has an update pending.
26+
Retrieves the current state of the system. It returns the currently running
27+
release name and version and if update is enabled.
3028

3129
**`params`:**
3230
| key (required *) | value | description |
3331
| ----------------- | -------- | ------------------- |
34-
| `"type"` * | string | `"partition_state"` |
32+
| `"type"` * | string | `"system_info"` |
3533

3634
**`result`**: JSON Object
3735

38-
| key | value | type | description |
39-
|-----------------|-----------|----------|----------------------------------------------------|
40-
| state | string | required | `"old"`, `"old_no_update"`, `"new"`, `"unknown"` |
41-
| message | string | required | Message describing the current state of the system |
42-
| action_required | boolean | required | Indicates whether any action is required (e.g., reboot, validation). |
36+
| key | value | type | description |
37+
|-----------------|----------------|----------|------------------------------------------------------------------|
38+
| relname | string or null | required | The name of the release running currently on the device |
39+
| relvsn | string or null | required | The version of the release running currently on the device |
40+
| update_enabled | boolean | required | If updating is enbaled on the device |
41+
| boot_source | map | optional | `{"type": "system", "id": ID}` or `{"type": "removable"}` |
42+
| update_status | string | optional | `"ready"`, `"updating"`, `"failed"`, or `"updated"` |
43+
| update_progress | integer | optional | The progress as a percentage |
44+
| update_message | string | optional | Message describing the current state of the system |
45+
| action_required | boolean | optional | `null`, `"reboot"`, `"remove_sdcard_and_reboot"` or `"validate"` |
4346

44-
Meaning of the state:
47+
Meaning of the status:
4548

4649
| key | description |
4750
|-------------------|--------------------------------------------------------------------------------------------|
48-
| `"new"` | The system has booted into a new partition. Validation is required to finalize the update. |
49-
| `"old"` | Current partition is old. A reboot is required to load the new partition. |
50-
| `"old_no_update"` | There is no update pending. The system is running the old partition. |
51-
| `"unknown"` | The current partition state does not match any of the previous described states. |
52-
53-
**`error`**:
54-
55-
| Error Content | When it Happens |
56-
| ----------------------------------------------------| -------------------------------------- |
57-
| `{code: -10, message: "grisp_updater_unavailable"}` | Grisp updater app is not running |
51+
| `"ready"` | The system is ready for initiating an update |
52+
| `"updating"` | The system is in the process of updating |
53+
| `"failed"` | The update failed, but a new update can be initiated |
54+
| `"updated"` | The update succeed, but actions are required like "reboot" or "validate" |
5855

56+
</p>
57+
</details>
5958
<details><summary><i>Post - Start an update</i></summary>
6059
<p>
6160

@@ -99,11 +98,44 @@ This should only be called if the new software is functioning as expected.
9998

10099
| Error Content | When it Happens |
101100
| ----------------------------------------------------| -------------------------------- |
101+
| `{code: -10, message: "grisp_updater_unavailable"}` | Grisp updater app is not running |
102102
| `{code: -13, message: "validate_from_unbooted", data: 0}` | The current partition N cannot be validated |
103103

104104
</p>
105105
</details>
106106

107+
<details><summary><i>Post - Reboot the device</i></summary>
108+
<p>
109+
110+
**`params`:**
111+
| key (required *) | value | description |
112+
| ----------------- | -------- | -------------------------- |
113+
| `"type"` * | string | `"reboot"` |
114+
115+
**`result`**: `"ok"`
116+
117+
</p>
118+
</details>
119+
120+
<details><summary><i>Post - Cancel the current update</i></summary>
121+
<p>
122+
123+
**`params`:**
124+
| key (required *) | value | description |
125+
| ----------------- | -------- | -------------------------- |
126+
| `"type"` * | string | `"cancel"` |
127+
128+
**`result`**: `"ok"`
129+
130+
**`error`**:
131+
132+
| Error Content | When it Happens |
133+
| ----------------------------------------------------| -------------------------------- |
134+
| `{code: -10, message: "grisp_updater_unavailable"}` | Grisp updater app is not running |
135+
136+
</p>
137+
</details>
138+
107139
### Notifications
108140

109141
<details><summary><code>update</code> <code>{"type":"software_update_event"}</code> - notify the current progess of grisp_updater </summary>

src/grisp_connect_api.erl

Lines changed: 20 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -63,25 +63,15 @@ handle_rpc_messages([{error, _Code, _Msg, _Data, _ID} = E | Batch], Replies) ->
6363
handle_rpc_messages(Batch, [handle_response(E)| Replies]);
6464
handle_rpc_messages([{internal_error, _, _} = E | Batch], Replies) ->
6565
?LOG_ERROR("JsonRPC: ~p",[E]),
66-
handle_rpc_messages(Batch,
67-
[grisp_connect_jsonrpc:format_error(E)| Replies]).
66+
handle_rpc_messages(Batch, Replies).
6867

69-
handle_request(?method_get, #{type := <<"partition_state">>} = _Params, ID) ->
70-
Info = get_partition_info(),
71-
Reply = case Info of
72-
#{state := _State,
73-
message := _Msg,
74-
action_required := _ActionRequired} = Response ->
75-
{result, Response, ID};
76-
{error, Reason} ->
77-
ReasonBinary = iolist_to_binary(io_lib:format("~p", [Reason])),
78-
grisp_connect_jsonrpc:format_error({internal_error, ReasonBinary, ID})
79-
end,
80-
{send_response, grisp_connect_jsonrpc:encode(Reply)};
68+
handle_request(?method_get, #{type := <<"system_info">>} = _Params, ID) ->
69+
Info = grisp_connect_updater:system_info(),
70+
{send_response, grisp_connect_jsonrpc:encode({result, Info, ID})};
8171
handle_request(?method_post, #{type := <<"start_update">>} = Params, ID) ->
8272
try
8373
URL = maps:get(url, Params),
84-
Reply = case start_update(URL) of
74+
Reply = case grisp_connect_updater:start_update(URL) of
8575
{error, grisp_updater_unavailable} ->
8676
{error, -10, grisp_updater_unavailable, undefined, ID};
8777
{error, already_updating} ->
@@ -102,7 +92,9 @@ handle_request(?method_post, #{type := <<"start_update">>} = Params, ID) ->
10292
{internal_error, invalid_params, ID})}
10393
end;
10494
handle_request(?method_post, #{type := <<"validate">>}, ID) ->
105-
Reply = case grisp_updater:validate() of
95+
Reply = case grisp_connect_updater:validate() of
96+
{error, grisp_updater_unavailable} ->
97+
{error, -10, grisp_updater_unavailable, undefined, ID};
10698
{error, {validate_from_unbooted, PartitionIndex}} ->
10799
{error, -13, validate_from_unbooted, PartitionIndex, ID};
108100
{error, Reason} ->
@@ -112,7 +104,18 @@ handle_request(?method_post, #{type := <<"validate">>}, ID) ->
112104
{result, ok, ID}
113105
end,
114106
{send_response, grisp_connect_jsonrpc:encode(Reply)};
115-
handle_request(_, _, ID) ->
107+
handle_request(?method_post, #{type := <<"reboot">>}, ID) ->
108+
grisp_connect_client:reboot(),
109+
{send_response, grisp_connect_jsonrpc:encode({result, ok, ID})};
110+
handle_request(?method_post, #{type := <<"cancel">>}, ID) ->
111+
Reply = case grisp_connect_updater:cancel() of
112+
{error, grisp_updater_unavailable} ->
113+
{error, -10, grisp_updater_unavailable, undefined, ID};
114+
ok ->
115+
{result, ok, ID}
116+
end,
117+
{send_response, grisp_connect_jsonrpc:encode(Reply)};
118+
handle_request(_T, _P, ID) ->
116119
Error = {internal_error, method_not_found, ID},
117120
FormattedError = grisp_connect_jsonrpc:format_error(Error),
118121
{send_response, grisp_connect_jsonrpc:encode(FormattedError)}.
@@ -126,79 +129,6 @@ handle_response(Response) ->
126129
end,
127130
{handle_response, ID, Reply}.
128131

129-
start_update(URL) ->
130-
case is_running(grisp_updater) of
131-
true -> grisp_updater:start(URL,
132-
grisp_connect_updater_progress,
133-
#{client => self()}, #{});
134-
false -> {error, grisp_updater_unavailable}
135-
end.
136-
137-
get_partition_info() ->
138-
case is_running(grisp_updater) of
139-
true ->
140-
Info = grisp_updater:info(),
141-
#{boot := Boot, valid := Valid, next := Next} = Info,
142-
ActionRequired = maps:get(action_required, Info, false),
143-
case evaluate_partition_state(Boot, Valid, Next) of
144-
new_boot ->
145-
#{state => <<"new">>,
146-
message => <<"New partition booted, validation required">>,
147-
action_required => ActionRequired};
148-
update_pending ->
149-
#{state => <<"old">>,
150-
message => <<"Reboot required to load new partition">>,
151-
action_required => ActionRequired};
152-
no_update_pending ->
153-
#{state => <<"old_no_update">>,
154-
message => <<"No update pending, running old partition">>,
155-
action_required => ActionRequired};
156-
_ ->
157-
#{state => <<"unknown">>,
158-
message => <<"Unknown partition state">>,
159-
action_required => ActionRequired}
160-
end;
161-
false -> {error, grisp_updater_unavailable}
162-
end.
163-
164-
evaluate_partition_state(BootPartition, ValidPartition, NextPartition) ->
165-
case {BootPartition, ValidPartition, NextPartition} of
166-
% Case 1: Booting from removable media, but system has a pending update
167-
{#{type := removable},
168-
#{type := system, id := ValidId},
169-
#{type := system, id := NextId}}
170-
when ValidId =/= NextId -> update_pending;
171-
% Case 2: Booted from system partition, but a different system partition is pending update
172-
{#{type := system, id := BootId},
173-
#{type := system, id := ValidId},
174-
#{type := system, id := NextId}}
175-
when BootId == ValidId, ValidId =/= NextId -> update_pending;
176-
% Case 3: Booted from a new partition, validation required
177-
{#{type := system, id := BootId},
178-
#{type := system, id := ValidId},
179-
_}
180-
when BootId =/= ValidId -> new_boot;
181-
% Case 4: Booted from removable media, no update pending
182-
{#{type := removable},
183-
#{type := system, id := ValidId},
184-
#{type := system, id := NextId}}
185-
when ValidId == NextId -> no_update_pending;
186-
% Case 5: Booted from system partition, no update pending
187-
{#{type := system, id := BootId},
188-
_,
189-
#{type := system, id := NextId}}
190-
when NextId == BootId -> no_update_pending;
191-
% Default case: Unknown partition state
192-
_ -> unknown_state
193-
end.
194-
195-
is_running(AppName) ->
196-
Apps = application:which_applications(),
197-
case [App || {App, _Desc, _VSN} <- Apps, App =:= AppName] of
198-
[] -> false;
199-
[_] -> true
200-
end.
201-
202132
error_atom(-1) -> device_not_linked;
203133
error_atom(-2) -> token_expired;
204134
error_atom(-3) -> device_already_linked;

src/grisp_connect_client.erl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
-export([connected/0]).
1717
-export([disconnected/0]).
1818
-export([handle_message/1]).
19+
-export([reboot/0]).
1920

2021
-behaviour(gen_statem).
2122
-export([init/1, terminate/3, code_change/4, callback_mode/0]).
@@ -63,6 +64,9 @@ disconnected() ->
6364
handle_message(Payload) ->
6465
gen_statem:cast(?MODULE, {?FUNCTION_NAME, Payload}).
6566

67+
reboot() ->
68+
erlang:send_after(1000, ?MODULE, reboot).
69+
6670
% gen_statem CALLBACKS ---------------------------------------------------------
6771

6872
init([]) ->
@@ -157,11 +161,17 @@ handle_common({call, From}, is_connected, State, _) when State =/= connected ->
157161
handle_common({call, From}, {request, _, _, _}, State, _Data)
158162
when State =/= connected ->
159163
{keep_state_and_data, [{reply, From, {error, disconnected}}]};
164+
handle_common(cast, {notify, _Method, _Type, _Params}, _State, _Data) ->
165+
% We ignore notifications sent while disconnected
166+
keep_state_and_data;
160167
handle_common({timeout, ID}, request, _, #data{requests = Requests} = Data) ->
161168
Caller = maps:get(ID, Requests),
162169
{keep_state,
163170
Data#data{requests = maps:remove(ID, Requests)},
164171
[{reply, Caller, {error, timeout}}]};
172+
handle_common(info, reboot, _, _) ->
173+
init:stop(),
174+
keep_state_and_data;
165175
handle_common(cast, Cast, _, _) ->
166176
error({unexpected_cast, Cast});
167177
handle_common({call, _}, Call, _, _) ->

0 commit comments

Comments
 (0)