simo 2.7.10__py3-none-any.whl → 2.7.11__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 (51) hide show
  1. simo/automation/__pycache__/gateways.cpython-38.pyc +0 -0
  2. simo/automation/__pycache__/helpers.cpython-38.pyc +0 -0
  3. simo/automation/__pycache__/models.cpython-38.pyc +0 -0
  4. simo/automation/__pycache__/serializers.cpython-38.pyc +0 -0
  5. simo/automation/gateways.py +45 -25
  6. simo/core/__pycache__/admin.cpython-38.pyc +0 -0
  7. simo/core/__pycache__/api_meta.cpython-38.pyc +0 -0
  8. simo/core/__pycache__/form_fields.cpython-38.pyc +0 -0
  9. simo/core/__pycache__/forms.cpython-38.pyc +0 -0
  10. simo/core/__pycache__/models.cpython-38.pyc +0 -0
  11. simo/core/__pycache__/serializers.cpython-38.pyc +0 -0
  12. simo/core/__pycache__/socket_consumers.cpython-38.pyc +0 -0
  13. simo/core/api_meta.py +3 -1
  14. simo/core/form_fields.py +7 -1
  15. simo/core/forms.py +6 -3
  16. simo/core/migrations/0047_alter_component_value_translation.py +18 -0
  17. simo/core/migrations/__pycache__/0047_alter_component_value_translation.cpython-38.pyc +0 -0
  18. simo/core/serializers.py +6 -1
  19. simo/core/templates/admin/wizard/wizard_add.html +1 -1
  20. simo/fleet/__pycache__/forms.cpython-38.pyc +0 -0
  21. simo/fleet/__pycache__/socket_consumers.cpython-38.pyc +0 -0
  22. simo/fleet/__pycache__/views.cpython-38.pyc +0 -0
  23. simo/fleet/migrations/__pycache__/0044_auto_20241210_0707.cpython-38.pyc +0 -0
  24. simo/generic/__pycache__/controllers.cpython-38.pyc +0 -0
  25. simo/generic/__pycache__/forms.cpython-38.pyc +0 -0
  26. simo/generic/__pycache__/gateways.cpython-38.pyc +0 -0
  27. simo/generic/__pycache__/socket_consumers.cpython-38.pyc +0 -0
  28. simo/generic/controllers.py +10 -1
  29. simo/generic/forms.py +36 -0
  30. simo/generic/gateways.py +45 -3
  31. simo/multimedia/__pycache__/admin.cpython-38.pyc +0 -0
  32. simo/multimedia/__pycache__/auto_urls.cpython-38.pyc +0 -0
  33. simo/multimedia/__pycache__/controllers.cpython-38.pyc +0 -0
  34. simo/multimedia/__pycache__/models.cpython-38.pyc +0 -0
  35. simo/multimedia/__pycache__/views.cpython-38.pyc +0 -0
  36. simo/multimedia/admin.py +4 -4
  37. simo/multimedia/auto_urls.py +10 -0
  38. simo/multimedia/controllers.py +9 -1
  39. simo/multimedia/migrations/0005_remove_sound_slug_sound_date_uploaded.py +24 -0
  40. simo/multimedia/migrations/__pycache__/0005_remove_sound_slug_sound_date_uploaded.cpython-38.pyc +0 -0
  41. simo/multimedia/models.py +16 -2
  42. simo/multimedia/views.py +19 -0
  43. simo/notifications/__pycache__/utils.cpython-38.pyc +0 -0
  44. simo/users/__pycache__/models.cpython-38.pyc +0 -0
  45. {simo-2.7.10.dist-info → simo-2.7.11.dist-info}/METADATA +1 -1
  46. {simo-2.7.10.dist-info → simo-2.7.11.dist-info}/RECORD +50 -42
  47. simo/multimedia/requirements.txt +0 -2
  48. {simo-2.7.10.dist-info → simo-2.7.11.dist-info}/LICENSE.md +0 -0
  49. {simo-2.7.10.dist-info → simo-2.7.11.dist-info}/WHEEL +0 -0
  50. {simo-2.7.10.dist-info → simo-2.7.11.dist-info}/entry_points.txt +0 -0
  51. {simo-2.7.10.dist-info → simo-2.7.11.dist-info}/top_level.txt +0 -0
@@ -33,9 +33,12 @@ class ScriptRunHandler(multiprocessing.Process):
33
33
  component = None
34
34
  logger = None
35
35
 
36
- def __init__(self, component_id, *args, **kwargs):
36
+ def __init__(self, component_id, exit_event, *args, **kwargs):
37
37
  super().__init__(*args, **kwargs)
38
38
  self.component_id = component_id
39
+ self.exit_event = exit_event
40
+ self.exit_in_use = multiprocessing.Event()
41
+ self.exin_in_use_fail = multiprocessing.Event()
39
42
 
40
43
  def run(self):
41
44
  db_connection.connect()
@@ -51,34 +54,40 @@ class ScriptRunHandler(multiprocessing.Process):
51
54
  sys.stderr = StreamToLogger(self.logger, logging.ERROR)
52
55
  self.component.meta['pid'] = os.getpid()
53
56
  self.component.set('running')
54
-
55
- if hasattr(self.component.controller, '_run'):
56
- def run_code():
57
- self.component.controller._run()
58
- else:
59
- code = self.component.config.get('code')
60
- def run_code():
61
- start = time.time()
62
- exec(code, globals())
63
- if 'class Automation:' in code and time.time() - start < 1:
64
- Automation().run()
65
-
66
- if not code:
67
- self.component.value = 'finished'
68
- self.component.save(update_fields=['value'])
69
- return
70
57
  print("------START-------")
71
58
  try:
72
- run_code()
59
+ self.run_code()
73
60
  except:
74
61
  print("------ERROR------")
75
62
  self.component.set('error')
76
63
  raise
77
64
  else:
78
- print("------FINISH-----")
79
- self.component.set('finished')
65
+ if not self.exit_event.is_set():
66
+ print("------FINISH-----")
67
+ self.component.set('finished')
80
68
  return
81
69
 
70
+ def run_code(self):
71
+ if hasattr(self.component.controller, '_run'):
72
+ self.component.controller._run()
73
+ else:
74
+ code = self.component.config.get('code')
75
+ if not code:
76
+ self.component.value = 'finished'
77
+ self.component.save(update_fields=['value'])
78
+ return
79
+ start = time.time()
80
+ namespace = {}
81
+ exec(code, namespace)
82
+ if 'Automation' in namespace and time.time() - start < 1:
83
+ self.exit_in_use.set()
84
+ try:
85
+ namespace['Automation']().run(self.exit_event)
86
+ except:
87
+ self.exin_in_use_fail.set()
88
+ namespace['Automation']().run()
89
+
90
+
82
91
 
83
92
  class GatesHandler:
