simo 2.1.0__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 (53) hide show
  1. simo/__pycache__/asgi.cpython-38.pyc +0 -0
  2. simo/asgi.py +1 -1
  3. simo/core/__pycache__/admin.cpython-38.pyc +0 -0
  4. simo/core/__pycache__/api.cpython-38.pyc +0 -0
  5. simo/core/__pycache__/apps.cpython-38.pyc +0 -0
  6. simo/core/__pycache__/auto_urls.cpython-38.pyc +0 -0
  7. simo/core/__pycache__/dynamic_settings.cpython-38.pyc +0 -0
  8. simo/core/__pycache__/forms.cpython-38.pyc +0 -0
  9. simo/core/__pycache__/models.cpython-38.pyc +0 -0
  10. simo/core/__pycache__/tasks.cpython-38.pyc +0 -0
  11. simo/core/__pycache__/views.cpython-38.pyc +0 -0
  12. simo/core/admin.py +0 -18
  13. simo/core/api.py +1 -0
  14. simo/core/apps.py +4 -1
  15. simo/core/auto_urls.py +2 -3
  16. simo/core/dynamic_settings.py +0 -8
  17. simo/core/forms.py +1 -98
  18. simo/core/migrations/0038_remove_instance_cover_image_and_more.py +30 -0
  19. simo/core/migrations/__pycache__/0038_remove_instance_cover_image_and_more.cpython-38.pyc +0 -0
  20. simo/core/models.py +2 -7
  21. simo/core/tasks.py +82 -49
  22. simo/core/templates/admin/user_tools.html +0 -3
  23. simo/core/views.py +2 -85
  24. simo/fleet/__pycache__/controllers.cpython-38.pyc +0 -0
  25. simo/fleet/controllers.py +1 -1
  26. simo/fleet/migrations/0037_alter_colonelpin_options_alter_colonelpin_no_and_more.py +27 -0
  27. simo/fleet/migrations/__pycache__/0037_alter_colonelpin_options_alter_colonelpin_no_and_more.cpython-38.pyc +0 -0
  28. simo/generic/__pycache__/controllers.cpython-38.pyc +0 -0
  29. simo/generic/__pycache__/forms.cpython-38.pyc +0 -0
  30. simo/generic/controllers.py +2 -2
  31. simo/generic/forms.py +0 -3
  32. simo/generic/templates/admin/controller_widgets/weather_forecast.html +1 -1
  33. simo/management/__init__.py +0 -0
  34. simo/management/__pycache__/__init__.cpython-38.pyc +0 -0
  35. simo/management/__pycache__/on_http_start.cpython-38.pyc +0 -0
  36. simo/{_hub_template → management/_hub_template}/hub/nginx.conf +2 -2
  37. simo/{auto_update.py → management/auto_update.py} +3 -0
  38. simo/{cli.py → management/copy_template.py} +3 -16
  39. simo/management/install.py +258 -0
  40. simo/{on_http_start.py → management/on_http_start.py} +22 -2
  41. {simo-2.1.0.dist-info → simo-2.1.2.dist-info}/METADATA +1 -1
  42. {simo-2.1.0.dist-info → simo-2.1.2.dist-info}/RECORD +52 -44
  43. simo-2.1.2.dist-info/entry_points.txt +2 -0
  44. simo/__pycache__/on_http_start.cpython-38.pyc +0 -0
  45. /simo/{_hub_template → management/_hub_template}/hub/asgi.py +0 -0
  46. /simo/{_hub_template → management/_hub_template}/hub/celeryc.py +0 -0
  47. /simo/{_hub_template → management/_hub_template}/hub/manage.py +0 -0
  48. /simo/{_hub_template → management/_hub_template}/hub/settings.py +0 -0
  49. /simo/{_hub_template → management/_hub_template}/hub/supervisor.conf +0 -0
  50. /simo/{_hub_template → management/_hub_template}/hub/urls.py +0 -0
  51. {simo-2.1.0.dist-info → simo-2.1.2.dist-info}/LICENSE.md +0 -0
  52. {simo-2.1.0.dist-info → simo-2.1.2.dist-info}/WHEEL +0 -0
  53. {simo-2.1.0.dist-info → simo-2.1.2.dist-info}/top_level.txt +0 -0
simo/core/views.py CHANGED
@@ -3,100 +3,17 @@ import threading
3
3
  import subprocess
4
4
  from django.contrib.auth.decorators import login_required
5
5
  from django.urls import reverse
