simo 1.7.20__py3-none-any.whl → 2.0.0__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.

Files changed (267) hide show
  1. simo/__pycache__/asgi.cpython-38.pyc +0 -0
  2. simo/__pycache__/settings.cpython-38.pyc +0 -0
  3. simo/__pycache__/urls.cpython-38.pyc +0 -0
  4. simo/__pycache__/wsgi.cpython-38.pyc +0 -0
  5. simo/core/__pycache__/admin.cpython-38.pyc +0 -0
  6. simo/core/__pycache__/api.cpython-38.pyc +0 -0
  7. simo/core/__pycache__/api_meta.cpython-38.pyc +0 -0
  8. simo/core/__pycache__/auto_urls.cpython-38.pyc +0 -0
  9. simo/core/__pycache__/autocomplete_views.cpython-38.pyc +0 -0
  10. simo/core/__pycache__/base_types.cpython-38.pyc +0 -0
  11. simo/core/__pycache__/context.cpython-38.pyc +0 -0
  12. simo/core/__pycache__/controllers.cpython-38.pyc +0 -0
  13. simo/core/__pycache__/events.cpython-38.pyc +0 -0
  14. simo/core/__pycache__/forms.cpython-38.pyc +0 -0
  15. simo/core/__pycache__/gateways.cpython-38.pyc +0 -0
  16. simo/core/__pycache__/managers.cpython-38.pyc +0 -0
  17. simo/core/__pycache__/middleware.cpython-38.pyc +0 -0
  18. simo/core/__pycache__/models.cpython-38.pyc +0 -0
  19. simo/core/__pycache__/permissions.cpython-38.pyc +0 -0
  20. simo/core/__pycache__/serializers.cpython-38.pyc +0 -0
  21. simo/core/__pycache__/signal_receivers.cpython-38.pyc +0 -0
  22. simo/core/__pycache__/socket_consumers.cpython-38.pyc +0 -0
  23. simo/core/__pycache__/tasks.cpython-38.pyc +0 -0
  24. simo/core/__pycache__/views.cpython-38.pyc +0 -0
  25. simo/core/admin.py +28 -18
  26. simo/core/api.py +157 -16
  27. simo/core/api_meta.py +87 -0
  28. simo/core/auto_urls.py +4 -1
  29. simo/core/autocomplete_views.py +8 -4
  30. simo/core/base_types.py +1 -0
  31. simo/core/context.py +3 -1
  32. simo/core/controllers.py +112 -32
  33. simo/core/db_backend/base.py +7 -22
  34. simo/core/drf_braces/README +3 -0
  35. simo/core/drf_braces/__init__.py +7 -0
  36. simo/core/drf_braces/__pycache__/__init__.cpython-38.pyc +0 -0
  37. simo/core/drf_braces/__pycache__/utils.cpython-38.pyc +0 -0
  38. simo/core/drf_braces/fields/__init__.py +5 -0
  39. simo/core/drf_braces/fields/__pycache__/__init__.cpython-38.pyc +0 -0
  40. simo/core/drf_braces/fields/__pycache__/_fields.cpython-38.pyc +0 -0
  41. simo/core/drf_braces/fields/__pycache__/custom.cpython-38.pyc +0 -0
  42. simo/core/drf_braces/fields/__pycache__/mixins.cpython-38.pyc +0 -0
  43. simo/core/drf_braces/fields/__pycache__/modified.cpython-38.pyc +0 -0
  44. simo/core/drf_braces/fields/_fields.py +48 -0
  45. simo/core/drf_braces/fields/custom.py +107 -0
  46. simo/core/drf_braces/fields/mixins.py +58 -0
  47. simo/core/drf_braces/fields/modified.py +41 -0
  48. simo/core/drf_braces/forms/__init__.py +0 -0
  49. simo/core/drf_braces/forms/fields.py +20 -0
  50. simo/core/drf_braces/forms/serializer_form.py +156 -0
  51. simo/core/drf_braces/mixins.py +52 -0
  52. simo/core/drf_braces/models.py +0 -0
  53. simo/core/drf_braces/parsers.py +72 -0
  54. simo/core/drf_braces/renderers.py +37 -0
  55. simo/core/drf_braces/serializers/__init__.py +0 -0
  56. simo/core/drf_braces/serializers/__pycache__/__init__.cpython-38.pyc +0 -0
  57. simo/core/drf_braces/serializers/__pycache__/form_serializer.cpython-38.pyc +0 -0
  58. simo/core/drf_braces/serializers/enforce_validation_serializer.py +214 -0
  59. simo/core/drf_braces/serializers/form_serializer.py +391 -0
  60. simo/core/drf_braces/serializers/swapping.py +48 -0
  61. simo/core/drf_braces/tests/__init__.py +0 -0
  62. simo/core/drf_braces/tests/fields/__init__.py +0 -0
  63. simo/core/drf_braces/tests/fields/test_custom.py +94 -0
  64. simo/core/drf_braces/tests/fields/test_fields.py +13 -0
  65. simo/core/drf_braces/tests/fields/test_mixins.py +96 -0
  66. simo/core/drf_braces/tests/fields/test_modified.py +40 -0
  67. simo/core/drf_braces/tests/forms/__init__.py +0 -0
  68. simo/core/drf_braces/tests/forms/test_fields.py +46 -0
  69. simo/core/drf_braces/tests/forms/test_serializer_form.py +256 -0
  70. simo/core/drf_braces/tests/serializers/__init__.py +0 -0
  71. simo/core/drf_braces/tests/serializers/test_enforce_validation_serializer.py +169 -0
  72. simo/core/drf_braces/tests/serializers/test_form_serializer.py +387 -0
  73. simo/core/drf_braces/tests/serializers/test_swapping.py +40 -0
  74. simo/core/drf_braces/tests/test_mixins.py +111 -0
  75. simo/core/drf_braces/tests/test_parsers.py +73 -0
  76. simo/core/drf_braces/tests/test_renderers.py +23 -0
  77. simo/core/drf_braces/tests/test_utils.py +73 -0
  78. simo/core/drf_braces/utils.py +209 -0
  79. simo/core/events.py +3 -3
  80. simo/core/forms.py +79 -37
  81. simo/core/gateways.py +31 -14
  82. simo/core/management/commands/gateways_manager.py +0 -1
  83. simo/core/managers.py +81 -0
  84. simo/core/middleware.py +25 -0
  85. simo/core/migrations/0026_category_instance.py +20 -0
  86. simo/core/migrations/0027_remove_component_tags.py +17 -0
  87. simo/core/migrations/0028_rename_subcomponents_component_slaves.py +18 -0
  88. simo/core/migrations/0029_auto_20240229_1331.py +33 -0
  89. simo/core/migrations/__pycache__/0026_category_instance.cpython-38.pyc +0 -0
  90. simo/core/migrations/__pycache__/0027_remove_component_tags.cpython-38.pyc +0 -0
  91. simo/core/migrations/__pycache__/0028_rename_subcomponents_component_slaves.cpython-38.pyc +0 -0
  92. simo/core/migrations/__pycache__/0029_auto_20240229_1331.cpython-38.pyc +0 -0
  93. simo/core/models.py +103 -66
  94. simo/core/permissions.py +28 -2
  95. simo/core/serializers.py +330 -26
  96. simo/core/socket_consumers.py +5 -14
  97. simo/core/tasks.py +11 -1
  98. simo/core/templates/admin/base.html +37 -10
  99. simo/core/templates/admin/wizard/discovery.html +188 -0
  100. simo/core/templates/admin/wizard/wizard_add.html +5 -5
  101. simo/core/utils/__pycache__/serialization.cpython-38.pyc +0 -0
  102. simo/core/utils/admin.py +9 -2
  103. simo/core/utils/formsets.py +17 -16
  104. simo/core/utils/helpers.py +1 -0
  105. simo/core/utils/serialization.py +56 -0
  106. simo/core/utils/type_constants.py +1 -1
  107. simo/core/utils/validators.py +14 -1
  108. simo/core/views.py +13 -0
  109. simo/fleet/__pycache__/admin.cpython-38.pyc +0 -0
  110. simo/fleet/__pycache__/api.cpython-38.pyc +0 -0
  111. simo/fleet/__pycache__/auto_urls.cpython-38.pyc +0 -0
  112. simo/fleet/__pycache__/controllers.cpython-38.pyc +0 -0
  113. simo/fleet/__pycache__/forms.cpython-38.pyc +0 -0
  114. simo/fleet/__pycache__/gateways.cpython-38.pyc +0 -0
  115. simo/fleet/__pycache__/managers.cpython-38.pyc +0 -0
  116. simo/fleet/__pycache__/models.cpython-38.pyc +0 -0
  117. simo/fleet/__pycache__/serializers.cpython-38.pyc +0 -0
  118. simo/fleet/__pycache__/socket_consumers.cpython-38.pyc +0 -0
  119. simo/fleet/__pycache__/utils.cpython-38.pyc +0 -0
  120. simo/fleet/__pycache__/views.cpython-38.pyc +0 -0
  121. simo/fleet/admin.py +54 -25
  122. simo/fleet/api.py +59 -3
  123. simo/fleet/auto_urls.py +2 -3
  124. simo/fleet/controllers.py +199 -16
  125. simo/fleet/forms.py +325 -483
  126. simo/fleet/gateways.py +44 -2
  127. simo/fleet/managers.py +32 -0
  128. simo/fleet/migrations/0025_auto_20240130_1334.py +27 -0
  129. simo/fleet/migrations/0026_rename_i2cinterface_scl_pin_and_more.py +64 -0
  130. simo/fleet/migrations/0027_auto_20240306_0802.py +170 -0
  131. simo/fleet/migrations/0028_remove_i2cinterface_scl_pin_no_and_more.py +21 -0
  132. simo/fleet/migrations/0029_alter_i2cinterface_scl_pin_and_more.py +24 -0
  133. simo/fleet/migrations/0030_colonelpin_label_alter_colonel_type.py +24 -0
  134. simo/fleet/migrations/0031_alter_colonel_type.py +18 -0
  135. simo/fleet/migrations/__pycache__/0025_auto_20240130_1334.cpython-38.pyc +0 -0
  136. simo/fleet/migrations/__pycache__/0026_rename_i2cinterface_scl_pin_and_more.cpython-38.pyc +0 -0
  137. simo/fleet/migrations/__pycache__/0027_auto_20240306_0802.cpython-38.pyc +0 -0
  138. simo/fleet/migrations/__pycache__/0028_remove_i2cinterface_scl_pin_no_and_more.cpython-38.pyc +0 -0
  139. simo/fleet/migrations/__pycache__/0029_alter_i2cinterface_scl_pin_and_more.cpython-38.pyc +0 -0
  140. simo/fleet/migrations/__pycache__/0030_colonelpin_label_alter_colonel_type.cpython-38.pyc +0 -0
  141. simo/fleet/migrations/__pycache__/0031_alter_colonel_type.cpython-38.pyc +0 -0
  142. simo/fleet/models.py +134 -82
  143. simo/fleet/serializers.py +35 -1
  144. simo/fleet/socket_consumers.py +239 -76
  145. simo/fleet/utils.py +15 -53
  146. simo/fleet/views.py +28 -14
  147. simo/generic/controllers.py +13 -89
  148. simo/generic/forms.py +29 -18
  149. simo/generic/gateways.py +73 -2
  150. simo/generic/models.py +3 -3
  151. simo/multimedia/controllers.py +9 -8
  152. simo/settings.py +7 -4
  153. simo/urls.py +4 -8
  154. simo/users/__pycache__/admin.cpython-38.pyc +0 -0
  155. simo/users/__pycache__/api.cpython-38.pyc +0 -0
  156. simo/users/__pycache__/auto_urls.cpython-38.pyc +0 -0
  157. simo/users/__pycache__/models.cpython-38.pyc +0 -0
  158. simo/users/__pycache__/serializers.cpython-38.pyc +0 -0
  159. simo/users/__pycache__/sso_urls.cpython-38.pyc +0 -0
  160. simo/users/admin.py +8 -1
  161. simo/users/api.py +38 -2
  162. simo/users/auto_urls.py +2 -2
  163. simo/users/migrations/0025_rename_name_fingerprint_type_and_more.py +22 -0
  164. simo/users/migrations/__pycache__/0025_rename_name_fingerprint_type_and_more.cpython-38.pyc +0 -0
  165. simo/users/models.py +2 -3
  166. simo/users/serializers.py +15 -1
  167. simo/users/sso_urls.py +3 -3
  168. simo/wsgi.py +7 -0
  169. {simo-1.7.20.dist-info → simo-2.0.0.dist-info}/METADATA +8 -9
  170. {simo-1.7.20.dist-info → simo-2.0.0.dist-info}/RECORD +173 -189
  171. {simo-1.7.20.dist-info → simo-2.0.0.dist-info}/WHEEL +1 -1
  172. simo/core/db_backend/__pycache__/__init__.cpython-38.pyc +0 -0
  173. simo/core/db_backend/__pycache__/base.cpython-38.pyc +0 -0
  174. simo/core/management/commands/__pycache__/gateways_manager.cpython-38.pyc +0 -0
  175. simo/core/migrations/__pycache__/0001_initial.cpython-38.pyc +0 -0
  176. simo/core/migrations/__pycache__/0002_load_icons.cpython-38.pyc +0 -0
  177. simo/core/migrations/__pycache__/0003_create_default_zones_and_categories.cpython-38.pyc +0 -0
  178. simo/core/migrations/__pycache__/0004_create_generic.cpython-38.pyc +0 -0
  179. simo/core/migrations/__pycache__/0005_component_subcomponents.cpython-38.pyc +0 -0
  180. simo/core/migrations/__pycache__/0006_alter_component_subcomponents.cpython-38.pyc +0 -0
  181. simo/core/migrations/__pycache__/0007_component_change_init_to.cpython-38.pyc +0 -0
  182. simo/core/migrations/__pycache__/0008_alter_component_change_init_to.cpython-38.pyc +0 -0
  183. simo/core/migrations/__pycache__/0009_auto_20220707_1404.cpython-38.pyc +0 -0
  184. simo/core/migrations/__pycache__/0010_historyaggregate.cpython-38.pyc +0 -0
  185. simo/core/migrations/__pycache__/0011_component_last_change.cpython-38.pyc +0 -0
  186. simo/core/migrations/__pycache__/0012_instance.cpython-38.pyc +0 -0
  187. simo/core/migrations/__pycache__/0013_auto_20231003_0754.cpython-38.pyc +0 -0
  188. simo/core/migrations/__pycache__/0014_zone_instance.cpython-38.pyc +0 -0
  189. simo/core/migrations/__pycache__/0015_auto_20231004_1113.cpython-38.pyc +0 -0
  190. simo/core/migrations/__pycache__/0016_auto_20231004_1113.cpython-38.pyc +0 -0
  191. simo/core/migrations/__pycache__/0017_auto_20231004_1313.cpython-38.pyc +0 -0
  192. simo/core/migrations/__pycache__/0018_auto_20231005_0622.cpython-38.pyc +0 -0
  193. simo/core/migrations/__pycache__/0019_alter_gateway_type.cpython-38.pyc +0 -0
  194. simo/core/migrations/__pycache__/0020_component_meta.cpython-38.pyc +0 -0
  195. simo/core/migrations/__pycache__/0021_auto_20231020_1041.cpython-38.pyc +0 -0
  196. simo/core/migrations/__pycache__/__init__.cpython-38.pyc +0 -0
  197. simo/core/templatetags/__pycache__/__init__.cpython-38.pyc +0 -0
  198. simo/core/templatetags/__pycache__/components_list.cpython-38.pyc +0 -0
  199. simo/core/utils/__pycache__/__init__.cpython-38.pyc +0 -0
  200. simo/core/utils/__pycache__/admin.cpython-38.pyc +0 -0
  201. simo/core/utils/__pycache__/config_values.cpython-38.pyc +0 -0
  202. simo/core/utils/__pycache__/easing.cpython-38.pyc +0 -0
  203. simo/core/utils/__pycache__/form_fields.cpython-38.pyc +0 -0
  204. simo/core/utils/__pycache__/form_widgets.cpython-38.pyc +0 -0
  205. simo/core/utils/__pycache__/formsets.cpython-38.pyc +0 -0
  206. simo/core/utils/__pycache__/helpers.cpython-38.pyc +0 -0
  207. simo/core/utils/__pycache__/logs.cpython-38.pyc +0 -0
  208. simo/core/utils/__pycache__/mixins.cpython-38.pyc +0 -0
  209. simo/core/utils/__pycache__/model_helpers.cpython-38.pyc +0 -0
  210. simo/core/utils/__pycache__/relay.cpython-38.pyc +0 -0
  211. simo/core/utils/__pycache__/type_constants.cpython-38.pyc +0 -0
  212. simo/core/utils/__pycache__/validators.cpython-38.pyc +0 -0
  213. simo/fleet/tasks.py +0 -25
  214. simo/generic/__pycache__/__init__.cpython-37.pyc +0 -0
  215. simo/generic/__pycache__/__init__.cpython-38.pyc +0 -0
  216. simo/generic/__pycache__/app_widgets.cpython-38.pyc +0 -0
  217. simo/generic/__pycache__/base_types.cpython-38.pyc +0 -0
  218. simo/generic/__pycache__/controllers.cpython-37.pyc +0 -0
  219. simo/generic/__pycache__/controllers.cpython-38.pyc +0 -0
  220. simo/generic/__pycache__/forms.cpython-38.pyc +0 -0
  221. simo/generic/__pycache__/gateways.cpython-38.pyc +0 -0
  222. simo/generic/__pycache__/models.cpython-38.pyc +0 -0
  223. simo/generic/__pycache__/routing.cpython-38.pyc +0 -0
  224. simo/generic/__pycache__/socket_consumers.cpython-38.pyc +0 -0
  225. simo/generic/__pycache__/widgets.cpython-37.pyc +0 -0
  226. simo/multimedia/__pycache__/__init__.cpython-38.pyc +0 -0
  227. simo/multimedia/__pycache__/admin.cpython-38.pyc +0 -0
  228. simo/multimedia/__pycache__/api.cpython-38.pyc +0 -0
  229. simo/multimedia/__pycache__/app_widgets.cpython-38.pyc +0 -0
  230. simo/multimedia/__pycache__/base_types.cpython-38.pyc +0 -0
  231. simo/multimedia/__pycache__/controllers.cpython-38.pyc +0 -0
  232. simo/multimedia/__pycache__/forms.cpython-38.pyc +0 -0
  233. simo/multimedia/__pycache__/models.cpython-38.pyc +0 -0
  234. simo/multimedia/__pycache__/serializers.cpython-38.pyc +0 -0
  235. simo/multimedia/migrations/__pycache__/0001_initial.cpython-38.pyc +0 -0
  236. simo/multimedia/migrations/__pycache__/0002_sound_length.cpython-38.pyc +0 -0
  237. simo/multimedia/migrations/__pycache__/0003_alter_sound_length.cpython-38.pyc +0 -0
  238. simo/multimedia/migrations/__pycache__/0004_auto_20231023_1055.cpython-38.pyc +0 -0
  239. simo/multimedia/migrations/__pycache__/__init__.cpython-38.pyc +0 -0
  240. simo/notifications/__pycache__/__init__.cpython-38.pyc +0 -0
  241. simo/notifications/__pycache__/admin.cpython-38.pyc +0 -0
  242. simo/notifications/__pycache__/api.cpython-38.pyc +0 -0
  243. simo/notifications/__pycache__/models.cpython-38.pyc +0 -0
  244. simo/notifications/__pycache__/serializers.cpython-38.pyc +0 -0
  245. simo/notifications/__pycache__/utils.cpython-38.pyc +0 -0
  246. simo/notifications/migrations/__pycache__/0001_initial.cpython-38.pyc +0 -0
  247. simo/notifications/migrations/__pycache__/0002_notification_instance.cpython-38.pyc +0 -0
  248. simo/notifications/migrations/__pycache__/__init__.cpython-38.pyc +0 -0
  249. simo/users/migrations/__pycache__/0001_initial.cpython-38.pyc +0 -0
  250. simo/users/migrations/__pycache__/0002_componentpermission.cpython-38.pyc +0 -0
  251. simo/users/migrations/__pycache__/0003_create_roles_and_system_user.cpython-38.pyc +0 -0
  252. simo/users/migrations/__pycache__/0004_user_secret_key.cpython-38.pyc +0 -0
  253. simo/users/migrations/__pycache__/0005_permissionsrole_instance.cpython-38.pyc +0 -0
  254. simo/users/migrations/__pycache__/0006_auto_20231003_0850.cpython-38.pyc +0 -0
  255. simo/users/migrations/__pycache__/0007_auto_20231003_1228.cpython-38.pyc +0 -0
  256. simo/users/migrations/__pycache__/0008_auto_20231003_1229.cpython-38.pyc +0 -0
  257. simo/users/migrations/__pycache__/0009_remove_user_role.cpython-38.pyc +0 -0
  258. simo/users/migrations/__pycache__/0010_auto_20231004_1313.cpython-38.pyc +0 -0
  259. simo/users/migrations/__pycache__/0011_auto_20231004_1313.cpython-38.pyc +0 -0
  260. simo/users/migrations/__pycache__/0012_alter_userinstancerole_unique_together.cpython-38.pyc +0 -0
  261. simo/users/migrations/__pycache__/0013_remove_user_roles.cpython-38.pyc +0 -0
  262. simo/users/migrations/__pycache__/0014_user_roles.cpython-38.pyc +0 -0
  263. simo/users/migrations/__pycache__/0015_remove_user_at_home.cpython-38.pyc +0 -0
  264. simo/users/migrations/__pycache__/0016_auto_20231005_1050.cpython-38.pyc +0 -0
  265. simo/users/migrations/__pycache__/__init__.cpython-38.pyc +0 -0
  266. {simo-1.7.20.dist-info → simo-2.0.0.dist-info}/LICENSE.md +0 -0
  267. {simo-1.7.20.dist-info → simo-2.0.0.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 .utils import get_gpio_pins_choices
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='wESP32',
55
+ max_length=20, default='ample-wall',
53
56
  choices=(
54
- ('wESP32', 'wESP32'), ('4-relays', '4 Relays'),
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=0, choices=(
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
- is_authorized = models.BooleanField(default=False, help_text="Temporrary field")
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
- obj = super().save(*args, **kwargs)
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,20 +158,26 @@ 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
- self.occupied_pins = {}
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 pin in pins:
172
- self.occupied_pins[pin] = component.id
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):
179
182
  other_colonel.refresh_from_db()
180
183
  assert list(other_colonel.components.all()) == [], \
@@ -184,21 +187,97 @@ class Colonel(DirtyFieldsMixin, models.Model):
184
187
  component.config['colonel'] = other_colonel.id
185
188
  component.save()
186
189
  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)
190
+ other_colonel.components.add(component)
191
191
 
192
192
  self.rebuild_occupied_pins()
193
- self.save()
194
-
195
193
  other_colonel.rebuild_occupied_pins()
196
- other_colonel.save()
194
+
195
+ other_colonel.i2c_interfaces.all().delete()
196
+
197
+ for i2c_interface in self.i2c_interfaces.all():
198
+ I2CInterface.objects.create(
199
+ colonel=other_colonel, name=i2c_interface.name,
200
+ freq=i2c_interface.freq,
201
+ scl_pin=ColonelPin.objects.get(
202
+ colonel=other_colonel, no=i2c_interface.scl_pin.no,
203
+ ),
204
+ sda_pin=ColonelPin.objects.get(
205
+ colonel=other_colonel, no=i2c_interface.sda_pin.no,
206
+ ),
207
+ )
197
208
 
198
209
  self.update_config()
199
210
  other_colonel.update_config()
200
211
 
201
212
 
213
+ class ColonelPin(models.Model):
214
+ colonel = models.ForeignKey(
215
+ Colonel, related_name='pins', on_delete=models.CASCADE
216
+ )
217
+ no = models.PositiveIntegerField()
218
+ label = models.CharField(db_index=True, max_length=200)
219
+ input = models.BooleanField(default=False, db_index=True)
220
+ output = models.BooleanField(default=False, db_index=True)
221
+ capacitive = models.BooleanField(default=False, db_index=True)
222
+ adc = models.BooleanField(default=False)
223
+ native = models.BooleanField(default=True, db_index=True)
224
+ default_pull = models.CharField(
225
+ max_length=50, db_index=True, null=True, blank=True,
226
+ choices=(('LOW', "LOW"), ("HIGH", "HIGH"))
227
+ )
228
+ note = models.CharField(max_length=100)
229
+ occupied_by_content_type = models.ForeignKey(
230
+ ContentType, on_delete=models.CASCADE, null=True
231
+ )
232
+ occupied_by_id = models.PositiveIntegerField(null=True)
233
+ occupied_by = GenericForeignKey(
234
+ "occupied_by_content_type", "occupied_by_id"
235
+ )
236
+
237
+ objects = ColonelPinsManager()
238
+
239
+ class Meta:
240
+ unique_together = 'colonel', 'no'
241
+ indexes = [
242
+ models.Index(
243
+ fields=["occupied_by_content_type", "occupied_by_id"]
244
+ ),
245
+ ]
246
+ def __str__(self):
247
+ if not self.label:
248
+ # Might be created via migration...
249
+ self.save()
250
+ return self.label
251
+
252
+ def save(self, *args, **kwargs):
253
+ if self.native:
254
+ self.label = f'GPIO{self.no}'
255
+ else:
256
+ no = self.no - 100
257
+ self.label = f'IO{no}'
258
+ if self.note:
259
+ self.label += ' | %s' % self.note
260
+ return super().save(*args, **kwargs)
261
+
262
+
263
+ @receiver(post_save, sender=Colonel)
264
+ def create_interfaces(sender, instance, created, *args, **kwargs):
265
+ if not created:
266
+ return
267
+ for no, data in GPIO_PINS.get(instance.type).items():
268
+ ColonelPin.objects.get_or_create(
269
+ colonel=instance, no=no,
270
+ input=data.get('input'), output=data.get('output'),
271
+ capacitive=data.get('capacitive'), adc=data.get('adc'),
272
+ native=data.get('native'), note=data.get('note')
273
+ )
274
+ if instance.type in ('ample-wall', 'game-changer'):
275
+ I2CInterface.objects.create(
276
+ colonel=instance, name='Main', no=0,
277
+ scl_pin=ColonelPin.objects.get(colonel=instance, no=4),
278
+ sda_pin=ColonelPin.objects.get(colonel=instance, no=15),
279
+ )
280
+
202
281
 
203
282
  @receiver(pre_delete, sender=Component)
204
283
  def post_component_delete(sender, instance, *args, **kwargs):
@@ -211,8 +290,7 @@ def post_component_delete(sender, instance, *args, **kwargs):
211
290
  for colonel in affected_colonels:
212
291
  print("Rebuild occupied pins for :", colonel)
213
292
  colonel.rebuild_occupied_pins()
214
- colonel.save()
215
- colonel.restart()
293
+ colonel.update_config()
216
294
 
217
295
  transaction.on_commit(update_colonel)
218
296
 
@@ -231,12 +309,24 @@ class I2CInterface(models.Model):
231
309
  no = models.IntegerField(
232
310
  default=0, choices=i2c_interface_no_choices
233
311
  )
234
- scl_pin = models.IntegerField(default=4, choices=get_gpio_pins_choices())
235
- sda_pin = models.IntegerField(default=15, choices=get_gpio_pins_choices())
312
+ scl_pin = models.ForeignKey(
313
+ ColonelPin, on_delete=models.CASCADE, limit_choices_to={
314
+ 'native': True, 'output': True,
315
+ },
316
+ null=True, related_name='i2c_scl'
317
+ )
318
+ sda_pin = models.ForeignKey(
319
+ ColonelPin, on_delete=models.CASCADE, limit_choices_to={
320
+ 'native': True, 'output': True,
321
+ },
322
+ null=True, related_name='i2c_sda'
323
+ )
236
324
  freq = models.IntegerField(
237
325
  default=100000, help_text="100000 - is a good middle point!"
238
326
  )
239
327
 
328
+ objects = I2CInterfacesManager()
329
+
240
330
  class Meta:
241
331
  unique_together = 'colonel', 'no'
242
332
 
@@ -244,56 +334,18 @@ class I2CInterface(models.Model):
244
334
  return self.name
245
335
 
246
336
 
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
337
  @receiver(post_save, sender=I2CInterface)
260
338
  def post_i2c_interface_delete(sender, instance, *args, **kwargs):
261
-
262
- def update_colonel():
263
- instance.colonel.rebuild_occupied_pins()
264
- instance.colonel.save()
265
- transaction.on_commit(update_colonel)
266
-
267
-
268
- # class BLEDevice(models.Model):
269
- # mac = models.CharField(max_length=50, unique=True)
270
- # name = models.CharField(max_length=50)
271
- # addr = models.BinaryField(max_length=50)
272
- # type = models.PositiveIntegerField(default=0, choices=(
273
- # (0, "Unknown"),
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
339
+ with transaction.atomic():
340
+ ct = ContentType.objects.get_for_model(instance)
341
+ for pin in ColonelPin.objects.filter(
342
+ occupied_by_content_type=ct,
343
+ occupied_by_id=instance.id
344
+ ):
345
+ pin.occupied_by_content_type = None
346
+ pin.occupied_by_content_id = None
347
+ pin.save()
348
+ instance.scl_pin.occupied_by = instance
349
+ instance.scl_pin.save()
350
+ instance.sda_pin.occupied_by = instance
351
+ 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