simo 2.8.15__py3-none-any.whl → 2.10.1__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/automation/__pycache__/gateways.cpython-312.pyc +0 -0
- simo/automation/gateways.py +12 -10
- simo/core/__pycache__/admin.cpython-312.pyc +0 -0
- simo/core/__pycache__/auto_urls.cpython-312.pyc +0 -0
- simo/core/__pycache__/controllers.cpython-312.pyc +0 -0
- simo/core/__pycache__/models.cpython-312.pyc +0 -0
- simo/core/__pycache__/serializers.cpython-312.pyc +0 -0
- simo/core/__pycache__/tasks.cpython-312.pyc +0 -0
- simo/core/__pycache__/views.cpython-312.pyc +0 -0
- simo/core/admin.py +5 -2
- simo/core/auto_urls.py +4 -1
- simo/core/controllers.py +42 -5
- simo/core/models.py +32 -16
- simo/core/serializers.py +2 -2
- simo/core/tasks.py +8 -1
- simo/core/templates/admin/core/component_change_form.html +1 -1
- simo/core/templates/admin/wizard/discovery.html +3 -4
- simo/core/templates/admin/wizard/wizard_add.html +1 -1
- simo/core/views.py +26 -2
- simo/fleet/__pycache__/api.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/base_types.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/controllers.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/forms.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/managers.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/models.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/serializers.cpython-312.pyc +0 -0
- simo/fleet/__pycache__/socket_consumers.cpython-312.pyc +0 -0
- simo/fleet/api.py +26 -3
- simo/fleet/base_types.py +1 -0
- simo/fleet/controllers.py +240 -7
- simo/fleet/custom_dali_operations.py +275 -0
- simo/fleet/forms.py +132 -3
- simo/fleet/managers.py +3 -1
- simo/fleet/migrations/0045_alter_colonel_type_customdalidevice.py +29 -0
- simo/fleet/migrations/0046_delete_customdalidevice.py +16 -0
- simo/fleet/migrations/0047_customdalidevice.py +28 -0
- simo/fleet/migrations/0048_remove_customdalidevice_colonel_and_more.py +28 -0
- simo/fleet/migrations/__pycache__/0045_alter_colonel_type_customdalidevice.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0046_delete_customdalidevice.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0047_customdalidevice.cpython-312.pyc +0 -0
- simo/fleet/migrations/__pycache__/0048_remove_customdalidevice_colonel_and_more.cpython-312.pyc +0 -0
- simo/fleet/models.py +54 -9
- simo/fleet/serializers.py +15 -1
- simo/fleet/socket_consumers.py +6 -0
- simo/fleet/tasks.py +22 -2
- simo/fleet/templates/fleet/controllers_info/RoomZonePresenceSensor.md +8 -0
- simo/generic/__pycache__/controllers.cpython-312.pyc +0 -0
- simo/generic/__pycache__/forms.cpython-312.pyc +0 -0
- simo/generic/__pycache__/gateways.cpython-312.pyc +0 -0
- simo/generic/controllers.py +99 -43
- simo/generic/forms.py +13 -10
- simo/generic/gateways.py +91 -2
- simo/generic/migrations/0003_auto_20250409_1404.py +33 -0
- simo/generic/migrations/__pycache__/0003_auto_20250409_1404.cpython-312.pyc +0 -0
- simo/users/__pycache__/api.cpython-312.pyc +0 -0
- simo/users/__pycache__/dynamic_settings.cpython-312.pyc +0 -0
- simo/users/api.py +71 -18
- simo/users/dynamic_settings.py +1 -1
- {simo-2.8.15.dist-info → simo-2.10.1.dist-info}/METADATA +1 -1
- {simo-2.8.15.dist-info → simo-2.10.1.dist-info}/RECORD +64 -52
- {simo-2.8.15.dist-info → simo-2.10.1.dist-info}/WHEEL +0 -0
- {simo-2.8.15.dist-info → simo-2.10.1.dist-info}/entry_points.txt +0 -0
- {simo-2.8.15.dist-info → simo-2.10.1.dist-info}/licenses/LICENSE.md +0 -0
- {simo-2.8.15.dist-info → simo-2.10.1.dist-info}/top_level.txt +0 -0
|
Binary file
|
simo/automation/gateways.py
CHANGED
|
@@ -165,7 +165,7 @@ class GatesHandler:
|
|
|
165
165
|
if iu_id != iuser.id:
|
|
166
166
|
continue
|
|
167
167
|
gate = Component.objects.get(id=gate_id)
|
|
168
|
-
if is_out:
|
|
168
|
+
if is_out > 4:
|
|
169
169
|
print(
|
|
170
170
|
f"{iuser.user.name} is out, "
|
|
171
171
|
f"let's see if we must open the gates for him"
|
|
@@ -174,17 +174,17 @@ class GatesHandler:
|
|
|
174
174
|
# he is now coming back and open the gate for him
|
|
175
175
|
if self._is_in_geofence(gate, iuser.last_seen_location):
|
|
176
176
|
print("Yes he is back in a geofence! Open THE GATEEE!!")
|
|
177
|
-
self.gate_iusers[gate_id][iuser.id] =
|
|
178
|
-
|
|
177
|
+
self.gate_iusers[gate_id][iuser.id] = 0
|
|
178
|
+
if iuser.last_seen_speed_kmh > 10:
|
|
179
|
+
gate.open()
|
|
179
180
|
else:
|
|
180
181
|
print("No he is not back yet.")
|
|
181
182
|
else:
|
|
182
183
|
print(f"Check if {iuser.user.name} is out.")
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
print(f"YES {iuser.user.name} is out!")
|
|
184
|
+
if self._is_out_of_geofence(gate, iuser.last_seen_location):
|
|
185
|
+
self.gate_iusers[gate_id][iuser.id] += 1
|
|
186
|
+
if self.gate_iusers[gate_id][iuser.id] > 4:
|
|
187
|
+
print(f"YES {iuser.user.name} is truly out!")
|
|
188
188
|
|
|
189
189
|
def watch_gates(self):
|
|
190
190
|
drop_current_instance()
|
|
@@ -204,9 +204,11 @@ class GatesHandler:
|
|
|
204
204
|
self.gate_iusers[gate.id] = {}
|
|
205
205
|
if iuser.id not in self.gate_iusers[gate.id]:
|
|
206
206
|
if iuser.last_seen_location:
|
|
207
|
-
self.gate_iusers[gate.id][iuser.id] =
|
|
207
|
+
self.gate_iusers[gate.id][iuser.id] = 0
|
|
208
|
+
if self._is_out_of_geofence(
|
|
208
209
|
gate, iuser.last_seen_location
|
|
209
|
-
)
|
|
210
|
+
):
|
|
211
|
+
self.gate_iusers[gate.id][iuser.id] += 1
|
|
210
212
|
iuser.on_change(self.check_gates)
|
|
211
213
|
|
|
212
214
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
simo/core/admin.py
CHANGED
|
@@ -366,7 +366,7 @@ class ComponentAdmin(EasyObjectsDeleteMixin, admin.ModelAdmin):
|
|
|
366
366
|
pop_fields_from_form(ctx['form'])
|
|
367
367
|
if ctx['form'].is_valid():
|
|
368
368
|
if ctx['form'].controller.is_discoverable:
|
|
369
|
-
ctx['form'].controller.
|
|
369
|
+
ctx['form'].controller._init_discovery(
|
|
370
370
|
ctx['form'].cleaned_data
|
|
371
371
|
)
|
|
372
372
|
ctx['discovery_msg'] = ctx['form'].controller.discovery_msg
|
|
@@ -383,6 +383,9 @@ class ComponentAdmin(EasyObjectsDeleteMixin, admin.ModelAdmin):
|
|
|
383
383
|
'components-list',
|
|
384
384
|
kwargs={'instance_slug': instance.slug}
|
|
385
385
|
)
|
|
386
|
+
ctx['finish_url'] = reverse(
|
|
387
|
+
'finish-discovery',
|
|
388
|
+
) + f"?uid={ctx['form'].controller.uid}"
|
|
386
389
|
return render(
|
|
387
390
|
request, 'admin/wizard/discovery.html', ctx
|
|
388
391
|
)
|
|
@@ -486,7 +489,7 @@ class ComponentAdmin(EasyObjectsDeleteMixin, admin.ModelAdmin):
|
|
|
486
489
|
def info(self, obj):
|
|
487
490
|
if not obj.controller:
|
|
488
491
|
return
|
|
489
|
-
info = obj.controller.info()
|
|
492
|
+
info = obj.controller.info(obj)
|
|
490
493
|
if not info:
|
|
491
494
|
return
|
|
492
495
|
return mark_safe(
|
simo/core/auto_urls.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from django.urls import path
|
|
2
2
|
from .views import (
|
|
3
|
-
get_timestamp, upgrade, restart, reboot, set_instance, delete_instance
|
|
3
|
+
get_timestamp, upgrade, restart, reboot, set_instance, delete_instance,
|
|
4
|
+
finish_discovery
|
|
4
5
|
)
|
|
5
6
|
from .autocomplete_views import (
|
|
6
7
|
IconModelAutocomplete,
|
|
@@ -31,6 +32,8 @@ urlpatterns = [
|
|
|
31
32
|
ComponentAutocomplete.as_view(), name='autocomplete-component'
|
|
32
33
|
),
|
|
33
34
|
path('set-instance/<slug:instance_slug>/', set_instance, name='set-instance'),
|
|
35
|
+
path('finish-discovery/',
|
|
36
|
+
finish_discovery, name='finish-discovery'),
|
|
34
37
|
path('upgrade/', upgrade, name='upgrade'),
|
|
35
38
|
path('restart/', restart, name='restart'),
|
|
36
39
|
path('reboot/', reboot, name='reboot'),
|
simo/core/controllers.py
CHANGED
|
@@ -111,9 +111,7 @@ class ControllerBase(ABC):
|
|
|
111
111
|
@classmethod
|
|
112
112
|
def is_discoverable(cls):
|
|
113
113
|
return hasattr(
|
|
114
|
-
cls, '
|
|
115
|
-
) and hasattr(
|
|
116
|
-
cls, '_process_discovery'
|
|
114
|
+
cls, '_init_discovery'
|
|
117
115
|
)
|
|
118
116
|
|
|
119
117
|
@classmethod
|
|
@@ -633,6 +631,15 @@ class Dimmer(ControllerBase, TimerMixin, OnOffPokerMixin):
|
|
|
633
631
|
else:
|
|
634
632
|
self.send(self.component.config.get('max', 90))
|
|
635
633
|
|
|
634
|
+
def max_out(self):
|
|
635
|
+
self.send(self.component.config.get('max', 90))
|
|
636
|
+
|
|
637
|
+
def output_percent(self, value):
|
|
638
|
+
min = self.component.config.get('min', 0)
|
|
639
|
+
max = self.component.config.get('max', 100)
|
|
640
|
+
delta = max - min
|
|
641
|
+
self.send(min + delta * value / 100)
|
|
642
|
+
|
|
636
643
|
def toggle(self):
|
|
637
644
|
self.component.refresh_from_db()
|
|
638
645
|
if self.component.value:
|
|
@@ -847,21 +854,51 @@ class Switch(MultiSwitchBase, TimerMixin, OnOffPokerMixin):
|
|
|
847
854
|
default_value = False
|
|
848
855
|
|
|
849
856
|
def turn_on(self):
|
|
857
|
+
if self.component.meta.get('pulse'):
|
|
858
|
+
self.component.meta.pop('pulse')
|
|
859
|
+
self.component.save()
|
|
850
860
|
self.send(True)
|
|
851
861
|
|
|
852
862
|
def turn_off(self):
|
|
863
|
+
if self.component.meta.get('pulse'):
|
|
864
|
+
self.component.meta.pop('pulse')
|
|
865
|
+
self.component.save()
|
|
853
866
|
self.send(False)
|
|
854
867
|
|
|
855
868
|
def toggle(self):
|
|
869
|
+
if self.component.meta.get('pulse'):
|
|
870
|
+
self.component.meta.pop('pulse')
|
|
871
|
+
self.component.save()
|
|
856
872
|
self.send(not self.component.value)
|
|
857
873
|
|
|
858
874
|
def click(self):
|
|
859
875
|
'''
|
|
860
876
|
Gateway specific implementation is very welcome of this!
|
|
861
877
|
'''
|
|
878
|
+
if self.component.meta.get('pulse'):
|
|
879
|
+
self.component.meta.pop('pulse')
|
|
880
|
+
self.component.save()
|
|
862
881
|
self.turn_on()
|
|
863
|
-
|
|
864
|
-
|
|
882
|
+
from .tasks import component_action
|
|
883
|
+
component_action.s(
|
|
884
|
+
self.component.id, 'turn_off'
|
|
885
|
+
).apply_async(countdown=1)
|
|
886
|
+
|
|
887
|
+
def pulse(self, frame_length_s, on_percentage):
|
|
888
|
+
self.component.meta['pulse'] = {
|
|
889
|
+
'frame': frame_length_s, 'duty': on_percentage / 100
|
|
890
|
+
}
|
|
891
|
+
self.component.save()
|
|
892
|
+
from simo.generic.gateways import GenericGatewayHandler
|
|
893
|
+
from .models import Gateway
|
|
894
|
+
generic_gateway = Gateway.objects.filter(
|
|
895
|
+
type=GenericGatewayHandler.uid
|
|
896
|
+
).first()
|
|
897
|
+
if generic_gateway:
|
|
898
|
+
GatewayObjectCommand(
|
|
899
|
+
generic_gateway, self.component,
|
|
900
|
+
pulse=self.component.meta['pulse']
|
|
901
|
+
).publish()
|
|
865
902
|
|
|
866
903
|
|
|
867
904
|
class DoubleSwitch(MultiSwitchBase):
|
simo/core/models.py
CHANGED
|
@@ -294,28 +294,44 @@ class Gateway(DirtyFieldsMixin, models.Model, SimoAdminMixin):
|
|
|
294
294
|
)
|
|
295
295
|
if result:
|
|
296
296
|
self.refresh_from_db()
|
|
297
|
-
|
|
298
|
-
for res in result:
|
|
299
|
-
if isinstance(res, models.Model):
|
|
300
|
-
if res.pk not in self.discovery['result']:
|
|
301
|
-
self.discovery['result'].append(res.pk)
|
|
302
|
-
else:
|
|
303
|
-
if res not in self.discovery['result']:
|
|
304
|
-
self.discovery['result'].append(res)
|
|
305
|
-
else:
|
|
306
|
-
if isinstance(result, models.Model):
|
|
307
|
-
if result.pk not in self.discovery['result']:
|
|
308
|
-
self.discovery['result'].append(result.pk)
|
|
309
|
-
else:
|
|
310
|
-
if result not in self.discovery['result']:
|
|
311
|
-
self.discovery['result'].append(result)
|
|
312
|
-
|
|
297
|
+
self.append_discovery_result(result)
|
|
313
298
|
|
|
314
299
|
self.save(update_fields=['discovery'])
|
|
315
300
|
|
|
301
|
+
|
|
316
302
|
def finish_discovery(self):
|
|
303
|
+
from .utils.type_constants import CONTROLLER_TYPES_MAP
|
|
317
304
|
self.discovery['finished'] = time.time()
|
|
318
305
|
self.save(update_fields=['discovery'])
|
|
306
|
+
ControllerClass = CONTROLLER_TYPES_MAP.get(
|
|
307
|
+
self.discovery['controller_uid']
|
|
308
|
+
)
|
|
309
|
+
if hasattr(ControllerClass, '_finish_discovery'):
|
|
310
|
+
result = ControllerClass._finish_discovery(
|
|
311
|
+
self.discovery['init_data']
|
|
312
|
+
)
|
|
313
|
+
self.append_discovery_result(result)
|
|
314
|
+
self.save(update_fields=['discovery'])
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def append_discovery_result(self, result):
|
|
318
|
+
if not isinstance(result, dict) and isinstance(result, Iterable):
|
|
319
|
+
for res in result:
|
|
320
|
+
if isinstance(res, models.Model):
|
|
321
|
+
if res.pk not in self.discovery['result']:
|
|
322
|
+
self.discovery['result'].append(res.pk)
|
|
323
|
+
else:
|
|
324
|
+
if res not in self.discovery['result']:
|
|
325
|
+
self.discovery['result'].append(res)
|
|
326
|
+
else:
|
|
327
|
+
if isinstance(result, models.Model):
|
|
328
|
+
if result.pk not in self.discovery['result']:
|
|
329
|
+
self.discovery['result'].append(result.pk)
|
|
330
|
+
else:
|
|
331
|
+
if result not in self.discovery['result']:
|
|
332
|
+
self.discovery['result'].append(result)
|
|
333
|
+
|
|
334
|
+
|
|
319
335
|
|
|
320
336
|
|
|
321
337
|
class Component(DirtyFieldsMixin, models.Model, SimoAdminMixin, OnChangeMixin):
|
simo/core/serializers.py
CHANGED
|
@@ -544,7 +544,7 @@ class ComponentSerializer(FormSerializer):
|
|
|
544
544
|
)
|
|
545
545
|
if form.is_valid():
|
|
546
546
|
if form.controller.is_discoverable:
|
|
547
|
-
form.controller.
|
|
547
|
+
form.controller._init_discovery(form.cleaned_data)
|
|
548
548
|
return form.save(commit=False)
|
|
549
549
|
return form.save(commit=True)
|
|
550
550
|
raise serializers.ValidationError(form.errors)
|
|
@@ -554,7 +554,7 @@ class ComponentSerializer(FormSerializer):
|
|
|
554
554
|
|
|
555
555
|
def get_info(self, obj):
|
|
556
556
|
if obj.controller:
|
|
557
|
-
return obj.controller.info()
|
|
557
|
+
return obj.controller.info(obj)
|
|
558
558
|
|
|
559
559
|
def get_read_only(self, obj):
|
|
560
560
|
user = self.context.get('user')
|
simo/core/tasks.py
CHANGED
|
@@ -16,11 +16,18 @@ from django.utils import timezone
|
|
|
16
16
|
from actstream.models import Action
|
|
17
17
|
from simo.conf import dynamic_settings
|
|
18
18
|
from simo.core.utils.helpers import get_self_ip
|
|
19
|
-
from simo.core.middleware import introduce_instance
|
|
19
|
+
from simo.core.middleware import introduce_instance, drop_current_instance
|
|
20
20
|
from simo.users.models import PermissionsRole, InstanceUser
|
|
21
21
|
from .models import Instance, Component, ComponentHistory, HistoryAggregate
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
@celery_app.task
|
|
25
|
+
def component_action(comp_id, method, args=None, kwargs=None):
|
|
26
|
+
drop_current_instance()
|
|
27
|
+
component = Component.objects.get(id=comp_id)
|
|
28
|
+
getattr(component, method)(*args, **kwargs)
|
|
29
|
+
|
|
30
|
+
|
|
24
31
|
@celery_app.task
|
|
25
32
|
def supervisor_restart():
|
|
26
33
|
time.sleep(2)
|
|
@@ -64,8 +64,7 @@
|
|
|
64
64
|
|
|
65
65
|
<div id="running-discovery">
|
|
66
66
|
<p style="margin: 30px 15px">
|
|
67
|
-
<i class="fas fa-spinner fa-spin fa-lg" style="margin-right: 10px"></i> DISCOVERY MODE ACTIVATED!
|
|
68
|
-
Your new components will appear down bellow once discovered.
|
|
67
|
+
<i class="fas fa-spinner fa-spin fa-lg" style="margin-right: 10px"></i> DISCOVERY MODE ACTIVATED!
|
|
69
68
|
</p>
|
|
70
69
|
{% if discovery_msg %}
|
|
71
70
|
<p style="margin: 30px 15px">{{ discovery_msg }}</p>
|
|
@@ -108,8 +107,8 @@
|
|
|
108
107
|
|
|
109
108
|
<div class="submit-row" style="text-align: left;">
|
|
110
109
|
|
|
111
|
-
<a href="{
|
|
112
|
-
<i class="fa fa-check"></i>
|
|
110
|
+
<a href="{{ finish_url }}" class="cancel-link" style="display: block; padding:9px 15px">
|
|
111
|
+
<i class="fa fa-check"></i> Finish
|
|
113
112
|
</a>
|
|
114
113
|
|
|
115
114
|
</div>
|
simo/core/views.py
CHANGED
|
@@ -9,7 +9,7 @@ from django.http import (
|
|
|
9
9
|
)
|
|
10
10
|
from django.contrib import messages
|
|
11
11
|
from simo.conf import dynamic_settings
|
|
12
|
-
from .models import Instance
|
|
12
|
+
from .models import Instance, Component, Gateway
|
|
13
13
|
from .tasks import update as update_task, supervisor_restart, hardware_reboot
|
|
14
14
|
from .middleware import introduce_instance
|
|
15
15
|
|
|
@@ -91,4 +91,28 @@ def hub_info(request):
|
|
|
91
91
|
request.get_host()
|
|
92
92
|
):
|
|
93
93
|
data['hub_secret'] = dynamic_settings['core__hub_secret']
|
|
94
|
-
return JsonResponse(data)
|
|
94
|
+
return JsonResponse(data)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@login_required
|
|
98
|
+
def finish_discovery(request):
|
|
99
|
+
# finish discovery function for admin discovery view
|
|
100
|
+
if not request.user.is_authenticated:
|
|
101
|
+
raise Http404()
|
|
102
|
+
if not request.user.is_master:
|
|
103
|
+
raise Http404()
|
|
104
|
+
result = None
|
|
105
|
+
for gateway in Gateway.objects.filter(
|
|
106
|
+
discovery__start__gt=time.time() - 60 * 60, # no more than an hour
|
|
107
|
+
discovery__controller_uid=request.GET['uid']
|
|
108
|
+
):
|
|
109
|
+
gateway.finish_discovery()
|
|
110
|
+
for res in gateway.discovery['result']:
|
|
111
|
+
comp = Component.objects.filter(
|
|
112
|
+
controller_uid=request.GET['uid'], pk=res
|
|
113
|
+
).first()
|
|
114
|
+
if comp:
|
|
115
|
+
result = comp
|
|
116
|
+
if result:
|
|
117
|
+
return redirect(result.get_admin_url())
|
|
118
|
+
return reverse('admin:core_component_changelist')
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
simo/fleet/api.py
CHANGED
|
@@ -4,11 +4,13 @@ from rest_framework import viewsets
|
|
|
4
4
|
from rest_framework.response import Response as RESTResponse
|
|
5
5
|
from rest_framework.decorators import action
|
|
6
6
|
from rest_framework.exceptions import ValidationError as APIValidationError
|
|
7
|
+
from django_filters.rest_framework import DjangoFilterBackend
|
|
7
8
|
from simo.core.api import InstanceMixin
|
|
8
9
|
from simo.core.permissions import IsInstanceSuperuser
|
|
9
|
-
from .models import InstanceOptions, Colonel, Interface
|
|
10
|
+
from .models import InstanceOptions, Colonel, Interface, CustomDaliDevice
|
|
10
11
|
from .serializers import (
|
|
11
|
-
InstanceOptionsSerializer, ColonelSerializer, ColonelInterfaceSerializer
|
|
12
|
+
InstanceOptionsSerializer, ColonelSerializer, ColonelInterfaceSerializer,
|
|
13
|
+
CustomDaliDeviceSerializer
|
|
12
14
|
)
|
|
13
15
|
|
|
14
16
|
|
|
@@ -99,4 +101,25 @@ class InterfaceViewSet(
|
|
|
99
101
|
return permissions
|
|
100
102
|
|
|
101
103
|
def get_queryset(self):
|
|
102
|
-
return Interface.objects.filter(colonel__instance=self.instance)
|
|
104
|
+
return Interface.objects.filter(colonel__instance=self.instance)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class CustomDaliDeviceViewSet(
|
|
108
|
+
InstanceMixin,
|
|
109
|
+
viewsets.mixins.RetrieveModelMixin, viewsets.mixins.UpdateModelMixin,
|
|
110
|
+
viewsets.mixins.ListModelMixin, viewsets.mixins.CreateModelMixin,
|
|
111
|
+
viewsets.GenericViewSet
|
|
112
|
+
):
|
|
113
|
+
url = 'fleet/custom-dali-devices'
|
|
114
|
+
basename = 'custom-dali-devices'
|
|
115
|
+
serializer_class = CustomDaliDeviceSerializer
|
|
116
|
+
filter_backends = [DjangoFilterBackend]
|
|
117
|
+
filterset_fields = ['random_address', 'name']
|
|
118
|
+
|
|
119
|
+
def get_permissions(self):
|
|
120
|
+
permissions = super().get_permissions()
|
|
121
|
+
permissions.append(IsInstanceSuperuser())
|
|
122
|
+
return permissions
|
|
123
|
+
|
|
124
|
+
def get_queryset(self):
|
|
125
|
+
return CustomDaliDevice.objects.filter(instance=self.instance)
|