84
93
  '''
@@ -225,6 +234,9 @@ class AutomationsGatewayHandler(GatesHandler, BaseObjectCommandsGatewayHandler):
225
234
  dead_scripts = False
226
235
  for id, process in list(self.running_scripts.items()):
227
236
  comp = Component.objects.filter(id=id).first()
237
+ if comp.value == 'finished':
238
+ self.running_scripts.pop(id)
239
+ continue
228
240
  if process.is_alive():
229
241
  if not comp and id not in self.terminating_scripts:
230
242
  # script is deleted, or instance deactivated
@@ -236,7 +248,7 @@ class AutomationsGatewayHandler(GatesHandler, BaseObjectCommandsGatewayHandler):
236
248
  if comp:
237
249
  logger = get_component_logger(comp)
238
250
  logger.log(logging.INFO, "-------DEAD!-------")
239
- self.stop_script(comp, 'error')
251
+ self.stop_script(comp, 'dead')
240
252
 
241
253
  if dead_scripts:
242
254
  # give 10s air before we wake these dead scripts up!
@@ -326,7 +338,9 @@ class AutomationsGatewayHandler(GatesHandler, BaseObjectCommandsGatewayHandler):
326
338
  def start_script(self, component):
327
339
  print("START SCRIPT %s" % str(component))
328
340
  if component.id in self.running_scripts:
329
- if component.id not in self.terminating_scripts:
341
+ if component.value == 'finished':
342
+ self.running_scripts.pop(component.id)
343
+ elif component.id not in self.terminating_scripts:
330
344
  if component.value != 'running':
331
345
  component.value = 'running'
332
346
  component.save()
@@ -343,7 +357,8 @@ class AutomationsGatewayHandler(GatesHandler, BaseObjectCommandsGatewayHandler):
343
357
  return self.stop_script(component, 'error')
344
358
 
345
359
  self.running_scripts[component.id] = ScriptRunHandler(
346
- component.id, daemon=True
360
+ component.id, multiprocessing.Event(),
361
+ daemon=True
347
362
  )
348
363
  self.running_scripts[component.id].start()
349
364
 
@@ -360,9 +375,14 @@ class AutomationsGatewayHandler(GatesHandler, BaseObjectCommandsGatewayHandler):
360
375
  logger = get_component_logger(component)
361
376
  if stop_status == 'error':
362
377
  logger.log(logging.INFO, "-------GATEWAY STOP-------")
363
- else:
378
+ elif stop_status == 'stopped':
364
379
  logger.log(logging.INFO, "-------STOP-------")
365
- self.running_scripts[component.id].terminate()
380
+
381
+ if self.running_scripts[component.id].exit_in_use.is_set()\
382
+ and not self.running_scripts[component.id].exin_in_use_fail.is_set():
383
+ self.running_scripts[component.id].exit_event.set()
384
+ else:
385
+ self.running_scripts[component.id].terminate()
366
386
 
367
387
  def kill():
368
388
  start = time.time()
Binary file
Binary file
Binary file
simo/core/api_meta.py CHANGED
@@ -8,7 +8,8 @@ from simo.core.models import Icon, Instance, Category, Zone
8
8
  from simo.core.middleware import introduce_instance
9
9
  from .serializers import (
10
10
  HiddenSerializerField, ComponentManyToManyRelatedField,
11
- TextAreaSerializerField, Component, LocationSerializer
11
+ TextAreaSerializerField, Component, LocationSerializer,
12
+ SoundSerializer
12
13
  )
13
14
 
14
15
 
@@ -33,6 +34,7 @@ class SIMOAPIMetadata(SimpleMetadata):
33
34
  serializers.MultipleChoiceField: 'multiple choice',
34
35
  serializers.FileField: 'file upload',
35
36
  serializers.ImageField: 'image upload',
37
+ SoundSerializer: 'sound upload',
36
38
  serializers.ListField: 'list',
37
39
  serializers.DictField: 'nested object',
38
40
  serializers.Serializer: 'nested object',
simo/core/form_fields.py CHANGED
@@ -182,4 +182,10 @@ class PlainLocationField(forms.fields.CharField):
182
182
  if attr in kwargs:
183
183
  dwargs[attr] = kwargs[attr]
184
184
 
185
- super(PlainLocationField, self).__init__(*args, **dwargs)
185
+ super(PlainLocationField, self).__init__(*args, **dwargs)
186
+
187
+
188
+
189
+ class SoundField(forms.fields.FileField):
190
+ pass
191
+
simo/core/forms.py CHANGED
@@ -1,4 +1,4 @@
1
- import traceback
1
+ import traceback, json
2
2
  from dal import forward
3
3
  from django.contrib.admin.forms import AdminAuthenticationForm as OrgAdminAuthenticationForm
4
4
  from django.db import models
@@ -117,8 +117,11 @@ class ConfigFieldsMixin:
117
117
  obj.pk for obj in self.cleaned_data[field_name]
118
118
  ]
119
119
  else:
120
- self.instance.config[field_name] = \
121
- self.cleaned_data[field_name]
120
+ try:
121
+ self.instance.config[field_name] = \
122
+ json.loads(json.dumps(self.cleaned_data[field_name]))
123
+ except:
124
+ continue
122
125
 
123
126
  if commit:
124
127
  from simo.users.middleware import get_current_user
@@ -0,0 +1,18 @@
1
+ # Generated by Django 4.2.10 on 2024-12-11 14:14
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('core', '0046_component_value_translation_alter_gateway_type'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name='component',
15
+ name='value_translation',
16
+ field=models.TextField(blank=True, default="def translate(value, occasion):\n if occasion == 'before-set':\n return value\n else: # 'before-send'\n return value", help_text='Adjust this to make value translations before value isset on to a component and before it is sent to a device from your SIMO.io smart home instance.'),
17
+ ),
18
+ ]
simo/core/serializers.py CHANGED
@@ -16,7 +16,7 @@ from simo.core.forms import HiddenField, FormsetField
16
16
  from simo.core.form_fields import (
17
17
  Select2ListChoiceField, Select2ModelChoiceField,
18
18
  Select2ListMultipleChoiceField, Select2ModelMultipleChoiceField,
19
- PlainLocationField
19
+ PlainLocationField, SoundField
20
20
  )
21
21
  from simo.core.models import Component
22
22
  from rest_framework.relations import PrimaryKeyRelatedField, ManyRelatedField
@@ -33,6 +33,10 @@ class LocationSerializer(serializers.CharField):
33
33
  pass
34
34
 
35
35
 
36
+ class SoundSerializer(serializers.FileField):
37
+ pass
38
+
39
+
36
40
 
37
41
  class TimestampField(serializers.Field):
38
42
 
@@ -145,6 +149,7 @@ class ComponentFormsetField(FormSerializer):
145
149
  field_mapping = {
146
150
  HiddenField: HiddenSerializerField,
147
151
  PlainLocationField: LocationSerializer,
152
+ SoundField: SoundSerializer,
148
153
  Select2ListChoiceField: serializers.ChoiceField,
149
154
  forms.ModelChoiceField: FormsetPrimaryKeyRelatedField,
150
155
  Select2ModelChoiceField: FormsetPrimaryKeyRelatedField,
@@ -33,7 +33,7 @@
33
33
  </ul>
34
34
  {% endif %}{% endif %}
35
35
  {% endblock %}
36
- <form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.model_name }}_form" novalidate>{% csrf_token %}{% block form_top %}{% endblock %}
36
+ <form enctype="multipart/form-data" action="{{ form_url }}" method="post" id="{{ opts.model_name }}_form" novalidate>{% csrf_token %}{% block form_top %}{% endblock %}
37
37
  <div>
38
38
  {% if is_popup %}<input type="hidden" name="{{ is_popup_var }}" value="1">{% endif %}
39
39
  {% if to_field %}<input type="hidden" name="{{ to_field_var }}" value="{{ to_field }}">{% endif %}
Binary file
Binary file
@@ -37,7 +37,7 @@ from .forms import (
37
37
  ThermostatConfigForm, AlarmGroupConfigForm,
38
38
  IPCameraConfigForm, WeatherForm,
39
39
  WateringConfigForm, StateSelectForm, MainStateSelectForm,
40
- AlarmClockConfigForm
40
+ AlarmClockConfigForm, AudioAlertConfigForm
41
41
  )
42
42
 
43
43
  # ----------- Generic controllers -----------------------------
@@ -1054,6 +1054,12 @@ class AlarmClock(ControllerBase):
1054
1054
  return current_value
1055
1055
 
1056
1056
 
1057
+ class AudioAlert(Switch):
1058
+ gateway_class = GenericGatewayHandler
1059
+ name = _("Audio Alert")
1060
+ config_form = AudioAlertConfigForm
1061
+
1062
+
1057
1063
  class StateSelect(ControllerBase):
1058
1064
  gateway_class = GenericGatewayHandler
1059
1065
  name = _("State select")
@@ -1188,6 +1194,9 @@ class MainState(StateSelect):
1188
1194
  return any(phones_on_charge)
1189
1195
 
1190
1196
 
1197
+
1198
+
1199
+
1191
1200
  # ----------- Dummy controllers -----------------------------
1192
1201
 
1193
1202
  class DummyBinarySensor(BinarySensor):
simo/generic/forms.py CHANGED
@@ -17,6 +17,8 @@ from simo.core.form_fields import (
17
17
  Select2ModelMultipleChoiceField
18
18
  )
19
19
  from simo.core.forms import DimmerConfigForm, SwitchForm
20
+ from simo.core.form_fields import SoundField
21
+ from simo.multimedia.models import Sound
20
22
 
21
23
  ACTION_METHODS = (
22
24
  ('turn_on', "Turn ON"), ('turn_off', "Turn OFF"),
@@ -613,3 +615,37 @@ class AlarmClockConfigForm(BaseComponentForm):
613
615
  if c:
614
616
  obj.slaves.add(c)
615
617
  return obj
618
+
619
+
620
+ class AudioAlertConfigForm(BaseComponentForm):
621
+ sound = SoundField()
622
+ loop = forms.BooleanField(initial=False, required=False)
623
+ volume = forms.IntegerField(initial=30, min_value=2, max_value=100)
624
+ players = Select2ModelMultipleChoiceField(
625
+ queryset=Component.objects.all(),#filter(base_type='audio-player'),
626
+ url='autocomplete-component',
627
+ )
628
+
629
+ def __init__(self, *args, **kwargs):
630
+ super().__init__(*args, **kwargs)
631
+ self.basic_fields.extend(['sound', 'loop', 'volume', 'players'])
632
+ if self.instance.id:
633
+ sound = Sound.objects.filter(
634
+ id=self.instance.config.get('sound_id', 0)
635
+ ).first()
636
+ if sound:
637
+ self.fields['sound'].initial = sound.file
638
+
639
+ def save(self, commit=True):
640
+ if commit and self.cleaned_data['sound'] \
641
+ and self.cleaned_data['sound'] != self.fields['sound'].initial:
642
+ sound = Sound(
643
+ name=self.cleaned_data['sound'].name,
644
+ )
645
+ sound.file.save(
646
+ self.cleaned_data['sound'].name, self.cleaned_data['sound'],
647
+ save=True
648
+ )
649
+ self.instance.config['sound_id'] = sound.id
650
+ self.cleaned_data.pop('sound')
651
+ return super().save(commit=commit)
simo/generic/gateways.py CHANGED
@@ -134,8 +134,47 @@ class GroupButtonsHandler:
134
134
  group.toggle()
135
135
 
136
136
 
137
+ class AudioAlertsHandler:
138
+
139
+ def control_audio_alert(self, component, val):
140
+ if val:
141
+ from simo.multimedia.models import Sound
142
+ sound = Sound.objects.filter(
143
+ id=component.config.get('sound_id')
144
+ ).first()
145
+ if not sound:
146
+ return
147
+ loop = component.config.get('loop', False)
148
+ for pl_id in component.config.get('players', []):
149
+ player = Component.objects.filter(
150
+ id=pl_id, base_type='audio-player'
151
+ ).first()
152
+ if not player:
153
+ continue
154
+ player.play_alert(
155
+ sound.id,
156
+ component.config.get('loop', False),
157
+ component.config.get('volume', 50)
158
+ )
159
+ if not loop:
160
+ def set_done(comp):
161
+ comp.set(False)
162
+ threading.Timer(sound.length, set_done, args=[component])
163
+ component.set(True)
164
+ else:
165
+ for pl_id in component.config.get('players', []):
166
+ player = Component.objects.filter(
167
+ id=pl_id, base_type='audio-player'
168
+ ).first()
169
+ if not player:
170
+ continue
171
+ player.cancel_alert()
172
+ component.set(False)
173
+
137
174
 
138
- class GenericGatewayHandler(BaseObjectCommandsGatewayHandler):
175
+ class GenericGatewayHandler(
176
+ BaseObjectCommandsGatewayHandler, GroupButtonsHandler, AudioAlertsHandler
177
+ ):
139
178
  name = "Generic"
140
179
  config_form = BaseGatewayForm
141
180
  info = "Provides generic type components which use other components to operate like " \
@@ -149,7 +188,8 @@ class GenericGatewayHandler(BaseObjectCommandsGatewayHandler):
149
188
  ('watch_watering', 60),
150
189
  ('watch_alarm_events', 1),
151
190
  ('watch_timers', 1),
152
- ('watch_main_states', 60)
191
+ ('watch_main_states', 60),
192
+ ('watch_groups', 60)
153
193
  )
154
194
 
155
195
  def __init__(self, *args, **kwargs):
@@ -229,7 +269,7 @@ class GenericGatewayHandler(BaseObjectCommandsGatewayHandler):
229
269
 
230
270
  def on_mqtt_message(self, client, userdata, msg):
231
271
  print("Mqtt message: ", msg.payload)
232
- from simo.generic.controllers import AlarmGroup
272
+ from simo.generic.controllers import AlarmGroup, AudioAlert
233
273
 
234
274
  payload = json.loads(msg.payload)
235
275
  drop_current_instance()
@@ -239,6 +279,8 @@ class GenericGatewayHandler(BaseObjectCommandsGatewayHandler):
239
279
  try:
240
280
  if component.controller_uid == AlarmGroup.uid:
241
281
  self.control_alarm_group(component, payload.get('set_val'))
282
+ elif component.controller_uid == AudioAlert.uid:
283
+ self.control_audio_alert(component, payload.get('set_val'))
242
284
  else:
243
285
  component.controller.set(payload.get('set_val'))
244
286
  except Exception:
simo/multimedia/admin.py CHANGED
@@ -7,10 +7,9 @@ from .forms import SoundModelForm
7
7
 
8
8
  @admin.register(Sound)
9
9
  class SoundAdmin(admin.ModelAdmin):
10
- list_display = 'id', 'name', 'slug', 'file', 'length_display'
11
- search_fields = 'name', 'slug', 'file'
12
- prepopulated_fields = {"slug": ["name"]}
13
- list_display_links = 'id', 'name', 'slug'
10
+ list_display = 'id', 'name', 'file', 'length_display', 'date_uploaded'
11
+ search_fields = 'name', 'file'
12
+ list_display_links = 'id', 'name',
14
13
  form = SoundModelForm
15
14
  readonly_fields = 'length_display',
16
15
 
@@ -22,6 +21,7 @@ class SoundAdmin(admin.ModelAdmin):
22
21
 
23
22
  def save_model(self, request, obj, form, change):
24
23
  super().save_model(request, obj, form, change)
24
+ # need to keep it here as using admin interface skips post_save signals
25
25
  try:
26
26
  obj.length = int(
27
27
  librosa.core.get_duration(
@@ -0,0 +1,10 @@
1
+ from django.urls import path, re_path
2
+ from .views import SoundAutocomplete
3
+
4
+
5
+ urlpatterns = [
6
+ path(
7
+ 'autocomplete-sound',
8
+ SoundAutocomplete.as_view(), name='autocomplete-sound'
9
+ )
10
+ ]
@@ -89,10 +89,11 @@ class BasePlayer(Switch):
89
89
  assert 0 <= volume <= 100
90
90
  self.send({"play_uri": uri, 'volume': volume})
91
91
 
92
- def play_alert(self, val, volume=None):
92
+ def play_alert(self, val, loop=False, volume=None):
93
93
  '''
