simo 2.1.0__py3-none-any.whl → 2.1.3__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/asgi.py +1 -1
- simo/core/__pycache__/admin.cpython-38.pyc +0 -0
- simo/core/__pycache__/api.cpython-38.pyc +0 -0
- simo/core/__pycache__/apps.cpython-38.pyc +0 -0
- simo/core/__pycache__/auto_urls.cpython-38.pyc +0 -0
- simo/core/__pycache__/dynamic_settings.cpython-38.pyc +0 -0
- simo/core/__pycache__/forms.cpython-38.pyc +0 -0
- simo/core/__pycache__/models.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 +0 -18
- simo/core/api.py +1 -0
- simo/core/apps.py +4 -1
- simo/core/auto_urls.py +2 -3
- simo/core/dynamic_settings.py +0 -8
- simo/core/forms.py +1 -98
- simo/core/migrations/0038_remove_instance_cover_image_and_more.py +30 -0
- simo/core/migrations/__pycache__/0038_remove_instance_cover_image_and_more.cpython-38.pyc +0 -0
- simo/core/models.py +2 -7
- simo/core/tasks.py +95 -51
- simo/core/templates/admin/user_tools.html +0 -3
- simo/core/views.py +2 -85
- simo/fleet/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/fleet/controllers.py +1 -1
- simo/fleet/migrations/0037_alter_colonelpin_options_alter_colonelpin_no_and_more.py +27 -0
- simo/fleet/migrations/__pycache__/0037_alter_colonelpin_options_alter_colonelpin_no_and_more.cpython-38.pyc +0 -0
- simo/generic/__pycache__/controllers.cpython-38.pyc +0 -0
- simo/generic/__pycache__/forms.cpython-38.pyc +0 -0
- simo/generic/controllers.py +2 -2
- simo/generic/forms.py +0 -3
- simo/generic/templates/admin/controller_widgets/weather_forecast.html +1 -1
- simo/management/__init__.py +0 -0
- simo/management/__pycache__/__init__.cpython-38.pyc +0 -0
- simo/management/__pycache__/on_http_start.cpython-38.pyc +0 -0
- simo/{_hub_template → management/_hub_template}/hub/nginx.conf +2 -2
- simo/{auto_update.py → management/auto_update.py} +3 -0
- simo/{cli.py → management/copy_template.py} +3 -16
- simo/management/install.py +258 -0
- simo/{on_http_start.py → management/on_http_start.py} +22 -2
- {simo-2.1.0.dist-info → simo-2.1.3.dist-info}/METADATA +1 -1
- {simo-2.1.0.dist-info → simo-2.1.3.dist-info}/RECORD +52 -44
- simo-2.1.3.dist-info/entry_points.txt +2 -0
- simo/__pycache__/on_http_start.cpython-38.pyc +0 -0
- /simo/{_hub_template → management/_hub_template}/hub/asgi.py +0 -0
- /simo/{_hub_template → management/_hub_template}/hub/celeryc.py +0 -0
- /simo/{_hub_template → management/_hub_template}/hub/manage.py +0 -0
- /simo/{_hub_template → management/_hub_template}/hub/settings.py +0 -0
- /simo/{_hub_template → management/_hub_template}/hub/supervisor.conf +0 -0
- /simo/{_hub_template → management/_hub_template}/hub/urls.py +0 -0
- {simo-2.1.0.dist-info → simo-2.1.3.dist-info}/LICENSE.md +0 -0
- {simo-2.1.0.dist-info → simo-2.1.3.dist-info}/WHEEL +0 -0
- {simo-2.1.0.dist-info → simo-2.1.3.dist-info}/top_level.txt +0 -0
|
Binary file
|
simo/asgi.py
CHANGED
|
@@ -21,7 +21,7 @@ for name, app in apps.app_configs.items():
|
|
|
21
21
|
if isinstance(item, list) and var_name == 'urlpatterns':
|
|
22
22
|
urlpatterns.extend(item)
|
|
23
23
|
|
|
24
|
-
from .on_http_start import *
|
|
24
|
+
from .management.on_http_start import *
|
|
25
25
|
|
|
26
26
|
application = ProtocolTypeRouter({
|
|
27
27
|
"http": get_asgi_application(),
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
simo/core/admin.py
CHANGED
|
@@ -60,24 +60,6 @@ class IconAdmin(EasyObjectsDeleteMixin, admin.ModelAdmin):
|
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
@admin.register(Instance)
|
|
65
|
-
class InstanceAdmin(EasyObjectsDeleteMixin, admin.ModelAdmin):
|
|
66
|
-
list_display = 'name', 'timezone', 'uid'
|
|
67
|
-
exclude = 'learn_fingerprints_start', 'learn_fingerprints'
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def has_module_permission(self, request):
|
|
71
|
-
return request.user.is_master
|
|
72
|
-
|
|
73
|
-
def has_view_permission(self, request, obj=None):
|
|
74
|
-
return self.has_module_permission(request)
|
|
75
|
-
|
|
76
|
-
def has_change_permission(self, request, obj=None):
|
|
77
|
-
return self.has_module_permission(request)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
63
|
@admin.register(Zone)
|
|
82
64
|
class ZoneAdmin(EasyObjectsDeleteMixin, SortableAdminMixin, admin.ModelAdmin):
|
|
83
65
|
list_display = 'name', 'instance'
|
simo/core/api.py
CHANGED
|
@@ -545,6 +545,7 @@ class SettingsViewSet(InstanceMixin, viewsets.GenericViewSet):
|
|
|
545
545
|
main_alarm_group_id = main_alarm_group.id
|
|
546
546
|
|
|
547
547
|
return RESTResponse({
|
|
548
|
+
'hub_uid': dynamic_settings['core__hub_uid'],
|
|
548
549
|
'instance_name': self.instance.name,
|
|
549
550
|
'instance_uid': self.instance.uid,
|
|
550
551
|
'timezone': self.instance.timezone,
|
simo/core/apps.py
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
from django.apps import AppConfig
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
|
|
4
5
|
class SIMOCoreAppConfig(AppConfig):
|
|
5
6
|
name = 'simo.core'
|
|
6
7
|
|
|
7
8
|
def ready(self):
|
|
8
9
|
from actstream import registry
|
|
9
10
|
registry.register(self.get_model('Component'))
|
|
10
|
-
registry.register(self.get_model('Gateway'))
|
|
11
|
+
registry.register(self.get_model('Gateway'))
|
|
12
|
+
|
|
13
|
+
|
simo/core/auto_urls.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from django.urls import path
|
|
2
2
|
from .views import (
|
|
3
|
-
get_timestamp,
|
|
3
|
+
get_timestamp, update, restart, reboot, set_instance
|
|
4
4
|
)
|
|
5
5
|
from .autocomplete_views import (
|
|
6
|
-
IconModelAutocomplete,
|
|
6
|
+
IconModelAutocomplete,
|
|
7
7
|
CategoryAutocomplete, ZoneAutocomplete,
|
|
8
8
|
ComponentAutocomplete,
|
|
9
9
|
)
|
|
@@ -31,7 +31,6 @@ urlpatterns = [
|
|
|
31
31
|
ComponentAutocomplete.as_view(), name='autocomplete-component'
|
|
32
32
|
),
|
|
33
33
|
path('set-instance/<slug:instance_slug>/', set_instance, name='set-instance'),
|
|
34
|
-
path('setup/', setup_wizard, name='setup-wizard'),
|
|
35
34
|
path('update/', update, name='update'),
|
|
36
35
|
path('restart/', restart, name='restart'),
|
|
37
36
|
path('reboot/', reboot, name='reboot')
|
simo/core/dynamic_settings.py
CHANGED
|
@@ -43,14 +43,6 @@ class RemoteConnectionVersion(IntegerPreference):
|
|
|
43
43
|
"when hub get's synced up to the simo.io."
|
|
44
44
|
|
|
45
45
|
|
|
46
|
-
@global_preferences_registry.register
|
|
47
|
-
class UnitsOfMeasure(ChoicePreference):
|
|
48
|
-
section = core
|
|
49
|
-
name = 'units_of_measure'
|
|
50
|
-
choices = (('metric', "Metric"), ('imperial', "Imperial"))
|
|
51
|
-
default = 'metric'
|
|
52
|
-
required = True
|
|
53
|
-
|
|
54
46
|
|
|
55
47
|
@global_preferences_registry.register
|
|
56
48
|
class LatestHubOSVersionAvailable(StringPreference):
|
simo/core/forms.py
CHANGED
|
@@ -1,28 +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
11
|
from actstream import action
|
|
16
|
-
from timezone_utils.choices import ALL_TIMEZONES_CHOICES
|
|
17
12
|
from dal import autocomplete
|
|
18
13
|
from .models import (
|
|
19
14
|
Icon, Category, Gateway, Component
|
|
20
15
|
)
|
|
21
|
-
from .widgets import
|
|
22
|
-
from simo.conf import dynamic_settings
|
|
23
|
-
from .widgets import SVGFileWidget, PythonCode, LogOutputWidget
|
|
24
|
-
from .widgets import ImageWidget
|
|
25
|
-
from .utils.helpers import get_random_string
|
|
16
|
+
from .widgets import SVGFileWidget, LogOutputWidget
|
|
26
17
|
from .utils.formsets import FormsetField
|
|
27
18
|
from .utils.validators import validate_slaves
|
|
28
19
|
|
|
@@ -35,94 +26,6 @@ class HiddenField(forms.CharField):
|
|
|
35
26
|
super().__init__(widget=forms.HiddenInput())
|
|
36
27
|
|
|
37
28
|
|
|
38
|
-
class HubConfigForm(forms.Form):
|
|
39
|
-
name = forms.CharField(
|
|
40
|
-
label=_("Hub Name"), required=True,
|
|
41
|
-
widget=forms.TextInput(attrs={'placeholder': "Home Sweet Home"})
|
|
42
|
-
)
|
|
43
|
-
uid = forms.CharField(
|
|
44
|
-
label=_('Unique Identifier (UID)'), required=False,
|
|
45
|
-
widget=forms.TextInput(attrs={'placeholder': "Df5Hd8v1"}),
|
|
46
|
-
help_text="Leave blank if this is a new instance."
|
|
47
|
-
)
|
|
48
|
-
time_zone = forms.ChoiceField(
|
|
49
|
-
label=_("Time zone"), required=True,
|
|
50
|
-
choices=ALL_TIMEZONES_CHOICES
|
|
51
|
-
)
|
|
52
|
-
units_of_measure = forms.ChoiceField(
|
|
53
|
-
label=_("Units of Measure"), required=True,
|
|
54
|
-
choices=(('metric', 'Metric'), ('imperial', 'Imperial'))
|
|
55
|
-
)
|
|
56
|
-
cover_image = forms.FileField(
|
|
57
|
-
label=_("Cover image"), required=True, widget=ImageWidget
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
def __init__(self, *args, **kwargs):
|
|
61
|
-
self.user = kwargs.pop('user')
|
|
62
|
-
super().__init__(*args, **kwargs)
|
|
63
|
-
|
|
64
|
-
def clean(self):
|
|
65
|
-
cleaned_data = super().clean()
|
|
66
|
-
post_data = cleaned_data.copy()
|
|
67
|
-
post_data.pop('cover_image')
|
|
68
|
-
if not dynamic_settings['core__hub_secret']:
|
|
69
|
-
dynamic_settings['core__hub_secret'] = get_random_string(20)
|
|
70
|
-
post_data['secret'] = dynamic_settings['core__hub_secret']
|
|
71
|
-
post_data['email'] = self.user.email
|
|
72
|
-
try:
|
|
73
|
-
resp = requests.post(
|
|
74
|
-
'https://simo.io/hubs/sync-initial-config/', json=post_data,
|
|
75
|
-
)
|
|
76
|
-
except Exception as e:
|
|
77
|
-
raise forms.ValidationError(
|
|
78
|
-
"Connection error. "
|
|
79
|
-
"Make sure your hub can reach https://simo.io and try again."
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
if resp.status_code == 400:
|
|
83
|
-
resp_json = resp.json()
|
|
84
|
-
resp_json.pop('status', None)
|
|
85
|
-
for field_name, msg in resp_json.items():
|
|
86
|
-
self.add_error(field_name, msg)
|
|
87
|
-
elif resp.status_code == 200:
|
|
88
|
-
cleaned_data['uid'] = resp.json()['uid']
|
|
89
|
-
else:
|
|
90
|
-
raise forms.ValidationError(
|
|
91
|
-
"Bad response from https://simo.io. Please try again. "
|
|
92
|
-
)
|
|
93
|
-
return cleaned_data
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
class CoordinatesForm(forms.Form):
|
|
97
|
-
location = forms.CharField(
|
|
98
|
-
label=_("Where is your hub located?"),
|
|
99
|
-
widget=LocationWidget(based_fields=[])
|
|
100
|
-
)
|
|
101
|
-
share_location = forms.BooleanField(
|
|
102
|
-
label="Share exact location with SIMO.io for "
|
|
103
|
-
"better accuracy of location related services.",
|
|
104
|
-
required=False
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
def __init__(self, *args, **kwargs):
|
|
108
|
-
super().__init__(*args, **kwargs)
|
|
109
|
-
if not self.initial['location']:
|
|
110
|
-
self.fields['location'].widget = LocationWidget(
|
|
111
|
-
based_fields=[], zoom=2
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
class TermsAndConditionsForm(forms.Form):
|
|
118
|
-
accept = forms.BooleanField(required=False)
|
|
119
|
-
|
|
120
|
-
def clean_accept(self):
|
|
121
|
-
if not self.cleaned_data['accept']:
|
|
122
|
-
raise forms.ValidationError(_("You must accept SIMO.io Terms & Conditions if you want to continue."))
|
|
123
|
-
return self.cleaned_data['accept']
|
|
124
|
-
|
|
125
|
-
|
|
126
29
|
class AdminAuthenticationForm(OrgAdminAuthenticationForm):
|
|
127
30
|
|
|
128
31
|
def confirm_login_allowed(self, user):
|
|
@@ -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
|
+
]
|
|
Binary file
|
simo/core/models.py
CHANGED
|
@@ -16,6 +16,7 @@ from actstream import action
|
|
|
16
16
|
from simo.core.utils.mixins import SimoAdminMixin
|
|
17
17
|
from simo.core.storage import OverwriteStorage
|
|
18
18
|
from simo.core.utils.validators import validate_svg
|
|
19
|
+
from simo.core.utils.helpers import get_random_string
|
|
19
20
|
from simo.users.models import User
|
|
20
21
|
from .managers import ZonesManager, CategoriesManager, ComponentsManager
|
|
21
22
|
from .events import GatewayObjectCommand, OnChangeMixin
|
|
@@ -65,16 +66,10 @@ class Instance(models.Model, SimoAdminMixin):
|
|
|
65
66
|
# or something of that kind.
|
|
66
67
|
# Usually, there will be only one.
|
|
67
68
|
uid = models.CharField(
|
|
68
|
-
max_length=50, unique=True,
|
|
69
|
-
help_text="Issued by SIMO.io"
|
|
69
|
+
max_length=50, unique=True, help_text="Issued by SIMO.io"
|
|
70
70
|
)
|
|
71
71
|
name = models.CharField(max_length=100, db_index=True, unique=True)
|
|
72
72
|
slug = models.CharField(max_length=100, db_index=True, unique=True)
|
|
73
|
-
cover_image = models.ImageField(
|
|
74
|
-
name='cover_image', upload_to='hub_covers', null=True, blank=True
|
|
75
|
-
)
|
|
76
|
-
cover_image_synced = models.BooleanField(default=False)
|
|
77
|
-
secret_key = models.CharField(max_length=100, blank=True)
|
|
78
73
|
date_created = models.DateTimeField(auto_now_add=True)
|
|
79
74
|
location = PlainLocationField(null=True, blank=True, zoom=7)
|
|
80
75
|
timezone = models.CharField(
|
simo/core/tasks.py
CHANGED
|
@@ -2,20 +2,23 @@ import time
|
|
|
2
2
|
import os
|
|
3
3
|
import io
|
|
4
4
|
import json
|
|
5
|
-
import base64
|
|
6
5
|
import datetime
|
|
7
6
|
import requests
|
|
8
7
|
import subprocess
|
|
9
8
|
import threading
|
|
10
9
|
import pkg_resources
|
|
10
|
+
import sys
|
|
11
|
+
import traceback
|
|
11
12
|
from django.db.models import Q
|
|
12
|
-
from django.db import connection
|
|
13
|
+
from django.db import connection, transaction
|
|
13
14
|
from django.template.loader import render_to_string
|
|
14
15
|
from celeryc import celery_app
|
|
15
16
|
from django.utils import timezone
|
|
17
|
+
from actstream.models import Action
|
|
16
18
|
from easy_thumbnails.files import get_thumbnailer
|
|
17
19
|
from simo.conf import dynamic_settings
|
|
18
20
|
from simo.core.utils.helpers import get_self_ip
|
|
21
|
+
from simo.users.models import PermissionsRole, InstanceUser
|
|
19
22
|
from .models import Instance, Component, ComponentHistory, HistoryAggregate
|
|
20
23
|
|
|
21
24
|
|
|
@@ -99,19 +102,15 @@ def save_config(data):
|
|
|
99
102
|
def sync_with_remote():
|
|
100
103
|
from simo.users.models import User
|
|
101
104
|
|
|
102
|
-
instances = Instance.objects.all()
|
|
103
|
-
if not instances:
|
|
104
|
-
# No initial configuration yet
|
|
105
|
-
return
|
|
106
|
-
|
|
107
105
|
report_data = {
|
|
108
106
|
'simo_version': pkg_resources.get_distribution('simo').version,
|
|
109
107
|
'local_http': 'https://%s' % get_self_ip(),
|
|
110
108
|
'hub_uid': dynamic_settings['core__hub_uid'],
|
|
111
109
|
'hub_secret': dynamic_settings['core__hub_secret'],
|
|
110
|
+
'remote_conn_version': dynamic_settings['core__remote_conn_version'],
|
|
112
111
|
'instances': []
|
|
113
112
|
}
|
|
114
|
-
for instance in
|
|
113
|
+
for instance in Instance.objects.all():
|
|
115
114
|
instance_data = {
|
|
116
115
|
'uid': instance.uid,
|
|
117
116
|
'name': instance.name,
|
|
@@ -131,10 +130,14 @@ def sync_with_remote():
|
|
|
131
130
|
user_role = user.get_role(instance)
|
|
132
131
|
if user_role and user_role.is_superuser:
|
|
133
132
|
is_superuser = True
|
|
133
|
+
is_owner = False
|
|
134
|
+
if user_role and user_role.is_owner:
|
|
135
|
+
is_owner = True
|
|
134
136
|
instance_data['users'].append({
|
|
135
137
|
'email': user.email,
|
|
136
138
|
'is_hub_master': user.is_master,
|
|
137
139
|
'is_superuser': is_superuser,
|
|
140
|
+
'is_owner': is_owner,
|
|
138
141
|
'device_token': user.primary_device_token
|
|
139
142
|
})
|
|
140
143
|
|
|
@@ -144,15 +147,6 @@ def sync_with_remote():
|
|
|
144
147
|
if last_event:
|
|
145
148
|
instance_data['last_event'] = last_event.date.timestamp()
|
|
146
149
|
|
|
147
|
-
if instance.cover_image and not instance.cover_image_synced:
|
|
148
|
-
thumbnailer = get_thumbnailer(instance.cover_image.path)
|
|
149
|
-
cover_imb_path = thumbnailer.get_thumbnail(
|
|
150
|
-
{'size': (880, 490), 'crop': True}
|
|
151
|
-
).path
|
|
152
|
-
with open(cover_imb_path, 'rb') as img:
|
|
153
|
-
instance_data['cover_image'] = base64.b64encode(
|
|
154
|
-
img.read()
|
|
155
|
-
).decode()
|
|
156
150
|
report_data['instances'].append(instance_data)
|
|
157
151
|
|
|
158
152
|
print("Sync UP with remote: ", json.dumps(report_data))
|
|
@@ -169,11 +163,7 @@ def sync_with_remote():
|
|
|
169
163
|
if 'hub_uid' in r_json:
|
|
170
164
|
dynamic_settings['core__hub_uid'] = r_json['hub_uid']
|
|
171
165
|
|
|
172
|
-
|
|
173
|
-
instance.cover_image_synced = True
|
|
174
|
-
instance.save()
|
|
175
|
-
|
|
176
|
-
dynamic_settings['core__remote_http'] = r_json.get('hub_remote_http')
|
|
166
|
+
dynamic_settings['core__remote_http'] = r_json.get('hub_remote_http', '')
|
|
177
167
|
if 'new_secret' in r_json:
|
|
178
168
|
dynamic_settings['core__hub_secret'] = r_json['new_secret']
|
|
179
169
|
|
|
@@ -182,9 +172,14 @@ def sync_with_remote():
|
|
|
182
172
|
dynamic_settings['core__remote_conn_version'] = r_json['remote_conn_version']
|
|
183
173
|
|
|
184
174
|
for data in r_json['instances']:
|
|
185
|
-
|
|
175
|
+
users_data = data.pop('users', {})
|
|
176
|
+
instance_uid = data.pop('uid')
|
|
177
|
+
weather_forecast = data.pop('weather_forecast', None)
|
|
178
|
+
instance, new_instance = Instance.objects.update_or_create(
|
|
179
|
+
uid=instance_uid, defaults=data
|
|
180
|
+
)
|
|
186
181
|
|
|
187
|
-
if
|
|
182
|
+
if weather_forecast:
|
|
188
183
|
from simo.generic.controllers import WeatherForecast
|
|
189
184
|
weather_component = Component.objects.filter(
|
|
190
185
|
zone__instance=instance,
|
|
@@ -192,27 +187,55 @@ def sync_with_remote():
|
|
|
192
187
|
).first()
|
|
193
188
|
if weather_component:
|
|
194
189
|
weather_component.track_history = False
|
|
195
|
-
weather_component.controller.set(
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
190
|
+
weather_component.controller.set(weather_forecast)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
for email, options in users_data.items():
|
|
194
|
+
with transaction.atomic():
|
|
195
|
+
if new_instance:
|
|
196
|
+
# Create users for new instance!
|
|
197
|
+
user, new_user = User.objects.update_or_create(
|
|
198
|
+
email=email, defaults={
|
|
199
|
+
'name': options.get('name'),
|
|
200
|
+
'is_master': options.get('is_hub_master', False),
|
|
201
|
+
'ssh_key': options.get('ssh_key')
|
|
202
|
+
})
|
|
203
|
+
role = None
|
|
204
|
+
if options.get('is_superuser'):
|
|
205
|
+
role = PermissionsRole.objects.filter(
|
|
206
|
+
instance=new_instance, is_superuser=True
|
|
207
|
+
).first()
|
|
208
|
+
elif options.get('is_owner'):
|
|
209
|
+
role = PermissionsRole.objects.filter(
|
|
210
|
+
instance=new_instance, is_owner=True
|
|
211
|
+
).first()
|
|
212
|
+
InstanceUser.objects.update_or_create(
|
|
213
|
+
user=user, instance=new_instance, defaults={
|
|
214
|
+
'is_active': True, 'role': role
|
|
215
|
+
}
|
|
216
|
+
)
|
|
217
|
+
else:
|
|
218
|
+
user = User.objects.filter(email=email).first()
|
|
219
|
+
|
|
220
|
+
if not user:
|
|
221
|
+
continue
|
|
222
|
+
|
|
223
|
+
if user.name != options.get('name'):
|
|
224
|
+
user.name = options['name']
|
|
225
|
+
user.save()
|
|
226
|
+
if user.ssh_key != options.get('ssh_key'):
|
|
227
|
+
user.ssh_key = options['ssh_key']
|
|
228
|
+
user.save()
|
|
229
|
+
|
|
230
|
+
avatar_url = options.get('avatar_url')
|
|
231
|
+
if avatar_url and user.avatar_url != avatar_url:
|
|
232
|
+
resp = requests.get(avatar_url)
|
|
233
|
+
user.avatar.save(
|
|
234
|
+
os.path.basename(avatar_url), io.BytesIO(resp.content)
|
|
235
|
+
)
|
|
236
|
+
user.avatar_url = avatar_url
|
|
237
|
+
user.avatar_last_change = timezone.now()
|
|
238
|
+
user.save()
|
|
216
239
|
|
|
217
240
|
|
|
218
241
|
@celery_app.task
|
|
@@ -223,7 +246,10 @@ def watch_timers():
|
|
|
223
246
|
component.meta['timer_to'] = 0
|
|
224
247
|
component.meta['timer_start'] = 0
|
|
225
248
|
component.save()
|
|
226
|
-
|
|
249
|
+
try:
|
|
250
|
+
component.controller._on_timer_end()
|
|
251
|
+
except Exception as e:
|
|
252
|
+
print(traceback.format_exc(), file=sys.stderr)
|
|
227
253
|
|
|
228
254
|
|
|
229
255
|
@celery_app.task
|
|
@@ -232,8 +258,15 @@ def clear_history():
|
|
|
232
258
|
old_times = timezone.now() - datetime.timedelta(
|
|
233
259
|
days=instance.history_days
|
|
234
260
|
)
|
|
235
|
-
ComponentHistory.objects.filter(
|
|
236
|
-
|
|
261
|
+
ComponentHistory.objects.filter(
|
|
262
|
+
component__zone__instance=instance, date__lt=old_times
|
|
263
|
+
).delete()
|
|
264
|
+
HistoryAggregate.objects.filter(
|
|
265
|
+
component__zone__instance=instance, start__lt=old_times
|
|
266
|
+
).delete()
|
|
267
|
+
Action.objects.filter(
|
|
268
|
+
data__instance_id=instance.id, timestamp__lt=old_times
|
|
269
|
+
)
|
|
237
270
|
|
|
238
271
|
|
|
239
272
|
@celery_app.task
|
|
@@ -288,10 +321,20 @@ def vacuum_full():
|
|
|
288
321
|
|
|
289
322
|
@celery_app.task
|
|
290
323
|
def update():
|
|
291
|
-
from simo.auto_update import perform_update
|
|
324
|
+
from simo.management.auto_update import perform_update
|
|
292
325
|
perform_update()
|
|
293
326
|
|
|
294
327
|
|
|
328
|
+
@celery_app.task
|
|
329
|
+
def auto_update():
|
|
330
|
+
if not dynamic_settings['core__auto_update']:
|
|
331
|
+
return
|
|
332
|
+
if dynamic_settings['core__latest_version_available'] != \
|
|
333
|
+
pkg_resources.get_distribution('simo').version:
|
|
334
|
+
return update()
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
|
|
295
338
|
@celery_app.task
|
|
296
339
|
def update_latest_version_available():
|
|
297
340
|
resp = requests.get("https://pypi.org/pypi/simo/json")
|
|
@@ -300,6 +343,7 @@ def update_latest_version_available():
|
|
|
300
343
|
return
|
|
301
344
|
latest = list(resp.json()['releases'].keys())[-1]
|
|
302
345
|
dynamic_settings['core__latest_version_available'] = latest
|
|
346
|
+
print("Got the latest version available!")
|
|
303
347
|
|
|
304
348
|
|
|
305
349
|
@celery_app.task
|
|
@@ -358,13 +402,13 @@ def low_battery_notifications():
|
|
|
358
402
|
)
|
|
359
403
|
|
|
360
404
|
|
|
361
|
-
|
|
362
405
|
@celery_app.on_after_finalize.connect
|
|
363
406
|
def setup_periodic_tasks(sender, **kwargs):
|
|
364
407
|
sender.add_periodic_task(1, watch_timers.s())
|
|
365
408
|
sender.add_periodic_task(20, sync_with_remote.s())
|
|
366
409
|
sender.add_periodic_task(60 * 60, clear_history.s())
|
|
367
|
-
sender.add_periodic_task(60 * 60
|
|
410
|
+
sender.add_periodic_task(60 * 60, update_latest_version_available.s())
|
|
411
|
+
sender.add_periodic_task(60 * 60, auto_update.s())
|
|
368
412
|
sender.add_periodic_task(60, drop_fingerprints_learn.s())
|
|
369
413
|
sender.add_periodic_task(60 * 60 * 24, restart_postgresql.s())
|
|
370
414
|
sender.add_periodic_task(60 * 60, low_battery_notifications.s())
|