simo 2.0.42__py3-none-any.whl → 2.1.2__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 (124) hide show
  1. simo/__pycache__/asgi.cpython-38.pyc +0 -0
  2. simo/__pycache__/settings.cpython-38.pyc +0 -0
  3. simo/__pycache__/wsgi.cpython-38.pyc +0 -0
  4. simo/asgi.py +1 -1
  5. simo/core/__init__.py +1 -0
  6. simo/core/__pycache__/__init__.cpython-38.pyc +0 -0
  7. simo/core/__pycache__/admin.cpython-38.pyc +0 -0
  8. simo/core/__pycache__/api.cpython-38.pyc +0 -0
  9. simo/core/__pycache__/api_meta.cpython-38.pyc +0 -0
  10. simo/core/__pycache__/app_widgets.cpython-38.pyc +0 -0
  11. simo/core/__pycache__/apps.cpython-38.pyc +0 -0
  12. simo/core/__pycache__/auto_urls.cpython-38.pyc +0 -0
  13. simo/core/__pycache__/base_types.cpython-38.pyc +0 -0
  14. simo/core/__pycache__/controllers.cpython-38.pyc +0 -0
  15. simo/core/__pycache__/dynamic_settings.cpython-38.pyc +0 -0
  16. simo/core/__pycache__/form_fields.cpython-38.pyc +0 -0
  17. simo/core/__pycache__/forms.cpython-38.pyc +0 -0
  18. simo/core/__pycache__/gateways.cpython-38.pyc +0 -0
  19. simo/core/__pycache__/managers.cpython-38.pyc +0 -0
  20. simo/core/__pycache__/models.cpython-38.pyc +0 -0
  21. simo/core/__pycache__/permissions.cpython-38.pyc +0 -0
  22. simo/core/__pycache__/serializers.cpython-38.pyc +0 -0
  23. simo/core/__pycache__/signal_receivers.cpython-38.pyc +0 -0
  24. simo/core/__pycache__/tasks.cpython-38.pyc +0 -0
  25. simo/core/__pycache__/views.cpython-38.pyc +0 -0
  26. simo/core/admin.py +26 -26
  27. simo/core/api.py +22 -2
  28. simo/core/api_meta.py +23 -13
  29. simo/core/app_widgets.py +6 -0
  30. simo/core/apps.py +13 -0
  31. simo/core/auto_urls.py +2 -3
  32. simo/core/base_types.py +1 -0
  33. simo/core/controllers.py +57 -0
  34. simo/core/dynamic_settings.py +0 -8
  35. simo/core/form_fields.py +93 -0
  36. simo/core/forms.py +16 -101
  37. simo/core/gateways.py +1 -1
  38. simo/core/managers.py +14 -1
  39. simo/core/migrations/0037_auto_20240606_1057.py +33 -0
  40. simo/core/migrations/0038_remove_instance_cover_image_and_more.py +30 -0
  41. simo/core/migrations/__pycache__/0037_auto_20240606_1057.cpython-38.pyc +0 -0
  42. simo/core/migrations/__pycache__/0038_remove_instance_cover_image_and_more.cpython-38.pyc +0 -0
  43. simo/core/models.py +30 -16
  44. simo/core/permissions.py +6 -3
  45. simo/core/serializers.py +77 -5
  46. simo/core/signal_receivers.py +25 -0
  47. simo/core/static/admin/css/simo.css +14 -0
  48. simo/core/tasks.py +82 -49
  49. simo/core/templates/admin/controller_widgets/button.html +8 -0
  50. simo/core/templates/admin/core/component_change_form.html +97 -0
  51. simo/core/templates/admin/formset_widget.html +88 -118
  52. simo/core/templates/admin/formset_widget_old.html +122 -0
  53. simo/core/templates/admin/user_tools.html +0 -3
  54. simo/core/templates/admin/wizard/wizard_add.html +16 -9
  55. simo/core/utils/__pycache__/admin.cpython-38.pyc +0 -0
  56. simo/core/utils/__pycache__/cache.cpython-38.pyc +0 -0
  57. simo/core/utils/__pycache__/formsets.cpython-38.pyc +0 -0
  58. simo/core/utils/admin.py +11 -0
  59. simo/core/utils/cache.py +15 -0
  60. simo/core/utils/formsets.py +11 -18
  61. simo/core/views.py +2 -85
  62. simo/fleet/__pycache__/auto_urls.cpython-38.pyc +0 -0
  63. simo/fleet/__pycache__/controllers.cpython-38.pyc +0 -0
  64. simo/fleet/__pycache__/forms.cpython-38.pyc +0 -0
  65. simo/fleet/__pycache__/gateways.cpython-38.pyc +0 -0
  66. simo/fleet/__pycache__/models.cpython-38.pyc +0 -0
  67. simo/fleet/__pycache__/socket_consumers.cpython-38.pyc +0 -0
  68. simo/fleet/__pycache__/utils.cpython-38.pyc +0 -0
  69. simo/fleet/__pycache__/views.cpython-38.pyc +0 -0
  70. simo/fleet/auto_urls.py +7 -1
  71. simo/fleet/controllers.py +194 -31
  72. simo/fleet/forms.py +223 -87
  73. simo/fleet/gateways.py +53 -2
  74. simo/fleet/migrations/0036_auto_20240605_0702.py +68 -0
  75. simo/fleet/migrations/0037_alter_colonelpin_options_alter_colonelpin_no_and_more.py +27 -0
  76. simo/fleet/migrations/__pycache__/0036_auto_20240605_0702.cpython-38.pyc +0 -0
  77. simo/fleet/migrations/__pycache__/0037_alter_colonelpin_options_alter_colonelpin_no_and_more.cpython-38.pyc +0 -0
  78. simo/fleet/models.py +35 -6
  79. simo/fleet/socket_consumers.py +1 -1
  80. simo/fleet/templates/fleet/controllers_info/button.md +16 -0
  81. simo/fleet/utils.py +31 -1
  82. simo/fleet/views.py +45 -0
  83. simo/generic/__pycache__/controllers.cpython-38.pyc +0 -0
  84. simo/generic/__pycache__/forms.cpython-38.pyc +0 -0
  85. simo/generic/__pycache__/gateways.cpython-38.pyc +0 -0
  86. simo/generic/controllers.py +61 -16
  87. simo/generic/forms.py +0 -3
  88. simo/generic/gateways.py +2 -0
  89. simo/generic/templates/admin/controller_widgets/blinds.html +2 -1
  90. simo/generic/templates/admin/controller_widgets/weather_forecast.html +1 -1
  91. simo/generic/templates/generic/controllers_info/dummy.md +3 -0
  92. simo/generic/templates/generic/controllers_info/stateselect.md +2 -0
  93. simo/management/__init__.py +0 -0
  94. simo/management/__pycache__/__init__.cpython-38.pyc +0 -0
  95. simo/management/__pycache__/on_http_start.cpython-38.pyc +0 -0
  96. simo/{_hub_template → management/_hub_template}/hub/nginx.conf +2 -2
  97. simo/{auto_update.py → management/auto_update.py} +3 -0
  98. simo/{cli.py → management/copy_template.py} +3 -16
  99. simo/management/install.py +258 -0
  100. simo/{on_http_start.py → management/on_http_start.py} +22 -2
  101. simo/settings.py +20 -4
  102. simo/users/__init__.py +1 -0
  103. simo/users/__pycache__/__init__.cpython-38.pyc +0 -0
  104. simo/users/__pycache__/admin.cpython-38.pyc +0 -0
  105. simo/users/__pycache__/apps.cpython-38.pyc +0 -0
  106. simo/users/__pycache__/models.cpython-38.pyc +0 -0
  107. simo/users/apps.py +9 -0
  108. simo/users/migrations/__pycache__/0029_alter_instanceuser_options_instanceuser_order.cpython-38.pyc +0 -0
  109. simo/users/migrations/__pycache__/0030_alter_instanceuser_options_remove_instanceuser_order.cpython-38.pyc +0 -0
  110. simo/users/models.py +16 -3
  111. {simo-2.0.42.dist-info → simo-2.1.2.dist-info}/METADATA +5 -3
  112. {simo-2.0.42.dist-info → simo-2.1.2.dist-info}/RECORD +122 -95
  113. simo-2.1.2.dist-info/entry_points.txt +2 -0
  114. simo/__pycache__/on_http_start.cpython-38.pyc +0 -0
  115. simo/wsgi.py +0 -7
  116. /simo/{_hub_template → management/_hub_template}/hub/asgi.py +0 -0
  117. /simo/{_hub_template → management/_hub_template}/hub/celeryc.py +0 -0
  118. /simo/{_hub_template → management/_hub_template}/hub/manage.py +0 -0
  119. /simo/{_hub_template → management/_hub_template}/hub/settings.py +0 -0
  120. /simo/{_hub_template → management/_hub_template}/hub/supervisor.conf +0 -0
  121. /simo/{_hub_template → management/_hub_template}/hub/urls.py +0 -0
  122. {simo-2.0.42.dist-info → simo-2.1.2.dist-info}/LICENSE.md +0 -0
  123. {simo-2.0.42.dist-info → simo-2.1.2.dist-info}/WHEEL +0 -0
  124. {simo-2.0.42.dist-info → simo-2.1.2.dist-info}/top_level.txt +0 -0
