simo 2.3.7__py3-none-any.whl → 2.4.2__py3-none-any.whl

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 simo might be problematic. Click here for more details.

Files changed (46) hide show
  1. simo/__pycache__/settings.cpython-38.pyc +0 -0
  2. simo/backups/__pycache__/admin.cpython-38.pyc +0 -0
  3. simo/backups/__pycache__/dynamic_settings.cpython-38.pyc +0 -0
  4. simo/backups/__pycache__/models.cpython-38.pyc +0 -0
  5. simo/backups/__pycache__/tasks.cpython-38.pyc +0 -0
  6. simo/backups/admin.py +64 -3
  7. simo/backups/dynamic_settings.py +0 -7
  8. simo/backups/migrations/0002_backuplog_backup_level_backup_size.py +32 -0
  9. simo/backups/migrations/0003_alter_backuplog_options_alter_backup_size.py +22 -0
  10. simo/backups/migrations/0004_alter_backup_options_alter_backuplog_options_and_more.py +29 -0
  11. simo/backups/migrations/__pycache__/0002_backuplog_backup_level_backup_size.cpython-38.pyc +0 -0
  12. simo/backups/migrations/__pycache__/0003_alter_backuplog_options_alter_backup_size.cpython-38.pyc +0 -0
  13. simo/backups/migrations/__pycache__/0004_alter_backup_options_alter_backuplog_options_and_more.cpython-38.pyc +0 -0
  14. simo/backups/models.py +9 -2
  15. simo/backups/tasks.py +255 -113
  16. simo/core/__pycache__/api.cpython-38.pyc +0 -0
  17. simo/core/__pycache__/context.cpython-38.pyc +0 -0
  18. simo/core/__pycache__/controllers.cpython-38.pyc +0 -0
  19. simo/core/__pycache__/models.cpython-38.pyc +0 -0
  20. simo/core/__pycache__/tasks.cpython-38.pyc +0 -0
  21. simo/core/__pycache__/views.cpython-38.pyc +0 -0
  22. simo/core/api.py +2 -7
  23. simo/core/controllers.py +8 -1
  24. simo/core/management/_hub_template/hub/supervisor.conf +4 -0
  25. simo/core/migrations/0042_alter_instance_timezone.py +18 -0
  26. simo/core/migrations/__pycache__/0042_alter_instance_timezone.cpython-38.pyc +0 -0
  27. simo/core/models.py +6 -2
  28. simo/core/tasks.py +8 -1
  29. simo/core/views.py +4 -12
  30. simo/fleet/__pycache__/forms.cpython-38.pyc +0 -0
  31. simo/fleet/forms.py +140 -176
  32. simo/fleet/migrations/0038_alter_colonel_type.py +18 -0
  33. simo/fleet/migrations/__pycache__/0038_alter_colonel_type.cpython-38.pyc +0 -0
  34. simo/notifications/__pycache__/models.cpython-38.pyc +0 -0
  35. simo/settings.py +1 -0
  36. simo/users/__pycache__/models.cpython-38.pyc +0 -0
  37. simo/users/__pycache__/serializers.cpython-38.pyc +0 -0
  38. simo/users/migrations/0033_alter_user_ssh_key.py +18 -0
  39. simo/users/migrations/__pycache__/0033_alter_user_ssh_key.cpython-38.pyc +0 -0
  40. simo/users/models.py +38 -16
  41. {simo-2.3.7.dist-info → simo-2.4.2.dist-info}/METADATA +2 -1
  42. {simo-2.3.7.dist-info → simo-2.4.2.dist-info}/RECORD +46 -33
  43. {simo-2.3.7.dist-info → simo-2.4.2.dist-info}/LICENSE.md +0 -0
  44. {simo-2.3.7.dist-info → simo-2.4.2.dist-info}/WHEEL +0 -0
  45. {simo-2.3.7.dist-info → simo-2.4.2.dist-info}/entry_points.txt +0 -0
  46. {simo-2.3.7.dist-info → simo-2.4.2.dist-info}/top_level.txt +0 -0