94
94
  Plays alert and goes back to whatever was playing initially
95
95
  :param val: Sound.id or uri
96
+ :param loop: Repeat infinitely
96
97
  :param volume: volume at which to play
97
98
  :return:
98
99
  '''
@@ -101,6 +102,10 @@ class BasePlayer(Switch):
101
102
  assert 0 <= volume <= 100
102
103
  self.send({"alert": val, 'volume': volume})
103
104
 
105
+ def cancel_alert(self):
106
+ '''Cancel alert if it's currently playing'''
107
+ self.send({"alert": None})
108
+
104
109
  def toggle(self):
105
110
  if self.component.value == 'playing':
106
111
  self.pause()
@@ -120,3 +125,6 @@ class BaseVideoPlayer(BasePlayer):
120
125
  name = _("Video Player")
121
126
  base_type = 'video-player'
122
127
  app_widget = VideoPlayerWidget
128
+
129
+
130
+
@@ -0,0 +1,24 @@
1
+ # Generated by Django 4.2.10 on 2024-12-11 14:14
2
+
3
+ from django.db import migrations, models
4
+ import django.utils.timezone
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('multimedia', '0004_auto_20231023_1055'),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.RemoveField(
15
+ model_name='sound',
16
+ name='slug',
17
+ ),
18
+ migrations.AddField(
19
+ model_name='sound',
20
+ name='date_uploaded',
21
+ field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
22
+ preserve_default=False,
23
+ ),
24
+ ]
simo/multimedia/models.py CHANGED
@@ -1,12 +1,13 @@
1
- import os
1
+ import os, librosa
2
2
  from django.db import models
3
+ from django.db.models.signals import post_save, post_delete
4
+ from django.dispatch import receiver
3
5
  from django.core.files.storage import FileSystemStorage
4
6
  from django.conf import settings
5
7
 
6
8
 
7
9
  class Sound(models.Model):
8
10
  name = models.CharField(max_length=100, db_index=True)
