easycoder 250915.1__tar.gz → 250926.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of easycoder might be problematic. Click here for more details.

Files changed (200) hide show
  1. {easycoder-250915.1 → easycoder-250926.1}/PKG-INFO +1 -1
  2. {easycoder-250915.1 → easycoder-250926.1}/easycoder/__init__.py +1 -1
  3. {easycoder-250915.1 → easycoder-250926.1}/easycoder/ec_compiler.py +0 -1
  4. {easycoder-250915.1 → easycoder-250926.1}/easycoder/ec_core.py +56 -17
  5. easycoder-250915.1/RBRNow copy/README.md +0 -89
  6. easycoder-250915.1/RBRNow copy/ap.py +0 -24
  7. easycoder-250915.1/RBRNow copy/blescan.py +0 -31
  8. easycoder-250915.1/RBRNow copy/boot.py +0 -5
  9. easycoder-250915.1/RBRNow copy/config.py +0 -117
  10. easycoder-250915.1/RBRNow copy/dht22.py +0 -45
  11. easycoder-250915.1/RBRNow copy/espcomms.py +0 -76
  12. easycoder-250915.1/RBRNow copy/files.py +0 -46
  13. easycoder-250915.1/RBRNow copy/files.txt +0 -13
  14. easycoder-250915.1/RBRNow copy/files2.txt +0 -23
  15. easycoder-250915.1/RBRNow copy/handler.py +0 -124
  16. easycoder-250915.1/RBRNow copy/lib/aioble/__init__.py +0 -32
  17. easycoder-250915.1/RBRNow copy/lib/aioble/central.py +0 -307
  18. easycoder-250915.1/RBRNow copy/lib/aioble/client.py +0 -456
  19. easycoder-250915.1/RBRNow copy/lib/aioble/core.py +0 -78
  20. easycoder-250915.1/RBRNow copy/lib/aioble/device.py +0 -304
  21. easycoder-250915.1/RBRNow copy/lib/aioble/files.txt +0 -9
  22. easycoder-250915.1/RBRNow copy/lib/aioble/l2cap.py +0 -214
  23. easycoder-250915.1/RBRNow copy/lib/aioble/peripheral.py +0 -178
  24. easycoder-250915.1/RBRNow copy/lib/aioble/security.py +0 -178
  25. easycoder-250915.1/RBRNow copy/lib/aioble/server.py +0 -340
  26. easycoder-250915.1/RBRNow copy/main.py +0 -79
  27. easycoder-250915.1/RBRNow copy/pin.py +0 -29
  28. easycoder-250915.1/RBRNow copy/server.py +0 -80
  29. easycoder-250915.1/RBRNow copy/sht41.py +0 -27
  30. easycoder-250915.1/RBRNow copy/sta.py +0 -31
  31. easycoder-250915.1/scripts/config.ecg +0 -196
  32. {easycoder-250915.1 → easycoder-250926.1}/.gitignore +0 -0
  33. {easycoder-250915.1 → easycoder-250926.1}/LICENSE +0 -0
  34. {easycoder-250915.1 → easycoder-250926.1}/README.md +0 -0
  35. {easycoder-250915.1 → easycoder-250926.1}/backdrop.jpg +0 -0
  36. {easycoder-250915.1 → easycoder-250926.1}/doc/README.md +0 -0
  37. {easycoder-250915.1 → easycoder-250926.1}/doc/core/README.md +0 -0
  38. {easycoder-250915.1 → easycoder-250926.1}/doc/core/conditions/boolean.md +0 -0
  39. {easycoder-250915.1 → easycoder-250926.1}/doc/core/conditions/empty.md +0 -0
  40. {easycoder-250915.1 → easycoder-250926.1}/doc/core/conditions/ends.md +0 -0
  41. {easycoder-250915.1 → easycoder-250926.1}/doc/core/conditions/even.md +0 -0
  42. {easycoder-250915.1 → easycoder-250926.1}/doc/core/conditions/exists.md +0 -0
  43. {easycoder-250915.1 → easycoder-250926.1}/doc/core/conditions/greater.md +0 -0
  44. {easycoder-250915.1 → easycoder-250926.1}/doc/core/conditions/hasProperty.md +0 -0
  45. {easycoder-250915.1 → easycoder-250926.1}/doc/core/conditions/includes.md +0 -0
  46. {easycoder-250915.1 → easycoder-250926.1}/doc/core/conditions/is.md +0 -0
  47. {easycoder-250915.1 → easycoder-250926.1}/doc/core/conditions/less.md +0 -0
  48. {easycoder-250915.1 → easycoder-250926.1}/doc/core/conditions/list.md +0 -0
  49. {easycoder-250915.1 → easycoder-250926.1}/doc/core/conditions/none.md +0 -0
  50. {easycoder-250915.1 → easycoder-250926.1}/doc/core/conditions/not.md +0 -0
  51. {easycoder-250915.1 → easycoder-250926.1}/doc/core/conditions/numeric.md +0 -0
  52. {easycoder-250915.1 → easycoder-250926.1}/doc/core/conditions/object.md +0 -0
  53. {easycoder-250915.1 → easycoder-250926.1}/doc/core/conditions/odd.md +0 -0
  54. {easycoder-250915.1 → easycoder-250926.1}/doc/core/conditions/starts.md +0 -0
  55. {easycoder-250915.1 → easycoder-250926.1}/doc/core/conditions/string.md +0 -0
  56. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/add.md +0 -0
  57. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/append.md +0 -0
  58. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/assert.md +0 -0
  59. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/begin.md +0 -0
  60. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/clear.md +0 -0
  61. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/close.md +0 -0
  62. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/create.md +0 -0
  63. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/debug.md +0 -0
  64. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/decrement.md +0 -0
  65. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/delete.md +0 -0
  66. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/divide.md +0 -0
  67. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/exit.md +0 -0
  68. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/file.md +0 -0
  69. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/fork.md +0 -0
  70. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/get.md +0 -0
  71. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/go.md +0 -0
  72. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/gosub.md +0 -0
  73. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/if.md +0 -0
  74. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/import.md +0 -0
  75. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/increment.md +0 -0
  76. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/index.md +0 -0
  77. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/init.md +0 -0
  78. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/input.md +0 -0
  79. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/load.md +0 -0
  80. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/lock.md +0 -0
  81. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/log.md +0 -0
  82. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/module.md +0 -0
  83. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/multiply.md +0 -0
  84. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/negate.md +0 -0
  85. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/on.md +0 -0
  86. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/open.md +0 -0
  87. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/pop.md +0 -0
  88. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/post.md +0 -0
  89. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/print.md +0 -0
  90. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/push.md +0 -0
  91. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/put.md +0 -0
  92. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/read.md +0 -0
  93. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/release.md +0 -0
  94. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/replace.md +0 -0
  95. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/return.md +0 -0
  96. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/run.md +0 -0
  97. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/save.md +0 -0
  98. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/script.md +0 -0
  99. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/send.md +0 -0
  100. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/set.md +0 -0
  101. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/shuffle.md +0 -0
  102. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/split.md +0 -0
  103. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/stack.md +0 -0
  104. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/stop.md +0 -0
  105. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/system.md +0 -0
  106. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/take.md +0 -0
  107. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/toggle.md +0 -0
  108. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/truncate.md +0 -0
  109. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/unlock.md +0 -0
  110. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/use.md +0 -0
  111. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/variable.md +0 -0
  112. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/wait.md +0 -0
  113. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/while.md +0 -0
  114. {easycoder-250915.1 → easycoder-250926.1}/doc/core/keywords/write.md +0 -0
  115. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/arg.md +0 -0
  116. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/args.md +0 -0
  117. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/cos.md +0 -0
  118. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/datime.md +0 -0
  119. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/decode.md +0 -0
  120. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/element.md +0 -0
  121. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/elements.md +0 -0
  122. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/empty.md +0 -0
  123. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/encode.md +0 -0
  124. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/error.md +0 -0
  125. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/files.md +0 -0
  126. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/float.md +0 -0
  127. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/from.md +0 -0
  128. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/hash.md +0 -0
  129. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/index.md +0 -0
  130. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/integer.md +0 -0
  131. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/json.md +0 -0
  132. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/keys.md +0 -0
  133. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/left.md +0 -0
  134. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/length.md +0 -0
  135. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/lowercase.md +0 -0
  136. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/memory.md +0 -0
  137. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/modification.md +0 -0
  138. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/modulo.md +0 -0
  139. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/newline.md +0 -0
  140. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/now.md +0 -0
  141. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/position.md +0 -0
  142. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/property.md +0 -0
  143. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/random.md +0 -0
  144. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/right.md +0 -0
  145. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/sin.md +0 -0
  146. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/stringify.md +0 -0
  147. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/tab.md +0 -0
  148. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/tan.md +0 -0
  149. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/timestamp.md +0 -0
  150. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/today.md +0 -0
  151. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/trim.md +0 -0
  152. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/type.md +0 -0
  153. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/uppercase.md +0 -0
  154. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/value.md +0 -0
  155. {easycoder-250915.1 → easycoder-250926.1}/doc/core/values/weekday.md +0 -0
  156. {easycoder-250915.1 → easycoder-250926.1}/doc/graphics/README.md +0 -0
  157. {easycoder-250915.1 → easycoder-250926.1}/doc/graphics/keywords/attach.md +0 -0
  158. {easycoder-250915.1 → easycoder-250926.1}/doc/graphics/keywords/close.md +0 -0
  159. {easycoder-250915.1 → easycoder-250926.1}/doc/graphics/keywords/create.md +0 -0
  160. {easycoder-250915.1 → easycoder-250926.1}/doc/graphics/keywords/ellipse.md +0 -0
  161. {easycoder-250915.1 → easycoder-250926.1}/doc/graphics/keywords/image.md +0 -0
  162. {easycoder-250915.1 → easycoder-250926.1}/doc/graphics/keywords/move.md +0 -0
  163. {easycoder-250915.1 → easycoder-250926.1}/doc/graphics/keywords/on.md +0 -0
  164. {easycoder-250915.1 → easycoder-250926.1}/doc/graphics/keywords/rectangle.md +0 -0
  165. {easycoder-250915.1 → easycoder-250926.1}/doc/graphics/keywords/render.md +0 -0
  166. {easycoder-250915.1 → easycoder-250926.1}/doc/graphics/keywords/run.md +0 -0
  167. {easycoder-250915.1 → easycoder-250926.1}/doc/graphics/keywords/set.md +0 -0
  168. {easycoder-250915.1 → easycoder-250926.1}/doc/graphics/keywords/text.md +0 -0
  169. {easycoder-250915.1 → easycoder-250926.1}/doc/graphics/values/attribute.md +0 -0
  170. {easycoder-250915.1 → easycoder-250926.1}/doc/graphics/values/window.md +0 -0
  171. {easycoder-250915.1 → easycoder-250926.1}/easycoder/close.png +0 -0
  172. {easycoder-250915.1 → easycoder-250926.1}/easycoder/ec_border.py +0 -0
  173. {easycoder-250915.1 → easycoder-250926.1}/easycoder/ec_classes.py +0 -0
  174. {easycoder-250915.1 → easycoder-250926.1}/easycoder/ec_condition.py +0 -0
  175. {easycoder-250915.1 → easycoder-250926.1}/easycoder/ec_handler.py +0 -0
  176. {easycoder-250915.1 → easycoder-250926.1}/easycoder/ec_keyboard.py +0 -0
  177. {easycoder-250915.1 → easycoder-250926.1}/easycoder/ec_program.py +0 -0
  178. {easycoder-250915.1 → easycoder-250926.1}/easycoder/ec_pyside.py +0 -0
  179. {easycoder-250915.1 → easycoder-250926.1}/easycoder/ec_timestamp.py +0 -0
  180. {easycoder-250915.1 → easycoder-250926.1}/easycoder/ec_value.py +0 -0
  181. {easycoder-250915.1 → easycoder-250926.1}/easycoder/tick.png +0 -0
  182. {easycoder-250915.1 → easycoder-250926.1}/images/Semoigo Dawn.jpg +0 -0
  183. {easycoder-250915.1 → easycoder-250926.1}/json/graphics-demo.json +0 -0
  184. {easycoder-250915.1 → easycoder-250926.1}/plugins/ec_keyboard.py +0 -0
  185. {easycoder-250915.1 → easycoder-250926.1}/plugins/ec_p100.py +0 -0
  186. {easycoder-250915.1 → easycoder-250926.1}/plugins/ec_pyside6.py +0 -0
  187. {easycoder-250915.1 → easycoder-250926.1}/plugins/points.py +0 -0
  188. {easycoder-250915.1 → easycoder-250926.1}/plugins/sql.py +0 -0
  189. {easycoder-250915.1 → easycoder-250926.1}/pyproject.toml +0 -0
  190. {easycoder-250915.1 → easycoder-250926.1}/scripts/benchmark.ecs +0 -0
  191. {easycoder-250915.1 → easycoder-250926.1}/scripts/ec_keyboard.py +0 -0
  192. {easycoder-250915.1 → easycoder-250926.1}/scripts/fizzbuzz.ecs +0 -0
  193. {easycoder-250915.1 → easycoder-250926.1}/scripts/hello.ecs +0 -0
  194. {easycoder-250915.1 → easycoder-250926.1}/scripts/points.ecs +0 -0
  195. {easycoder-250915.1 → easycoder-250926.1}/scripts/test.ecs +0 -0
  196. {easycoder-250915.1 → easycoder-250926.1}/scripts/tests.ecs +0 -0
  197. {easycoder-250915.1 → easycoder-250926.1}/test.py +0 -0
  198. {easycoder-250915.1 → easycoder-250926.1}/testrc.py +0 -0
  199. {easycoder-250915.1 → easycoder-250926.1}/testsql.py +0 -0
  200. {easycoder-250915.1 → easycoder-250926.1}/testui.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: easycoder
