simo 1.7.20__py3-none-any.whl → 2.0.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/__pycache__/asgi.cpython-38.pyc +0 -0
- simo/__pycache__/settings.cpython-38.pyc +0 -0
- simo/__pycache__/urls.cpython-38.pyc +0 -0
- simo/__pycache__/wsgi.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_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__/signal_receivers.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 +28 -18
- simo/core/api.py +157 -16
- simo/core/api_meta.py +87 -0
- simo/core/auto_urls.py +4 -1
- simo/core/autocomplete_views.py +8 -4
- simo/core/base_types.py +1 -0
- simo/core/context.py +3 -1
- simo/core/controllers.py +134 -36
- simo/core/db_backend/base.py +7 -22
- simo/core/drf_braces/README +3 -0
- simo/core/drf_braces/__init__.py +7 -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/__init__.py +5 -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/fields/_fields.py +48 -0
- simo/core/drf_braces/fields/custom.py +107 -0
- simo/core/drf_braces/fields/mixins.py +58 -0
- simo/core/drf_braces/fields/modified.py +41 -0
- simo/core/drf_braces/forms/__init__.py +0 -0
- simo/core/drf_braces/forms/fields.py +20 -0
- simo/core/drf_braces/forms/serializer_form.py +156 -0
- simo/core/drf_braces/mixins.py +52 -0
- simo/core/drf_braces/models.py +0 -0
- simo/core/drf_braces/parsers.py +72 -0
- simo/core/drf_braces/renderers.py +37 -0
- simo/core/drf_braces/serializers/__init__.py +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/drf_braces/serializers/enforce_validation_serializer.py +214 -0
- simo/core/drf_braces/serializers/form_serializer.py +391 -0
- simo/core/drf_braces/serializers/swapping.py +48 -0
- simo/core/drf_braces/tests/__init__.py +0 -0
- simo/core/drf_braces/tests/fields/__init__.py +0 -0
- simo/core/drf_braces/tests/fields/test_custom.py +94 -0
- simo/core/drf_braces/tests/fields/test_fields.py +13 -0
- simo/core/drf_braces/tests/fields/test_mixins.py +96 -0
- simo/core/drf_braces/tests/fields/test_modified.py +40 -0
- simo/core/drf_braces/tests/forms/__init__.py +0 -0
- simo/core/drf_braces/tests/forms/test_fields.py +46 -0
- simo/core/drf_braces/tests/forms/test_serializer_form.py +256 -0
- simo/core/drf_braces/tests/serializers/__init__.py +0 -0
- simo/core/drf_braces/tests/serializers/test_enforce_validation_serializer.py +169 -0
- simo/core/drf_braces/tests/serializers/test_form_serializer.py +387 -0
- simo/core/drf_braces/tests/serializers/test_swapping.py +40 -0
- simo/core/drf_braces/tests/test_mixins.py +111 -0
- simo/core/drf_braces/tests/test_parsers.py +73 -0
- simo/core/drf_braces/tests/test_renderers.py +23 -0
- simo/core/drf_braces/tests/test_utils.py +73 -0
- simo/core/drf_braces/utils.py +209 -0
- simo/core/events.py +3 -3
- simo/core/forms.py +79 -37
- simo/core/gateways.py +31 -14
- simo/core/management/commands/gateways_manager.py +0 -1
- simo/core/managers.py +81 -0
- simo/core/middleware.py +25 -0
- simo/core/migrations/0026_category_instance.py +20 -0
- simo/core/migrations/0027_remove_component_tags.py +17 -0
- simo/core/migrations/0028_rename_subcomponents_component_slaves.py +18 -0
- simo/core/migrations/0029_auto_20240229_1331.py +33 -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/models.py +103 -66
- simo/core/permissions.py +28 -2
- simo/core/serializers.py +330 -26
- simo/core/socket_consumers.py +5 -14
- simo/core/tasks.py +11 -1
- simo/core/templates/admin/base.html +37 -10
- simo/core/templates/admin/wizard/discovery.html +188 -0
- simo/core/templates/admin/wizard/wizard_add.html +5 -5
- simo/core/utils/__pycache__/serialization.cpython-38.pyc +0 -0
- simo/core/utils/admin.py +9 -2
- simo/core/utils/formsets.py +17 -16
- simo/core/utils/helpers.py +1 -0
- simo/core/utils/serialization.py +56 -0
- simo/core/utils/type_constants.py +1 -1
- simo/core/utils/validators.py +14 -1
- simo/core/views.py +13 -0
- 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 +53 -29
- simo/fleet/api.py +59 -3
- simo/fleet/auto_urls.py +2 -3
- simo/fleet/controllers.py +199 -16
- simo/fleet/forms.py +325 -483
- simo/fleet/gateways.py +44 -2
- simo/fleet/managers.py +32 -0
- simo/fleet/migrations/0025_auto_20240130_1334.py +27 -0
- simo/fleet/migrations/0026_rename_i2cinterface_scl_pin_and_more.py +64 -0
- simo/fleet/migrations/0027_auto_20240306_0802.py +170 -0
- simo/fleet/migrations/0028_remove_i2cinterface_scl_pin_no_and_more.py +21 -0
- simo/fleet/migrations/0029_alter_i2cinterface_scl_pin_and_more.py +24 -0
- simo/fleet/migrations/0030_colonelpin_label_alter_colonel_type.py +24 -0
- simo/fleet/migrations/0031_alter_colonel_type.py +18 -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/models.py +140 -82
- simo/fleet/serializers.py +35 -1
- simo/fleet/socket_consumers.py +239 -76
- simo/fleet/utils.py +15 -53
- simo/fleet/views.py +28 -14
- simo/generic/controllers.py +13 -89
- simo/generic/forms.py +29 -18
- simo/generic/gateways.py +73 -2
- simo/generic/models.py +3 -3
- simo/multimedia/controllers.py +9 -8
- simo/settings.py +7 -4
- simo/urls.py +4 -8
- 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__/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/admin.py +8 -1
- simo/users/api.py +38 -2
- simo/users/auto_urls.py +2 -2
- simo/users/migrations/0025_rename_name_fingerprint_type_and_more.py +22 -0
- simo/users/migrations/__pycache__/0025_rename_name_fingerprint_type_and_more.cpython-38.pyc +0 -0
- simo/users/models.py +2 -3
- simo/users/serializers.py +15 -1
- simo/users/sso_urls.py +3 -3
- simo/wsgi.py +7 -0
- {simo-1.7.20.dist-info → simo-2.0.1.dist-info}/METADATA +8 -9
- {simo-1.7.20.dist-info → simo-2.0.1.dist-info}/RECORD +173 -189
- {simo-1.7.20.dist-info → simo-2.0.1.dist-info}/WHEEL +1 -1
- simo/core/db_backend/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/core/db_backend/__pycache__/base.cpython-38.pyc +0 -0
- simo/core/management/commands/__pycache__/gateways_manager.cpython-38.pyc +0 -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__/__init__.cpython-38.pyc +0 -0
- 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__/type_constants.cpython-38.pyc +0 -0
- simo/core/utils/__pycache__/validators.cpython-38.pyc +0 -0
- simo/fleet/tasks.py +0 -25
- simo/generic/__pycache__/__init__.cpython-37.pyc +0 -0
- 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-37.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/__pycache__/widgets.cpython-37.pyc +0 -0
- 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/__pycache__/utils.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/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__/__init__.cpython-38.pyc +0 -0
- {simo-1.7.20.dist-info → simo-2.0.1.dist-info}/LICENSE.md +0 -0
- {simo-1.7.20.dist-info → simo-2.0.1.dist-info}/top_level.txt +0 -0
simo/fleet/models.py
CHANGED
|
@@ -3,12 +3,15 @@ from django.db import transaction
|
|
|
3
3
|
from django.db import models
|
|
4
4
|
from django.db.models.signals import post_save, pre_delete, post_delete
|
|
5
5
|
from django.dispatch import receiver
|
|
6
|
+
from django.contrib.contenttypes.models import ContentType
|
|
7
|
+
from django.contrib.contenttypes.fields import GenericForeignKey
|
|
6
8
|
from dirtyfields import DirtyFieldsMixin
|
|
7
9
|
from simo.core.models import Instance, Gateway, Component
|
|
8
10
|
from simo.core.utils.helpers import get_random_string
|
|
9
11
|
from simo.core.events import GatewayObjectCommand
|
|
10
12
|
from .gateways import FleetGatewayHandler
|
|
11
|
-
from .
|
|
13
|
+
from .managers import ColonelsManager, ColonelPinsManager, I2CInterfacesManager
|
|
14
|
+
from .utils import GPIO_PINS
|
|
12
15
|
|
|
13
16
|
|
|
14
17
|
|
|
@@ -49,10 +52,11 @@ class Colonel(DirtyFieldsMixin, models.Model):
|
|
|
49
52
|
)
|
|
50
53
|
name = models.CharField(max_length=100, blank=True)
|
|
51
54
|
type = models.CharField(
|
|
52
|
-
max_length=20, default='
|
|
55
|
+
max_length=20, default='ample-wall',
|
|
53
56
|
choices=(
|
|
54
|
-
('
|
|
55
|
-
('ample-wall', "Ample Wall")
|
|
57
|
+
('4-relays', "4 Relay"),
|
|
58
|
+
('ample-wall', "Ample Wall"),
|
|
59
|
+
('game-changer', "Game Changer"),
|
|
56
60
|
)
|
|
57
61
|
)
|
|
58
62
|
firmware_version = models.CharField(
|
|
@@ -81,11 +85,11 @@ class Colonel(DirtyFieldsMixin, models.Model):
|
|
|
81
85
|
default=False, help_text="Might cause unnecessary overhead. "
|
|
82
86
|
"Better to leave this off if things are running smoothly."
|
|
83
87
|
)
|
|
84
|
-
pwm_frequency = models.IntegerField(default=
|
|
88
|
+
pwm_frequency = models.IntegerField(default=1, choices=(
|
|
85
89
|
(0, "3kHz"), (1, "22kHz")
|
|
86
90
|
), help_text="Affects Ample Wall dimmer PWM output (dimmer) frequency")
|
|
87
91
|
|
|
88
|
-
|
|
92
|
+
objects = ColonelsManager()
|
|
89
93
|
|
|
90
94
|
def __str__(self):
|
|
91
95
|
return self.name if self.name else self.uid
|
|
@@ -95,20 +99,13 @@ class Colonel(DirtyFieldsMixin, models.Model):
|
|
|
95
99
|
for comp in self.components.all():
|
|
96
100
|
comp.alive = self.is_connected
|
|
97
101
|
comp.save()
|
|
98
|
-
is_new = self.pk is None
|
|
99
102
|
|
|
100
103
|
if self.minor_upgrade_available and self.firmware_version == self.minor_upgrade_available:
|
|
101
104
|
self.minor_upgrade_available = None
|
|
102
105
|
if self.major_upgrade_available and self.firmware_version == self.major_upgrade_available:
|
|
103
106
|
self.major_upgrade_available = None
|
|
104
107
|
|
|
105
|
-
|
|
106
|
-
if is_new and self.type == 'ample-wall':
|
|
107
|
-
I2CInterface.objects.create(
|
|
108
|
-
colonel=self, name="Main", no=0, scl_pin=4, sda_pin=15,
|
|
109
|
-
freq=100000
|
|
110
|
-
)
|
|
111
|
-
return obj
|
|
108
|
+
return super().save(*args, **kwargs)
|
|
112
109
|
|
|
113
110
|
@property
|
|
114
111
|
def is_connected(self):
|
|
@@ -161,21 +158,28 @@ class Colonel(DirtyFieldsMixin, models.Model):
|
|
|
161
158
|
gateway, self, command='update_config'
|
|
162
159
|
).publish()
|
|
163
160
|
|
|
161
|
+
@transaction.atomic
|
|
164
162
|
def rebuild_occupied_pins(self):
|
|
165
|
-
|
|
163
|
+
for pin in ColonelPin.objects.filter(colonel=self):
|
|
164
|
+
if isinstance(pin.occupied_by, Component):
|
|
165
|
+
pin.occupied_by_content_type = None
|
|
166
|
+
pin.occupied_by_id = None
|
|
167
|
+
pin.save()
|
|
168
|
+
|
|
166
169
|
for component in self.components.all():
|
|
167
170
|
try:
|
|
168
171
|
pins = component.controller._get_occupied_pins()
|
|
169
172
|
except:
|
|
170
173
|
pins = []
|
|
171
|
-
for
|
|
172
|
-
|
|
174
|
+
for no in pins:
|
|
175
|
+
pin, new = ColonelPin.objects.get_or_create(colonel=self, no=no)
|
|
176
|
+
pin.occupied_by = component
|
|
177
|
+
pin.save()
|
|
173
178
|
|
|
174
|
-
for i2c_interface in self.i2c_interfaces.all():
|
|
175
|
-
self.occupied_pins[i2c_interface.scl_pin] = 'scl_%d' % i2c_interface.no
|
|
176
|
-
self.occupied_pins[i2c_interface.sda_pin] = 'sda_%d' % i2c_interface.no
|
|
177
179
|
|
|
180
|
+
@transaction.atomic()
|
|
178
181
|
def move_to(self, other_colonel):
|
|
182
|
+
# TODO: Need to replace pins on components!
|
|
179
183
|
other_colonel.refresh_from_db()
|
|
180
184
|
assert list(other_colonel.components.all()) == [], \
|
|
181
185
|
"Other colonel must be completely empty!"
|
|
@@ -184,21 +188,102 @@ class Colonel(DirtyFieldsMixin, models.Model):
|
|
|
184
188
|
component.config['colonel'] = other_colonel.id
|
|
185
189
|
component.save()
|
|
186
190
|
self.components.remove(component)
|
|
187
|
-
other_colonel.add(component)
|
|
188
|
-
|
|
189
|
-
other_colonel.i2c_interfaces.all().delete()
|
|
190
|
-
self.i2c_interfaces.all().update(colonel=other_colonel)
|
|
191
|
+
other_colonel.components.add(component)
|
|
191
192
|
|
|
192
193
|
self.rebuild_occupied_pins()
|
|
193
|
-
self.save()
|
|
194
|
-
|
|
195
194
|
other_colonel.rebuild_occupied_pins()
|
|
196
|
-
|
|
195
|
+
|
|
196
|
+
other_colonel.i2c_interfaces.all().delete()
|
|
197
|
+
|
|
198
|
+
for i2c_interface in self.i2c_interfaces.all():
|
|
199
|
+
I2CInterface.objects.create(
|
|
200
|
+
colonel=other_colonel, name=i2c_interface.name,
|
|
201
|
+
freq=i2c_interface.freq,
|
|
202
|
+
scl_pin=ColonelPin.objects.get(
|
|
203
|
+
colonel=other_colonel, no=i2c_interface.scl_pin.no,
|
|
204
|
+
),
|
|
205
|
+
sda_pin=ColonelPin.objects.get(
|
|
206
|
+
colonel=other_colonel, no=i2c_interface.sda_pin.no,
|
|
207
|
+
),
|
|
208
|
+
)
|
|
197
209
|
|
|
198
210
|
self.update_config()
|
|
199
211
|
other_colonel.update_config()
|
|
200
212
|
|
|
201
213
|
|
|
214
|
+
class ColonelPin(models.Model):
|
|
215
|
+
colonel = models.ForeignKey(
|
|
216
|
+
Colonel, related_name='pins', on_delete=models.CASCADE
|
|
217
|
+
)
|
|
218
|
+
no = models.PositiveIntegerField()
|
|
219
|
+
label = models.CharField(db_index=True, max_length=200)
|
|
220
|
+
input = models.BooleanField(default=False, db_index=True)
|
|
221
|
+
output = models.BooleanField(default=False, db_index=True)
|
|
222
|
+
capacitive = models.BooleanField(default=False, db_index=True)
|
|
223
|
+
adc = models.BooleanField(default=False)
|
|
224
|
+
native = models.BooleanField(default=True, db_index=True)
|
|
225
|
+
default_pull = models.CharField(
|
|
226
|
+
max_length=50, db_index=True, null=True, blank=True,
|
|
227
|
+
choices=(('LOW', "LOW"), ("HIGH", "HIGH"))
|
|
228
|
+
)
|
|
229
|
+
note = models.CharField(max_length=100)
|
|
230
|
+
occupied_by_content_type = models.ForeignKey(
|
|
231
|
+
ContentType, on_delete=models.CASCADE, null=True
|
|
232
|
+
)
|
|
233
|
+
occupied_by_id = models.PositiveIntegerField(null=True)
|
|
234
|
+
occupied_by = GenericForeignKey(
|
|
235
|
+
"occupied_by_content_type", "occupied_by_id"
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
objects = ColonelPinsManager()
|
|
239
|
+
|
|
240
|
+
class Meta:
|
|
241
|
+
unique_together = 'colonel', 'no'
|
|
242
|
+
indexes = [
|
|
243
|
+
models.Index(
|
|
244
|
+
fields=["occupied_by_content_type", "occupied_by_id"]
|
|
245
|
+
),
|
|
246
|
+
]
|
|
247
|
+
def __str__(self):
|
|
248
|
+
if not self.label:
|
|
249
|
+
# Might be created via migration...
|
|
250
|
+
self.save()
|
|
251
|
+
return self.label
|
|
252
|
+
|
|
253
|
+
def save(self, *args, **kwargs):
|
|
254
|
+
if self.native:
|
|
255
|
+
self.label = f'GPIO{self.no}'
|
|
256
|
+
else:
|
|
257
|
+
no = self.no - 100
|
|
258
|
+
self.label = f'IO{no}'
|
|
259
|
+
if self.note:
|
|
260
|
+
self.label += ' | %s' % self.note
|
|
261
|
+
return super().save(*args, **kwargs)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
@receiver(post_save, sender=Colonel)
|
|
265
|
+
def after_colonel_save(sender, instance, created, *args, **kwargs):
|
|
266
|
+
if not created:
|
|
267
|
+
return
|
|
268
|
+
|
|
269
|
+
def after_update():
|
|
270
|
+
for no, data in GPIO_PINS.get(instance.type).items():
|
|
271
|
+
ColonelPin.objects.get_or_create(
|
|
272
|
+
colonel=instance, no=no,
|
|
273
|
+
input=data.get('input'), output=data.get('output'),
|
|
274
|
+
capacitive=data.get('capacitive'), adc=data.get('adc'),
|
|
275
|
+
native=data.get('native'), note=data.get('note')
|
|
276
|
+
)
|
|
277
|
+
if instance.type in ('ample-wall', 'game-changer'):
|
|
278
|
+
I2CInterface.objects.create(
|
|
279
|
+
colonel=instance, name='Main', no=0,
|
|
280
|
+
scl_pin=ColonelPin.objects.get(colonel=instance, no=4),
|
|
281
|
+
sda_pin=ColonelPin.objects.get(colonel=instance, no=15),
|
|
282
|
+
)
|
|
283
|
+
instance.update_config()
|
|
284
|
+
|
|
285
|
+
transaction.on_commit(after_update)
|
|
286
|
+
|
|
202
287
|
|
|
203
288
|
@receiver(pre_delete, sender=Component)
|
|
204
289
|
def post_component_delete(sender, instance, *args, **kwargs):
|
|
@@ -211,8 +296,7 @@ def post_component_delete(sender, instance, *args, **kwargs):
|
|
|
211
296
|
for colonel in affected_colonels:
|
|
212
297
|
print("Rebuild occupied pins for :", colonel)
|
|
213
298
|
colonel.rebuild_occupied_pins()
|
|
214
|
-
colonel.
|
|
215
|
-
colonel.restart()
|
|
299
|
+
colonel.update_config()
|
|
216
300
|
|
|
217
301
|
transaction.on_commit(update_colonel)
|
|
218
302
|
|
|
@@ -231,12 +315,24 @@ class I2CInterface(models.Model):
|
|
|
231
315
|
no = models.IntegerField(
|
|
232
316
|
default=0, choices=i2c_interface_no_choices
|
|
233
317
|
)
|
|
234
|
-
scl_pin = models.
|
|
235
|
-
|
|
318
|
+
scl_pin = models.ForeignKey(
|
|
319
|
+
ColonelPin, on_delete=models.CASCADE, limit_choices_to={
|
|
320
|
+
'native': True, 'output': True,
|
|
321
|
+
},
|
|
322
|
+
null=True, related_name='i2c_scl'
|
|
323
|
+
)
|
|
324
|
+
sda_pin = models.ForeignKey(
|
|
325
|
+
ColonelPin, on_delete=models.CASCADE, limit_choices_to={
|
|
326
|
+
'native': True, 'output': True,
|
|
327
|
+
},
|
|
328
|
+
null=True, related_name='i2c_sda'
|
|
329
|
+
)
|
|
236
330
|
freq = models.IntegerField(
|
|
237
331
|
default=100000, help_text="100000 - is a good middle point!"
|
|
238
332
|
)
|
|
239
333
|
|
|
334
|
+
objects = I2CInterfacesManager()
|
|
335
|
+
|
|
240
336
|
class Meta:
|
|
241
337
|
unique_together = 'colonel', 'no'
|
|
242
338
|
|
|
@@ -244,56 +340,18 @@ class I2CInterface(models.Model):
|
|
|
244
340
|
return self.name
|
|
245
341
|
|
|
246
342
|
|
|
247
|
-
@receiver(post_delete, sender=I2CInterface)
|
|
248
|
-
def post_i2c_interface_delete(sender, instance, *args, **kwargs):
|
|
249
|
-
|
|
250
|
-
def update_colonel():
|
|
251
|
-
try:
|
|
252
|
-
instance.colonel.rebuild_occupied_pins()
|
|
253
|
-
instance.colonel.save()
|
|
254
|
-
except Colonel.DoesNotExist: # deleting colonel
|
|
255
|
-
pass
|
|
256
|
-
transaction.on_commit(update_colonel)
|
|
257
|
-
|
|
258
|
-
|
|
259
343
|
@receiver(post_save, sender=I2CInterface)
|
|
260
344
|
def post_i2c_interface_delete(sender, instance, *args, **kwargs):
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
# (BLE_DEVICE_TYPE_GOVEE_MULTISENSOR, "GOVEE Climate sensor")
|
|
275
|
-
# ))
|
|
276
|
-
# last_seen = models.DateTimeField(auto_now_add=True)
|
|
277
|
-
# component = models.ForeignKey(
|
|
278
|
-
# Component, null=True, blank=True, on_delete=models.SET_NULL,
|
|
279
|
-
# help_text='Only for tracking if it is already used as a component'
|
|
280
|
-
# )
|
|
281
|
-
# colonels = models.ManyToManyField(
|
|
282
|
-
# Colonel, through='ColonelBLEDevice', related_name='ble_devices'
|
|
283
|
-
# )
|
|
284
|
-
#
|
|
285
|
-
# def __str__(self):
|
|
286
|
-
# return '%s (%s)' % (self.name, self.mac)
|
|
287
|
-
#
|
|
288
|
-
#
|
|
289
|
-
# class ColonelBLEDevice(models.Model):
|
|
290
|
-
# colonel = models.ForeignKey(Colonel, on_delete=models.CASCADE)
|
|
291
|
-
# device = models.ForeignKey(BLEDevice, on_delete=models.CASCADE)
|
|
292
|
-
# last_seen = models.DateTimeField(auto_now_add=True)
|
|
293
|
-
# data = JSONField(default={})
|
|
294
|
-
#
|
|
295
|
-
# def save(self, *args, **kwargs):
|
|
296
|
-
# obj = super().save(*args, **kwargs)
|
|
297
|
-
# self.device.last_seen = self.last_seen
|
|
298
|
-
# self.device.save()
|
|
299
|
-
# return obj
|
|
345
|
+
with transaction.atomic():
|
|
346
|
+
ct = ContentType.objects.get_for_model(instance)
|
|
347
|
+
for pin in ColonelPin.objects.filter(
|
|
348
|
+
occupied_by_content_type=ct,
|
|
349
|
+
occupied_by_id=instance.id
|
|
350
|
+
):
|
|
351
|
+
pin.occupied_by_content_type = None
|
|
352
|
+
pin.occupied_by_content_id = None
|
|
353
|
+
pin.save()
|
|
354
|
+
instance.scl_pin.occupied_by = instance
|
|
355
|
+
instance.scl_pin.save()
|
|
356
|
+
instance.sda_pin.occupied_by = instance
|
|
357
|
+
instance.sda_pin.save()
|
simo/fleet/serializers.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from rest_framework import serializers
|
|
2
|
-
from .models import InstanceOptions
|
|
2
|
+
from .models import InstanceOptions, Colonel, ColonelPin
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class InstanceOptionsSerializer(serializers.ModelSerializer):
|
|
@@ -11,3 +11,37 @@ class InstanceOptionsSerializer(serializers.ModelSerializer):
|
|
|
11
11
|
|
|
12
12
|
def get_instance(self, obj):
|
|
13
13
|
return obj.instance.uid
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ColonelPinSerializer(serializers.ModelSerializer):
|
|
17
|
+
occupied = serializers.SerializerMethodField()
|
|
18
|
+
|
|
19
|
+
class Meta:
|
|
20
|
+
model = ColonelPin
|
|
21
|
+
fields = 'id', 'label', 'occupied'
|
|
22
|
+
read_only_fields = fields
|
|
23
|
+
|
|
24
|
+
def get_occupied(self, obj):
|
|
25
|
+
return bool(obj.occupied_by)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ColonelSerializer(serializers.ModelSerializer):
|
|
29
|
+
pins = serializers.SerializerMethodField()
|
|
30
|
+
|
|
31
|
+
class Meta:
|
|
32
|
+
model = Colonel
|
|
33
|
+
fields = (
|
|
34
|
+
'id', 'uid', 'name', 'type', 'firmware_version', 'firmware_auto_update',
|
|
35
|
+
'socket_connected', 'last_seen', 'enabled', 'pwm_frequency',
|
|
36
|
+
'logs_stream', 'pins'
|
|
37
|
+
)
|
|
38
|
+
read_only_fields = [
|
|
39
|
+
'uid', 'type', 'firmware_version', 'socket_connected',
|
|
40
|
+
'last_seen', 'pins'
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
def get_pins(self, obj):
|
|
44
|
+
result = []
|
|
45
|
+
for pin in obj.pins.all():
|
|
46
|
+
result.append(ColonelPinSerializer(pin).data)
|
|
47
|
+
return result
|