6
- from django.shortcuts import get_object_or_404, render, redirect
7
- from django.http import FileResponse, HttpResponse, Http404
8
- from django.contrib.gis.geos import Point
6
+ from django.shortcuts import redirect
7
+ from django.http import HttpResponse, Http404
9
8
  from django.contrib import messages
10
- from simo.users.models import User
11
- from simo.conf import dynamic_settings
12
9
  from .models import Instance
13
10
  from .tasks import update as update_task, supervisor_restart
14
- from .forms import (
15
- HubConfigForm, CoordinatesForm, TermsAndConditionsForm
16
- )
17
11
  from .middleware import introduce_instance
18
12
 
19
13
 
20
14
  def get_timestamp(request):
21
15
  return HttpResponse(time.time())
22
16
 
23
-
24
- @login_required
25
- def setup_wizard(request):
26
-
27
- step = request.session.get('setup_wizard_step', 1)
28
-
29
- if request.method == 'POST' and 'back' in request.POST and step > 1:
30
- request.session['setup_wizard_step'] = step - 1
31
- return redirect(request.path)
32
-
33
- if step == 1:
34
- cover_img = dynamic_settings['core__cover_image']
35
- if cover_img:
36
- cover_img.field.storage = cover_img.storage
37
- initial = {
38
- 'name': dynamic_settings['core__hub_name'],
39
- 'uid': dynamic_settings['core__hub_uid'],
40
- 'time_zone': dynamic_settings['core__time_zone'],
41
- 'units_of_measure': dynamic_settings['core__units_of_measure'],
42
- 'cover_image': cover_img
43
- }
44
- form = HubConfigForm(initial=initial, user=request.user)
45
- if request.method == 'POST':
46
- form = HubConfigForm(
47
- request.POST, request.FILES, initial=initial, user=request.user
48
- )
49
- if form.is_valid():
50
- dynamic_settings['core__hub_name'] = form.cleaned_data['name']
51
- dynamic_settings['core__hub_uid'] = form.cleaned_data['uid']
52
- dynamic_settings['core__time_zone'] = form.cleaned_data[
53
- 'time_zone'
54
- ]
55
- dynamic_settings['core__units_of_measure'] = form.cleaned_data[
56
- 'units_of_measure'
57
- ]
58
- if not dynamic_settings['core__location_coordinates'] \
59
- and request.POST.get('location-guess'):
60
- dynamic_settings['core__location_coordinates'] = \
61
- request.POST['location-guess']
62
- if form.cleaned_data['cover_image']:
63
- dynamic_settings['core__cover_image'] = \
64
- form.cleaned_data['cover_image']
65
- dynamic_settings['core__cover_image_synced'] = False
66
- request.session['setup_wizard_step'] = 2
67
- return redirect(request.path)
68
- elif step == 2:
69
- initial = {
70
- 'location': dynamic_settings['core__location_coordinates'],
71
- 'share_location': dynamic_settings['core__share_location']
72
- }
73
- form = CoordinatesForm(initial=initial)
74
- if request.method == 'POST':
75
- form = CoordinatesForm(request.POST, initial=initial)
76
- if form.is_valid():
77
- dynamic_settings['core__location_coordinates'] = \
78
- form.cleaned_data['location']
79
- dynamic_settings['core__share_location'] = \
80
- form.cleaned_data['share_location']
81
- request.session['setup_wizard_step'] = 3
82
- return redirect(request.path)
83
- else:
84
- form = TermsAndConditionsForm()
85
- if request.method == 'POST':
86
- form = TermsAndConditionsForm(request.POST)
87
- if form.is_valid() and form.cleaned_data['accept']:
88
- request.session.pop('setup_wizard_step')
89
- messages.success(
90
- request, "Congratulations! "
91
- "Your Hub is now configured and restarting in the background. "
92
- "Will be fully ready in 30 seconds."
93
- )
94
- threading.Thread(target=supervisor_restart).start()
95
- return redirect(reverse('admin:index'))
96
-
97
- return render(request, 'setup_wizard/form.html', {'form': form, 'step': step})
98
-
99
-
100
17
  @login_required
101
18
  def update(request):
102
19
  if not request.user.is_superuser:
simo/fleet/controllers.py CHANGED
@@ -158,7 +158,7 @@ class BaseClimateSensor(FleeDeviceMixin, BasicSensorMixin, BaseMultiSensor):
158
158
  def __init__(self, *args, **kwargs):
159
159
  super().__init__(*args, **kwargs)
