simo 1.7.15__py3-none-any.whl → 1.7.17__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 (41) hide show
  1. simo/__pycache__/asgi.cpython-38.pyc +0 -0
  2. simo/__pycache__/on_http_start.cpython-38.pyc +0 -0
  3. simo/asgi.py +2 -0
  4. simo/core/__pycache__/app_widgets.cpython-38.pyc +0 -0
  5. simo/core/__pycache__/controllers.cpython-38.pyc +0 -0
  6. simo/core/__pycache__/events.cpython-38.pyc +0 -0
  7. simo/core/__pycache__/models.cpython-38.pyc +0 -0
  8. simo/core/__pycache__/socket_consumers.cpython-38.pyc +0 -0
  9. simo/core/__pycache__/tasks.cpython-38.pyc +0 -0
  10. simo/core/app_widgets.py +5 -0
  11. simo/core/controllers.py +34 -6
  12. simo/core/events.py +0 -3
  13. simo/core/management/commands/__pycache__/gateways_manager.cpython-38.pyc +0 -0
  14. simo/core/management/commands/gateways_manager.py +1 -1
  15. simo/core/migrations/0025_auto_20240122_1321.py +32 -0
  16. simo/core/migrations/__pycache__/0025_auto_20240122_1321.cpython-38.pyc +0 -0
  17. simo/core/models.py +8 -0
  18. simo/core/socket_consumers.py +1 -0
  19. simo/core/tasks.py +12 -0
  20. simo/core/templates/admin/controller_widgets/lock.html +11 -0
  21. simo/core/templates/admin/controller_widgets/switch.html +1 -2
  22. simo/generic/__pycache__/controllers.cpython-38.pyc +0 -0
  23. simo/generic/__pycache__/gateways.cpython-38.pyc +0 -0
  24. simo/generic/controllers.py +2 -4
  25. simo/generic/gateways.py +11 -14
  26. simo/on_http_start.py +78 -0
  27. simo/users/__pycache__/models.cpython-38.pyc +0 -0
  28. simo/users/migrations/0024_fingerprint.py +26 -0
  29. simo/users/migrations/__pycache__/0024_fingerprint.cpython-38.pyc +0 -0
  30. simo/users/models.py +12 -2
  31. {simo-1.7.15.dist-info → simo-1.7.17.dist-info}/METADATA +1 -2
  32. {simo-1.7.15.dist-info → simo-1.7.17.dist-info}/RECORD +35 -34
  33. simo/core/__pycache__/apps.cpython-38.pyc +0 -0
  34. simo/core/apps.py +0 -28
  35. simo/core/utils/__pycache__/serialization.cpython-38.pyc +0 -0
  36. simo/core/utils/serialization.py +0 -29
  37. simo/users/__pycache__/apps.cpython-38.pyc +0 -0
  38. simo/users/apps.py +0 -56
  39. {simo-1.7.15.dist-info → simo-1.7.17.dist-info}/LICENSE.md +0 -0
  40. {simo-1.7.15.dist-info → simo-1.7.17.dist-info}/WHEEL +0 -0
  41. {simo-1.7.15.dist-info → simo-1.7.17.dist-info}/top_level.txt +0 -0
Binary file
simo/asgi.py CHANGED
@@ -21,6 +21,8 @@ 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 .on_http_start import *
25
+
24
26
  application = ProtocolTypeRouter({
25
27
  "http": get_asgi_application(),
26
28
  'websocket': AuthMiddlewareStack(
Binary file
Binary file
Binary file
simo/core/app_widgets.py CHANGED
@@ -89,3 +89,8 @@ class QuintupleSwitchWidget(BaseAppWidget):
89
89
  size = [4, 1]
90
90
 
91
91
 
92
+ class LockWidget(BaseAppWidget):
93
+ uid = 'lock'
94
+ name = _("Lock")
95
+ size = [2, 2]
96
+
simo/core/controllers.py CHANGED
@@ -1,3 +1,4 @@
1
+ import sys
1
2
  import time
2
3
  import datetime
3
4
  import statistics
@@ -149,8 +150,10 @@ class ControllerBase(ABC):
149
150
  ]
150
151
 
151
152
  def _get_actor(self, to_value):
153
+ if self.component.change_init_fingerprint and self.component.change_init_fingerprint.user:
154
+ return self.component.change_init_fingerprint.user
152
155
  if self.component.change_init_by:
153
- if self.component.change_init_date < timezone.now() - datetime.timedelta(seconds=5):
156
+ if self.component.change_init_date < timezone.now() - datetime.timedelta(seconds=30):
154
157
  self.component.change_init_by = None
155
158
  self.component.change_init_date = None
156
159
  self.component.change_init_to = None
@@ -159,15 +162,15 @@ class ControllerBase(ABC):
159
162
  'change_init_to', 'alive']
160
163
  )
161
164
  return None
162
- if self.component.change_init_to == to_value:
165
+ else:
163
166
  return self.component.change_init_by
164
167
 
165
168
  def set(self, value, actor=None):
166
- from .models import ComponentHistory
167
169
  if not actor:
168
170
  actor = self._get_actor(value)
169
171
  if not actor:
170
172
  actor = get_current_user()
173
+
171
174
  # Introducing user to this thread for changes that might happen to other components
172
175
  # in relation to the change of this component
173
176
  introduce(actor)
@@ -175,13 +178,13 @@ class ControllerBase(ABC):
175
178
  value = self.component.translate_before_set(value)
176
179
  value = self._validate_val(value, BEFORE_SET)
177
180
  self.component.refresh_from_db()
178
- old_arm_status = self.component.arm_status
179
181
  if value != self.component.value:
180
182
  self.component.value_previous = self.component.value
181
183
  self.component.value = value
182
184
  self.component.change_init_by = None
183
185
  self.component.change_init_date = None
184
186
  self.component.change_init_to = None
187
+ self.component.change_init_fingerprint = None
185
188
  self.component.save()
186
189
 
187
190
  def _send_to_device(self, value):
@@ -214,9 +217,8 @@ class ControllerBase(ABC):
214
217
 
215
218
  self.component.change_init_by = get_current_user()
216
219
  self.component.change_init_date = timezone.now()
217
- self.component.change_init_to = value
218
220
  self.component.save(
219
- update_fields=['change_init_by', 'change_init_date', 'change_init_to']
221
+ update_fields=['change_init_by', 'change_init_date']
220
222
  )
221
223
  value = self._prepare_for_send(value)
222
224
  self._send_to_device(value)
@@ -668,9 +670,35 @@ class QuintupleSwitch(MultiSwitchBase):
668
670
  class Lock(Switch):
669
671
  name = _("Lock")
670
672
  base_type = 'lock'
673
+ app_widget = LockWidget
674
+ admin_widget_template = 'admin/controller_widgets/lock.html'
671
675
 
672
676
  def lock(self):
673
677
  self.turn_on()
674
678
 
675
679
  def unlock(self):
676
680
  self.turn_off()
681
+
682
+ def _receive_from_device(self, value, is_alive=True):
683
+ if type(value) in (int, bool):
684
+ if value:
685
+ value = 'locked'
686
+ else:
687
+ value = 'unlocked'
688
+ return super()._receive_from_device(value, is_alive=is_alive)
689
+
690
+ def _validate_val(self, value, occasion=None):
691
+ if occasion == BEFORE_SEND:
692
+ if type(value) != bool:
693
+ raise ValidationError("Boolean required to lock/unlock.")
694
+ else:
695
+ available_values = (
696
+ 'locked', 'unlocked', 'locking', 'unlocking',
697
+ 'operating', 'fault'
698
+ )
699
+ if value not in available_values:
700
+ raise ValidationError(
701
+ f"Received value ({value}) that is not "
702
+ f"one of available values [{available_values}] for lock."
703
+ )
704
+ return value
simo/core/events.py CHANGED
@@ -8,7 +8,6 @@ from django.conf import settings
8
8
  import paho.mqtt.client as mqtt
9
9
  from django.utils import timezone
10
10
  import paho.mqtt.publish as mqtt_publish
11
- from .utils.serialization import normalize
12
11
 
13
12
  logger = logging.getLogger(__name__)
14
13
 
@@ -111,8 +110,6 @@ class OnChangeMixin:
111
110
  return
112
111
  if payload['obj_ct_pk'] != self._obj_ct_id:
113
112
  return
