pymscada 0.1.10__tar.gz → 0.1.11b2__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 pymscada might be problematic. Click here for more details.

Files changed (90) hide show
  1. {pymscada-0.1.10 → pymscada-0.1.11b2}/PKG-INFO +4 -2
  2. {pymscada-0.1.10 → pymscada-0.1.11b2}/pyproject.toml +9 -2
  3. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/bus_client.py +23 -22
  4. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/bus_server.py +28 -25
  5. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/console.py +11 -14
  6. pymscada-0.1.11b2/src/pymscada/demo/openweather.yaml +25 -0
  7. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/tags.yaml +3 -0
  8. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/wwwserver.yaml +0 -263
  9. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/files.py +12 -17
  10. pymscada-0.1.11b2/src/pymscada/iodrivers/openweather.py +126 -0
  11. pymscada-0.1.11b2/src/pymscada/main.py +57 -0
  12. pymscada-0.1.11b2/src/pymscada/module_config.py +208 -0
  13. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/protocol_constants.py +29 -39
  14. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/validate.py +29 -26
  15. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/www_server.py +10 -11
  16. {pymscada-0.1.10 → pymscada-0.1.11b2}/tests/test_assets/db.sqlite +0 -0
  17. {pymscada-0.1.10 → pymscada-0.1.11b2}/tests/test_bus_server.py +13 -13
  18. pymscada-0.1.11b2/tests/test_openweather.py +47 -0
  19. {pymscada-0.1.10 → pymscada-0.1.11b2}/tests/test_opnotes.py +10 -14
  20. {pymscada-0.1.10 → pymscada-0.1.11b2}/tests/test_validate.py +2 -2
  21. pymscada-0.1.10/src/pymscada/main.py +0 -311
  22. {pymscada-0.1.10 → pymscada-0.1.11b2}/LICENSE +0 -0
  23. {pymscada-0.1.10 → pymscada-0.1.11b2}/README.md +0 -0
  24. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/__init__.py +0 -0
  25. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/__main__.py +0 -0
  26. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/checkout.py +0 -0
  27. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/config.py +0 -0
  28. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/README.md +0 -0
  29. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/__init__.py +0 -0
  30. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/accuweather.yaml +0 -0
  31. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/bus.yaml +0 -0
  32. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/files.yaml +0 -0
  33. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/history.yaml +0 -0
  34. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/logixclient.yaml +0 -0
  35. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/modbus_plc.py +0 -0
  36. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/modbusclient.yaml +0 -0
  37. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/modbusserver.yaml +0 -0
  38. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/opnotes.yaml +0 -0
  39. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/ping.yaml +0 -0
  40. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/pymscada-bus.service +0 -0
  41. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/pymscada-demo-modbus_plc.service +0 -0
  42. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/pymscada-files.service +0 -0
  43. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/pymscada-history.service +0 -0
  44. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/pymscada-io-accuweather.service +0 -0
  45. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/pymscada-io-logixclient.service +0 -0
  46. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/pymscada-io-modbusclient.service +0 -0
  47. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/pymscada-io-modbusserver.service +0 -0
  48. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/pymscada-io-ping.service +0 -0
  49. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/pymscada-io-snmpclient.service +0 -0
  50. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/pymscada-opnotes.service +0 -0
  51. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/pymscada-wwwserver.service +0 -0
  52. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/demo/snmpclient.yaml +0 -0
  53. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/history.py +0 -0
  54. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/iodrivers/__init__.py +0 -0
  55. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/iodrivers/accuweather.py +0 -0
  56. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/iodrivers/logix_client.py +0 -0
  57. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/iodrivers/logix_map.py +0 -0
  58. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/iodrivers/modbus_client.py +0 -0
  59. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/iodrivers/modbus_map.py +0 -0
  60. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/iodrivers/modbus_server.py +0 -0
  61. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/iodrivers/ping_client.py +0 -0
  62. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/iodrivers/ping_map.py +0 -0
  63. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/iodrivers/snmp_client.py +0 -0
  64. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/iodrivers/snmp_map.py +0 -0
  65. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/misc.py +0 -0
  66. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/opnotes.py +0 -0
  67. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/pdf/__init__.py +0 -0
  68. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/pdf/one.pdf +0 -0
  69. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/pdf/two.pdf +0 -0
  70. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/periodic.py +0 -0
  71. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/samplers.py +0 -0
  72. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/tag.py +0 -0
  73. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/tools/snmp_client2.py +0 -0
  74. {pymscada-0.1.10 → pymscada-0.1.11b2}/src/pymscada/tools/walk.py +0 -0
  75. {pymscada-0.1.10 → pymscada-0.1.11b2}/tests/__init__.py +0 -0
  76. {pymscada-0.1.10 → pymscada-0.1.11b2}/tests/bus_echo.py +0 -0
  77. {pymscada-0.1.10 → pymscada-0.1.11b2}/tests/iodrivers/test_logix.py +0 -0
  78. {pymscada-0.1.10 → pymscada-0.1.11b2}/tests/iodrivers/test_modbus.py +0 -0
  79. {pymscada-0.1.10 → pymscada-0.1.11b2}/tests/test_assets/busserver.yaml +0 -0
  80. {pymscada-0.1.10 → pymscada-0.1.11b2}/tests/test_assets/hist_tag_0_0.dat +0 -0
  81. {pymscada-0.1.10 → pymscada-0.1.11b2}/tests/test_assets/hist_tag_0_10_2.dat +0 -0
  82. {pymscada-0.1.10 → pymscada-0.1.11b2}/tests/test_assets/hist_tag_0_15.dat +0 -0
  83. {pymscada-0.1.10 → pymscada-0.1.11b2}/tests/test_assets/hist_tag_0_26.dat +0 -0
  84. {pymscada-0.1.10 → pymscada-0.1.11b2}/tests/test_assets/hist_tag_0_50.dat +0 -0
  85. {pymscada-0.1.10 → pymscada-0.1.11b2}/tests/test_config.py +0 -0
  86. {pymscada-0.1.10 → pymscada-0.1.11b2}/tests/test_history.py +0 -0
  87. {pymscada-0.1.10 → pymscada-0.1.11b2}/tests/test_misc.py +0 -0
  88. {pymscada-0.1.10 → pymscada-0.1.11b2}/tests/test_periodic.py +0 -0
  89. {pymscada-0.1.10 → pymscada-0.1.11b2}/tests/test_samplers.py +0 -0
  90. {pymscada-0.1.10 → pymscada-0.1.11b2}/tests/test_tag.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pymscada
