simo 2.2.10__py3-none-any.whl → 2.2.12__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 (37) hide show
  1. simo/__pycache__/settings.cpython-38.pyc +0 -0
  2. simo/asgi.py +0 -2
  3. simo/core/__pycache__/api.cpython-38.pyc +0 -0
  4. simo/core/__pycache__/models.cpython-38.pyc +0 -0
  5. simo/core/__pycache__/tasks.cpython-38.pyc +0 -0
  6. simo/core/api.py +0 -1
  7. simo/core/dynamic_settings.py +0 -11
  8. simo/core/management/commands/__pycache__/on_http_start.cpython-38.pyc +0 -0
  9. simo/core/management/commands/on_http_start.py +64 -0
  10. simo/core/tasks.py +23 -14
  11. simo/fleet/__pycache__/models.cpython-38.pyc +0 -0
  12. simo/fleet/__pycache__/utils.cpython-38.pyc +0 -0
  13. simo/fleet/models.py +1 -0
  14. simo/fleet/utils.py +11 -1
  15. simo/management/_hub_template/hub/supervisor.conf +5 -5
  16. simo/management/install.py +4 -41
  17. simo/settings.py +0 -1
  18. simo/users/__pycache__/admin.cpython-38.pyc +0 -0
  19. simo/users/__pycache__/api.cpython-38.pyc +0 -0
  20. simo/users/__pycache__/models.cpython-38.pyc +0 -0
  21. simo/users/admin.py +16 -30
  22. simo/users/api.py +12 -5
  23. simo/users/migrations/0030_userdevice_users.py +19 -0
  24. simo/users/migrations/0031_auto_20240923_1115.py +25 -0
  25. simo/users/migrations/0032_remove_userdevice_user_alter_userdevice_users.py +23 -0
  26. simo/users/migrations/__pycache__/0030_userdevice_users.cpython-38.pyc +0 -0
  27. simo/users/migrations/__pycache__/0031_auto_20240923_1115.cpython-38.pyc +0 -0
  28. simo/users/migrations/__pycache__/0032_remove_userdevice_user_alter_userdevice_users.cpython-38.pyc +0 -0
  29. simo/users/models.py +10 -25
  30. {simo-2.2.10.dist-info → simo-2.2.12.dist-info}/METADATA +2 -2
  31. {simo-2.2.10.dist-info → simo-2.2.12.dist-info}/RECORD +35 -29
  32. {simo-2.2.10.dist-info → simo-2.2.12.dist-info}/WHEEL +1 -1
  33. simo/management/__pycache__/on_http_start.cpython-38.pyc +0 -0
  34. simo/management/on_http_start.py +0 -103
  35. {simo-2.2.10.dist-info → simo-2.2.12.dist-info}/LICENSE.md +0 -0
  36. {simo-2.2.10.dist-info → simo-2.2.12.dist-info}/entry_points.txt +0 -0
  37. {simo-2.2.10.dist-info → simo-2.2.12.dist-info}/top_level.txt +0 -0
Binary file
simo/asgi.py CHANGED
@@ -21,8 +21,6 @@ for name, app in apps.app_configs.items():
21
21
  if isinstance(item, list) and var_name == 'urlpatterns':
22
22
  urlpatterns.extend(item)
23
23
 
24
- from .management.on_http_start import *
25
-
26
24
  application = ProtocolTypeRouter({
27
25
  "http": get_asgi_application(),
28
26
  'websocket': AuthMiddlewareStack(
Binary file
Binary file
Binary file
simo/core/api.py CHANGED
@@ -235,7 +235,6 @@ class ComponentViewSet(
235
235
 
236
236
  return RESTResponse(result)
237
237
 
238
- # TODO: remove post when app is updated for all users
239
238
  @action(detail=True, methods=['post'])
240
239
  def subcomponent(self, request, pk=None, *args, **kwargs):
241
240
  component = self.get_object()
@@ -57,17 +57,6 @@ class AutoUpdate(BooleanPreference):
57
57
  name = 'auto_update'
58
58
  default = True
59
59
 
60
- def validate(self, value):
61
- if value:
62
- with open(os.path.join(settings.VAR_DIR, 'auto_update'), 'w') as f:
63
- f.write("YES!")
64
- else:
65
- try:
66
- os.remove(os.path.join(settings.VAR_DIR, 'auto_update'))
67
- except:
68
- pass
69
- return
70
-
71
60
 
72
61
  @global_preferences_registry.register
73
62
  class NeedsMqttAclsRebuild(BooleanPreference):
@@ -0,0 +1,64 @@
1
+ from django.core.management.base import BaseCommand
2
+ import os
3
+ import pwd
4
+ import grp
5
+ import subprocess
6
+ import pkg_resources
7
+ from django.conf import settings
8
+ from django.template.loader import render_to_string
9
+
10
+
11
+ def prepare_mosquitto():
12
+ if os.geteuid() != 0:
13
+ return
14
+
15
+ from simo.users.models import User
16
+
17
+ users_file = '/etc/mosquitto/mosquitto_users'
18
+ if not os.path.exists(users_file):
19
+ with open(users_file, 'w') as f:
20
+ f.write('')
21
+
22
+ uid = pwd.getpwnam("mosquitto").pw_uid
23
+ gid = grp.getgrnam("mosquitto").gr_gid
24
+ os.chown(users_file, uid, gid)
25
+ os.chmod(users_file, 0o640)
26
+
27
+ acls_file = '/etc/mosquitto/acls.conf'
28
+ with open(acls_file, 'w') as f:
29
+ f.write('')
30
+
31
+ uid = pwd.getpwnam("mosquitto").pw_uid
32
+ gid = grp.getgrnam("mosquitto").gr_gid
33
+ os.chown(acls_file, uid, gid)
34
+ os.chmod(acls_file, 0o640)
35
+
36
+ ps = subprocess.Popen(
37
+ ['mosquitto_passwd /etc/mosquitto/mosquitto_users root'],
38
+ shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE
39
+ )
40
+ ps.communicate(f"{settings.SECRET_KEY}\n{settings.SECRET_KEY}".encode())
41
+
42
+ for user in User.objects.all():
43
+ user.update_mqtt_secret(reload=False)
44
+
45
+ from simo.users.utils import update_mqtt_acls
46
+
47
+ update_mqtt_acls()
48
+
49
+ if not os.path.exists('/etc/mosquitto/conf.d/simo.conf'):
50
+ with open('/etc/mosquitto/conf.d/simo.conf', 'w') as f:
51
+ f.write(render_to_string('conf/mosquitto.conf'))
52
+
53
+ subprocess.run(
54
+ ['service', 'mosquitto', 'reload'], stdout=subprocess.PIPE
55
+ )
56
+
57
+
58
+
59
+ class Command(BaseCommand):
60
+
61
+ def handle(self, *args, **options):
62
+ prepare_mosquitto()
63
+ from simo.core.tasks import maybe_update_to_latest
64
+ maybe_update_to_latest.delay()
simo/core/tasks.py CHANGED
@@ -210,7 +210,6 @@ def sync_with_remote():
210
210
  email=email, defaults={
211
211
  'name': options.get('name'),
212
212
  'is_master': options.get('is_hub_master', False),
213
- 'ssh_key': options.get('ssh_key')
214
213
  })
215
214
  role = None
216
215
  if options.get('is_hub_master') or options.get('is_superuser'):
@@ -236,9 +235,6 @@ def sync_with_remote():
236
235
  if user.name != options.get('name'):
237
236
  user.name = options['name']
238
237
  user.save()
239
- if user.ssh_key != options.get('ssh_key'):
240
- user.ssh_key = options['ssh_key']
241
- user.save()
242
238
 
243
239
  avatar_url = options.get('avatar_url')
244
240
  if avatar_url and user.avatar_url != avatar_url:
@@ -329,15 +325,6 @@ def update():
329
325
  perform_update()
330
326
 
331
327
 
332
- @celery_app.task
333
- def update_latest_version_available():
334
- resp = requests.get("https://pypi.org/pypi/simo/json")
335
- if resp.status_code != 200:
336
- print("Bad response from server")
337
- return
338
- latest = list(resp.json()['releases'].keys())[-1]
339
- dynamic_settings['core__latest_version_available'] = latest
340
- print("Got the latest version available!")
341
328
 
342
329
 
343
330
  @celery_app.task
@@ -396,11 +383,33 @@ def low_battery_notifications():
396
383
  )
397
384
 
398
385
 
386
+ @celery_app.task
387
+ def maybe_update_to_latest():
388
+ from simo.core.models import Instance
389
+ from simo.conf import dynamic_settings
390
+ resp = requests.get("https://pypi.org/pypi/simo/json")
391
+ if resp.status_code != 200:
392
+ print("Bad response from server")
393
+ return
394
+ latest = list(resp.json()['releases'].keys())[-1]
395
+ dynamic_settings['core__latest_version_available'] = latest
396
+ if dynamic_settings['core__latest_version_available'] == \
397
+ pkg_resources.get_distribution('simo').version:
398
+ print("Up to date!")
399
+ return
400
+
401
+ if not Instance.objects.all().count() or dynamic_settings['auto_update']:
402
+ print("Need to update!!")
403
+ return update.s()
404
+
405
+ print("New version is available, but auto update is disabled.")
406
+
407
+
399
408
  @celery_app.on_after_finalize.connect
400
409
  def setup_periodic_tasks(sender, **kwargs):
401
410
  sender.add_periodic_task(20, sync_with_remote.s())
402
411
  sender.add_periodic_task(60 * 60, clear_history.s())
403
- sender.add_periodic_task(60 * 60, update_latest_version_available.s())
412
+ sender.add_periodic_task(60 * 60, maybe_update_to_latest.s())
404
413
  sender.add_periodic_task(60, drop_fingerprints_learn.s())
405
414
  sender.add_periodic_task(60 * 60 * 24, restart_postgresql.s())
406
415
  sender.add_periodic_task(60 * 60, low_battery_notifications.s())
Binary file
simo/fleet/models.py CHANGED
@@ -58,6 +58,7 @@ class Colonel(DirtyFieldsMixin, models.Model):
58
58
  ('4-relays', "4 Relay"),
59
59
  ('ample-wall', "Ample Wall"),
60
60
  ('game-changer', "Game Changer"),
61
+ ('game-changer-mini', "Game Changer Mini"),
61
62
  )
62
63
  )