114
- if payload['event'] != 'changed':
115
- return
116
113
  if 'value' not in payload.get('dirty_fields', {}):
117
114
  return
118
115
 
@@ -138,7 +138,7 @@ class GatewaysManager:
138
138
  return sys.exit()
139
139
 
140
140
  def on_mqtt_connect(self, mqtt_client, userdata, flags, rc):
141
- mqtt_client.subscribe(GatewayObjectCommand.TOPIC)
141
+ mqtt_client.subscribe(f'{GatewayObjectCommand.TOPIC}/*')
142
142
 
143
143
  def on_mqtt_message(self, client, userdata, msg):
144
144
  payload = json.loads(msg.payload)
@@ -0,0 +1,32 @@
1
+ # Generated by Django 3.2.9 on 2024-01-22 13:21
2
+
3
+ from django.conf import settings
4
+ from django.db import migrations, models
5
+ import django.db.models.deletion
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+
10
+ dependencies = [
11
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12
+ ('users', '0024_fingerprint'),
13
+ ('core', '0024_alter_instance_device_report_history_days'),
14
+ ]
15
+
16
+ operations = [
17
+ migrations.AddField(
18
+ model_name='component',
19
+ name='change_init_fingerprint',
20
+ field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, to='users.fingerprint'),
21
+ ),
22
+ migrations.AddField(
23
+ model_name='instance',
24
+ name='learn_fingerprints',
25
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
26
+ ),
27
+ migrations.AddField(
28
+ model_name='instance',
29
+ name='learn_fingerprints_start',
30
+ field=models.DateTimeField(blank=True, null=True),
31
+ ),
32
+ ]
simo/core/models.py CHANGED
@@ -107,6 +107,10 @@ class Instance(DirtyFieldsMixin, models.Model, SimoAdminMixin):
107
107
  help_text="How many days of user device reports log do we keep? "
108
108
  "Use 0 if you do not want to keep these logs at all."
109
109
  )
110
+ learn_fingerprints_start = models.DateTimeField(null=True, blank=True)
111
+ learn_fingerprints = models.ForeignKey(
112
+ User, null=True, blank=True, on_delete=models.SET_NULL
113
+ )
110
114
 
111
115
  def __str__(self):
112
116
  return self.name
@@ -288,6 +292,10 @@ class Component(DirtyFieldsMixin, models.Model, SimoAdminMixin, OnChangeMixin):
288
292
  )
289
293
  change_init_date = models.DateTimeField(null=True, editable=False)
290
294
  change_init_to = models.JSONField(null=True, editable=False)
295
+ change_init_fingerprint = models.ForeignKey(
296
+ 'users.Fingerprint', null=True, editable=False,
297
+ on_delete=models.SET_NULL
298
+ )
291
299
  last_change = models.DateTimeField(
292
300
  null=True, editable=False, auto_now_add=True
293
301
  )
@@ -257,6 +257,7 @@ class ComponentController(SIMOWebsocketConsumer):
257
257
  ))
258
258
 
259
259
  def receive(self, text_data=None, bytes_data=None, **kwargs):
260
+ introduce(self.scope['user'])
260
261
  json_data = json.loads(text_data)
261
262
  self.send_value = json_data.pop('send_value', False)
262
263
  for method, param in json_data.items():
simo/core/tasks.py CHANGED
@@ -302,9 +302,21 @@ def update_latest_version_available():
302
302
  dynamic_settings['core__latest_version_available'] = latest
303
303
 
304
304
 
305
+ @celery_app.task
306
+ def drop_fingerprints_learn():
307
+ Instance.objects.filter(
308
+ learn_fingerprints__isnull=False,
309
+ learn_fingerprints_start__lt=timezone.now() - datetime.timedelta(minutes=5)
310
+ ).update(
311
+ learn_fingerprints=None,
312
+ learn_fingerprints_start=None
313
+ )
314
+
315
+
305
316
  @celery_app.on_after_finalize.connect
306
317
  def setup_periodic_tasks(sender, **kwargs):
307
318
  sender.add_periodic_task(1, watch_timers.s())
308
319
  sender.add_periodic_task(20, sync_with_remote.s())
309
320
  sender.add_periodic_task(60 * 60, clear_history.s())
310
321
  sender.add_periodic_task(60 * 60 * 6, update_latest_version_available.s())
322
+ sender.add_periodic_task(60, drop_fingerprints_learn.s())
@@ -0,0 +1,11 @@
1
+ <div class="component-controller" data-ws_url="{{ obj.get_socket_url|default_if_none:"" }}">
2
+ {% if obj.value == 'locked' %}
3
+ <i class="fas fa-toggle-on fa-2x component_switch action"
4
+ data-method="turn_off"
5
+ ></i>
6
+ {% else %}
7
+ <i class="fas fa-toggle-off fa-2x component_switch action"
8
+ data-method="turn_on"
9
+ ></i>
10
+ {% endif %}
11
+ </div>
@@ -1,5 +1,4 @@
1
- <div class="component-controller"
2
- data-ws_url="{{ obj.get_socket_url|default_if_none:"" }}">
1
+ <div class="component-controller" data-ws_url="{{ obj.get_socket_url|default_if_none:"" }}">
3
2
  {% if obj.value %}
4
3
  <i class="fas fa-toggle-on fa-2x component_switch action"
5
4
  data-method="turn_off"
@@ -71,9 +71,7 @@ class Script(ControllerBase, TimerMixin):
71
71
  self.component.refresh_from_db()
72
72
  self.component.config['code'] = new_code
73
73
  self.component.save(update_fields=['config'])
74
- GatewayObjectCommand(self.component, **{'set_val': 'start'}).publish()
75
- elif value == 'stop':
76
- GatewayObjectCommand(self.component, **{'set_val': 'stop'}).publish()
74
+ return super()._send_to_device(value)
77
75
 
78
76
  def _val_to_success(self, value):
79
77
  if value == 'start':
@@ -1238,7 +1236,7 @@ class AlarmClock(ControllerBase):
1238
1236
 
1239
1237
  # At this point there is an alarm that we are looking forward or we are in it already
1240
1238
 
1241
- if current_value['alarm_uid'] in current_value.get('ignore_alarms', {}):
1239
+ if current_value.get('alarm_uid') in current_value.get('ignore_alarms', {}):
1242
1240
  return current_value
1243
1241
 
1244
1242
  for event in current_value['events']:
simo/generic/gateways.py CHANGED
@@ -239,28 +239,25 @@ class GenericGatewayHandler(BaseObjectCommandsGatewayHandler):
239
239
  while len(script_ids):
240
240
  time.sleep(0.1)
241
241
 
242
-
243
242
  def on_mqtt_connect(self, mqtt_client, userdata, flags, rc):
244
- mqtt_client.subscribe(GatewayObjectCommand.TOPIC)
243
+ command = GatewayObjectCommand(self.gateway_instance)
244
+ mqtt_client.subscribe(command.get_topic())
245
245
 
246
246
  def on_mqtt_message(self, client, userdata, msg):
247
- from simo.core.controllers import Switch, BinarySensor
248
- from simo.generic.controllers import Script, Gate, Blinds
247
+ from simo.generic.controllers import Script, Blinds
249
248
  payload = json.loads(msg.payload)
250
249
  component = get_event_obj(payload, Component)
251
250
  if not component:
252
251
  return
253
252
 
254
- if msg.topic == GatewayObjectCommand.TOPIC:
255
- # Handle scripts
256
- if isinstance(component.controller, Script):
257
- if payload['kwargs'].get('set_val') == 'start':
258
- self.start_script(component)
259
- elif payload['kwargs'].get('set_val') == 'stop':
260
- self.stop_script(component)
261
- return
262
- elif component.controller_uid == Blinds.uid:
263
- self.control_blinds(component, payload['kwargs'].get('set_val'))
253
+ if isinstance(component.controller, Script):
254
+ if payload.get('set_val') == 'start':
255
+ self.start_script(component)
256
+ elif payload.get('set_val') == 'stop':
257
+ self.stop_script(component)
258
+ return
259
+ elif component.controller_uid == Blinds.uid:
260
+ self.control_blinds(component, payload.get('set_val'))
264
261
 
265
262
  def start_script(self, component):
266
263
  print("START SCRIPT %s" % str(component))
