Browse Source

unfinished py ota

Thomas Buck 6 months ago
parent
commit
76436adf41
3 changed files with 196 additions and 0 deletions
  1. 187
    0
      python-test/ota.py
  2. 2
    0
      python-test/state_scan.py
  3. 7
    0
      python-test/states.py

+ 187
- 0
python-test/ota.py View File

@@ -0,0 +1,187 @@
1
+#!/usr/bin/env python3
2
+
3
+# Uses the Gitea API to fetch the latest revision of the project from a repo.
4
+#
5
+# Inspired by:
6
+# https://github.com/olivergregorius/micropython_ota
7
+#
8
+# ----------------------------------------------------------------------------
9
+# Copyright (c) 2023 Thomas Buck (thomas@xythobuz.de)
10
+#
11
+# This program is free software: you can redistribute it and/or modify
12
+# it under the terms of the GNU General Public License as published by
13
+# the Free Software Foundation, either version 3 of the License, or
14
+# (at your option) any later version.
15
+#
16
+# This program is distributed in the hope that it will be useful,
17
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
+# GNU General Public License for more details.
20
+#
21
+# See <http://www.gnu.org/licenses/>.
22
+# ----------------------------------------------------------------------------
23
+
24
+import util
25
+import sys
26
+import os
27
+
28
+class StateUpdate:
29
+    def __init__(self, lcd):
30
+        self.host = "https://git.xythobuz.de"
31
+        self.repo = "thomas/sb-py"
32
+        self.branch = None
33
+        self.exe_path = "states.py"
34
+        self.blacklist = [
35
+            "README.md",
36
+            "COPYING",
37
+            ".gitignore",
38
+            "python-test/.gitignore",
39
+            "python-test/copy.sh",
40
+            "web-app/fetch.sh",
41
+        ]
42
+
43
+        self.lcd = lcd
44
+
45
+        self.get = None
46
+        self.version_file = "_ota_version"
47
+
48
+    def fetch(self, url):
49
+        # lazily initialize WiFi
50
+        if self.get == None:
51
+            self.get, post = util.getRequests()
52
+            if self.get == None:
53
+                return None
54
+
55
+        try:
56
+            #print("GET " + url)
57
+            r = self.get(url)
58
+
59
+            # explitic close on Response object not needed,
60
+            # handled internally by r.content / r.text / r.json()
61
+            # to avoid this automatic behaviour, first access r.content
62
+            # to trigger caching it in response object, then close
63
+            # socket.
64
+            tmp = r.content
65
+            if hasattr(r, "raw"):
66
+                if r.raw != None:
67
+                    r.raw.close()
68
+                    r.raw = None
69
+
70
+            return r
71
+        except Exception as e:
72
+            print()
73
+            print(url)
74
+            if hasattr(sys, "print_exception"):
75
+                sys.print_exception(e)
76
+            else:
77
+                print(e)
78
+            print()
79
+            return None
80
+
81
+    def get_stored_commit(self):
82
+        current = "unknown"
83
+        try:
84
+            f = open(self.version_file, "r")
85
+            current = f.readline().strip()
86
+            f.close()
87
+        except Exception as e:
88
+            print()
89
+            if hasattr(sys, "print_exception"):
90
+                sys.print_exception(e)
91
+            else:
92
+                print(e)
93
+            print()
94
+        return current
95
+
96
+    def get_previous_commit(self, commit):
97
+        r = self.fetch(self.host + "/" + self.repo + "/commit/" + commit).text
98
+        for line in r.splitlines():
99
+            if not (self.repo + "/commit/") in line:
100
+                continue
101
+
102
+            line = line[line.find("/commit/") : ][8 : ][ : 40]
103
+            if line != commit:
104
+                return line
105
+        return "unknown"
106
+
107
+    def check(self, verbose = False):
108
+        if self.branch == None:
109
+            # get default branch
110
+            r = self.fetch(self.host + "/api/v1/repos/" + self.repo).json()
111
+            self.branch = r["default_branch"]
112
+
113
+            if verbose:
114
+                print("Selected default branch " + self.branch)
115
+
116
+        # check for latest commit in branch
117
+        r = self.fetch(self.host + "/api/v1/repos/" + self.repo + "/branches/" + self.branch).json()
118
+        commit = r["commit"]["id"]
119
+
120
+        if verbose:
121
+            print("Latest commit is " + commit)
122
+
123
+        current = self.get_stored_commit()
124
+
125
+        if verbose:
126
+            if current != commit:
127
+                print("Current commit " + current + " is different!")
128
+            else:
129
+                print("No update required")
130
+
131
+        return (current != commit, commit)
132
+
133
+    def update_to_commit(self, commit, verbose = False):
134
+        # list all files for a commit
135
+        r = self.fetch(self.host + "/api/v1/repos/" + self.repo + "/git/trees/" + commit).json()
136
+
137
+        # TODO does not support sub-folders
138
+
139
+        if verbose:
140
+            if len(r["tree"]) > 0:
141
+                print(str(len(r["tree"])) + " files in repo:")
142
+                for f in r["tree"]:
143
+                    if f["path"] in self.blacklist:
144
+                        print("  - (IGNORED) " + f["path"])
145
+                    else:
146
+                        print("  - " + f["path"])
147
+            else:
148
+                print("No files in repo?!")
149
+
150
+        for f in r["tree"]:
151
+            if f["path"] in self.blacklist:
152
+                continue
153
+
154
+            # get a file from a commit
155
+            r = self.fetch(self.host + "/" + self.repo + "/raw/commit/" + commit + "/" + f["path"]).text
156
+
157
+            if verbose:
158
+                print("Writing " + f["path"])
159
+
160
+            # overwrite existing file
161
+            fo = open(f["path"], "w")
162
+            fo.write(r)
163
+            fo.close()
164
+
165
+            if f["path"] == self.exe_path:
166
+                if verbose:
167
+                    print("Writing " + f["path"] + " to main.py")
168
+
169
+                fo = open("./main.py", "w")
170
+                fo.write(r)
171
+                fo.close()
172
+
173
+        # Write new commit id to local file
174
+        f = open(self.version_file, "w")
175
+        f.write(commit + "\n")
176
+        f.close()
177
+
178
+def pico_ota_run():
179
+    print("Checking for updates")
180
+    newer, commit = ota.check(True)
181
+
182
+    if newer:
183
+        print("Updating to:", commit)
184
+        ota.update_to_commit(commit, True)
185
+
186
+        print("Resetting")
187
+        machine.soft_reset()