63
64
  firmware_version = models.CharField(
simo/fleet/utils.py CHANGED
@@ -54,7 +54,10 @@ BASE_ESP32_GPIO_PINS = {
54
54
  39: {'output': False, 'adc': True},
55
55
  }
56
56
 
57
- GPIO_PINS = {'generic': {}, '4-relays': {}, 'ample-wall': {}, 'game-changer': {}}
57
+ GPIO_PINS = {
58
+ 'generic': {}, '4-relays': {}, 'ample-wall': {},
59
+ 'game-changer': {}, 'game-changer-mini': {}
60
+ }
58
61
 
59
62
  for no, data in BASE_ESP32_GPIO_PINS.items():
60
63
  GPIO_PINS['generic'][no] = GPIO_PIN_DEFAULTS.copy()
@@ -90,6 +93,13 @@ for no in range(101, 139):
90
93
  'capacitive': False, 'note': ''
91
94
  }
92
95
 
96
+ for no in range(101, 105):
97
+ GPIO_PINS['game-changer-mini'][no] = {
98
+ 'output': True, 'input': True, 'default_pull': 'LOW',
99
+ 'native': False, 'adc': False,
100
+ 'capacitive': False, 'note': ''
101
+ }
102
+
93
103
 
94
104
  #4-relays
95
105
  for no, data in BASE_ESP32_GPIO_PINS.items():
@@ -1,7 +1,7 @@
1
1
  # using gunicorn for regular requests
2
2
  [program:simo-gunicorn]
3
3
  directory={{ project_dir }}/hub/
4
- command=/usr/local/bin/gunicorn --workers 4 --timeout 120 --bind unix:/tmp/gunicorn.sock wsgi:application
4
+ command=/bin/sh -c "{{ venv_path }}/python manage.py on_http_start && {{ venv_path }}/gunicorn --workers 4 --timeout 120 --bind unix:/tmp/gunicorn.sock wsgi:application"
5
5
  process_name=%(program_name)s
6
6
  user=root
7
7
  stdout_logfile=/var/log/simo/gunicorn.log
@@ -14,7 +14,7 @@ autorestart=true
14
14
  # using daphne for socket connections routed to /ws/ on nginx.conf
15
15
  [program:simo-daphne]
16
16
  directory={{ project_dir }}/hub/
17
- command=/usr/local/bin/daphne -u /tmp/http.sock --access-log /dev/stdout --proxy-headers asgi:application
17
+ command={{ venv_path }}/daphne -u /tmp/http.sock --access-log /dev/stdout --proxy-headers asgi:application
18
18
  process_name=%(program_name)s
19
19
  user=root
20
20
  stdout_logfile=/var/log/simo/daphne.log
@@ -26,7 +26,7 @@ autorestart=true
26
26
 
27
27
 
28
28
  [program:simo-gateways]
29
- command=/usr/bin/python3 {{ project_dir }}/hub/manage.py gateways_manager
29
+ command={{ venv_path }}/python {{ project_dir }}/hub/manage.py gateways_manager
30
30
  process_name=%(program_name)s
31
31
  user=root
32
32
  stopsignal=INT
@@ -40,7 +40,7 @@ autorestart=true
40
40
 
41
41
  [program:simo-celery-beat]
42
42
  directory={{ project_dir }}/hub/
43
- command=/usr/local/bin/celery -A celeryc.celery_app beat -l info --pidfile="/var/run/celerybeat.pid"
43
+ command={{ venv_path }}/celery -A celeryc.celery_app beat -l info --pidfile="/var/run/celerybeat.pid"
44
44
  process_name=%(program_name)s
45
45
  user=root
46
46
  stdout_logfile=/var/log/simo/celery_beat.log
@@ -52,7 +52,7 @@ autorestart=true
52
52
 
53
53
  [program:simo-celery-worker]
54
54
  directory={{ project_dir }}/hub/
55
- command=/usr/local/bin/celery -A celeryc.celery_app worker -l info --concurrency=4
55
+ command={{ venv_path }}/celery -A celeryc.celery_app worker -l info --concurrency=4
56
56
  process_name=%(program_name)s
57
57
  user=root
58
58
  stdout_logfile=/var/log/simo/celery_worker.log
@@ -19,7 +19,7 @@ def install_dependencies():
19
19
  'postgresql-client-common python3-pip redis-server supervisor '
20
20
  'mosquitto libopenjp2-7 libtiff5 pkg-config libcairo2-dev '
21
21
  'libgirepository1.0-dev libcairo2 libudev-dev gdal-bin net-tools '
22
- 'timeshift nginx postgis openvpn ffmpeg libsm6 libxext6 ssh keychain -y',
22
+ 'nginx postgis openvpn ffmpeg libsm6 libxext6 ssh keychain -y',
23
23
  shell=True
24
24
  )
25
25
  if status != 0:
@@ -48,7 +48,8 @@ def copy_template(to_directory='/etc/SIMO'):
48
48
  context = Context({
49
49
  'secret_key': get_random_secret_key(),
50
50
  'project_dir': to_directory,
51
- 'base_dir': to_directory
51
+ 'base_dir': to_directory,
52
+ 'venv_path': os.path.dirname(sys.executable),
52
53
  }, autoescape=False)