160
160
  self.sys_temp_units = 'C'
161
- if dynamic_settings['core__units_of_measure'] == 'imperial':
161
+ if self.component.zone.instance.units_of_measure == 'imperial':
162
162
  self.sys_temp_units = 'F'
163
163
 
164
164
  @property
@@ -0,0 +1,27 @@
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
+ ('fleet', '0036_auto_20240605_0702'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterModelOptions(
14
+ name='colonelpin',
15
+ options={'ordering': ('colonel', 'no')},
16
+ ),
17
+ migrations.AlterField(
18
+ model_name='colonelpin',
19
+ name='no',
20
+ field=models.PositiveIntegerField(db_index=True),
21
+ ),
22
+ migrations.AlterField(
23
+ model_name='interfaceaddress',
24
+ name='address_type',
25
+ field=models.CharField(choices=[('i2c', 'I2C'), ('dali-gear', 'DALI Gear'), ('dali-group', 'DALI Gear Group'), ('dali-device', 'DALI Control Device')], db_index=True, max_length=100),
26
+ ),
27
+ ]
@@ -223,7 +223,7 @@ class Thermostat(ControllerBase):
223
223
  def default_config(self):
224
224
  min = 3
225
225
  max = 36
226
- if dynamic_settings['core__units_of_measure'] == 'imperial':
226
+ if self.component.zone.instance.units_of_measure == 'imperial':
227
227
  min = 36
228
228
  max = 100