3
- Version: 250915.1
3
+ Version: 250926.1
4
4
  Summary: Rapid scripting in English
5
5
  Keywords: compiler,scripting,prototyping,programming,coding,python,low code,hypertalk,computer language,learn to code
6
6
  Author-email: Graham Trott <gtanyware@gmail.com>
@@ -12,4 +12,4 @@ from .ec_pyside import *
12
12
  from .ec_timestamp import *
13
13
  from .ec_value import *
14
14
 
15
- __version__ = "250915.1"
15
+ __version__ = "250926.1"
@@ -211,7 +211,6 @@ class Compiler:
211
211
  token = self.tokens[self.index]
212
212
  keyword = token.token
213
213
  if self.debugCompile: print(self.script.lines[token.lino])
214
- # print(f'{keyword} - {line}')
215
214
  # if keyword != 'else':
216
215
  if self.compileOne() == True:
217
216
  if self.index == len(self.tokens) - 1:
@@ -160,23 +160,25 @@ class Core(Handler):
160
160
  else:
161
161
  return self.compileFromHere(['end'])
162
162
 
163
- # Clear (set false)
164
163
  # clear {variable}
165
164
  def k_clear(self, command):
166
165
  if self.nextIsSymbol():
167
166
  target = self.getSymbolRecord()
