simo 2.0.5__py3-none-any.whl → 2.0.7__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/__pycache__/settings.cpython-38.pyc +0 -0
- simo/__pycache__/urls.cpython-38.pyc +0 -0
- simo/core/__pycache__/admin.cpython-38.pyc +0 -0
- simo/core/__pycache__/api.cpython-38.pyc +0 -0
- simo/core/__pycache__/api_auth.cpython-38.pyc +0 -0
- simo/core/__pycache__/api_meta.cpython-38.pyc +0 -0
- simo/core/__pycache__/auto_urls.cpython-38.pyc +0 -0
- simo/core/__pycache__/autocomplete_views.cpython-38.pyc +0 -0
- simo/core/__pycache__/base_types.cpython-38.pyc +0 -0
- simo/core/__pycache__/context.cpython-38.pyc +0 -0
- simo/core/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/core/__pycache__/events.cpython-38.pyc +0 -0
- simo/core/__pycache__/forms.cpython-38.pyc +0 -0
- simo/core/__pycache__/gateways.cpython-38.pyc +0 -0
- simo/core/__pycache__/managers.cpython-38.pyc +0 -0
- simo/core/__pycache__/middleware.cpython-38.pyc +0 -0
- simo/core/__pycache__/models.cpython-38.pyc +0 -0
- simo/core/__pycache__/permissions.cpython-38.pyc +0 -0
- simo/core/__pycache__/serializers.cpython-38.pyc +0 -0
- simo/core/__pycache__/socket_consumers.cpython-38.pyc +0 -0
- simo/core/__pycache__/tasks.cpython-38.pyc +0 -0
- simo/core/__pycache__/views.cpython-38.pyc +0 -0
- simo/core/admin.py +10 -10
- simo/core/api.py +7 -6
- simo/core/controllers.py +1 -0
- simo/core/db_backend/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/db_backend/__pycache__/base.cpython-38.pyc +0 -0
- simo/core/drf_braces/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/drf_braces/__pycache__/utils.cpython-38.pyc +0 -0
- simo/core/drf_braces/fields/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/drf_braces/fields/__pycache__/_fields.cpython-38.pyc +0 -0
- simo/core/drf_braces/fields/__pycache__/custom.cpython-38.pyc +0 -0
- simo/core/drf_braces/fields/__pycache__/mixins.cpython-38.pyc +0 -0
- simo/core/drf_braces/fields/__pycache__/modified.cpython-38.pyc +0 -0
- simo/core/drf_braces/serializers/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/drf_braces/serializers/__pycache__/form_serializer.cpython-38.pyc +0 -0
- simo/core/forms.py +8 -6
- simo/core/migrations/0030_alter_instance_timezone.py +18 -0
- simo/core/migrations/__pycache__/0001_initial.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0002_load_icons.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0003_create_default_zones_and_categories.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0004_create_generic.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0005_component_subcomponents.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0006_alter_component_subcomponents.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0007_component_change_init_to.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0008_alter_component_change_init_to.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0009_auto_20220707_1404.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0010_historyaggregate.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0011_component_last_change.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0012_instance.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0013_auto_20231003_0754.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0014_zone_instance.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0015_auto_20231004_1113.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0016_auto_20231004_1113.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0017_auto_20231004_1313.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0018_auto_20231005_0622.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0019_alter_gateway_type.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0020_component_meta.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0021_auto_20231020_1041.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0026_category_instance.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0027_remove_component_tags.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0028_rename_subcomponents_component_slaves.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0029_auto_20240229_1331.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/0030_alter_instance_timezone.cpython-38.pyc +0 -0
- simo/core/migrations/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/models.py +31 -46
- simo/core/serializers.py +3 -4
- simo/core/templatetags/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/templatetags/__pycache__/components_list.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/admin.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/config_values.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/easing.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/form_fields.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/form_widgets.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/formsets.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/helpers.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/logs.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/mixins.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/model_helpers.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/relay.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/serialization.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/type_constants.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/validators.cpython-38.pyc +0 -0
- simo/core/utils/serialization.py +4 -2
- simo/core/utils/type_constants.py +32 -11
- simo/fleet/__pycache__/admin.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/api.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/auto_urls.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/forms.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/gateways.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/managers.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/models.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/serializers.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/socket_consumers.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/utils.cpython-38.pyc +0 -0
- simo/fleet/__pycache__/views.cpython-38.pyc +0 -0
- simo/fleet/admin.py +6 -6
- simo/fleet/auto_urls.py +4 -4
- simo/fleet/controllers.py +75 -2
- simo/fleet/forms.py +68 -33
- simo/fleet/gateways.py +7 -3
- simo/fleet/managers.py +4 -2
- simo/fleet/migrations/0032_auto_20240415_0736.py +33 -0
- simo/fleet/migrations/0033_auto_20240415_0736.py +28 -0
- simo/fleet/migrations/__pycache__/0025_auto_20240130_1334.cpython-38.pyc +0 -0
- simo/fleet/migrations/__pycache__/0026_rename_i2cinterface_scl_pin_and_more.cpython-38.pyc +0 -0
- simo/fleet/migrations/__pycache__/0027_auto_20240306_0802.cpython-38.pyc +0 -0
- simo/fleet/migrations/__pycache__/0028_remove_i2cinterface_scl_pin_no_and_more.cpython-38.pyc +0 -0
- simo/fleet/migrations/__pycache__/0029_alter_i2cinterface_scl_pin_and_more.cpython-38.pyc +0 -0
- simo/fleet/migrations/__pycache__/0030_colonelpin_label_alter_colonel_type.cpython-38.pyc +0 -0
- simo/fleet/migrations/__pycache__/0031_alter_colonel_type.cpython-38.pyc +0 -0
- simo/fleet/migrations/__pycache__/0032_auto_20240415_0736.cpython-38.pyc +0 -0
- simo/fleet/migrations/__pycache__/0033_auto_20240415_0736.cpython-38.pyc +0 -0
- simo/fleet/models.py +82 -14
- simo/fleet/socket_consumers.py +19 -12
- simo/fleet/utils.py +6 -1
- simo/fleet/views.py +10 -9
- simo/generic/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/generic/__pycache__/app_widgets.cpython-38.pyc +0 -0
- simo/generic/__pycache__/base_types.cpython-38.pyc +0 -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/__pycache__/models.cpython-38.pyc +0 -0
- simo/generic/__pycache__/routing.cpython-38.pyc +0 -0
- simo/generic/__pycache__/socket_consumers.cpython-38.pyc +0 -0
- simo/generic/controllers.py +0 -7
- simo/generic/forms.py +0 -3
- simo/generic/gateways.py +0 -8
- simo/multimedia/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/admin.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/api.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/app_widgets.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/base_types.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/forms.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/models.cpython-38.pyc +0 -0
- simo/multimedia/__pycache__/serializers.cpython-38.pyc +0 -0
- simo/multimedia/migrations/__pycache__/0001_initial.cpython-38.pyc +0 -0
- simo/multimedia/migrations/__pycache__/0002_sound_length.cpython-38.pyc +0 -0
- simo/multimedia/migrations/__pycache__/0003_alter_sound_length.cpython-38.pyc +0 -0
- simo/multimedia/migrations/__pycache__/0004_auto_20231023_1055.cpython-38.pyc +0 -0
- simo/multimedia/migrations/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/notifications/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/notifications/__pycache__/admin.cpython-38.pyc +0 -0
- simo/notifications/__pycache__/api.cpython-38.pyc +0 -0
- simo/notifications/__pycache__/models.cpython-38.pyc +0 -0
- simo/notifications/__pycache__/serializers.cpython-38.pyc +0 -0
- simo/notifications/migrations/__pycache__/0001_initial.cpython-38.pyc +0 -0
- simo/notifications/migrations/__pycache__/0002_notification_instance.cpython-38.pyc +0 -0
- simo/notifications/migrations/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/users/__pycache__/admin.cpython-38.pyc +0 -0
- simo/users/__pycache__/api.cpython-38.pyc +0 -0
- simo/users/__pycache__/auto_urls.cpython-38.pyc +0 -0
- simo/users/__pycache__/middleware.cpython-38.pyc +0 -0
- simo/users/__pycache__/models.cpython-38.pyc +0 -0
- simo/users/__pycache__/serializers.cpython-38.pyc +0 -0
- simo/users/__pycache__/sso_urls.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0001_initial.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0002_componentpermission.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0003_create_roles_and_system_user.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0004_user_secret_key.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0005_permissionsrole_instance.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0006_auto_20231003_0850.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0007_auto_20231003_1228.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0008_auto_20231003_1229.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0009_remove_user_role.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0010_auto_20231004_1313.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0011_auto_20231004_1313.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0012_alter_userinstancerole_unique_together.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0013_remove_user_roles.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0014_user_roles.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0015_remove_user_at_home.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0016_auto_20231005_1050.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/0025_rename_name_fingerprint_type_and_more.cpython-38.pyc +0 -0
- simo/users/migrations/__pycache__/__init__.cpython-38.pyc +0 -0
- {simo-2.0.5.dist-info → simo-2.0.7.dist-info}/METADATA +1 -1
- {simo-2.0.5.dist-info → simo-2.0.7.dist-info}/RECORD +183 -89
- {simo-2.0.5.dist-info → simo-2.0.7.dist-info}/LICENSE.md +0 -0
- {simo-2.0.5.dist-info → simo-2.0.7.dist-info}/WHEEL +0 -0
- {simo-2.0.5.dist-info → simo-2.0.7.dist-info}/top_level.txt +0 -0
|
Binary file
|
simo/core/utils/serialization.py
CHANGED
|
@@ -21,14 +21,14 @@ def serialize_form_data(data):
|
|
|
21
21
|
serialized_data[field_name] = {
|
|
22
22
|
'model': 'many',
|
|
23
23
|
'val': json.loads(model_serializers.serialize(
|
|
24
|
-
'json', val
|
|
24
|
+
'json', val, fields=['pk']
|
|
25
25
|
))
|
|
26
26
|
}
|
|
27
27
|
else:
|
|
28
28
|
serialized_data[field_name] = {
|
|
29
29
|
'model': 'single',
|
|
30
30
|
'val': json.loads(model_serializers.serialize(
|
|
31
|
-
'json', [val]
|
|
31
|
+
'json', [val], fields=['pk']
|
|
32
32
|
))
|
|
33
33
|
}
|
|
34
34
|
else:
|
|
@@ -46,11 +46,13 @@ def deserialize_form_data(data):
|
|
|
46
46
|
if val['model'] == 'single':
|
|
47
47
|
for item in deserializer_generator:
|
|
48
48
|
deserialized_data[field_name] = item.object
|
|
49
|
+
deserialized_data[field_name].refresh_from_db()
|
|
49
50
|
break
|
|
50
51
|
else:
|
|
51
52
|
deserialized_data[field_name] = []
|
|
52
53
|
for item in deserializer_generator:
|
|
53
54
|
deserialized_data[field_name].append(item.object)
|
|
55
|
+
deserialized_data[field_name][-1].refresh_from_db()
|
|
54
56
|
else:
|
|
55
57
|
deserialized_data[field_name] = val
|
|
56
58
|
return deserialized_data
|
|
@@ -19,20 +19,32 @@ def get_controller_types_map(gateway=None):
|
|
|
19
19
|
except ModuleNotFoundError:
|
|
20
20
|
continue
|
|
21
21
|
for cls_name, cls in configs.__dict__.items():
|
|
22
|
-
if inspect.isclass(cls)
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
if not inspect.isclass(cls):
|
|
23
|
+
continue
|
|
24
|
+
if not issubclass(cls, ControllerBase):
|
|
25
|
+
continue
|
|
26
|
+
if inspect.isabstract(cls):
|
|
27
|
+
continue
|
|
28
|
+
if gateway:
|
|
29
|
+
if issubclass(gateway, BaseGatewayHandler) \
|
|
30
|
+
or isinstance(gateway, BaseGatewayHandler):
|
|
31
|
+
if gateway.uid != cls.gateway_class.uid:
|
|
32
|
+
continue
|
|
33
|
+
else:
|
|
25
34
|
try:
|
|
26
|
-
same = gateway.
|
|
35
|
+
same = gateway.handler.uid == cls.gateway_class.uid
|
|
27
36
|
except:
|
|
28
37
|
continue
|
|
29
38
|
else:
|
|
30
39
|
if not same:
|
|
31
40
|
continue
|
|
32
|
-
|
|
41
|
+
controllers_map[cls.uid] = cls
|
|
33
42
|
return controllers_map
|
|
34
43
|
|
|
35
44
|
|
|
45
|
+
CONTROLLER_TYPES_MAP = get_controller_types_map()
|
|
46
|
+
|
|
47
|
+
|
|
36
48
|
def get_controller_types_choices(gateway=None):
|
|
37
49
|
choices = []
|
|
38
50
|
for controller_cls in get_controller_types_map(gateway).values():
|
|
@@ -40,11 +52,8 @@ def get_controller_types_choices(gateway=None):
|
|
|
40
52
|
return choices
|
|
41
53
|
|
|
42
54
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
# (slug, cls.name) for slug, cls in ALL_CONTROLLER_TYPES.items()
|
|
46
|
-
# ]
|
|
47
|
-
# CONTROLLER_TYPE_CHOICES.sort(key=lambda e: e[0])
|
|
55
|
+
CONTROLLER_TYPES_CHOICES = get_controller_types_choices()
|
|
56
|
+
|
|
48
57
|
|
|
49
58
|
def get_all_gateways():
|
|
50
59
|
all_gateways = {}
|
|
@@ -65,13 +74,25 @@ def get_all_gateways():
|
|
|
65
74
|
return all_gateways
|
|
66
75
|
|
|
67
76
|
|
|
77
|
+
GATEWAYS_MAP = get_all_gateways()
|
|
78
|
+
|
|
79
|
+
|
|
68
80
|
def get_gateway_choices():
|
|
69
81
|
choices = [
|
|
70
|
-
(slug, cls.name) for slug, cls in
|
|
82
|
+
(slug, cls.name) for slug, cls in GATEWAYS_MAP.items()
|
|
71
83
|
]
|
|
72
84
|
choices.sort(key=lambda e: e[1])
|
|
73
85
|
return choices
|
|
74
86
|
|
|
87
|
+
GATEWAYS_CHOICES = get_gateway_choices()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
CONTROLLERS_BY_GATEWAY = {}
|
|
91
|
+
for gateway_slug, gateway_cls in GATEWAYS_MAP.items():
|
|
92
|
+
CONTROLLERS_BY_GATEWAY[gateway_slug] = {}
|
|
93
|
+
for ctrl_uid, ctrl_cls in get_controller_types_map(gateway_cls).items():
|
|
94
|
+
CONTROLLERS_BY_GATEWAY[gateway_slug][ctrl_uid] = ctrl_cls
|
|
95
|
+
|
|
75
96
|
|
|
76
97
|
ALL_BASE_TYPES = {}
|
|
77
98
|
for name, app in apps.app_configs.items():
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
simo/fleet/admin.py
CHANGED
|
@@ -5,14 +5,14 @@ from django.template.loader import render_to_string
|
|
|
5
5
|
from django.templatetags.static import static
|
|
6
6
|
from simo.core.models import Component
|
|
7
7
|
from simo.core.utils.admin import FormAction
|
|
8
|
-
from .models import Colonel,
|
|
9
|
-
from .forms import ColonelAdminForm, MoveColonelForm,
|
|
8
|
+
from .models import Colonel, Interface, ColonelPin
|
|
9
|
+
from .forms import ColonelAdminForm, MoveColonelForm, InterfaceAdminForm
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class
|
|
13
|
-
model =
|
|
12
|
+
class InterfaceInline(admin.TabularInline):
|
|
13
|
+
model = Interface
|
|
14
14
|
extra = 0
|
|
15
|
-
form =
|
|
15
|
+
form = InterfaceAdminForm
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class ColonelPinsInline(admin.TabularInline):
|
|
@@ -71,7 +71,7 @@ class ColonelAdmin(admin.ModelAdmin):
|
|
|
71
71
|
'rebuild_occupied_pins'
|
|
72
72
|
)
|
|
73
73
|
|
|
74
|
-
inlines =
|
|
74
|
+
inlines = InterfaceInline, ColonelPinsInline
|
|
75
75
|
|
|
76
76
|
def get_queryset(self, request):
|
|
77
77
|
qs = super().get_queryset(request)
|
simo/fleet/auto_urls.py
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
from django.urls import path, re_path
|
|
2
|
-
from django.views.generic import TemplateView
|
|
3
2
|
from .views import (
|
|
4
3
|
colonels_ping,
|
|
5
4
|
PinsSelectAutocomplete,
|
|
6
|
-
|
|
5
|
+
InterfaceSelectAutocomplete
|
|
7
6
|
)
|
|
8
7
|
|
|
9
8
|
urlpatterns = [
|
|
@@ -15,7 +14,8 @@ urlpatterns = [
|
|
|
15
14
|
PinsSelectAutocomplete.as_view(), name='autocomplete-colonel-pins'
|
|
16
15
|
),
|
|
17
16
|
path(
|
|
18
|
-
'autocomplete-colonel-
|
|
19
|
-
|
|
17
|
+
'autocomplete-colonel-interfaces',
|
|
18
|
+
InterfaceSelectAutocomplete.as_view(),
|
|
19
|
+
name='autocomplete-interfaces'
|
|
20
20
|
)
|
|
21
21
|
]
|
simo/fleet/controllers.py
CHANGED
|
@@ -24,7 +24,7 @@ from .forms import (
|
|
|
24
24
|
ColonelDHTSensorConfigForm, DS18B20SensorConfigForm,
|
|
25
25
|
BME680SensorConfigForm, MPC9808SensorConfigForm,
|
|
26
26
|
DualMotorValveForm, BlindsConfigForm, BurglarSmokeDetectorConfigForm,
|
|
27
|
-
TTLockConfigForm
|
|
27
|
+
TTLockConfigForm, DALIDeviceConfigForm
|
|
28
28
|
)
|
|
29
29
|
|
|
30
30
|
|
|
@@ -294,6 +294,7 @@ class TTLock(FleeDeviceMixin, Lock):
|
|
|
294
294
|
@classmethod
|
|
295
295
|
def init_discovery(self, form_cleaned_data):
|
|
296
296
|
from simo.core.models import Gateway
|
|
297
|
+
print("INIT discovery form cleaned data: ", form_cleaned_data)
|
|
297
298
|
print("Serialized form: ", serialize_form_data(form_cleaned_data))
|
|
298
299
|
gateway = Gateway.objects.filter(type=self.gateway_class.uid).first()
|
|
299
300
|
gateway.start_discovery(
|
|
@@ -307,7 +308,7 @@ class TTLock(FleeDeviceMixin, Lock):
|
|
|
307
308
|
|
|
308
309
|
@classmethod
|
|
309
310
|
def _process_discovery(cls, started_with, data):
|
|
310
|
-
if data['
|
|
311
|
+
if data['discovery-result'] == 'fail':
|
|
311
312
|
if data['result'] == 0:
|
|
312
313
|
return {'error': 'Internal Colonel error. See Colonel logs.'}
|
|
313
314
|
if data['result'] == 1:
|
|
@@ -348,6 +349,7 @@ class TTLock(FleeDeviceMixin, Lock):
|
|
|
348
349
|
},
|
|
349
350
|
}
|
|
350
351
|
new_component.save()
|
|
352
|
+
new_component.gateway.finish_discovery()
|
|
351
353
|
GatewayObjectCommand(
|
|
352
354
|
new_component.gateway, Colonel(
|
|
353
355
|
id=new_component.config['colonel']
|
|
@@ -417,4 +419,75 @@ class TTLock(FleeDeviceMixin, Lock):
|
|
|
417
419
|
).publish()
|
|
418
420
|
|
|
419
421
|
|
|
422
|
+
class DALIDevice(FleeDeviceMixin):
|
|
423
|
+
gateway_class = FleetGatewayHandler
|
|
424
|
+
config_form = DALIDeviceConfigForm
|
|
425
|
+
discovery_msg = _("Please hook up your new DALI device to your DALI bus.")
|
|
426
|
+
|
|
427
|
+
@classmethod
|
|
428
|
+
def init_discovery(self, form_cleaned_data):
|
|
429
|
+
from simo.core.models import Gateway
|
|
430
|
+
gateway = Gateway.objects.filter(type=self.gateway_class.uid).first()
|
|
431
|
+
gateway.start_discovery(
|
|
432
|
+
self.uid, serialize_form_data(form_cleaned_data),
|
|
433
|
+
timeout=60
|
|
434
|
+
)
|
|
435
|
+
GatewayObjectCommand(
|
|
436
|
+
gateway, form_cleaned_data['colonel'],
|
|
437
|
+
command=f'discover-dali',
|
|
438
|
+
interface=form_cleaned_data['interface'].no
|
|
439
|
+
).publish()
|
|
440
|
+
|
|
441
|
+
@classmethod
|
|
442
|
+
def _process_discovery(cls, started_with, data):
|
|
443
|
+
started_with = deserialize_form_data(started_with)
|
|
444
|
+
form = TTLockConfigForm(controller_uid=cls.uid, data=started_with)
|
|
445
|
+
|
|
446
|
+
if form.is_valid():
|
|
447
|
+
new_component = form.save()
|
|
448
|
+
new_component.config.update(data.get('result', {}).get('config'))
|
|
449
|
+
new_component.meta['finalization_data'] = {
|
|
450
|
+
'temp_id': data['result']['id'],
|
|
451
|
+
'permanent_id': new_component.id,
|
|
452
|
+
'config': {
|
|
453
|
+
'type': cls.uid.split('.')[-1],
|
|
454
|
+
'config': new_component.config,
|
|
455
|
+
'val': False,
|
|
456
|
+
},
|
|
457
|
+
}
|
|
458
|
+
new_component.save()
|
|
459
|
+
new_component.gateway.finish_discovery()
|
|
460
|
+
GatewayObjectCommand(
|
|
461
|
+
new_component.gateway, Colonel(
|
|
462
|
+
id=new_component.config['colonel']
|
|
463
|
+
), command='finalize',
|
|
464
|
+
data=new_component.meta['finalization_data'],
|
|
465
|
+
).publish()
|
|
466
|
+
return [new_component]
|
|
467
|
+
|
|
468
|
+
# Literally impossible, but just in case...
|
|
469
|
+
return {'error': 'INVALID INITIAL DISCOVERY FORM!'}
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
class DALIGear(DALIDevice):
|
|
473
|
+
|
|
474
|
+
def _send_to_device(self, value):
|
|
475
|
+
GatewayObjectCommand(
|
|
476
|
+
self.component.gateway,
|
|
477
|
+
Colonel(id=self.component.config['colonel']),
|
|
478
|
+
set_val=value,
|
|
479
|
+
component_id=self.component.id,
|
|
480
|
+
).publish()
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
class DALILamp(DALIGear, BaseSwitch):
|
|
484
|
+
manual_add = False
|
|
485
|
+
name = 'DALI Lamp'
|
|
486
|
+
discovery_msg = _("Please hook up your new DALI device to your DALI bus.")
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
class DALIDimmableLamp(DALIGear, BaseDimmer):
|
|
490
|
+
manual_add = False
|
|
491
|
+
name = 'DALI Dimmable Lamp'
|
|
492
|
+
discovery_msg = _("Please hook up your new DALI lamp to your DALI bus.")
|
|
420
493
|
|
simo/fleet/forms.py
CHANGED
|
@@ -12,7 +12,8 @@ from simo.core.widgets import LogOutputWidget
|
|
|
12
12
|
from simo.core.utils.easing import EASING_CHOICES
|
|
13
13
|
from simo.core.utils.validators import validate_slaves
|
|
14
14
|
from simo.core.utils.admin import AdminFormActionForm
|
|
15
|
-
from .models import Colonel, ColonelPin,
|
|
15
|
+
from .models import Colonel, ColonelPin, Interface
|
|
16
|
+
from .utils import INTERFACES_PINS_MAP
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
class ColonelPinChoiceField(forms.ModelChoiceField):
|
|
@@ -23,6 +24,11 @@ class ColonelPinChoiceField(forms.ModelChoiceField):
|
|
|
23
24
|
filter_by = 'colonel'
|
|
24
25
|
|
|
25
26
|
|
|
27
|
+
class ColonelInterfacesChoiceField(forms.ModelChoiceField):
|
|
28
|
+
filter_by = 'colonel'
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
26
32
|
class ColonelAdminForm(forms.ModelForm):
|
|
27
33
|
log = forms.CharField(
|
|
28
34
|
widget=forms.HiddenInput, required=False
|
|
@@ -52,34 +58,29 @@ class MoveColonelForm(AdminFormActionForm):
|
|
|
52
58
|
)
|
|
53
59
|
|
|
54
60
|
|
|
55
|
-
class
|
|
56
|
-
scl_pin = ColonelPinChoiceField(
|
|
57
|
-
queryset=ColonelPin.objects.filter(output=True, native=True),
|
|
58
|
-
widget=autocomplete.ListSelect2(
|
|
59
|
-
url='autocomplete-colonel-pins',
|
|
60
|
-
forward=[
|
|
61
|
-
forward.Self(),
|
|
62
|
-
forward.Field('colonel'),
|
|
63
|
-
forward.Const({'output': True, 'native': True}, 'filters')
|
|
64
|
-
]
|
|
65
|
-
)
|
|
66
|
-
)
|
|
67
|
-
sda_pin = ColonelPinChoiceField(
|
|
68
|
-
queryset=ColonelPin.objects.filter(output=True, native=True),
|
|
69
|
-
widget=autocomplete.ListSelect2(
|
|
70
|
-
url='autocomplete-colonel-pins',
|
|
71
|
-
forward=[
|
|
72
|
-
forward.Self(),
|
|
73
|
-
forward.Field('colonel'),
|
|
74
|
-
forward.Const({'output': True, 'native': True}, 'filters')
|
|
75
|
-
]
|
|
76
|
-
)
|
|
77
|
-
)
|
|
61
|
+
class InterfaceAdminForm(forms.ModelForm):
|
|
78
62
|
|
|
79
63
|
class Meta:
|
|
80
|
-
model =
|
|
64
|
+
model = Interface
|
|
81
65
|
fields = '__all__'
|
|
82
66
|
|
|
67
|
+
|
|
68
|
+
def clean(self):
|
|
69
|
+
if self.instance.pk:
|
|
70
|
+
return self.cleaned_data
|
|
71
|
+
|
|
72
|
+
for pin_no in INTERFACES_PINS_MAP[self.cleaned_data['no']]:
|
|
73
|
+
cpin = ColonelPin.objects.get(
|
|
74
|
+
colonel=self.instance.colonel, no=pin_no
|
|
75
|
+
)
|
|
76
|
+
if cpin.occupied_by:
|
|
77
|
+
raise forms.ValidationError(
|
|
78
|
+
f"Interface can not be created, because "
|
|
79
|
+
f"GPIO{cpin} is already occupied by {cpin.occupied_by}."
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
83
84
|
def clean_scl_pin(self):
|
|
84
85
|
if self.cleaned_data['scl_pin'].occupied_by \
|
|
85
86
|
and self.cleaned_data['scl_pin'].occupied_by != self.instance:
|
|
@@ -416,13 +417,16 @@ class ColonelDHTSensorConfigForm(ColonelComponentForm):
|
|
|
416
417
|
|
|
417
418
|
|
|
418
419
|
class BME680SensorConfigForm(ColonelComponentForm):
|
|
419
|
-
|
|
420
|
-
|
|
420
|
+
interface = ColonelInterfacesChoiceField(
|
|
421
|
+
queryset=Interface.objects.filter(type='i2c'),
|
|
421
422
|
widget=autocomplete.ListSelect2(
|
|
422
|
-
url='autocomplete-
|
|
423
|
+
url='autocomplete-interfaces',
|
|
423
424
|
forward=[
|
|
424
425
|
forward.Self(),
|
|
425
426
|
forward.Field('colonel'),
|
|
427
|
+
forward.Const(
|
|
428
|
+
{'type': 'i2c'}, 'filters'
|
|
429
|
+
)
|
|
426
430
|
]
|
|
427
431
|
)
|
|
428
432
|
)
|
|
@@ -437,15 +441,22 @@ class BME680SensorConfigForm(ColonelComponentForm):
|
|
|
437
441
|
|
|
438
442
|
)
|
|
439
443
|
|
|
444
|
+
def save(self, commit=True):
|
|
445
|
+
self.instance.config['i2c_interface'] = self.cleaned_data['interface'].no
|
|
446
|
+
return super().save(commit=commit)
|
|
447
|
+
|
|
440
448
|
|
|
441
449
|
class MPC9808SensorConfigForm(ColonelComponentForm):
|
|
442
|
-
|
|
443
|
-
|
|
450
|
+
interface = ColonelInterfacesChoiceField(
|
|
451
|
+
queryset=Interface.objects.filter(type='i2c'),
|
|
444
452
|
widget=autocomplete.ListSelect2(
|
|
445
|
-
url='autocomplete-
|
|
453
|
+
url='autocomplete-interfaces',
|
|
446
454
|
forward=[
|
|
447
455
|
forward.Self(),
|
|
448
456
|
forward.Field('colonel'),
|
|
457
|
+
forward.Const(
|
|
458
|
+
{'type': 'i2c'}, 'filters'
|
|
459
|
+
)
|
|
449
460
|
]
|
|
450
461
|
)
|
|
451
462
|
)
|
|
@@ -460,6 +471,10 @@ class MPC9808SensorConfigForm(ColonelComponentForm):
|
|
|
460
471
|
|
|
461
472
|
)
|
|
462
473
|
|
|
474
|
+
def save(self, commit=True):
|
|
475
|
+
self.instance.config['i2c_interface'] = self.cleaned_data['interface'].no
|
|
476
|
+
return super().save(commit=commit)
|
|
477
|
+
|
|
463
478
|
|
|
464
479
|
class ColonelTouchSensorConfigForm(ColonelComponentForm):
|
|
465
480
|
pin = ColonelPinChoiceField(
|
|
@@ -987,7 +1002,6 @@ class BurglarSmokeDetectorConfigForm(ColonelComponentForm):
|
|
|
987
1002
|
|
|
988
1003
|
|
|
989
1004
|
class TTLockConfigForm(ColonelComponentForm):
|
|
990
|
-
pass
|
|
991
1005
|
|
|
992
1006
|
def clean(self):
|
|
993
1007
|
if not self.instance or not self.instance.pk:
|
|
@@ -1002,7 +1016,6 @@ class TTLockConfigForm(ColonelComponentForm):
|
|
|
1002
1016
|
)
|
|
1003
1017
|
return self.cleaned_data
|
|
1004
1018
|
|
|
1005
|
-
|
|
1006
1019
|
def save(self, commit=True):
|
|
1007
1020
|
obj = super(ColonelComponentForm, self).save(commit)
|
|
1008
1021
|
if commit:
|
|
@@ -1010,3 +1023,25 @@ class TTLockConfigForm(ColonelComponentForm):
|
|
|
1010
1023
|
self.cleaned_data['colonel'].save()
|
|
1011
1024
|
return obj
|
|
1012
1025
|
|
|
1026
|
+
|
|
1027
|
+
|
|
1028
|
+
class DALIDeviceConfigForm(ColonelComponentForm):
|
|
1029
|
+
interface = ColonelInterfacesChoiceField(
|
|
1030
|
+
queryset=Interface.objects.filter(type='dali'),
|
|
1031
|
+
widget=autocomplete.ListSelect2(
|
|
1032
|
+
url='autocomplete-interfaces',
|
|
1033
|
+
forward=[
|
|
1034
|
+
forward.Self(),
|
|
1035
|
+
forward.Field('colonel'),
|
|
1036
|
+
forward.Const(
|
|
1037
|
+
{'type': 'dali'}, 'filters'
|
|
1038
|
+
)
|
|
1039
|
+
]
|
|
1040
|
+
)
|
|
1041
|
+
)
|
|
1042
|
+
|
|
1043
|
+
def save(self, commit=True):
|
|
1044
|
+
self.instance.config['dali_interface'] = self.cleaned_data['interface'].no
|
|
1045
|
+
return super().save(commit=commit)
|
|
1046
|
+
|
|
1047
|
+
|
simo/fleet/gateways.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import datetime
|
|
2
|
+
import time
|
|
2
3
|
from django.utils import timezone
|
|
3
4
|
from simo.core.gateways import BaseObjectCommandsGatewayHandler
|
|
4
5
|
from simo.core.forms import BaseGatewayForm
|
|
@@ -14,7 +15,7 @@ class FleetGatewayHandler(BaseObjectCommandsGatewayHandler):
|
|
|
14
15
|
periodic_tasks = (
|
|
15
16
|
('look_for_updates', 600),
|
|
16
17
|
('watch_colonels_connection', 30),
|
|
17
|
-
('push_discoveries',
|
|
18
|
+
('push_discoveries', 10),
|
|
18
19
|
)
|
|
19
20
|
|
|
20
21
|
def _on_mqtt_message(self, client, userdata, msg):
|
|
@@ -37,9 +38,12 @@ class FleetGatewayHandler(BaseObjectCommandsGatewayHandler):
|
|
|
37
38
|
def push_discoveries(self):
|
|
38
39
|
from .models import Colonel
|
|
39
40
|
for gw in Gateway.objects.filter(
|
|
40
|
-
type=self.uid,
|
|
41
|
-
discovery__has_key='start',
|
|
41
|
+
type=self.uid, discovery__has_key='start',
|
|
42
42
|
).exclude(discovery__has_key='finished'):
|
|
43
|
+
if time.time() - gw.discovery.get('last_check') > 10:
|
|
44
|
+
gw.finish_discovery()
|
|
45
|
+
continue
|
|
46
|
+
|
|
43
47
|
colonel = Colonel.objects.get(
|
|
44
48
|
id=gw.discovery['init_data']['colonel']['val'][0]['pk']
|
|
45
49
|
)
|
simo/fleet/managers.py
CHANGED
|
@@ -22,11 +22,13 @@ class ColonelPinsManager(models.Manager):
|
|
|
22
22
|
return qs
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
class
|
|
25
|
+
class InterfacesManager(models.Manager):
|
|
26
26
|
|
|
27
27
|
def get_queryset(self):
|
|
28
28
|
qs = super().get_queryset()
|
|
29
29
|
instance = get_current_instance()
|
|
30
30
|
if instance:
|
|
31
31
|
qs = qs.filter(colonel__instance=instance)
|
|
32
|
-
return qs
|
|
32
|
+
return qs
|
|
33
|
+
|
|
34
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Generated by Django 3.2.9 on 2024-04-15 07:36
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
import django.db.models.deletion
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
|
|
9
|
+
dependencies = [
|
|
10
|
+
('fleet', '0031_alter_colonel_type'),
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
operations = [
|
|
14
|
+
migrations.AlterField(
|
|
15
|
+
model_name='colonel',
|
|
16
|
+
name='logs_stream',
|
|
17
|
+
field=models.BooleanField(default=False, help_text='ATENTION! Causes serious overhead and significantly degrades the lifespan of a chip due to a lot of writes to the memory. It also causes Colonel websocket to run out of memory and reset if a lot of data is being transmitted. Leave this off, unleess you know what you are doing!'),
|
|
18
|
+
),
|
|
19
|
+
migrations.CreateModel(
|
|
20
|
+
name='Interface',
|
|
21
|
+
fields=[
|
|
22
|
+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
23
|
+
('no', models.PositiveIntegerField(choices=[(1, '1'), (2, '2')])),
|
|
24
|
+
('type', models.CharField(choices=[('i2c', 'I2C'), ('dali', 'DALI')], max_length=20)),
|
|
25
|
+
('pin_a', models.ForeignKey(editable=False, limit_choices_to={'native': True, 'output': True}, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='interface_a', to='fleet.colonelpin')),
|
|
26
|
+
('pin_b', models.ForeignKey(editable=False, limit_choices_to={'native': True, 'output': True}, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='interface_b', to='fleet.colonelpin')),
|
|
27
|
+
('colonel', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='interfaces', to='fleet.colonel')),
|
|
28
|
+
],
|
|
29
|
+
options={
|
|
30
|
+
'unique_together': {('colonel', 'no')},
|
|
31
|
+
},
|
|
32
|
+
),
|
|
33
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Generated by Django 3.2.9 on 2024-04-15 07:36
|
|
2
|
+
|
|
3
|
+
from django.db import migrations
|
|
4
|
+
|
|
5
|
+
def create_objects(apps, schema_editor):
|
|
6
|
+
I2CInterface = apps.get_model("fleet", "I2CInterface")
|
|
7
|
+
Interface = apps.get_model("fleet", "Interface")
|
|
8
|
+
for i2c_i in I2CInterface.objects.filter(no__gt=0):
|
|
9
|
+
Interface.objects.create(
|
|
10
|
+
colonel=i2c_i.colonel, type='i2c',
|
|
11
|
+
pin_a=i2c_i.scl_pin, pin_b=i2c_i.sda_pin
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def delete_objects(apps, schema_editor):
|
|
16
|
+
Interface = apps.get_model("fleet", "Interface")
|
|
17
|
+
Interface.delete()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Migration(migrations.Migration):
|
|
21
|
+
|
|
22
|
+
dependencies = [
|
|
23
|
+
('fleet', '0032_auto_20240415_0736'),
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
operations = [
|
|
27
|
+
migrations.RunPython(create_objects, delete_objects),
|
|
28
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
simo/fleet/migrations/__pycache__/0028_remove_i2cinterface_scl_pin_no_and_more.cpython-38.pyc
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|