simo/backups/tasks.py CHANGED
@@ -1,7 +1,8 @@
1
- import os, subprocess, json, uuid, datetime, shutil
1
+ import os, subprocess, json, uuid, datetime, shutil, pytz
2
2
  from datetime import datetime, timezone
3
3
  from celeryc import celery_app
4
4
  from simo.conf import dynamic_settings
5
+ from simo.core.utils.helpers import get_random_string
5
6
 
6
7
 
7
8
  @celery_app.task
@@ -9,15 +10,15 @@ def check_backups():
9
10
  '''
10
11
  syncs up backups on external medium to the database
11
12
  '''
12
- from .models import Backup
13
+ from simo.backups.models import Backup
13
14
 
14
15
  try:
15
- lv_group, lv_name, mountpoint = get_partitions()
16
+ lv_group, lv_name, sd_mountpoint = get_partitions()
16
17
  except:
17
18
  return Backup.objects.all().delete()
18
19
 
19
20
 
20
- backups_dir = os.path.join(mountpoint, 'simo_backups')
21
+ backups_dir = os.path.join(sd_mountpoint, 'simo_backups')
21
22
  if not os.path.exists(backups_dir):
22
23
  return Backup.objects.all().delete()
23
24
 
@@ -33,40 +34,108 @@ def check_backups():
33
34
  year, month = int(year), int(month)
34
35
  except:
35
36
  continue
36
- for filename in os.listdir(os.path.join(hub_dir, month_folder)):
37
- try:
38
- day, time, back = filename.split('.')
39
- hour, minute, second = time.split('-')
40
- day, hour, minute, second = \
41
- int(day), int(hour), int(minute), int(second)
42
- except:
43
- continue
37
+
38
+ month_folder_path = os.path.join(hub_dir, month_folder)
39
+ res = subprocess.run(
40
+ f"borg list {month_folder_path} --json",
41
+ shell=True, stdout=subprocess.PIPE
42
+ )
43
+ try:
44
+ archives = json.loads(res.stdout.decode())['archives']
45
+ except Exception as e:
46
+ continue
47
+
48
+ for archive in archives:
49
+ make_datetime = datetime.fromisoformat(archive['start'])
50
+ make_datetime = make_datetime.replace(tzinfo=pytz.UTC)
51
+ filepath = f"{month_folder_path}::{archive['name']}"
44
52
 