168
- if target['hasValue']:
169
- command['target'] = target['name']
167
+ command['target'] = target['name']
168
+ if target['hasValue'] or target['keyword'] == 'ssh':
170
169
  self.add(command)
171
170
  return True
172
171
  return False
173
172
 
174
173
  def r_clear(self, command):
175
174
  target = self.getVariable(command['target'])
176
- val = {}
177
- val['type'] = 'boolean'
178
- val['content'] = False
179
- self.putSymbolValue(target, val)
175
+ if target['keyword'] == 'ssh':
176
+ target['ssh'] = None
177
+ else:
178
+ val = {}
179
+ val['type'] = 'boolean'
180
+ val['content'] = False
181
+ self.putSymbolValue(target, val)
180
182
  return self.nextPC()
181
183
 
182
184
  # Close a file
@@ -277,7 +279,7 @@ class Core(Handler):
277
279
  return self.incdec(command, '-')
278
280
 
279
281
  # Delete a file or a property
280
- # delete {filename}
282
+ # delete file {filename}
281
283
  # delete property {value} of {variable}
282
284
  # delete element {name} of {variable}
283
285
  def k_delete(self, command):
@@ -306,8 +308,8 @@ class Core(Handler):
306
308
  type = command['type']
307
309
  if type == 'file':
