simo 2.0.37__py3-none-any.whl → 2.0.38__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__/controllers.cpython-38.pyc +0 -0
- simo/core/__pycache__/forms.cpython-38.pyc +0 -0
- simo/core/controllers.py +10 -0
- simo/core/utils/__pycache__/formsets.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/operations.cpython-38.pyc +0 -0
- simo/core/utils/formsets.py +16 -1
- simo/core/utils/operations.py +10 -0
- simo/fleet/__pycache__/forms.cpython-38.pyc +0 -0
- simo/fleet/forms.py +16 -0
- simo/generic/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/generic/__pycache__/forms.cpython-38.pyc +0 -0
- simo/generic/__pycache__/gateways.cpython-38.pyc +0 -0
- simo/generic/controllers.py +101 -1
- simo/generic/forms.py +140 -1
- simo/generic/gateways.py +16 -7
- simo/users/__pycache__/api.cpython-38.pyc +0 -0
- simo/users/__pycache__/serializers.cpython-38.pyc +0 -0
- {simo-2.0.37.dist-info → simo-2.0.38.dist-info}/METADATA +1 -1
- {simo-2.0.37.dist-info → simo-2.0.38.dist-info}/RECORD +22 -20
- {simo-2.0.37.dist-info → simo-2.0.38.dist-info}/LICENSE.md +0 -0
- {simo-2.0.37.dist-info → simo-2.0.38.dist-info}/WHEEL +0 -0
- {simo-2.0.37.dist-info → simo-2.0.38.dist-info}/top_level.txt +0 -0
|
Binary file
|
|
Binary file
|
simo/core/controllers.py
CHANGED
|
@@ -180,6 +180,16 @@ class ControllerBase(ABC):
|
|
|
180
180
|
else:
|
|
181
181
|
return self.component.change_init_by
|
|
182
182
|
|
|
183
|
+
def _string_to_vals(self, v):
|
|
184
|
+
'''
|
|
185
|
+
Convert a string containing list of values to approporiate list of component values
|
|
186
|
+
:param v:
|
|
187
|
+
:return:
|
|
188
|
+
'''
|
|
189
|
+
val_type = type(self.default_value)
|
|
190
|
+
v = str(v).strip('(').strip('[').rstrip(')').rstrip(']')
|
|
191
|
+
return [val_type(val.strip()) for val in v.split(',')]
|
|
192
|
+
|
|
183
193
|
def send(self, value):
|
|
184
194
|
self.component.refresh_from_db()
|
|
185
195
|
|
|
Binary file
|
|
Binary file
|
simo/core/utils/formsets.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import copy
|
|
2
1
|
from django import forms
|
|
3
2
|
from django.db import models
|
|
4
3
|
from django.template.loader import render_to_string
|
|
@@ -148,10 +147,21 @@ class FormsetField(forms.Field):
|
|
|
148
147
|
if formset_data.get('%s-%d-DELETE' % (prefix, i)) == 'on':
|
|
149
148
|
continue
|
|
150
149
|
form_data = {}
|
|
150
|
+
|
|
151
151
|
for field_name, field in self.formset_cls().form.declared_fields.items():
|
|
152
152
|
form_data[field_name] = formset_data.get(
|
|
153
153
|
'%s-%d-%s' % (prefix, i, field_name)
|
|
154
154
|
)
|
|
155
|
+
|
|
156
|
+
f_data = {}
|
|
157
|
+
for key, val in form_data.items():
|
|
158
|
+
f_data[f"{self.formset_cls.form.prefix}-{key}"] = val
|
|
159
|
+
form = self.formset_cls.form(f_data)
|
|
160
|
+
if form.is_valid():
|
|
161
|
+
form_data = form.cleaned_data
|
|
162
|
+
|
|
163
|
+
for field_name, field in self.formset_cls().form.declared_fields.items():
|
|
164
|
+
|
|
155
165
|
if isinstance(field, forms.models.ModelChoiceField):
|
|
156
166
|
if isinstance(form_data[field_name], models.Model):
|
|
157
167
|
form_data[field_name] = form_data[field_name].pk
|
|
@@ -168,12 +178,17 @@ class FormsetField(forms.Field):
|
|
|
168
178
|
form_data[field_name] = int(form_data[field_name])
|
|
169
179
|
except:
|
|
170
180
|
form_data[field_name] = None
|
|
181
|
+
|
|
171
182
|
if self.widget.formset.can_order:
|
|
172
183
|
form_data['order'] = int(formset_data.get(
|
|
173
184
|
'%s-%d-ORDER' % (prefix, i), 0
|
|
174
185
|
))
|
|
186
|
+
|
|
187
|
+
|
|
175
188
|
cleaned_value.append(form_data)
|
|
176
189
|
|
|
190
|
+
|
|
191
|
+
|
|
177
192
|
if self.widget.formset.can_order:
|
|
178
193
|
cleaned_value = sorted(cleaned_value, key=lambda d: d['order'])
|
|
179
194
|
for i in range(len(cleaned_value)):
|
|
Binary file
|
simo/fleet/forms.py
CHANGED
|
@@ -219,6 +219,22 @@ class ColonelBinarySensorConfigForm(ColonelComponentForm):
|
|
|
219
219
|
"Set debounce value in milliseconds, to remediate this. "
|
|
220
220
|
"50ms offers a good starting point!"
|
|
221
221
|
)
|
|
222
|
+
hold_time = forms.TypedChoiceField(
|
|
223
|
+
initial=0, coerce=int, choices=(
|
|
224
|
+
(0, "-----"),
|
|
225
|
+
(1, "10 s"), (2, "20 s"), (3, "30 s"), (4, "40 s"), (5, "50 s"),
|
|
226
|
+
(6, "1 min"), (9, "1.5 min"), (12, "2 min"), (18, "3 min"),
|
|
227
|
+
(30, "5 min"), (60, "10 min"), (120, "20 min"),
|
|
228
|
+
), required=False,
|
|
229
|
+
help_text="Holds positive value for given amount of time "
|
|
230
|
+
"after last negative value has been observed. "
|
|
231
|
+
"Super useful with regular motion detectors for controlling "
|
|
232
|
+
"lights or other means of automation."
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
def __init__(self, *args, **kwargs):
|
|
236
|
+
super().__init__(*args, **kwargs)
|
|
237
|
+
self.basic_fields.append('hold_time')
|
|
222
238
|
|
|
223
239
|
def clean(self):
|
|
224
240
|
super().clean()
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
simo/generic/controllers.py
CHANGED
|
@@ -15,6 +15,7 @@ from simo.users.utils import get_system_user
|
|
|
15
15
|
from simo.core.events import GatewayObjectCommand
|
|
16
16
|
from simo.core.models import RUN_STATUS_CHOICES_MAP, Component
|
|
17
17
|
from simo.core.utils.helpers import get_random_string
|
|
18
|
+
from simo.core.utils.operations import OPERATIONS
|
|
18
19
|
from simo.core.controllers import (
|
|
19
20
|
BEFORE_SEND, BEFORE_SET, ControllerBase,
|
|
20
21
|
BinarySensor, NumericSensor, MultiSensor, Switch, Dimmer, DimmerPlus,
|
|
@@ -35,7 +36,8 @@ from .app_widgets import (
|
|
|
35
36
|
WateringWidget, StateSelectWidget, AlarmClockWidget
|
|
36
37
|
)
|
|
37
38
|
from .forms import (
|
|
38
|
-
ScriptConfigForm,
|
|
39
|
+
ScriptConfigForm, PresenceLightingConfigForm,
|
|
40
|
+
ThermostatConfigForm, AlarmGroupConfigForm,
|
|
39
41
|
IPCameraConfigForm, WeatherForecastForm, GateConfigForm,
|
|
40
42
|
BlindsConfigForm, WateringConfigForm, StateSelectForm,
|
|
41
43
|
AlarmClockConfigForm
|
|
@@ -99,6 +101,104 @@ class Script(ControllerBase, TimerMixin):
|
|
|
99
101
|
self.send('start')
|
|
100
102
|
|
|
101
103
|
|
|
104
|
+
class PresenceLighting(Script):
|
|
105
|
+
name = _("Presence lighting")
|
|
106
|
+
config_form = PresenceLightingConfigForm
|
|
107
|
+
|
|
108
|
+
# script specific variables
|
|
109
|
+
sensors = {}
|
|
110
|
+
light_org_values = {}
|
|
111
|
+
is_on = None
|
|
112
|
+
turn_off_task = None
|
|
113
|
+
|
|
114
|
+
def _run(self):
|
|
115
|
+
while True:
|
|
116
|
+
self._on_sensor()
|
|
117
|
+
time.sleep(10)
|
|
118
|
+
|
|
119
|
+
def _on_sensor(self, sensor=None):
|
|
120
|
+
|
|
121
|
+
self.component.refresh_from_db()
|
|
122
|
+
for id in self.component.config['presence_sensors']:
|
|
123
|
+
if id not in self.sensors:
|
|
124
|
+
sensor = Component.objects.filter(id=id).first()
|
|
125
|
+
if sensor:
|
|
126
|
+
sensor.on_change(self._on_sensor)
|
|
127
|
+
self.sensors[id] = sensor
|
|
128
|
+
|
|
129
|
+
if sensor:
|
|
130
|
+
self.sensors[sensor.id] = sensor
|
|
131
|
+
|
|
132
|
+
presence_values = [s.value for id, s in self.sensors.items()]
|
|
133
|
+
if self.component.config.get('act_on', 0):
|
|
134
|
+
must_on = any(presence_values)
|
|
135
|
+
else:
|
|
136
|
+
must_on = all(presence_values)
|
|
137
|
+
|
|
138
|
+
additional_conditions_met = True
|
|
139
|
+
for condition in self.component.config.get('conditions', []):
|
|
140
|
+
if not additional_conditions_met:
|
|
141
|
+
continue
|
|
142
|
+
comp = Component.objects.filter(
|
|
143
|
+
id=condition.get('component', 0)
|
|
144
|
+
).first()
|
|
145
|
+
if not comp:
|
|
146
|
+
continue
|
|
147
|
+
op = OPERATIONS.get(condition.get('op'))
|
|
148
|
+
if not op:
|
|
149
|
+
continue
|
|
150
|
+
if condition['op'] == 'in':
|
|
151
|
+
if comp.value not in self._string_to_vals(condition['value']):
|
|
152
|
+
additional_conditions_met = False
|
|
153
|
+
continue
|
|
154
|
+
|
|
155
|
+
if not op(comp.value, condition['value']):
|
|
156
|
+
additional_conditions_met = False
|
|
157
|
+
|
|
158
|
+
if must_on and not additional_conditions_met:
|
|
159
|
+
print("Presence detected, but additional conditions not met!")
|
|
160
|
+
|
|
161
|
+
if must_on and additional_conditions_met and not self.is_on:
|
|
162
|
+
print("Turn the lights ON!")
|
|
163
|
+
self.is_on = True
|
|
164
|
+
if self.turn_off_task:
|
|
165
|
+
self.turn_off_task.cancel()
|
|
166
|
+
self.turn_off_task = None
|
|
167
|
+
self.light_org_values = {}
|
|
168
|
+
for id in self.component.config['lights']:
|
|
169
|
+
comp = Component.objects.filter(id=id).first()
|
|
170
|
+
if not comp or not comp.controller:
|
|
171
|
+
continue
|
|
172
|
+
self.light_org_values[comp.id] = comp.value
|
|
173
|
+
comp.controller.send(self.component.config['on_value'])
|
|
174
|
+
return
|
|
175
|
+
|
|
176
|
+
if self.is_on or self.is_on is None:
|
|
177
|
+
if not additional_conditions_met:
|
|
178
|
+
return self._turn_it_off()
|
|
179
|
+
if not any(presence_values):
|
|
180
|
+
if not self.component.config.get('hold_time', 0):
|
|
181
|
+
return self._turn_it_off()
|
|
182
|
+
if not self.turn_off_task:
|
|
183
|
+
self.turn_off_task = threading.Timer(
|
|
184
|
+
self.component.config['hold_time'] * 10,
|
|
185
|
+
self._turn_it_off
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
def _turn_it_off(self):
|
|
189
|
+
print("Turn the lights OFF!")
|
|
190
|
+
self.is_on = False
|
|
191
|
+
self.turn_off_task = None
|
|
192
|
+
for id in self.component.config['lights']:
|
|
193
|
+
comp = Component.objects.filter(id=id).first()
|
|
194
|
+
if not comp or not comp.controller:
|
|
195
|
+
continue
|
|
196
|
+
if self.component.config['off_value'] == 0:
|
|
197
|
+
comp.send(0)
|
|
198
|
+
else:
|
|
199
|
+
comp.send(self.light_org_values.get(comp.id, 0))
|
|
200
|
+
|
|
201
|
+
|
|
102
202
|
class Thermostat(ControllerBase):
|
|
103
203
|
name = _("Thermostat")
|
|
104
204
|
base_type = 'thermostat'
|
simo/generic/forms.py
CHANGED
|
@@ -7,7 +7,7 @@ from django.contrib.contenttypes.models import ContentType
|
|
|
7
7
|
from simo.core.forms import HiddenField, BaseComponentForm
|
|
8
8
|
from simo.core.models import Icon, Component
|
|
9
9
|
from simo.core.controllers import (
|
|
10
|
-
BinarySensor, NumericSensor, MultiSensor, Switch
|
|
10
|
+
BEFORE_SET, BinarySensor, NumericSensor, MultiSensor, Switch
|
|
11
11
|
)
|
|
12
12
|
from simo.core.widgets import PythonCode, LogOutputWidget
|
|
13
13
|
from dal import autocomplete, forward
|
|
@@ -72,6 +72,145 @@ class ScriptConfigForm(BaseComponentForm):
|
|
|
72
72
|
return fieldsets
|
|
73
73
|
|
|
74
74
|
|
|
75
|
+
class ConditionForm(forms.Form):
|
|
76
|
+
component = forms.ModelChoiceField(
|
|
77
|
+
Component.objects.all(),
|
|
78
|
+
widget=autocomplete.ModelSelect2(
|
|
79
|
+
url='autocomplete-component', attrs={'data-html': True},
|
|
80
|
+
),
|
|
81
|
+
)
|
|
82
|
+
op = forms.ChoiceField(
|
|
83
|
+
initial="==", choices=(
|
|
84
|
+
('==', "is equal to"),
|
|
85
|
+
('>', "is greather than"), ('>=', "Is greather or equal to"),
|
|
86
|
+
('<', "is lower than"), ('<=', "is lower or equal to"),
|
|
87
|
+
('in', "is one of")
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
value = forms.CharField()
|
|
91
|
+
prefix = 'breach_events'
|
|
92
|
+
|
|
93
|
+
def clean(self):
|
|
94
|
+
if not self.cleaned_data.get('component'):
|
|
95
|
+
return self.cleaned_data
|
|
96
|
+
if not self.cleaned_data.get('op'):
|
|
97
|
+
return self.cleaned_data
|
|
98
|
+
component = self.cleaned_data.get('component')
|
|
99
|
+
|
|
100
|
+
if self.cleaned_data['op'] == 'in':
|
|
101
|
+
self.cleaned_data['value'] = self.cleaned_data['value']\
|
|
102
|
+
.strip('(').strip('[').rstrip(')').rstrip(']').strip()
|
|
103
|
+
values = self.cleaned_data['value'].split(',')
|
|
104
|
+
else:
|
|
105
|
+
values = [self.cleaned_data['value']]
|
|
106
|
+
|
|
107
|
+
final_values = []
|
|
108
|
+
controller_val_type = type(component.controller.default_value)
|
|
109
|
+
for val in values:
|
|
110
|
+
val = val.strip()
|
|
111
|
+
if controller_val_type == 'bool':
|
|
112
|
+
if val.lower() in ('0', 'false', 'none', 'null'):
|
|
113
|
+
final_val = False
|
|
114
|
+
else:
|
|
115
|
+
final_val = True
|
|
116
|
+
else:
|
|
117
|
+
try:
|
|
118
|
+
final_val = controller_val_type(val)
|
|
119
|
+
except:
|
|
120
|
+
self.add_error(
|
|
121
|
+
'value', f"{val} bad value type for selected component."
|
|
122
|
+
)
|
|
123
|
+
continue
|
|
124
|
+
try:
|
|
125
|
+
component.controller._validate_val(final_val, BEFORE_SET)
|
|
126
|
+
except Exception as e:
|
|
127
|
+
self.add_error(
|
|
128
|
+
'value', f"{val} is not compatible with selected component."
|
|
129
|
+
)
|
|
130
|
+
continue
|
|
131
|
+
final_values.append(final_val)
|
|
132
|
+
|
|
133
|
+
if self.cleaned_data['op'] == 'in':
|
|
134
|
+
self.cleaned_data['value'] = ', '.join(str(v) for v in final_values)
|
|
135
|
+
else:
|
|
136
|
+
self.cleaned_data['value'] = final_values[0]
|
|
137
|
+
|
|
138
|
+
return self.cleaned_data
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class PresenceLightingConfigForm(BaseComponentForm):
|
|
142
|
+
lights = forms.ModelMultipleChoiceField(
|
|
143
|
+
Component.objects.filter(
|
|
144
|
+
base_type__in=('switch', 'dimmer', 'rgbw-light', 'rgb-light')
|
|
145
|
+
),
|
|
146
|
+
required=True,
|
|
147
|
+
widget=autocomplete.ModelSelect2Multiple(
|
|
148
|
+
url='autocomplete-component', attrs={'data-html': True},
|
|
149
|
+
forward=(
|
|
150
|
+
forward.Const(['switch', 'dimmer', 'rgbw-light', 'rgb-light'],
|
|
151
|
+
'base_type'),
|
|
152
|
+
)
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
on_value = forms.IntegerField(
|
|
156
|
+
min_value=0, initial=100,
|
|
157
|
+
help_text="Value applicable for dimmers. "
|
|
158
|
+
"Switches will receive tunrn on command."
|
|
159
|
+
)
|
|
160
|
+
off_value = forms.TypedChoiceField(
|
|
161
|
+
coerce=int, initial=1, choices=(
|
|
162
|
+
(0, "0"), (1, "Original value before turning the lights on.")
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
presence_sensors = forms.ModelMultipleChoiceField(
|
|
166
|
+
Component.objects.filter(base_type='binary-sensor'),
|
|
167
|
+
required=True,
|
|
168
|
+
widget=autocomplete.ModelSelect2Multiple(
|
|
169
|
+
url='autocomplete-component', attrs={'data-html': True},
|
|
170
|
+
forward=(forward.Const(['binary-sensor'], 'base_type'),)
|
|
171
|
+
)
|
|
172
|
+
)
|
|
173
|
+
act_on = forms.TypedChoiceField(
|
|
174
|
+
coerce=int, initial=0, choices=(
|
|
175
|
+
(0, "At least one sensor detects presence"),
|
|
176
|
+
(1, "All sensors detect presence"),
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
hold_time = forms.TypedChoiceField(
|
|
180
|
+
initial=3, coerce=int, required=False, choices=(
|
|
181
|
+
(0, '----'),
|
|
182
|
+
(1, "10 s"), (2, "20 s"), (3, "30 s"), (4, "40 s"), (5, "50 s"),
|
|
183
|
+
(6, "1 min"), (9, "1.5 min"), (12, "2 min"), (18, "3 min"),
|
|
184
|
+
(30, "5 min"), (60, "10 min"), (120, "20 min"),
|
|
185
|
+
),
|
|
186
|
+
help_text="Hold off time after last presence detector is deactivated."
|
|
187
|
+
)
|
|
188
|
+
conditions = FormsetField(
|
|
189
|
+
formset_factory(
|
|
190
|
+
ConditionForm, can_delete=True, can_order=True, extra=0
|
|
191
|
+
), label='Additional conditions'
|
|
192
|
+
)
|
|
193
|
+
log = forms.CharField(
|
|
194
|
+
widget=forms.HiddenInput, required=False
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
def __init__(self, *args, **kwargs):
|
|
198
|
+
super().__init__(*args, **kwargs)
|
|
199
|
+
if self.instance.pk:
|
|
200
|
+
prefix = get_script_prefix()
|
|
201
|
+
if prefix == '/':
|
|
202
|
+
prefix = ''
|
|
203
|
+
self.fields['log'].widget = LogOutputWidget(
|
|
204
|
+
prefix + '/ws/log/%d/%d/' % (
|
|
205
|
+
ContentType.objects.get_for_model(Component).id,
|
|
206
|
+
self.instance.id
|
|
207
|
+
)
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
|
|
75
214
|
class ThermostatConfigForm(BaseComponentForm):
|
|
76
215
|
temperature_sensor = forms.ModelChoiceField(
|
|
77
216
|
Component.objects.filter(
|
simo/generic/gateways.py
CHANGED
|
@@ -136,14 +136,22 @@ class ScriptRunHandler(multiprocessing.Process):
|
|
|
136
136
|
sys.stderr = StreamToLogger(self.logger, logging.ERROR)
|
|
137
137
|
self.component.value = 'running'
|
|
138
138
|
self.component.save(update_fields=['value'])
|
|
139
|
-
|
|
140
|
-
if
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
139
|
+
|
|
140
|
+
if hasattr(self.component.controller, '_run'):
|
|
141
|
+
def run_code():
|
|
142
|
+
self.component.controller._run()
|
|
143
|
+
else:
|
|
144
|
+
code = self.component.config.get('code')
|
|
145
|
+
def run_code():
|
|
146
|
+
exec(code, globals())
|
|
147
|
+
|
|
148
|
+
if not code:
|
|
149
|
+
self.component.value = 'finished'
|
|
150
|
+
self.component.save(update_fields=['value'])
|
|
151
|
+
return
|
|
144
152
|
print("------START-------")
|
|
145
153
|
try:
|
|
146
|
-
|
|
154
|
+
run_code()
|
|
147
155
|
except:
|
|
148
156
|
print("------ERROR------")
|
|
149
157
|
self.component.value = 'error'
|
|
@@ -259,7 +267,7 @@ class GenericGatewayHandler(BaseObjectCommandsGatewayHandler):
|
|
|
259
267
|
# as well as those who are designed to be kept alive, but
|
|
260
268
|
# got terminated unexpectedly
|
|
261
269
|
for script in Component.objects.filter(
|
|
262
|
-
|
|
270
|
+
base_type='script',
|
|
263
271
|
).filter(
|
|
264
272
|
Q(config__autostart=True) |
|
|
265
273
|
Q(value='error', config__keep_alive=True)
|
|
@@ -302,6 +310,7 @@ class GenericGatewayHandler(BaseObjectCommandsGatewayHandler):
|
|
|
302
310
|
|
|
303
311
|
if isinstance(component.controller, Script):
|
|
304
312
|
if payload.get('set_val') == 'start':
|
|
313
|
+
print("START THIS SCRIPT!!!", component)
|
|
305
314
|
self.start_script(component)
|
|
306
315
|
elif payload.get('set_val') == 'stop':
|
|
307
316
|
self.stop_script(component)
|
|
Binary file
|
|
Binary file
|
|
@@ -34,7 +34,7 @@ simo/core/auto_urls.py,sha256=0gu-IL7PHobrmKW6ksffiOkAYu-aIorykWdxRNtwGYo,1194
|
|
|
34
34
|
simo/core/autocomplete_views.py,sha256=JT5LA2_Wtr60XYSAIqaXFKFYPjrmkEf6yunXD9y2zco,4022
|
|
35
35
|
simo/core/base_types.py,sha256=yqbIZqBksrAkEuHRbt6iExwPDDy0K5II2NzRCkmOvMU,589
|
|
36
36
|
simo/core/context.py,sha256=98PXAMie43faRVBFkOG22uNpvGRNprcGhzjBFkrxaRY,1367
|
|
37
|
-
simo/core/controllers.py,sha256=
|
|
37
|
+
simo/core/controllers.py,sha256=oPyAKJjPMQFP-gz5V0aDfIxs__6v3b90DocX3R-8wjM,27639
|
|
38
38
|
simo/core/dynamic_settings.py,sha256=U2WNL96JzVXdZh0EqMPWrxqO6BaRR2Eo5KTDqz7MC4o,1943
|
|
39
39
|
simo/core/events.py,sha256=LvtonJGNyCb6HLozs4EG0WZItnDwNdtnGQ4vTcnKvUs,4438
|
|
40
40
|
simo/core/filters.py,sha256=ghtOZcrwNAkIyF5_G9Sn73NkiI71mXv0NhwCk4IyMIM,411
|
|
@@ -65,11 +65,11 @@ simo/core/__pycache__/auto_urls.cpython-38.pyc,sha256=SVl4fF0-yiq7e9gt08jIM6_rL4
|
|
|
65
65
|
simo/core/__pycache__/autocomplete_views.cpython-38.pyc,sha256=hJ6JILI1LqrAtpQMvxnLvljGdW1v1gpvBsD79vFkZ58,3972
|
|
66
66
|
simo/core/__pycache__/base_types.cpython-38.pyc,sha256=CasZJN42cK_ymoQgn5E4s8oOkuZJ18fVHCgN4GPuT7c,735
|
|
67
67
|
simo/core/__pycache__/context.cpython-38.pyc,sha256=MSZPDhqMhCpUuBJl3HCIBHZA3BntYeP8RAnQcdqAH9k,1278
|
|
68
|
-
simo/core/__pycache__/controllers.cpython-38.pyc,sha256=
|
|
68
|
+
simo/core/__pycache__/controllers.cpython-38.pyc,sha256=Rsmre6XaYAbQH4aHHs6c-9EgmEZ8SyHm9Y7FYEteTpM,24542
|
|
69
69
|
simo/core/__pycache__/dynamic_settings.cpython-38.pyc,sha256=ELu06Hub4DOidja71ybvD3ZM4HdXiyZjNJrZfnXZXNA,2476
|
|
70
70
|
simo/core/__pycache__/events.cpython-38.pyc,sha256=A1Axx-qftd1r7st7wkO3DkvTdt9-RkcJe5KJhpzJVk8,5109
|
|
71
71
|
simo/core/__pycache__/filters.cpython-38.pyc,sha256=VIMADCBiYhziIyRmxAyUDJluZvuZmiC4bNYWTRsGSao,721
|
|
72
|
-
simo/core/__pycache__/forms.cpython-38.pyc,sha256=
|
|
72
|
+
simo/core/__pycache__/forms.cpython-38.pyc,sha256=ksJVqk2X2XV1SmWFeEP2DStU1wvPQ7GaKO0CaFj_pTE,20465
|
|
73
73
|
simo/core/__pycache__/gateways.cpython-38.pyc,sha256=XBiwMfBkjoQ2re6jvADJOwK0_0Aav-crzie9qtfqT9U,4599
|
|
74
74
|
simo/core/__pycache__/loggers.cpython-38.pyc,sha256=Z-cdQnC6XlIonPV4Sl4E52tP4NMEdPAiHK0cFaIL7I8,1623
|
|
75
75
|
simo/core/__pycache__/managers.cpython-38.pyc,sha256=ObkzRjSOs2UQmjwFWDvZHreNzc_P5k7dVA_f7L7S7Q4,2529
|
|
@@ -10149,12 +10149,13 @@ simo/core/utils/config_values.py,sha256=4HCQmv5wQdupd16WOp80oJSyU7EDccjUO6blX--d
|
|
|
10149
10149
|
simo/core/utils/easing.py,sha256=N2NwD0CjLh82RGaYJKjyt-VVpVeS9z0mba8fqr8A1t0,1499
|
|
10150
10150
|
simo/core/utils/form_fields.py,sha256=UOzYdPd71qgCw1H3qH01u85YjrOlETPJAHOJrZKhyD0,627
|
|
10151
10151
|
simo/core/utils/form_widgets.py,sha256=Zxn9jJqPle9Q_BKNJnyTDn7MosYwNp1TFu5LoKs0bfc,408
|
|
10152
|
-
simo/core/utils/formsets.py,sha256=
|
|
10152
|
+
simo/core/utils/formsets.py,sha256=ZpExLsnDihnrlsPfYQrwy5qx54IowEmL8hnlO7KlyqE,6924
|
|
10153
10153
|
simo/core/utils/helpers.py,sha256=TOWy3slspaEYEhe9zDcb0RgzHUYslF6LZDlrWPGSqUI,3791
|
|
10154
10154
|
simo/core/utils/json.py,sha256=x3kMiiK30vuyWSYfghLVsDKo0N2JlCxZ5n8cwel85Vk,464
|
|
10155
10155
|
simo/core/utils/logs.py,sha256=Zn9JQxqCH9Odx2J1BWT84nFCfkJ4Z4p5X8psdll7hNc,2366
|
|
10156
10156
|
simo/core/utils/mixins.py,sha256=X6kUPKAi_F-uw7tgm8LEaYalBXpvDA-yrLNFCGr2rks,259
|
|
10157
10157
|
simo/core/utils/model_helpers.py,sha256=3IzJeOvBoYdUJVXCJkY20npOZXPjNPAiEFvuT0OPhwA,884
|
|
10158
|
+
simo/core/utils/operations.py,sha256=W234NEetDnMWP7_tvxJq5CWo_rT6Xhe4Cw7n9-VOpyU,176
|
|
10158
10159
|
simo/core/utils/relay.py,sha256=i1xy_nPTgY5Xn0l2W4lNI3xeVUpDQTUUfV3M8h2DeBg,457
|
|
10159
10160
|
simo/core/utils/serialization.py,sha256=v0HLQ98r3zOlsf6dv6S4WxOEt6BzmFz2eTXa_iuKjSM,2057
|
|
10160
10161
|
simo/core/utils/type_constants.py,sha256=xR2HXZOw9GZhC47iO1Py5B8mpaQMPbzvqX5nHWakhsY,4116
|
|
@@ -10166,12 +10167,13 @@ simo/core/utils/__pycache__/config_values.cpython-38.pyc,sha256=fqTVDhkjaWFv14Pr
|
|
|
10166
10167
|
simo/core/utils/__pycache__/easing.cpython-38.pyc,sha256=LupxHv19OiBqL0VXmTbMCtAOpgvBpq_b0XwLU8El-Jk,2137
|
|
10167
10168
|
simo/core/utils/__pycache__/form_fields.cpython-38.pyc,sha256=nBk6k9aj6BpWwdkpceIXdl5NU0fB6NPFhSBPaA-VtPs,1252
|
|
10168
10169
|
simo/core/utils/__pycache__/form_widgets.cpython-38.pyc,sha256=MYAYEq0I4P0WErG9FamTJYWue7-cPartAWbFAiSSg5w,908
|
|
10169
|
-
simo/core/utils/__pycache__/formsets.cpython-38.pyc,sha256=
|
|
10170
|
+
simo/core/utils/__pycache__/formsets.cpython-38.pyc,sha256=vwlFLdQ2bpZgXNUpekhtapwfouNPCIRo-SMrgOdAIMA,4813
|
|
10170
10171
|
simo/core/utils/__pycache__/helpers.cpython-38.pyc,sha256=jTGaN7kSJRwouP0EuYSaiJeMylo_RzJwSm-DKRwceHA,4291
|
|
10171
10172
|
simo/core/utils/__pycache__/json.cpython-38.pyc,sha256=akSSiSUOnza4N15GAH399gTz-X8x-5gijxZdjZoPz5Q,504
|
|
10172
10173
|
simo/core/utils/__pycache__/logs.cpython-38.pyc,sha256=BVVeQoOhfRHm3SHnCoE1d5G84kTpJZFmr_btc3jDYTU,2156
|
|
10173
10174
|
simo/core/utils/__pycache__/mixins.cpython-38.pyc,sha256=8Js2T7jVQ7hugRUIRu3rdxW86dJW4KeUUWqKqSkIGb0,615
|
|
10174
10175
|
simo/core/utils/__pycache__/model_helpers.cpython-38.pyc,sha256=QzO0rh1NuQePHDCSLmUCRrAZEnV4o8jh9CF_jp7IoUo,1351
|
|
10176
|
+
simo/core/utils/__pycache__/operations.cpython-38.pyc,sha256=1BUP7gbpBgX7mwWLpkmRH0eXgisbjn-6TIa8VDPj6v8,381
|
|
10175
10177
|
simo/core/utils/__pycache__/relay.cpython-38.pyc,sha256=gs4iN9TWBo_JIW07emIggIcv6gHKuOY_4jfmAFhuL3k,697
|
|
10176
10178
|
simo/core/utils/__pycache__/serialization.cpython-38.pyc,sha256=9nTbzozDi8Avl6krHvAo67CLdiTrYW0ij3hQtucHty0,1338
|
|
10177
10179
|
simo/core/utils/__pycache__/type_constants.cpython-38.pyc,sha256=bEMvzkBxzc6MKq6gn9A6wszXzMjLTbX-V-IK4NMht8E,3363
|
|
@@ -10183,7 +10185,7 @@ simo/fleet/auto_urls.py,sha256=X04oKJWA48wFW5iXg3PPROY2KDdHn_a99orQSE28QC4,518
|
|
|
10183
10185
|
simo/fleet/base_types.py,sha256=wL9RVkHr0gA7HI1wZq0pruGEIgvQqpfnCL4cC3ywsvw,102
|
|
10184
10186
|
simo/fleet/ble.py,sha256=eHA_9ABjbmH1vUVCv9hiPXQL2GZZSEVwfO0xyI1S0nI,1081
|
|
10185
10187
|
simo/fleet/controllers.py,sha256=WCqOA5Qrn9RavdfcB8X06WwaTE-9TGUprTQHZ8V8-nA,23172
|
|
10186
|
-
simo/fleet/forms.py,sha256=
|
|
10188
|
+
simo/fleet/forms.py,sha256=VVjFlWfU2jSfRp8CW9ntEd21Mr272LacRokzMon-YdU,48870
|
|
10187
10189
|
simo/fleet/gateways.py,sha256=KV5i5fxXIrlK-k6zyEkk83x11GJt-ELQ0npb4Ac83cM,3693
|
|
10188
10190
|
simo/fleet/managers.py,sha256=XOpDOA9L-f_550TNSyXnJbun2EmtGz1TenVTMlUSb8E,807
|
|
10189
10191
|
simo/fleet/models.py,sha256=bD5AebGFCAYGXPYhTA2nK1X9KpMG4WK4zFk9OzBDoHI,15301
|
|
@@ -10200,7 +10202,7 @@ simo/fleet/__pycache__/auto_urls.cpython-38.pyc,sha256=SqyTuaz_kEBvx-bL46SclsZEE
|
|
|
10200
10202
|
simo/fleet/__pycache__/base_types.cpython-38.pyc,sha256=deyPwjpT6xZiFxBGFnj5b7R-lbdOTh2krgpJhrcGVhc,274
|
|
10201
10203
|
simo/fleet/__pycache__/ble.cpython-38.pyc,sha256=Nrof9w7cm4OlpFWHeVnmvvanh2_oF9oQ3TknJiV93-0,1267
|
|
10202
10204
|
simo/fleet/__pycache__/controllers.cpython-38.pyc,sha256=TN3yvfZJgS7FwzgP4S1aDoaOqxbKj2oXfXOxqbkIXJU,19856
|
|
10203
|
-
simo/fleet/__pycache__/forms.cpython-38.pyc,sha256=
|
|
10205
|
+
simo/fleet/__pycache__/forms.cpython-38.pyc,sha256=0js770XdKp9PePLN-az0TLnpbfWE6u2Lwr8_sbfqOow,33943
|
|
10204
10206
|
simo/fleet/__pycache__/gateways.cpython-38.pyc,sha256=YAcgTOqJbtjGI03lvEcU6keFfrwAHkObVmErYzfRvjk,3569
|
|
10205
10207
|
simo/fleet/__pycache__/managers.cpython-38.pyc,sha256=8uz-xpUiqbGDgXIZ_XRZtFb-Tju6NGxflGg-Ee4Yo6k,1310
|
|
10206
10208
|
simo/fleet/__pycache__/models.cpython-38.pyc,sha256=LjcLsSytCQd17xhH-5RrzvnZ6JYI1ilvNdCY2iUCsGc,12935
|
|
@@ -10285,18 +10287,18 @@ simo/fleet/migrations/__pycache__/__init__.cpython-38.pyc,sha256=5k1KW0jeSDzw6Rn
|
|
|
10285
10287
|
simo/generic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10286
10288
|
simo/generic/app_widgets.py,sha256=E_pnpA1hxMIhenRCrHoQ5cik06jm2BAHCkl_eo-OudU,1264
|
|
10287
10289
|
simo/generic/base_types.py,sha256=djymox_boXTHX1BTTCLXrCH7ED-uAsV_idhaDOc3OLI,409
|
|
10288
|
-
simo/generic/controllers.py,sha256=
|
|
10289
|
-
simo/generic/forms.py,sha256=
|
|
10290
|
-
simo/generic/gateways.py,sha256=
|
|
10290
|
+
simo/generic/controllers.py,sha256=qbcKSrKLvi4DCN4Tt1GpHMScVSGz0snQRRuWi4_-r_s,55823
|
|
10291
|
+
simo/generic/forms.py,sha256=FVhQ-buK6vUp7x7PADWo31pPVtHWgBMgshnyH-CN8wI,29239
|
|
10292
|
+
simo/generic/gateways.py,sha256=dZQPzO23UbW9q4dEB9fqgt9Meg8mX94euXnRvfwxusY,18004
|
|
10291
10293
|
simo/generic/models.py,sha256=92TACMhJHadAg0TT9GnARO_R3_Sl6i-GGjhG_x7YdFI,7391
|
|
10292
10294
|
simo/generic/routing.py,sha256=elQVZmgnPiieEuti4sJ7zITk1hlRxpgbotcutJJgC60,228
|
|
10293
10295
|
simo/generic/socket_consumers.py,sha256=NfTQGYtVAc864IoogZRxf_0xpDPM0eMCWn0SlKA5P7Y,1751
|
|
10294
10296
|
simo/generic/__pycache__/__init__.cpython-38.pyc,sha256=mLu54WS9KIl-pHwVCBKpsDFIlOqml--JsOVzAUHg6cU,161
|
|
10295
10297
|
simo/generic/__pycache__/app_widgets.cpython-38.pyc,sha256=0IoKRG9n1tkNRRkrqAeOQwWBPd_33u98JBcVtMVVCio,2374
|
|
10296
10298
|
simo/generic/__pycache__/base_types.cpython-38.pyc,sha256=ptw6axyAqemZA35oa6vzr7EihzvbhW9w7Y-G6kfDedU,555
|
|
10297
|
-
simo/generic/__pycache__/controllers.cpython-38.pyc,sha256=
|
|
10298
|
-
simo/generic/__pycache__/forms.cpython-38.pyc,sha256=
|
|
10299
|
-
simo/generic/__pycache__/gateways.cpython-38.pyc,sha256=
|
|
10299
|
+
simo/generic/__pycache__/controllers.cpython-38.pyc,sha256=6WhWx_QaWrYV_WqU5coEp9VFR11DxJVjPGTrhcUoYQ4,35583
|
|
10300
|
+
simo/generic/__pycache__/forms.cpython-38.pyc,sha256=LsYmGecSEdOnWnskBoTktLEUfYYKWMtd40lIGHbS-ZY,21207
|
|
10301
|
+
simo/generic/__pycache__/gateways.cpython-38.pyc,sha256=B35GiB4wBRzvd91ugL89Z3vYADiLiERP0T-21CnLqKc,13324
|
|
10300
10302
|
simo/generic/__pycache__/models.cpython-38.pyc,sha256=PzlZsM1jxo3FVb7QDm3bny8UFwTsGrMQe4mj4tJ06eQ,5675
|
|
10301
10303
|
simo/generic/__pycache__/routing.cpython-38.pyc,sha256=xtxTUTBTdivzFyA5Wh7k-hUj1WDO_FiRq6HYXdbr9Ks,382
|
|
10302
10304
|
simo/generic/__pycache__/socket_consumers.cpython-38.pyc,sha256=piFHces0J9QuXu_CNBCQCYjoZEeoaxyVjLfJ9KaR8C8,1898
|
|
@@ -10387,14 +10389,14 @@ simo/users/utils.py,sha256=7gU_TDnAOsDYqJM0CFo8efPah2bTXfGpXxRqzD5RiSs,1270
|
|
|
10387
10389
|
simo/users/views.py,sha256=dOQVvmlHG7ihWKJLFUBcqKOA0UDctlMKR0pTc36JZqg,3487
|
|
10388
10390
|
simo/users/__pycache__/__init__.cpython-38.pyc,sha256=9otuYxq331c4lGy0DR8pigaPpzq0lQ4nrNLhlYiFAF0,159
|
|
10389
10391
|
simo/users/__pycache__/admin.cpython-38.pyc,sha256=53Do-W6tvaOUPJ1BDx0abBbtvmQxAgdI2ShlqmYfUvI,7500
|
|
10390
|
-
simo/users/__pycache__/api.cpython-38.pyc,sha256=
|
|
10392
|
+
simo/users/__pycache__/api.cpython-38.pyc,sha256=GcGFVxv0GUcH-TVdvj3v3hty1snKJw3O3-f4PM8DIyM,8305
|
|
10391
10393
|
simo/users/__pycache__/auth_backends.cpython-38.pyc,sha256=MuOieBIXt6lrDx83-UQtdDyI_U8kE3pU9XR4yFLKBnE,3007
|
|
10392
10394
|
simo/users/__pycache__/auto_urls.cpython-38.pyc,sha256=K-3sz2h-cEitoflSmZk1t0eUg5mQMMGLNZFREVwG7_o,430
|
|
10393
10395
|
simo/users/__pycache__/dynamic_settings.cpython-38.pyc,sha256=6F8JBjZkHykySnmZjNEzjS0ijbmPdcp9yUAZ5kqq_Fo,864
|
|
10394
10396
|
simo/users/__pycache__/middleware.cpython-38.pyc,sha256=Tj4nVEAvxEW3xA63fBRiJWRJpz_M848ZOqbHioc_IPE,1149
|
|
10395
10397
|
simo/users/__pycache__/models.cpython-38.pyc,sha256=D5N0lYn3U9jt0M8-Xiz94DbiPuJefZQ9AUFJqKrYmsg,17562
|
|
10396
10398
|
simo/users/__pycache__/permissions.cpython-38.pyc,sha256=ez5NxoL_JUeeH6GsKhvFreuA3FCBgGf9floSypdXUtM,633
|
|
10397
|
-
simo/users/__pycache__/serializers.cpython-38.pyc,sha256=
|
|
10399
|
+
simo/users/__pycache__/serializers.cpython-38.pyc,sha256=tZzdmCdSnqekAgRl0kyq-msm7QfUA0J_IipfrysAMRM,3477
|
|
10398
10400
|
simo/users/__pycache__/sso_urls.cpython-38.pyc,sha256=uAwDozpOmrhUald-8tOHANILXkH7-TI8fNYXOtPkSY8,402
|
|
10399
10401
|
simo/users/__pycache__/sso_views.cpython-38.pyc,sha256=sHEoxLOac3U3Epmhm197huFnW_J3gGCDZSji57itijU,3969
|
|
10400
10402
|
simo/users/__pycache__/tasks.cpython-38.pyc,sha256=xq-XaJ5gzkpVVZRWe0bvGdA31Eh_WS2rKSY62p4eY5E,1111
|
|
@@ -10466,8 +10468,8 @@ simo/users/templates/invitations/expired_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCe
|
|
|
10466
10468
|
simo/users/templates/invitations/expired_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10467
10469
|
simo/users/templates/invitations/taken_msg.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10468
10470
|
simo/users/templates/invitations/taken_suggestion.html,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10469
|
-
simo-2.0.
|
|
10470
|
-
simo-2.0.
|
|
10471
|
-
simo-2.0.
|
|
10472
|
-
simo-2.0.
|
|
10473
|
-
simo-2.0.
|
|
10471
|
+
simo-2.0.38.dist-info/LICENSE.md,sha256=M7wm1EmMGDtwPRdg7kW4d00h1uAXjKOT3HFScYQMeiE,34916
|
|
10472
|
+
simo-2.0.38.dist-info/METADATA,sha256=lSORUFdH9kvVvxyK_3k0swsm7DHyYLhjUmxM_WTAFF8,1730
|
|
10473
|
+
simo-2.0.38.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
10474
|
+
simo-2.0.38.dist-info/top_level.txt,sha256=GmS1hrAbpVqn9OWZh6UX82eIOdRLgYA82RG9fe8v4Rs,5
|
|
10475
|
+
simo-2.0.38.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|