simo/on_http_start.py ADDED
@@ -0,0 +1,78 @@
1
+ import os
2
+ import pwd
3
+ import grp
4
+ import subprocess
5
+ from django.conf import settings
6
+ from django.template.loader import render_to_string
7
+
8
+
9
+ def prepare_mosquitto():
10
+ if os.geteuid() != 0:
11
+ return
12
+
13
+ from simo.users.models import User
14
+
15
+ users_file = '/etc/mosquitto/mosquitto_users'
16
+ if not os.path.exists(users_file):
17
+ with open(users_file, 'w') as f:
18
+ f.write('')
19
+
20
+ uid = pwd.getpwnam("mosquitto").pw_uid
21
+ gid = grp.getgrnam("mosquitto").gr_gid
22
+ os.chown(users_file, uid, gid)
23
+ os.chmod(users_file, 0o640)
24
+
25
+ acls_file = '/etc/mosquitto/acls.conf'
26
+ with open(acls_file, 'w') as f:
27
+ f.write('')
28
+
29
+ uid = pwd.getpwnam("mosquitto").pw_uid
30
+ gid = grp.getgrnam("mosquitto").gr_gid
31
+ os.chown(acls_file, uid, gid)
32
+ os.chmod(acls_file, 0o640)
33
+
34
+ ps = subprocess.Popen(
35
+ ['mosquitto_passwd /etc/mosquitto/mosquitto_users root'],
36
+ shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE
37
+ )
38
+ ps.communicate(f"{settings.SECRET_KEY}\n{settings.SECRET_KEY}".encode())
39
+
40
+ for user in User.objects.all():
41
+ user.update_mqtt_secret(reload=False)
42
+
43
+ from simo.users.utils import update_mqtt_acls
44
+
45
+ update_mqtt_acls()
46
+
47
+ if not os.path.exists('/etc/mosquitto/conf.d/simo.conf'):
48
+ with open('/etc/mosquitto/conf.d/simo.conf', 'w') as f:
49
+ f.write(render_to_string('conf/mosquitto.conf'))
50
+
51
+ subprocess.run(
52
+ ['service', 'mosquitto', 'reload'], stdout=subprocess.PIPE
53
+ )
54
+
55
+ prepare_mosquitto()
56
+
57
+
58
+ def update_auto_update():
59
+ import simo
60
+ auto_update_file_path = os.path.join(
61
+ os.path.dirname(simo.__file__), 'auto_update.py'
62
+ )
63
+ st = os.stat(auto_update_file_path)
64
+ os.chmod(auto_update_file_path, st.st_mode | 0o111)
65
+
66
+ executable_path = '/usr/local/bin/simo-auto-update'
67
+ if os.geteuid() == 0:
68
+ # We are running as root!
69
+ if not os.path.islink(executable_path):
70
+ # There is no symbolic link yet made for auto updates.
71
+ # Let's make it!
72
+ os.symlink(auto_update_file_path, executable_path)
73
+ auto_update_cron = f'0 * * * * {executable_path} \n'
74
+ cron_out = subprocess.Popen(['crontab', '-'], stdin=subprocess.PIPE)
75
+ cron_out.communicate(input=str.encode(auto_update_cron))
76
+
77
+
78
+ update_auto_update()
@@ -0,0 +1,26 @@
1
+ # Generated by Django 3.2.9 on 2024-01-22 13:21
2
+
3
+ from django.conf import settings
4
+ from django.db import migrations, models
5
+ import django.db.models.deletion
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+
10
+ dependencies = [
11
+ ('users', '0023_auto_20240105_0719'),
12
+ ]
13
+
14
+ operations = [
15
+ migrations.CreateModel(
16
+ name='Fingerprint',
17
+ fields=[
18
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
19
+ ('value', models.CharField(db_index=True, max_length=200, unique=True)),
20
+ ('date_created', models.DateTimeField(auto_now_add=True)),
21
+ ('name', models.CharField(blank=True, max_length=100, null=True)),
22
+ ('is_valid', models.BooleanField(db_index=True, default=True)),
23
+ ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='fingerprints', to=settings.AUTH_USER_MODEL)),
24
+ ],
25
+ ),
26
+ ]
simo/users/models.py CHANGED
@@ -120,7 +120,6 @@ def post_instance_user_save(sender, instance, created, **kwargs):
120
120
  rebuild_mqtt_acls.delay()
121
121
 
122
122
 
123
-
124
123
  class User(AbstractBaseUser, SimoAdminMixin):
125
124
  name = models.CharField(_('name'), max_length=150)
126
125
  email = models.EmailField(_('email address'), unique=True)
@@ -354,6 +353,17 @@ class User(AbstractBaseUser, SimoAdminMixin):
354
353
  return components
355
354
 
356
355
 
356
+ class Fingerprint(models.Model):
357
+ value = models.CharField(max_length=200, db_index=True, unique=True)
358
+ user = models.ForeignKey(
359
+ User, on_delete=models.CASCADE, null=True, blank=True,
360
+ related_name='fingerprints'
361
+ )
362
+ date_created = models.DateTimeField(auto_now_add=True)
363
+ name = models.CharField(max_length=100, null=True, blank=True)
364
+ is_valid = models.BooleanField(default=True, db_index=True)
365
+
366
+
357
367
  class UserDevice(models.Model, SimoAdminMixin):