308
310
  filename = self.getRuntimeValue(command['filename'])
309
- if os.path.isfile(filename):
310
- os.remove(filename)
311
+ if filename != None:
312
+ if os.path.isfile(filename): os.remove(filename)
311
313
  elif type == 'property':
312
314
  key = self.getRuntimeValue(command['key'])
313
315
  symbolRecord = self.getVariable(command['var'])
@@ -1504,8 +1506,11 @@ class Core(Handler):
1504
1506
  ssh = paramiko.SSHClient()
1505
1507
  target['ssh'] = ssh
1506
1508
  ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
1507
- ssh.connect(host, username=user, password=password)
1508
- target['sftp'] = ssh.open_sftp()
1509
+ try:
1510
+ ssh.connect(host, username=user, password=password, timeout=10)
1511
+ target['sftp'] = ssh.open_sftp()
1512
+ except:
1513
+ target['error'] = f'Unable to connect to {host} (timeout)'
1509
1514
  return self.nextPC()
1510
1515
 
1511
1516
  # Shuffle a list
@@ -2109,14 +2114,24 @@ class Core(Handler):
2109
2114
  return value
2110
2115
 
2111
2116
  if token == 'error':
2112
- if self.peek() == 'code':
2117
+ token = self.peek()
2118
+ if token == 'code':
2113
2119
  self.nextToken()
2114
2120
  value['item'] = 'errorCode'
2115
2121
  return value
2116
- if self.peek() == 'reason':
2122
+ elif token == 'reason':
2117
2123
  self.nextToken()
2118
2124
  value['item'] = 'errorReason'
2119
2125
  return value
2126
+ elif token in ['in', 'of']:
2127
+ self.nextToken()
2128
+ if self.nextIsSymbol():
2129
+ record = self.getSymbolRecord()
2130
+ if record['keyword'] == 'ssh':
2131
+ value['item'] = 'sshError'
2132
+ value['name'] = record['name']
2133
+ return value
2134
+ return None
2120
2135
 
2121
2136
  if token == 'type':
2122
2137
  if self.nextIs('of'):
@@ -2280,6 +2295,10 @@ class Core(Handler):
2280
2295
  elif v['item'] == 'errorReason':
2281
2296
  value['type'] = 'text'
2282
2297
  value['content'] = errorReason
2298
+ elif v['item'] == 'sshError':
2299
+ record = self.getVariable(v['name'])
2300
+ value['type'] = 'text'
2301
+ value['content'] = record['error'] if 'error' in record else ''
2283
2302
  return value
2284
2303
 
2285
2304
  def v_files(self, v):
@@ -2511,7 +2530,7 @@ class Core(Handler):
2511
2530
  elif keyword == 'ssh':
2512
2531
  v = {}
2513
2532
  v['type'] = 'boolean'
2514
- v['content'] = True if 'ssh' in symbolRecord else False
2533
+ v['content'] = True if 'ssh' in symbolRecord and symbolRecord['ssh'] != None else False
2515
2534
  return v
2516
2535
  else:
2517
2536
  return None
@@ -2614,12 +2633,25 @@ class Core(Handler):
2614
2633
  def compileCondition(self):
2615
2634
  condition = Condition()
2616
2635
 
2617
- if self.getToken() == 'not':
2636
+ token = self.getToken()
2637
+
2638
+ if token == 'not':
2618
2639
  condition.type = 'not'
2619
2640
  condition.value = self.nextValue()
2620
2641
  return condition
2621
2642
 
2622
- if self.getToken() == 'file':
2643
+ elif token == 'error':
2644
+ self.nextToken()
2645
+ self.skip('in')
2646
+ if self.nextIsSymbol():
2647
+ record = self.getSymbolRecord()
2648
+ if record['keyword'] == 'ssh':
2649
+ condition.type = 'sshError'
2650
+ condition.target = record['name']
2651
+ return condition
2652
+ return None
2653
+
2654
+ elif token == 'file':
2623
2655
  path = self.nextValue()