simo/core/forms.py CHANGED
@@ -1,27 +1,19 @@
1
- import os
2
1
  import traceback
3
- import requests
4
2
  from dal import forward
5
- from django import forms
6
3
  from django.contrib.admin.forms import AdminAuthenticationForm as OrgAdminAuthenticationForm
7
4
  from django.db import models
8
5
  from django import forms
9
6
  from django.forms import formset_factory
10
- from django.conf import settings
11
7
  from django.urls.base import get_script_prefix
12
8
  from django.utils.safestring import mark_safe
13
9
  from django.utils.translation import gettext_lazy as _
14
10
  from django.contrib.contenttypes.models import ContentType
15
- from timezone_utils.choices import ALL_TIMEZONES_CHOICES
11
+ from actstream import action
16
12
  from dal import autocomplete
17
13
  from .models import (
18
14
  Icon, Category, Gateway, Component
19
15
  )
20
- from .widgets import LocationWidget
21
- from simo.conf import dynamic_settings
22
- from .widgets import SVGFileWidget, PythonCode, LogOutputWidget
23
- from .widgets import ImageWidget
24
- from .utils.helpers import get_random_string
16
+ from .widgets import SVGFileWidget, LogOutputWidget
25
17
  from .utils.formsets import FormsetField
26
18
  from .utils.validators import validate_slaves
27
19
 
@@ -34,94 +26,6 @@ class HiddenField(forms.CharField):
34
26
  super().__init__(widget=forms.HiddenInput())
35
27
 
36
28
 
37
- class HubConfigForm(forms.Form):
38
- name = forms.CharField(
39
- label=_("Hub Name"), required=True,
40
- widget=forms.TextInput(attrs={'placeholder': "Home Sweet Home"})
41
- )
42
- uid = forms.CharField(
43
- label=_('Unique Identifier (UID)'), required=False,
44
- widget=forms.TextInput(attrs={'placeholder': "Df5Hd8v1"}),
45
- help_text="Leave blank if this is a new instance."
46
- )
47
- time_zone = forms.ChoiceField(
48
- label=_("Time zone"), required=True,
49
- choices=ALL_TIMEZONES_CHOICES
50
- )
51
- units_of_measure = forms.ChoiceField(
52
- label=_("Units of Measure"), required=True,
53
- choices=(('metric', 'Metric'), ('imperial', 'Imperial'))
54
- )
55
- cover_image = forms.FileField(
56
- label=_("Cover image"), required=True, widget=ImageWidget
57
- )
58
-
59
- def __init__(self, *args, **kwargs):
60
- self.user = kwargs.pop('user')
61
- super().__init__(*args, **kwargs)
62
-
63
- def clean(self):
64
- cleaned_data = super().clean()
65
- post_data = cleaned_data.copy()
66
- post_data.pop('cover_image')
67
- if not dynamic_settings['core__hub_secret']:
68
- dynamic_settings['core__hub_secret'] = get_random_string(20)
69
- post_data['secret'] = dynamic_settings['core__hub_secret']
70
- post_data['email'] = self.user.email
71
- try:
72
- resp = requests.post(
73
- 'https://simo.io/hubs/sync-initial-config/', json=post_data,
74
- )
75
- except Exception as e:
76
- raise forms.ValidationError(
77
- "Connection error. "
78
- "Make sure your hub can reach https://simo.io and try again."
79
- )
80
-
81
- if resp.status_code == 400:
82
- resp_json = resp.json()
83
- resp_json.pop('status', None)
84
- for field_name, msg in resp_json.items():
85
- self.add_error(field_name, msg)
86
- elif resp.status_code == 200:
87
- cleaned_data['uid'] = resp.json()['uid']
88
- else:
89
- raise forms.ValidationError(
90
- "Bad response from https://simo.io. Please try again. "
91
- )
92
- return cleaned_data
93
-
94
-
95
- class CoordinatesForm(forms.Form):
96
- location = forms.CharField(
97
- label=_("Where is your hub located?"),
98
- widget=LocationWidget(based_fields=[])
99
- )
100
- share_location = forms.BooleanField(
101
- label="Share exact location with SIMO.io for "
102
- "better accuracy of location related services.",
103
- required=False
104
- )
105
-
106
- def __init__(self, *args, **kwargs):
107
- super().__init__(*args, **kwargs)
108
- if not self.initial['location']:
109
- self.fields['location'].widget = LocationWidget(
110
- based_fields=[], zoom=2
111
- )
112
-
113
-
114
-
115
-
116
- class TermsAndConditionsForm(forms.Form):
117
- accept = forms.BooleanField(required=False)
118
-
119
- def clean_accept(self):
120
- if not self.cleaned_data['accept']:
121
- raise forms.ValidationError(_("You must accept SIMO.io Terms & Conditions if you want to continue."))
122
- return self.cleaned_data['accept']
123
-
124
-
125
29
  class AdminAuthenticationForm(OrgAdminAuthenticationForm):
126
30
 
127
31
  def confirm_login_allowed(self, user):
@@ -209,6 +113,19 @@ class ConfigFieldsMixin:
209
113
  self.instance.config[field_name] = \
210
114
  self.cleaned_data[field_name]
211
115
 
116
+ if commit:
117
+ from simo.users.middleware import get_current_user
118
+ actor = get_current_user()
119
+ if self.instance.pk:
120
+ verb = 'modified'
121
+ else:
122
+ verb = 'created'
123
+ action.send(
124
+ actor, target=self.instance, verb=verb,
125
+ instance_id=self.instance.zone.instance.id,
126
+ action_type='management_event'
127
+ )
128
+
212
129
  return super().save(commit)
213
130
 
214
131
 
@@ -364,7 +281,7 @@ class ComponentAdminForm(forms.ModelForm):
364
281
  'alarm_category', 'arm_status',
365
282
  'notes'
366
283
  )
367
- base_fields = ['id', 'gateway', 'base_type', 'name']
284
+ base_fields = ['id', 'gateway', 'base_type', 'info', 'name']
368
285
  if cls.has_icon:
369
286
  base_fields.append('icon')
370
287
 
@@ -432,8 +349,6 @@ class ComponentAdminForm(forms.ModelForm):
432
349
  return self.cleaned_data['instance_methods']
433
350
 
434
351
 
435
-
436
-
437
352
  class BaseComponentForm(ConfigFieldsMixin, ComponentAdminForm):
438
353
  pass
439
354
 
simo/core/gateways.py CHANGED
@@ -74,7 +74,7 @@ class BaseObjectCommandsGatewayHandler(BaseGatewayHandler):
74
74
  def _run_periodic_task(self, exit, task, period):
75
75
  while not exit.is_set():
76
76
  try:
77
- print(f"Run periodic task {task}!")
77
+ #print(f"Run periodic task {task}!")
78
78
  getattr(self, task)()
79
79
  except Exception as e:
80
80
  self.logger.error(e, exc_info=True)
simo/core/managers.py CHANGED
@@ -1,10 +1,21 @@
1
1
  import sys
2
2
  import traceback
3
+ from actstream.managers import ActionManager as OrgActionManager
3
4
  from .middleware import get_current_instance
4
5
  from django.utils import timezone
5
6
  from django.db import models
6
7
 
7
8
 