3
- Version: 0.1.10
3
+ Version: 0.1.11b2
4
4
  Summary: Shared tag value SCADA with python backup and Angular UI
5
5
  Author-Email: Jamie Walton <jamie@walton.net.nz>
6
6
  License: GPL-3.0-or-later
@@ -10,10 +10,12 @@ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
10
10
  Classifier: Operating System :: OS Independent
11
11
  Classifier: Environment :: Console
12
12
  Classifier: Development Status :: 1 - Planning
13
+ Project-URL: Homepage, https://github.com/jamie0walton/pymscada
14
+ Project-URL: Bug Tracker, https://github.com/jamie0walton/pymscada/issues
13
15
  Requires-Python: >=3.9
14
16
  Requires-Dist: PyYAML>=6.0.1
15
17
  Requires-Dist: aiohttp>=3.8.5
16
- Requires-Dist: pymscada-html>=0.1.2
18
+ Requires-Dist: pymscada-html>=0.1.10b2
17
19
  Requires-Dist: cerberus>=1.3.5
18
20
  Requires-Dist: pycomm3>=1.2.14
19
21
  Requires-Dist: pysnmplib>=5.0.24
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pymscada"
3
- version = "0.1.10"
3
+ version = "0.1.11b2"
4
4
  description = "Shared tag value SCADA with python backup and Angular UI"