53
54
  template_dir = os.path.join(
54
55
  os.path.dirname(simo.__file__), 'management', '_hub_template'
@@ -170,6 +171,7 @@ def install():
170
171
  os.remove('/etc/supervisor/conf.d/SIMO.conf')
171
172
  except:
172
173
  pass
174
+
173
175
  os.symlink(
174
176
  f'{simo_directory}/hub/supervisor.conf',
175
177
  '/etc/supervisor/conf.d/SIMO.conf'
@@ -247,45 +249,6 @@ def install():
247
249
  if status != 0:
248
250
  sys.exit("INSTALLATION FAILED! Unable to enable UFW")
249
251
 
250
-
251
- step += 1
252
- print("%d.__________ CONFIGURE TIMESHIFT _____________________" % step)
253
-
254
- default_timeshift_file_path = '/etc/timeshift/default.json'
255
- if not os.path.exists(default_timeshift_file_path):
256
- default_timeshift_file_path = '/etc/timeshift/timeshift.json'
257
- if not os.path.exists(default_timeshift_file_path):
258
- default_timeshift_file_path = '/etc/default/timeshift.json'
259
-
260
- if not os.path.exists(default_timeshift_file_path):
261
- print("Unable to find default TimeShift config! Skip TimeShift configuration.")
262
-
263
- else:
264
-
265
- with open(default_timeshift_file_path, 'r') as conf_f:
266
- timeshift_conf = json.loads(conf_f.read())
267
-
268
- timeshift_conf['backup_device_uuid'] = subprocess.check_output(
269
- "lsblk -no UUID $(df -P /etc/SIMO/hub/settings.py | awk 'END{print $1}')",
270
- shell=True
271
- ).decode()[:-1]
272
- timeshift_conf['schedule_monthly'] = "true"
273
- timeshift_conf['schedule_weekly'] = "true"
274
- timeshift_conf['schedule_daily'] = "true"
275
- timeshift_conf['exclude'] = []
276
-
277
- # Must be copied to /etc/timeshift/timeshift.json to work
278
- with open('/etc/timeshift/timeshift.json', 'w') as conf_f:
279
- conf_f.write(json.dumps(timeshift_conf))
280
-
281
- # status = subprocess.call([
282
- # '/usr/bin/timeshift', '--create',
283
- # '--comments', '"Initial backup"', '--tags', 'M'
284
- # ])
285
- # if status != 0:
286
- # print("Unable to start TimeShift")
287
-
288
-
289
252
  step += 1
290
253
  print("%d.__________ PUT UP INSTALL COMPLETE FLAG! _____________________" % step)
291
254
 
simo/settings.py CHANGED
@@ -197,7 +197,6 @@ REST_FRAMEWORK = {
197
197
 
198
198
  REDIS_DB = {
199
199
  'celery': 0, 'default_cache': 1, 'select2_cache': 2,
200
- 'fleet_rs485': 11, 'fleet_wifi': 12
201
200
  }
202
201
 
203
202
  CACHES = {
Binary file
Binary file
simo/users/admin.py CHANGED
@@ -46,22 +46,6 @@ class PermissionsRoleAdmin(admin.ModelAdmin):
46
46
  return fields
47
47
 
48
48
 
49
- class UserDeviceInline(admin.TabularInline):
50
- model = UserDevice
51
- extra = 0
52
- readonly_fields = 'token', 'os', 'last_seen', 'is_primary', 'more'
53
- fields = readonly_fields
54
-
55
- def has_delete_permission(self, request, obj=None):
56
- return False
57
-
58
- def has_add_permission(self, request, obj=None):
59
- return False
60
-
61
- def more(self, obj):
62
- return mark_safe('<a href="%s">more >></a>' % obj.get_admin_url())
63
-
64
-
65
49
  class InstanceUserInline(admin.TabularInline):
66
50
  model = InstanceUser
67
51
  extra = 0
@@ -84,10 +68,9 @@ class UserAdmin(OrgUserAdmin):
84
68
  'last_seen_location',
85
69
  )
86
70
  readonly_fields = (
87
- 'name', 'email', 'avatar',
88
- 'last_action', 'ssh_key', 'is_active'
71
+ 'name', 'email', 'avatar', 'last_action', 'is_active'
89
72
  )
90
- inlines = UserDeviceInline, InstanceUserInline
73
+ inlines = InstanceUserInline,
91
74
 
92
75
  def name_display(self, obj=None):
93
76
  if not obj:
@@ -129,20 +112,18 @@ admin.site.unregister(Group)
129
112
  @admin.register(UserDeviceReportLog)
130
113
  class UserDeviceLogInline(admin.ModelAdmin):
131
114
  model = UserDeviceReportLog
132
- readonly_fields = 'datetime', 'app_open', 'location', 'relay', 'user'
133
- list_display = 'datetime', 'app_open', 'location', 'relay', 'user'
115
+ readonly_fields = 'datetime', 'app_open', 'location', 'relay', 'users'
116
+ list_display = 'datetime', 'app_open', 'location', 'relay', 'users'
134
117
  fields = readonly_fields
135
- list_filter = 'user_device__user',
136
118
 
137
119
  def has_add_permission(self, request, obj=None):
138
120
  return False
139
121
 
140
- def user(self, obj):
141
- return mark_safe(
142
- f'<a href="{obj.user_device.user.get_admin_url()}">'
143
- f'{obj.user_device.user}'
144
- f'</a>'
145
- )
122
+ def users(self, obj):
123
+ return mark_safe(', '.join([
124
+ f'<a href="{user.get_admin_url()}">{user}</a>'
125
+ for user in obj.user_device.users.all()
126
+ ]))
146
127
 
147
128
  def get_queryset(self, request):
148
129
  qs = super().get_queryset(request)
@@ -155,9 +136,9 @@ class UserDeviceLogInline(admin.ModelAdmin):
155
136
 
156
137
  @admin.register(UserDevice)
157
138
  class UserDeviceAdmin(admin.ModelAdmin):
158
- list_display = 'token', 'os', 'last_seen', 'is_primary', 'user'
139
+ list_display = 'token', 'os', 'last_seen', 'is_primary', 'users_display'
159
140
  readonly_fields = (
160
- 'user', 'token', 'os', 'last_seen',
141
+ 'users_display', 'token', 'os', 'last_seen',
161
142
  )
162
143
  fields = readonly_fields + ('last_seen_location', 'is_primary')
163
144
 
@@ -167,6 +148,11 @@ class UserDeviceAdmin(admin.ModelAdmin):
167
148
  return qs
168
149
  return qs.filter(user__role__instance__in=request.user.instances)
169
150
 
151
+ def users_display(self, obj):
152
+ return ', '.join([str(u) for u in obj.users.all()])
153
+ users_display.short_description = 'Users'
154
+
155
+
170
156
 
171
157
  @admin.register(InstanceInvitation)
172
158
  class InstanceInvitationAdmin(admin.ModelAdmin):
simo/users/api.py CHANGED
@@ -37,7 +37,7 @@ class UsersViewSet(mixins.RetrieveModelMixin,
37
37
 
38
38
  return queryset.filter(
39
39
  Q(roles__instance=self.instance) | Q(id=self.request.user.id)
40
- )
40
+ ).distinct()
41
41
 
42
42
 
43
43
  def check_permission_to_change(self, request, target_user):
@@ -160,14 +160,13 @@ class UserDeviceReport(InstanceMixin, viewsets.GenericViewSet):
160
160
  status=status.HTTP_400_BAD_REQUEST
161
161
  )
162
162
 
163
- defaults = {'os': request.data['os'], 'user': request.user}
163
+ defaults = {'os': request.data['os']}
164
164
  user_device, new = UserDevice.objects.get_or_create(
165
165
  token=request.data['device_token'],
166
166
  defaults=defaults
167
167
  )
168
- if not new:
169
- for key, val in defaults.items():
170
- setattr(user_device, key, val)
168
+ user_device.users.add(request.user)
169
+
171
170
  try:
172
171
  location = Point(
173
172
  *[float(c) for c in request.data.get('location').split(',')],
@@ -181,10 +180,18 @@ class UserDeviceReport(InstanceMixin, viewsets.GenericViewSet):
181
180
  user_device.last_seen_location = ','.join(
182
181
  [str(i) for i in location]
183
182
  ) if location else None
183
+
184
184
  if request.data.get('app_open', False):
185
185
  user_device.is_primary = True
186
+ UserDevice.objects.filter(
187
+ users=request.user
188
+ ).exclude(id=user_device.id).update(is_primary=False)
186
189
  user_device.save()
187
190
 
191
+ request.user.last_seen_location = user_device.last_seen_location
192
+ request.user.last_seen_location_datetime = user_device.last_seen
193
+ request.user.save()
194
+
188
195
  relay = None
189
196
  if request.META.get('HTTP_HOST', '').endswith('.simo.io'):
190
197
  relay = request.META.get('HTTP_HOST')
@@ -0,0 +1,19 @@
1
+ # Generated by Django 4.2.10 on 2024-09-23 11:15
2
+
3
+ from django.conf import settings
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('users', '0029_alter_instanceuser_instance'),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AddField(
15
+ model_name='userdevice',
16
+ name='users',
17
+ field=models.ManyToManyField(to=settings.AUTH_USER_MODEL),
18
+ ),
19
+ ]
@@ -0,0 +1,25 @@
1
+ # Generated by Django 4.2.10 on 2024-09-23 10:54
2
+
3
+ from django.db import migrations
4
+
5
+
6
+ def forwards_func(apps, schema_editor):
7
+ UserDevice = apps.get_model("users", "UserDevice")
8
+
9
+ for ud in UserDevice.objects.all():
10
+ ud.users.add(ud.user)
11
+
12
+
13
+ def reverse_func(apps, schema_editor):
14
+ pass
15
+
16
+
17
+ class Migration(migrations.Migration):
18
+
19
+ dependencies = [
20
+ ('users', '0030_userdevice_users'),
21
+ ]
22
+
23
+ operations = [
24
+ migrations.RunPython(forwards_func, reverse_func, elidable=True),
25
+ ]
@@ -0,0 +1,23 @@
1
+ # Generated by Django 4.2.10 on 2024-09-23 11:40
2
+
3
+ from django.conf import settings
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('users', '0031_auto_20240923_1115'),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.RemoveField(
15
+ model_name='userdevice',
16
+ name='user',
17
+ ),
18
+ migrations.AlterField(
19
+ model_name='userdevice',
20
+ name='users',
21
+ field=models.ManyToManyField(related_name='devices', to=settings.AUTH_USER_MODEL),
22
+ ),
23
+ ]
simo/users/models.py CHANGED
@@ -164,9 +164,8 @@ class User(AbstractBaseUser, SimoAdminMixin):
164
164
  )