358
368
  user = models.ForeignKey(
359
369
  User, on_delete=models.CASCADE, related_name='devices'
@@ -557,4 +567,4 @@ class InstanceInvitation(models.Model):
557
567
  return response
558
568
 
559
569
  def get_absolute_url(self):
560
- return reverse('accept_invitation', kwargs={'token': self.token})
570
+ return reverse('accept_invitation', kwargs={'token': self.token})
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: simo
3
- Version: 1.7.15
3
+ Version: 1.7.17
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
@@ -37,7 +37,6 @@ Requires-Dist: webservices ==0.7
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
40
- Requires-Dist: click ==8.0.3
41
40
  Requires-Dist: suntime ==1.2.5
42
41
  Requires-Dist: django-location-field ==2.1.0
43
42
  Requires-Dist: django-taggit ==2.0.0
@@ -1,16 +1,18 @@
1
1
  simo/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
2
- simo/asgi.py,sha256=L8CUVZLM32IMzWDZ4IShdDN-m69t7oxAUeHods4-xNM,822
2
+ simo/asgi.py,sha256=625LsYcpGk0jKWUwZwjKNJx4btrzVCp6LThS2gga6Qo,852
3
3
  simo/auto_update.py,sha256=KfYosx6_V0VvtASBED29l7HAyAIzMyLSob9OmAjyYxQ,2026
4
4
  simo/celeryc.py,sha256=eab7_e9rw0c__DCeoUFUh_tjAGVlulxVrk75BaJf57Q,1512
5
5
  simo/cli.py,sha256=kB1dhZ30Pnq7mDawWGbX5WnCuoZ6qNMcnWH-c8XxcaU,2233
6
6
  simo/conf.py,sha256=H2BhXAV8MEDVXF8AbkaLSfR4ULd-9_bS4bnhE5sE5fg,112
7
+ simo/on_http_start.py,sha256=PJQlKYeZbtGCxRjDV6zcCqyA5Ns9d5NND30Tb6vIav4,2358
7
8
  simo/scripting.py,sha256=PVIkGsiMDWj4CNTbOM3rq7pJ6ruavuns-ZMU7VudLa4,923
8
9
  simo/settings.py,sha256=pMT2rdYKNpvXLxBFNWbqnRNkw8eVCW28u6DbI05AibM,6791
9
10
  simo/urls.py,sha256=cmegi8VcNCq22k9v2rUYWDEcr2_ND2YQFUvpzISK7y0,2553
10
11
  simo/__pycache__/__init__.cpython-38.pyc,sha256=j81de0BqHMr6bs0C7cuYrXl7HwtK_vv8hDEtAdSwDJc,153
11
- simo/__pycache__/asgi.cpython-38.pyc,sha256=bOdmI-G4socquYp4aZk2H-v5JSPcjKrGgPjEodme7NQ,845
12
+ simo/__pycache__/asgi.cpython-38.pyc,sha256=h60ZAnfczA1UpvJ1JofmQkC-DzmXSkFWOMyISyiABA8,880
12
13
  simo/__pycache__/celeryc.cpython-38.pyc,sha256=eSRoaKwfYlxVaxAiwqpQ2ndEcx7W-VpZtbxRFSV8UYg,1653
13
14
  simo/__pycache__/conf.cpython-38.pyc,sha256=MYP2yk3ULxiYwZsZR6tCLjKnU-z03A3avzQzIn66y3k,273
15
+ simo/__pycache__/on_http_start.cpython-38.pyc,sha256=QDNZHnJnLNmP3TSZ6oiySy2SbSgZ9LBs5m2KSwQPmAI,2067
14
16
  simo/__pycache__/settings.cpython-38.pyc,sha256=lyN4n0YWf5paUvhBHZwAeg2TB2iqCjSuc5uxSZjxZLM,6040
15
17
  simo/__pycache__/urls.cpython-38.pyc,sha256=_jjBuEJRAf7oXHOqt3MUElK2-uaYRL2jWK7l2StnUhQ,2210
16
18
  simo/_hub_template/hub/asgi.py,sha256=ElN_fdeSkf0Ysa7pS9rJVmZ1HmLhFxb8jFaMLqe1220,126
@@ -24,28 +26,27 @@ simo/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
26
  simo/core/admin.py,sha256=4l2XFoI9yUoOuQkyYdpwxt-PGj-VTVcMb-BiA_NHYok,16588
25
27
  simo/core/api.py,sha256=Hv-Dy9Lc8H1hZXlz4Uoqx7CNVFoHy6ZbiHs1xEIaNCA,19157
26
28
  simo/core/api_auth.py,sha256=2MRmCid_Fy67chKiJEt6_UaJZmb1PDF8KdbBlkA7YgU,1139
27
- simo/core/app_widgets.py,sha256=SP1txwI8_robAapIjalciS1cnJGoPH6udi6-k3fGC-g,1928
28
- simo/core/apps.py,sha256=CbkdIxKG5geo3ObOI4bkp55yA-3Yjs7RFk0Q3blrAqc,1046
29
+ simo/core/app_widgets.py,sha256=EEQOto3fGR0syDqpJE38tQrx8DoTTyg26nF5kYzHY38,2018
29
30
  simo/core/auto_urls.py,sha256=YZcvHqmAs4_azA_F_yz3CBsxrHAGijOPHib3SJCAoR0,1089
30
31
  simo/core/autocomplete_views.py,sha256=ygbVEOfyygWNy2BanB46Ov0NfWxBFAwwKp5CqAkrGMc,3849
31
32
  simo/core/base_types.py,sha256=OkRoAHp8P3r79k3IlUzkhycqlHDkeq7AAhOgsz7qGz8,552
32
33
  simo/core/context.py,sha256=xmGQ5h6HXw9-_XTuJSXyNiWDRHAVauqGCxmSJo0mxjE,1261
33
- simo/core/controllers.py,sha256=tJif3k3aJNFEks929BK99qZ1b_gSedrXDKhiYGUIT-M,22474
34
+ simo/core/controllers.py,sha256=-6WAduDDoXPoRk2za3LpSRUYrn-50X_kjgf1rMBJLFs,23493
34
35
  simo/core/dynamic_settings.py,sha256=U2WNL96JzVXdZh0EqMPWrxqO6BaRR2Eo5KTDqz7MC4o,1943
35
- simo/core/events.py,sha256=znn4xH9OCCUrYv_r3v16LqJ4YjJ0nsD1nBVWPKImzHA,4302
36
+ simo/core/events.py,sha256=gfew3bduIjiFusbnIoQg1LhOXRIfyLTilNMc0ROfpr8,4198
36
37
  simo/core/filters.py,sha256=ghtOZcrwNAkIyF5_G9Sn73NkiI71mXv0NhwCk4IyMIM,411
37
38
  simo/core/forms.py,sha256=s-Cjw--vibCmxk6IQ_4M5IQOMuE3Z8re75NFPPmi4O4,19735
38
39
  simo/core/gateways.py,sha256=MblN73zJC_6dBNJQel8KBblO4UvR-wLetea3FTrkWeE,3081
39
40
  simo/core/loggers.py,sha256=EBdq23gTQScVfQVH-xeP90-wII2DQFDjoROAW6ggUP4,1645
40
41
  simo/core/middleware.py,sha256=VKt9OBYU2o8BUjc5MESjt2vx_HixwprozmCRkAQDwZI,1252
41
- simo/core/models.py,sha256=HzpkUsmBwBIhFzvkIfMPlIX9wmkO-DGPNOC8H4ngPyw,18110
42
+ simo/core/models.py,sha256=pnvSB-8HkdL6FWHvIxdb0gd-XuA3oBamZ8D2EIxjC5A,18443
42
43
  simo/core/permissions.py,sha256=1GI59JZxl_sL9qzSt2uhoOnYnORDmcOJ8Fb37LbrhJE,576
43
44
  simo/core/routing.py,sha256=X1_IHxyA-_Q7hw1udDoviVP4_FSBDl8GYETTC2zWTbY,499
44
45
  simo/core/serializers.py,sha256=styX6kt-h4k3GNu31RWEYIJ7I-u6BGRLfC0zhrtYV-U,4181
45
46
  simo/core/signal_receivers.py,sha256=tyIljxTqvzBgYzcKDsSHeyFw9mKQiwA6NA1iJZEvL6I,2593
46
- simo/core/socket_consumers.py,sha256=5kUaUlmeKF3V6tZaV9MFaezRNMPHwGvdpx1XaA3eJFc,9789
47
+ simo/core/socket_consumers.py,sha256=y5I5BclO5i_pFGhh-oVxXSObVSMfvwQueQZfSZYSUTM,9827
47
48
  simo/core/storage.py,sha256=YlxmdRs-zhShWtFKgpJ0qp2NDBuIkJGYC1OJzqkbttQ,572
48
- simo/core/tasks.py,sha256=TSMI2jijUxXqQ9AwamLFc1wGFcR_qLw13kfW4kYf9II,10330
49
+ simo/core/tasks.py,sha256=vvjXZGHfDqZl2eQxYedNPTqSE5tUcTFSqoy6T6SJQI4,10688
49
50
  simo/core/todos.py,sha256=eYVXfLGiapkxKK57XuviSNe3WsUYyIWZ0hgQJk7ThKo,665
50
51
  simo/core/types.py,sha256=WJEq48mIbFi_5Alt4wxWMGXxNxUTXqfQU5koH7wqHHI,1108
51
52
  simo/core/views.py,sha256=Vr4eyLLIbxyPld-mOWH-hn0iqFfAbrK2jCHo_7nhvYE,5420
@@ -57,17 +58,16 @@ simo/core/__pycache__/admin.cpython-38.pyc,sha256=ZgDx7HuLwuV_9j6xEx090AU1-35jiA
57
58
  simo/core/__pycache__/api.cpython-37.pyc,sha256=srtJhR4q9LU9VmVxkTC2YgjBiqFvSWytHmQiX9W897c,1780
58
59
  simo/core/__pycache__/api.cpython-38.pyc,sha256=7oxM_bKV_uCAzcIasshtZYEOkzRbFPU4LNgsxS-DcUU,14480
59
60
  simo/core/__pycache__/api_auth.cpython-38.pyc,sha256=Df2nEIBP9BU4XB3NxlI335eIYOXjG6rjfLWQR-Jki7k,1627
60
- simo/core/__pycache__/app_widgets.cpython-38.pyc,sha256=4oI7IrXxPx2esGzEpAGikyJlDYYYtLaQK4yeBQHRqlA,3290
61
- simo/core/__pycache__/apps.cpython-38.pyc,sha256=p5MFXSBz2sEDRiuelD_hMIAzin2XSiJRLPbBPfJXr1w,1133
61
+ simo/core/__pycache__/app_widgets.cpython-38.pyc,sha256=o122Qpv-RrR61gsJVs29yNTVT7MmwxuqrqBHpFCPG_4,3465
62
62
  simo/core/__pycache__/auto_urls.cpython-38.pyc,sha256=LmdNUMSWfwNXzyoxG4Ki82B6tegLSHn1ethfnPSRR5Y,836
63
63
  simo/core/__pycache__/autocomplete_views.cpython-38.pyc,sha256=Rrrx6jwbhYDa9sU1eia9xBuTx-0H4F1aBsMvrK8cIz0,3861
64
64
  simo/core/__pycache__/base_types.cpython-38.pyc,sha256=LyaOjfDh0ZQNUbYNoJwOAyeBOy1qoEyBvdV0aYV8w7E,693
65
65
  simo/core/__pycache__/context.cpython-38.pyc,sha256=Dq_Z-4U7ppQhu8T-7kqu7LjT6DgbRI9bWbQ17rwX-Us,1184
66
66
  simo/core/__pycache__/controllers.cpython-37.pyc,sha256=18C2XXXvrzaipTVWRx_KFDHP5TmeWM-xnTnqVkzy8as,1663
67
- simo/core/__pycache__/controllers.cpython-38.pyc,sha256=d2EajsCcWnZQtV-58J8-JoQJ9em2xy7VU60NRXmqQJo,20659
67
+ simo/core/__pycache__/controllers.cpython-38.pyc,sha256=79kYGOmaJpZ8eqqcYfnmy-lKtmp0lQDXh8RG7Nf2y94,21360
68
68
  simo/core/__pycache__/dynamic_settings.cpython-38.pyc,sha256=P14nFp70tx6eCkyQN0ZxLaX21DiUc3TeZ4bs3h8XJgA,2476
69
69
  simo/core/__pycache__/events.cpython-37.pyc,sha256=E17-McBIoKrqwv1Qjh0WaVKYvcL-xzGRXvCg70uRUgs,3154
70
- simo/core/__pycache__/events.cpython-38.pyc,sha256=c0i1zGkHhY24ky8vRVZILMQezRJyOr3-misfVCZwfDM,5065
70
+ simo/core/__pycache__/events.cpython-38.pyc,sha256=5EJe1jSpBzqSuH_hCKAY7vsNIfeYX0KUtPaU-cIjX6w,4973
71
71
  simo/core/__pycache__/filters.cpython-38.pyc,sha256=d2yr0vEtbHsi5L4dysiRz4_m3Zz6ZMGV569mJTzB9Ro,713
72
72
  simo/core/__pycache__/forms.cpython-37.pyc,sha256=jTCg2uFkNumlWn5IEXay9_hL4wbAfZgmqZzhAaRBf6c,1784
73
73
  simo/core/__pycache__/forms.cpython-38.pyc,sha256=5N-umEqnAyIh5gYYLLLKAT-uWlDRFFTjvIDeOphFGe8,16394
@@ -76,16 +76,16 @@ simo/core/__pycache__/gateways.cpython-38.pyc,sha256=YyscgVM5eGIE7dL5GcaSco6NzsF
76
76
  simo/core/__pycache__/loggers.cpython-38.pyc,sha256=Z-cdQnC6XlIonPV4Sl4E52tP4NMEdPAiHK0cFaIL7I8,1623
77
77
  simo/core/__pycache__/middleware.cpython-38.pyc,sha256=vRjRp2hUrMLNvP2dExBVpP4T7wa6owWPCJbq5jPqY00,1480
78
78
  simo/core/__pycache__/models.cpython-37.pyc,sha256=kJ1wVtRlrDNAD6svKaiDTNRx7d4t5ect_hsDgpgzb0M,4240
79
- simo/core/__pycache__/models.cpython-38.pyc,sha256=JoFoaTq5mDvvImzSsVKscbEfaYYXsE6s5DDL7YjeNCM,15957
79
+ simo/core/__pycache__/models.cpython-38.pyc,sha256=VCiK_tFooJKrohW6Rmn1bZUySJLMFvHmEe5vYWL17iU,16139
80
80
  simo/core/__pycache__/permissions.cpython-38.pyc,sha256=BBstKtFelsJz8oTC3b3M2Z2XUi7cYFjnBaTuvcRraoo,850
81
81
  simo/core/__pycache__/routing.cpython-38.pyc,sha256=SnHuviYlYSp1pBON71-14yWa4jg5GDo8ksjfS5n1Gmk,591
82
82
  simo/core/__pycache__/scripts.cpython-37.pyc,sha256=aVttBA4V6xl6hwaFNbhIp45VbP0wId0vIC6iTvvw7iQ,613
83
83
  simo/core/__pycache__/serializers.cpython-37.pyc,sha256=SraS2BK9PTfrSXZGE7IeT6VvnuYDxrONwuDPba2Vcz8,3129
84
84
  simo/core/__pycache__/serializers.cpython-38.pyc,sha256=MhbEoC3bxpiqJc2iPmZRKnlclRdVWiwCLn6baoXNfXQ,5762
85
85
  simo/core/__pycache__/signal_receivers.cpython-38.pyc,sha256=BihR5Qnr9P72Xx4M9QWwCoxPa33ekDpLbQgS0kM0LC8,2321
86
- simo/core/__pycache__/socket_consumers.cpython-38.pyc,sha256=YqoBkpezPLw5GIfuqVz0uTqjV1QjBvRmWJkN7pngAls,8808
86
+ simo/core/__pycache__/socket_consumers.cpython-38.pyc,sha256=Xw_9Y0WcvO9vacbHA4uNHPW2dF2XcMG1nb0jiS8cRzs,8839
87
87
  simo/core/__pycache__/storage.cpython-38.pyc,sha256=BTkYH8QQyjqI0WOtJC8fHNtgu0YA1vjqZclXjC2vCVI,1116
88
- simo/core/__pycache__/tasks.cpython-38.pyc,sha256=BaRm0XSvTc4OBEiYQvC8ZnLFxxGTUQw_dS5Gmrdq8K8,7678
88
+ simo/core/__pycache__/tasks.cpython-38.pyc,sha256=vtkxATYpSP6hB0zh5hJykUM5HYS4MAHb1jN0P-t3imE,8048
89
89
  simo/core/__pycache__/todos.cpython-38.pyc,sha256=CkHQ6v8gmynr83KQsJY6VPR3OnLZnHvVCX5R1Dr8J9Q,245
90
90
  simo/core/__pycache__/views.cpython-38.pyc,sha256=I75ChNeoySYr2uXV0PScwpVWsOoPpyzP9cHKkTJfWAc,3825
91
91
  simo/core/__pycache__/widgets.cpython-37.pyc,sha256=myov4OZi5dkR2dYs2hBAVgMIgOTJT5jdLiM93PI8gvw,688
@@ -97,11 +97,11 @@ simo/core/db_backend/__pycache__/base.cpython-38.pyc,sha256=e4S8FC9O5fNJPHO2H9M6
97
97
  simo/core/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
98
98
  simo/core/management/__pycache__/__init__.cpython-38.pyc,sha256=Fh8tziI4OtqmaJ_b1lAWh91jNunBUvA5mEbOnV6Vy9o,154
99
99
  simo/core/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
100
- simo/core/management/commands/gateways_manager.py,sha256=jDGpLnuGyFzm9Iq-eYFSSd_PhWp_JLjJRwd-DjKktKs,6967
100
+ simo/core/management/commands/gateways_manager.py,sha256=oogLAywD6-ohTHVA8RzZqQBcPvvf6BjWcnZK8kzXqJY,6974
101
101
  simo/core/management/commands/run_gateway.py,sha256=bp0FQQoBeOSoxjHCCMicDL1fxPZZGyLgnq2QKht3bJo,645
102
102
  simo/core/management/commands/update.py,sha256=Y2_6EL8E757nr-MjSuIpdSsEItI0yN42DT5P1e1zkno,175
103
103
  simo/core/management/commands/__pycache__/__init__.cpython-38.pyc,sha256=xFGh3qGeMqHMcdTExaynbV7hr8jwuEa8nuSxsQIvCD4,163
104
- simo/core/management/commands/__pycache__/gateways_manager.cpython-38.pyc,sha256=mHK92ZQkiBRYZ81tW5VG68Tfc1HO7yNiqX4efd4S7r8,6733
104
+ simo/core/management/commands/__pycache__/gateways_manager.cpython-38.pyc,sha256=LV5RXd7eOKYwxvQ4cFleojEJqls-qe9Lt0qsWzX9zUw,6035
105
105
  simo/core/management/commands/__pycache__/run_gateway.cpython-38.pyc,sha256=ojjiJmq5VYsongyLSLBYgudtRfnksTkKCR7NP4B-EGM,1059
106
106
  simo/core/migrations/0001_initial.py,sha256=0Uy7IqJxQQYlurs8Mw_RJy7NaWS7BU0VFmZBBz8YkQI,9220
107
107
  simo/core/migrations/0002_load_icons.py,sha256=s9TtGo5NWEyWV3BspfbDNAlWqmQWxmDj7GjEaJXruFk,2044
@@ -127,6 +127,7 @@ simo/core/migrations/0021_auto_20231020_1041.py,sha256=OmXoVeWfb8HPvGlP0iC1ZYqBy
127
127
  simo/core/migrations/0022_auto_20231221_0735.py,sha256=XR4rfCHy6_oZkybvzcba_Oxx8HST-vSCKy8pPVxX8cs,23525
128
128
  simo/core/migrations/0023_auto_20231229_1352.py,sha256=TuCDRtgmFDLW7e1oIs_3Ndxn-ub2rV87VAzZ_1U0bwQ,1267
129
129
  simo/core/migrations/0024_alter_instance_device_report_history_days.py,sha256=CqCWl3GQNeFnNMr0hfdlQG9LcXbN9dILqxw0peHVihs,530
130
+ simo/core/migrations/0025_auto_20240122_1321.py,sha256=ocRAPsO2QRUBn8tOwtXNcc82fA0KHLsclDy2Nz5X-ds,1111
130
131
  simo/core/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
131
132
  simo/core/migrations/__pycache__/0001_initial.cpython-38.pyc,sha256=ftWpf_RgprbqNo_syjYdeblvFH5nIEc87XEpdi1INHo,5373
132
133
  simo/core/migrations/__pycache__/0002_load_icons.cpython-38.pyc,sha256=gYV4sH0cXffvNtVyFgRQQ1A4I5DeIRnZX9eUrTpEhlk,2115
@@ -152,6 +153,7 @@ simo/core/migrations/__pycache__/0021_auto_20231020_1041.cpython-38.pyc,sha256=p
152
153
  simo/core/migrations/__pycache__/0022_auto_20231221_0735.cpython-38.pyc,sha256=RvnYOHFuY8siQ69Fed3oHoXbiF17Vr54YXRYyJFwp-4,16477
153
154
  simo/core/migrations/__pycache__/0023_auto_20231229_1352.cpython-38.pyc,sha256=NYA0kVuQGWjz2epoLH0XG7FgyaC6JXW28J18bjRAqCk,1281
154
155
  simo/core/migrations/__pycache__/0024_alter_instance_device_report_history_days.cpython-38.pyc,sha256=ALrhitBQRd-y9Ragwa0_ZuJ9DDkUIgw72A0CZpWznmE,777
156
+ simo/core/migrations/__pycache__/0025_auto_20240122_1321.cpython-38.pyc,sha256=u6tcFJClTrrgPrYhtKPea9IIwG4O2yWjjoUCR7_dQ60,1090
155
157
  simo/core/migrations/__pycache__/__init__.cpython-38.pyc,sha256=a0yc0qWaV-D5f64GdcpP54haEDBzwmAWRGyErHeHztU,161
156
158
  simo/core/static/ansi_styles.css,sha256=4ieJGrjZPKyPSago9FdB_gflHoGE1vxCHi8qVn5tY-Y,37352
157
159
  simo/core/static/admin/Img/plus.svg,sha256=2NpSFPWqGIjpAQGFI7LDQHPKagEhYkJiJX95ufCoZaI,741
@@ -10058,8 +10060,9 @@ simo/core/templates/admin/zone_table_row.html,sha256=4XCgDsnrvn6s9ac0_e74pPsjDFd
10058
10060
  simo/core/templates/admin/controller_widgets/binary_sensor.html,sha256=PTwoLyppBIH8VL3-PKXCT4SWFQOeWqrzfsriWyROnzU,277
10059
10061
  simo/core/templates/admin/controller_widgets/generic.html,sha256=4TNLI0ri2qpuXNy4IIi4CTN4L98JtmERE0D16a4wCNE,163
10060
10062
  simo/core/templates/admin/controller_widgets/knob.html,sha256=ukTJoQBZfly0gLUx0vJdqHbnggaiDV6jS5tM8Jd8JuA,221
10063
+ simo/core/templates/admin/controller_widgets/lock.html,sha256=JyIB5CIaXBb4cKLSg53lWBRjNIeTxVaHBJTuOI4nYkQ,363
10061
10064
  simo/core/templates/admin/controller_widgets/rgb.html,sha256=3xRjo_d1jALiJabWYk5kTLhdwhylzOA-iwyDNfSKlgw,224
10062
- simo/core/templates/admin/controller_widgets/switch.html,sha256=pCXJ7k6TFbeSU_2rBSc50cqtRpLmCoyb8ON37T1tcIM,358
10065
+ simo/core/templates/admin/controller_widgets/switch.html,sha256=fyNEZgxO0dY61QVSrnsSYt7-JsS9RQTGkPXaeMyNNWM,351
10063
10066
  simo/core/templates/admin/core/icon_preview.html,sha256=D1FS0PwKRopFQgy4NlUPtQp7n78opB6XMRVf2CAAtaM,605
10064
10067
  simo/core/templates/admin/gateway_control/widget.html,sha256=hJ8oL0wZjhwQXiVDCXVAqb-nJQUkSYPtfKoaAUl0Hwc,163
10065
10068
  simo/core/templates/admin/gateway_control/widget_internals.html,sha256=6UsVKQtHWM2W29Rf1SLiLdNdkxL6P_WGZUKhHzcw48k,1262
@@ -10090,7 +10093,6 @@ simo/core/utils/logs.py,sha256=JBUHhnm9Cn81csrQ4xHbujBFRL9YWulMwUj_zJPNvyw,1057
10090
10093
  simo/core/utils/mixins.py,sha256=X6kUPKAi_F-uw7tgm8LEaYalBXpvDA-yrLNFCGr2rks,259
10091
10094
  simo/core/utils/model_helpers.py,sha256=3IzJeOvBoYdUJVXCJkY20npOZXPjNPAiEFvuT0OPhwA,884
10092
10095
  simo/core/utils/relay.py,sha256=i1xy_nPTgY5Xn0l2W4lNI3xeVUpDQTUUfV3M8h2DeBg,457
10093
- simo/core/utils/serialization.py,sha256=acx2a26rwsr5-Tb2oXhVk7EHRsrNn12SuiW273M3JzA,625
10094
10096
  simo/core/utils/type_constants.py,sha256=y0YnRa7txooMZUVf1IuFUTXPwLaHo_UToKIChJH5Vgk,3544
10095
10097
  simo/core/utils/validators.py,sha256=PGqW9E8tA6I68RiZg_38DS_MsZs1ew_6vQY4YRKhBdU,772
10096
10098
  simo/core/utils/__pycache__/__init__.cpython-38.pyc,sha256=fm7tTfba3f_RZC2Q7UCRztAw55rK-ZCVYh0QHl8vvj8,149
@@ -10106,7 +10108,6 @@ simo/core/utils/__pycache__/logs.cpython-38.pyc,sha256=t2jOYAU8R58Jw4I7SXZ0M8Klx
10106
10108
  simo/core/utils/__pycache__/mixins.cpython-38.pyc,sha256=PSdlUHeaURWWy6lQPROvhqVAQ397LKvyVWMTp6RPJ5U,600
10107
10109
  simo/core/utils/__pycache__/model_helpers.cpython-38.pyc,sha256=fHAFterjLpjaLGMCkDpCYeCpAjhgNkI5kDBINkBupf4,1343
10108
10110
  simo/core/utils/__pycache__/relay.cpython-38.pyc,sha256=J7k-psm64REAy3VpJ7dk9HDIoxl3Vj-u2sjL4Xd5P40,689
10109
- simo/core/utils/__pycache__/serialization.cpython-38.pyc,sha256=hgXTkR2FavMlAeyaFdaGYeCMG2ZSYKddps_H6YDmSEw,686
10110
10111
  simo/core/utils/__pycache__/type_constants.cpython-38.pyc,sha256=qg39w7UCcUET_Tt9md0wLVExeLTESwQn0FY5LDdMsVk,3008
10111
10112
  simo/core/utils/__pycache__/validators.cpython-38.pyc,sha256=VyUnCgzm4LXCmPqVKfwfLI1uF7KUtrhu-pjMI0zjUok,841
10112
10113
  simo/fleet/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -10191,9 +10192,9 @@ simo/fleet/migrations/__pycache__/__init__.cpython-38.pyc,sha256=5k1KW0jeSDzw6Rn
10191
10192
  simo/generic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10192
10193
  simo/generic/app_widgets.py,sha256=E_pnpA1hxMIhenRCrHoQ5cik06jm2BAHCkl_eo-OudU,1264
10193
10194
  simo/generic/base_types.py,sha256=djymox_boXTHX1BTTCLXrCH7ED-uAsV_idhaDOc3OLI,409
10194
- simo/generic/controllers.py,sha256=L1NI6qyif8mi5IGfoZvqwAKKelplNHE9-JAALyAJcAE,54452
10195
+ simo/generic/controllers.py,sha256=nD3w4X5Lt2wd2n6F9qJV3eAXqIaB5Fux5qfqB3DKtng,54307
10195
10196
  simo/generic/forms.py,sha256=zF-QYfu6C0TZ7sJ5_-VkhUweV0_J8gg0CC07At6RIK8,19829
10196
- simo/generic/gateways.py,sha256=vuEcyhjcHfSt9eT2vaAeZwBtmEDCmp7edk8FNOMFQy8,12065
10197
+ simo/generic/gateways.py,sha256=A8jno143joAQSlbBLQ44SR0-yvYyoJhoSgdOmGGnUvU,11907
10197
10198
  simo/generic/models.py,sha256=XG8VnLSpMSiBnbKwvMknF9J9kdbmBolXvJ9L3Giz8pQ,3330
10198
10199
  simo/generic/routing.py,sha256=elQVZmgnPiieEuti4sJ7zITk1hlRxpgbotcutJJgC60,228
10199
10200
  simo/generic/socket_consumers.py,sha256=NfTQGYtVAc864IoogZRxf_0xpDPM0eMCWn0SlKA5P7Y,1751
@@ -10203,9 +10204,9 @@ simo/generic/__pycache__/__init__.cpython-38.pyc,sha256=MCjhrSijserYi14I03LlOwRU
10203
10204
  simo/generic/__pycache__/app_widgets.cpython-38.pyc,sha256=QGPG6EkuNwk5PhFtNFIYfbJVrXiA76fBZlu31lwuvdY,2366
10204
10205
  simo/generic/__pycache__/base_types.cpython-38.pyc,sha256=ePcCgG0U4mrc_I3MLs2cnUh5KPX-Kf13jTy_d5pTP78,547
10205
10206
  simo/generic/__pycache__/controllers.cpython-37.pyc,sha256=5cET2P5vw-ujjYhyyLOoski91fXqiuV4zZdhRKgb028,2461
10206
- simo/generic/__pycache__/controllers.cpython-38.pyc,sha256=m034izyxaMTlvV5nOVWQ1-zG8ROyPv9c2podF32kEE8,35257
10207
+ simo/generic/__pycache__/controllers.cpython-38.pyc,sha256=nf4yQTt-E9dPVNLHuM-PGeuhTutrTNQCNGRs46BmT2A,35232
10207
10208
  simo/generic/__pycache__/forms.cpython-38.pyc,sha256=fhJCmt60aYabvuylL4ZADGuIcI7YA62iJVFav9zZ_QU,15457
10208
- simo/generic/__pycache__/gateways.cpython-38.pyc,sha256=B567hKZWsfNN05obi9vNr0qplzQVr0skwaE0fKZYIdI,9339
10209
+ simo/generic/__pycache__/gateways.cpython-38.pyc,sha256=Wj69nJQ6ea7KahupRWTr3SEvq0OokTIf9SOJZ67BKS4,9218
10209
10210
  simo/generic/__pycache__/models.cpython-38.pyc,sha256=8upnlvxpuYsg16gghJg9JZCcuyatsrWuYzDYT4psaC0,3035
10210
10211
  simo/generic/__pycache__/routing.cpython-38.pyc,sha256=1-1r3oasl1nWP36UvPcqb-mSYDPOtiuO3HAMRSpS4b8,374
10211
10212
  simo/generic/__pycache__/socket_consumers.cpython-38.pyc,sha256=KVv0940VOj7I2vIJySrBOeHelyxn12e8otavCFRtSO4,1890
@@ -10285,12 +10286,11 @@ simo/notifications/migrations/__pycache__/__init__.cpython-38.pyc,sha256=wXGUV-i
10285
10286
  simo/users/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10286
10287
  simo/users/admin.py,sha256=XpUrz1zIVLuK3Y9MF7kCJBExHpMEMkU3iNZG1YZdxO0,6560
10287
10288
  simo/users/api.py,sha256=_1Z8m4pU-9LAbLfSdZ6qXLj0amgulVPqmLX2kRGJWF4,7886
10288
- simo/users/apps.py,sha256=rf_6OOpkiwTrOTY8pIsozrVUlx-0wpOheLEPj0lIYsU,1728
10289
10289
  simo/users/auth_backends.py,sha256=I5pnaTa20-Lxfw_dFG8471xDITb0_fQl1PVhJalp5vU,3992
10290
10290
  simo/users/auto_urls.py,sha256=ee0d6fWABkJY5mQOTC8KhrqDI6d8xZkYZ4ja-gZJ-rw,269
10291
10291
  simo/users/dynamic_settings.py,sha256=sEIsi4yJw3kH46Jq_aOkSuK7QTfQACGUE-lkyBogCaM,570
10292
10292
  simo/users/middleware.py,sha256=9epN8xDcnYAMoEjAeJGg4W9e54szTgh48LKz3rlywFI,1287
10293
- simo/users/models.py,sha256=kSBRc7Y-P15q6LwJJLOVhtc5z7tooaFvNFWvyUtPrtU,18121
10293
+ simo/users/models.py,sha256=HyN4Euh4j4dQqToR0LOBLHphztHGdQgDN41lSLarlRU,18552
10294
10294
  simo/users/permissions.py,sha256=IwtYS8yQdupWbYKR9VimSRDV3qCJ2jXP57Lyjpb2EQM,242
10295
10295
  simo/users/serializers.py,sha256=Mh2pWmsKDp7CNIxK2OTw3CfVkteZHiVoXWNF1fluvX8,2161
10296
10296
  simo/users/sso_urls.py,sha256=pcb_GhYHRtmairxJhMXE1bdcTma0BcYfKU3nCRtHQMQ,244
@@ -10301,12 +10301,11 @@ simo/users/views.py,sha256=dOQVvmlHG7ihWKJLFUBcqKOA0UDctlMKR0pTc36JZqg,3487
10301
10301
  simo/users/__pycache__/__init__.cpython-38.pyc,sha256=9otuYxq331c4lGy0DR8pigaPpzq0lQ4nrNLhlYiFAF0,159
10302
10302
  simo/users/__pycache__/admin.cpython-38.pyc,sha256=ggaIuUdEjvYZ6Y5msOlhP_LmYnqUQpkJeS6FuZr6ass,7274
10303
10303
  simo/users/__pycache__/api.cpython-38.pyc,sha256=W_VuG6hotcDA2iIsbO1p01ouqcWIl7C3JZbAk4Wp3l8,6676
10304
- simo/users/__pycache__/apps.cpython-38.pyc,sha256=E14VsKsNu8V3dPk089YREOzWUB_1xGLgprEbpMh2kMw,1694
10305
10304
  simo/users/__pycache__/auth_backends.cpython-38.pyc,sha256=MuOieBIXt6lrDx83-UQtdDyI_U8kE3pU9XR4yFLKBnE,3007
10306
10305
  simo/users/__pycache__/auto_urls.cpython-38.pyc,sha256=q8SFA5EN-tpypBzfiAsorpWdXlrC_kt4HgMCZS9bcoI,431
10307
10306
  simo/users/__pycache__/dynamic_settings.cpython-38.pyc,sha256=6F8JBjZkHykySnmZjNEzjS0ijbmPdcp9yUAZ5kqq_Fo,864
10308
10307
  simo/users/__pycache__/middleware.cpython-38.pyc,sha256=U2ENmFDCHK4qtf2Q6xLqe-VNmM6dN-sUb6q70epSgZs,1165
10309
- simo/users/__pycache__/models.cpython-38.pyc,sha256=PVjKOiTNQAT4cV6L0_U3_kJAV2B9YZJKCjtL2jnPTaw,17102
10308
+ simo/users/__pycache__/models.cpython-38.pyc,sha256=KH1jIoLIZgG22dYuuZqnVCSsnPfe4D74k76gGQdw9jk,17498
10310
10309
  simo/users/__pycache__/permissions.cpython-38.pyc,sha256=ez5NxoL_JUeeH6GsKhvFreuA3FCBgGf9floSypdXUtM,633
10311
10310
  simo/users/__pycache__/serializers.cpython-38.pyc,sha256=rCAdmyL95NkChqspE-FmAtJ6vAWjdJnPa2jrd8e6hUo,2868
10312
10311
  simo/users/__pycache__/sso_urls.cpython-38.pyc,sha256=9TkKOWZugSPhwModg5yJn2IE3YgvMQA8Vm3WGznoOuk,403
@@ -10337,6 +10336,7 @@ simo/users/migrations/0020_rename_is_god_user_is_master.py,sha256=opZkM36xt6sKuJ
10337
10336
  simo/users/migrations/0021_alter_permissionsrole_instance.py,sha256=Taw2fdHy6U1PlThC3QIWaL_ADbx7gchWsJcGxoMMElo,630
10338
10337
  simo/users/migrations/0022_userdevicereportlog_instance.py,sha256=h0ue6uMcIyVs2o58Aj0PyjGLPvCCfS28HfPIiIu_VLc,549
10339
10338
  simo/users/migrations/0023_auto_20240105_0719.py,sha256=OAkWJusXjzT6dx4EgwjvNvMEgrP_zvcG8zgi774pYG0,700
10339
+ simo/users/migrations/0024_fingerprint.py,sha256=0wfplJ3iHv_6heJx7yIQYX3D68Nf9pLPlIZoM5NcPk8,1021
10340
10340
  simo/users/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10341
10341
  simo/users/migrations/__pycache__/0001_initial.cpython-38.pyc,sha256=ngXA1QR-Qc2VS-BTTZWybVXiEfifIgKaVS6bADiN8nU,4269
10342
10342
  simo/users/migrations/__pycache__/0002_componentpermission.cpython-38.pyc,sha256=pknJnpic8p6Vdx9DX41FfODXNnvexDswJtUCmC5w1tg,995
@@ -10361,6 +10361,7 @@ simo/users/migrations/__pycache__/0020_rename_is_god_user_is_master.cpython-38.p
10361
10361
  simo/users/migrations/__pycache__/0021_alter_permissionsrole_instance.cpython-38.pyc,sha256=aC1h7lz5JZbpZU55X9GdW_XJ_JixSxh0_I7TCheO_rM,863
10362
10362
  simo/users/migrations/__pycache__/0022_userdevicereportlog_instance.cpython-38.pyc,sha256=xGtWss6Blm02eNE-sBYQxwbRydqii2YcSGJNh1zseEw,785
10363
10363
  simo/users/migrations/__pycache__/0023_auto_20240105_0719.cpython-38.pyc,sha256=VOc5g9AlyhnB3QhU6GmOw6RPWUh4WhtLIOypCYb4jXY,739
10364
+ simo/users/migrations/__pycache__/0024_fingerprint.cpython-38.pyc,sha256=y7usfd4nuqXG2ZKq2hEVPq8ukEeszGpLfNFOzeLX9yU,1132
10364
10365
  simo/users/migrations/__pycache__/__init__.cpython-38.pyc,sha256=NKq7WLgktK8WV1oOqCPbAbdkrPV5GRGhYx4VxxI4dcs,170
10365
10366
  simo/users/templates/conf/mosquitto.conf,sha256=1eIGNuRu4Y3hfAU6qiWix648eCRrw0oOT24PnyFI4ys,189
10366
10367
  simo/users/templates/conf/mosquitto_acls.conf,sha256=ga44caTDNQE0CBKw55iM2jOuna6-9fKGwAhjyERZdRE,500
@@ -10370,8 +10371,8 @@ simo/users/templates/invitations/expired_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCe
10370
10371
  simo/users/templates/invitations/expired_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10371
10372
  simo/users/templates/invitations/taken_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10372
10373
  simo/users/templates/invitations/taken_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10373
- simo-1.7.15.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10374
- simo-1.7.15.dist-info/METADATA,sha256=RTyA75AFdpXYt3prknBHXZbC7rehwQSNjRalAcOcpJw,1749
10375
- simo-1.7.15.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
10376
- simo-1.7.15.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10377
- simo-1.7.15.dist-info/RECORD,,
10374
+ simo-1.7.17.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10375
+ simo-1.7.17.dist-info/METADATA,sha256=pzt1U0wzNjr7EQHueQkcGXm1ZLAQ_0gDysSogCWA7pQ,1720
10376
+ simo-1.7.17.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
10377
+ simo-1.7.17.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10378
+ simo-1.7.17.dist-info/RECORD,,
Binary file
simo/core/apps.py DELETED
@@ -1,28 +0,0 @@
1
- import os
2
- import subprocess
3
- from django.conf import settings
4
- from django.template.loader import render_to_string
5
- from django.apps import AppConfig
6
-
7
-
8
- class CoreAppConfig(AppConfig):
9
- name = 'simo.core'
10
-
11
- def ready(self):
12
- import simo
13
- auto_update_file_path = os.path.join(
14
- os.path.dirname(simo.__file__), 'auto_update.py'
15
- )
16
- st = os.stat(auto_update_file_path)
17
- os.chmod(auto_update_file_path, st.st_mode | 0o111)
18
-
19
- executable_path = '/usr/local/bin/simo-auto-update'
20
- if os.geteuid() == 0:
21
- # We are running as root!
22
- if not os.path.islink(executable_path):
23
- # There is no symbolic link yet made for auto updates.
24
- # Let's make it!
25
- os.symlink(auto_update_file_path, executable_path)
26
- auto_update_cron = f'0 * * * * {executable_path} \n'
27
- cron_out = subprocess.Popen(['crontab', '-'], stdin=subprocess.PIPE)
28
- cron_out.communicate(input=str.encode(auto_update_cron))
@@ -1,29 +0,0 @@
1
- import datetime
2
- from collections.abc import Iterable
3
- from decimal import Decimal as D
4
-
5
-
6
- def normalize(data):
7
-
8
- if isinstance(data, dict):
9
- result = {}
10
- for key, val in data.items():
11
- result[key] = normalize(val)
12
- return result
13
-
14
- if isinstance(data, Iterable):
15
- result = []
16
- for item in data:
17
- result.append(normalize(item))
18
- return result
19
-
20
- if isinstance(data, D):
21
- return float(data)
22
-
23
- if isinstance(data, datetime.datetime):
24
- return data.timestamp()
25
-
26
- if type(data) in (bool, int, float):
27
- return data
28
-
29
- return str(data)
Binary file
simo/users/apps.py DELETED
@@ -1,56 +0,0 @@
1
- import os
2
- import pwd
3
- import grp
4
- import subprocess
5
- from django.conf import settings
6
- from django.template.loader import render_to_string
7
- from django.apps import AppConfig
8
-
9
-
10
- class CoreAppConfig(AppConfig):
11
- name = 'simo.users'
12
-
13
- def ready(self):
14
- if os.geteuid() != 0:
15
- return
16
-
17
- from .models import User
18
- users_file = '/etc/mosquitto/mosquitto_users'
19
- if not os.path.exists(users_file):
20
- with open(users_file, 'w') as f:
21
- f.write('')
22
-
23
- uid = pwd.getpwnam("mosquitto").pw_uid
24
- gid = grp.getgrnam("mosquitto").gr_gid
25
- os.chown(users_file, uid, gid)
26
- os.chmod(users_file, 0o640)
27
-
28
- acls_file = '/etc/mosquitto/acls.conf'
29
- with open(acls_file, 'w') as f:
30
- f.write('')
31
-
32
- uid = pwd.getpwnam("mosquitto").pw_uid
33
- gid = grp.getgrnam("mosquitto").gr_gid
34
- os.chown(acls_file, uid, gid)
35
- os.chmod(acls_file, 0o640)
36
-
37
-
38
- ps = subprocess.Popen(
39
- ['mosquitto_passwd /etc/mosquitto/mosquitto_users root'],
40
- shell=True, stdin=subprocess.PIPE,stdout=subprocess.PIPE
41
- )
42
- ps.communicate(f"{settings.SECRET_KEY}\n{settings.SECRET_KEY}".encode())
43
-
44
- for user in User.objects.all():
45
- user.update_mqtt_secret(reload=False)
46
-
47
- from .utils import update_mqtt_acls
48
- update_mqtt_acls()
49
-
50
- if not os.path.exists('/etc/mosquitto/conf.d/simo.conf'):
51
- with open('/etc/mosquitto/conf.d/simo.conf', 'w') as f:
52
- f.write(render_to_string('conf/mosquitto.conf'))
53
-
54
- subprocess.run(
55
- ['service', 'mosquitto', 'reload'], stdout=subprocess.PIPE
56
- )
File without changes