+ 2
- 0
python-test/state_scan.py View File

@@ -124,6 +124,8 @@ class StateScan:
124 124
                     self.current = 0
125 125
                 else:
126 126
                     self.current += 1
127
+            elif keys.once("x"):
128
+                return 10
127 129
             elif keys.held("left"):
128 130
                 v = self.lcd.curr_brightness - 0.05
129 131
                 if v < 0.05:

+ 7
- 0
python-test/states.py View File

@@ -120,6 +120,8 @@ class States:
120 120
             max_w = self.lcd.width - off - 2
121 121
             w = int(percentage / 100.0 * max_w)
122 122
             self.lcd.rect(off + 1, self.lcd.height - 9, w, 6, c, True)
123
+        else:
124
+            pass # TODO charge indicator (lightning bolt?)
123 125
 
124 126
         self.lcd.show()
125 127
         return ret
@@ -186,6 +188,11 @@ def state_machine(lcd):
186 188
     notify = StateNotify(lcd)
187 189
     states.add(notify)
188 190
 
191
+    # 10 - OTA Update
192
+    #from ota import StateUpdate
193
+    #update = StateUpdate(lcd)
194
+    #states.add(update)
195
+
189 196
     while True:
190 197
         states.run()
191 198
 

Loading…
Cancel
Save