2624
2656
  condition.path = path
2625
2657
  condition.type = 'exists'
@@ -2799,6 +2831,13 @@ class Core(Handler):
2799
2831
 
2800
2832
  def c_odd(self, condition):
2801
2833
  return self.getRuntimeValue(condition.value1) % 2 == 1
2834
+
2835
+ def c_sshError(self, condition):
2836
+ target = self.getVariable(condition.target)
2837
+ errormsg = target['error'] if 'error' in target else None
2838
+ condition.errormsg = errormsg
2839
+ test = errormsg != None
2840
+ return not test if condition.negate else test
2802
2841
 
2803
2842
  def c_starts(self, condition):
2804
2843
  value1 = self.getRuntimeValue(condition.value1)
@@ -1,89 +0,0 @@
1
- # RBR-Now #
2
-
3
- **RBR-Now** is a networked control system based on the ESP32 microcontroller, designed for ESP32-based devices such as a radiator TRV or a DHT22 thermometer, and uses ESP-Now to implement chained networking allowing any device to run its own subnetwork to overcome range and capacity issues.
4
-
5
- All the software in RBR-Now is MicroPython (uPython) and every device in the RBR-Now network is equipped with the same set of files. The behaviour of each device is determined by a small JSON configuration file.
6
-
7
- The system can be deployed simply by loading devices with the core file set and a suitable configuration file, but is best managed by the RBRConf program; a Python GUI application for Linux computers. (It cannot run on Windows because it relies on networking commands that are not available on that OS.) RBRConf allows any device on the network to be interrogated, controlled and have its firmware updated remotely without any need to remove it from service.
8
-
9
- An RBR-Now network comprises a hub (master) device and a number of slaves. A control system using RBR-Now connects solely to the hub device and controls the slaves through it. This documentation will first describe the slave functionality as this is the simpler device..
10
-
11
- ## The RBR-Now Slave device
12
-
13
- An ESP-Now slave unit only receives; it doesn't initiate messages as it has no information about where to send any. To send a message to one we have to know its MAC address. This can be found by setting up the device as a wifi hotspot and arrange for the SSID to include the MAC address. To make things simpler, the RBR-Now system automates this process as follows.
14
-
15
- When the device starts it looks for its configuration file, but this will not initially exist. So it drops into slave mode and creates a hotspot whose SSID is based on its MAC address but with a specific RBR prefix. When the configuration program is told to scan for slave devices it looks for this prefix and offers a list of all devices that match. The user selects one (there usually is only one, as I'll explain shortly) and the program notes the relevant MAC address. The new device is added to the list in the Slaves panel as an untitled device. When the user clicks that item, all relevant details for the device can be added in a panel on the UI screen. These details comprise the name by which the device will be known (usually the room name) and pin numbers for the onboard LED, the relay and the thermometer (if either of these are present). With this information, a JSON structure is created and sent to the device as its config file, so that next time it starts up it can configure itself appropriately.
16
-
17
- The hotspot is basically open (otherwise the device couldn't be connected) so this is an obvious vulnerability. To overcome this, the hotspot is disabled after 2 minutes. (It cannot be removed entirely because this would prevent ESP-Now from working, so it's given an SSID comprising a single dash and a randomly generated numeric password, making it virtually impossible to get into and rendering the MAC address inaccessible.) So by the time a second device is to be configured, existing ones are unlikely to appear in a search.
18
-
19
- Apart from initial configuration, all comms with a slave is done using ESP-Now, which only requires the MAC address to be known. The device indicates which mode it's in by flashing its onboard LED on-off with a 1-second cycle for the initial 2 minutes, then a brief flash every 5 seconds after that.
20
-
21
- The slave code waits for messages to arrive and acts on them. Messages will only arrive from the system controller, although in some cases they may be relayed via another slave device. Each message is acknowledged and any requested data is passed back. This includes such things as the relay state, the up time (seconds from startup) and thermometer readings.
22
-
23
- The device also runs concurrent code that monitors BLE transmissions, looking for the signature of a Mijia thermometer. Every time one is seen, its advertising packet is read and the relevant data extracted. This will be returned as part of the response to the next message from the controller. Queuing is not implemented, so if several Mijias are detected at the same time, only one of them will be captured on this occasion.
24
-
25
- Among the commands that can be sent are some that permit any file on the device to be updated. (This does not include MicroPython itself.) The configurator has the ability to update one or all the files on a device.
26
-
27
- ## The RBR-Now Master device
28
-
29
- The Master (or Hub) device is the gateway between the system controller and the network of RBR-Now devices. It can itself be one of those devices, or it can be a separate module placed centrally where it can easily be seen by all the others, which are all Slave devices as described above. The Master has no knowledge of the other devices, nor of the system controller; it just responds to requests. Most of the functionality of the Slave is also shared by the Master.
30
-
31
- The Master must be the first device set up by the configurator, which treats it specially. As with the slaves, the device initially has no configuration file, so as above it publishes a hotspot and waits for a connection. The configurator creates a JSON configuration file similar to that for a slave but additionally containing the SSID and password of the home network router and identifying the device as the master, then sends the file to the device and asks it to reset itself. When it restarts, the device will connect to the router and will get an IP address. The configurator waits for 10 seconds to allow this to complete, then sends another message to the hotspot requesting the IP address. From then on, all communication is done through this IP address. As with the slave, after 2 minutes the hotspot is disabled and the LED cycle changes, this time to a double flash every 5 seconds.
32
-
33
- ## System messaging
34
-
35
- All messages from the system controller to an RBR-Now device are sent to the Master/Hub device at its IP address on the home network. Messages are prefixed by the MAC address of the target device, as recorded by the configurator when the device was added to the system.
36
-
37
- When the Master device receives a message it checks to see who the message is addressed to. If it's the master device itself the message is handled and a reply sent. If it's any other address the message is forwarded to that device via the ESP-Now network,and the reply from that device is returned to the system controller.
38
-
39
- In some cases a device is too far away from the hub to receive messages reliably. In such cases the device can piggy-back off another, closer device. Here, the MAC address given has a special prefix and identifies the path to the target device, going via as many intermediates as necessary. The path is part of the configuration for a device; in most cases it is empty.
40
-
41
- The messages understood by devices are as follows:
42
-
43
- ### on
44
-
45
- Turns on the device relay. It returns 'OK' then the up time of the device (in seconds) and the most recent Mijia reading it has received (if any).
46
-
47
- ### off
48
-
49
- Turns off the device relay. The reply is the same as for **on**.
50
-
51
- ### relay
52
-
53
- Returns the current state of the relay.
54
-
55
- ### temp
56
-
57
- Returns the current temperature, if the device is a thermometer.
58
-
59
- ### reset
60
-
61
- Causes the device to reset itself.
62
-
63
- ### ipaddr
64
-
65
- Returns the IP address of the device (master only).
66
-
67
- ### pause
68
-
69
- Requests the device to pause all operations that may interfere with actions such as updating files.
70
-
71
- ### resume
72
-
73
- Requests the device to resume operations as normal.
74
-
75
- ### part
76
-
77
- Signals that the attached data (up to 100 characters) is part of a file to be saved by the device. The file content is sent in hex format, 100 bytes at a time, each with a part number starting with zero, and the device saves each part as it arrives. Part numbers must be in the correct sequence or an error occurs.
78
-
79
- ### save
80
-
81
- Saves the accumulated parts into the file whose name is given. The device saves the file then reads it back to check it matches what was received by the **part** requests.
82
-
83
- ### delete
84
-
85
- Deletes the file whose full path is given.
86
-
87
- ## mkdir
88
-
89
- Creates a directory with the name given.
@@ -1,24 +0,0 @@
1
- import network,binascii,asyncio,random
2
-
3
- class AP():
4
-
5
- def __init__(self,config):
6
- self.config=config
7
- config.setAP(self)
8
- ap=network.WLAN(network.AP_IF)
9
- ap.active(True)
10
- self.ap=ap
11
- mac=binascii.hexlify(self.ap.config('mac')).decode()
12
- self.config.setMAC(mac)
13
- self.ssid=f'RBR-Now-{mac}'
14
- ap.config(essid=self.ssid,password='00000000')
15
- ap.config(channel=config.getChannel())
16
- ap.ifconfig(('192.168.9.1','255.255.255.0','192.168.9.1','8.8.8.8'))
17
- print(mac,config.getName())
18
-
19
- def stop(self):
20
- password=str(random.randrange(100000,999999))
21
- print(password)
22
- self.ap.config(essid='-',password=password)
23
-
24
- def getChannel(self): return self.ap.config('channel')
@@ -1,31 +0,0 @@
1
- import asyncio
2
- import aioble
3
- from binascii import hexlify
4
-
5
- class BLEScan():
6
-
7
- def __init__(self):
8
- self.values=''
9
-
10
- async def scan(self):
11
- print("Scanning for BLE devices...")
12
- while True:
13
- async with aioble.scan(duration_ms=20000) as scanner:
14
- async for result in scanner:
15
- addr = result.device.addr_hex()
16
- if addr.find('a4:c1:38:') == 0:
17
- rssi = result.rssi
18
- data = hexlify(result.adv_data).decode()
19
- if len(data) > 28:
20
- temp = int(data[20:24], 16)
21
- hum = int(data[24:26], 16)
22
- batt = int(data[26:28], 16)
23
- print(f'{addr[9:]} {rssi} {temp} {hum} {batt}')
24
- self.values = f'{addr[9:]};{rssi};{temp};{hum};{batt}'
25
- # else: print(f'{addr} - no')
26
- await asyncio.sleep(0.1)
27
-
28
- def getValues(self):
29
- values=self.values
30
- self.values=''
31
- return values
@@ -1,5 +0,0 @@
1
- # This file is executed on every boot (including wake-boot from deepsleep)
2
- #import esp
3
- #esp.osdebug(None)
4
- #import webrepl
5
- #webrepl.start()
@@ -1,117 +0,0 @@
1
- import json,asyncio,time
2
- from files import readFile,writeFile,fileExists
3
- from pin import PIN
4
- from server import Server
5
- from dht22 import DHT22
6
- from blescan import BLEScan
7
-
8
- class Config():
9
-
10
- def __init__(self):
11
- if fileExists('config.json'):
12
- self.config=json.loads(readFile('config.json'))
13
- else:
14
- self.config={}
15
- self.config['name']='(none)'
16
- self.config['master']=False
17
- self.config['channel']=1
18
- self.config['path']=''
19
- self.config['pins']={}
20
- pin={}
21
- pin['pin']=''
22
- pin['invert']=False
23
- self.config['pins']['led']=pin
24
- pin={}
25
- pin['pin']=''
26
- pin['invert']=False
27
- self.config['pins']['relay']=pin
28
- pin={}
29
- pin['pin']=''
30
- self.config['pins']['dht22']=pin
31
- writeFile('config.json',json.dumps(self.config))
32
- pin,invert=self.getPinInfo('led')
33
- self.led=PIN(self,pin,invert)
34
- pin,invert=self.getPinInfo('relay')
35
- self.relay=PIN(self,pin,invert)
36
- pin,_=self.getPinInfo('dht22')
37
- if pin=='': self.dht22=None
38
- else:
39
- self.dht22=DHT22(pin)
40
- asyncio.create_task(self.dht22.measure())
41
- print('path:',self.config['path'])
42
- self.ipaddr=None
43
- self.uptime=0
44
- self.resetRequested=False
45
- self.server=Server(self)
46
- asyncio.create_task(self.runWatchdog())
47
- self.bleScan=BLEScan()
48
-
49
- async def respond(self,response,writer):
50
- await self.server.respond(response,writer)
51
- async def sendDefaultResponse(self,writer):
52
- await self.server.sendDefaultResponse(writer)
53
- async def handleClient(self,reader,writer):
54
- await self.server.handleClient(reader,writer)
55
-
56
- async def send(self,peer,espmsg): return await self.espComms.send(peer,espmsg)
57
-
58
- def reset(self):
59
- print('Reset requested')
60
- self.resetRequested=True
61
-
62
- def pause(self):
63
- if self.dht22!=None: self.dht22.pause()
64
-
65
- def resume(self):
66
- if self.dht22!=None: self.dht22.resume()
67
-
68
- def setAP(self,ap): self.ap=ap
69
- def setSTA(self,sta): self.sta=sta
70
- def setMAC(self,mac): self.mac=mac
71
- def setChannel(self,channel):
72
- self.config['channel']=channel
73
- writeFile('config.json',json.dumps(self.config))
74
- def setIPAddr(self,ipaddr): self.ipaddr=ipaddr
75
- def setHandler(self,handler): self.handler=handler
76
- def setESPComms(self,espComms): self.espComms=espComms
77
- def addUptime(self,t): self.uptime+=t
78
-
79
- def isMaster(self): return self.config['master']
80
- def isESP8266(self): return False
81
- def getDevice(self): return self.config['device']
82
- def getName(self): return self.config['name']
83
- def getSSID(self): return self.config['hostssid']
84
- def getPassword(self): return self.config['hostpass']
85
- def getMAC(self): return self.mac
86
- def getAP(self): return self.ap
87
- def getSTA(self): return self.sta
88
- def stopAP(self): self.ap.stop()
89
- def startServer(self): self.server.startup()
90
- def getIPAddr(self): return self.ipaddr
91
- def getChannel(self): return self.ap.getChannel()
92
- def getHandler(self): return self.handler
93
- def getESPComms(self): return self.espComms
94
- def getBLEScan(self): return self.bleScan
95
- def getRBRNow(self): return self.rbrNow
96
- def getPinInfo(self,name):
97
- pin=self.config['pins'][name]
98
- print(name,pin)
99
- if 'invert' in pin: invert=pin['invert']
100
- else: invert=False
101
- return pin['pin'],invert
102
- def getLED(self): return self.led
103
- def getRelay(self): return self.relay
104
- def getUptime(self): return int(round(self.uptime))
105
- def getTemperature(self): return self.dht22.getTemperature()
106
-
107
- async def runWatchdog(self):
108
- while True:
109
- self.active=False
110
- await asyncio.sleep(180)
111
- if not self.active:
112
- print('No activity - reset')
113
- asyncio.get_event_loop().stop()
114
- pass
115
-
116
- def kickWatchdog(self):
117
- self.active=True
@@ -1,45 +0,0 @@
1
- import asyncio,dht
2
- from machine import Pin,reset
3
-
4
- class DHT22():
5
-
6
- def __init__(self,sensorpin,verbose=False):
7
- if verbose:
8
- print('Initialise sensor on pin',sensorpin)
9
- if sensorpin is '': self.sensor=None
10
- else: self.sensor=dht.DHT22(Pin(int(sensorpin)))
11
- self.verbose=verbose
12
- self.temp=0
13
- self.errors=0
14
- self.paused=False
15
-
16
- async def measure(self):
17
- if self.sensor==None: return
18
- msg=None
19
- print('Run the temperature sensor')
20
- while True:
21
- if not self.paused:
22
- try:
23
- self.sensor.measure()
24
- temperature=self.sensor.temperature()
25
- if temperature>50: temperature=0
26
- self.temp=round(temperature*10)
27
- if self.verbose:
28
- print('Temperature:',temperature)
29
- self.errors=0
30
- except OSError as e:
31
- if self.verbose:
32
- msg=f'Failed to read sensor: {str(e)}'
33
- print(msg)
34
- self.errors+=1
35
- if self.errors>50: reset()
36
- await asyncio.sleep(5)
37
-
38
- def pause(self):
39
- self.paused=True
40
-
41
- def resume(self):
42
- self.paused=False
43
-
44
- def getTemperature(self):
45
- return self.temp
@@ -1,76 +0,0 @@
1
- import asyncio
2
- from binascii import hexlify,unhexlify
3
- from espnow import ESPNow as E
4
-
5
- class ESPComms():
6
-
7
- def __init__(self,config):
8
- self.config=config
9
- config.setESPComms(self)
10
- E().active(True)
11
- self.peers=[]
12
- print('ESP-Now initialised')
13
-
14
- def checkPeer(self,peer):
15
- if not peer in self.peers:
16
- self.peers.append(peer)
17
- E().add_peer(peer)
18
-
19
- async def send(self,mac,espmsg):
20
- peer=unhexlify(mac.encode())
21
- # Flush any incoming messages
22
- while E().any(): _,_ = E().irecv()
23
- self.checkPeer(peer)
24
- try:
25
- # print(f'Send {espmsg[0:20]}... to {mac}')
26
- result=E().send(peer,espmsg)
27
- # print(f'Result: {result}')
28
- if result:
29
- counter=50
30
- while counter>0:
31
- if E().any():
32
- mac,response = E().irecv()
33
- if response:
34
- # print(f"Received response: {response.decode()}")
35
- result=response.decode()
36
- break
37
- await asyncio.sleep(.1)
38
- counter-=1
39
- if counter==0:
40
- result='Response timeout'
41
- else: result='Fail (no result)'
42
- except Exception as e:
43
- print(e)
44
- result=f'Fail ({e})'
45
- return result
46
-
47
- async def receive(self):
48
- print('Starting ESPNow receiver on channel',self.config.getChannel())
49
- self.waiting=False
50
- while True:
51
- if E().any():
52
- mac,msg=E().recv()
53
- sender=hexlify(mac).decode()
54
- msg=msg.decode()
55
- # print(f'Message from {sender}: {msg[0:30]}...')
56
- if msg[0]=='!':
57
- # It's a message to be relayed
58
- comma=msg.index(',')
59
- slave=msg[1:comma]
60
- msg=msg[comma+1:]
61
- # print(f'Slave: {slave}, msg: {msg}')
62
- response=await self.send(slave,msg)
63
- else:
64
- # It's a message for me
65
- response=self.config.getHandler().handleMessage(msg)
66
- response=f'{response} {self.getRSS(sender)}'
67
- print(f'Response to {sender}: {response}')
68
- self.checkPeer(mac)
69
- E().send(mac,response)
70
- await asyncio.sleep(.1)
71
- self.config.kickWatchdog()
72
-
73
- def getRSS(self,mac):
74
- peer=unhexlify(mac.encode())
75
- try: return E().peers_table[peer][0]
76
- except: return 0
@@ -1,46 +0,0 @@
1
- import os
2
-
3
- def fileExists(filename):
4
- try:
5
- os.stat(filename)
6
- return True
7
- except OSError:
8
- return False
9
-
10
- def readFile(name):
11
- try:
12
- f=open(name,'r')
13
- value=f.read()
14
- f.close()
15
- except:
16
- value=None
17
- return value
18
-
19
- def writeFile(name,text):
20
- f=open(name,'w')
21
- f.write(text)
22
- f.close()
23
-
24
- def appendFile(name,text):
25
- f=open(name,'a')
26
- f.write(text)
27
- f.close()
28
-
29
- def createDirectory(path):
30
- try:
31
- os.mkdir(path)
32
- except OSError as e:
33
- if e.args[0] != 17: return False
34
- return True
35
-
36
- def clearFile(name):
37
- open(name,'w').close()
38
-
39
- def deleteFile(file):
40
- try:
41
- os.remove(file)
42
- return True
43
- except: return False
44
-
45
- def renameFile(oldName,newName):
46
- os.rename(oldName,newName)
@@ -1,13 +0,0 @@
1
- ap.py
2
- blescan.py
3
- boot.py
4
- config.py
5
- dht22.py
6
- espcomms.py
7
- files.py
8
- handler.py
9
- main.py
10
- pin.py
11
- server.py
12
- sta.py
13
-
@@ -1,23 +0,0 @@
1
- lib/
2
- lib/aioble/
3
- lib/aioble/central.py
4
- lib/aioble/client.py
5
- lib/aioble/core.py
6
- lib/aioble/device.py
7
- lib/aioble/__init__.py
8
- lib/aioble/l2cap.py
9
- lib/aioble/peripheral.py
10
- lib/aioble/security.py
11
- lib/aioble/server.py
12
- ap.py
13
- blescan.py
14
- boot.py
15
- config.py
16
- dht22.py
17
- espcomms.py
18
- files.py
19
- handler.py
20
- main.py
21
- pin.py
22
- server.py
23
- sta.py