9
- slug = models.SlugField()
10
11
  file = models.FileField(
11
12
  upload_to='sounds', storage=FileSystemStorage(
12
13
  location=os.path.join(settings.VAR_DIR, 'public_media'),
@@ -17,9 +18,22 @@ class Sound(models.Model):
17
18
  length = models.PositiveIntegerField(
18
19
  editable=False, default=0, help_text='Sound length in seconds'
19
20
  )
21
+ date_uploaded = models.DateTimeField(auto_now_add=True)
20
22
 
21
23
  def __str__(self):
22
24
  return self.name
23
25
 
24
26
  def get_absolute_url(self):
25
27
  return self.file.url
28
+
29
+
30
+ @receiver(post_save, sender=Sound)
31
+ def determine_duration(sender, instance, created, **kwargs):
32
+ if not instance.length:
33
+ instance.length = int(
34
+ librosa.core.get_duration(
35
+ sr=22050, filename=instance.file.path
36
+ )
37
+ )
38
+ instance.save()
39
+
@@ -0,0 +1,19 @@
1
+ from django.http import Http404
2
+ from dal import autocomplete
3
+ from simo.core.utils.helpers import search_queryset
4
+ from .models import Sound
5
+
6
+
7
+ class SoundAutocomplete(autocomplete.Select2QuerySetView):
8
+
9
+ def get_queryset(self):
10
+ if not self.request.user.is_authenticated:
11
+ raise Http404()
12
+
13
+ qs = Sound.objects.all()
14
+
15
+ if self.request.GET.get('value'):
16
+ qs = qs.filter(pk__in=self.request.GET['value'].split(','))
17
+ elif self.q:
18
+ qs = search_queryset(qs, self.q, ('name', 'slug'))
19
+ return qs
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: simo
3
- Version: 2.7.10
3
+ Version: 2.7.11
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
@@ -15,7 +15,7 @@ simo/automation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  simo/automation/app_widgets.py,sha256=gaqImMZjuMHm7nIb9a4D-Y3qipz_WhSPAHXcwGx4Uzs,199
16
16
  simo/automation/controllers.py,sha256=Ow8xG9hkdyMRZbrNPX5uZloYM9jZqa9mgCh4k6FeoJw,11492
17
17
  simo/automation/forms.py,sha256=UWnkxw8pILPK0smRPTo4SLgsZl78zOySx7JIc30Bgtk,10228
18
- simo/automation/gateways.py,sha256=nWiWaDcNFpOumtTEE1xbhuYLt4Qvs8K0-zVEupdRPRU,14563
18
+ simo/automation/gateways.py,sha256=dTrx4r3aoUynWQCHHXZFKkoHhKbC3-UMZIY755C-sdI,15470
19
19
  simo/automation/helpers.py,sha256=iP-fxxB8HsFQy3k2CjFubu86aMqvWgmh-p24DiyOrek,4330
20
20
  simo/automation/models.py,sha256=l45FHgeKGsfpLtd1X1PVFpIjB5JI4BlvKkodpcxm6aE,927
21
21
  simo/automation/serializers.py,sha256=PjyFrjdPK1mBsgbNhyqMi9SWzcymqTa742ipy0LhAN4,1996
@@ -24,10 +24,10 @@ simo/automation/__pycache__/__init__.cpython-38.pyc,sha256=YmP0xAD-mxpQHgdTZeC64
24
24
  simo/automation/__pycache__/app_widgets.cpython-38.pyc,sha256=7DfUA9V_1MiwrOe_ta-ts8dYY8xXb9UMg2_9A3XoRcs,523
25
25
  simo/automation/__pycache__/controllers.cpython-38.pyc,sha256=CL-0Tq9B4-E36fYfWT1XEBTq1dkq1W8003f6MrBnQU0,8391
26
26
  simo/automation/__pycache__/forms.cpython-38.pyc,sha256=cpA5hA2Iz3JsPC0Dq01ki1I7S9c5DKRcXveHApI1dJo,7772
27
- simo/automation/__pycache__/gateways.cpython-38.pyc,sha256=K8y3DNQFfcEQiLTgHl5p0Uk58R5QCuqFCluvbQ6I_9g,10652
28
- simo/automation/__pycache__/helpers.cpython-38.pyc,sha256=4VSSarOFnUk_KExWwvDlx5dEhv8aHUCHMZDtGG--pUY,3627
29
- simo/automation/__pycache__/models.cpython-38.pyc,sha256=6gXdIMcrWaGACal2omj-b2if5JW8QvLIrtRfYVpDRQU,1230
30
- simo/automation/__pycache__/serializers.cpython-38.pyc,sha256=gWgcuPE8aY-TmuRLXCuSR74mvtKerpZ04m0MfPfw0AI,3405
27
+ simo/automation/__pycache__/gateways.cpython-38.pyc,sha256=F4zO0G15gGMSaQaB_fW13qtcBHQ3KYlDqZ4_Ch0YW70,10956
28
+ simo/automation/__pycache__/helpers.cpython-38.pyc,sha256=fNjSyn4Mfq7-JQx-bdsnj-rSxgu20dVJ9-5ZEMT6yiM,3627
29
+ simo/automation/__pycache__/models.cpython-38.pyc,sha256=VeQNAygVr0AXPlF5LEYk90Tq8zuCVPfsA2r1NBjrkxU,1230
30
+ simo/automation/__pycache__/serializers.cpython-38.pyc,sha256=9Te21FW5-Tki1a-tq3gt0rme0afBA8o-7V249b4muFk,3405
31
31
  simo/automation/__pycache__/state.cpython-38.pyc,sha256=TO2IM6h2hbGVlOUcoMwHkDUF4V-54d_KVhcebMgNtCk,784
32
32
  simo/automation/migrations/0001_initial.py,sha256=VB6WIK1RlUdtqlWYCEhtnVE24odp2dfUvOVcxUZPpQ4,990
33
33
  simo/automation/migrations/0002_update_helpers_in_scripts.py,sha256=GHtc8ibfURDVoWHw17UxaaA0sEy4tIklIjP6GEMLjhs,714
@@ -63,7 +63,7 @@ simo/core/__init__.py,sha256=_s2TjJfQImsMrTIxqLAx9AZie1Ojmm6sCHASdl3WLGU,50
63
63
  simo/core/admin.py,sha256=j07Dy3IsW7SRRsAJ2XXGzE0MTxmfY87CEEbT22kLlS4,18550
64
64
  simo/core/api.py,sha256=D5FBWvhYpQeM9wVfapzRvBi_ypOtXBt9tmmCaUxpGjI,29992
65
65
  simo/core/api_auth.py,sha256=vCxvczA8aWNcW0VyKs5WlC_ytlqeGP_H_hkKUNVkCwM,1247
66
- simo/core/api_meta.py,sha256=DuX6BmUC2uY_mXb8NC_UMq6T3FcMSQoD2Rv0Pp0NdQo,5228
66
+ simo/core/api_meta.py,sha256=Bh-UOzANXl9Bg3FadFf7biM_olAr9GgjNJXu55SO6Og,5290
67
67
  simo/core/app_widgets.py,sha256=VxZzapuc-a29wBH7JzpvNF2SK1ECrgNUySId5ke1ffc,2509
68
68
  simo/core/apps.py,sha256=CsqpiQerhmrMsH-wGiG-gQgXd9qEkIi-LUaA9cXpKSw,425
69
69
  simo/core/auto_urls.py,sha256=FBDclIeRp5UVWomIUbRzUgY-AoMk-r2qC2htlwKD4Lo,1106
@@ -74,8 +74,8 @@ simo/core/controllers.py,sha256=gVwQHYkJ6GNTHaKTSjf4H2VVykQbvKh4_fSe_MdAVi4,3741
74
74
  simo/core/dynamic_settings.py,sha256=bUs58XEZOCIEhg1TigR3LmYggli13KMryBZ9pC7ugAQ,1872
75
75
  simo/core/events.py,sha256=1_KIk5pJqdLPRQlCQ9xSyALst2Cn0b2lAEAJ3QjwIjE,4801
76
76
  simo/core/filters.py,sha256=6wbn8C2WvKTTjtfMwwLBp2Fib1V0-DMpS4iqJd6jJQo,2540
77
- simo/core/form_fields.py,sha256=m3dR31M31q3DQM-7P4jBOXLofm-PiC0ykIfUrpWhOjg,5007
78
- simo/core/forms.py,sha256=UGGsBU8qPFWf_NMGg6IvImloojhoU0sTX2jrJ9mJPjs,21748
77
+ simo/core/form_fields.py,sha256=Os4bLhEt6fQpQ5JWPThs5XxMI2429kQNgr7SgDDr_y0,5063
78
+ simo/core/forms.py,sha256=iPUBclY5aSX4fCy2VdAMi48De95lj17RiQaggAKmTnA,21860
79
79
  simo/core/gateways.py,sha256=Y2BME6zSyeUq_e-hzEUF6gErCUCP6nFxedkLZKiLVOo,4141
80
80
  simo/core/loggers.py,sha256=EBdq23gTQScVfQVH-xeP90-wII2DQFDjoROAW6ggUP4,1645
81
81
  simo/core/managers.py,sha256=n-b3I4uXzfHKTeB1VMjSaMsDUxp8FegFJwnbV1IsWQ4,3019
@@ -83,7 +83,7 @@ simo/core/middleware.py,sha256=eUFf6iP-Snx_0TE3MoXsSwqrd5IjlukqZk2GQGStRCo,3385
83
83
  simo/core/models.py,sha256=-zhQEvQRqacTjdqL4Gv7shlP1Uo09Rj3vHKqNONlxWU,22716
84
84
  simo/core/permissions.py,sha256=2YNRot2qoHjHKWPGOpO4PBseecctPbTlUQpepnFkCRs,3027
85
85
  simo/core/routing.py,sha256=X1_IHxyA-_Q7hw1udDoviVP4_FSBDl8GYETTC2zWTbY,499
86
- simo/core/serializers.py,sha256=pa4F5fSvIxgGJWBEcXdzpmemRWl6QbUoUDMqQ_RdA2o,22514
86
+ simo/core/serializers.py,sha256=ikt1_UzEqJKPC1UFvvvf6q424VXkCY1oh61uXTDBeYc,22624
87
87
  simo/core/signal_receivers.py,sha256=y8p0BG3PzhHI3w2WXS8QXyxXc7YKUE_mmO2-QElvrP4,6276
88
88
  simo/core/socket_consumers.py,sha256=Es_NmacQGZjsncBXDTEXR2yZbRs7mf2FKOBJjbZRGac,9607
89
89
  simo/core/storage.py,sha256=_5igjaoWZAiExGWFEJMElxUw55DzJG1jqFty33xe8BE,342
@@ -93,10 +93,10 @@ simo/core/types.py,sha256=WJEq48mIbFi_5Alt4wxWMGXxNxUTXqfQU5koH7wqHHI,1108
93
93
  simo/core/views.py,sha256=yx9I0byeVUa-LAOnklpWIYwpNNOf5m9fyjKBvj4YCh4,2475
94
94
  simo/core/widgets.py,sha256=J9e06C6I22F6xKic3VMgG7WeX07glAcl-4bF2Mg180A,2827
95
95
  simo/core/__pycache__/__init__.cpython-38.pyc,sha256=ZJFM_XN0RmJMULQulgA_wFiOnEtsMoedcOWnXjH-Y8o,208
96
- simo/core/__pycache__/admin.cpython-38.pyc,sha256=oDG9Kem_VbiRDB6ednmvPS9asBDb3iki7OyLzzNRYCQ,14155
96
+ simo/core/__pycache__/admin.cpython-38.pyc,sha256=zdegA3IsH3QhT7Hm6Aj08F2sv-3h1X7VBcwN05fhVis,14155
97
97
  simo/core/__pycache__/api.cpython-38.pyc,sha256=JXCs5U9MPyZTdO4_j-ytpH1_Fo9-w36zs1eHXZ-czwo,22925
98
98
  simo/core/__pycache__/api_auth.cpython-38.pyc,sha256=mi3mu5qEKio_PvfQEvr3Q6AhdPLAHxzxAxrMbAz_pKU,1712
99
- simo/core/__pycache__/api_meta.cpython-38.pyc,sha256=km2IBFS1ot6CTYuIAnCwQToQLVQqRasAvfQ6UC8-1ts,3853
99
+ simo/core/__pycache__/api_meta.cpython-38.pyc,sha256=euP2EQsfTVCdvMT9NY10bK7xrz2dQouqQKZP0efN1ss,3906
100
100
  simo/core/__pycache__/app_widgets.cpython-38.pyc,sha256=oN657XMMZ6GYN9nblv7fX3kdnTEzSP9XV6PXM6Z0wl4,4358
101
101
  simo/core/__pycache__/apps.cpython-38.pyc,sha256=JL0BEqgXcSQvMlcK48PBpPfyDEkPMdO1Y0teqMRGirs,713
102
102
  simo/core/__pycache__/auto_urls.cpython-38.pyc,sha256=ib_ns5Ko8ybfrdJJWYVV1jevihxOFs39aBF4bez6Lzs,874
@@ -107,18 +107,18 @@ simo/core/__pycache__/controllers.cpython-38.pyc,sha256=LtrQQ8egOIOuQbAckeM-z8Of
107
107
  simo/core/__pycache__/dynamic_settings.cpython-38.pyc,sha256=wGpnscX1DxFpRl54MQURhjz2aD3NJohSzw9JCFnzh2Y,2384
108
108
  simo/core/__pycache__/events.cpython-38.pyc,sha256=1y8YaZsiDkBOeIWzH7SQz4holmMG_RLlMWi8kuSZcoE,5280
109
109
  simo/core/__pycache__/filters.cpython-38.pyc,sha256=WBBDwcDQwOmgbrRhyUxenSN80rU4Eq9jQ6RcrRGCP_o,2440
110
- simo/core/__pycache__/form_fields.cpython-38.pyc,sha256=BXiHLzmLgex-JrCcTo5k26CyZyKZ_QJv7eoEwyJGE-g,5745
111
- simo/core/__pycache__/forms.cpython-38.pyc,sha256=GaAOXoLUc8xvu5sqJG1Z6K-xkkPtd2Y6WrGFft7dklw,17658
110
+ simo/core/__pycache__/form_fields.cpython-38.pyc,sha256=WJgjOqvl5BZoVAGVtxGeDOOW-jn3_fdJwVYBx1IRJlY,5886
111
+ simo/core/__pycache__/forms.cpython-38.pyc,sha256=Ye8lP-xG_yxMKOftzhXNPWfY4nkjNynBn-eJhLnb-GM,17754
112
112
  simo/core/__pycache__/gateways.cpython-38.pyc,sha256=-_ugqnUOA1Cl6VfMqpV96n7ekVOEwYg_jNvoaZEcx9I,4815
113
113
  simo/core/__pycache__/loggers.cpython-38.pyc,sha256=Z-cdQnC6XlIonPV4Sl4E52tP4NMEdPAiHK0cFaIL7I8,1623
114
114
  simo/core/__pycache__/managers.cpython-38.pyc,sha256=6RTIxyjOgpQGtAqcUyE2vFPS09w1V5Wmd_vOV7rHRRI,3370
115
115
  simo/core/__pycache__/middleware.cpython-38.pyc,sha256=SgTLFNkKxvJ62hevSAVNZHgHdG_u2p7AZBhrj-jfFPs,2649
116
- simo/core/__pycache__/models.cpython-38.pyc,sha256=I6u3Gs4Af0lv6GWDDhlTSTqJWiErhMWSoksNWxSODsg,18865
116
+ simo/core/__pycache__/models.cpython-38.pyc,sha256=3nVIrsYP-ZCmkr5z71zivCdaZ8hqmh521BvlMK5gP6g,18468
117
117
  simo/core/__pycache__/permissions.cpython-38.pyc,sha256=UdtxCTXPEbe99vgZOfRz9wfKSYvUn9hSRbpIV9CJSyI,2988
118
118
  simo/core/__pycache__/routing.cpython-38.pyc,sha256=3T3FPJ8Cn99xZCGvMyg2xjl7al-Shm9CelbSpkJtNP8,599
119
- simo/core/__pycache__/serializers.cpython-38.pyc,sha256=xegjaPbTp71RK9pfle_vgbHiUhr4CQIq34A7RxpvNGg,19817
119
+ simo/core/__pycache__/serializers.cpython-38.pyc,sha256=SkPKqNRo4PBdHBqJ6KsX5SnJBkC6zk3SO8bpr4enwu4,20000
120
120
  simo/core/__pycache__/signal_receivers.cpython-38.pyc,sha256=v-qSKC9KX--aw9-vSNX8z8HXNFr0yOII0Aw8JxrYM0U,4920
121
- simo/core/__pycache__/socket_consumers.cpython-38.pyc,sha256=KqbO1cOewodVPcy0-htVefyUjCuELKV0o7fOfYqfgPc,8490
121
+ simo/core/__pycache__/socket_consumers.cpython-38.pyc,sha256=Mr1-x-vGjBNffbz0S6AKpJCuzHJgRm8kXefv3qVVY_E,8397
122
122
  simo/core/__pycache__/storage.cpython-38.pyc,sha256=9R1Xu0FJDflfRXUPsqEgt0SpwiP7FGk7HaR8s8XRyI8,721
123
123
  simo/core/__pycache__/tasks.cpython-38.pyc,sha256=-J2is-l5XsfhamreN2TPQDTK-Jw6XGYL81bcVfjXsU8,11213
124
124
  simo/core/__pycache__/todos.cpython-38.pyc,sha256=lOqGZ58siHM3isoJV4r7sg8igrfE9fFd-jSfeBa0AQI,253
@@ -237,6 +237,7 @@ simo/core/migrations/0043_alter_category_instance_alter_instance_timezone_and_mo
237
237
  simo/core/migrations/0044_alter_gateway_type.py,sha256=xoQvOSz_JrWHECAAII83518tmhEEfLUHqxpIjoo4oLY,758
238
238
  simo/core/migrations/0045_alter_instance_device_report_history_days_and_more.py,sha256=saR7gEbTDfXTcKnT1R193Aboqlg32HvDRI_JAvZ97TY,1360
239
239
  simo/core/migrations/0046_component_value_translation_alter_gateway_type.py,sha256=SUybDSLRCGevVS9mCQGvozO8LA43tKze3R5rCf1TQ4E,1282
240
+ simo/core/migrations/0047_alter_component_value_translation.py,sha256=3GAgUBkZKUwWJMbDJxJJLBicC8eKkQ2ZJA__-xYpBCo,733
240
241
  simo/core/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
241
242
  simo/core/migrations/__pycache__/0001_initial.cpython-38.pyc,sha256=w6GiBXVxWj30Bg4Sn_pFVeA041d-pCrkaq8mR3KuF70,5381
242
243
  simo/core/migrations/__pycache__/0002_load_icons.cpython-38.pyc,sha256=Nb9RrPjVYo_RpZ5PmzoaEIWGCeVt4kicpmGiKlBrpIw,2123
@@ -284,6 +285,7 @@ simo/core/migrations/__pycache__/0043_alter_category_instance_alter_instance_tim
284
285
  simo/core/migrations/__pycache__/0044_alter_gateway_type.cpython-38.pyc,sha256=KwqGKoo8C1wH9ZUhagpC3plTwheSMcWtwlCGcSPd5WQ,964
285
286
  simo/core/migrations/__pycache__/0045_alter_instance_device_report_history_days_and_more.cpython-38.pyc,sha256=e1_hKjdokggnPndFkoBWrHQ18IkBi2W7WahcSjAbvKo,1278
286
287
  simo/core/migrations/__pycache__/0046_component_value_translation_alter_gateway_type.cpython-38.pyc,sha256=0WowsGpjyPX7Z341bhU4bXErZqqFZaL1gtiusabrD0I,1445
288
+ simo/core/migrations/__pycache__/0047_alter_component_value_translation.cpython-38.pyc,sha256=ucmaVOjI3q78vBTiEC7Aq76CklWq3fsC24EiH-mIzGc,961
287
289
  simo/core/migrations/__pycache__/__init__.cpython-38.pyc,sha256=VZmDQ57BTcebuM0KMhjiTOabgWZCBxQmSJzWZos9SO8,169
288
290
  simo/core/static/ansi_styles.css,sha256=4ieJGrjZPKyPSago9FdB_gflHoGE1vxCHi8qVn5tY-Y,37352
289
291
  simo/core/static/admin/Img/plus.svg,sha256=2NpSFPWqGIjpAQGFI7LDQHPKagEhYkJiJX95ufCoZaI,741
@@ -10201,7 +10203,7 @@ simo/core/templates/admin/gateway_control/widget_internals.html,sha256=6UsVKQtHW
10201
10203
  simo/core/templates/admin/users/qr_code.html,sha256=kE4WNSVsPUvNUFnSUAY8jiD9JCvc6YasIS9vLuw0cbI,125
10202
10204
  simo/core/templates/admin/wizard/discovery.html,sha256=xpAiXgKneT0d53c7h1MyAjoOFdXc_lDaH9TdelrggVU,6608
10203
10205
  simo/core/templates/admin/wizard/field.html,sha256=ZbgzAUMs6RdJDH44yQ3-rmAkPmBdsIvBTk1RAQaENAs,413
10204
- simo/core/templates/admin/wizard/wizard_add.html,sha256=RPkwKXctu_yxJxjJqcvVEzG4YtK9-CJK801RPxleWRE,4127
10206
+ simo/core/templates/admin/wizard/wizard_add.html,sha256=ULT1Bj--YnMJ2uVs4zfI6hGZ4zNWTVLLnbADL4ATtLk,4093
10205
10207
  simo/core/templates/core/icon_acutocomplete_select_item.html,sha256=D7aWefAJjrDqyMcwt4vtuwkruklJ1aSjCTi99HLrQUY,768
10206
10208
  simo/core/templates/core/object_acutocomplete_select_item.html,sha256=pS8APlv1IdhfttONh3npzQNfrOhj63YC7837tnOfSHk,465
10207
10209
  simo/core/templates/core/openvpn_client.conf,sha256=z1838G8v8vmIiQKyfE8URCCMpLsges5jbX3hDJkztF4,249
@@ -10276,16 +10278,16 @@ simo/fleet/__pycache__/auto_urls.cpython-38.pyc,sha256=jHsvfwAumiBusr91QK1-qC-nm
10276
10278
  simo/fleet/__pycache__/base_types.cpython-38.pyc,sha256=deyPwjpT6xZiFxBGFnj5b7R-lbdOTh2krgpJhrcGVhc,274
10277
10279
  simo/fleet/__pycache__/ble.cpython-38.pyc,sha256=Nrof9w7cm4OlpFWHeVnmvvanh2_oF9oQ3TknJiV93-0,1267
10278
10280
  simo/fleet/__pycache__/controllers.cpython-38.pyc,sha256=vH7mK1K4JBcLU9eKtqTJwbgB0SFMJ-s7WvO0qOsWjrg,24739
10279
- simo/fleet/__pycache__/forms.cpython-38.pyc,sha256=NkchZIvx79WswYepd8IgWwI7Pu3hd4sPkhsWt79YOII,45304
10281
+ simo/fleet/__pycache__/forms.cpython-38.pyc,sha256=hrNfyGm2NfoNG1II-OqeHzR2TkkAWp3igvHGs6X0iIM,45252
10280
10282
  simo/fleet/__pycache__/gateways.cpython-38.pyc,sha256=MIpXuGWitGNdsxJ99fWvMXJ6sVE96ac7iR4K4aM4Sds,5148
10281
10283
  simo/fleet/__pycache__/managers.cpython-38.pyc,sha256=Vmm23zoQnS3-uS5_WJt2n3wtjhLiEhLWaYxXJCU6Gts,1339
10282
10284
  simo/fleet/__pycache__/models.cpython-38.pyc,sha256=AXk1Q_nnHDXirHYgM3EW5pLsrR2CaPWk4EuvGCuDUpI,14131
10283
10285
  simo/fleet/__pycache__/routing.cpython-38.pyc,sha256=aPrCmxFKVyB8R8ZbJDwdPdFfvT7CvobovvZeq_mqRgY,314
10284
10286
  simo/fleet/__pycache__/serializers.cpython-38.pyc,sha256=l_FzORWCM1hcSZV0AaGRO-p0CMTcEfqnLGgbn2IVvI0,3648
10285
- simo/fleet/__pycache__/socket_consumers.cpython-38.pyc,sha256=m8sJWln-uyjeTD-alyoNikEJcHCxZGTe2vXnMGVNZog,14170
10287
+ simo/fleet/__pycache__/socket_consumers.cpython-38.pyc,sha256=lEC1SkY_KgRY0QoBUMPjnbFwSa7qmCf-4eNQ45hAy68,14141
10286
10288
  simo/fleet/__pycache__/tasks.cpython-38.pyc,sha256=RoNxL2WUiW67s9O9DjaYVVjCBSZu2nje0Qn9FJkWVS0,1116
10287
10289
  simo/fleet/__pycache__/utils.cpython-38.pyc,sha256=obUd-X2Y-ybx4icqUWq_qwIxrP9yyarJjexWAfO4MTI,3344
10288
- simo/fleet/__pycache__/views.cpython-38.pyc,sha256=xj7aOlbCwYwrlIuyS9jceiPlMHxY-mmlbfXMJRJ6O5g,4337
10290
+ simo/fleet/__pycache__/views.cpython-38.pyc,sha256=awPe_fz7V5DgknQGEyHqbOFhX6iBhK7Y5TIa7zcO-so,4342
10289
10291
  simo/fleet/migrations/0001_initial.py,sha256=lce8nkD8Sz6pYr-XJSpDm4CMDuB6TA__WtnHpIp-eA4,1326
10290
10292
  simo/fleet/migrations/0002_auto_20220422_0743.py,sha256=sFOfAjnQOzcJjE8lHrrHgTaGilJNYswMdXphgVzUZqY,825
10291
10293
  simo/fleet/migrations/0003_auto_20220422_0752.py,sha256=VcH7DyMAniEwT76hDVofS8FTNpM3nxz_J9AC2zKHDSA,543
@@ -10374,27 +10376,28 @@ simo/fleet/migrations/__pycache__/0040_alter_colonel_pwm_frequency.cpython-38.py
10374
10376
  simo/fleet/migrations/__pycache__/0041_alter_colonel_instance_and_more.cpython-38.pyc,sha256=76yu9gz3pcAq5IYbNeZIoOSgdi0HVqTvc3EdXLGriQI,1178
10375
10377
  simo/fleet/migrations/__pycache__/0042_auto_20241120_1028.cpython-38.pyc,sha256=yK2HRc7MYMc4i9ev7FaguBdnXUnUmpFn8E8JOP_yluA,1030
10376
10378
  simo/fleet/migrations/__pycache__/0043_auto_20241203_0930.cpython-38.pyc,sha256=0QWVIvorcCuhfuGFu0fMdu5vkxcQSosxjt8KT6Ld07E,980
10379
+ simo/fleet/migrations/__pycache__/0044_auto_20241210_0707.cpython-38.pyc,sha256=M99PMBDgOdS_0KIEhlUamHWeTVCTdIHFXIvYTRX15Z8,1029
10377
10380
  simo/fleet/migrations/__pycache__/__init__.cpython-38.pyc,sha256=5k1KW0jeSDzw6RnVPRq4CaO13Lg7M0F-pxA_gqqZ6Mg,170
10378
10381
  simo/fleet/templates/fleet/controllers_info/Button.md,sha256=GIuxqG617174NEtpPeCGVocxO4YMe7-CacgVSu_L5-E,739
10379
10382
  simo/fleet/templates/fleet/controllers_info/ENS160AirQualitySensor.md,sha256=3LSTY9YPFuVPIbVsYCAifcotrXJcOXl2k774_vo6nAE,770
10380
10383
  simo/generic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10381
10384
  simo/generic/app_widgets.py,sha256=y8W3jR76Hh26O9pPQyg2SophMbYIOtAWD33MPKbB8Mg,856
10382
10385
  simo/generic/base_types.py,sha256=u3SlfpNYaCwkVBwomWgso4ODzL71ay9MhiAW-bxgnDU,341
10383
- simo/generic/controllers.py,sha256=Y-juxa7OSz24XYysFe785DCHPlbGeEurGTkK471S3iI,46123
10384
- simo/generic/forms.py,sha256=ikSwn6hg6m4XoaWyZNydw9e5aoFRHxbeM8tWsyYlen4,22993
10385
- simo/generic/gateways.py,sha256=SBQPlcU2oNCWzRQ1H1XWQ11O2E3zWDNImXlWNDzKg-Y,15865
10386
+ simo/generic/controllers.py,sha256=gqL1Evt-8Cq8MeHG-eV8aCtQ4St_gylE52wnXwOQUWU,46285
10387
+ simo/generic/forms.py,sha256=AIY9w8wNq2-Jwo2ekq2yjdSTZzqDFr70ZDcmT4kaU4Q,24381
10388
+ simo/generic/gateways.py,sha256=Dm1Yb7JtSbqeo4AIbsHvFK3Og7ZCWHw71BZY8wwAkWY,17466
10386
10389
  simo/generic/models.py,sha256=Adq7ipWK-renxJlNW-SZnAq2oGEOwKx8EdUWaKnfcVQ,7597
10387
10390
  simo/generic/routing.py,sha256=elQVZmgnPiieEuti4sJ7zITk1hlRxpgbotcutJJgC60,228
10388
10391
  simo/generic/socket_consumers.py,sha256=pyiqzfGxSKBNqfrfEJ_kCU0UbSC28XnvDn6QjKkbqyY,1767
10389
10392
  simo/generic/__pycache__/__init__.cpython-38.pyc,sha256=mLu54WS9KIl-pHwVCBKpsDFIlOqml--JsOVzAUHg6cU,161
10390
10393
  simo/generic/__pycache__/app_widgets.cpython-38.pyc,sha256=D9b13pbMlirgHmjDnQhfLIDGSVINoSouHb4SWOeCRrs,1642
10391
10394
  simo/generic/__pycache__/base_types.cpython-38.pyc,sha256=aV5NdIuvXR-ItKpI__MwcyPZHD6Z882TFdgYkPCkr1I,493
10392
- simo/generic/__pycache__/controllers.cpython-38.pyc,sha256=tRYKQsZuWBBKXbfd32BGz3AsV4Jz4y-KsEkumoRP_T4,30195
10393
- simo/generic/__pycache__/forms.cpython-38.pyc,sha256=at1PQBgwr11YzSiTP92v3nyz1G59oKhu7vTa0sK0zUE,17289
10394
- simo/generic/__pycache__/gateways.cpython-38.pyc,sha256=7Y190Iylo2X2AykcKrKzVllfDmW02B5suRMlcS0y6KQ,11712
10395
+ simo/generic/__pycache__/controllers.cpython-38.pyc,sha256=jJjwKVaDYyazrRGNjUFoY74nr_jX_DEnsC9KjyxZCgc,30427
10396
+ simo/generic/__pycache__/forms.cpython-38.pyc,sha256=rNLPZ5K45hNf4Hx7QvcNFkIdUrX1CSltDA-TwkDnbUs,18460
10397
+ simo/generic/__pycache__/gateways.cpython-38.pyc,sha256=ZoHZv3_kjOqfFqzs4MmgZ22-u1d3mbBO9PmBycDVKU0,12823
10395
10398
  simo/generic/__pycache__/models.cpython-38.pyc,sha256=MZpum7syAFxuulf47K7gtUlJJ7xRD-IBUBAwUM1ZRnw,5825
10396
10399
  simo/generic/__pycache__/routing.cpython-38.pyc,sha256=xtxTUTBTdivzFyA5Wh7k-hUj1WDO_FiRq6HYXdbr9Ks,382
10397
- simo/generic/__pycache__/socket_consumers.cpython-38.pyc,sha256=qJO5kvQLWhsQDOr1AtAtsAybuRWioxSkQei3Pc7rdP0,1737
10400
+ simo/generic/__pycache__/socket_consumers.cpython-38.pyc,sha256=FaVCf_uJI2uwj1Zz-jwsOXou65oV9gFCIB8e-YKquRk,1662
10398
10401
  simo/generic/migrations/0001_initial.py,sha256=7FpPcfpRU5ya0b8s2KbxR5a3npf92YruvZltUybjzys,676
10399
10402
  simo/generic/migrations/0002_auto_20241126_0726.py,sha256=SX38JwP732QooOm5HM1Xo7Th_Mv_6YZloT3eozULOhs,922
10400
10403
  simo/generic/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -10427,33 +10430,38 @@ simo/generic/templates/admin/controller_widgets/weather.html,sha256=84SESQBhhzNU
10427
10430
  simo/generic/templates/generic/controllers_info/dummy.md,sha256=DcdkpYXpK7sroINukZZPUQs9uekN9kkE7p5hfnArgFo,147
10428
10431
  simo/generic/templates/generic/controllers_info/stateselect.md,sha256=T0w3vJg02W3RMSsljN1EPRnkVaeRW5acSZaSq9FYvZw,135
10429
10432
  simo/multimedia/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10430
- simo/multimedia/admin.py,sha256=GgXiKTLfi3omjBurU-bKgneJRK-tAeiR8o2jo3zD7zs,1002
10433
+ simo/multimedia/admin.py,sha256=S7WYYTwsd73WVU60MDVsheyRH9bDnsMZezV_kPJhYZ0,1031
10431
10434
  simo/multimedia/api.py,sha256=mZ5BTggWdc_kL8P70JGC3rTCiZKPnxWYoyNcAQkFnX4,285
10432
10435
  simo/multimedia/app_widgets.py,sha256=g_IPx5bNmIS6JbaXXDCzYZYV2KVKAiYvWjH4oI30lWM,331
10436
+ simo/multimedia/auto_urls.py,sha256=9FUwlifvAM-AJbAVK6ZCjsQdAZfREuf_sf-kSYjMu7I,204
10433
10437
  simo/multimedia/base_types.py,sha256=dAP7_uh_b3A03yXBJZyQdRFucKIro4_RkIZ5yOaWXVE,151
10434
- simo/multimedia/controllers.py,sha256=A_kSKOVRJIiGvEl4CzpqjcWt4v-JsDQNQJ8ylrgplCc,3368
10438
+ simo/multimedia/controllers.py,sha256=pLrJew-WJloR7MGO6hlnIBCAalezasmHLcp1v0vBTWA,3539
10435
10439
  simo/multimedia/forms.py,sha256=oMCVUXRNiESrY3w_uBLRRgjMjx8BrmNeVglzorA9QtY,239
10436
- simo/multimedia/models.py,sha256=5aWGLWDdCkekGAOGZIdanvX1l6ULnhgJN4JAvDZT4nQ,734
10437
- simo/multimedia/requirements.txt,sha256=QeIhjf1RfNGCYn_WZm3VuinPI2wK31WEJPbCxRWxssY,28
10440
+ simo/multimedia/models.py,sha256=qUQaADxFegVMbcEdjyP72UCq5kkqJ5ZSe2lu9qyoom8,1169
10438
10441
  simo/multimedia/serializers.py,sha256=9DRGsJVJLKdqmOLiVHMY06bTTYxpABhDy1JB_klzsBw,383
10442
+ simo/multimedia/views.py,sha256=oFhtJQJT5-XRtzoIKo7SWqLydCB9AWzcj2a-kQ5P7dw,558
10439
10443
  simo/multimedia/__pycache__/__init__.cpython-38.pyc,sha256=BOLHOifu6r_MuWjddGcQVsYkqYlkmHvPQIcx3selLIk,164
10440
- simo/multimedia/__pycache__/admin.cpython-38.pyc,sha256=kIVA9sfSgBYNMC64CaSDXLl4ZPYwFp1xjrrC55XCrbw,1353
10444
+ simo/multimedia/__pycache__/admin.cpython-38.pyc,sha256=xIax_AtzQlJiQmEBG5xuhJz2-Ztf40e26AQs5oBISfw,1309
10441
10445
  simo/multimedia/__pycache__/api.cpython-38.pyc,sha256=lFGEB74vgyEM98B42wwcN9WvH8FAupIiSY0SwEBd5Dw,605
10442
10446
  simo/multimedia/__pycache__/app_widgets.cpython-38.pyc,sha256=6pr3fz21tQ5ReC9SJ8VzheUZ0hpxDIClB0SA8YCwcPk,730
10447
+ simo/multimedia/__pycache__/auto_urls.cpython-38.pyc,sha256=JNI3pZNFC3NoJV4cxsy-Oe6Vc6TA9fpPU63hjlXWINE,357
10443
10448
  simo/multimedia/__pycache__/base_types.cpython-38.pyc,sha256=c4nmNziLs4RB3MAnxltn3W5XNW6PM5_vK_mm3Yvy42Y,324
10444
- simo/multimedia/__pycache__/controllers.cpython-38.pyc,sha256=C9stIL6FgGiFOxMy76QemVvc7wshVEsPxfa0wCtG4PQ,3464
10449
+ simo/multimedia/__pycache__/controllers.cpython-38.pyc,sha256=aeAYsu7v1HH-Or9IDq41TI5SRMakuQYIIR1uwyci2zI,5011
10445
10450
  simo/multimedia/__pycache__/forms.cpython-38.pyc,sha256=99h7Yj2jim3QOrqej00wiPufrCF3F--RoYvwa6lzhPI,697
10446
- simo/multimedia/__pycache__/models.cpython-38.pyc,sha256=2LViRCOULb9g_k3wZvrODVIuj-iFgfwoWXyg-mAx8Ys,1195
10451
+ simo/multimedia/__pycache__/models.cpython-38.pyc,sha256=LmQ_XMA--wvLq11xYp24nU-y-kbx17iEZ10y9twCMJ0,1662
10447
10452
  simo/multimedia/__pycache__/serializers.cpython-38.pyc,sha256=n86txYSrkmN0Xlrr8dMwKSY7rEzMc1iovepCZi_Fcw8,886
10453
+ simo/multimedia/__pycache__/views.cpython-38.pyc,sha256=yq5Hi2HrbSNr8MP4uUZO7_GMeVgqg21R6yFe85zNNhM,899
10448
10454
  simo/multimedia/migrations/0001_initial.py,sha256=YxTbmI-aF9OZUkFPnCNqV3t3r5nSJg59pKcTvm-ZYbU,610
10449
10455
  simo/multimedia/migrations/0002_sound_length.py,sha256=XpPemInQwL1QsiqhUPkdBPBYI2E5gDPyC_1vhyImq5M,471
10450
10456
  simo/multimedia/migrations/0003_alter_sound_length.py,sha256=tt1OJVkrCo6Ybws1mGobdvTP5o68P3-cHZdGG_PeZwU,442
10451
10457
  simo/multimedia/migrations/0004_auto_20231023_1055.py,sha256=ilH1o_wxkK5QDjcPEEDhoHWx2GcjZWLG6XjlUfQRmhI,718
10458
+ simo/multimedia/migrations/0005_remove_sound_slug_sound_date_uploaded.py,sha256=SzkgIhCIEVMSmxVwGHQfggdsX4wWybvskqVx-vVp2Qs,602
10452
10459
  simo/multimedia/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10453
10460
  simo/multimedia/migrations/__pycache__/0001_initial.cpython-38.pyc,sha256=slsqUkj8A11Oj5bYJ1s5dS60Qx4H9k_oRZ3HaAhuho4,760
10454
10461
  simo/multimedia/migrations/__pycache__/0002_sound_length.cpython-38.pyc,sha256=5Qh_yhupAkn6xcxP_T6KhA2__l6FdQmQb2Y0dzqq33s,678
10455
10462
  simo/multimedia/migrations/__pycache__/0003_alter_sound_length.cpython-38.pyc,sha256=dkdqLx_cWLAxxVCUxuuAX39iTv7G5kG-PhJ40HsPzx8,669
10456
10463
  simo/multimedia/migrations/__pycache__/0004_auto_20231023_1055.cpython-38.pyc,sha256=tX6x1gNeVcUHcCFvisP9Z2jPBE029TJG632fvtvUjV8,892
10464
+ simo/multimedia/migrations/__pycache__/0005_remove_sound_slug_sound_date_uploaded.cpython-38.pyc,sha256=vpu4EfpVp5rYRKYEVn4vDylgpeasD31ZV62sNR_HXhY,801
10457
10465
  simo/multimedia/migrations/__pycache__/__init__.cpython-38.pyc,sha256=mCgSiQBphL85imdWyTi9-4zBDYF6HfXbhB0ycSPVVuA,175
10458
10466
  simo/multimedia/templates/admin/controller_widgets/player.html,sha256=v4PFoL37-C7L6ILZI6yF5ff9iO3EnEs5kXbRayaHy0s,358
10459
10467
  simo/notifications/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -10467,7 +10475,7 @@ simo/notifications/__pycache__/admin.cpython-38.pyc,sha256=MScNrtVM1wavefsPfxy0A
10467
10475
  simo/notifications/__pycache__/api.cpython-38.pyc,sha256=eQmCHwTnr4zQ3ZY_SKZVCryiSEnzmyIjHQM13pZ5LT0,2071
10468
10476
  simo/notifications/__pycache__/models.cpython-38.pyc,sha256=PoqLuOnlaAWQQ-20AtqhvAlLSkakPmdn7J7wGvHNW3g,2449
10469
10477
  simo/notifications/__pycache__/serializers.cpython-38.pyc,sha256=7-eRGKYuQ4g1SpKOMpz17SIiu1HmaMoYv-cJbaO9QGA,1028
10470
- simo/notifications/__pycache__/utils.cpython-38.pyc,sha256=6Tq7VkW-pZLzWzcxPtBU9MDFZLO7iLY8-ygyefoJ5OQ,1529
10478
+ simo/notifications/__pycache__/utils.cpython-38.pyc,sha256=4ZnI-pmWji84EXBkPrl4ir1kGjfanO4bH5--bLNRxCA,1648
10471
10479
  simo/notifications/migrations/0001_initial.py,sha256=Zh69AQ-EKlQKfqfnMDVRcxvo1MxRY-TFLCdnNcgqi6g,2003
10472
10480
  simo/notifications/migrations/0002_notification_instance.py,sha256=B3msbMeKvsuq-V7gvRADRjj5PFLayhi3pQvHZjqzO5g,563
10473
10481
  simo/notifications/migrations/0003_alter_notification_instance.py,sha256=CXrOkdbXYwlEK5O8iEjDzrocAP5WQ9fQ-0_TcT4VgEA,609
@@ -10502,7 +10510,7 @@ simo/users/__pycache__/auto_urls.cpython-38.pyc,sha256=bc6BOgghLdMzUroGMj7eB6YX9
10502
10510
  simo/users/__pycache__/dynamic_settings.cpython-38.pyc,sha256=6F8JBjZkHykySnmZjNEzjS0ijbmPdcp9yUAZ5kqq_Fo,864
10503
10511
  simo/users/__pycache__/managers.cpython-38.pyc,sha256=O0Y8ABp42RAosrbODmYsPMaj9AyOPyJ-aqzuO0Qpi2s,679
10504
10512
  simo/users/__pycache__/middleware.cpython-38.pyc,sha256=Tj4nVEAvxEW3xA63fBRiJWRJpz_M848ZOqbHioc_IPE,1149
10505
- simo/users/__pycache__/models.cpython-38.pyc,sha256=UH-lceJI5fYxYn85dIUJ8HYESwb9irIPkvu_d-vGSkA,17777
10513
+ simo/users/__pycache__/models.cpython-38.pyc,sha256=qNtAn_eWVmRTWhTxN8GtCc549dcJsTdaF7Uk19yNMgg,18330
10506
10514
  simo/users/__pycache__/permissions.cpython-38.pyc,sha256=ez5NxoL_JUeeH6GsKhvFreuA3FCBgGf9floSypdXUtM,633
10507
10515
  simo/users/__pycache__/serializers.cpython-38.pyc,sha256=Dy8RAcwNkNSXoJHvLp8fozURyHCtucqpSPyqZtbnMZc,3732
10508
10516
  simo/users/__pycache__/sso_urls.cpython-38.pyc,sha256=uAwDozpOmrhUald-8tOHANILXkH7-TI8fNYXOtPkSY8,402
@@ -10608,9 +10616,9 @@ simo/users/templates/invitations/expired_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCe
10608
10616
  simo/users/templates/invitations/expired_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10609
10617
  simo/users/templates/invitations/taken_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10610
10618
  simo/users/templates/invitations/taken_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10611
- simo-2.7.10.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10612
- simo-2.7.10.dist-info/METADATA,sha256=FMpBbEAAJ0ZOZX4tmR9O9f-8ytqI4l4BHTCYUWAELbA,1953
10613
- simo-2.7.10.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
10614
- simo-2.7.10.dist-info/entry_points.txt,sha256=S9PwnUYmTSW7681GKDCxUbL0leRJIaRk6fDQIKgbZBA,135
10615
- simo-2.7.10.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10616
- simo-2.7.10.dist-info/RECORD,,
10619
+ simo-2.7.11.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
10620
+ simo-2.7.11.dist-info/METADATA,sha256=Jf5swgKHP4RoINsccaxIEMqjQSMRULyR-3IVDej8sq0,1953
10621
+ simo-2.7.11.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
10622
+ simo-2.7.11.dist-info/entry_points.txt,sha256=S9PwnUYmTSW7681GKDCxUbL0leRJIaRk6fDQIKgbZBA,135
10623
+ simo-2.7.11.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
10624
+ simo-2.7.11.dist-info/RECORD,,
@@ -1,2 +0,0 @@
1
- numpy>=1.24
2
- librosa>=0.10.1
File without changes