simo 2.5.8__py3-none-any.whl → 2.5.9__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.
- simo/core/__pycache__/form_fields.cpython-38.pyc +0 -0
- simo/core/__pycache__/forms.cpython-38.pyc +0 -0
- simo/core/__pycache__/models.cpython-38.pyc +0 -0
- simo/core/__pycache__/serializers.cpython-38.pyc +0 -0
- simo/core/__pycache__/tasks.cpython-38.pyc +0 -0
- simo/core/form_fields.py +8 -7
- simo/core/forms.py +13 -25
- simo/core/models.py +2 -1
- simo/core/serializers.py +3 -2
- simo/core/tasks.py +0 -1
- simo/fleet/__pycache__/forms.cpython-38.pyc +0 -0
- simo/fleet/forms.py +28 -35
- simo/generic/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/generic/__pycache__/forms.cpython-38.pyc +0 -0
- simo/generic/controllers.py +17 -14
- simo/generic/forms.py +84 -123
- simo/users/__pycache__/api.cpython-38.pyc +0 -0
- simo/users/api.py +1 -15
- {simo-2.5.8.dist-info → simo-2.5.9.dist-info}/METADATA +1 -1
- {simo-2.5.8.dist-info → simo-2.5.9.dist-info}/RECORD +24 -24
- {simo-2.5.8.dist-info → simo-2.5.9.dist-info}/LICENSE.md +0 -0
- {simo-2.5.8.dist-info → simo-2.5.9.dist-info}/WHEEL +0 -0
- {simo-2.5.8.dist-info → simo-2.5.9.dist-info}/entry_points.txt +0 -0
- {simo-2.5.8.dist-info → simo-2.5.9.dist-info}/top_level.txt +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
simo/core/form_fields.py
CHANGED
|
@@ -26,10 +26,11 @@ class LazyChoicesMixin:
|
|
|
26
26
|
return obj
|
|
27
27
|
|
|
28
28
|
def optgroups(self, name, value, attrs=None):
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
if len(value) == 2:
|
|
30
|
+
for (val, display) in self.choices:
|
|
31
|
+
if val == value[0]:
|
|
32
|
+
self.choices = [(val, display)]
|
|
33
|
+
break
|
|
33
34
|
return super().optgroups(name, value, attrs)
|
|
34
35
|
|
|
35
36
|
|
|
@@ -67,7 +68,7 @@ class Select2ListChoiceField(Select2ListMixin, forms.ChoiceField):
|
|
|
67
68
|
|
|
68
69
|
class Select2MultipleMixin:
|
|
69
70
|
|
|
70
|
-
def __init__(self, url, forward=None, *args, **kwargs):
|
|
71
|
+
def __init__(self, url=None, forward=None, *args, **kwargs):
|
|
71
72
|
self.url = url
|
|
72
73
|
self.forward = []
|
|
73
74
|
if forward:
|
|
@@ -76,9 +77,9 @@ class Select2MultipleMixin:
|
|
|
76
77
|
widget = Select2Multiple(
|
|
77
78
|
url=url, forward=forward, attrs={'data-html': True}
|
|
78
79
|
)
|
|
79
|
-
widget.choices = kwargs.pop('choices',
|
|
80
|
+
widget.choices = kwargs.pop('choices', [])
|
|
80
81
|
|
|
81
|
-
super().__init__(widget, *args, **kwargs)
|
|
82
|
+
super().__init__(widget=widget, *args, **kwargs)
|
|
82
83
|
|
|
83
84
|
|
|
84
85
|
class Select2ModelMultipleChoiceField(
|
simo/core/forms.py
CHANGED
|
@@ -13,6 +13,7 @@ from dal import autocomplete
|
|
|
13
13
|
from .models import (
|
|
14
14
|
Icon, Category, Gateway, Component
|
|
15
15
|
)
|
|
16
|
+
from .form_fields import Select2ModelMultipleChoiceField
|
|
16
17
|
from .widgets import SVGFileWidget, LogOutputWidget
|
|
17
18
|
from .utils.formsets import FormsetField
|
|
18
19
|
from .utils.validators import validate_slaves
|
|
@@ -459,19 +460,17 @@ class MultiSensorConfigForm(BaseComponentForm):
|
|
|
459
460
|
|
|
460
461
|
|
|
461
462
|
class SwitchForm(BaseComponentForm):
|
|
462
|
-
slaves =
|
|
463
|
-
required=False,
|
|
463
|
+
slaves = Select2ModelMultipleChoiceField(
|
|
464
464
|
queryset=Component.objects.filter(
|
|
465
465
|
base_type__in=(
|
|
466
466
|
'dimmer', 'switch', 'blinds', 'script'
|
|
467
467
|
)
|
|
468
468
|
),
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
)
|
|
469
|
+
url='autocomplete-component',
|
|
470
|
+
forward=(forward.Const(
|
|
471
|
+
['dimmer', 'switch', 'blinds', 'script'], 'base_type'),
|
|
472
|
+
),
|
|
473
|
+
required=False
|
|
475
474
|
)
|
|
476
475
|
|
|
477
476
|
def __init__(self, *args, **kwargs):
|
|
@@ -596,29 +595,18 @@ class DimmerConfigForm(BaseComponentForm):
|
|
|
596
595
|
initial=0.0, help_text="Minimum component value."
|
|
597
596
|
)
|
|
598
597
|
max = forms.FloatField(
|
|
599
|
-
initial=
|
|
598
|
+
initial=100.0, help_text="Maximum component value."
|
|
600
599
|
)
|
|
601
600
|
value_units = forms.CharField(required=False)
|
|
602
|
-
|
|
603
|
-
label=_("Inverse dimmer signal"), required=False
|
|
604
|
-
)
|
|
605
|
-
slaves = forms.ModelMultipleChoiceField(
|
|
606
|
-
required=False,
|
|
601
|
+
slaves = Select2ModelMultipleChoiceField(
|
|
607
602
|
queryset=Component.objects.filter(
|
|
608
603
|
base_type__in='dimmer',
|
|
609
604
|
),
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
)
|
|
605
|
+
url='autocomplete-component',
|
|
606
|
+
forward=(forward.Const(['dimmer', ], 'base_type'),),
|
|
607
|
+
required=False
|
|
614
608
|
)
|
|
615
609
|
|
|
616
|
-
def __init__(self, *args, **kwargs):
|
|
617
|
-
super().__init__(*args, **kwargs)
|
|
618
|
-
self.fields['value_units'].initial = self.controller.default_value_units
|
|
619
|
-
if self.instance.pk:
|
|
620
|
-
self.fields['slaves'].initial = self.instance.slaves.all()
|
|
621
|
-
|
|
622
610
|
def clean_slaves(self):
|
|
623
611
|
if not self.cleaned_data['slaves'] or not self.instance:
|
|
624
612
|
return self.cleaned_data['slaves']
|
|
@@ -626,7 +614,7 @@ class DimmerConfigForm(BaseComponentForm):
|
|
|
626
614
|
|
|
627
615
|
def save(self, commit=True):
|
|
628
616
|
obj = super().save(commit=commit)
|
|
629
|
-
if commit:
|
|
617
|
+
if commit and 'slaves' in self.cleaned_data:
|
|
630
618
|
obj.slaves.set(self.cleaned_data['slaves'])
|
|
631
619
|
return obj
|
|
632
620
|
|
simo/core/models.py
CHANGED
|
@@ -78,7 +78,8 @@ class Instance(DirtyFieldsMixin, models.Model, SimoAdminMixin):
|
|
|
78
78
|
)
|
|
79
79
|
location = PlainLocationField(null=True, blank=True, zoom=7)
|
|
80
80
|
timezone = models.CharField(
|
|
81
|
-
max_length=50, db_index=True, choices=ALL_TIMEZONES_CHOICES
|
|
81
|
+
max_length=50, db_index=True, choices=ALL_TIMEZONES_CHOICES,
|
|
82
|
+
default='UTC'
|
|
82
83
|
)
|
|
83
84
|
units_of_measure = models.CharField(
|
|
84
85
|
max_length=100, default='metric',
|
simo/core/serializers.py
CHANGED
|
@@ -13,8 +13,8 @@ from rest_framework.relations import Hyperlink, PKOnlyObject
|
|
|
13
13
|
from actstream.models import Action
|
|
14
14
|
from simo.core.forms import HiddenField, FormsetField
|
|
15
15
|
from simo.core.form_fields import (
|
|
16
|
-
Select2ListChoiceField,
|
|
17
|
-
|
|
16
|
+
Select2ListChoiceField, Select2ModelChoiceField,
|
|
17
|
+
Select2ListMultipleChoiceField, Select2ModelMultipleChoiceField
|
|
18
18
|
)
|
|
19
19
|
from simo.core.models import Component
|
|
20
20
|
from rest_framework.relations import PrimaryKeyRelatedField, ManyRelatedField
|
|
@@ -273,6 +273,7 @@ class ComponentSerializer(FormSerializer):
|
|
|
273
273
|
Select2ModelChoiceField: ComponentPrimaryKeyRelatedField,
|
|
274
274
|
forms.ModelMultipleChoiceField: ComponentManyToManyRelatedField,
|
|
275
275
|
Select2ListMultipleChoiceField: ComponentManyToManyRelatedField,
|
|
276
|
+
Select2ModelMultipleChoiceField: ComponentManyToManyRelatedField,
|
|
276
277
|
FormsetField: ComponentFormsetField,
|
|
277
278
|
}
|
|
278
279
|
|
simo/core/tasks.py
CHANGED
|
@@ -403,7 +403,6 @@ def restart_postgresql():
|
|
|
403
403
|
|
|
404
404
|
@celery_app.task
|
|
405
405
|
def low_battery_notifications():
|
|
406
|
-
from simo.users.models import User
|
|
407
406
|
from simo.notifications.utils import notify_users
|
|
408
407
|
for instance in Instance.objects.filter(is_active=True):
|
|
409
408
|
timezone.activate(instance.timezone)
|
|
Binary file
|
simo/fleet/forms.py
CHANGED
|
@@ -8,7 +8,7 @@ from dal import autocomplete
|
|
|
8
8
|
from dal import forward
|
|
9
9
|
from simo.core.models import Component
|
|
10
10
|
from simo.core.forms import (
|
|
11
|
-
BaseComponentForm, ValueLimitForm, NumericSensorForm
|
|
11
|
+
BaseComponentForm, ValueLimitForm, NumericSensorForm
|
|
12
12
|
)
|
|
13
13
|
from simo.core.utils.formsets import FormsetField
|
|
14
14
|
from simo.core.widgets import LogOutputWidget
|
|
@@ -16,7 +16,10 @@ from simo.core.utils.easing import EASING_CHOICES
|
|
|
16
16
|
from simo.core.utils.validators import validate_slaves
|
|
17
17
|
from simo.core.utils.admin import AdminFormActionForm
|
|
18
18
|
from simo.core.events import GatewayObjectCommand
|
|
19
|
-
from simo.core.form_fields import
|
|
19
|
+
from simo.core.form_fields import (
|
|
20
|
+
Select2ModelChoiceField, Select2ListChoiceField,
|
|
21
|
+
Select2ModelMultipleChoiceField
|
|
22
|
+
)
|
|
20
23
|
from .models import Colonel, ColonelPin, Interface
|
|
21
24
|
from .utils import INTERFACES_PINS_MAP, get_all_control_input_choices
|
|
22
25
|
|
|
@@ -654,19 +657,16 @@ class ColonelSwitchConfigForm(ColonelComponentForm):
|
|
|
654
657
|
"given amount of seconds after every turn on event."
|
|
655
658
|
)
|
|
656
659
|
inverse = forms.BooleanField(required=False)
|
|
657
|
-
slaves =
|
|
658
|
-
required=False,
|
|
660
|
+
slaves = Select2ModelMultipleChoiceField(
|
|
659
661
|
queryset=Component.objects.filter(
|
|
660
662
|
base_type__in=(
|
|
661
663
|
'dimmer', 'switch', 'blinds', 'script'
|
|
662
664
|
)
|
|
663
665
|
),
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
forward
|
|
667
|
-
|
|
668
|
-
)
|
|
669
|
-
)
|
|
666
|
+
url='autocomplete-component',
|
|
667
|
+
forward=[
|
|
668
|
+
forward.Const(['dimmer', 'switch', 'blinds', 'script'], 'base_type')
|
|
669
|
+
]
|
|
670
670
|
)
|
|
671
671
|
|
|
672
672
|
controls = FormsetField(
|
|
@@ -775,15 +775,13 @@ class ColonelPWMOutputConfigForm(ColonelComponentForm):
|
|
|
775
775
|
help_text="ON value when used with toggle switch"
|
|
776
776
|
)
|
|
777
777
|
|
|
778
|
-
slaves =
|
|
779
|
-
required=False,
|
|
778
|
+
slaves = Select2ModelMultipleChoiceField(
|
|
780
779
|
queryset=Component.objects.filter(
|
|
781
780
|
base_type__in=('dimmer', ),
|
|
782
781
|
),
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
)
|
|
782
|
+
url='autocomplete-component',
|
|
783
|
+
forward=(forward.Const(['dimmer', ], 'base_type'),),
|
|
784
|
+
required=False
|
|
787
785
|
)
|
|
788
786
|
controls = FormsetField(
|
|
789
787
|
formset_factory(
|
|
@@ -1412,16 +1410,13 @@ class BurglarSmokeDetectorConfigForm(ColonelComponentForm):
|
|
|
1412
1410
|
|
|
1413
1411
|
class TTLockConfigForm(ColonelComponentForm):
|
|
1414
1412
|
|
|
1415
|
-
door_sensor =
|
|
1416
|
-
Component.objects.filter(base_type='binary-sensor'),
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
forward.Const(['binary-sensor'], 'base_type'),
|
|
1423
|
-
)
|
|
1424
|
-
)
|
|
1413
|
+
door_sensor = Select2ModelChoiceField(
|
|
1414
|
+
queryset=Component.objects.filter(base_type='binary-sensor'),
|
|
1415
|
+
url='autocomplete-component',
|
|
1416
|
+
forward=(
|
|
1417
|
+
forward.Const(['binary-sensor'], 'base_type'),
|
|
1418
|
+
), required=False,
|
|
1419
|
+
help_text="Quickens up lock status reporting on open/close if provided."
|
|
1425
1420
|
)
|
|
1426
1421
|
|
|
1427
1422
|
def clean(self):
|
|
@@ -1591,18 +1586,16 @@ class DaliGearGroupForm(DALIDeviceConfigForm, BaseComponentForm):
|
|
|
1591
1586
|
help_text="If provided, group will be turned off after "
|
|
1592
1587
|
"given amount of seconds after last turn on event."
|
|
1593
1588
|
)
|
|
1594
|
-
members =
|
|
1595
|
-
Component.objects.filter(
|
|
1589
|
+
members = Select2ModelMultipleChoiceField(
|
|
1590
|
+
queryset=Component.objects.filter(
|
|
1596
1591
|
controller_uid='simo.fleet.controllers.DALILamp',
|
|
1597
1592
|
),
|
|
1598
1593
|
label="Members", required=True,
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
forward
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
),
|
|
1605
|
-
)
|
|
1594
|
+
url='autocomplete-component',
|
|
1595
|
+
forward=(
|
|
1596
|
+
forward.Const(
|
|
1597
|
+
['simo.fleet.controllers.DALILamp', ], 'controller_uid'
|
|
1598
|
+
),
|
|
1606
1599
|
)
|
|
1607
1600
|
)
|
|
1608
1601
|
on_value = forms.FloatField(
|
|
Binary file
|
|
Binary file
|
simo/generic/controllers.py
CHANGED
|
@@ -6,6 +6,7 @@ import json
|
|
|
6
6
|
import requests
|
|
7
7
|
import traceback
|
|
8
8
|
import sys
|
|
9
|
+
import random
|
|
9
10
|
from bs4 import BeautifulSoup
|
|
10
11
|
from django.core.exceptions import ValidationError
|
|
11
12
|
from django.utils import timezone
|
|
@@ -43,7 +44,7 @@ from .app_widgets import (
|
|
|
43
44
|
from .forms import (
|
|
44
45
|
ScriptConfigForm, PresenceLightingConfigForm,
|
|
45
46
|
ThermostatConfigForm, AlarmGroupConfigForm,
|
|
46
|
-
IPCameraConfigForm, WeatherForecastForm,
|
|
47
|
+
IPCameraConfigForm, WeatherForecastForm,
|
|
47
48
|
WateringConfigForm, StateSelectForm,
|
|
48
49
|
AlarmClockConfigForm
|
|
49
50
|
)
|
|
@@ -155,11 +156,17 @@ class PresenceLighting(Script):
|
|
|
155
156
|
light_org_values = {}
|
|
156
157
|
is_on = False
|
|
157
158
|
turn_off_task = None
|
|
159
|
+
last_presence = 0
|
|
158
160
|
|
|
159
161
|
def _run(self):
|
|
160
162
|
while True:
|
|
161
163
|
self._on_sensor()
|
|
162
|
-
|
|
164
|
+
hold_time = self.component.config.get('hold_time', 0) * 10
|
|
165
|
+
if self.last_presence and hold_time and (
|
|
166
|
+
time.time() - hold_time > self.last_presence
|
|
167
|
+
):
|
|
168
|
+
self._turn_it_off()
|
|
169
|
+
time.sleep(random.randint(5, 15))
|
|
163
170
|
|
|
164
171
|
def _on_sensor(self, sensor=None):
|
|
165
172
|
|
|
@@ -180,6 +187,9 @@ class PresenceLighting(Script):
|
|
|
180
187
|
else:
|
|
181
188
|
must_on = all(presence_values)
|
|
182
189
|
|
|
190
|
+
if sensor and must_on:
|
|
191
|
+
print("Presence detected!")
|
|
192
|
+
|
|
183
193
|
additional_conditions_met = True
|
|
184
194
|
for condition in self.component.config.get('conditions', []):
|
|
185
195
|
if not additional_conditions_met:
|
|
@@ -198,17 +208,13 @@ class PresenceLighting(Script):
|
|
|
198
208
|
continue
|
|
199
209
|
|
|
200
210
|
if not op(comp.value, condition['value']):
|
|
211
|
+
if sensor and must_on:
|
|
212
|
+
print(f"Condition not met: [{comp} value:{comp.value} {condition['op']} {condition['value']}]")
|
|
201
213
|
additional_conditions_met = False
|
|
202
214
|
|
|
203
|
-
if must_on and not additional_conditions_met:
|
|
204
|
-
print("Presence detected, but additional conditions not met!")
|
|
205
|
-
|
|
206
215
|
if must_on and additional_conditions_met and not self.is_on:
|
|
207
216
|
print("Turn the lights ON!")
|
|
208
217
|
self.is_on = True
|
|
209
|
-
if self.turn_off_task:
|
|
210
|
-
self.turn_off_task.cancel()
|
|
211
|
-
self.turn_off_task = None
|
|
212
218
|
self.light_org_values = {}
|
|
213
219
|
for id in self.component.config['lights']:
|
|
214
220
|
comp = Component.objects.filter(id=id).first()
|
|
@@ -224,16 +230,13 @@ class PresenceLighting(Script):
|
|
|
224
230
|
if not any(presence_values):
|
|
225
231
|
if not self.component.config.get('hold_time', 0):
|
|
226
232
|
return self._turn_it_off()
|
|
227
|
-
if not self.
|
|
228
|
-
self.
|
|
229
|
-
self.component.config['hold_time'] * 10,
|
|
230
|
-
self._turn_it_off
|
|
231
|
-
)
|
|
233
|
+
if not self.last_presence:
|
|
234
|
+
self.last_presence = time.time()
|
|
232
235
|
|
|
233
236
|
def _turn_it_off(self):
|
|
234
237
|
print("Turn the lights OFF!")
|
|
235
238
|
self.is_on = False
|
|
236
|
-
self.
|
|
239
|
+
self.last_presence = 0
|
|
237
240
|
for id in self.component.config['lights']:
|
|
238
241
|
comp = Component.objects.filter(id=id).first()
|
|
239
242
|
if not comp or not comp.controller:
|
simo/generic/forms.py
CHANGED
|
@@ -16,7 +16,10 @@ from simo.core.utils.config_values import config_to_dict
|
|
|
16
16
|
from simo.core.utils.formsets import FormsetField
|
|
17
17
|
from simo.core.utils.helpers import get_random_string
|
|
18
18
|
from simo.core.utils.form_fields import ListSelect2Widget
|
|
19
|
-
from simo.
|
|
19
|
+
from simo.core.form_fields import (
|
|
20
|
+
Select2ModelChoiceField, Select2ListChoiceField,
|
|
21
|
+
Select2ModelMultipleChoiceField
|
|
22
|
+
)
|
|
20
23
|
|
|
21
24
|
|
|
22
25
|
ACTION_METHODS = (
|
|
@@ -134,11 +137,9 @@ class ScriptConfigForm(BaseComponentForm):
|
|
|
134
137
|
|
|
135
138
|
|
|
136
139
|
class ConditionForm(forms.Form):
|
|
137
|
-
component =
|
|
138
|
-
Component.objects.all(),
|
|
139
|
-
|
|
140
|
-
url='autocomplete-component', attrs={'data-html': True},
|
|
141
|
-
),
|
|
140
|
+
component = Select2ModelChoiceField(
|
|
141
|
+
queryset=Component.objects.all(),
|
|
142
|
+
url='autocomplete-component',
|
|
142
143
|
)
|
|
143
144
|
op = forms.ChoiceField(
|
|
144
145
|
initial="==", choices=(
|
|
@@ -200,19 +201,18 @@ class ConditionForm(forms.Form):
|
|
|
200
201
|
|
|
201
202
|
|
|
202
203
|
class PresenceLightingConfigForm(BaseComponentForm):
|
|
203
|
-
lights =
|
|
204
|
-
Component.objects.filter(
|
|
204
|
+
lights = Select2ModelMultipleChoiceField(
|
|
205
|
+
queryset=Component.objects.filter(
|
|
205
206
|
base_type__in=('switch', 'dimmer', 'rgbw-light', 'rgb-light')
|
|
206
207
|
),
|
|
207
208
|
required=True,
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
forward
|
|
211
|
-
|
|
212
|
-
'base_type'),
|
|
213
|
-
)
|
|
209
|
+
url='autocomplete-component',
|
|
210
|
+
forward=(
|
|
211
|
+
forward.Const(['switch', 'dimmer', 'rgbw-light', 'rgb-light'],
|
|
212
|
+
'base_type'),
|
|
214
213
|
)
|
|
215
214
|
)
|
|
215
|
+
|
|
216
216
|
on_value = forms.IntegerField(
|
|
217
217
|
min_value=0, initial=100,
|
|
218
218
|
help_text="Value applicable for dimmers. "
|
|
@@ -223,13 +223,13 @@ class PresenceLightingConfigForm(BaseComponentForm):
|
|
|
223
223
|
(0, "0"), (1, "Original value before turning the lights on.")
|
|
224
224
|
)
|
|
225
225
|
)
|
|
226
|
-
presence_sensors =
|
|
227
|
-
Component.objects.filter(
|
|
226
|
+
presence_sensors = Select2ModelMultipleChoiceField(
|
|
227
|
+
queryset=Component.objects.filter(
|
|
228
|
+
base_type__in=('binary-sensor', 'switch')
|
|
229
|
+
),
|
|
228
230
|
required=True,
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
forward=(forward.Const(['binary-sensor'], 'base_type'),)
|
|
232
|
-
)
|
|
231
|
+
url='autocomplete-component',
|
|
232
|
+
forward=(forward.Const(['binary-sensor', 'switch'], 'base_type'),)
|
|
233
233
|
)
|
|
234
234
|
act_on = forms.TypedChoiceField(
|
|
235
235
|
coerce=int, initial=0, choices=(
|
|
@@ -263,6 +263,8 @@ class PresenceLightingConfigForm(BaseComponentForm):
|
|
|
263
263
|
widget=forms.HiddenInput, required=False
|
|
264
264
|
)
|
|
265
265
|
|
|
266
|
+
app_exclude_fields = ('alarm_category', 'code', 'log')
|
|
267
|
+
|
|
266
268
|
def __init__(self, *args, **kwargs):
|
|
267
269
|
super().__init__(*args, **kwargs)
|
|
268
270
|
self.basic_fields.extend(
|
|
@@ -280,46 +282,53 @@ class PresenceLightingConfigForm(BaseComponentForm):
|
|
|
280
282
|
)
|
|
281
283
|
)
|
|
282
284
|
|
|
285
|
+
def save(self, commit=True):
|
|
286
|
+
obj = super().save(commit)
|
|
287
|
+
if commit:
|
|
288
|
+
obj.controller.stop()
|
|
289
|
+
if self.cleaned_data.get('keep_alive') \
|
|
290
|
+
or self.cleaned_data.get('autostart'):
|
|
291
|
+
time.sleep(2)
|
|
292
|
+
obj.controller.start()
|
|
293
|
+
return obj
|
|
294
|
+
|
|
283
295
|
|
|
284
296
|
class ThermostatConfigForm(BaseComponentForm):
|
|
285
|
-
temperature_sensor =
|
|
286
|
-
Component.objects.filter(
|
|
297
|
+
temperature_sensor = Select2ModelChoiceField(
|
|
298
|
+
queryset=Component.objects.filter(
|
|
287
299
|
base_type__in=(
|
|
288
300
|
NumericSensor.base_type,
|
|
289
301
|
MultiSensor.base_type
|
|
290
302
|
)
|
|
291
303
|
),
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
forward
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
], 'base_type'),
|
|
299
|
-
)
|
|
304
|
+
url='autocomplete-component',
|
|
305
|
+
forward=(
|
|
306
|
+
forward.Const([
|
|
307
|
+
NumericSensor.base_type,
|
|
308
|
+
MultiSensor.base_type
|
|
309
|
+
], 'base_type'),
|
|
300
310
|
)
|
|
301
311
|
)
|
|
302
|
-
heater =
|
|
303
|
-
Component.objects.filter(base_type=Switch.base_type),
|
|
304
|
-
required=False,
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
)
|
|
312
|
+
heater = Select2ModelChoiceField(
|
|
313
|
+
queryset=Component.objects.filter(base_type=Switch.base_type),
|
|
314
|
+
required=False,
|
|
315
|
+
url='autocomplete-component',
|
|
316
|
+
forward=(
|
|
317
|
+
forward.Const([
|
|
318
|
+
Switch.base_type,
|
|
319
|
+
], 'base_type'),
|
|
311
320
|
)
|
|
312
321
|
)
|
|
313
|
-
cooler =
|
|
314
|
-
Component.objects.filter(base_type=Switch.base_type),
|
|
315
|
-
required=False,
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
)
|
|
322
|
+
cooler = Select2ModelChoiceField(
|
|
323
|
+
queryset=Component.objects.filter(base_type=Switch.base_type),
|
|
324
|
+
required=False,
|
|
325
|
+
url='autocomplete-component',
|
|
326
|
+
forward=(
|
|
327
|
+
forward.Const([
|
|
328
|
+
Switch.base_type,
|
|
329
|
+
], 'base_type'),
|
|
322
330
|
)
|
|
331
|
+
|
|
323
332
|
)
|
|
324
333
|
mode = forms.ChoiceField(
|
|
325
334
|
choices=(('heater', "Heater"), ('cooler', "Cooler"), ('auto', "Auto"),),
|
|
@@ -368,11 +377,9 @@ class ThermostatConfigForm(BaseComponentForm):
|
|
|
368
377
|
|
|
369
378
|
class AlarmBreachEventForm(forms.Form):
|
|
370
379
|
uid = HiddenField(required=False)
|
|
371
|
-
component =
|
|
372
|
-
Component.objects.all(),
|
|
373
|
-
|
|
374
|
-
url='autocomplete-component', attrs={'data-html': True},
|
|
375
|
-
),
|
|
380
|
+
component = Select2ModelChoiceField(
|
|
381
|
+
queryset=Component.objects.all(),
|
|
382
|
+
url='autocomplete-component',
|
|
376
383
|
)
|
|
377
384
|
breach_action = forms.ChoiceField(
|
|
378
385
|
initial='turn_on', choices=ACTION_METHODS
|
|
@@ -413,16 +420,14 @@ class AlarmBreachEventForm(forms.Form):
|
|
|
413
420
|
|
|
414
421
|
# TODO: create control widget for admin use.
|
|
415
422
|
class AlarmGroupConfigForm(BaseComponentForm):
|
|
416
|
-
components =
|
|
417
|
-
Component.objects.filter(
|
|
423
|
+
components = Select2ModelMultipleChoiceField(
|
|
424
|
+
queryset=Component.objects.filter(
|
|
418
425
|
Q(alarm_category__isnull=False) | Q(base_type='alarm-group')
|
|
419
426
|
),
|
|
420
427
|
required=True,
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
forward
|
|
424
|
-
forward.Const(['security', 'fire', 'flood', 'other'], 'alarm_category'),
|
|
425
|
-
)
|
|
428
|
+
url='autocomplete-component',
|
|
429
|
+
forward=(
|
|
430
|
+
forward.Const(['security', 'fire', 'flood', 'other'], 'alarm_category'),
|
|
426
431
|
)
|
|
427
432
|
)
|
|
428
433
|
is_main = forms.BooleanField(
|
|
@@ -439,28 +444,24 @@ class AlarmGroupConfigForm(BaseComponentForm):
|
|
|
439
444
|
),
|
|
440
445
|
help_text="Arm automatically as soon as everybody leaves.<br>"
|
|
441
446
|
)
|
|
442
|
-
arming_locks =
|
|
443
|
-
Component.objects.filter(base_type='lock'),
|
|
447
|
+
arming_locks = Select2ModelMultipleChoiceField(
|
|
448
|
+
queryset=Component.objects.filter(base_type='lock'),
|
|
444
449
|
label="Arming locks", required=False,
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
forward
|
|
448
|
-
forward.Const(['lock'], 'base_type'),
|
|
449
|
-
)
|
|
450
|
+
url='autocomplete-component',
|
|
451
|
+
forward=(
|
|
452
|
+
forward.Const(['lock'], 'base_type'),
|
|
450
453
|
),
|
|
451
454
|
help_text="Alarm group will get armed automatically whenever "
|
|
452
455
|
"any of assigned locks changes it's state to locked. <br>"
|
|
453
456
|
"If Arm on away is enabled and set to work with arming locks, "
|
|
454
457
|
"arming will take effect only after everybody leaves."
|
|
455
458
|
)
|
|
456
|
-
disarming_locks =
|
|
457
|
-
Component.objects.filter(base_type='lock'),
|
|
459
|
+
disarming_locks = Select2ModelMultipleChoiceField(
|
|
460
|
+
queryset=Component.objects.filter(base_type='lock'),
|
|
458
461
|
label="Disarming locks", required=False,
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
forward
|
|
462
|
-
forward.Const(['lock'], 'base_type'),
|
|
463
|
-
)
|
|
462
|
+
url='autocomplete-component',
|
|
463
|
+
forward=(
|
|
464
|
+
forward.Const(['lock'], 'base_type'),
|
|
464
465
|
),
|
|
465
466
|
help_text="Alarm group will be disarmed automatically whenever "
|
|
466
467
|
"any of assigned locks changes it's state to unlocked. "
|
|
@@ -584,58 +585,20 @@ class WeatherForecastForm(BaseComponentForm):
|
|
|
584
585
|
return obj
|
|
585
586
|
|
|
586
587
|
|
|
587
|
-
class GateConfigForm(BaseComponentForm):
|
|
588
|
-
open_closed_sensor = forms.ModelChoiceField(
|
|
589
|
-
Component.objects.filter(base_type=BinarySensor.base_type),
|
|
590
|
-
label="Open/Closed sensor",
|
|
591
|
-
widget=autocomplete.ModelSelect2(
|
|
592
|
-
url='autocomplete-component', attrs={'data-html': True},
|
|
593
|
-
forward=(
|
|
594
|
-
forward.Const([BinarySensor.base_type], 'base_type'),
|
|
595
|
-
)
|
|
596
|
-
)
|
|
597
|
-
)
|
|
598
|
-
action_switch = forms.ModelChoiceField(
|
|
599
|
-
Component.objects.filter(base_type=Switch.base_type),
|
|
600
|
-
widget=autocomplete.ModelSelect2(
|
|
601
|
-
url='autocomplete-component', attrs={'data-html': True},
|
|
602
|
-
forward=(
|
|
603
|
-
forward.Const([Switch.base_type], 'base_type'),
|
|
604
|
-
)
|
|
605
|
-
)
|
|
606
|
-
)
|
|
607
|
-
action_method = forms.ChoiceField(
|
|
608
|
-
required=True, choices=(
|
|
609
|
-
('click', "Click"),
|
|
610
|
-
('toggle', "Toggle"),
|
|
611
|
-
),
|
|
612
|
-
help_text="Action switch method to initiate move/stop event on "
|
|
613
|
-
"your gate."
|
|
614
|
-
)
|
|
615
|
-
gate_open_duration = forms.FloatField(
|
|
616
|
-
label='Gate open duration', min_value=0.01, max_value=360000,
|
|
617
|
-
initial=30,
|
|
618
|
-
help_text="Time in seconds it takes for your gate to go "
|
|
619
|
-
"from fully closed to fully open."
|
|
620
|
-
)
|
|
621
|
-
|
|
622
|
-
|
|
623
588
|
|
|
624
589
|
class ContourForm(forms.Form):
|
|
625
590
|
uid = forms.CharField(widget=forms.HiddenInput(), required=False)
|
|
626
591
|
color = forms.CharField(widget=forms.HiddenInput(), required=False)
|
|
627
592
|
|
|
628
593
|
name = forms.CharField()
|
|
629
|
-
switch =
|
|
630
|
-
Component.objects.filter(
|
|
594
|
+
switch = Select2ModelChoiceField(
|
|
595
|
+
queryset=Component.objects.filter(
|
|
631
596
|
base_type__in=(Switch.base_type, Dimmer.base_type)
|
|
632
597
|
),
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
forward
|
|
636
|
-
|
|
637
|
-
)
|
|
638
|
-
),
|
|
598
|
+
url='autocomplete-component',
|
|
599
|
+
forward=(
|
|
600
|
+
forward.Const([Switch.base_type], 'base_type'),
|
|
601
|
+
)
|
|
639
602
|
)
|
|
640
603
|
runtime = forms.IntegerField(
|
|
641
604
|
min_value=0,
|
|
@@ -748,11 +711,9 @@ class AlarmClockEventForm(forms.Form):
|
|
|
748
711
|
uid = HiddenField(required=False)
|
|
749
712
|
enabled = forms.BooleanField(initial=True)
|
|
750
713
|
name = forms.CharField(max_length=30)
|
|
751
|
-
component =
|
|
752
|
-
Component.objects.all(),
|
|
753
|
-
|
|
754
|
-
url='autocomplete-component', attrs={'data-html': True},
|
|
755
|
-
),
|
|
714
|
+
component = Select2ModelChoiceField(
|
|
715
|
+
queryset=Component.objects.all(),
|
|
716
|
+
url='autocomplete-component'
|
|
756
717
|
)
|
|
757
718
|
play_action = forms.ChoiceField(
|
|
758
719
|
initial='turn_on', choices=ACTION_METHODS
|
|
Binary file
|
simo/users/api.py
CHANGED
|
@@ -219,7 +219,7 @@ class UserDeviceReport(InstanceMixin, viewsets.GenericViewSet):
|
|
|
219
219
|
).exclude(id=user_device.id).update(is_primary=False)
|
|
220
220
|
user_device.save()
|
|
221
221
|
|
|
222
|
-
speed_kmh = 0
|
|
222
|
+
speed_kmh = request.data.get('speed', 0) * 3.6
|
|
223
223
|
for iu in request.user.instance_roles.filter(is_active=True):
|
|
224
224
|
if location:
|
|
225
225
|
iu.at_home = haversine_distance(
|
|
@@ -228,20 +228,6 @@ class UserDeviceReport(InstanceMixin, viewsets.GenericViewSet):
|
|
|
228
228
|
elif not relay:
|
|
229
229
|
iu.at_home = True
|
|
230
230
|
|
|
231
|
-
if user_device.last_seen_location and iu.last_seen_location \
|
|
232
|
-
and iu.last_seen > timezone.now() - datetime.timedelta(seconds=30):
|
|
233
|
-
if user_device.last_seen_location == iu.last_seen_location:
|
|
234
|
-
speed_kmh = iu.last_seen_speed_kmh
|
|
235
|
-
else:
|
|
236
|
-
seconds_passed = (timezone.now() - user_device.last_seen).seconds
|
|
237
|
-
if not seconds_passed:
|
|
238
|
-
speed_kmh = 0
|
|
239
|
-
else:
|
|
240
|
-
moved_distance = haversine_distance(
|
|
241
|
-
iu.last_seen_location, user_device.last_seen_location
|
|
242
|
-
)
|
|
243
|
-
speed_mps = moved_distance / seconds_passed
|
|
244
|
-
speed_kmh = speed_mps * 3.6
|
|
245
231
|
iu.last_seen = user_device.last_seen
|
|
246
232
|
iu.last_seen_location = user_device.last_seen_location
|
|
247
233
|
iu.last_seen_speed_kmh = speed_kmh
|
|
@@ -46,20 +46,20 @@ simo/core/controllers.py,sha256=JvXdwXD7iotA7fIjZmBVshKLQGSt9Na48FMAyHRDh84,3561
|
|
|
46
46
|
simo/core/dynamic_settings.py,sha256=bUs58XEZOCIEhg1TigR3LmYggli13KMryBZ9pC7ugAQ,1872
|
|
47
47
|
simo/core/events.py,sha256=fH6d5HcPdDqT3R7CZPdo69qTszJ23j3GJt3IFOso3WA,4757
|
|
48
48
|
simo/core/filters.py,sha256=ghtOZcrwNAkIyF5_G9Sn73NkiI71mXv0NhwCk4IyMIM,411
|
|
49
|
-
simo/core/form_fields.py,sha256=
|
|
50
|
-
simo/core/forms.py,sha256=
|
|
49
|
+
simo/core/form_fields.py,sha256=wowWocYgxkKBr0WYzpKn4UvH4ScnImus56Tg2G8OPBc,2274
|
|
50
|
+
simo/core/forms.py,sha256=O40apPH7a4qX4WdCc10A1aoAaGWOpKqmjB8d-OhEKCo,21523
|
|
51
51
|
simo/core/gateways.py,sha256=m0eS3XjVe34Dge6xtoCq16kFWCKJcdQrT0JW0REqoq8,3715
|
|
52
52
|
simo/core/loggers.py,sha256=EBdq23gTQScVfQVH-xeP90-wII2DQFDjoROAW6ggUP4,1645
|
|
53
53
|
simo/core/managers.py,sha256=n-b3I4uXzfHKTeB1VMjSaMsDUxp8FegFJwnbV1IsWQ4,3019
|
|
54
54
|
simo/core/middleware.py,sha256=hExD7Vmw7eitk0vAjOwKzkwrtuw8YxpflF92j_CA2YY,3193
|
|
55
|
-
simo/core/models.py,sha256=
|
|
55
|
+
simo/core/models.py,sha256=Z6WLvU4K-LWIXghCBwTyZAOW5n2hCFNU-pteiPnpOAQ,22617
|
|
56
56
|
simo/core/permissions.py,sha256=v0iJM4LOeYoEfMiw3OLPYio272G1aUEAg_z9Wd1q5m0,2993
|
|
57
57
|
simo/core/routing.py,sha256=X1_IHxyA-_Q7hw1udDoviVP4_FSBDl8GYETTC2zWTbY,499
|
|
58
|
-
simo/core/serializers.py,sha256=
|
|
58
|
+
simo/core/serializers.py,sha256=JoMsGoeeTfnoNSdLPMlx4ZbaFyhtw5Kh4KpbAIUovX8,20869
|
|
59
59
|
simo/core/signal_receivers.py,sha256=qCpzEUv5Bl9--K8fe08GVDmE6EBOj292YBia1TYDdSE,9267
|
|
60
60
|
simo/core/socket_consumers.py,sha256=trRZvBGTJ7xIbfdmVvn7zoiWp_qssSkMZykDrI5YQyE,9783
|
|
61
61
|
simo/core/storage.py,sha256=_5igjaoWZAiExGWFEJMElxUw55DzJG1jqFty33xe8BE,342
|
|
62
|
-
simo/core/tasks.py,sha256=
|
|
62
|
+
simo/core/tasks.py,sha256=M86_0KAK95hpfxD-mWLYtWU-6L7rHVkytlINjEdtimI,15632
|
|
63
63
|
simo/core/todos.py,sha256=eYVXfLGiapkxKK57XuviSNe3WsUYyIWZ0hgQJk7ThKo,665
|
|
64
64
|
simo/core/types.py,sha256=WJEq48mIbFi_5Alt4wxWMGXxNxUTXqfQU5koH7wqHHI,1108
|
|
65
65
|
simo/core/views.py,sha256=3SRZr00fyLQf8ja3U-9eekKt-ld5TvU1WQqUWprXfQ4,2390
|
|
@@ -79,20 +79,20 @@ simo/core/__pycache__/controllers.cpython-38.pyc,sha256=s7onEMtWmHjvTGvWXIbpvWMW
|
|
|
79
79
|
simo/core/__pycache__/dynamic_settings.cpython-38.pyc,sha256=wGpnscX1DxFpRl54MQURhjz2aD3NJohSzw9JCFnzh2Y,2384
|
|
80
80
|
simo/core/__pycache__/events.cpython-38.pyc,sha256=yip7WSyX4pUy2wJE820W4fD7iwoIWGhdHfloFb_N0R8,5257
|
|
81
81
|
simo/core/__pycache__/filters.cpython-38.pyc,sha256=VIMADCBiYhziIyRmxAyUDJluZvuZmiC4bNYWTRsGSao,721
|
|
82
|
-
simo/core/__pycache__/form_fields.cpython-38.pyc,sha256=
|
|
83
|
-
simo/core/__pycache__/forms.cpython-38.pyc,sha256=
|
|
82
|
+
simo/core/__pycache__/form_fields.cpython-38.pyc,sha256=phMdhDFRmaFDhJSnasAg8VKNP6PxkPjFqkLwEuWicIs,3465
|
|
83
|
+
simo/core/__pycache__/forms.cpython-38.pyc,sha256=vqU3No0S56V1q1RrY7y5otT-o0p-5qAiffvqECG2Gg8,17525
|
|
84
84
|
simo/core/__pycache__/gateways.cpython-38.pyc,sha256=D1ooHL-iSpQrxnD8uAl4xWFJmm-QWZfbkLiLlFOMtdU,4553
|
|
85
85
|
simo/core/__pycache__/loggers.cpython-38.pyc,sha256=Z-cdQnC6XlIonPV4Sl4E52tP4NMEdPAiHK0cFaIL7I8,1623
|
|
86
86
|
simo/core/__pycache__/managers.cpython-38.pyc,sha256=6RTIxyjOgpQGtAqcUyE2vFPS09w1V5Wmd_vOV7rHRRI,3370
|
|
87
87
|
simo/core/__pycache__/middleware.cpython-38.pyc,sha256=iOSTXSQl3sEsa-9kx_6w5zbEByRtfzJHT6XkUIYMGdE,2469
|
|
88
|
-
simo/core/__pycache__/models.cpython-38.pyc,sha256=
|
|
88
|
+
simo/core/__pycache__/models.cpython-38.pyc,sha256=Nw2Fb11EwBdMVTHCpzkY6x0hRNCqRo0RtLNlRxS1VZE,18534
|
|
89
89
|
simo/core/__pycache__/permissions.cpython-38.pyc,sha256=fH4iyqd9DdzRLEu2b621-FeM-napR0M7hzBUTHo9Q3g,2972
|
|
90
90
|
simo/core/__pycache__/routing.cpython-38.pyc,sha256=3T3FPJ8Cn99xZCGvMyg2xjl7al-Shm9CelbSpkJtNP8,599
|
|
91
|
-
simo/core/__pycache__/serializers.cpython-38.pyc,sha256=
|
|
91
|
+
simo/core/__pycache__/serializers.cpython-38.pyc,sha256=S4eNvJkMZO98FQXgNGbeKBJihG5LXdnhl0tm2tjjXTE,19266
|
|
92
92
|
simo/core/__pycache__/signal_receivers.cpython-38.pyc,sha256=lBVca6zNPVn3Ev98ekjPGzBR1MJk4xI19CyMcm4lf6A,7056
|
|
93
93
|
simo/core/__pycache__/socket_consumers.cpython-38.pyc,sha256=KqbO1cOewodVPcy0-htVefyUjCuELKV0o7fOfYqfgPc,8490
|
|
94
94
|
simo/core/__pycache__/storage.cpython-38.pyc,sha256=9R1Xu0FJDflfRXUPsqEgt0SpwiP7FGk7HaR8s8XRyI8,721
|
|
95
|
-
simo/core/__pycache__/tasks.cpython-38.pyc,sha256=
|
|
95
|
+
simo/core/__pycache__/tasks.cpython-38.pyc,sha256=rhf1RimWBxylGo8nk0orAJ4SqTsLF0OKW2TThpREp54,10472
|
|
96
96
|
simo/core/__pycache__/todos.cpython-38.pyc,sha256=lOqGZ58siHM3isoJV4r7sg8igrfE9fFd-jSfeBa0AQI,253
|
|
97
97
|
simo/core/__pycache__/views.cpython-38.pyc,sha256=K_QM967bIJeU02DJu0Dm7j8RiFDKn_TLzX77YzNkA7c,2495
|
|
98
98
|
simo/core/__pycache__/widgets.cpython-38.pyc,sha256=sR0ZeHCHrhnNDBJuRrxp3zUsfBp0xrtF0xrK2TkQv1o,3520
|
|
@@ -10222,7 +10222,7 @@ simo/fleet/auto_urls.py,sha256=UX66eR2ykMqFgfIllW-RTdjup5-FieCWl_BVm3CcXKg,702
|
|
|
10222
10222
|
simo/fleet/base_types.py,sha256=wL9RVkHr0gA7HI1wZq0pruGEIgvQqpfnCL4cC3ywsvw,102
|
|
10223
10223
|
simo/fleet/ble.py,sha256=eHA_9ABjbmH1vUVCv9hiPXQL2GZZSEVwfO0xyI1S0nI,1081
|
|
10224
10224
|
simo/fleet/controllers.py,sha256=fjri1GtCnflkkDpNqhTwy6i9CK6RDEB0Q_BtADzcG8E,29156
|
|
10225
|
-
simo/fleet/forms.py,sha256=
|
|
10225
|
+
simo/fleet/forms.py,sha256=344V3bLxXQdHiYzZYnwuKlyvc_6tVz8Z6XbBuSRN88s,62598
|
|
10226
10226
|
simo/fleet/gateways.py,sha256=lKEJW0MgaOEiNnijH50DNSVChvaUT3TA3UurcI57P8k,5677
|
|
10227
10227
|
simo/fleet/managers.py,sha256=XOpDOA9L-f_550TNSyXnJbun2EmtGz1TenVTMlUSb8E,807
|
|
10228
10228
|
simo/fleet/models.py,sha256=4ZHiT2yoUv3xnDzsMbPi0Z3DOvYzEfwYZw-jxFlqG30,16884
|
|
@@ -10239,7 +10239,7 @@ simo/fleet/__pycache__/auto_urls.cpython-38.pyc,sha256=Tc6a6BCXHjijP8U2jE2ghlJwn
|
|
|
10239
10239
|
simo/fleet/__pycache__/base_types.cpython-38.pyc,sha256=deyPwjpT6xZiFxBGFnj5b7R-lbdOTh2krgpJhrcGVhc,274
|
|
10240
10240
|
simo/fleet/__pycache__/ble.cpython-38.pyc,sha256=Nrof9w7cm4OlpFWHeVnmvvanh2_oF9oQ3TknJiV93-0,1267
|
|
10241
10241
|
simo/fleet/__pycache__/controllers.cpython-38.pyc,sha256=jtFHr_uyjCCeuidL-o-hGaf21u0fnxK_O6hTRdY6lpc,24906
|
|
10242
|
-
simo/fleet/__pycache__/forms.cpython-38.pyc,sha256=
|
|
10242
|
+
simo/fleet/__pycache__/forms.cpython-38.pyc,sha256=vGc539_6e6-Ap-qVPoHd78p2bThdVidhV8XUzW0wQu8,42296
|
|
10243
10243
|
simo/fleet/__pycache__/gateways.cpython-38.pyc,sha256=0RKVn0ndreVKhsrukqeLPSdMnRrsQ_W7yeVeBkRLfIk,5058
|
|
10244
10244
|
simo/fleet/__pycache__/managers.cpython-38.pyc,sha256=8uz-xpUiqbGDgXIZ_XRZtFb-Tju6NGxflGg-Ee4Yo6k,1310
|
|
10245
10245
|
simo/fleet/__pycache__/models.cpython-38.pyc,sha256=ePD11IHU9AoiufsuwcgaXukCGkNE0dco-JPajXB96c0,13935
|
|
@@ -10336,8 +10336,8 @@ simo/fleet/templates/fleet/controllers_info/ENS160AirQualitySensor.md,sha256=3LS
|
|
|
10336
10336
|
simo/generic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10337
10337
|
simo/generic/app_widgets.py,sha256=vwYYVRKzKZ9XTvWTfEbaH4zpoHiDIHP6qFTNgZI-xvY,979
|
|
10338
10338
|
simo/generic/base_types.py,sha256=Bvf3lv6PXx_SwtwBH7qpkwysWuloNcKNRh3LiuZf-Dc,359
|
|
10339
|
-
simo/generic/controllers.py,sha256=
|
|
10340
|
-
simo/generic/forms.py,sha256=
|
|
10339
|
+
simo/generic/controllers.py,sha256=kGYdcuU7kpbhb6QeuOo4l-W82Zldh8vpdMnJRMKCkgU,49378
|
|
10340
|
+
simo/generic/forms.py,sha256=oAvWeYzLyWiNLRnXGa2124ICRD7GpLwwsF6i6L56HCI,29044
|
|
10341
10341
|
simo/generic/gateways.py,sha256=dUzRBxVDdsx70mrCvxSQwhJr0ydQ5NtoLHoYeVLJmtA,15224
|
|
10342
10342
|
simo/generic/models.py,sha256=Adq7ipWK-renxJlNW-SZnAq2oGEOwKx8EdUWaKnfcVQ,7597
|
|
10343
10343
|
simo/generic/routing.py,sha256=elQVZmgnPiieEuti4sJ7zITk1hlRxpgbotcutJJgC60,228
|
|
@@ -10345,8 +10345,8 @@ simo/generic/socket_consumers.py,sha256=K2OjphIhKJH48BvfFfoCOyCQZ1NmXb_phs6y1IP-
|
|
|
10345
10345
|
simo/generic/__pycache__/__init__.cpython-38.pyc,sha256=mLu54WS9KIl-pHwVCBKpsDFIlOqml--JsOVzAUHg6cU,161
|
|
10346
10346
|
simo/generic/__pycache__/app_widgets.cpython-38.pyc,sha256=dt7fSf38eDA5hVUvVfpytOKoAFLQJuFffoAfqhJUKNc,1846
|
|
10347
10347
|
simo/generic/__pycache__/base_types.cpython-38.pyc,sha256=8YDxrsRFGqaeBfSF3Y1WmIGDRHGH1Ww7dSBkxRxkKyc,511
|
|
10348
|
-
simo/generic/__pycache__/controllers.cpython-38.pyc,sha256=
|
|
10349
|
-
simo/generic/__pycache__/forms.cpython-38.pyc,sha256=
|
|
10348
|
+
simo/generic/__pycache__/controllers.cpython-38.pyc,sha256=DsxStuB6A2NhZOqx2tcgMisR4Y2Tkkr30PAU45DHiok,32819
|
|
10349
|
+
simo/generic/__pycache__/forms.cpython-38.pyc,sha256=dJiITb6CUlTPS8qzBOOyRx0Xcj5vQ_xIQXnX27Pvw30,21268
|
|
10350
10350
|
simo/generic/__pycache__/gateways.cpython-38.pyc,sha256=IazhRBe-YZ9t7_wq1fULoyLnxn3frR967lAN9D7MbKY,11583
|
|
10351
10351
|
simo/generic/__pycache__/models.cpython-38.pyc,sha256=MZpum7syAFxuulf47K7gtUlJJ7xRD-IBUBAwUM1ZRnw,5825
|
|
10352
10352
|
simo/generic/__pycache__/routing.cpython-38.pyc,sha256=xtxTUTBTdivzFyA5Wh7k-hUj1WDO_FiRq6HYXdbr9Ks,382
|
|
@@ -10433,7 +10433,7 @@ simo/notifications/migrations/__pycache__/0002_notification_instance.cpython-38.
|
|
|
10433
10433
|
simo/notifications/migrations/__pycache__/__init__.cpython-38.pyc,sha256=YMBRHVon2nWDtIUbghckjnC12sIg_ykPWhV5aM0tto4,178
|
|
10434
10434
|
simo/users/__init__.py,sha256=6a7uBpCWB_DR7p54rbHusc0xvi1qfT1ZCCQGb6TiBh8,52
|
|
10435
10435
|
simo/users/admin.py,sha256=Xr7faGeupUKkpo1QLRm84OS63u-5Rf2ir_nVEaAPBZw,6839
|
|
10436
|
-
simo/users/api.py,sha256=
|
|
10436
|
+
simo/users/api.py,sha256=FdBd3D_TSrTXk1zTPtLTqppbDsf_siTvTHROjWe3Wzo,12066
|
|
10437
10437
|
simo/users/apps.py,sha256=cq0A8-U1HALEwev0TicgFhr4CAu7Icz8rwq0HfOaL4E,207
|
|
10438
10438
|
simo/users/auth_backends.py,sha256=bBSNXQJ88TRXaQxyh1aETfmOIfiDr08Jnj8rSY9sHDk,4074
|
|
10439
10439
|
simo/users/auto_urls.py,sha256=lcJvteBsbHQMJieZpDz-63tDYejLApqsW3CUnDakd7k,272
|
|
@@ -10450,7 +10450,7 @@ simo/users/utils.py,sha256=1HGSZyHRqQvdJ4RtAiZDg1juvgG8aOlrGXR7CcvsyQc,1886
|
|
|
10450
10450
|
simo/users/views.py,sha256=dOQVvmlHG7ihWKJLFUBcqKOA0UDctlMKR0pTc36JZqg,3487
|
|
10451
10451
|
simo/users/__pycache__/__init__.cpython-38.pyc,sha256=VFoDJE_SKKaPqqYaaBYd1Ndb1hjakkTo_u0EG_XJ1GM,211
|
|
10452
10452
|
simo/users/__pycache__/admin.cpython-38.pyc,sha256=tL8b3f181AjcN2dSsDUPkqjpZziEOtVzI535SbnbDzc,7793
|
|
10453
|
-
simo/users/__pycache__/api.cpython-38.pyc,sha256=
|
|
10453
|
+
simo/users/__pycache__/api.cpython-38.pyc,sha256=As8xBUdgmpVeUYCRCR5ZH0aU9vLdEXgnZL6TR3wAhrY,10200
|
|
10454
10454
|
simo/users/__pycache__/apps.cpython-38.pyc,sha256=dgbWL8CxzzISJQTmq_4IztPJ2UzykNVdqA2Ae1PmeGk,605
|
|
10455
10455
|
simo/users/__pycache__/auth_backends.cpython-38.pyc,sha256=n5nx2QSXNj2idzRcGE6bAagMN-8qxoCs580H1EFZXls,3105
|
|
10456
10456
|
simo/users/__pycache__/auto_urls.cpython-38.pyc,sha256=K-3sz2h-cEitoflSmZk1t0eUg5mQMMGLNZFREVwG7_o,430
|
|
@@ -10551,9 +10551,9 @@ simo/users/templates/invitations/expired_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCe
|
|
|
10551
10551
|
simo/users/templates/invitations/expired_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10552
10552
|
simo/users/templates/invitations/taken_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10553
10553
|
simo/users/templates/invitations/taken_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10554
|
-
simo-2.5.
|
|
10555
|
-
simo-2.5.
|
|
10556
|
-
simo-2.5.
|
|
10557
|
-
simo-2.5.
|
|
10558
|
-
simo-2.5.
|
|
10559
|
-
simo-2.5.
|
|
10554
|
+
simo-2.5.9.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
|
|
10555
|
+
simo-2.5.9.dist-info/METADATA,sha256=seKSLClAKBnMi8ejXmRi6A8gouqbRobDzkzLi4yiFvc,1923
|
|
10556
|
+
simo-2.5.9.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
|
10557
|
+
simo-2.5.9.dist-info/entry_points.txt,sha256=S9PwnUYmTSW7681GKDCxUbL0leRJIaRk6fDQIKgbZBA,135
|
|
10558
|
+
simo-2.5.9.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
|
|
10559
|
+
simo-2.5.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|