9
+ class ActionManager(OrgActionManager):
10
+
11
+ def get_queryset(self):
12
+ qs = super().get_queryset()
13
+ instance = get_current_instance()
14
+ if instance:
15
+ qs = qs.filter(data__instance_id=instance.id)
16
+ return qs
17
+
18
+
8
19
  class ZonesManager(models.Manager):
9
20
 
10
21
  def get_queryset(self):
@@ -32,7 +43,7 @@ class ComponentsManager(models.Manager):
32
43
  instance = get_current_instance()
33
44
  if instance:
34
45
  qs = qs.filter(zone__instance=instance)
35
- return qs
46
+ return qs.select_related('zone', 'zone__instance', 'gateway')
36
47
 
37
48
  def bulk_send(self, data):
38
49
  """
@@ -50,6 +61,8 @@ class ComponentsManager(models.Manager):
50
61
 
51
62
  gateway_components = {}
52
63
  for comp, value in data.items():
64
+ if not comp.controller:
65
+ continue
53
66
  try:
54
67
  value = comp.controller._validate_val(value, BEFORE_SEND)
55
68
  except:
@@ -0,0 +1,33 @@
1
+ # Generated by Django 4.2.10 on 2024-06-06 10:57
2
+
3
+ from django.db import migrations
4
+
5
+ def forwards_func(apps, schema_editor):
6
+
7
+ Component = apps.get_model("core", "Component")
8
+ Gateway = apps.get_model('core', "Gateway")
9
+
10
+ generic_gateway = Gateway.objects.filter(
11
+ type='simo.generic.gateways.GenericGatewayHandler'
12
+ ).first()
13
+ if not generic_gateway:
14
+ return
15
+
16
+ Component.objects.filter(
17
+ controller_uid='simo.generic.controllers.StateSelect'
18
+ ).update(gateway=generic_gateway)
19
+
20
+
21
+ def reverse_func(apps, schema_editor):
22
+ pass
23
+
24
+
25
+ class Migration(migrations.Migration):
26
+
27
+ dependencies = [
28
+ ('core', '0036_auto_20240521_0823'),
29
+ ]
30
+
31
+ operations = [
32
+ migrations.RunPython(forwards_func, reverse_func, elidable=True),
33
+ ]
@@ -0,0 +1,30 @@
1
+ # Generated by Django 4.2.10 on 2024-06-12 08:14
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('core', '0037_auto_20240606_1057'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.RemoveField(
14
+ model_name='instance',
15
+ name='cover_image',
16
+ ),
17
+ migrations.RemoveField(
18
+ model_name='instance',
19
+ name='cover_image_synced',
20
+ ),
21
+ migrations.RemoveField(
22
+ model_name='instance',
23
+ name='secret_key',
24
+ ),
25
+ migrations.AlterField(
26
+ model_name='instance',
27
+ name='timezone',
28
+ field=models.CharField(choices=[('Africa/Abidjan', 'Africa/Abidjan'), ('Africa/Accra', 'Africa/Accra'), ('Africa/Addis_Ababa', 'Africa/Addis_Ababa'), ('Africa/Algiers', 'Africa/Algiers'), ('Africa/Asmara', 'Africa/Asmara'), ('Africa/Asmera', 'Africa/Asmera'), ('Africa/Bamako', 'Africa/Bamako'), ('Africa/Bangui', 'Africa/Bangui'), ('Africa/Banjul', 'Africa/Banjul'), ('Africa/Bissau', 'Africa/Bissau'), ('Africa/Blantyre', 'Africa/Blantyre'), ('Africa/Brazzaville', 'Africa/Brazzaville'), ('Africa/Bujumbura', 'Africa/Bujumbura'), ('Africa/Cairo', 'Africa/Cairo'), ('Africa/Casablanca', 'Africa/Casablanca'), ('Africa/Ceuta', 'Africa/Ceuta'), ('Africa/Conakry', 'Africa/Conakry'), ('Africa/Dakar', 'Africa/Dakar'), ('Africa/Dar_es_Salaam', 'Africa/Dar_es_Salaam'), ('Africa/Djibouti', 'Africa/Djibouti'), ('Africa/Douala', 'Africa/Douala'), ('Africa/El_Aaiun', 'Africa/El_Aaiun'), ('Africa/Freetown', 'Africa/Freetown'), ('Africa/Gaborone', 'Africa/Gaborone'), ('Africa/Harare', 'Africa/Harare'), ('Africa/Johannesburg', 'Africa/Johannesburg'), ('Africa/Juba', 'Africa/Juba'), ('Africa/Kampala', 'Africa/Kampala'), ('Africa/Khartoum', 'Africa/Khartoum'), ('Africa/Kigali', 'Africa/Kigali'), ('Africa/Kinshasa', 'Africa/Kinshasa'), ('Africa/Lagos', 'Africa/Lagos'), ('Africa/Libreville', 'Africa/Libreville'), ('Africa/Lome', 'Africa/Lome'), ('Africa/Luanda', 'Africa/Luanda'), ('Africa/Lubumbashi', 'Africa/Lubumbashi'), ('Africa/Lusaka', 'Africa/Lusaka'), ('Africa/Malabo', 'Africa/Malabo'), ('Africa/Maputo', 'Africa/Maputo'), ('Africa/Maseru', 'Africa/Maseru'), ('Africa/Mbabane', 'Africa/Mbabane'), ('Africa/Mogadishu', 'Africa/Mogadishu'), ('Africa/Monrovia', 'Africa/Monrovia'), ('Africa/Nairobi', 'Africa/Nairobi'), ('Africa/Ndjamena', 'Africa/Ndjamena'), ('Africa/Niamey', 'Africa/Niamey'), ('Africa/Nouakchott', 'Africa/Nouakchott'), ('Africa/Ouagadougou', 'Africa/Ouagadougou'), ('Africa/Porto-Novo', 'Africa/Porto-Novo'), ('Africa/Sao_Tome', 'Africa/Sao_Tome'), ('Africa/Timbuktu', 'Africa/Timbuktu'), ('Africa/Tripoli', 'Africa/Tripoli'), ('Africa/Tunis', 'Africa/Tunis'), ('Africa/Windhoek', 'Africa/Windhoek'), ('America/Adak', 'America/Adak'), ('America/Anchorage', 'America/Anchorage'), ('America/Anguilla', 'America/Anguilla'), ('America/Antigua', 'America/Antigua'), ('America/Araguaina', 'America/Araguaina'), ('America/Argentina/Buenos_Aires', 'America/Argentina/Buenos_Aires'), ('America/Argentina/Catamarca', 'America/Argentina/Catamarca'), ('America/Argentina/ComodRivadavia', 'America/Argentina/ComodRivadavia'), ('America/Argentina/Cordoba', 'America/Argentina/Cordoba'), ('America/Argentina/Jujuy', 'America/Argentina/Jujuy'), ('America/Argentina/La_Rioja', 'America/Argentina/La_Rioja'), ('America/Argentina/Mendoza', 'America/Argentina/Mendoza'), ('America/Argentina/Rio_Gallegos', 'America/Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'America/Argentina/Salta'), ('America/Argentina/San_Juan', 'America/Argentina/San_Juan'), ('America/Argentina/San_Luis', 'America/Argentina/San_Luis'), ('America/Argentina/Tucuman', 'America/Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'America/Argentina/Ushuaia'), ('America/Aruba', 'America/Aruba'), ('America/Asuncion', 'America/Asuncion'), ('America/Atikokan', 'America/Atikokan'), ('America/Atka', 'America/Atka'), ('America/Bahia', 'America/Bahia'), ('America/Bahia_Banderas', 'America/Bahia_Banderas'), ('America/Barbados', 'America/Barbados'), ('America/Belem', 'America/Belem'), ('America/Belize', 'America/Belize'), ('America/Blanc-Sablon', 'America/Blanc-Sablon'), ('America/Boa_Vista', 'America/Boa_Vista'), ('America/Bogota', 'America/Bogota'), ('America/Boise', 'America/Boise'), ('America/Buenos_Aires', 'America/Buenos_Aires'), ('America/Cambridge_Bay', 'America/Cambridge_Bay'), ('America/Campo_Grande', 'America/Campo_Grande'), ('America/Cancun', 'America/Cancun'), ('America/Caracas', 'America/Caracas'), ('America/Catamarca', 'America/Catamarca'), ('America/Cayenne', 'America/Cayenne'), ('America/Cayman', 'America/Cayman'), ('America/Chicago', 'America/Chicago'), ('America/Chihuahua', 'America/Chihuahua'), ('America/Ciudad_Juarez', 'America/Ciudad_Juarez'), ('America/Coral_Harbour', 'America/Coral_Harbour'), ('America/Cordoba', 'America/Cordoba'), ('America/Costa_Rica', 'America/Costa_Rica'), ('America/Creston', 'America/Creston'), ('America/Cuiaba', 'America/Cuiaba'), ('America/Curacao', 'America/Curacao'), ('America/Danmarkshavn', 'America/Danmarkshavn'), ('America/Dawson', 'America/Dawson'), ('America/Dawson_Creek', 'America/Dawson_Creek'), ('America/Denver', 'America/Denver'), ('America/Detroit', 'America/Detroit'), ('America/Dominica', 'America/Dominica'), ('America/Edmonton', 'America/Edmonton'), ('America/Eirunepe', 'America/Eirunepe'), ('America/El_Salvador', 'America/El_Salvador'), ('America/Ensenada', 'America/Ensenada'), ('America/Fort_Nelson', 'America/Fort_Nelson'), ('America/Fort_Wayne', 'America/Fort_Wayne'), ('America/Fortaleza', 'America/Fortaleza'), ('America/Glace_Bay', 'America/Glace_Bay'), ('America/Godthab', 'America/Godthab'), ('America/Goose_Bay', 'America/Goose_Bay'), ('America/Grand_Turk', 'America/Grand_Turk'), ('America/Grenada', 'America/Grenada'), ('America/Guadeloupe', 'America/Guadeloupe'), ('America/Guatemala', 'America/Guatemala'), ('America/Guayaquil', 'America/Guayaquil'), ('America/Guyana', 'America/Guyana'), ('America/Halifax', 'America/Halifax'), ('America/Havana', 'America/Havana'), ('America/Hermosillo', 'America/Hermosillo'), ('America/Indiana/Indianapolis', 'America/Indiana/Indianapolis'), ('America/Indiana/Knox', 'America/Indiana/Knox'), ('America/Indiana/Marengo', 'America/Indiana/Marengo'), ('America/Indiana/Petersburg', 'America/Indiana/Petersburg'), ('America/Indiana/Tell_City', 'America/Indiana/Tell_City'), ('America/Indiana/Vevay', 'America/Indiana/Vevay'), ('America/Indiana/Vincennes', 'America/Indiana/Vincennes'), ('America/Indiana/Winamac', 'America/Indiana/Winamac'), ('America/Indianapolis', 'America/Indianapolis'), ('America/Inuvik', 'America/Inuvik'), ('America/Iqaluit', 'America/Iqaluit'), ('America/Jamaica', 'America/Jamaica'), ('America/Jujuy', 'America/Jujuy'), ('America/Juneau', 'America/Juneau'), ('America/Kentucky/Louisville', 'America/Kentucky/Louisville'), ('America/Kentucky/Monticello', 'America/Kentucky/Monticello'), ('America/Knox_IN', 'America/Knox_IN'), ('America/Kralendijk', 'America/Kralendijk'), ('America/La_Paz', 'America/La_Paz'), ('America/Lima', 'America/Lima'), ('America/Los_Angeles', 'America/Los_Angeles'), ('America/Louisville', 'America/Louisville'), ('America/Lower_Princes', 'America/Lower_Princes'), ('America/Maceio', 'America/Maceio'), ('America/Managua', 'America/Managua'), ('America/Manaus', 'America/Manaus'), ('America/Marigot', 'America/Marigot'), ('America/Martinique', 'America/Martinique'), ('America/Matamoros', 'America/Matamoros'), ('America/Mazatlan', 'America/Mazatlan'), ('America/Mendoza', 'America/Mendoza'), ('America/Menominee', 'America/Menominee'), ('America/Merida', 'America/Merida'), ('America/Metlakatla', 'America/Metlakatla'), ('America/Mexico_City', 'America/Mexico_City'), ('America/Miquelon', 'America/Miquelon'), ('America/Moncton', 'America/Moncton'), ('America/Monterrey', 'America/Monterrey'), ('America/Montevideo', 'America/Montevideo'), ('America/Montreal', 'America/Montreal'), ('America/Montserrat', 'America/Montserrat'), ('America/Nassau', 'America/Nassau'), ('America/New_York', 'America/New_York'), ('America/Nipigon', 'America/Nipigon'), ('America/Nome', 'America/Nome'), ('America/Noronha', 'America/Noronha'), ('America/North_Dakota/Beulah', 'America/North_Dakota/Beulah'), ('America/North_Dakota/Center', 'America/North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'America/North_Dakota/New_Salem'), ('America/Nuuk', 'America/Nuuk'), ('America/Ojinaga', 'America/Ojinaga'), ('America/Panama', 'America/Panama'), ('America/Pangnirtung', 'America/Pangnirtung'), ('America/Paramaribo', 'America/Paramaribo'), ('America/Phoenix', 'America/Phoenix'), ('America/Port-au-Prince', 'America/Port-au-Prince'), ('America/Port_of_Spain', 'America/Port_of_Spain'), ('America/Porto_Acre', 'America/Porto_Acre'), ('America/Porto_Velho', 'America/Porto_Velho'), ('America/Puerto_Rico', 'America/Puerto_Rico'), ('America/Punta_Arenas', 'America/Punta_Arenas'), ('America/Rainy_River', 'America/Rainy_River'), ('America/Rankin_Inlet', 'America/Rankin_Inlet'), ('America/Recife', 'America/Recife'), ('America/Regina', 'America/Regina'), ('America/Resolute', 'America/Resolute'), ('America/Rio_Branco', 'America/Rio_Branco'), ('America/Rosario', 'America/Rosario'), ('America/Santa_Isabel', 'America/Santa_Isabel'), ('America/Santarem', 'America/Santarem'), ('America/Santiago', 'America/Santiago'), ('America/Santo_Domingo', 'America/Santo_Domingo'), ('America/Sao_Paulo', 'America/Sao_Paulo'), ('America/Scoresbysund', 'America/Scoresbysund'), ('America/Shiprock', 'America/Shiprock'), ('America/Sitka', 'America/Sitka'), ('America/St_Barthelemy', 'America/St_Barthelemy'), ('America/St_Johns', 'America/St_Johns'), ('America/St_Kitts', 'America/St_Kitts'), ('America/St_Lucia', 'America/St_Lucia'), ('America/St_Thomas', 'America/St_Thomas'), ('America/St_Vincent', 'America/St_Vincent'), ('America/Swift_Current', 'America/Swift_Current'), ('America/Tegucigalpa', 'America/Tegucigalpa'), ('America/Thule', 'America/Thule'), ('America/Thunder_Bay', 'America/Thunder_Bay'), ('America/Tijuana', 'America/Tijuana'), ('America/Toronto', 'America/Toronto'), ('America/Tortola', 'America/Tortola'), ('America/Vancouver', 'America/Vancouver'), ('America/Virgin', 'America/Virgin'), ('America/Whitehorse', 'America/Whitehorse'), ('America/Winnipeg', 'America/Winnipeg'), ('America/Yakutat', 'America/Yakutat'), ('America/Yellowknife', 'America/Yellowknife'), ('Antarctica/Casey', 'Antarctica/Casey'), ('Antarctica/Davis', 'Antarctica/Davis'), ('Antarctica/DumontDUrville', 'Antarctica/DumontDUrville'), ('Antarctica/Macquarie', 'Antarctica/Macquarie'), ('Antarctica/Mawson', 'Antarctica/Mawson'), ('Antarctica/McMurdo', 'Antarctica/McMurdo'), ('Antarctica/Palmer', 'Antarctica/Palmer'), ('Antarctica/Rothera', 'Antarctica/Rothera'), ('Antarctica/South_Pole', 'Antarctica/South_Pole'), ('Antarctica/Syowa', 'Antarctica/Syowa'), ('Antarctica/Troll', 'Antarctica/Troll'), ('Antarctica/Vostok', 'Antarctica/Vostok'), ('Arctic/Longyearbyen', 'Arctic/Longyearbyen'), ('Asia/Aden', 'Asia/Aden'), ('Asia/Almaty', 'Asia/Almaty'), ('Asia/Amman', 'Asia/Amman'), ('Asia/Anadyr', 'Asia/Anadyr'), ('Asia/Aqtau', 'Asia/Aqtau'), ('Asia/Aqtobe', 'Asia/Aqtobe'), ('Asia/Ashgabat', 'Asia/Ashgabat'), ('Asia/Ashkhabad', 'Asia/Ashkhabad'), ('Asia/Atyrau', 'Asia/Atyrau'), ('Asia/Baghdad', 'Asia/Baghdad'), ('Asia/Bahrain', 'Asia/Bahrain'), ('Asia/Baku', 'Asia/Baku'), ('Asia/Bangkok', 'Asia/Bangkok'), ('Asia/Barnaul', 'Asia/Barnaul'), ('Asia/Beirut', 'Asia/Beirut'), ('Asia/Bishkek', 'Asia/Bishkek'), ('Asia/Brunei', 'Asia/Brunei'), ('Asia/Calcutta', 'Asia/Calcutta'), ('Asia/Chita', 'Asia/Chita'), ('Asia/Choibalsan', 'Asia/Choibalsan'), ('Asia/Chongqing', 'Asia/Chongqing'), ('Asia/Chungking', 'Asia/Chungking'), ('Asia/Colombo', 'Asia/Colombo'), ('Asia/Dacca', 'Asia/Dacca'), ('Asia/Damascus', 'Asia/Damascus'), ('Asia/Dhaka', 'Asia/Dhaka'), ('Asia/Dili', 'Asia/Dili'), ('Asia/Dubai', 'Asia/Dubai'), ('Asia/Dushanbe', 'Asia/Dushanbe'), ('Asia/Famagusta', 'Asia/Famagusta'), ('Asia/Gaza', 'Asia/Gaza'), ('Asia/Harbin', 'Asia/Harbin'), ('Asia/Hebron', 'Asia/Hebron'), ('Asia/Ho_Chi_Minh', 'Asia/Ho_Chi_Minh'), ('Asia/Hong_Kong', 'Asia/Hong_Kong'), ('Asia/Hovd', 'Asia/Hovd'), ('Asia/Irkutsk', 'Asia/Irkutsk'), ('Asia/Istanbul', 'Asia/Istanbul'), ('Asia/Jakarta', 'Asia/Jakarta'), ('Asia/Jayapura', 'Asia/Jayapura'), ('Asia/Jerusalem', 'Asia/Jerusalem'), ('Asia/Kabul', 'Asia/Kabul'), ('Asia/Kamchatka', 'Asia/Kamchatka'), ('Asia/Karachi', 'Asia/Karachi'), ('Asia/Kashgar', 'Asia/Kashgar'), ('Asia/Kathmandu', 'Asia/Kathmandu'), ('Asia/Katmandu', 'Asia/Katmandu'), ('Asia/Khandyga', 'Asia/Khandyga'), ('Asia/Kolkata', 'Asia/Kolkata'), ('Asia/Krasnoyarsk', 'Asia/Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Asia/Kuala_Lumpur'), ('Asia/Kuching', 'Asia/Kuching'), ('Asia/Kuwait', 'Asia/Kuwait'), ('Asia/Macao', 'Asia/Macao'), ('Asia/Macau', 'Asia/Macau'), ('Asia/Magadan', 'Asia/Magadan'), ('Asia/Makassar', 'Asia/Makassar'), ('Asia/Manila', 'Asia/Manila'), ('Asia/Muscat', 'Asia/Muscat'), ('Asia/Nicosia', 'Asia/Nicosia'), ('Asia/Novokuznetsk', 'Asia/Novokuznetsk'), ('Asia/Novosibirsk', 'Asia/Novosibirsk'), ('Asia/Omsk', 'Asia/Omsk'), ('Asia/Oral', 'Asia/Oral'), ('Asia/Phnom_Penh', 'Asia/Phnom_Penh'), ('Asia/Pontianak', 'Asia/Pontianak'), ('Asia/Pyongyang', 'Asia/Pyongyang'), ('Asia/Qatar', 'Asia/Qatar'), ('Asia/Qostanay', 'Asia/Qostanay'), ('Asia/Qyzylorda', 'Asia/Qyzylorda'), ('Asia/Rangoon', 'Asia/Rangoon'), ('Asia/Riyadh', 'Asia/Riyadh'), ('Asia/Saigon', 'Asia/Saigon'), ('Asia/Sakhalin', 'Asia/Sakhalin'), ('Asia/Samarkand', 'Asia/Samarkand'), ('Asia/Seoul', 'Asia/Seoul'), ('Asia/Shanghai', 'Asia/Shanghai'), ('Asia/Singapore', 'Asia/Singapore'), ('Asia/Srednekolymsk', 'Asia/Srednekolymsk'), ('Asia/Taipei', 'Asia/Taipei'), ('Asia/Tashkent', 'Asia/Tashkent'), ('Asia/Tbilisi', 'Asia/Tbilisi'), ('Asia/Tehran', 'Asia/Tehran'), ('Asia/Tel_Aviv', 'Asia/Tel_Aviv'), ('Asia/Thimbu', 'Asia/Thimbu'), ('Asia/Thimphu', 'Asia/Thimphu'), ('Asia/Tokyo', 'Asia/Tokyo'), ('Asia/Tomsk', 'Asia/Tomsk'), ('Asia/Ujung_Pandang', 'Asia/Ujung_Pandang'), ('Asia/Ulaanbaatar', 'Asia/Ulaanbaatar'), ('Asia/Ulan_Bator', 'Asia/Ulan_Bator'), ('Asia/Urumqi', 'Asia/Urumqi'), ('Asia/Ust-Nera', 'Asia/Ust-Nera'), ('Asia/Vientiane', 'Asia/Vientiane'), ('Asia/Vladivostok', 'Asia/Vladivostok'), ('Asia/Yakutsk', 'Asia/Yakutsk'), ('Asia/Yangon', 'Asia/Yangon'), ('Asia/Yekaterinburg', 'Asia/Yekaterinburg'), ('Asia/Yerevan', 'Asia/Yerevan'), ('Atlantic/Azores', 'Atlantic/Azores'), ('Atlantic/Bermuda', 'Atlantic/Bermuda'), ('Atlantic/Canary', 'Atlantic/Canary'), ('Atlantic/Cape_Verde', 'Atlantic/Cape_Verde'), ('Atlantic/Faeroe', 'Atlantic/Faeroe'), ('Atlantic/Faroe', 'Atlantic/Faroe'), ('Atlantic/Jan_Mayen', 'Atlantic/Jan_Mayen'), ('Atlantic/Madeira', 'Atlantic/Madeira'), ('Atlantic/Reykjavik', 'Atlantic/Reykjavik'), ('Atlantic/South_Georgia', 'Atlantic/South_Georgia'), ('Atlantic/St_Helena', 'Atlantic/St_Helena'), ('Atlantic/Stanley', 'Atlantic/Stanley'), ('Australia/ACT', 'Australia/ACT'), ('Australia/Adelaide', 'Australia/Adelaide'), ('Australia/Brisbane', 'Australia/Brisbane'), ('Australia/Broken_Hill', 'Australia/Broken_Hill'), ('Australia/Canberra', 'Australia/Canberra'), ('Australia/Currie', 'Australia/Currie'), ('Australia/Darwin', 'Australia/Darwin'), ('Australia/Eucla', 'Australia/Eucla'), ('Australia/Hobart', 'Australia/Hobart'), ('Australia/LHI', 'Australia/LHI'), ('Australia/Lindeman', 'Australia/Lindeman'), ('Australia/Lord_Howe', 'Australia/Lord_Howe'), ('Australia/Melbourne', 'Australia/Melbourne'), ('Australia/NSW', 'Australia/NSW'), ('Australia/North', 'Australia/North'), ('Australia/Perth', 'Australia/Perth'), ('Australia/Queensland', 'Australia/Queensland'), ('Australia/South', 'Australia/South'), ('Australia/Sydney', 'Australia/Sydney'), ('Australia/Tasmania', 'Australia/Tasmania'), ('Australia/Victoria', 'Australia/Victoria'), ('Australia/West', 'Australia/West'), ('Australia/Yancowinna', 'Australia/Yancowinna'), ('Brazil/Acre', 'Brazil/Acre'), ('Brazil/DeNoronha', 'Brazil/DeNoronha'), ('Brazil/East', 'Brazil/East'), ('Brazil/West', 'Brazil/West'), ('CET', 'CET'), ('CST6CDT', 'CST6CDT'), ('Canada/Atlantic', 'Canada/Atlantic'), ('Canada/Central', 'Canada/Central'), ('Canada/Eastern', 'Canada/Eastern'), ('Canada/Mountain', 'Canada/Mountain'), ('Canada/Newfoundland', 'Canada/Newfoundland'), ('Canada/Pacific', 'Canada/Pacific'), ('Canada/Saskatchewan', 'Canada/Saskatchewan'), ('Canada/Yukon', 'Canada/Yukon'), ('Chile/Continental', 'Chile/Continental'), ('Chile/EasterIsland', 'Chile/EasterIsland'), ('Cuba', 'Cuba'), ('EET', 'EET'), ('EST', 'EST'), ('EST5EDT', 'EST5EDT'), ('Egypt', 'Egypt'), ('Eire', 'Eire'), ('Etc/GMT', 'Etc/GMT'), ('Etc/GMT+0', 'Etc/GMT+0'), ('Etc/GMT+1', 'Etc/GMT+1'), ('Etc/GMT+10', 'Etc/GMT+10'), ('Etc/GMT+11', 'Etc/GMT+11'), ('Etc/GMT+12', 'Etc/GMT+12'), ('Etc/GMT+2', 'Etc/GMT+2'), ('Etc/GMT+3', 'Etc/GMT+3'), ('Etc/GMT+4', 'Etc/GMT+4'), ('Etc/GMT+5', 'Etc/GMT+5'), ('Etc/GMT+6', 'Etc/GMT+6'), ('Etc/GMT+7', 'Etc/GMT+7'), ('Etc/GMT+8', 'Etc/GMT+8'), ('Etc/GMT+9', 'Etc/GMT+9'), ('Etc/GMT-0', 'Etc/GMT-0'), ('Etc/GMT-1', 'Etc/GMT-1'), ('Etc/GMT-10', 'Etc/GMT-10'), ('Etc/GMT-11', 'Etc/GMT-11'), ('Etc/GMT-12', 'Etc/GMT-12'), ('Etc/GMT-13', 'Etc/GMT-13'), ('Etc/GMT-14', 'Etc/GMT-14'), ('Etc/GMT-2', 'Etc/GMT-2'), ('Etc/GMT-3', 'Etc/GMT-3'), ('Etc/GMT-4', 'Etc/GMT-4'), ('Etc/GMT-5', 'Etc/GMT-5'), ('Etc/GMT-6', 'Etc/GMT-6'), ('Etc/GMT-7', 'Etc/GMT-7'), ('Etc/GMT-8', 'Etc/GMT-8'), ('Etc/GMT-9', 'Etc/GMT-9'), ('Etc/GMT0', 'Etc/GMT0'), ('Etc/Greenwich', 'Etc/Greenwich'), ('Etc/UCT', 'Etc/UCT'), ('Etc/UTC', 'Etc/UTC'), ('Etc/Universal', 'Etc/Universal'), ('Etc/Zulu', 'Etc/Zulu'), ('Europe/Amsterdam', 'Europe/Amsterdam'), ('Europe/Andorra', 'Europe/Andorra'), ('Europe/Astrakhan', 'Europe/Astrakhan'), ('Europe/Athens', 'Europe/Athens'), ('Europe/Belfast', 'Europe/Belfast'), ('Europe/Belgrade', 'Europe/Belgrade'), ('Europe/Berlin', 'Europe/Berlin'), ('Europe/Bratislava', 'Europe/Bratislava'), ('Europe/Brussels', 'Europe/Brussels'), ('Europe/Bucharest', 'Europe/Bucharest'), ('Europe/Budapest', 'Europe/Budapest'), ('Europe/Busingen', 'Europe/Busingen'), ('Europe/Chisinau', 'Europe/Chisinau'), ('Europe/Copenhagen', 'Europe/Copenhagen'), ('Europe/Dublin', 'Europe/Dublin'), ('Europe/Gibraltar', 'Europe/Gibraltar'), ('Europe/Guernsey', 'Europe/Guernsey'), ('Europe/Helsinki', 'Europe/Helsinki'), ('Europe/Isle_of_Man', 'Europe/Isle_of_Man'), ('Europe/Istanbul', 'Europe/Istanbul'), ('Europe/Jersey', 'Europe/Jersey'), ('Europe/Kaliningrad', 'Europe/Kaliningrad'), ('Europe/Kiev', 'Europe/Kiev'), ('Europe/Kirov', 'Europe/Kirov'), ('Europe/Kyiv', 'Europe/Kyiv'), ('Europe/Lisbon', 'Europe/Lisbon'), ('Europe/Ljubljana', 'Europe/Ljubljana'), ('Europe/London', 'Europe/London'), ('Europe/Luxembourg', 'Europe/Luxembourg'), ('Europe/Madrid', 'Europe/Madrid'), ('Europe/Malta', 'Europe/Malta'), ('Europe/Mariehamn', 'Europe/Mariehamn'), ('Europe/Minsk', 'Europe/Minsk'), ('Europe/Monaco', 'Europe/Monaco'), ('Europe/Moscow', 'Europe/Moscow'), ('Europe/Nicosia', 'Europe/Nicosia'), ('Europe/Oslo', 'Europe/Oslo'), ('Europe/Paris', 'Europe/Paris'), ('Europe/Podgorica', 'Europe/Podgorica'), ('Europe/Prague', 'Europe/Prague'), ('Europe/Riga', 'Europe/Riga'), ('Europe/Rome', 'Europe/Rome'), ('Europe/Samara', 'Europe/Samara'), ('Europe/San_Marino', 'Europe/San_Marino'), ('Europe/Sarajevo', 'Europe/Sarajevo'), ('Europe/Saratov', 'Europe/Saratov'), ('Europe/Simferopol', 'Europe/Simferopol'), ('Europe/Skopje', 'Europe/Skopje'), ('Europe/Sofia', 'Europe/Sofia'), ('Europe/Stockholm', 'Europe/Stockholm'), ('Europe/Tallinn', 'Europe/Tallinn'), ('Europe/Tirane', 'Europe/Tirane'), ('Europe/Tiraspol', 'Europe/Tiraspol'), ('Europe/Ulyanovsk', 'Europe/Ulyanovsk'), ('Europe/Uzhgorod', 'Europe/Uzhgorod'), ('Europe/Vaduz', 'Europe/Vaduz'), ('Europe/Vatican', 'Europe/Vatican'), ('Europe/Vienna', 'Europe/Vienna'), ('Europe/Vilnius', 'Europe/Vilnius'), ('Europe/Volgograd', 'Europe/Volgograd'), ('Europe/Warsaw', 'Europe/Warsaw'), ('Europe/Zagreb', 'Europe/Zagreb'), ('Europe/Zaporozhye', 'Europe/Zaporozhye'), ('Europe/Zurich', 'Europe/Zurich'), ('GB', 'GB'), ('GB-Eire', 'GB-Eire'), ('GMT', 'GMT'), ('GMT+0', 'GMT+0'), ('GMT-0', 'GMT-0'), ('GMT0', 'GMT0'), ('Greenwich', 'Greenwich'), ('HST', 'HST'), ('Hongkong', 'Hongkong'), ('Iceland', 'Iceland'), ('Indian/Antananarivo', 'Indian/Antananarivo'), ('Indian/Chagos', 'Indian/Chagos'), ('Indian/Christmas', 'Indian/Christmas'), ('Indian/Cocos', 'Indian/Cocos'), ('Indian/Comoro', 'Indian/Comoro'), ('Indian/Kerguelen', 'Indian/Kerguelen'), ('Indian/Mahe', 'Indian/Mahe'), ('Indian/Maldives', 'Indian/Maldives'), ('Indian/Mauritius', 'Indian/Mauritius'), ('Indian/Mayotte', 'Indian/Mayotte'), ('Indian/Reunion', 'Indian/Reunion'), ('Iran', 'Iran'), ('Israel', 'Israel'), ('Jamaica', 'Jamaica'), ('Japan', 'Japan'), ('Kwajalein', 'Kwajalein'), ('Libya', 'Libya'), ('MET', 'MET'), ('MST', 'MST'), ('MST7MDT', 'MST7MDT'), ('Mexico/BajaNorte', 'Mexico/BajaNorte'), ('Mexico/BajaSur', 'Mexico/BajaSur'), ('Mexico/General', 'Mexico/General'), ('NZ', 'NZ'), ('NZ-CHAT', 'NZ-CHAT'), ('Navajo', 'Navajo'), ('PRC', 'PRC'), ('PST8PDT', 'PST8PDT'), ('Pacific/Apia', 'Pacific/Apia'), ('Pacific/Auckland', 'Pacific/Auckland'), ('Pacific/Bougainville', 'Pacific/Bougainville'), ('Pacific/Chatham', 'Pacific/Chatham'), ('Pacific/Chuuk', 'Pacific/Chuuk'), ('Pacific/Easter', 'Pacific/Easter'), ('Pacific/Efate', 'Pacific/Efate'), ('Pacific/Enderbury', 'Pacific/Enderbury'), ('Pacific/Fakaofo', 'Pacific/Fakaofo'), ('Pacific/Fiji', 'Pacific/Fiji'), ('Pacific/Funafuti', 'Pacific/Funafuti'), ('Pacific/Galapagos', 'Pacific/Galapagos'), ('Pacific/Gambier', 'Pacific/Gambier'), ('Pacific/Guadalcanal', 'Pacific/Guadalcanal'), ('Pacific/Guam', 'Pacific/Guam'), ('Pacific/Honolulu', 'Pacific/Honolulu'), ('Pacific/Johnston', 'Pacific/Johnston'), ('Pacific/Kanton', 'Pacific/Kanton'), ('Pacific/Kiritimati', 'Pacific/Kiritimati'), ('Pacific/Kosrae', 'Pacific/Kosrae'), ('Pacific/Kwajalein', 'Pacific/Kwajalein'), ('Pacific/Majuro', 'Pacific/Majuro'), ('Pacific/Marquesas', 'Pacific/Marquesas'), ('Pacific/Midway', 'Pacific/Midway'), ('Pacific/Nauru', 'Pacific/Nauru'), ('Pacific/Niue', 'Pacific/Niue'), ('Pacific/Norfolk', 'Pacific/Norfolk'), ('Pacific/Noumea', 'Pacific/Noumea'), ('Pacific/Pago_Pago', 'Pacific/Pago_Pago'), ('Pacific/Palau', 'Pacific/Palau'), ('Pacific/Pitcairn', 'Pacific/Pitcairn'), ('Pacific/Pohnpei', 'Pacific/Pohnpei'), ('Pacific/Ponape', 'Pacific/Ponape'), ('Pacific/Port_Moresby', 'Pacific/Port_Moresby'), ('Pacific/Rarotonga', 'Pacific/Rarotonga'), ('Pacific/Saipan', 'Pacific/Saipan'), ('Pacific/Samoa', 'Pacific/Samoa'), ('Pacific/Tahiti', 'Pacific/Tahiti'), ('Pacific/Tarawa', 'Pacific/Tarawa'), ('Pacific/Tongatapu', 'Pacific/Tongatapu'), ('Pacific/Truk', 'Pacific/Truk'), ('Pacific/Wake', 'Pacific/Wake'), ('Pacific/Wallis', 'Pacific/Wallis'), ('Pacific/Yap', 'Pacific/Yap'), ('Poland', 'Poland'), ('Portugal', 'Portugal'), ('ROC', 'ROC'), ('ROK', 'ROK'), ('Singapore', 'Singapore'), ('Turkey', 'Turkey'), ('UCT', 'UCT'), ('US/Alaska', 'US/Alaska'), ('US/Aleutian', 'US/Aleutian'), ('US/Arizona', 'US/Arizona'), ('US/Central', 'US/Central'), ('US/East-Indiana', 'US/East-Indiana'), ('US/Eastern', 'US/Eastern'), ('US/Hawaii', 'US/Hawaii'), ('US/Indiana-Starke', 'US/Indiana-Starke'), ('US/Michigan', 'US/Michigan'), ('US/Mountain', 'US/Mountain'), ('US/Pacific', 'US/Pacific'), ('US/Samoa', 'US/Samoa'), ('UTC', 'UTC'), ('Universal', 'Universal'), ('W-SU', 'W-SU'), ('WET', 'WET'), ('Zulu', 'Zulu')], db_index=True, max_length=50),
29
+ ),
30
+ ]
simo/core/models.py CHANGED
@@ -12,9 +12,11 @@ from timezone_utils.choices import ALL_TIMEZONES_CHOICES
12
12
  from location_field.models.plain import PlainLocationField
13
13
  from model_utils import FieldTracker
14
14
  from dirtyfields import DirtyFieldsMixin
15
+ from actstream import action
15
16
  from simo.core.utils.mixins import SimoAdminMixin
16
17
  from simo.core.storage import OverwriteStorage
17
18
  from simo.core.utils.validators import validate_svg
19
+ from simo.core.utils.helpers import get_random_string
18
20
  from simo.users.models import User
19
21
  from .managers import ZonesManager, CategoriesManager, ComponentsManager
20
22
  from .events import GatewayObjectCommand, OnChangeMixin
@@ -64,16 +66,10 @@ class Instance(models.Model, SimoAdminMixin):
64
66
  # or something of that kind.
65
67
  # Usually, there will be only one.
66
68
  uid = models.CharField(
67
- max_length=50, unique=True,
68
- help_text="Issued by SIMO.io"
69
+ max_length=50, unique=True, help_text="Issued by SIMO.io"
69
70
  )
70
71
  name = models.CharField(max_length=100, db_index=True, unique=True)
71
72
  slug = models.CharField(max_length=100, db_index=True, unique=True)
72
- cover_image = models.ImageField(
73
- name='cover_image', upload_to='hub_covers', null=True, blank=True
74
- )
75
- cover_image_synced = models.BooleanField(default=False)
76
- secret_key = models.CharField(max_length=100, blank=True)
77
73
  date_created = models.DateTimeField(auto_now_add=True)
78
74
  location = PlainLocationField(null=True, blank=True, zoom=7)
79
75
  timezone = models.CharField(
@@ -399,6 +395,8 @@ def is_in_alarm(self):
399
395
 
400
396
  controller_cls = None
401
397
 
398
+ _controller_initiated = False
399
+
402
400
  _mqtt_client = None
403
401
  _on_change_function = None
404
402
  _obj_ct_id = 0
@@ -408,15 +406,21 @@ def is_in_alarm(self):
408
406
  verbose_name_plural = _("Components")
409
407
  ordering = 'zone', 'base_type', 'name'
410
408
 
411
- def __init__(self, *args, **kwargs):
412
- super().__init__(*args, **kwargs)
413
- self.prepare_controller()
414
-
415
409
  def __str__(self):
416
410
  if self.zone:
417
411
  return '%s | %s' % (self.zone.name, self.name)
418
412
  return self.name
419
413
 
414
+ def __getattribute__(self, attr):
415
+ try:
416
+ return super().__getattribute__(attr)
417
+ except Exception as e:
418
+ if not attr.startswith('_') and not self._controller_initiated:
419
+ self._controller_initiated = True
420
+ self.prepare_controller()
421
+ return super().__getattribute__(attr)
422
+ raise e
423
+
420
424
  @cached_property
421
425
  def controller(self):
422
426
  from .utils.type_constants import (
@@ -425,13 +429,13 @@ def is_in_alarm(self):
425
429
  )
426
430
  self._meta.get_field('controller_uid').choices = CONTROLLER_TYPES_CHOICES
427
431
  if self.controller_uid:
428
- controller_cls = None
429
- if not controller_cls:
430
- controller_cls = CONTROLLERS_BY_GATEWAY.get(
432
+ self.controller_cls = None
433
+ if not self.controller_cls:
434
+ self.controller_cls = CONTROLLERS_BY_GATEWAY.get(
431
435
  self.gateway.type, {}
432
436
  ).get(self.controller_uid)
433
- if controller_cls:
434
- return controller_cls(self)
437
+ if self.controller_cls:
438
+ return self.controller_cls(self)
435
439
 
436
440
  def prepare_controller(self):
437
441
  if self.controller:
@@ -483,6 +487,11 @@ def is_in_alarm(self):
483
487
  component=self, type='value', value=self.value,
484
488
  user=actor
485
489
  )
490
+ action.send(
491
+ actor, target=self, verb="value change",
492
+ instance_id=self.zone.instance.id,
493
+ action_type='comp_value', value=self.value
494
+ )
486
495
  action_performed = True
487
496
  self.last_change = timezone.now()
488
497
  if 'arm_status' in dirty_fields:
@@ -490,6 +499,11 @@ def is_in_alarm(self):
490
499
  component=self, type='security',
491
500
  value=self.arm_status, user=actor
492
501
  )
502
+ action.send(
503
+ actor, target=self, verb="security event",
504
+ instance_id=self.zone.instance.id,
505
+ action_type='security', value=self.value
506
+ )
493
507
  action_performed = True
494
508
  self.last_change = timezone.now()
495
509
  if action_performed:
simo/core/permissions.py CHANGED
@@ -10,9 +10,12 @@ class InstancePermission(BasePermission):
10
10
  if not request.user.is_active:
11
11
  return False
12
12
 
13
- instance = Instance.objects.filter(
14
- slug=request.resolver_match.kwargs.get('instance_slug')
15
- ).first()
13
+ instance = getattr(view, 'instance', None)
14
+ if not instance:
15
+ instance = Instance.objects.filter(
16
+ slug=request.resolver_match.kwargs.get('instance_slug')
17
+ ).first()
18
+
16
19
  if not instance:
17
20
  raise Http404()
18
21
 
simo/core/serializers.py CHANGED
@@ -1,9 +1,8 @@
1
1
  import inspect
2
2
  import datetime
3
- import json
3
+ import re
4
4
  from django import forms
5
5
  from collections import OrderedDict
6
- from django.forms.utils import ErrorDict
7
6
  from django.conf import settings
8
7
  from collections.abc import Iterable
9
8
  from easy_thumbnails.files import get_thumbnailer
@@ -11,7 +10,13 @@ from simo.core.middleware import get_current_request
11
10
  from rest_framework import serializers
12
11
  from rest_framework.fields import SkipField
13
12
  from rest_framework.relations import Hyperlink, PKOnlyObject
13
+ from actstream.models import Action
14
14
  from simo.core.forms import HiddenField, FormsetField
15
+ from simo.core.form_fields import (
16
+ Select2ListChoiceField, Select2ListChoiceField,
17
+ Select2ModelChoiceField, Select2ListMultipleChoiceField
18
+ )
19
+ from simo.core.models import Component
15
20
  from rest_framework.relations import PrimaryKeyRelatedField, ManyRelatedField
16
21
  from .drf_braces.serializers.form_serializer import (
17
22
  FormSerializer, FormSerializerBase, reduce_attr_dict_from_instance,
@@ -106,6 +111,7 @@ class ComponentFormsetField(FormSerializer):
106
111
  form = forms.Form
107
112
  field_mapping = {
108
113
  HiddenField: HiddenSerializerField,
114
+ Select2ListChoiceField: serializers.ChoiceField,
109
115
  forms.ModelChoiceField: FormsetPrimaryKeyRelatedField,
110
116
  forms.TypedChoiceField: serializers.ChoiceField,
111
117
  forms.FloatField: serializers.FloatField,
@@ -249,6 +255,9 @@ class ComponentSerializer(FormSerializer):
249
255
  arm_status = ObjectSerializerMethodField()
250
256
  battery_level = ObjectSerializerMethodField()
251
257
  controller_methods = serializers.SerializerMethodField()
258
+ info = serializers.SerializerMethodField()
259
+
260
+ _forms = {}
252
261
 
253
262
  class Meta:
254
263
  form = ComponentAdminForm
@@ -258,11 +267,27 @@ class ComponentSerializer(FormSerializer):
258
267
  forms.FloatField: serializers.FloatField,
259
268
  forms.SlugField: serializers.CharField,
260
269
  forms.ModelChoiceField: ComponentPrimaryKeyRelatedField,
270
+ Select2ModelChoiceField: ComponentPrimaryKeyRelatedField,
261
271
  forms.ModelMultipleChoiceField: ComponentManyToManyRelatedField,
272
+ Select2ListMultipleChoiceField: ComponentManyToManyRelatedField,
262
273
  FormsetField: ComponentFormsetField,
263
274
  }
264
275
 
276
+
277
+
278
+ def __init__(self, *args, **kwargs):
279
+ super().__init__(*args, **kwargs)
280
+ # Set proper instance for OPTIONS request
281
+ if not self.instance:
282
+ res = re.findall(
283
+ r'.*\/core\/components\/(?P<component_id>[0-9]+)\/',
284
+ self.context['request'].path
285
+ )
286
+ if res:
287
+ self.instance = Component.objects.filter(id=res[0]).first()
288
+
265
289
  def get_fields(self):
290
+
266
291
  self.set_form_cls()
267
292
 
268
293
  ret = OrderedDict()
@@ -322,6 +347,7 @@ class ComponentSerializer(FormSerializer):
322
347
  if name in ret:
323
348
  continue
324
349
  ret[name] = field
350
+
325
351
  return ret
326
352
 
327
353
  def _get_field_kwargs(self, form_field, serializer_field_class):
@@ -359,7 +385,15 @@ class ComponentSerializer(FormSerializer):
359
385
  if controller:
360
386
  self.Meta.form = controller.config_form
361
387
 
362
- def get_form(self, data=None, **kwargs):
388
+ def get_form(self, data=None, instance=None, **kwargs):
389
+ form_key = None
390
+ if not data:
391
+ form_key = 0
392
+ if instance:
393
+ form_key = instance.id
394
+ if form_key in self._forms:
395
+ return self._forms[form_key]
396
+
363
397
  self.set_form_cls()
364
398
  if not self.instance or isinstance(self.instance, Iterable):
365
399
  #controller_uid = 'simo.generic.controllers.AlarmClock'
@@ -368,7 +402,7 @@ class ComponentSerializer(FormSerializer):
368
402
  controller_uid = self.instance.controller_uid
369
403
  form = self.Meta.form(
370
404
  data=data, request=self.context['request'],
371
- controller_uid=controller_uid,
405
+ controller_uid=controller_uid, instance=instance,
372
406
  **kwargs
373
407
  )
374
408
  if not self.context['request'].user.is_master:
@@ -380,6 +414,10 @@ class ComponentSerializer(FormSerializer):
380
414
  if field_name not in form.basic_fields:
381
415
  print("DELETE FIELD: ", field_name)
382
416
  del form.fields[field_name]
417
+
418
+ if form_key is not None:
419
+ self._forms[form_key] = form
420
+
383
421
  return form
384
422
 
385
423
  def accomodate_formsets(self, form, data):
@@ -426,7 +464,6 @@ class ComponentSerializer(FormSerializer):
426
464
  a_data = self.accomodate_formsets(form, validated_data)
427
465
  form = self.get_form(instance=instance, data=a_data)
428
466
  if form.is_valid():
429
- print("FORM FIELDS", form.fields)
430
467
  instance = form.save(commit=True)
431
468
  return instance
432
469
  raise serializers.ValidationError(form.errors)
@@ -450,6 +487,10 @@ class ComponentSerializer(FormSerializer):
450
487
  c_methods.extend(['arm', 'disarm'])
451
488
  return c_methods
452
489
 
490
+ def get_info(self, obj):
491
+ if obj.controller:
492
+ return obj.controller.info()
493
+
453
494
  def get_read_only(self, obj):
454
495
  user = self.context.get('user')
455
496
  if not user:
@@ -507,3 +548,34 @@ class ComponentHistorySerializer(serializers.ModelSerializer):
507
548
  class Meta:
508
549
  model = ComponentHistory
509
550
  fields = '__all__'
551
+
552
+
553
+ class ActionSerializer(serializers.ModelSerializer):
554
+ timestamp = TimestampField()
555
+ actor = serializers.SerializerMethodField()
556
+ target = serializers.SerializerMethodField()
557
+ action_type = serializers.SerializerMethodField()
558
+ value = serializers.SerializerMethodField()
559
+
560
+ class Meta:
561
+ model = Action
562
+ fields = (
563
+ 'id', 'timestamp', 'actor', 'target', 'verb',
564
+ 'action_type', 'value'
565
+ )
566
+
567
+ def get_actor(self, obj):
568
+ if obj.actor:
569
+ return str(obj.actor)
570
+
571
+ def get_target(self, obj):
572
+ if obj.target:
573
+ return str(obj.target)
574
+
575
+
576
+ def get_action_type(self, obj):
577
+ return obj.data.get('action_type')
578
+
579
+ def get_value(self, obj):
580
+ return obj.data.get('value')
581
+