165
165
  ssh_key = models.TextField(
166
166
  null=True, blank=True,
167
- help_text="DO NOT EDIT IT MANUALLY! Comes from SIMO.io. <br>"
168
- "Will be placed in /root/.ssh/authorized_keys "
169
- "if user is active and has superuser rights."
167
+ help_text="Will be placed in /root/.ssh/authorized_keys "
168
+ "if user is active and is master of a hub."
170
169
  )
171
170
  last_seen_location = PlainLocationField(
172
171
  zoom=7, null=True, blank=True, help_text="Sent by user mobile app"
@@ -390,22 +389,13 @@ class Fingerprint(models.Model):
390
389
 
391
390
 
392
391
  class UserDevice(models.Model, SimoAdminMixin):
393
- user = models.ForeignKey(
394
- User, on_delete=models.CASCADE, related_name='devices'
395
- )
392
+ users = models.ManyToManyField(User, related_name='devices')
396
393
  os = models.CharField(max_length=100, db_index=True)
397
394
  token = models.CharField(max_length=1000, db_index=True, unique=True)
398
395
  is_primary = models.BooleanField(default=True, db_index=True)
399
396
  last_seen = models.DateTimeField(auto_now_add=True, db_index=True)
400
397
  last_seen_location = PlainLocationField(zoom=7, null=True, blank=True)
401
398
 
402
- def save(self, *args, **kwargs):
403
- if self.is_primary:
404
- UserDevice.objects.filter(user=self.user).exclude(
405
- pk=self.pk
406
- ).update(is_primary=False)
407
- return super().save(*args, **kwargs)
408
-
409
399
  class Meta:
410
400
  ordering = '-last_seen',
411
401
 
@@ -437,21 +427,15 @@ def set_user_at_home(sender, instance, created, **kwargs):
437
427
  if not created:
438
428
  return
439
429
 
440
- if not instance.relay:
441
- for item in InstanceUser.objects.filter(user=instance.user_device.user):
430
+ if not instance.location and not instance.relay:
431
+ for item in InstanceUser.objects.filter(
432
+ user__in=instance.user_device.users.all()
433
+ ):
442
434
  item.at_home = True
443
435
  item.save()
444
436
  return
445
- if not instance.location:
446
- return
447
-
448
- instance.user_device.last_seen_location = instance.location
449
- instance.user_device.save()
450
- instance.user_device.user.last_seen_location = instance.location
451
- instance.user_device.user.last_seen_location_datetime = timezone.now()
452
- instance.user_device.user.save()
453
437
 
454
- for hub_instance in Instance.objects.all():
438
+ for hub_instance in Instance.objects.filter(is_active=True):
455
439
  try:
456
440
  instance_location = Point(
457
441
  [float(hub_instance.location.split(',')[0]),
@@ -471,7 +455,8 @@ def set_user_at_home(sender, instance, created, **kwargs):
471
455
  return
472
456
  else:
473
457
  for item in InstanceUser.objects.filter(
474
- user=instance.user_device.user, instance=hub_instance
458
+ user__in=instance.user_device.users.all(),
459
+ instance=hub_instance
475
460
  ):
476
461
  item.at_home = distance(
477
462
  instance_location, log_location
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: simo
3
- Version: 2.2.10
3
+ Version: 2.2.12
4
4
  Summary: Smart Home on Steroids!
5
5
  Author-email: Simanas Venčkauskas <simanas@simo.io>
6
6
  Project-URL: Homepage, https://simo.io
@@ -33,7 +33,7 @@ Requires-Dist: itsdangerous ==2.0.1
33
33
  Requires-Dist: redis ==3.5.3
34
34
  Requires-Dist: django-redis ==4.12.1
35
35
  Requires-Dist: webservices ==0.7
36
- Requires-Dist: numpy ==1.24.4
36
+ Requires-Dist: numpy ==1.26.4
37
37
  Requires-Dist: opencv-python ==4.5.4.60
38
38
  Requires-Dist: geopy ==2.2.0
39
39
  Requires-Dist: requests ==2.26.0
@@ -1,20 +1,20 @@
1
1
  simo/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
2
- simo/asgi.py,sha256=RphoxqFJjh8Tm8RGC2ztDfFedCyn3a3JQwsLMdAtuaM,863
2
+ simo/asgi.py,sha256=L8CUVZLM32IMzWDZ4IShdDN-m69t7oxAUeHods4-xNM,822
3
3
  simo/celeryc.py,sha256=eab7_e9rw0c__DCeoUFUh_tjAGVlulxVrk75BaJf57Q,1512
4
4
  simo/conf.py,sha256=H2BhXAV8MEDVXF8AbkaLSfR4ULd-9_bS4bnhE5sE5fg,112
5
5
  simo/scripting.py,sha256=PVIkGsiMDWj4CNTbOM3rq7pJ6ruavuns-ZMU7VudLa4,923
6
- simo/settings.py,sha256=p1T_M-eooNjIAcqRPTYsQRMTRNeOAgi24ZJ703nYKh4,6953
6
+ simo/settings.py,sha256=E3hOPNASGnoNvhw_K3GbApsHjCnskJgEyeWYIQlEdxE,6913
7
7
  simo/urls.py,sha256=fRmAsNQ_pzFloimLmxNeDcR6hHRJ3rOoZ3kGy8zOQ_A,2402
8
8
  simo/__pycache__/__init__.cpython-38.pyc,sha256=j81de0BqHMr6bs0C7cuYrXl7HwtK_vv8hDEtAdSwDJc,153
9
9
  simo/__pycache__/asgi.cpython-38.pyc,sha256=UI_puPxc6q4otRqtz0IALTwrh00975iGxc0UyQgLreQ,891
10
10
  simo/__pycache__/celeryc.cpython-38.pyc,sha256=eSRoaKwfYlxVaxAiwqpQ2ndEcx7W-VpZtbxRFSV8UYg,1653
11
11
  simo/__pycache__/conf.cpython-38.pyc,sha256=MYP2yk3ULxiYwZsZR6tCLjKnU-z03A3avzQzIn66y3k,273
12
- simo/__pycache__/settings.cpython-38.pyc,sha256=ZdhdXOZPZOAljVIHlClO3YFQLpRlqi4LnwLKXcoCcRk,6108
12
+ simo/__pycache__/settings.cpython-38.pyc,sha256=FOalzITYVDHyqsGfbl8zNws0oBITfcs5XgHPao0QyLg,6065
13
13
  simo/__pycache__/urls.cpython-38.pyc,sha256=u0x6EqT8S1YfDOSPgbI8Kf-RDlveY9OV-EDXMYKAQ7w,2125
14
14
  simo/__pycache__/wsgi.cpython-38.pyc,sha256=TpRxO7VM_ql31hbKphVdanydC5RI1nHB4l0QA2pdWxo,322
15
15
  simo/core/__init__.py,sha256=_s2TjJfQImsMrTIxqLAx9AZie1Ojmm6sCHASdl3WLGU,50
16
16
  simo/core/admin.py,sha256=hoJ0OhfWL9T0d6JCY6_Gm3GR-nrgtDR2UQM67rMsjiE,18141
17
- simo/core/api.py,sha256=bA_dw4JRNQvmQ7yyet7P2nrIfTy86L0rlCsGE6VtUnA,28010
17
+ simo/core/api.py,sha256=laWtqQUL0ngjTtdDGqcUH42WYzWd9WYJwcKL3yCsDk4,27952
18
18
  simo/core/api_auth.py,sha256=vCxvczA8aWNcW0VyKs5WlC_ytlqeGP_H_hkKUNVkCwM,1247
19
19
  simo/core/api_meta.py,sha256=EaiY-dCADP__9MvLpoHvhjytFT92IrxPZDv95xgqasU,4955
20
20
  simo/core/app_widgets.py,sha256=4Lh9FDzdkfh_mccJMe09dyRTT3Uqf9VXwbkurJ9E9oQ,2115
@@ -24,7 +24,7 @@ simo/core/autocomplete_views.py,sha256=JT5LA2_Wtr60XYSAIqaXFKFYPjrmkEf6yunXD9y2z
24
24
  simo/core/base_types.py,sha256=qVh6MrXZEfN7bFOyFftC7u0yyz0PkvpsjllLBc6SCp4,616
25
25
  simo/core/context.py,sha256=98PXAMie43faRVBFkOG22uNpvGRNprcGhzjBFkrxaRY,1367
26
26
  simo/core/controllers.py,sha256=rLgJqnEMzRC0GpZnQb0m_Cz43Gp2zaYbODU7UUxE5oA,29602
27
- simo/core/dynamic_settings.py,sha256=U9pY7p_hoeD1LxobIvxZqQ7Zn_4MhYMqZvsr4O0PAYs,1871
27
+ simo/core/dynamic_settings.py,sha256=txfRcU2QA_WF6-7Ykc4Hl5gpTkZzi94N8gmmvEolezM,1549
28
28
  simo/core/events.py,sha256=LvtonJGNyCb6HLozs4EG0WZItnDwNdtnGQ4vTcnKvUs,4438
29
29
  simo/core/filters.py,sha256=ghtOZcrwNAkIyF5_G9Sn73NkiI71mXv0NhwCk4IyMIM,411
30
30
  simo/core/form_fields.py,sha256=9tIjiEN3IE55GPyB4tOlfkd51JDne3-h8pKhpL3tLFE,2220
@@ -40,14 +40,14 @@ simo/core/serializers.py,sha256=quXznnTKCm57rvRgke0mBnlWmaO8C5scJ8R251wa1jY,2086
40
40
  simo/core/signal_receivers.py,sha256=9-qFCCeSLcMFEMg6QUtKOVgUsoNoqhzGoI98nuNSTEo,6228
41
41
  simo/core/socket_consumers.py,sha256=n7VE2Fvqt4iEAYLTRbTPOcI-7tszMAADu7gimBxB-Fg,9635
42
42
  simo/core/storage.py,sha256=_5igjaoWZAiExGWFEJMElxUw55DzJG1jqFty33xe8BE,342
43
- simo/core/tasks.py,sha256=BAnCnNPuHtZQL9CG4n_e6G_aKL0aZBZFjp-HOWQg4O0,14049
43
+ simo/core/tasks.py,sha256=HE3VsVGJjLKtlZyjYF-TQ2-9vChxZ2EtY6A5vNkZl-0,14235
44
44
  simo/core/todos.py,sha256=eYVXfLGiapkxKK57XuviSNe3WsUYyIWZ0hgQJk7ThKo,665
45
45
  simo/core/types.py,sha256=WJEq48mIbFi_5Alt4wxWMGXxNxUTXqfQU5koH7wqHHI,1108
46
46
  simo/core/views.py,sha256=VVqfEPzK0EdbVMMarkG8rd7cODG5QHpXnr3e8UdrTQE,2600
47
47
  simo/core/widgets.py,sha256=J9e06C6I22F6xKic3VMgG7WeX07glAcl-4bF2Mg180A,2827
48
48
  simo/core/__pycache__/__init__.cpython-38.pyc,sha256=ZJFM_XN0RmJMULQulgA_wFiOnEtsMoedcOWnXjH-Y8o,208
49
49
  simo/core/__pycache__/admin.cpython-38.pyc,sha256=c5Q8YfIiLk25yVCxQvNsH-lwuOwfQa88X6xDYjOBogU,13428
50
- simo/core/__pycache__/api.cpython-38.pyc,sha256=RKwWpa0RqwMqDJ9U9AE6JIzrjIknt-Lsv0ZqENf6UzU,21838
50
+ simo/core/__pycache__/api.cpython-38.pyc,sha256=Fs0SFPiz8rA0zUUDGEGnY6EAZuPuvwtvx9yByf6O3uQ,21838
51
51
  simo/core/__pycache__/api_auth.cpython-38.pyc,sha256=6M9Cl_ha4y_Vf8Rv4GMYL8dcBCmp0KzYi6jn3SQTgys,1712
52
52
  simo/core/__pycache__/api_meta.cpython-38.pyc,sha256=VYx5ZeDyNBI4B_CBEIhV5B3GnLsMOx9s3rNZTSMODco,3703
53
53
  simo/core/__pycache__/app_widgets.cpython-38.pyc,sha256=vUCEAYqppjgRZYMs6pTuSxWWuZxreLygPuPBGw044dQ,3643
@@ -66,14 +66,14 @@ simo/core/__pycache__/gateways.cpython-38.pyc,sha256=D1ooHL-iSpQrxnD8uAl4xWFJmm-
66
66
  simo/core/__pycache__/loggers.cpython-38.pyc,sha256=Z-cdQnC6XlIonPV4Sl4E52tP4NMEdPAiHK0cFaIL7I8,1623
67
67
  simo/core/__pycache__/managers.cpython-38.pyc,sha256=6RTIxyjOgpQGtAqcUyE2vFPS09w1V5Wmd_vOV7rHRRI,3370
68
68
  simo/core/__pycache__/middleware.cpython-38.pyc,sha256=UZTX3tUUHkigr_3GiSLbUX3RbJBg1_JBPiXy6saJ9GA,1911
69
- simo/core/__pycache__/models.cpython-38.pyc,sha256=nAgEN2wfcQkeWtRDwN_u-3mOn09irA2hFX23ziFJwIw,17911
69
+ simo/core/__pycache__/models.cpython-38.pyc,sha256=h_neN4zzReOTuou4NX9RbgVJ8T47z0tLqSr37Fm1iHU,17874
70
70
  simo/core/__pycache__/permissions.cpython-38.pyc,sha256=fH4iyqd9DdzRLEu2b621-FeM-napR0M7hzBUTHo9Q3g,2972
71
71
  simo/core/__pycache__/routing.cpython-38.pyc,sha256=3T3FPJ8Cn99xZCGvMyg2xjl7al-Shm9CelbSpkJtNP8,599
72
72
  simo/core/__pycache__/serializers.cpython-38.pyc,sha256=PzKJTctrfXeTtD1QlRL5vKKIW4YxB5MuEPj_Z2w9Wgc,19495
73
73
  simo/core/__pycache__/signal_receivers.cpython-38.pyc,sha256=3Bt9S47DR_ZFS3O-crElFgLLXPIYyDgPIc2ibwEkaic,4904
74
74
  simo/core/__pycache__/socket_consumers.cpython-38.pyc,sha256=NJUr7nRyHFvmAumxxWpsod5wzVVZM99rCEuJs1utHA4,8432
75
75
  simo/core/__pycache__/storage.cpython-38.pyc,sha256=9R1Xu0FJDflfRXUPsqEgt0SpwiP7FGk7HaR8s8XRyI8,721
76
- simo/core/__pycache__/tasks.cpython-38.pyc,sha256=O3FjdRHaMDFiuOGMCrzaMjNYSpekogYXbEzD8IUfkv0,9604
76
+ simo/core/__pycache__/tasks.cpython-38.pyc,sha256=WHUW2vB78F0J-E_JX5SNW8Yieugam8Jqerg88NRJTDE,9533
77
77
  simo/core/__pycache__/todos.cpython-38.pyc,sha256=lOqGZ58siHM3isoJV4r7sg8igrfE9fFd-jSfeBa0AQI,253
78
78
  simo/core/__pycache__/views.cpython-38.pyc,sha256=D7X_fFxYySe-mKNB4bEnIt9ubYM7CbbRcIpX52Ou4RE,2809
79
79
  simo/core/__pycache__/widgets.cpython-38.pyc,sha256=sR0ZeHCHrhnNDBJuRrxp3zUsfBp0xrtF0xrK2TkQv1o,3520
@@ -130,10 +130,12 @@ simo/core/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
130
130
  simo/core/management/__pycache__/__init__.cpython-38.pyc,sha256=Ptf1WzljXMt3wP1tzOy6q3JfLERYDs66wSHBVdrzjHg,169
131
131
  simo/core/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
132
132
  simo/core/management/commands/gateways_manager.py,sha256=a_JmUG1SPJhbhPh5QdViCT5DL0YvKQH-KsO8ptLhB34,6970
133
+ simo/core/management/commands/on_http_start.py,sha256=un8r0oOwU7niC7uHf63IFvFBVWkl4GeTLEkpmDUpanA,1802
133
134
  simo/core/management/commands/run_gateway.py,sha256=bp0FQQoBeOSoxjHCCMicDL1fxPZZGyLgnq2QKht3bJo,645
134
135
  simo/core/management/commands/update.py,sha256=Y2_6EL8E757nr-MjSuIpdSsEItI0yN42DT5P1e1zkno,175
135
136
  simo/core/management/commands/__pycache__/__init__.cpython-38.pyc,sha256=WKpfZZpAB9D7U4X6oWQIrU_H-6rUmq8Gl9fj9XaY2fw,178
136
137
  simo/core/management/commands/__pycache__/gateways_manager.cpython-38.pyc,sha256=pgEJdchhOcqKCpjdRMeF0_QKJfMmfSkl_W4TUwcgS9o,6031
138
+ simo/core/management/commands/__pycache__/on_http_start.cpython-38.pyc,sha256=LQeFW3oYYRrEPEcGghyeahFE114-4VbnKH4XaVGcQcg,3235
137
139
  simo/core/migrations/0001_initial.py,sha256=0Uy7IqJxQQYlurs8Mw_RJy7NaWS7BU0VFmZBBz8YkQI,9220
138
140
  simo/core/migrations/0002_load_icons.py,sha256=s9TtGo5NWEyWV3BspfbDNAlWqmQWxmDj7GjEaJXruFk,2044
139
141
  simo/core/migrations/0003_create_default_zones_and_categories.py,sha256=g7Ee6_gLQvrhM9u2tgLpcYTN5xmsRtuvU3_Ya3yoxqM,395
@@ -10195,12 +10197,12 @@ simo/fleet/controllers.py,sha256=NOoM7XmMIJfqw92JE5PI6sQo-Vlzb67YDMtebUW7T9k,305
10195
10197
  simo/fleet/forms.py,sha256=YF34ZCDbLeAKpq4u0KTi8IsyqhDtNUWcal2DELRayuI,57668
10196
10198
  simo/fleet/gateways.py,sha256=lKEJW0MgaOEiNnijH50DNSVChvaUT3TA3UurcI57P8k,5677
10197
10199
  simo/fleet/managers.py,sha256=XOpDOA9L-f_550TNSyXnJbun2EmtGz1TenVTMlUSb8E,807
10198
- simo/fleet/models.py,sha256=t_oi6EYSkg8Y5p3trJPv4MqW6AyUcylge9Bfw83mWCg,16462
10200
+ simo/fleet/models.py,sha256=dJYDTHBz1OizzP51G0fdKHSuW98CsqBX-NhgjcVqYHY,16518
10199
10201
  simo/fleet/routing.py,sha256=cofGsVWXMfPDwsJ6HM88xxtRxHwERhJ48Xyxc8mxg5o,149
10200
10202
  simo/fleet/serializers.py,sha256=-16BjY_bp9VbDOYuD0V54h7r_RHpuLNkJX0SydWL9aU,2247
10201
10203
  simo/fleet/socket_consumers.py,sha256=aBNTxvYIw5a5l2ns9x0LnjVJvp4NValEJG4MT4hGAT0,17903
10202
10204
  simo/fleet/tasks.py,sha256=AGq9BXFNAqkhOANsPvId8yjEbDtVCB3MRsi_AKDpgIM,821
10203
- simo/fleet/utils.py,sha256=BYqvLexbIJzBdPVPsPuMfkrzM0zHVwo0HPkCFBxOJEQ,4455
10205
+ simo/fleet/utils.py,sha256=4RaoxyOByh3_Svb-WgTdQjG6R6ZGRN4Zf7-daFc4H80,4708
10204
10206
  simo/fleet/views.py,sha256=OzsumjMjjt2WEXuThBzSAHcTNLU2dyBtvz4IyeHoAaA,3226
10205
10207
  simo/fleet/__pycache__/__init__.cpython-38.pyc,sha256=pIZE7EL6-cuJ3pQtaSwjKLrKLsTYelp1k9sRhXKLh6s,159
10206
10208
  simo/fleet/__pycache__/admin.cpython-38.pyc,sha256=IqVvpyyOnHChnEc07GubvtH6Tk1PA0rcYGYrJDg0hm4,6503
@@ -10212,12 +10214,12 @@ simo/fleet/__pycache__/controllers.cpython-38.pyc,sha256=Pot0NMkAvOR6YEX0UYVApcG
10212
10214
  simo/fleet/__pycache__/forms.cpython-38.pyc,sha256=LOxl1jynQbd_36Qa8eyTRkHLXiA575Ph29XVqGX0qsU,39066
10213
10215
  simo/fleet/__pycache__/gateways.cpython-38.pyc,sha256=0RKVn0ndreVKhsrukqeLPSdMnRrsQ_W7yeVeBkRLfIk,5058
10214
10216
  simo/fleet/__pycache__/managers.cpython-38.pyc,sha256=8uz-xpUiqbGDgXIZ_XRZtFb-Tju6NGxflGg-Ee4Yo6k,1310
10215
- simo/fleet/__pycache__/models.cpython-38.pyc,sha256=GZ01BjdvTn6_XJBfV8VrSldJ67X06ne-xW4CsQ6N6Wc,13756
10217
+ simo/fleet/__pycache__/models.cpython-38.pyc,sha256=pEiMWANjJpKXSR60PosKCUQOKpo6tSvCeWX4VP-0Lns,13796
10216
10218
  simo/fleet/__pycache__/routing.cpython-38.pyc,sha256=aPrCmxFKVyB8R8ZbJDwdPdFfvT7CvobovvZeq_mqRgY,314
10217
10219
  simo/fleet/__pycache__/serializers.cpython-38.pyc,sha256=9ljhwoHkolcVrJwOVbYCbGPAUKgALRwor_M3W_K0adE,3173
10218
10220
  simo/fleet/__pycache__/socket_consumers.cpython-38.pyc,sha256=oAnUJbrKjhC3-G-o4F-bx3ZztQf7JhmHi-Sh3cm4-4s,13549
10219
10221
  simo/fleet/__pycache__/tasks.cpython-38.pyc,sha256=RoNxL2WUiW67s9O9DjaYVVjCBSZu2nje0Qn9FJkWVS0,1116
10220
- simo/fleet/__pycache__/utils.cpython-38.pyc,sha256=e-cajNdKJjfBbZ9LdO7KUIccafp1xIIrVosjzM2v1BQ,3239
10222
+ simo/fleet/__pycache__/utils.cpython-38.pyc,sha256=J2N68RzYUnzFeqKH50x9Vtrwd3nrkMKBWOfD99IvfIs,3344
10221
10223
  simo/fleet/__pycache__/views.cpython-38.pyc,sha256=qwW2t_SNX7lLBS1RvYuxYBk9XRkeUcir5O5VYcrjMLY,3136
10222
10224
  simo/fleet/migrations/0001_initial.py,sha256=lce8nkD8Sz6pYr-XJSpDm4CMDuB6TA__WtnHpIp-eA4,1326
10223
10225
  simo/fleet/migrations/0002_auto_20220422_0743.py,sha256=sFOfAjnQOzcJjE8lHrrHgTaGilJNYswMdXphgVzUZqY,825
@@ -10344,17 +10346,15 @@ simo/generic/templates/generic/controllers_info/stateselect.md,sha256=T0w3vJg02W
10344
10346
  simo/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10345
10347
  simo/management/auto_update.py,sha256=4MDrJHdtC5LxEJM258Y0kc5yI4yloeKhDjh-S2BN-ZQ,2115
10346
10348
  simo/management/copy_template.py,sha256=Iehq57FYMzdHNp3LU4ue6rr6AkRiGeOthG7PoGWd88Q,2002
10347
- simo/management/install.py,sha256=hy2LB0sPNpaFHv2Wvp6CeiItvKJb3sFVU0lJuz02VDM,10127
10348
- simo/management/on_http_start.py,sha256=ZDotfMQaCjksD5FFf3eZYgJS-gd_-7eZhYTHLaD-448,3312
10349
+ simo/management/install.py,sha256=hKyjqzE90cyCKYkXEyMhMToYk3xzNZgyjO7nPwrklJo,8668
10349
10350
  simo/management/__pycache__/__init__.cpython-38.pyc,sha256=ey9k5mPsmvAHRVf5Du6QUqy40LgBCAPN_B5EaR6h9Eg,164
10350
10351
  simo/management/__pycache__/auto_update.cpython-38.pyc,sha256=OAp7w4PdXY0xeo7Yof7O_x9y7qNMyZV4ZerRlswjQ2I,1681
10351
- simo/management/__pycache__/on_http_start.cpython-38.pyc,sha256=ERfcMNz3QnqDJTJ4PwbmDfLgPCh5hrEcaedejARFkUQ,2864
10352
10352
  simo/management/_hub_template/hub/asgi.py,sha256=ElN_fdeSkf0Ysa7pS9rJVmZ1HmLhFxb8jFaMLqe1220,126
10353
10353
  simo/management/_hub_template/hub/celeryc.py,sha256=3ksDXftIZKJ4Cq9WNKJERdZdQlDEnjTQXycweRFmsSQ,27
10354
10354
  simo/management/_hub_template/hub/manage.py,sha256=PNNlw3EVeIJDgkG0l-klqoxsKWfTYWG9jzRG0upmAaI,620
10355
10355
  simo/management/_hub_template/hub/nginx.conf,sha256=40hvXL42MeiqqkLURNcDQsRudv1dNFLJnvb2-Y3RCkk,2394
10356
10356
  simo/management/_hub_template/hub/settings.py,sha256=4QhvhbtLRxHvAntwqG_qeAAtpDUqKvN4jzw9u3vqff8,361
10357
- simo/management/_hub_template/hub/supervisor.conf,sha256=DpLGDoyTRs27tVN-vg5ANpUnDUC68cFdlBsAp-XEm78,1815
10357
+ simo/management/_hub_template/hub/supervisor.conf,sha256=tl1nW95DrVwcGxgLXAOgIn5ow1p_-LRNNE1bCEoq00k,1888
10358
10358
  simo/management/_hub_template/hub/urls.py,sha256=Ydm-1BkYAzWeEF-MKSDIFf-7aE4qNLPm48-SA51XgJQ,25
10359
10359
  simo/management/_hub_template/hub/wsgi.py,sha256=Lo-huLHnMDTxSmMBOodVFMWBls9poddrV2KRzXU0xGo,280
10360
10360
  simo/multimedia/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -10404,15 +10404,15 @@ simo/notifications/migrations/__pycache__/0001_initial.cpython-38.pyc,sha256=YnO
10404
10404
  simo/notifications/migrations/__pycache__/0002_notification_instance.cpython-38.pyc,sha256=Gkb3Qwr_zglGibQg9g5ekIgxtGapS4ENXVWQVHqM56I,794
10405
10405
  simo/notifications/migrations/__pycache__/__init__.cpython-38.pyc,sha256=YMBRHVon2nWDtIUbghckjnC12sIg_ykPWhV5aM0tto4,178
10406
10406
  simo/users/__init__.py,sha256=6a7uBpCWB_DR7p54rbHusc0xvi1qfT1ZCCQGb6TiBh8,52
10407
- simo/users/admin.py,sha256=6RKGnwcrmewJFPzpqnxYn8rxjHO4tJPVFJvA3eMum2s,6746
10408
- simo/users/api.py,sha256=HUY4H9kK_HZKeN4VFERcbNDp6Mmp6p2LdDKBDFvWGUE,10096
10407
+ simo/users/admin.py,sha256=QQgan5_DyX22LdNFSWf2LHnTRO8gdQD4N295R-owuz0,6397
10408
+ simo/users/api.py,sha256=kUcdHm-WUn6z_AnFEXHnjYu8YX2MW1Z-NlAysRmSdLI,10333
10409
10409
  simo/users/apps.py,sha256=cq0A8-U1HALEwev0TicgFhr4CAu7Icz8rwq0HfOaL4E,207
10410
10410
  simo/users/auth_backends.py,sha256=bBSNXQJ88TRXaQxyh1aETfmOIfiDr08Jnj8rSY9sHDk,4074
10411
10411
  simo/users/auto_urls.py,sha256=lcJvteBsbHQMJieZpDz-63tDYejLApqsW3CUnDakd7k,272
10412
10412
  simo/users/dynamic_settings.py,sha256=sEIsi4yJw3kH46Jq_aOkSuK7QTfQACGUE-lkyBogCaM,570
10413
10413
  simo/users/managers.py,sha256=M_51bk9z4jn8e2Ci3pJfIqbf6cRNqfQNSOAg0vPl6Vo,175
10414
10414
  simo/users/middleware.py,sha256=GMCrnWSc_2qCleyQIkfQGdL-pU-UTEcSg1wPvIKZ9uk,1210
10415
- simo/users/models.py,sha256=F3rkYmofakoVzr7szjr8t8014-bbN_Nkv1TVOVKoIF8,19761
10415
+ simo/users/models.py,sha256=5ZMXBFExo1oO6vKYn4qOBSbgCp2kiAFNK82hm2k70vA,19195
10416
10416
  simo/users/permissions.py,sha256=IwtYS8yQdupWbYKR9VimSRDV3qCJ2jXP57Lyjpb2EQM,242
10417
10417
  simo/users/serializers.py,sha256=a4R408ZgWVbF7OFw4bBfN33Wnn8ljqS8iFcsqmllkWU,2552
10418
10418
  simo/users/sso_urls.py,sha256=gQOaPvGMYFD0NCVSwyoWO-mTEHe5j9sbzV_RK7kdvp0,251
@@ -10421,15 +10421,15 @@ simo/users/tasks.py,sha256=HJAqiyWGsaN3wSfquU0UyQ20jL-njXeaaTOdDT3TQ3s,979
10421
10421
  simo/users/utils.py,sha256=7gU_TDnAOsDYqJM0CFo8efPah2bTXfGpXxRqzD5RiSs,1270
10422
10422
  simo/users/views.py,sha256=dOQVvmlHG7ihWKJLFUBcqKOA0UDctlMKR0pTc36JZqg,3487
10423
10423
  simo/users/__pycache__/__init__.cpython-38.pyc,sha256=VFoDJE_SKKaPqqYaaBYd1Ndb1hjakkTo_u0EG_XJ1GM,211
10424
- simo/users/__pycache__/admin.cpython-38.pyc,sha256=paoWxwJgOyDF7RT7LIviDqggdELG9-fbydc9UfqHV10,7500
10425
- simo/users/__pycache__/api.cpython-38.pyc,sha256=QqQL0MyG8-_7HkqPvqwINuYoco-pJEQ8zT8Crr7t3Rc,8602
10424
+ simo/users/__pycache__/admin.cpython-38.pyc,sha256=zwD7hbwzZ4VwsQlrtG8j8KOqTRDQmhup-Y_hcbZDXD0,7315
10425
+ simo/users/__pycache__/api.cpython-38.pyc,sha256=25he7r5DHfcLYveXWhn6gQDV2R6YnpKbUVBNInR1q_s,8714
10426
10426
  simo/users/__pycache__/apps.cpython-38.pyc,sha256=dgbWL8CxzzISJQTmq_4IztPJ2UzykNVdqA2Ae1PmeGk,605
10427
10427
  simo/users/__pycache__/auth_backends.cpython-38.pyc,sha256=n5nx2QSXNj2idzRcGE6bAagMN-8qxoCs580H1EFZXls,3105
10428
10428
  simo/users/__pycache__/auto_urls.cpython-38.pyc,sha256=K-3sz2h-cEitoflSmZk1t0eUg5mQMMGLNZFREVwG7_o,430
10429
10429
  simo/users/__pycache__/dynamic_settings.cpython-38.pyc,sha256=6F8JBjZkHykySnmZjNEzjS0ijbmPdcp9yUAZ5kqq_Fo,864
10430
10430
  simo/users/__pycache__/managers.cpython-38.pyc,sha256=C5-diljm874RAFMTkZdcfzPhkHzlUGPAhz2gTvqkDy8,604
10431
10431
  simo/users/__pycache__/middleware.cpython-38.pyc,sha256=Tj4nVEAvxEW3xA63fBRiJWRJpz_M848ZOqbHioc_IPE,1149
10432
- simo/users/__pycache__/models.cpython-38.pyc,sha256=_EIJC_Q1XeqxI7yKr6dOw6tv6o_GkA3TaFZ9YH5de1g,18193
10432
+ simo/users/__pycache__/models.cpython-38.pyc,sha256=q5ROEWKeXud_MK3olV7aMTstmbl4hAYIjs7H8L_v4Jc,17817
10433
10433
  simo/users/__pycache__/permissions.cpython-38.pyc,sha256=ez5NxoL_JUeeH6GsKhvFreuA3FCBgGf9floSypdXUtM,633
10434
10434
  simo/users/__pycache__/serializers.cpython-38.pyc,sha256=PuMy6H0PhEhq89RFmdnFH4pMHB0N3w7opJEFS90JUCY,3477
10435
10435
  simo/users/__pycache__/sso_urls.cpython-38.pyc,sha256=uAwDozpOmrhUald-8tOHANILXkH7-TI8fNYXOtPkSY8,402
@@ -10466,6 +10466,9 @@ simo/users/migrations/0026_fingerprint_name.py,sha256=DPmfi1brbaPymdNiPgc7dINSKy
10466
10466
  simo/users/migrations/0027_permissionsrole_can_manage_components.py,sha256=VcGZE6u-q6UkGo7D01K_T1XBtIvIGe8SCk5ZPRrPpGo,485
10467
10467
  simo/users/migrations/0028_auto_20240506_1146.py,sha256=7RUFF2rJH-bnPeHwc77p8Q4kEAc3owyG4qp9Kc4aKhU,716
10468
10468
  simo/users/migrations/0029_alter_instanceuser_instance.py,sha256=5ebO0vX9lCnTXBMkWg8633sBCBLNtMLfbocVY-uyQhE,588
10469
+ simo/users/migrations/0030_userdevice_users.py,sha256=cYY7yChsxOOauHAPIQUzLjLLiOA6kkopTQBE-M9HH10,448
10470
+ simo/users/migrations/0031_auto_20240923_1115.py,sha256=RyhmpsxpSXK9Ejbh3cHm9fTnDOcLFQsTH9-2MpNK5YU,514
10471
+ simo/users/migrations/0032_remove_userdevice_user_alter_userdevice_users.py,sha256=pM0B4ghsw6LWDvT_9owUmqIHQiraauTGYm_EePhVBUQ,570
10469
10472
  simo/users/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10470
10473
  simo/users/migrations/__pycache__/0001_initial.cpython-38.pyc,sha256=e4XOKaYRb7l0P7cBnHHi5FQQJMlwjK0g7iqgM-xKmNI,4215
10471
10474
  simo/users/migrations/__pycache__/0002_componentpermission.cpython-38.pyc,sha256=pknJnpic8p6Vdx9DX41FfODXNnvexDswJtUCmC5w1tg,995
@@ -10498,6 +10501,9 @@ simo/users/migrations/__pycache__/0028_auto_20240506_1146.cpython-38.pyc,sha256=
10498
10501
  simo/users/migrations/__pycache__/0029_alter_instanceuser_instance.cpython-38.pyc,sha256=2EsX6ksqPomNHiPCORDYENqn2yI7dQUK_4hMSpMVxDE,823
10499
10502
  simo/users/migrations/__pycache__/0029_alter_instanceuser_options_instanceuser_order.cpython-38.pyc,sha256=aqC63NS8xpFKzLuvL00_axUAvjTzqmvlgfkD03bzakg,721
10500
10503
  simo/users/migrations/__pycache__/0030_alter_instanceuser_options_remove_instanceuser_order.cpython-38.pyc,sha256=uC2t0g2AGdQ53Z621ZcS4-n6UGxjRUM6AFUAMrGIMnY,658
10504
+ simo/users/migrations/__pycache__/0030_userdevice_users.cpython-38.pyc,sha256=PWQ77FvS69xbGdzC9E7qXX9RLQ9QC4a__0h0oRAI7Ec,674
10505
+ simo/users/migrations/__pycache__/0031_auto_20240923_1115.cpython-38.pyc,sha256=0nT2ftDPi0yV6j-p9Am-nVsTy2kY0msM_f2k0eP9EoU,866
10506
+ simo/users/migrations/__pycache__/0032_remove_userdevice_user_alter_userdevice_users.cpython-38.pyc,sha256=SDKPEIkt9DYEBRm63GAnxG1yRKKm-JP3pizaJdbAUks,772
10501
10507
  simo/users/migrations/__pycache__/__init__.cpython-38.pyc,sha256=NKq7WLgktK8WV1oOqCPbAbdkrPV5GRGhYx4VxxI4dcs,170
10502
10508
  simo/users/templates/conf/mosquitto.conf,sha256=1eIGNuRu4Y3hfAU6qiWix648eCRrw0oOT24PnyFI4ys,189
10503
10509
  simo/users/templates/conf/mosquitto_acls.conf,sha256=ga44caTDNQE0CBKw55iM2jOuna6-9fKGwAhjyERZdRE,500
@@ -10507,9 +10513,9 @@ simo/users/templates/invitations/expired_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCe
10507
10513
  simo/users/templates/invitations/expired_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10508
10514
  simo/users/templates/invitations/taken_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10509
10515
  simo/users/templates/invitations/taken_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10510
- simo-2.2.10.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10511
- simo-2.2.10.dist-info/METADATA,sha256=EEKuyC6CQhzBgYsGV0L6R-28zL45JxZuLNnulJrs5wU,1881
10512
- simo-2.2.10.dist-info/WHEEL,sha256=5Mi1sN9lKoFv_gxcPtisEVrJZihrm_beibeg5R6xb4I,91
10513
- simo-2.2.10.dist-info/entry_points.txt,sha256=SJBxiDpH7noO0STxVI_eRIsGR-nLgdXXeqCDe8cXlbM,65
10514
- simo-2.2.10.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10515
- simo-2.2.10.dist-info/RECORD,,
10516
+ simo-2.2.12.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10517
+ simo-2.2.12.dist-info/METADATA,sha256=H_OyTQyMFUXkAZxf_qvyTv_9ecYRdM1azzwJd5dkI-g,1881
10518
+ simo-2.2.12.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
10519
+ simo-2.2.12.dist-info/entry_points.txt,sha256=SJBxiDpH7noO0STxVI_eRIsGR-nLgdXXeqCDe8cXlbM,65
10520
+ simo-2.2.12.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10521
+ simo-2.2.12.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.0.0)
2
+ Generator: setuptools (75.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,103 +0,0 @@
1
- import os
2
- import pwd
3
- import grp
4
- import subprocess
5
- import pkg_resources
6
- from django.conf import settings
7
- from django.template.loader import render_to_string
8
-
9
-
10
- def prepare_mosquitto():
11
- if os.geteuid() != 0:
12
- return
13
-
14
- from simo.users.models import User
15
-
16
- users_file = '/etc/mosquitto/mosquitto_users'
17
- if not os.path.exists(users_file):
18
- with open(users_file, 'w') as f:
19
- f.write('')
20
-
21
- uid = pwd.getpwnam("mosquitto").pw_uid
22
- gid = grp.getgrnam("mosquitto").gr_gid
23
- os.chown(users_file, uid, gid)
24
- os.chmod(users_file, 0o640)
25
-
26
- acls_file = '/etc/mosquitto/acls.conf'
27
- with open(acls_file, 'w') as f:
28
- f.write('')
29
-
30
- uid = pwd.getpwnam("mosquitto").pw_uid
31
- gid = grp.getgrnam("mosquitto").gr_gid
32
- os.chown(acls_file, uid, gid)
33
- os.chmod(acls_file, 0o640)
34
-
35
- ps = subprocess.Popen(
36
- ['mosquitto_passwd /etc/mosquitto/mosquitto_users root'],
37
- shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE
38
- )
39
- ps.communicate(f"{settings.SECRET_KEY}\n{settings.SECRET_KEY}".encode())
40
-
41
- for user in User.objects.all():
42
- user.update_mqtt_secret(reload=False)
43
-
44
- from simo.users.utils import update_mqtt_acls
45
-
46
- update_mqtt_acls()
47
-
48
- if not os.path.exists('/etc/mosquitto/conf.d/simo.conf'):
49
- with open('/etc/mosquitto/conf.d/simo.conf', 'w') as f:
50
- f.write(render_to_string('conf/mosquitto.conf'))
51
-
52
- subprocess.run(
53
- ['service', 'mosquitto', 'reload'], stdout=subprocess.PIPE
54
- )
55
-
56
- prepare_mosquitto()
57
-
58
-
59
- def update_auto_update():
60
- import simo
61
- auto_update_file_path = os.path.join(
62
- os.path.dirname(simo.__file__), 'management',
63
- 'auto_update.py'
64
- )
65
- st = os.stat(auto_update_file_path)
66
- os.chmod(auto_update_file_path, st.st_mode | 0o111)
67
-
68
- executable_path = '/usr/local/bin/simo-auto-update'
69
- if os.geteuid() == 0:
70
- # We are running as root!
71
- if os.path.exists(executable_path):
72
- # refresh the link if it already exists
73
- os.remove(executable_path)
74
- os.symlink(auto_update_file_path, executable_path)
75
-
76
- if not os.path.islink(executable_path):
77
- # There is no symbolic link yet made for auto updates.
78
- # Let's make it!
79
- os.symlink(auto_update_file_path, executable_path)
80
- auto_update_cron = f'0 * * * * {executable_path} \n'
81
- cron_out = subprocess.Popen(['crontab', '-'], stdin=subprocess.PIPE)
82
- cron_out.communicate(input=str.encode(auto_update_cron))
83
-
84
-
85
- update_auto_update()
86
-
87
-
88
- def maybe_update_to_latest_immediately():
89
- from simo.core.tasks import update_latest_version_available, update
90
- from simo.core.models import Instance
91
- from simo.conf import dynamic_settings
92
- update_latest_version_available()
93
- if dynamic_settings['core__latest_version_available'] != \
94
- pkg_resources.get_distribution('simo').version:
95
- print("There is newer version, we should probably update!")
96
- if not Instance.objects.all().count():
97
- print("Yes let's do it asynchronously!")
98
- return update.s()
99
- print("Nope, we already have some instances running, "
100
- "so we leave that for hub owners.")
101
-
102
-
103
- maybe_update_to_latest_immediately()