45
53
  obj, new = Backup.objects.update_or_create(
46
- datetime=datetime(
47
- year, month, day, hour, minute, second,
48
- tzinfo=timezone.utc
49
- ), mac=hub_mac, defaults={
50
- 'filepath': os.path.join(
51
- hub_dir, month_folder, filename
52
- )
54
+ datetime=make_datetime, mac=hub_mac, defaults={
55
+ 'filepath': f"{month_folder_path}::{archive['name']}",
53
56
  }
54
57
  )
55
58
  backups_mentioned.append(obj.id)
56
59
 
57
60
  Backup.objects.all().exclude(id__in=backups_mentioned).delete()
58
61
 
59
- dynamic_settings['backups__last_check'] = datetime.now()
62
+ dynamic_settings['backups__last_check'] = int(datetime.now().timestamp())
63
+
64
+
65
+ def clean_backup_snaps(lv_group, lv_name):
66
+ res = subprocess.run(
67
+ 'lvs --report-format json', shell=True, stdout=subprocess.PIPE
68
+ )
69
+ lvs_data = json.loads(res.stdout.decode())
70
+ for volume in lvs_data['report'][0]['lv']:
71
+ if volume['vg_name'] != lv_group:
72
+ continue
73
+ if volume['origin'] != lv_name:
74
+ continue
75
+ if not volume['lv_name'].startswith(f"{lv_name}-bk-"):
76
+ continue
77
+ subprocess.run(
78
+ f"lvremove -f {lv_group}/{volume['lv_name']}", shell=True
79
+ )
80
+
60
81
 
61
82
 
62
- def create_snap(lv_group, lv_name):
83
+ def create_snap(lv_group, lv_name, snap_name=None, size=None, try_no=1):
84
+ '''
85
+ :param lv_group:
86
+ :param lv_name:
87
+ :param snap_name: random snap name will be generated if not provided
88
+ :param size: Size in GB. If not provided, maximum available space in lvm will be used.
89
+ :return: snap_name
90
+ '''
91
+ if not snap_name:
92
+ snap_name = f"{lv_name}-bk-{get_random_string(5)}"
93
+
94
+ clean_backup_snaps(lv_group, lv_name)
95
+
96
+ res = subprocess.run(
97
+ 'vgs --report-format json', shell=True, stdout=subprocess.PIPE
98
+ )
63
99
  try:
64
- return subprocess.check_output(
65
- f'lvcreate -s -n {lv_name}-snap {lv_group}/{lv_name} -L 3G',
66
- shell=True
67
- ).decode()
100
+ vgs_data = json.loads(res.stdout.decode())
101
+ free_space = vgs_data['report'][0]['vg'][0]['vg_free']
68
102
  except:
69
- return ''
103
+ if try_no < 3:
104
+ clean_backup_snaps(lv_group, lv_name)
105
+ return create_snap(lv_group, lv_name, snap_name, size, try_no+1)
106
+ raise Exception("Unable to find free space on LVM!")
107
+
108
+ if not free_space.lower().endswith('g'):
109
+ if try_no < 3:
110
+ clean_backup_snaps(lv_group, lv_name)
111
+ return create_snap(lv_group, lv_name, snap_name, size, try_no+1)
112
+ raise Exception("Not enough free space on LVM!")
113
+
114
+ free_space = int(float(
115
+ vgs_data['report'][0]['vg'][0]['vg_free'].strip('g').strip('<')
116
+ ))
117
+
118
+ if not size:
119
+ size = free_space
120
+ else:
121
+ if size > free_space:
122
+ if try_no < 3:
123
+ clean_backup_snaps(lv_group, lv_name)
124
+ return create_snap(lv_group, lv_name, snap_name, size, try_no + 1)
125
+ raise Exception(
126
+ f"There's only {free_space}G available on LVM, "
127
+ f"but you asked for {size}G"
128
+ )
129
+
130
+ res = subprocess.run(
131
+ f'lvcreate -s -n {snap_name} {lv_group}/{lv_name} -L {size}G',
132
+ shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
133
+ )
134
+ if res.returncode:
135
+ raise Exception(res.stderr)
136
+
137
+ return snap_name
138
+
70
139
 
71
140
 
72
141
  def get_lvm_partition(lsblk_data):
@@ -93,6 +162,7 @@ def get_backup_device(lsblk_data):
93
162
 
94
163
 
95
164
  def get_partitions():
165
+ from simo.backups.models import BackupLog
96
166
 
97
167
  lsblk_data = json.loads(subprocess.check_output(
98
168
  'lsblk --output NAME,HOTPLUG,MOUNTPOINT,FSTYPE,TYPE,LABEL,PARTLABEL --json',
@@ -104,7 +174,9 @@ def get_partitions():
104
174
  lvm_partition = get_lvm_partition(lsblk_data)
105
175
  if not lvm_partition:
106
176
  print("No LVM partition!")
107
- dynamic_settings['backups__last_error'] = 'No LVM partition!'
177
+ BackupLog.objects.create(
178
+ level='warning', msg="Can't backup. No LVM partition!"
179
+ )
108
180
  return
109
181
 
110
182
  try:
@@ -114,14 +186,17 @@ def get_partitions():
114
186
  lv_name = name[split_at + 1:].replace('--', '-')
115
187
  except:
116
188
  print("Failed to identify LVM partition")
117
- dynamic_settings['backups__last_error'] = \
118
- 'Failed to identify LVM partition'
189
+ BackupLog.objects.create(
190
+ level='warning', msg="Can't backup. Failed to identify LVM partition."
191
+ )
119
192
  return
120
193
 
121
194
  if not lv_name:
122
195
  print("LVM was not found on this system. Abort!")
123
- dynamic_settings['backups__last_error'] = \
124
- 'Failed to identify LVM partition name'
196
+ BackupLog.objects.create(
197
+ level='warning',
198
+ msg="Can't backup. Failed to identify LVM partition name."
199
+ )
125
200
  return
126
201
 
127
202
 
@@ -130,143 +205,210 @@ def get_partitions():
130
205
  backup_device = get_backup_device(lsblk_data)
131
206
 
132
207
  if not backup_device:
133
- dynamic_settings['backups__last_error'] = \
134
- 'No external exFAT backup device on this machine'
208
+ BackupLog.objects.create(
209
+ level='warning',
210
+ msg="Can't backup. No external exFAT backup device on this machine."
211
+ )
135
212
  return
136
213
 
137
214
  if lvm_partition.get('partlabel'):
138
- mountpoint = f"/media/{backup_device['partlabel']}"
215
+ sd_mountpoint = f"/media/{backup_device['partlabel']}"
139
216
  elif lvm_partition.get('label'):
140
- mountpoint = f"/media/{backup_device['label']}"
217
+ sd_mountpoint = f"/media/{backup_device['label']}"
141
218
  else:
142
- mountpoint = f"/media/{backup_device['name']}"
219
+ sd_mountpoint = f"/media/{backup_device['name']}"
143
220
 
144
- if not os.path.exists(mountpoint):
145
- os.makedirs(mountpoint)
221
+ if not os.path.exists(sd_mountpoint):
222
+ os.makedirs(sd_mountpoint)
146
223
 
147
- if backup_device.get('mountpoint') != mountpoint:
224
+ if backup_device.get('mountpoint') != sd_mountpoint:
148
225
 
149
226
  if backup_device.get('mountpoint'):
150
227
  subprocess.call(f"umount {backup_device['mountpoint']}", shell=True)
151
228
 
152
229
  subprocess.call(
153
- f'mount /dev/{backup_device["name"]} {mountpoint}', shell=True,
230
+ f'mount /dev/{backup_device["name"]} {sd_mountpoint}', shell=True,
154
231
  stdout=subprocess.PIPE
155
232
  )
156
233
 
157
- return lv_group, lv_name, mountpoint
234
+ return lv_group, lv_name, sd_mountpoint
158
235
 
159
236
 
160
237
  @celery_app.task
161
238
  def perform_backup():
162
-
239
+ from simo.backups.models import BackupLog
163
240
  try:
164
- lv_group, lv_name, mountpoint = get_partitions()
241
+ lv_group, lv_name, sd_mountpoint = get_partitions()
165
242
  except:
166
243
  return
167
244
 
168
- output = create_snap(lv_group, lv_name)
169
- if not output:
170
- subprocess.check_output(
171
- f'lvremove -f {lv_group}/{lv_name}-snap',
172
- shell=True
173
- )
174
- output = create_snap(lv_group, lv_name)
245
+ snap_mount_point = '/var/backups/simo-main'
246
+ subprocess.run(f'umount {snap_mount_point}', shell=True)
175
247
 
176
- if f'Logical volume "{lv_name}-snap" created' not in output:
177
- print(output)
178
- print(f"Unable to create {lv_name}-snap.")
248
+ try:
249
+ snap_name = create_snap(lv_group, lv_name)
250
+ except Exception as e:
251
+ print("Error creating temporary snap\n" + str(e))
252
+ BackupLog.objects.create(
253
+ level='error',
254
+ msg="Backup error. Unable to create temporary snap\n" + str(e)
255
+ )
179
256
  return
180
257
 
258
+ shutil.rmtree(snap_mount_point, ignore_errors=True)
259
+ os.makedirs(snap_mount_point)
260
+ subprocess.run([
261
+ "mount",
262
+ f"/dev/mapper/{lv_group}-{snap_name.replace('-', '--')}",
263
+ snap_mount_point
264
+ ])
265
+
181
266
  mac = str(hex(uuid.getnode()))
182
- device_backups_path = f'{mountpoint}/simo_backups/hub-{mac}'
183
- if not os.path.exists(device_backups_path):
184
- os.makedirs(device_backups_path)
267
+ device_backups_path = f'{sd_mountpoint}/simo_backups/hub-{mac}'
185
268
 
186
- now = datetime.datetime.now()
187
- level = now.day
269
+
270
+ now = datetime.now()
188
271
  month_folder = os.path.join(
189
272
  device_backups_path, f'{now.year}-{now.month}'
190
273
  )
191
274
  if not os.path.exists(month_folder):
192
275
  os.makedirs(month_folder)
193
- level = 0
194
-
195
- if level != 0:
196
- # check if level 0 exists
197
- level_0_exists = False
198
- for filename in os.listdir(month_folder):
199
- if '-' not in filename:
200
- continue
201
- try:
202
- level, date = filename.split('-')
203
- level = int(level)
204
- if level == 0:
205
- level_0_exists = True
206
- break
207
- except:
208
- continue
209
- if not level_0_exists:
210
- print("Level 0 does not exist! Backups must be started from 0!")
276
+ subprocess.run(
277
+ f'borg init --encryption=none {month_folder}', shell=True
278
+ )
279
+ else:
280
+ res = subprocess.run(
281
+ f'borg info --json {month_folder}',
282
+ shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
283
+ )
284
+ if res.returncode:
211
285
  shutil.rmtree(month_folder)
212
- os.makedirs(month_folder)
213
- level = 0
286
+ subprocess.run(
287
+ f'borg init --encryption=none {month_folder}', shell=True
288
+ )
214
289
 
215
- time_mark = now.strftime("%H-%M-%S")
216
- backup_file = f"{month_folder}/{now.day}.{time_mark}.back"
217
- snap_mapper = f"/dev/mapper/{lv_group}-{lv_name.replace('-', '--')}--snap"
218
- label = f"simo {now.strftime('%Y-%m-%d')}"
219
- dumpdates_file = os.path.join(month_folder, 'dumpdates')
290
+ exclude_dirs = (
291
+ 'tmp', 'lost+found', 'proc', 'cdrom', 'dev', 'mnt', 'sys', 'run',
292
+ 'var/tmp', 'var/cache', 'var/log', 'media',
293
+ )
294
+ backup_command = 'borg create --compression lz4'
295
+ for dir in exclude_dirs:
296
+ backup_command += f' --exclude={dir}'
220
297
 
221
- estimated_size = int(subprocess.check_output(
222
- f'dump -{level} -Squz9 -b 1024 {snap_mapper}',
223
- shell=True
224
- )) * 0.5
225
298
 
226
- folders = []
299
+ other_month_folders = []
227
300
  for item in os.listdir(device_backups_path):
228
301
  if not os.path.isdir(os.path.join(device_backups_path, item)):
229
302
  continue
303
+ if os.path.join(device_backups_path, item) == month_folder:
304
+ continue
230
305
  try:
231
306
  year, month = item.split('-')
232
- folders.append([
307
+ other_month_folders.append([
233
308
  os.path.join(device_backups_path, item),
234
309
  int(year) * 12 + int(month)
235
310
  ])
236
311
  except:
237
312
  continue
238
- folders.sort(key=lambda v: v[1])
313
+ other_month_folders.sort(key=lambda v: v[1])
314
+
315
+ if other_month_folders:
316
+ # delete old backups to free up at least 20G of space
317
+ while shutil.disk_usage('/media/backup').free < 20 * 1024 * 1024 * 1024:
318
+ remove_folder = other_month_folders.pop()[0]
319
+ print(f"REMOVE: {remove_folder}")
320
+ shutil.rmtree(remove_folder)
321
+
322
+ backup_command += f' {month_folder}::{get_random_string()} .'
323
+ res = subprocess.run(
324
+ backup_command, shell=True, cwd=snap_mount_point,
325
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE
326
+ )
239
327
 
240
- # delete old backups to free up space required for this backup to take place
241
- while shutil.disk_usage('/media/backup').free < estimated_size:
242
- remove_folder = folders.pop()[0]
243
- print(f"REMOVE: {remove_folder}")
244
- shutil.rmtree(remove_folder)
328
+ subprocess.run(["umount", snap_mount_point])
329
+ subprocess.run(
330
+ f"lvremove -f {lv_group}/{snap_name}", shell=True
331
+ )
245
332
 
246
- success = False
247
- try:
248
- subprocess.check_call(
249
- f'dump -{level} -quz9 -b 1024 -L "{label}" -D {dumpdates_file} -f {backup_file} {snap_mapper}',
250
- shell=True,
333
+ if res.returncode:
334
+ print("Backup error!")
335
+ BackupLog.objects.create(
336
+ level='error', msg="Backup error: \n" + res.stderr.decode()
251
337
  )
252
- success = True
338
+ else:
339
+ print("Backup done!")
340
+ BackupLog.objects.create(
341
+ level='info', msg="Backup success!"
342
+ )
343
+
344
+
345
+ @celery_app.task
346
+ def restore_backup(backup_id):
347
+ from simo.backups.models import Backup, BackupLog
348
+ backup = Backup.objects.get(id=backup_id)
349
+
350
+ try:
351
+ lv_group, lv_name, sd_mountpoint = get_partitions()
253
352
  except:
254
- try:
255
- os.remove(backup_file)
256
- print("Dump failed!")
257
- except:
258
- pass
353
+ BackupLog.objects.create(
354
+ level='error',
355
+ msg="Can't restore. LVM group is not present on this machine."
356
+ )
357
+ return
358
+
359
+ snap_mount_point = '/var/backups/simo-main'
360
+ subprocess.run(f'umount {snap_mount_point}', shell=True)
361
+
362
+ try:
363
+ snap_name = create_snap(lv_group, lv_name)
364
+ except Exception as e:
365
+ print("Error creating temporary snap\n" + str(e))
366
+ BackupLog.objects.create(
367
+ level='error',
368
+ msg="Can't restore. \n\n" + str(e)
369
+ )
370
+ return
259
371
 
260
- subprocess.call(
261
- f"lvremove -f {lv_group}/{lv_name}-snap", shell=True,
262
- stdout=subprocess.PIPE
372
+ shutil.rmtree(snap_mount_point, ignore_errors=True)
373
+ os.makedirs(snap_mount_point)
374
+ subprocess.run([
375
+ "mount",
376
+ f"/dev/mapper/{lv_group}-{snap_name.replace('-', '--')}",
377
+ snap_mount_point
378
+ ])
379
+
380
+ # delete current contents of a snap
381
+ print("Delete original files and folders")
382
+ for f in os.listdir(snap_mount_point):
383
+ shutil.rmtree(os.path.join(snap_mount_point, f), ignore_errors=True)
384
+
385
+ print("Perform restoration")
386
+ res = subprocess.run(
387
+ f"borg extract {backup.filepath}", shell=True, cwd=snap_mount_point,
388
+ stderr=subprocess.PIPE
263
389
  )
264
- if success:
265
- print("DONE!")
266
- dynamic_settings['backups__last_error'] = ''
390
+
391
+ subprocess.run(["umount", snap_mount_point])
392
+ subprocess.run(
393
+ f"lvremove -f {lv_group}/{snap_name}", shell=True
394
+ )
395
+
396
+ if res.returncode:
397
+ BackupLog.objects.create(
398
+ level='error',
399
+ msg="Can't restore. \n\n" + res.stderr.decode()
400
+ )
401
+ else:
402
+ print("Restore successful! Merge snapshot and reboot!")
403
+ subprocess.call(
404
+ f"lvconvert --mergesnapshot {lv_group}/{snap_name}",
405
+ shell=True
406
+ )
407
+ subprocess.run('reboot', shell=True)
267
408
 
268
409
 
269
410
  @celery_app.on_after_finalize.connect
270
411
  def setup_periodic_tasks(sender, **kwargs):
271
412
  sender.add_periodic_task(60 * 60, check_backups.s())
272
- sender.add_periodic_task(60 * 60 * 8, perform_backup.s()) # perform auto backup every 8 hours
413
+ # perform auto backup every 12 hours
414
+ sender.add_periodic_task(60 * 60 * 12, perform_backup.s())
Binary file
Binary file
Binary file
Binary file
simo/core/api.py CHANGED
@@ -262,13 +262,11 @@ class ComponentViewSet(
262
262
  def controller(self, request, pk=None, *args, **kwargs):
263
263
  start = time.time()
264
264
  component = self.get_object()
265
- print(f"Component retrieved in : {time.time() - start}s")
266
265
  data = request.data
267
266
  if not isinstance(request.data, dict):
268
267
  data = data.dict()
269
268
  request_data = restore_json(data)
270
269
  resp = self.perform_controller_method(request_data, component)
271
- print(f"Command executed in : {time.time() - start}s")
272
270
  return resp
273
271
 
274
272
  @action(detail=False, methods=['post'])
@@ -359,8 +357,6 @@ class ComponentHistoryViewSet(InstanceMixin, viewsets.ReadOnlyModelViewSet):
359
357
  minutes=start_from.minute, seconds=start_from.second
360
358
  )
361
359
 
362
- print("START FROM: ", start_from)
363
-
364
360
  return RESTResponse(
365
361
  self.get_aggregated_data(
366
362
  component, request.GET['interval'], start_from
@@ -645,9 +641,9 @@ class StatesViewSet(InstanceMixin, viewsets.GenericViewSet):
645
641
  component_values = get_components_queryset(
646
642
  self.instance, request.user
647
643
  ).filter(zone__instance=self.instance).values(
648
- 'id', 'value', 'last_change', 'last_modified',
644
+ 'id', 'last_change', 'last_modified',
649
645
  'arm_status', 'battery_level', 'alive', 'error_msg',
650
- 'meta'
646
+ 'value', 'meta'
651
647
  )
652
648
  for vals in component_values:
653
649
  vals['last_change'] = datetime.datetime.timestamp(
@@ -665,7 +661,6 @@ class StatesViewSet(InstanceMixin, viewsets.GenericViewSet):
665
661
  cat['last_modified']
666
662
  )
667
663
 
668
-
669
664
  return RESTResponse({
670
665
  'zones': Zone.objects.filter(instance=self.instance).values(
671
666
  'id', 'name'
simo/core/controllers.py CHANGED
@@ -539,6 +539,14 @@ class Dimmer(ControllerBase, TimerMixin, OnOffPokerMixin):
539
539
  default_value = 0
540
540
  default_value_units = '%'
541
541
 
542
+ def _receive_from_device(self, value, *args, **kwargs):
543
+ if isinstance(value, bool):
544
+ if value:
545
+ value = self.component.config.get('max', 100.0)
546
+ else:
547
+ value = self.component.config.get('min', 0)
548
+ return super()._receive_from_device(value, *args, **kwargs)
549
+
542
550
  def _prepare_for_send(self, value):
543
551
  if isinstance(value, bool):
544
552
  if value:
@@ -579,7 +587,6 @@ class Dimmer(ControllerBase, TimerMixin, OnOffPokerMixin):
579
587
  self.turn_on()
580
588
 
581
589
 
582
-
583
590
  class DimmerPlus(ControllerBase, TimerMixin, OnOffPokerMixin):
584
591
  name = _("Dimmer Plus")
585
592
  base_type = 'dimmer-plus'
@@ -10,6 +10,10 @@ stdout_logfile_backups=3
10
10
  redirect_stderr=true
11
11
  autostart=true
12
12
  autorestart=true
13
+ stopasgroup=true
14
+ killasgroup=true
15
+ stopsignal=INT
16
+
13
17
 
14
18
  # using daphne for socket connections routed to /ws/ on nginx.conf
15
19
  [program:simo-daphne]