5
5
  authors = [
6
6
  { name = "Jamie Walton", email = "jamie@walton.net.nz" },
@@ -8,7 +8,7 @@ authors = [
8
8
  dependencies = [
9
9
  "PyYAML>=6.0.1",
10
10
  "aiohttp>=3.8.5",
11
- "pymscada-html>=0.1.2",
11
+ "pymscada-html>=0.1.10b2",
12
12
  "cerberus>=1.3.5",
13
13
  "pycomm3>=1.2.14",
14
14
  "pysnmplib>=5.0.24",
@@ -30,6 +30,10 @@ text = "GPL-3.0-or-later"
30
30
  [project.scripts]
31
31
  pymscada = "pymscada.__main__:cmd_line"
32
32
 
33
+ [project.urls]
34
+ Homepage = "https://github.com/jamie0walton/pymscada"
35
+ "Bug Tracker" = "https://github.com/jamie0walton/pymscada/issues"
36
+
33
37
  [build-system]
34
38
  requires = [
35
39
  "pdm-backend",
@@ -50,3 +54,6 @@ pdm = []
50
54
  omit = [
51
55
  "tests/*",
52
56
  ]
57
+
58
+ [tool.pytest.ini_options]
59
+ addopts = "-v -s"
@@ -42,26 +42,26 @@ class BusClient:
42
42
  self.to_publish[tag.name] = tag
43
43
  return
44
44
  if tag.type is float:
45
- data = struct.pack('!Bd', pc.TYPE_FLOAT, tag.value)
45
+ data = struct.pack('!Bd', pc.TYPE.FLOAT, tag.value)
46
46
  elif tag.type is int:
47
- data = struct.pack('!Bq', pc.TYPE_INT, tag.value)
47
+ data = struct.pack('!Bq', pc.TYPE.INT, tag.value)
48
48
  elif tag.type is bytes:
49
49
  size = len(tag.value)
50
50
  try:
51
- data = struct.pack(f'!B{size}s', pc.TYPE_BYTES, tag.value)
51
+ data = struct.pack(f'!B{size}s', pc.TYPE.BYTES, tag.value)
52
52
  except struct.error as e:
53
53
  logging.error(f'bus_client {tag.name} {e}')
54
54
  elif tag.type is str:
55
55
  size = len(tag.value)
56
- data = struct.pack(f'!B{size}s', pc.TYPE_STR, tag.value.encode())
56
+ data = struct.pack(f'!B{size}s', pc.TYPE.STR, tag.value.encode())
57
57
  elif tag.type in [list, dict]:
58
58
  jsonstr = json.dumps(tag.value).encode()
59
59
  size = len(jsonstr)
60
- data = struct.pack(f'!B{size}s', pc.TYPE_JSON, jsonstr)
60
+ data = struct.pack(f'!B{size}s', pc.TYPE.JSON, jsonstr)
61
61
  else:
62
62
  logging.warning(f'publish {tag.name} unhandled {tag.type}')
63
63
  return
64
- self.write(pc.CMD_SET, tag.id, tag.time_us, data)
64
+ self.write(pc.COMMAND.SET, tag.id, tag.time_us, data)
65
65
 
66
66
  def add_callback_rta(self, tagname, handler):
67
67
  """Collect callback handlers."""
@@ -75,10 +75,10 @@ class BusClient:
75
75
  time_us = int(time.time() * 1e6)
76
76
  jsonstr = json.dumps(request).encode()
77
77
  size = len(jsonstr)
78
- data = struct.pack(f'>B{size}s', pc.TYPE_JSON, jsonstr)
79
- self.write(pc.CMD_RTA, self.tag_by_name[tagname].id, time_us, data)
78
+ data = struct.pack(f'>B{size}s', pc.TYPE.JSON, jsonstr)
79
+ self.write(pc.COMMAND.RTA, self.tag_by_name[tagname].id, time_us, data)
80
80
 
81
- def write(self, command: pc.COMMANDS, tag_id: int, time_us: int,
81
+ def write(self, command: pc.COMMAND, tag_id: int, time_us: int,
82
82
  data: bytes):
83
83
  """Write a message."""
84
84
  if data is None:
@@ -101,14 +101,14 @@ class BusClient:
101
101
  self.tag_by_name[tag.name] = tag
102
102
  if tag.value is not None:
103
103
  self.to_publish[tag.name] = tag
104
- self.write(pc.CMD_ID, 0, 0, tag.name.encode())
104
+ self.write(pc.COMMAND.ID, 0, 0, tag.name.encode())
105
105
 
106
106
  async def open_connection(self):
107
107
  """Establish connection and callbacks."""
108
108
  self.reader, self.writer = await asyncio.open_connection(
109
109
  self.ip, self.port)
110
110
  self.addr = self.writer.get_extra_info('sockname')
111
- self.write(pc.CMD_LOG, 0, 0, f'{self.module} connected'.encode())
111
+ self.write(pc.COMMAND.LOG, 0, 0, f'{self.module} connected'.encode())
112
112
  logging.warning(f'connected {self.addr} {self.port}')
113
113
  for tag in Tag.get_all_tags().values():
114
114
  self.add_tag(tag)
@@ -157,14 +157,15 @@ class BusClient:
157
157
 
158
158
  def process(self, cmd, tag_id, time_us, value):
159
159
  """Process bus message, updating the local tag value."""
160
- if cmd == pc.CMD_ERR:
161
- logging.warning(f'Bus server error {tag_id} {value}')
160
+ if cmd == pc.COMMAND.ERR:
161
+ logging.warning(f'Bus server error {tag_id} '
162
+ f'{pc.COMMAND.text(cmd)} {value}')
162
163
  return
163
- if cmd == pc.CMD_ID:
164
+ if cmd == pc.COMMAND.ID:
164
165
  tag = self.tag_by_name[value.decode()]
165
166
  tag.id = tag_id
166
167
  self.tag_by_id[tag_id] = tag
167
- self.write(pc.CMD_SUB, tag.id, 0, b'')
168
+ self.write(pc.COMMAND.SUB, tag.id, 0, b'')
168
169
  if tag.name in self.tag_by_name:
169
170
  self.tag_by_id[tag.id] = tag
170
171
  if tag.name in self.to_publish:
@@ -172,7 +173,7 @@ class BusClient:
172
173
  del self.to_publish[tag.name]
173
174
  return
174
175
  tag = self.tag_by_id[tag_id]
175
- if cmd == pc.CMD_SET:
176
+ if cmd == pc.COMMAND.SET:
176
177
  if value is None:
177
178
  try:
178
179
  if self.tag_info is None:
@@ -186,17 +187,17 @@ class BusClient:
186
187
  pass
187
188
  return
188
189
  data_type = struct.unpack_from('!B', value, offset=0)[0]
189
- if data_type == pc.TYPE_FLOAT:
190
+ if data_type == pc.TYPE.FLOAT:
190
191
  data = struct.unpack_from('!d', value, offset=1)[0]
191
- elif data_type == pc.TYPE_INT:
192
+ elif data_type == pc.TYPE.INT:
192
193
  data = struct.unpack_from('!q', value, offset=1)[0]
193
- elif data_type == pc.TYPE_BYTES:
194
+ elif data_type == pc.TYPE.BYTES:
194
195
  data = struct.unpack_from(f'!{len(value) - 1}s', value,
195
196
  offset=1)[0]
196
- elif data_type == pc.TYPE_STR:
197
+ elif data_type == pc.TYPE.STR:
197
198
  data = struct.unpack_from(f'!{len(value) - 1}s', value,
198
199
  offset=1)[0].decode()
199
- elif data_type == pc.TYPE_JSON:
200
+ elif data_type == pc.TYPE.JSON:
200
201
  data = json.loads(struct.unpack_from(f'!{len(value) - 1}s',
201
202
  value, offset=1
202
203
  )[0].decode())
@@ -204,7 +205,7 @@ class BusClient:
204
205
  logging.warning(f'process error {tag.name} {tag.type} {value}')
205
206
  return
206
207
  tag.value = data, time_us, id(self)
207
- elif cmd == pc.CMD_RTA:
208
+ elif cmd == pc.COMMAND.RTA:
208
209
  data = struct.unpack_from(f'!{len(value) - 1}s', value, offset=1
209
210
  )[0].decode()
210
211
  data = json.loads(data)
@@ -85,12 +85,12 @@ class BusConnection():
85
85
  del self.addr
86
86
  del self.pending
87
87
 
88
- def write(self, command: pc.COMMANDS, tag_id: int, time_us: int,
88
+ def write(self, command: pc.COMMAND, tag_id: int, time_us: int,
89
89
  data: bytes):
90
90
  """Write a message."""
91
91
  if data is None:
92
92
  data = b''
93
- logging.info(f'write {pc.CMD_TEXT[command]} {tag_id}')
93
+ logging.info(f'write {command.text} {tag_id}')
94
94
  for i in range(0, len(data) + 1, pc.MAX_LEN):
95
95
  snip = data[i:i+pc.MAX_LEN]
96
96
  size = len(snip)
@@ -168,78 +168,78 @@ class BusServer:
168
168
  if tag.from_bus == bus_id:
169
169
  return
170
170
  try:
171
- self.connections[bus_id].write(pc.CMD_SET, tag.id, tag.time_us,
171
+ self.connections[bus_id].write(pc.COMMAND.SET, tag.id, tag.time_us,
172
172
  tag.value)
173
173
  except KeyError:
174
174
  tag.del_callback(self.publish, bus_id)
175
175
 
176
- def process(self, bus_id, cmd, tag_id, time_us, data):
176
+ def process(self, bus_id, cmd: int, tag_id, time_us, data):
177
177
  """Process bus message, updating the local tag value."""
178
- logging.info(f'write {pc.CMD_TEXT[cmd]} {tag_id} '
178
+ logging.info(f'write {pc.COMMAND.text(cmd)} {tag_id} '
179
179
  f'{"None" if data is None else data[:20]}')
180
- if cmd == pc.CMD_SET:
180
+ if cmd == pc.COMMAND.SET:
181
181
  try:
182
182
  tag = BusTags._tag_by_id[tag_id]
183
183
  tag.update(data, time_us, bus_id)
184
184
  except KeyError:
185
185
  self.connections[bus_id].write(
186
- pc.CMD_ERR, tag_id, time_us,
186
+ pc.COMMAND.ERR, tag_id, time_us,
187
187
  f"SET KeyError {tag_id}".encode())
188
- elif cmd == pc.CMD_RTA:
188
+ elif cmd == pc.COMMAND.RTA:
189
189
  try:
190
190
  tag = BusTags._tag_by_id[tag_id]
191
191
  except KeyError:
192
192
  self.connections[bus_id].write(
193
- pc.CMD_ERR, tag_id, time_us,
193
+ pc.COMMAND.ERR, tag_id, time_us,
194
194
  f"RTA KeyError {tag_id}".encode())
195
195
  try:
196
196
  self.connections[tag.from_bus].write(
197
- pc.CMD_RTA, tag_id, tag.time_us, data)
197
+ pc.COMMAND.RTA, tag_id, tag.time_us, data)
198
198
  except KeyError:
199
199
  logging.warning(f'likely busclient for {tag.name} is gone')
200
200
  except Exception as e:
201
201
  self.connections[bus_id].write(
202
- pc.CMD_ERR, tag_id, time_us,
202
+ pc.COMMAND.ERR, tag_id, time_us,
203
203
  f"RTA {tag_id} {e}".encode())
204
204
  """Reply comes from another BusClient, not the Server."""
205
- elif cmd == pc.CMD_SUB:
205
+ elif cmd == pc.COMMAND.SUB:
206
206
  try:
207
207
  tag = BusTags._tag_by_id[tag_id]
208
208
  except KeyError:
209
209
  self.connections[bus_id].write(
210
- pc.CMD_ERR, tag_id, time_us,
210
+ pc.COMMAND.ERR, tag_id, time_us,
211
211
  f"SUBscribe KeyError {tag_id}".encode())
212
- self.connections[bus_id].write(pc.CMD_SET, tag_id, tag.time_us,
212
+ self.connections[bus_id].write(pc.COMMAND.SET, tag_id, tag.time_us,
213
213
  tag.value)
214
214
  tag.add_callback(self.publish, bus_id)
215
- elif cmd == pc.CMD_ID:
215
+ elif cmd == pc.COMMAND.ID:
216
216
  try:
217
217
  tag = BusTags._tag_by_name[data]
218
218
  except KeyError:
219
219
  self.connections[bus_id].write(
220
- pc.CMD_ERR, tag_id, time_us,
220
+ pc.COMMAND.ERR, tag_id, time_us,
221
221
  f"ID {data} undefined".encode())
222
222
  tag = BusTag(data)
223
- self.connections[bus_id].write(pc.CMD_ID, tag.id, tag.time_us,
223
+ self.connections[bus_id].write(pc.COMMAND.ID, tag.id, tag.time_us,
224
224
  tag.name)
225
- elif cmd == pc.CMD_GET:
225
+ elif cmd == pc.COMMAND.GET:
226
226
  try:
227
227
  tag = BusTags._tag_by_id[tag_id]
228
228
  except KeyError:
229
229
  self.connections[bus_id].write(
230
- pc.CMD_ERR, tag_id, time_us,
230
+ pc.COMMAND.ERR, tag_id, time_us,
231
231
  f"GET KeyError for {tag_id}".encode())
232
- self.connections[bus_id].write(pc.CMD_SET, tag.id, tag.time_us,
232
+ self.connections[bus_id].write(pc.COMMAND.SET, tag.id, tag.time_us,
233
233
  tag.value)
234
- elif cmd == pc.CMD_UNSUB:
234
+ elif cmd == pc.COMMAND.UNSUB:
235
235
  try:
236
236
  tag = BusTags._tag_by_id[tag_id]
237
237
  except KeyError:
238
238
  self.connections[bus_id].write(
239
- pc.CMD_ERR, tag_id, time_us,
239
+ pc.COMMAND.ERR, tag_id, time_us,
240
240
  f"UNSubscribe KeyError for {tag_id}".encode())
241
241
  tag.del_callback(self.publish, bus_id)
242
- elif cmd == pc.CMD_LIST:
242
+ elif cmd == pc.COMMAND.LIST:
243
243
  tagname_list = []
244
244
  if len(data) == 0:
245
245
  for _, tag in BusTags._tag_by_id.items():
@@ -260,8 +260,8 @@ class BusServer:
260
260
  if data in tag.name:
261
261
  tagname_list.append(tag.name)
262
262
  self.connections[bus_id].write(
263
- pc.CMD_LIST, 0, time_us, b' '.join(tagname_list))
264
- elif cmd == pc.CMD_LOG:
263
+ pc.COMMAND.LIST, 0, time_us, b' '.join(tagname_list))
264
+ elif cmd == pc.COMMAND.LOG:
265
265
  if len(data) > 300:
266
266
  logging.warning(f'process: log message too long from {bus_id}')
267
267
  else:
@@ -273,6 +273,9 @@ class BusServer:
273
273
  """Process read messages, delete broken connections."""
274
274
  bus_id, cmd, tag_id, time_us, data = command
275
275
  if cmd is None:
276
+ # Clean up tag subscriptions before deleting it
277
+ for tag in BusTags._tag_by_id.values():
278
+ tag.del_callback(self.publish, bus_id)
276
279
  self.connections[bus_id].delete()
277
280
  del self.connections[bus_id]
278
281
  return
@@ -13,7 +13,6 @@ except ModuleNotFoundError:
13
13
 
14
14
  class EC:
15
15
  """Escape codes."""
16
-
17
16
  backspace = b'\x7f'
18
17
  enter = b'\r'
19
18
  tab = b'\t'
@@ -59,17 +58,14 @@ class KeypressProtocol(asyncio.Protocol):
59
58
  def data_received(self, data):
60
59
  """Got keypress, update edit line, send to writer."""
61
60
  if len(data) == 1:
62
- if data == EC.backspace:
61
+ if data == EC.backspace and self.cursor > 0:
63
62
  self.line = self.line[:self.cursor-1] + self.line[self.cursor:]
64
- if self.cursor > 0:
65
- self.cursor -= 1
63
+ self.cursor -= 1
66
64
  elif data == EC.enter:
67
- self.stash = None
68
- if self.lines:
69
- if self.line != self.lines[-1]:
70
- self.lines.append(self.line)
71
- else:
65
+ if self.line and (not self.lines or
66
+ self.line != self.lines[-1]):
72
67
  self.lines.append(self.line)
68
+ self.stash = None
73
69
  self.edit_line(None, 0)
74
70
  self.process_command(self.line)
75
71
  self.line = None
@@ -129,11 +125,12 @@ class ConsoleWriter:
129
125
 
130
126
  def write(self, data: bytes):
131
127
  """Stream writer, primarily for logging."""
128
+ cursor_str = b''
129
+ if self.edit is not None and self.cursor > 0:
130
+ cursor_str = b'\x1b[' + str(self.cursor).encode() + b'C'
132
131
  ln = EC.cr_clr + data + b'\r\n'
133
132
  if self.edit is not None:
134
- ln += EC.cr_clr + self.edit + EC.mv_left
135
- if self.cursor > 0:
136
- ln += b'\x1b[' + str(self.cursor).encode() + b'C'
133
+ ln += EC.cr_clr + self.edit + EC.mv_left + cursor_str
137
134
  sys.stdout.buffer.write(ln)
138
135
  sys.stdout.flush()
139
136
 
@@ -173,8 +170,8 @@ class Console:
173
170
  # all to add '\r\n' to the logging output
174
171
  logger = logging.getLogger()
175
172
  handler = CustomHandler()
176
- handler.setFormatter(logging.Formatter(
177
- '%(levelname)s:%(name)s:%(message)s'))
173
+ handler.setFormatter(logging.Formatter('%(levelname)s:console: '
174
+ '%(message)s'))
178
175
  logger.handlers.clear()
179
176
  logger.addHandler(handler)
180
177
  self.busclient = BusClient(bus_ip, bus_port, module='Console')
@@ -0,0 +1,25 @@
1
+ bus_ip: 127.0.0.1
2
+ bus_port: 1325
3
+ proxy:
4
+ api:
5
+ api_key: ${OPENWEATHER_API_KEY}
6
+ units: metric
7
+ locations:
8
+ Murupara:
9
+ lat: -38.45747036367239
10
+ lon: 176.70484322806843
11
+ times: [3]
12
+ parameters:
13
+ - Temp
14
+ - WindSpeed
15
+ - WindDir
16
+ - Rain
17
+ tags:
18
+ - Murupara_Temp
19
+ - Murupara_WindSpeed
20
+ - Murupara_WindDir
21
+ - Murupara_Rain
22
+ - Murupara_Temp_03
23
+ - Murupara_WindSpeed_03
24
+ - Murupara_WindDir_03
25
+ - Murupara_Rain_03
@@ -256,10 +256,13 @@ Router_eth8_bytes_out:
256
256
  type: int
257
257
  localhost_ping:
258
258
  desc: Ping time to localhost
259
+ type: int
259
260
  units: ms
260
261
  google_ping:
261
262
  desc: Ping time to google
263
+ type: int
262
264
  units: ms
263
265
  electronet_ping:
264
266
  desc: Ping time to electronet
267
+ type: int
265
268
  units: ms
@@ -88,269 +88,6 @@ pages:
88
88
  parent: Dropdown
89
89
  items:
90
90
  - type: files
91
- - name: Temperature
92
- parent: Weather
93
- items:
94
- - type: uplot # Do all times in seconds, which uplot uses.
95
- ms:
96
- desc: Temperature
97
- age: 172800
98
- legend_pos: left
99
- time_pos: left
100
- time_res: m
101
- axes:
102
- - scale: x
103
- range: [-604800, 86400] # 86400 172800 1209600
104
- - scale: 'C'
105
- range: [0.0, 35.0]
106
- dp: 1
107
- series:
108
- - tagname: temperature
109
- label: Current Temperature
110
- scale: 'C'
111
- color: black
112
- width: 2
113
- dp: 1
114
- - tagname: temperature_01
115
- label: 1h Temperature
116
- scale: 'C'
117
- color: darkgray
118
- width: 1.5
119
- dp: 1
120
- - tagname: temperature_04
121
- label: 4h Temperature
122
- scale: 'C'
123
- color: green
124
- width: 1
125
- dp: 1
126
- - tagname: temperature_12
127
- label: 12h Temperature
128
- scale: 'C'
129
- color: orange
130
- width: 0.75
131
- dp: 1
132
- - tagname: temperature_24
133
- label: 24h Temperature
134
- scale: 'C'
135
- color: red
136
- width: 0.5
137
- dp: 1
138
- - name: Wind Speed
139
- parent: Weather
140
- items:
141
- - type: uplot # Do all times in seconds, which uplot uses.
142
- ms:
143
- desc: Wind Speed
144
- age: 172800
145
- legend_pos: left
146
- time_pos: left
147
- time_res: m
148
- axes:
149
- - scale: x
150
- range: [-604800, 86400] # 86400 172800 1209600
151
- - scale: 'm/s'
152
- range: [0.0, 20.0]
153
- dp: 1
154
- series:
155
- - tagname: windSpeed
156
- label: Current Wind Speed
157
- scale: 'm/s'
158
- color: black
159
- width: 2
160
- dp: 1
161
- - tagname: windSpeed_01
162
- label: 1h Wind Speed
163
- scale: 'm/s'
164
- color: darkgray
165
- width: 1.5
166
- dp: 1
167
- - tagname: windSpeed_04
168
- label: 4h Wind Speed
169
- scale: 'm/s'
170
- color: green
171
- width: 1
172
- dp: 1
173
- - tagname: windSpeed_12
174
- label: 12h Wind Speed
175
- scale: 'm/s'
176
- color: orange
177
- width: 0.75
178
- dp: 1
179
- - tagname: windSpeed_24
180
- label: 24h Wind Speed
181
- scale: 'm/s'
182
- color: red
183
- width: 0.5
184
- dp: 1
185
- - name: Wind Direction
186
- parent: Weather
187
- items:
188
- - type: uplot # Do all times in seconds, which uplot uses.
189
- ms:
190
- desc: Wind Direction
191
- age: 172800
192
- legend_pos: left
193
- time_pos: left
194
- time_res: m
195
- axes:
196
- - scale: x
197
- range: [-604800, 86400] # 86400 172800 1209600
198
- - scale: 'deg'
199
- range: [0.0, 360.0]
200
- dp: 1
201
- series:
202
- - tagname: windDirection
203
- label: Current Wind Direction
204
- scale: 'deg'
205
- color: black
206
- width: 2
207
- dp: 1
208
- - tagname: windDirection_01
209
- label: 1h Wind Direction
210
- scale: 'deg'
211
- color: darkgray
212
- width: 1.5
213
- dp: 1
214
- - tagname: windDirection_04
215
- label: 4h Wind Direction
216
- scale: 'deg'
217
- color: green
218
- width: 1
219
- dp: 1
220
- - tagname: windDirection_12
221
- label: 12h Wind Direction
222
- scale: 'deg'
223
- color: orange
224
- width: 0.75
225
- dp: 1
226
- - tagname: windDirection_24
227
- label: 24h Wind Direction
228
- scale: 'deg'
229
- color: red
230
- width: 0.5
231
- dp: 1
232
- - name: Rain Accumulation
233
- parent: Weather
234
- items:
235
- - type: uplot # Do all times in seconds, which uplot uses.
236
- ms:
237
- desc: Rain Accumulation
238
- age: 172800
239
- legend_pos: left
240
- time_pos: left
241
- time_res: m
242
- axes:
243
- - scale: x
244
- range: [-604800, 86400] # 86400 172800 1209600
245
- - scale: 'mm'
246
- range: [0.0, 10.0]
247
- dp: 1
248
- series:
249
- - tagname: rainAccumulation
250
- label: Current Rain Accumulation
251
- scale: 'mm'
252
- color: black
253
- width: 2
254
- dp: 1
255
- - tagname: rainAccumulation_01
256
- label: 1h Rain Accumulation
257
- scale: 'mm'
258
- color: darkgray
259
- width: 1.5
260
- dp: 1
261
- - tagname: rainAccumulation_04
262
- label: 4h Rain Accumulation
263
- scale: 'mm'
264
- color: green
265
- width: 1
266
- dp: 1
267
- - tagname: rainAccumulation_12
268
- label: 12h Rain Accumulation
269
- scale: 'mm'
270
- color: orange
271
- width: 0.75
272
- dp: 1
273
- - tagname: rainAccumulation_24
274
- label: 24h Rain Accumulation
275
- scale: 'mm'
276
- color: red
277
- width: 0.5
278
- dp: 1
279
- - name: Humidity
280
- parent: Weather
281
- items:
282
- - type: uplot # Do all times in seconds, which uplot uses.
283
- ms:
284
- desc: Humidity
285
- age: 172800
286
- legend_pos: left
287
- time_pos: left
288
- time_res: m
289
- axes:
290
- - scale: x
291
- range: [-604800, 86400] # 86400 172800 1209600
292
- - scale: '%'
293
- range: [0.0, 100.0]
294
- dp: 1
295
- series:
296
- - tagname: humidity
297
- label: Current Humidity
298
- scale: '%'
299
- color: black
300
- width: 2
301
- dp: 1
302
- - tagname: humidity_01
303
- label: 1h Humidity
304
- scale: '%'
305
- color: darkgray
306
- width: 1.5
307
- dp: 1
308
- - tagname: humidity_04
309
- label: 4h Humidity
310
- scale: '%'
311
- color: green
312
- width: 1
313
- dp: 1
314
- - tagname: humidity_12
315
- label: 12h Humidity
316
- scale: '%'
317
- color: orange
318
- width: 0.75
319
- dp: 1
320
- - tagname: humidity_24
321
- label: 24h Humidity
322
- scale: '%'
323
- color: red
324
- width: 0.5
325
- dp: 1
326
- - name: Values
327
- parent: Weather
328
- items:
329
- - {tagname: temperature, type: value}
330
- - {tagname: temperature_01, type: value}
331
- - {tagname: temperature_04, type: value}
332
- - {tagname: temperature_12, type: value}
333
- - {tagname: temperature_24, type: value}
334
- - {tagname: windSpeed, type: value}
335
- - {tagname: windSpeed_01, type: value}
336
- - {tagname: windSpeed_04, type: value}
337
- - {tagname: windSpeed_12, type: value}
338
- - {tagname: windSpeed_24, type: value}
339
- - {tagname: windDirection, type: value}
340
- - {tagname: windDirection_01, type: value}
341
- - {tagname: windDirection_04, type: value}
342
- - {tagname: windDirection_12, type: value}
343
- - {tagname: windDirection_24, type: value}
344
- - {tagname: rainAccumulation, type: value}
345
- - {tagname: rainAccumulation_01, type: value}
346
- - {tagname: rainAccumulation_04, type: value}
347
- - {tagname: rainAccumulation_12, type: value}
348
- - {tagname: rainAccumulation_24, type: value}
349
- - {tagname: humidity, type: value}
350
- - {tagname: humidity_01, type: value}
351
- - {tagname: humidity_04, type: value}
352
- - {tagname: humidity_12, type: value}
353
- - {tagname: humidity_24, type: value}
354
91
  - name: Logix
355
92
  items:
356
93
  - {tagname: Ani_Fin_20, type: setpoint}