229
229
  return {
@@ -237,7 +237,7 @@ class Thermostat(ControllerBase):
237
237
  raise ValidationError("This component type does not accept set value!")
238
238
 
239
239
  def _get_default_user_config(self):
240
- if dynamic_settings['core__units_of_measure'] == 'imperial':
240
+ if self.component.zone.instance.units_of_measure == 'imperial':
241
241
  target_temp = 70
242
242
  low_target = 60
243
243
  high_target = 75
simo/generic/forms.py CHANGED
@@ -273,9 +273,6 @@ class ThermostatConfigForm(BaseComponentForm):
273
273
 
274
274
  def __init__(self, *args, **kwargs):
275
275
  super().__init__(*args, **kwargs)
276
- if dynamic_settings['core__units_of_measure'] == 'imperial':
277
- self.fields['min'].initial = 36
278
- self.fields['max'].initial = 100
279
276
  if self.instance.pk:
280
277
  self.fields['mode'].initial = \
281
278
  self.instance.config['user_config']['mode']
@@ -9,5 +9,5 @@
9
9
  position: relative;
10
10
  bottom: -4px;
11
11
  "></span>
12
- {{ obj.value.current.temp }}ᴼ {% if global_preferences.core__units_of_measure == 'metric' %}C{% else %}F{% endif %}
12
+ {{ obj.value.current.temp }}ᴼ {% if obj__zone__instance__units_of_measure == 'metric' %}C{% else %}F{% endif %}
13
13
  </div>
File without changes
@@ -4,7 +4,7 @@ server{
4
4
 
5
5
  charset utf-8;
6
6
 
7
- client_max_body_size 20M;
7
+ client_max_body_size 100M;
8
8
 
9
9
 
10
10
  location /protected/static {
@@ -55,7 +55,7 @@ server{
55
55
 
56
56
  ssl_session_cache shared:TLS:2m;
57
57
 
58
- client_max_body_size 20M;
58
+ client_max_body_size 100M;
59
59
 
60
60
 
61
61
  location /protected/static {
@@ -26,6 +26,9 @@ def perform_update():
26
26
  if proc.returncode:
27
27
  raise Exception(err.decode())
28
28
 
29
+ from simo.management.install import install_dependencies
30
+ install_dependencies()
31
+
29
32
  proc = subprocess.Popen(
30
33
  [os.path.join(HUB_DIR, 'manage.py'), 'migrate'],
31
34
  cwd=HUB_DIR,
@@ -1,6 +1,5 @@
1
- """Console script for simo."""
1
+ #!/usr/bin/env python
2
2
  import sys
3
- import click
4
3
  import os
5
4
  import shutil
6
5
  import simo
@@ -8,7 +7,7 @@ from django.template import Context, Engine
8
7
  from django.core.management.utils import get_random_secret_key
9
8
 
10
9
 
11
- def copy_default_template(to_directory='/etc/SIMO'):
10
+ def copy_template(to_directory='/etc/SIMO'):
12
11
  template_file_extensions = ['.py', '.conf']
13
12
 
14
13
  context = Context({
@@ -49,18 +48,6 @@ def copy_default_template(to_directory='/etc/SIMO'):
49
48
  os.makedirs(os.path.join(root, dirname), exist_ok=True)
50
49
 
51
50
 
52
- @click.command()
53
- def init():
54
- copy_default_template()
55
-
56
-
57
- @click.group()
58
- def main(args=None):
59
- pass
60
-
61
-
62
- main.add_command(init)
63
-
64
51
 
65
52
  if __name__ == "__main__":
66
- sys.exit(main()) # pragma: no cover
53
+ sys.exit(copy_template())
@@ -0,0 +1,258 @@
1
+ #!/usr/bin/env python
2
+
3
+ """
4
+ This should be called after simo has just been installed.
5
+ It prepares and powers up SIMO.io hub.
6
+ However it is not guaranteed to be fully operational.
7
+ """
8
+ import os, sys, json, subprocess, socket, shutil, traceback
9
+ import simo
10
+ from django.template import Context, Engine
11
+ from django.core.management.utils import get_random_secret_key
12
+
13
+
14
+
15
+ def install_dependencies():
16
+
17
+ status = subprocess.call(
18
+ 'apt install postgresql libpq-dev postgresql-client '
19
+ 'postgresql-client-common python3-pip redis-server supervisor '
20
+ 'mosquitto libopenjp2-7 libtiff5 pkg-config libcairo2-dev '
21
+ 'libgirepository1.0-dev libcairo2 libudev-dev gdal-bin net-tools '
22
+ 'timeshift nginx postgis openvpn ffmpeg libsm6 libxext6 ssh keychain -y',
23
+ shell=True
24
+ )
25
+ if status != 0:
26
+ print("Unable install required packages.")
27
+ return
28
+
29
+ return True
30
+
31
+
32
+ def get_ip():
33
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
34
+ try:
35
+ # doesn't even have to be reachable
36
+ s.connect(('10.255.255.255', 1))
37
+ IP = s.getsockname()[0]
38
+ except Exception:
39
+ IP = '127.0.0.1'
40
+ finally:
41
+ s.close()
42
+ return IP
43
+
44
+
45
+ def copy_template(to_directory='/etc/SIMO'):
46
+ template_file_extensions = ['.py', '.conf']
47
+
48
+ context = Context({
49
+ 'secret_key': get_random_secret_key(),
50
+ 'project_dir': to_directory,
51
+ 'base_dir': to_directory
52
+ }, autoescape=False)
53
+ template_dir = os.path.join(
54
+ os.path.dirname(simo.__file__), '_hub_template'
55
+ )
56
+ prefix_length = len(template_dir) + 1
57
+ for root, dirs, files in os.walk(template_dir):
58
+ relative_dir = root[prefix_length:]
59
+ target_dir = os.path.join(to_directory, relative_dir)
60
+ os.makedirs(target_dir, exist_ok=True)
61
+ for filename in files:
62
+ if filename.endswith(('.pyo', '.pyc', '.py.class')):
63
+ # Ignore some files as they cause various breakages.
64
+ continue
65
+ old_path = os.path.join(root, filename)
66
+ new_path = os.path.join(target_dir, filename)
67
+ os.makedirs(target_dir, exist_ok=True)
68
+ fn, file_extension = os.path.splitext(new_path)
69
+ if file_extension in template_file_extensions:
70
+ with open(old_path, encoding='utf-8') as template_file:
71
+ content = template_file.read()
72
+ template = Engine().from_string(content)
73
+ content = template.render(context)
74
+ with open(new_path, 'w', encoding='utf-8') as new_file:
75
+ new_file.write(content)
76
+ else:
77
+ shutil.copyfile(old_path, new_path)
78
+ shutil.copymode(old_path, new_path)
79
+ for dirname in dirs[:]:
80
+ if dirname.startswith('.') or dirname == '__pycache__':
81
+ dirs.remove(dirname)
82
+ else:
83
+ os.makedirs(os.path.join(root, dirname), exist_ok=True)
84
+
85
+
86
+ def install():
87
+ simo_directory = '/etc/SIMO'
88
+ installed_flag_file_path = os.path.join(simo_directory, 'is_installed.json')
89
+
90
+ if os.path.exists(installed_flag_file_path):
91
+ print("SIMO.io hub is already installed. ")
92
+ print(f"Please delete {installed_flag_file_path} file manually if you want to force this.")
93
+ return
94
+
95
+ step = 1
96
+ print(f"{step}.___________Install dependencies__________________")
97
+ status = subprocess.call(
98
+ 'apt-add-repository ppa:mosquitto-dev/mosquitto-ppa -y', shell=True
99
+ )
100
+ if status != 0:
101
+ print("Unable to add mosquitto-dev dependency")
102
+ print("Installation failed!")
103
+ return
104
+
105
+ status = subprocess.call('apt-get update -y', shell=True)
106
+ if status != 0:
107
+ print("Unable to apt-update")
108
+ print("Installation failed!")
109
+ return
110
+ success = install_dependencies()
111
+ if not success:
112
+ print("Installation failed!")
113
+ return
114
+
115
+ step += 1
116
+ print(f"{step}.___________Copy default template__________________")
117
+
118
+ if os.path.exists(simo_directory):
119
+ print("Already exists!")
120
+ else:
121
+ try:
122
+ copy_template(simo_directory)
123
+ except Exception as e:
124
+ print(traceback.format_exc(), file=sys.stderr)
125
+ shutil.rmtree(simo_directory, ignore_errors=True)
126
+ return
127
+
128
+ step += 1
129
+ print(f"{step}.___________Configure supervisor__________________")
130
+
131
+ try:
132
+ os.remove('/etc/supervisor/conf.d/SIMO.conf', ignore_errors=True)
133
+ except:
134
+ pass
135
+ os.symlink(
136
+ f'{simo_directory}/hub/supervisor.conf',
137
+ '/etc/supervisor/conf.d/SIMO.conf'
138
+ )
139
+ status = subprocess.call(['supervisorctl', 'update', 'all'])
140
+ if status != 0:
141
+ sys.exit("INSTALLATION FAILED! Unable to start supervisord")
142
+
143
+
144
+
145
+ step += 1
146
+ print("%d._____________ Configure NGINX _________________________" % step)
147
+
148
+ try:
149
+ os.remove('/etc/nginx/sites-enabled/default')
150
+ except:
151
+ pass
152
+
153
+ os.symlink(
154
+ '/etc/SIMO/hub/nginx.conf', '/etc/nginx/sites-enabled/SIMO'
155
+ )
156
+
157
+ try:
158
+ os.remove('/etc/ssl/private/localhost.key')
159
+ except:
160
+ pass
161
+ try:
162
+ os.remove('/etc/ssl/certs/localhost.crt')
163
+ except:
164
+ pass
165
+
166
+ status = subprocess.call([
167
+ 'openssl', 'req', '-x509', '-nodes', '-days', '36500',
168
+ '-newkey', 'rsa:2048',
169
+ '-subj', '/C=US/ST=Denial/L=Springfield/O=Dis/CN=simo.io',
170
+ '-keyout', '/etc/ssl/private/localhost.key',
171
+ '-out', '/etc/ssl/certs/localhost.crt'
172
+ ])
173
+ if status != 0:
174
+ sys.exit(
175
+ "INSTALLATION FAILED! Unable to prepare self signed certificate.")
176
+
177
+ status = subprocess.call(['service', 'nginx', 'reload'])
178
+ if status != 0:
179
+ sys.exit("INSTALLATION FAILED! Something is wrong witn NGINX conf.")
180
+
181
+ step += 1
182
+ print("%d._____________ Configure SSH and Firewall_____________" % step)
183
+ new_ssh_conf = ''
184
+ with open('/etc/ssh/sshd_config', 'r') as ssh_conf:
185
+ line = ssh_conf.readline()
186
+ while line:
187
+ if 'PasswordAuthentication' in line:
188
+ new_ssh_conf += 'PasswordAuthentication no'
189
+ else:
190
+ new_ssh_conf += line
191
+ line = ssh_conf.readline()
192
+ with open('/etc/ssh/sshd_config', 'w') as ssh_conf:
193
+ ssh_conf.write(new_ssh_conf)
194
+
195
+ status = subprocess.call(['service', 'ssh', 'restart'])
196
+ if status != 0:
197
+ sys.exit("INSTALLATION FAILED! Unable to restart SSH")
198
+
199
+ stats = []
200
+ stats.append(subprocess.call(['ufw', 'allow', 'ssh']))
201
+ stats.append(subprocess.call(['ufw', 'allow', 'http']))
202
+ stats.append(subprocess.call(['ufw', 'allow', 'https']))
203
+ stats.append(subprocess.call(['ufw', 'allow', '1194']))
204
+ stats.append(subprocess.call(['ufw', 'allow', '1883']))
205
+ if any(stats):
206
+ sys.exit("INSTALLATION FAILED! Unable to update UFW rules")
207
+
208
+ status = subprocess.call('echo y | ufw enable', shell=True)
209
+ if status != 0:
210
+ sys.exit("INSTALLATION FAILED! Unable to enable UFW")
211
+
212
+
213
+ step += 1
214
+ print("%d.__________ CONFIGURE TIMESHIFT _____________________" % step)
215
+
216
+ default_timeshift_file_path = '/etc/timeshift/default.json'
217
+ if not os.path.exists(default_timeshift_file_path):
218
+ default_timeshift_file_path = '/etc/timeshift/timeshift.json'
219
+ if not os.path.exists(default_timeshift_file_path):
220
+ default_timeshift_file_path = '/etc/default/timeshift.json'
221
+
222
+ if not os.path.exists(default_timeshift_file_path):
223
+ print("Unable to find default TimeShift config! Skip TimeShift configuration.")
224
+
225
+ else:
226
+
227
+ with open(default_timeshift_file_path, 'r') as conf_f:
228
+ timeshift_conf = json.loads(conf_f.read())
229
+
230
+ timeshift_conf['backup_device_uuid'] = subprocess.check_output(
231
+ "lsblk -no UUID $(df -P /etc/SIMO/hub/settings.py | awk 'END{print $1}')",
232
+ shell=True
233
+ ).decode()[:-1]
234
+ timeshift_conf['schedule_monthly'] = "true"
235
+ timeshift_conf['schedule_weekly'] = "true"
236
+ timeshift_conf['schedule_daily'] = "true"
237
+
238
+ # Must be copied to /etc/timeshift.json to work
239
+ with open('/etc/timeshift.json', 'w') as conf_f:
240
+ conf_f.write(json.dumps(timeshift_conf))
241
+
242
+ status = subprocess.call([
243
+ '/usr/bin/timeshift', '--create',
244
+ '--comments', '"Initial backup"', '--tags', 'M'
245
+ ])
246
+ if status != 0:
247
+ print("Unable to start TimeShift")
248
+
249
+ with open(installed_flag_file_path, 'w') as f:
250
+ f.write(json.dumps(True))
251
+
252
+ print("--------------------------------------------------------------")
253
+ print("DONE!")
254
+ print("Your SIMO.io Hub is Up and running at: https://%s/" % get_ip())
255
+
256
+
257
+ if __name__ == "__main__":
258
+ sys.exit(init())
@@ -2,6 +2,7 @@ import os
2
2
  import pwd
3
3
  import grp
4
4
  import subprocess
5
+ import pkg_resources
5
6
  from django.conf import settings
6
7
  from django.template.loader import render_to_string
7
8
 
@@ -58,7 +59,8 @@ prepare_mosquitto()
58
59
  def update_auto_update():
59
60
  import simo
60
61
  auto_update_file_path = os.path.join(
61
- os.path.dirname(simo.__file__), 'auto_update.py'
62
+ os.path.dirname(simo.__file__), 'management',
63
+ 'auto_update.py'
62
64
  )
63
65
  st = os.stat(auto_update_file_path)
64
66
  os.chmod(auto_update_file_path, st.st_mode | 0o111)
@@ -75,4 +77,22 @@ def update_auto_update():
75
77
  cron_out.communicate(input=str.encode(auto_update_cron))
76
78
 
77
79
 
78
- update_auto_update()
80
+ update_auto_update()
81
+
82
+
83
+ def maybe_update_to_latest_immediately():
84
+ from simo.core.tasks import update_latest_version_available, update
85
+ from simo.core.models import Instance
86
+ from simo.conf import dynamic_settings
87
+ update_latest_version_available()
88
+ if dynamic_settings['core__latest_version_available'] != \
89
+ pkg_resources.get_distribution('simo').version:
90
+ print("There is newer version, we should probably update!")
91
+ if not Instance.objects.all().count():
92
+ print("Yes let's do it asynchronously!")
93
+ return update.s()
94
+ print("Nope, we already have some instances running, "
95
+ "so we leave that for hub owners.")
96
+
97
+
98
+ maybe_update_to_latest_immediately()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: simo
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: Smart Home on Steroids!
5
5
  Author-email: Simanas Venčkauskas <simanas@simo.io>
6
6
  Project-URL: Homepage, https://simo.io