diff --git a/api/__init__.py b/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/admin.py b/api/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/api/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/api/apps.py b/api/apps.py new file mode 100644 index 0000000..66656fd --- /dev/null +++ b/api/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ApiConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'api' diff --git a/api/mikrotik/api.py b/api/mikrotik/api.py new file mode 100644 index 0000000..85a77e9 --- /dev/null +++ b/api/mikrotik/api.py @@ -0,0 +1,5 @@ +import ros_api + +router = ros_api.Api('192.168.10.1', user='server', password='Cbvgcjy0V', port='8728') +r = router.talk('/interface/wireless/cap/print') +print(r[0]['enabled']) \ No newline at end of file diff --git a/api/mikrotik/total.py b/api/mikrotik/total.py new file mode 100644 index 0000000..9efc179 --- /dev/null +++ b/api/mikrotik/total.py @@ -0,0 +1,15 @@ +import netmiko +from netmiko import ConnectHandler + +mikrotik_router_1 = { +'device_type': 'mikrotik_routeros', +'host': '192.168.10.1', +'port': '2202', +'username': 'server', +'password': 'Cbvgcjy0V' +} + +sshCli = ConnectHandler(**mikrotik_router_1) +print(sshCli.find_prompt()) +output = sshCli.send_command("/interface ethernet print") +print(output) \ No newline at end of file diff --git a/api/models.py b/api/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/api/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/api/serializers.py b/api/serializers.py new file mode 100644 index 0000000..4c35757 --- /dev/null +++ b/api/serializers.py @@ -0,0 +1,15 @@ +from rest_framework import serializers +from django.contrib.auth import get_user_model +User = get_user_model() +from upanel.models import Device,Hostname + +class DeviceSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = Device + fields = ('id','name', 'device_id','device_ip','local_key','device_version','customuser_id') + + +class UserSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = User + fields = ('id','username','email','ipaddress') \ No newline at end of file diff --git a/api/tests.py b/api/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/api/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/api/tuya/__init__.py b/api/tuya/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/api/tuya/connect.py b/api/tuya/connect.py new file mode 100644 index 0000000..ec98c06 --- /dev/null +++ b/api/tuya/connect.py @@ -0,0 +1,32 @@ +import psycopg2 +import tinytuya +import json +from tuya_connector import TuyaOpenAPI + +tuya_file='tuya_con.json' + +def connect_base(): + conn = psycopg2.connect(host='172.17.0.1', + port='54322', + user='miroca', + password='cbvgcjy0', + database='miroca') + return conn + + +def connect_local(): + device=tinytuya.deviceScan() + return device + +def connect_tuya(): + ACCESS_ID = "pardyqsmdjetwdxjcaxa" + ACCESS_KEY = "4f81a40a36f349dc9ad11d53d74cb34b" + API_ENDPOINT = "https://openapi.tuyaeu.com" + + try: + openapi = TuyaOpenAPI(API_ENDPOINT, ACCESS_ID, ACCESS_KEY) + openapi.connect() + return openapi + except Exception as e: + with open(tuya_file, 'w', encoding='utf-8') as f: + json.dump([{'error': str(e)}], f,ensure_ascii=False, indent=4) \ No newline at end of file diff --git a/api/tuya/device.py b/api/tuya/device.py new file mode 100644 index 0000000..35a1c6d --- /dev/null +++ b/api/tuya/device.py @@ -0,0 +1,85 @@ +from itertools import chain +from psycopg2.extras import execute_values +from connect import * +import json + +file_psycopd = 'psycopd_con.json' +file_local = 'local_device.json' +file_tuya = 'tuya_con.json' + + +class DeviceError(Exception): + pass + + +def update_base(): + conn = connect_base() + cursor = conn.cursor() + count = 0 + try: + with open(file_tuya, 'r', encoding='utf-8') as f: + tuya_devices = json.load(f) + if tuya_devices['error']: + raise DeviceError(tuya_devices['error']) + except Exception as e: + print(f'Ошибка update_base: {e}') + with open(file_local, 'r', encoding='utf-8') as f: + local_devices = json.load(f) + for device in tuya_devices: + if device['result']['id'] == [v for v in local_devices.values()][count]['gwId']: + cursor.execute("UPDATE upanel_device SET local_key = %s, name = %s " + "WHERE device_id= %s", + (device['result']['local_key'], + device['result']['name'], + device['result']['id'])) + conn.commit() + count += 1 + cursor.close() + + +def add_base(): + conn = connect_base() + cursor = conn.cursor() + try: + with open(file_local, 'r', encoding='utf-8') as f: + local_devices_dict = json.load(f) + local_devices = {i + 1: v for i, v in enumerate(local_devices_dict.values())} + cursor.execute("SELECT * FROM upanel_device") + devices = list(chain.from_iterable(cursor.fetchall())) + execute_values(cursor, "INSERT INTO upanel_device " + "(device_ip, device_id,device_version) " + "VALUES %s", + [(local_devices_dict[local_devices[device]['ip']]['ip'], + local_devices_dict[local_devices[device]['ip']]['gwId'], + local_devices_dict[local_devices[device]['ip']]['version'],) + for device in local_devices if local_devices[device]['ip'] not in devices]) + conn.commit() + cursor.close() + except Exception as e: + print(f'ошибка add_base: {e}') + with open('error_add.json', 'w', encoding='utf-8') as f: + json.dump({'Error_add':e}, f) + +def delete_base(): + conn = connect_base() + cursor = conn.cursor() + try: + with open(file_local, 'r', encoding='utf-8') as f: + local_devices_dict = json.load(f) + ip_file=sorted([v for k,v in enumerate(local_devices_dict)]) + cursor.execute('SELECT * FROM upanel_device') + cursor.executemany("DELETE FROM upanel_device WHERE device_ip=%s", + [tuple(str(item) for item in ip[2].split()) + for ip in cursor.fetchall() + if ip[2] not in ip_file]) + conn.commit() + + except Exception as e: + print(f'Ошибка delete_base: {e}') + with open('error_delete.json', 'w', encoding='utf-8') as f: + json.dump({'Error_delete':e}, f) + +if __name__ == '__main__': + add_base() + update_base() + delete_base() diff --git a/api/tuya/input_file.py b/api/tuya/input_file.py new file mode 100644 index 0000000..d32b778 --- /dev/null +++ b/api/tuya/input_file.py @@ -0,0 +1,44 @@ +from connect import * + +psycopd_file = 'psycopd_con.json' +local_file = 'local_device.json' +tuya_file='tuya_con.json' + +def file_base(): + try: + cursor=connect_base().cursor() + cursor.execute('SELECT * FROM upanel_device') + except Exception as e: + with open(psycopd_file, 'w', encoding='utf-8') as f: + json.dump({'error': str(e)}, f) + else: + with open(psycopd_file, 'w', encoding='utf-8') as f: + json.dump({v[0]:v[1:] for k,v in enumerate(cursor.fetchall())}, f, + ensure_ascii=False, indent=4) + + + +def file_local(): + try: + with open(local_file, 'w', encoding='utf-8') as f: + json.dump(connect_local(), f, ensure_ascii=False, indent=4) + except Exception as e: + with open(local_file, 'w', encoding='utf-8') as f: + json.dump({'error': str(e)}, f) + + +def file_tuya(): + try: + with open(local_file, 'r', encoding='utf-8') as f: + local_device = json.load(f) + with open(tuya_file, 'w', encoding='utf-8') as f: + json.dump([connect_tuya().get(f"/v1.0/iot-03/devices/{local_device[dev]['id']}") + for dev in local_device],f,ensure_ascii=False, indent=4) + except Exception as e: + with open(tuya_file, 'w', encoding='utf-8') as f: + json.dump({'error': str(e)}, f) + +if __name__ == '__main__': + file_base() + file_local() + file_tuya() \ No newline at end of file diff --git a/api/urls.py b/api/urls.py new file mode 100644 index 0000000..b6fd8f8 --- /dev/null +++ b/api/urls.py @@ -0,0 +1,12 @@ +from django.urls import path,include +from rest_framework.routers import DefaultRouter +from api import views + +router = DefaultRouter() +router.register(r'devices', views.DeviceViewSet) +router.register(r'user', views.UserViewSet) + +urlpatterns = [ + path('', include((router.urls, 'app_name'), namespace='api/device')), + path('', include((router.urls, 'app_name'), namespace='api/user')), +] diff --git a/api/views.py b/api/views.py new file mode 100644 index 0000000..6b6e41c --- /dev/null +++ b/api/views.py @@ -0,0 +1,24 @@ +from django.shortcuts import render +from django.contrib.auth import get_user_model +User = get_user_model() +from rest_framework import viewsets +from api.serializers import DeviceSerializer,UserSerializer +from upanel.models import * + +from rest_framework.authentication import BasicAuthentication +from rest_framework.permissions import IsAuthenticated + + + +class DeviceViewSet(viewsets.ModelViewSet): + authentication_classes = (BasicAuthentication,) + permission_classes = (IsAuthenticated,) + queryset = Device.objects.values().order_by('id') + serializer_class = DeviceSerializer + + +class UserViewSet(viewsets.ModelViewSet): + authentication_classes = (BasicAuthentication,) + permission_classes = (IsAuthenticated,) + queryset = User.objects.values().order_by('id') + serializer_class = UserSerializer diff --git a/auth_miroca/__init__.py b/auth_miroca/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/auth_miroca/admin.py b/auth_miroca/admin.py new file mode 100644 index 0000000..55b90f2 --- /dev/null +++ b/auth_miroca/admin.py @@ -0,0 +1,26 @@ +from django.contrib import admin +from django.contrib.auth.admin import UserAdmin +from .forms import CustomUserCreationForm, CustomUserChangeForm +from .models import CustomUser + +class CustomUserAdmin(UserAdmin): + add_form = CustomUserCreationForm + form = CustomUserChangeForm + model = CustomUser + list_display = ('email', 'is_superuser','is_staff', 'is_active',) + list_filter = ('email','is_superuser','is_staff', 'is_active',) + fieldsets = ( + (None, {'fields': ('email', 'password')}), + ('Разрешения', {'fields': ('is_superuser','is_staff','is_active','groups',)}), + ('Пользователь',{'fields':('username','last_name','first_name','phone','org','address')}), + ('Регистрация и вход', {'fields': ('date_joined','last_login')}), + ) + add_fieldsets = ( + (None, { + 'classes': ('wide',), + 'fields': ('email','is_superuser', 'password1', 'password2', 'is_staff', 'is_active')} + ), + ) + search_fields = ('email',) + ordering = ('email',) +admin.site.register(CustomUser, CustomUserAdmin) \ No newline at end of file diff --git a/auth_miroca/apps.py b/auth_miroca/apps.py new file mode 100644 index 0000000..cf3c29e --- /dev/null +++ b/auth_miroca/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class AuthMirocaConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'auth_miroca' diff --git a/auth_miroca/forms.py b/auth_miroca/forms.py new file mode 100644 index 0000000..15cf555 --- /dev/null +++ b/auth_miroca/forms.py @@ -0,0 +1,10 @@ +from django.contrib.auth.forms import UserCreationForm, UserChangeForm +from .models import CustomUser +class CustomUserCreationForm(UserCreationForm): + class Meta(UserCreationForm): + model = CustomUser + fields = ('email',) +class CustomUserChangeForm(UserChangeForm): + class Meta: + model = CustomUser + fields = ('email',) \ No newline at end of file diff --git a/auth_miroca/logout.py b/auth_miroca/logout.py new file mode 100644 index 0000000..a6acdfd --- /dev/null +++ b/auth_miroca/logout.py @@ -0,0 +1,9 @@ +from django.shortcuts import render, redirect + +def logout_view(func): + def wrapper(request): + if request.method == 'POST': + request.session.clear() + return redirect ('login') + return func(request) + return wrapper \ No newline at end of file diff --git a/auth_miroca/managers.py b/auth_miroca/managers.py new file mode 100644 index 0000000..2e2ffd5 --- /dev/null +++ b/auth_miroca/managers.py @@ -0,0 +1,34 @@ +from django.contrib.auth.base_user import BaseUserManager +from django.utils.translation import gettext_lazy as _ + + +class CustomUserManager(BaseUserManager): + """ + Custom user model manager where email is the unique identifiers + for authentication instead of usernames. + """ + def create_user(self, email, password, **extra_fields): + """ + Create and save a user with the given email and password. + """ + if not email: + raise ValueError(_("The Email must be set")) + email = self.normalize_email(email) + user = self.model(email=email, **extra_fields) + user.set_password(password) + user.save() + return user + + def create_superuser(self, email, password, **extra_fields): + """ + Create and save a SuperUser with the given email and password. + """ + extra_fields.setdefault("is_staff", True) + extra_fields.setdefault("is_superuser", True) + extra_fields.setdefault("is_active", True) + + if extra_fields.get("is_staff") is not True: + raise ValueError(_("Superuser must have is_staff=True.")) + if extra_fields.get("is_superuser") is not True: + raise ValueError(_("Superuser must have is_superuser=True.")) + return self.create_user(email, password, **extra_fields) \ No newline at end of file diff --git a/auth_miroca/migrations/0001_initial.py b/auth_miroca/migrations/0001_initial.py new file mode 100644 index 0000000..dcb8a5e --- /dev/null +++ b/auth_miroca/migrations/0001_initial.py @@ -0,0 +1,37 @@ +# Generated by Django 5.1.3 on 2024-11-29 06:19 + +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='CustomUser', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('email', models.EmailField(max_length=254, unique=True, verbose_name='email address')), + ('is_staff', models.BooleanField(default=False, verbose_name='Персонал')), + ('is_active', models.BooleanField(default=True, verbose_name='Активный')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('username', models.CharField(max_length=255, null=True, verbose_name='username')), + ('image', models.ImageField(blank=True, null=True, upload_to='user/%Y/%m/%d', verbose_name='Изображение')), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), + ], + options={ + 'verbose_name': ('user',), + 'verbose_name_plural': 'Пользователь', + }, + ), + ] diff --git a/auth_miroca/migrations/0002_customuser_ipaddress.py b/auth_miroca/migrations/0002_customuser_ipaddress.py new file mode 100644 index 0000000..44bed51 --- /dev/null +++ b/auth_miroca/migrations/0002_customuser_ipaddress.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.3 on 2024-11-29 12:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('auth_miroca', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='customuser', + name='ipaddress', + field=models.GenericIPAddressField(null=True), + ), + ] diff --git a/auth_miroca/migrations/0003_customuser_last_name_customuser_phone.py b/auth_miroca/migrations/0003_customuser_last_name_customuser_phone.py new file mode 100644 index 0000000..49b4b9a --- /dev/null +++ b/auth_miroca/migrations/0003_customuser_last_name_customuser_phone.py @@ -0,0 +1,24 @@ +# Generated by Django 5.1.3 on 2024-11-29 12:53 + +import phone_field.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('auth_miroca', '0002_customuser_ipaddress'), + ] + + operations = [ + migrations.AddField( + model_name='customuser', + name='last_name', + field=models.CharField(max_length=255, null=True, verbose_name='Фамилия'), + ), + migrations.AddField( + model_name='customuser', + name='phone', + field=phone_field.models.PhoneField(blank=True, help_text='Контактный номер', max_length=31, null=True), + ), + ] diff --git a/auth_miroca/migrations/0004_customuser_address_customuser_first_name.py b/auth_miroca/migrations/0004_customuser_address_customuser_first_name.py new file mode 100644 index 0000000..a29e12f --- /dev/null +++ b/auth_miroca/migrations/0004_customuser_address_customuser_first_name.py @@ -0,0 +1,26 @@ +# Generated by Django 5.1.3 on 2024-11-29 13:00 + +import address.models +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('address', '0003_auto_20200830_1851'), + ('auth_miroca', '0003_customuser_last_name_customuser_phone'), + ] + + operations = [ + migrations.AddField( + model_name='customuser', + name='address', + field=address.models.AddressField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='address.address', verbose_name='Адрес'), + ), + migrations.AddField( + model_name='customuser', + name='first_name', + field=models.CharField(max_length=255, null=True, verbose_name='Имя'), + ), + ] diff --git a/auth_miroca/migrations/0005_alter_customuser_address.py b/auth_miroca/migrations/0005_alter_customuser_address.py new file mode 100644 index 0000000..d7ef8eb --- /dev/null +++ b/auth_miroca/migrations/0005_alter_customuser_address.py @@ -0,0 +1,21 @@ +# Generated by Django 5.1.3 on 2024-11-29 13:02 + +import address.models +import django.db.models.deletion +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('address', '0003_auto_20200830_1851'), + ('auth_miroca', '0004_customuser_address_customuser_first_name'), + ] + + operations = [ + migrations.AlterField( + model_name='customuser', + name='address', + field=address.models.AddressField(null=True, on_delete=django.db.models.deletion.SET_NULL, to='address.address', verbose_name='Адрес'), + ), + ] diff --git a/auth_miroca/migrations/0006_remove_customuser_address.py b/auth_miroca/migrations/0006_remove_customuser_address.py new file mode 100644 index 0000000..0c6d8cd --- /dev/null +++ b/auth_miroca/migrations/0006_remove_customuser_address.py @@ -0,0 +1,17 @@ +# Generated by Django 5.1.3 on 2024-11-29 13:05 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('auth_miroca', '0005_alter_customuser_address'), + ] + + operations = [ + migrations.RemoveField( + model_name='customuser', + name='address', + ), + ] diff --git a/auth_miroca/migrations/0007_customuser_ipaddress_mikrotik.py b/auth_miroca/migrations/0007_customuser_ipaddress_mikrotik.py new file mode 100644 index 0000000..91ef599 --- /dev/null +++ b/auth_miroca/migrations/0007_customuser_ipaddress_mikrotik.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.3 on 2024-11-29 16:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('auth_miroca', '0006_remove_customuser_address'), + ] + + operations = [ + migrations.AddField( + model_name='customuser', + name='ipaddress_mikrotik', + field=models.GenericIPAddressField(null=True), + ), + ] diff --git a/auth_miroca/migrations/0008_remove_customuser_ipaddress_mikrotik.py b/auth_miroca/migrations/0008_remove_customuser_ipaddress_mikrotik.py new file mode 100644 index 0000000..8a0b184 --- /dev/null +++ b/auth_miroca/migrations/0008_remove_customuser_ipaddress_mikrotik.py @@ -0,0 +1,17 @@ +# Generated by Django 5.1.3 on 2024-11-29 17:28 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('auth_miroca', '0007_customuser_ipaddress_mikrotik'), + ] + + operations = [ + migrations.RemoveField( + model_name='customuser', + name='ipaddress_mikrotik', + ), + ] diff --git a/auth_miroca/migrations/__init__.py b/auth_miroca/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/auth_miroca/models.py b/auth_miroca/models.py new file mode 100644 index 0000000..305f5a7 --- /dev/null +++ b/auth_miroca/models.py @@ -0,0 +1,33 @@ +from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin +from django.db import models +from django.utils import timezone +from django.utils.translation import gettext_lazy as _ +from phone_field import PhoneField +from address.models import AddressField + +from .managers import CustomUserManager + + +class CustomUser(AbstractBaseUser, PermissionsMixin): + email = models.EmailField(_("email address"), unique=True) + is_staff = models.BooleanField(default=False, verbose_name='Персонал') + is_active = models.BooleanField(default=True, verbose_name='Активный') + date_joined = models.DateTimeField(_('date joined'), default=timezone.now) + username = models.CharField(max_length=255, null=True, verbose_name='username') + image = models.ImageField(upload_to='user/%Y/%m/%d', blank=True, null=True, verbose_name='Изображение') + ipaddress=models.GenericIPAddressField(null=True) + last_name=models.CharField(max_length=255, null=True,verbose_name='Фамилия') + first_name=models.CharField(max_length=255,null=True,verbose_name='Имя') + phone = PhoneField(blank=True,null=True, help_text='Контактный номер') + + USERNAME_FIELD = 'email' + REQUIRED_FIELDS = [] + + objects = CustomUserManager() + + def __str__(self): + return self.email + + class Meta: + verbose_name = 'user', + verbose_name_plural = 'Пользователь' diff --git a/auth_miroca/tests.py b/auth_miroca/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/auth_miroca/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/auth_miroca/views.py b/auth_miroca/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/auth_miroca/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/home/__init__.py b/home/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/home/admin.py b/home/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/home/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/home/apps.py b/home/apps.py new file mode 100644 index 0000000..e5ea0af --- /dev/null +++ b/home/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class HomeConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'home' diff --git a/home/models.py b/home/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/home/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/home/tests.py b/home/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/home/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/home/views.py b/home/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/home/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/kiosk/__init__.py b/kiosk/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/kiosk/admin.py b/kiosk/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/kiosk/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/kiosk/models.py b/kiosk/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/kiosk/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/kiosk/templates/kiosk/base/base.html b/kiosk/templates/kiosk/base/base.html new file mode 100644 index 0000000..b68b739 --- /dev/null +++ b/kiosk/templates/kiosk/base/base.html @@ -0,0 +1,69 @@ + +{% load static %} + + + + + + Панель управления Умный дом Мирока + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +{% block content %} + +{% endblock %} + \ No newline at end of file diff --git a/kiosk/templates/kiosk/content/index.html b/kiosk/templates/kiosk/content/index.html new file mode 100644 index 0000000..2803028 --- /dev/null +++ b/kiosk/templates/kiosk/content/index.html @@ -0,0 +1,49 @@ +{% extends 'kiosk/base/base.html' %} +{% load static %} +{% block content %} + + +
+
+ +
+ + {% include 'kiosk/include/content_wrapper.html' %} + +
+ +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +{% endblock content %} + diff --git a/kiosk/templates/kiosk/content/smarthome.html b/kiosk/templates/kiosk/content/smarthome.html new file mode 100644 index 0000000..dcbda1f --- /dev/null +++ b/kiosk/templates/kiosk/content/smarthome.html @@ -0,0 +1,39 @@ +{% extends 'kiosk/base/base.html' %} +{% load static %} +{#Блоки температуры и влажности#} +{% block styleinclude %} + +{% endblock styleinclude %} +{#График#} +{% block js %} + + + + +{% endblock js %} +{% block content %} + +
+
+
+ {% include 'upanel/upanel/base/label.html' %} +

+ {% include 'upanel/upanel/base/user.html' %} +
+ {% include 'upanel/upanel/base/status.html' %} + +
+ + +
+
+
+ + + +{% endblock content %} \ No newline at end of file diff --git a/kiosk/templates/kiosk/include/content_wrapper.html b/kiosk/templates/kiosk/include/content_wrapper.html new file mode 100644 index 0000000..d3ed71d --- /dev/null +++ b/kiosk/templates/kiosk/include/content_wrapper.html @@ -0,0 +1,31 @@ +{% load static %} +
+ + +
+ +
+ + {% include 'kiosk/include/sidebar.html' %} + + +
+ + {% include 'kiosk/include/greenhouse.html' %} + +
+
+
+ + {% include 'kiosk/include/transaction.html' %} + + + + {% include 'kiosk/include/transaction.html' %} + +
+
+ + +
+
\ No newline at end of file diff --git a/kiosk/templates/kiosk/include/greenhouse.html b/kiosk/templates/kiosk/include/greenhouse.html new file mode 100644 index 0000000..8768f46 --- /dev/null +++ b/kiosk/templates/kiosk/include/greenhouse.html @@ -0,0 +1,49 @@ +
+
+
+
Показания датчиков
+
+
+
+
+
+ +
+
+
+
Показания датчиков теплицы
+ +
+
+
+ +
+
+ Температура + {% if temp != 0 %} +
+{{ temp }}°C
+ {% elif temp == 0 %} +
+{{ temp }}°C
+ {% else %} +
-{{ temp }}°C
+ {% endif %} +
+
+
+
+ +
+
+ Влаж. почвы +
{{ damp }}%
+
+
+
+
+
+
diff --git a/kiosk/templates/kiosk/include/orderstatic.html b/kiosk/templates/kiosk/include/orderstatic.html new file mode 100644 index 0000000..beaa289 --- /dev/null +++ b/kiosk/templates/kiosk/include/orderstatic.html @@ -0,0 +1,100 @@ +{% load static %} +
+
+
+
+
Order Statistics
+ 42.82k Total Sales +
+ +
+
+
+
+

8,258

+ Total Orders +
+
+
+
    +
  • +
    + +
    +
    +
    +
    Electronic
    + Mobile, Earbuds, TV +
    +
    + 82.5k +
    +
    +
  • +
  • +
    + +
    +
    +
    +
    Fashion
    + T-shirt, Jeans, Shoes +
    +
    + 23.8k +
    +
    +
  • +
  • +
    + +
    +
    +
    +
    Decor
    + Fine Art, Dining +
    +
    + 849k +
    +
    +
  • +
  • +
    + +
    +
    +
    +
    Sports
    + Football, Cricket Kit +
    +
    + 99 +
    +
    +
  • +
+
+
+
\ No newline at end of file diff --git a/kiosk/templates/kiosk/include/sidebar.html b/kiosk/templates/kiosk/include/sidebar.html new file mode 100644 index 0000000..05a87d0 --- /dev/null +++ b/kiosk/templates/kiosk/include/sidebar.html @@ -0,0 +1,151 @@ +{% load static %} +
+
+
+
+
+
+
+ chart success +
+
+ Сервер +

+
+
+
+ +
+
+
+
+
+ temper +
+
+ Интернет +

+
+
+
+ +
+
+ +
+
+
+ wet +
+
+ Влажность +

50%

+
+
+
+ +
+
+
+
+
+ Credit Card +
+
+ Блок +

$147

+
+
+
+ +
+
+
+
+
+ wet +
+
+ Влажность +

125%

+
+
+
+ +
+
+
+
+
+ Credit Card +
+
+ Блок +

$147

+
+
+
+ +
+
+
+
+
+ Credit Card +
+
+ Блок +

$147

+
+
+
+ +
+
+
+
+
+ Credit Card +
+
+ Блок +

$147

+
+
+
+ +
+ +
+ +
+ +
+ + +
+
\ No newline at end of file diff --git a/kiosk/templates/kiosk/include/transaction.html b/kiosk/templates/kiosk/include/transaction.html new file mode 100644 index 0000000..3cebc20 --- /dev/null +++ b/kiosk/templates/kiosk/include/transaction.html @@ -0,0 +1,76 @@ +{% load static %} +
+
+
+
    +
  • +
    + User +
    +
    +
    + Paypal +
    Send money
    +
    +
    +
    +82.6
    + USD +
    +
    +
  • +
  • +
    + User +
    +
    +
    + Wallet +
    Mac'D
    +
    +
    +
    +270.69
    + USD +
    +
    +
  • +
  • +
    + User +
    +
    +
    + Transfer +
    Refund
    +
    +
    +
    +637.91
    + USD +
    +
    +
  • +
  • +
    + User +
    +
    +
    + Credit Card +
    Ordered Food
    +
    +
    +
    -838.71
    + USD +
    +
    +
  • +
+
+
+
\ No newline at end of file diff --git a/kiosk/tests.py b/kiosk/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/kiosk/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/kiosk/urls.py b/kiosk/urls.py new file mode 100644 index 0000000..91902de --- /dev/null +++ b/kiosk/urls.py @@ -0,0 +1,6 @@ +from django.urls import path,include +from kiosk import views + +urlpatterns = [ + path('', views.kiosk, name='kiosk'), +] diff --git a/kiosk/views.py b/kiosk/views.py new file mode 100644 index 0000000..fc59b16 --- /dev/null +++ b/kiosk/views.py @@ -0,0 +1,21 @@ +from django.shortcuts import render +from upanel.models import * +import tinytuya + +def turn_on_off(click): + devices = Device.objects.values('device_id', 'device_ip', 'local_key', 'device_version').order_by('id') + d = tinytuya.OutletDevice( + dev_id=str(devices[0]['device_id']), + address=str(devices[0]['device_ip']), + local_key=str(devices[0]['local_key']), + version=devices[0]['device_version']) + match click: + case 'on': + return d.turn_on() + case 'off': + return d.turn_off() + +def kiosk(request): + if request.method == 'POST': + turn_on_off(request.POST['click']) + return render(request, 'kiosk/content/index.html',) \ No newline at end of file diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..bbbe8e5 --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'miroca.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/miroca/__init__.py b/miroca/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/miroca/asgi.py b/miroca/asgi.py new file mode 100644 index 0000000..e2745d5 --- /dev/null +++ b/miroca/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for miroca project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'miroca.settings') + +application = get_asgi_application() diff --git a/miroca/settings.py b/miroca/settings.py new file mode 100644 index 0000000..d2e1fa1 --- /dev/null +++ b/miroca/settings.py @@ -0,0 +1,161 @@ +""" +Django settings for miroca project. + +Generated by 'django-admin startproject' using Django 3.2.25. + +For more information on this file, see +https://docs.djangoproject.com/en/3.2/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/3.2/ref/settings/ +""" +import os +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-cy5#sc7)e)f$$fl@w0%p7i$xie-1qc3uxi4xri_q$8e2@s!kdi' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = ['miroca.ru'] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'auth_miroca.apps.AuthMirocaConfig', + 'upanel.apps.UpanelConfig', + 'home.apps.HomeConfig', + 'address.apps.AddressConfig', + 'rest_framework', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': [ + 'rest_framework.authentication.BasicAuthentication', + 'rest_framework.authentication.SessionAuthentication', + ] +} + +ROOT_URLCONF = 'miroca.urls' + +AUTHENTICATION_BACKENDS = ( + 'django.contrib.auth.backends.ModelBackend', +) +AUTH_USER_MODEL = 'auth_miroca.CustomUser' +LOGIN_REDIRECT_URL = "upanel" + +ROOT_URLCONF = 'miroca.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'miroca.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/3.2/ref/settings/#databases + +# DATABASES = { +# 'default': { +# 'ENGINE': 'django.db.backends.sqlite3', +# 'NAME': BASE_DIR / 'db.sqlite3', +# } +# } + + +# Password validation +# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/3.2/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/3.2/howto/static-files/ + +STATIC_URL = '/static/' +STATIC_ROOT = '/home/c76533/miroca.ru/www/static' + +MEDIA_URL = '/media/' +MEDIA_ROOT = '/home/c76533/miroca.ru/www/media' + +# Default primary key field type +# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +# database settings +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'HOST': 'postgres.c76533.h2', + 'PORT': '5432', + 'NAME': 'c76533_miroca_ru', + 'USER': 'c76533_miroca_ru', + 'PASSWORD': 'DaFsaQicrogot12', + }, +} diff --git a/miroca/urls.py b/miroca/urls.py new file mode 100644 index 0000000..569309e --- /dev/null +++ b/miroca/urls.py @@ -0,0 +1,23 @@ +"""miroca URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/3.2/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path, include + +urlpatterns = [ + # path('admin/', admin.site.urls), + path('', include('upanel.urls')), + path('api/', include('api.urls')), +] diff --git a/miroca/wsgi.py b/miroca/wsgi.py new file mode 100644 index 0000000..4a66a06 --- /dev/null +++ b/miroca/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for miroca project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'miroca.settings') + +application = get_wsgi_application() diff --git a/status/status.sh b/status/status.sh new file mode 100644 index 0000000..fb21baa --- /dev/null +++ b/status/status.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# Объявляем переменные +status="$(cat /home/orangepi/miroca.ru/status/status.log)" +# указывает IP хоста, количество пингов - 2 раза, и смотрим количество ошибок соединения +result=$(ping -c 2 91.201.52.229 2<&1| grep -icE 'unknown|expired|unreachable|time out|100% packet loss') +#Если система загружена более 10 мин +#if test $(cut -d '.' -f1 /proc/uptime) -gt 600; then +# systemctl restart gunicorn +#fi +if [[ "$result" == 0 ]]; then + echo "connect" > "/home/orangepi/miroca.ru/status/status.log" +else + echo "disconnect" > "/home/orangepi/miroca.ru/status/status.log" +fi +# Связь пропала +if [[ "$result" > 0 && "$status" == connect ]]; then + echo -e "$(date "+%d.%m.%Y %H:%M:%S") Связь пропала" >> /home/orangepi/miroca.ru/status/hoststatus.log + echo "disconnect" > "/home/orangepi/miroca.ru/status/status.log" + systemctl restart gunicorn +# Связь появилась +elif [[ "$result" == 0 && "$status" == disconnect ]]; then + echo -e "$(date "+%d.%m.%Y %H:%M:%S") Связь появилась" >> /home/orangepi/miroca.ru/status/hoststatus.log + echo "connect" > "/home/orangepi/miroca.ru/status/status.log" + systemctl restart gunicorn +# В других случаях ничего не делаем +fi \ No newline at end of file diff --git a/tasks/__init__.py b/tasks/__init__.py new file mode 100644 index 0000000..6bd1307 --- /dev/null +++ b/tasks/__init__.py @@ -0,0 +1,2 @@ +# import celery +from celery import app as celery_app \ No newline at end of file diff --git a/tasks/admin.py b/tasks/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/tasks/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/tasks/apps.py b/tasks/apps.py new file mode 100644 index 0000000..3ff3ab3 --- /dev/null +++ b/tasks/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class TasksConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'tasks' diff --git a/tasks/models.py b/tasks/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/tasks/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/tasks/tasks.py b/tasks/tasks.py new file mode 100644 index 0000000..aee8892 --- /dev/null +++ b/tasks/tasks.py @@ -0,0 +1,7 @@ +from celery import Celery + +app = Celery('tasks', broker='amqp://172.17.0.4:5672') + +@app.task +def add(x, y): + return x + y diff --git a/tasks/tests.py b/tasks/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/tasks/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/tasks/views.py b/tasks/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/tasks/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/upanel/__init__.py b/upanel/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/upanel/admin.py b/upanel/admin.py new file mode 100644 index 0000000..e69de29 diff --git a/upanel/apps.py b/upanel/apps.py new file mode 100644 index 0000000..dc43833 --- /dev/null +++ b/upanel/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class UpanelConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'upanel' diff --git a/upanel/logout.py b/upanel/logout.py new file mode 100644 index 0000000..a6acdfd --- /dev/null +++ b/upanel/logout.py @@ -0,0 +1,9 @@ +from django.shortcuts import render, redirect + +def logout_view(func): + def wrapper(request): + if request.method == 'POST': + request.session.clear() + return redirect ('login') + return func(request) + return wrapper \ No newline at end of file diff --git a/upanel/migrations/0001_initial.py b/upanel/migrations/0001_initial.py new file mode 100644 index 0000000..4de90ce --- /dev/null +++ b/upanel/migrations/0001_initial.py @@ -0,0 +1,57 @@ +# Generated by Django 5.1.3 on 2024-11-29 06:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Device', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('device_id', models.CharField(db_index=True, max_length=200, verbose_name='Id устройства')), + ('name', models.TextField(max_length=300, null=True, verbose_name='Имя')), + ('device_ip', models.CharField(db_index=True, max_length=200, null=True, verbose_name='IP адрес')), + ('local_key', models.TextField(db_index=True, max_length=200, null=True, verbose_name='Ключ устройства')), + ('device_version', models.FloatField(blank=True, null=True, verbose_name='Версия устройства')), + ('on_off', models.BooleanField(null=True, verbose_name='Вкл')), + ], + options={ + 'verbose_name': 'Устройство', + 'verbose_name_plural': 'Устройства', + 'ordering': ['device_id'], + }, + ), + migrations.CreateModel( + name='Hostname', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, null=True)), + ('area', models.CharField(blank=True, max_length=255, null=True, verbose_name='Зона температуры')), + ], + options={ + 'verbose_name': 'Зона температуры', + 'verbose_name_plural': 'Зона температуры', + 'ordering': ['name'], + }, + ), + migrations.CreateModel( + name='Month', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(db_index=True, max_length=200, verbose_name='Месяц')), + ('temp', models.JSONField(blank=True, default=list, null=True, verbose_name='Список температур')), + ], + options={ + 'verbose_name': 'Месяц года', + 'verbose_name_plural': 'Месяц года', + 'ordering': ['name'], + }, + ), + ] diff --git a/upanel/migrations/0002_device_customuser.py b/upanel/migrations/0002_device_customuser.py new file mode 100644 index 0000000..d5832e8 --- /dev/null +++ b/upanel/migrations/0002_device_customuser.py @@ -0,0 +1,21 @@ +# Generated by Django 5.1.3 on 2024-11-30 14:32 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('upanel', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='device', + name='customuser', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Пользователь'), + ), + ] diff --git a/upanel/migrations/0003_hostname_customuser.py b/upanel/migrations/0003_hostname_customuser.py new file mode 100644 index 0000000..13b5fc2 --- /dev/null +++ b/upanel/migrations/0003_hostname_customuser.py @@ -0,0 +1,21 @@ +# Generated by Django 5.1.3 on 2024-11-30 15:03 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('upanel', '0002_device_customuser'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='hostname', + name='customuser', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Пользователь'), + ), + ] diff --git a/upanel/migrations/__init__.py b/upanel/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/upanel/models.py b/upanel/models.py new file mode 100644 index 0000000..8f16d2c --- /dev/null +++ b/upanel/models.py @@ -0,0 +1,47 @@ +from django.db import models +from django.db.models.deletion import CASCADE +from django.db.models.fields.related import ForeignKey + +class Hostname(models.Model): + customuser = ForeignKey('auth_miroca.CustomUser', null=True, blank=True, on_delete=CASCADE, related_name='+', verbose_name='Пользователь') + name = models.CharField(max_length=255, null=True, blank=True) + area = models.CharField(max_length=255, null=True, blank=True, verbose_name='Зона температуры') + + class Meta: + ordering = ['name'] + verbose_name = 'Зона температуры' + verbose_name_plural = 'Зона температуры' + + def __str__(self): + return self.name + +class Month(models.Model): + name = models.CharField(max_length=200, db_index=True,verbose_name='Месяц') + temp = models.JSONField(default=list, null=True, blank=True, verbose_name='Список температур') + + + class Meta: + ordering = ['name'] + verbose_name = 'Месяц года' + verbose_name_plural = 'Месяц года' + + def __str__(self): + return self.name + +class Device(models.Model): + customuser = ForeignKey('auth_miroca.CustomUser', null=True, blank=True, on_delete=CASCADE, related_name='+', verbose_name='Пользователь') + device_id=models.CharField(max_length=200,db_index=True,verbose_name='Id устройства') + name =models.TextField(max_length=300,null=True,verbose_name='Имя') + device_ip=models.CharField(max_length=200,db_index=True,null=True,verbose_name='IP адрес') + local_key=models.TextField(max_length=200,db_index=True,null=True,verbose_name='Ключ устройства') + device_version=models.FloatField(blank=True,null=True,verbose_name='Версия устройства') + on_off = models.BooleanField(null=True, verbose_name='Вкл') + + + class Meta: + ordering = ['device_id'] + verbose_name = 'Устройство' + verbose_name_plural = 'Устройства' + + def __str__(self): + return self.device_id diff --git a/upanel/templates/upanel/login.html b/upanel/templates/upanel/login.html new file mode 100644 index 0000000..eada7d7 --- /dev/null +++ b/upanel/templates/upanel/login.html @@ -0,0 +1,64 @@ + + +{% load static %} + + + Система Miroca + + + + + + + + +
+ +
+ {% csrf_token %} +
+ +
+
+ + +
+ +
+ + +
+ +
+ +
+ +
+
+ + + + + + + + + + + + + + + + + diff --git a/upanel/templates/upanel/upanel/base/base.html b/upanel/templates/upanel/upanel/base/base.html new file mode 100644 index 0000000..e900186 --- /dev/null +++ b/upanel/templates/upanel/upanel/base/base.html @@ -0,0 +1,61 @@ + +{% load static %} + + + + + + Панель управления Умный дом Мирока + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +{% block content %} +{% endblock %} + \ No newline at end of file diff --git a/upanel/templates/upanel/upanel/base/button.html b/upanel/templates/upanel/upanel/base/button.html new file mode 100644 index 0000000..fe7b5c8 --- /dev/null +++ b/upanel/templates/upanel/upanel/base/button.html @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/upanel/templates/upanel/upanel/base/label.html b/upanel/templates/upanel/upanel/base/label.html new file mode 100644 index 0000000..a5a7135 --- /dev/null +++ b/upanel/templates/upanel/upanel/base/label.html @@ -0,0 +1,7 @@ +{% load static %} +
+ + +

iroca

+
+
\ No newline at end of file diff --git a/upanel/templates/upanel/upanel/base/status.html b/upanel/templates/upanel/upanel/base/status.html new file mode 100644 index 0000000..0003af6 --- /dev/null +++ b/upanel/templates/upanel/upanel/base/status.html @@ -0,0 +1,21 @@ +{% load static %} +
+
+
+ +
+
+

+

Облачный сервер

+
+
+
+
+ +
+
+

OK

+

Система контроля

+
+
+
\ No newline at end of file diff --git a/upanel/templates/upanel/upanel/base/temperature.html b/upanel/templates/upanel/upanel/base/temperature.html new file mode 100644 index 0000000..873593f --- /dev/null +++ b/upanel/templates/upanel/upanel/base/temperature.html @@ -0,0 +1,29 @@ +
+
+
+
+
+
ТЕМПЕРАТУРА
+
+
22
+
(°C)
+
+
+
+
+
+
+
ВЛАЖНОСТЬ
+
+
45
+
г/м3
+
+
+
+
+
+ +
+
+
+
\ No newline at end of file diff --git a/upanel/templates/upanel/upanel/base/user.html b/upanel/templates/upanel/upanel/base/user.html new file mode 100644 index 0000000..670d6aa --- /dev/null +++ b/upanel/templates/upanel/upanel/base/user.html @@ -0,0 +1,22 @@ +{% load static %} + + \ No newline at end of file diff --git a/upanel/templates/upanel/upanel/content/content_wrapper.html b/upanel/templates/upanel/upanel/content/content_wrapper.html new file mode 100644 index 0000000..810440b --- /dev/null +++ b/upanel/templates/upanel/upanel/content/content_wrapper.html @@ -0,0 +1,51 @@ +{% load static %} +
+ + +
+
+ + {% include 'upanel/upanel/content/top.html' %} + + + + {% include 'upanel/upanel/content/ping_server.html' %} + + + + {% include 'upanel/upanel/content/greenhouse.html' %} + + + +
+
+ + + {% include 'upanel/upanel/content/total.html' %} + + + + {% include 'upanel/upanel/content/profile.html' %} + + +
+
+
+
+ + {% include 'upanel/upanel/content/orderstatic.html' %} + + + + {% include 'upanel/upanel/content/transaction.html' %} + +
+
+ + + + {% include 'upanel/upanel/content/footer.html' %} + + +
+
\ No newline at end of file diff --git a/upanel/templates/upanel/upanel/content/footer.html b/upanel/templates/upanel/upanel/content/footer.html new file mode 100644 index 0000000..6b54b28 --- /dev/null +++ b/upanel/templates/upanel/upanel/content/footer.html @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/upanel/templates/upanel/upanel/content/greenhouse.html b/upanel/templates/upanel/upanel/content/greenhouse.html new file mode 100644 index 0000000..939653b --- /dev/null +++ b/upanel/templates/upanel/upanel/content/greenhouse.html @@ -0,0 +1,52 @@ + +
+
+
+
+
Теплица показания датчиков
+
+
+
+
+
+ +
+
+
+
Показания датчиков теплицы
+ +
+
+
+ +
+
+ Температура + {% if temp != 0 %} +
+{{ temp }}°C
+ {% elif temp == 0 %} +
+{{ temp }}°C
+ {% else %} +
-{{ temp }}°C
+ {% endif %} +
+
+
+
+ +
+
+ Влаж. почвы +
{{ damp }}%
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/upanel/templates/upanel/upanel/content/orderstatic.html b/upanel/templates/upanel/upanel/content/orderstatic.html new file mode 100644 index 0000000..beaa289 --- /dev/null +++ b/upanel/templates/upanel/upanel/content/orderstatic.html @@ -0,0 +1,100 @@ +{% load static %} +
+
+
+
+
Order Statistics
+ 42.82k Total Sales +
+ +
+
+
+
+

8,258

+ Total Orders +
+
+
+
    +
  • +
    + +
    +
    +
    +
    Electronic
    + Mobile, Earbuds, TV +
    +
    + 82.5k +
    +
    +
  • +
  • +
    + +
    +
    +
    +
    Fashion
    + T-shirt, Jeans, Shoes +
    +
    + 23.8k +
    +
    +
  • +
  • +
    + +
    +
    +
    +
    Decor
    + Fine Art, Dining +
    +
    + 849k +
    +
    +
  • +
  • +
    + +
    +
    +
    +
    Sports
    + Football, Cricket Kit +
    +
    + 99 +
    +
    +
  • +
+
+
+
\ No newline at end of file diff --git a/upanel/templates/upanel/upanel/content/ping_server.html b/upanel/templates/upanel/upanel/content/ping_server.html new file mode 100644 index 0000000..83c49b7 --- /dev/null +++ b/upanel/templates/upanel/upanel/content/ping_server.html @@ -0,0 +1,37 @@ +{% load static %} +
+
+
+
+
+
+
+ chart success +
+
+ Сервер +

+
+
+
+
+
+
+
+
+ temper +
+
+ Интернет +

+
+
+
+
+
\ No newline at end of file diff --git a/upanel/templates/upanel/upanel/content/profile.html b/upanel/templates/upanel/upanel/content/profile.html new file mode 100644 index 0000000..ae6b024 --- /dev/null +++ b/upanel/templates/upanel/upanel/content/profile.html @@ -0,0 +1,21 @@ +
+
+
+
+
+
+
Profile Report
+ Year 2021 +
+ +
+ 68.2% +

$84,686k

+
+
+
+
+
+
+
\ No newline at end of file diff --git a/upanel/templates/upanel/upanel/content/top.html b/upanel/templates/upanel/upanel/content/top.html new file mode 100644 index 0000000..d036700 --- /dev/null +++ b/upanel/templates/upanel/upanel/content/top.html @@ -0,0 +1,25 @@ +{% load static %} +
+
+
+
+
+
Сервер конфигурации
+

+ Вы находитесь на главной странице конфигуратора +

+
+
+
+
+ View Badge User +
+
+
+
+
\ No newline at end of file diff --git a/upanel/templates/upanel/upanel/content/total.html b/upanel/templates/upanel/upanel/content/total.html new file mode 100644 index 0000000..93a33a7 --- /dev/null +++ b/upanel/templates/upanel/upanel/content/total.html @@ -0,0 +1,30 @@ +{% load static %} +
+
+
+
+
+ wet +
+
+ Влажность +

50%

+
+
+
+
+
+
+
+
+ Credit Card +
+
+ Блок +

$147

+
+
+
\ No newline at end of file diff --git a/upanel/templates/upanel/upanel/content/transaction.html b/upanel/templates/upanel/upanel/content/transaction.html new file mode 100644 index 0000000..d99a1f8 --- /dev/null +++ b/upanel/templates/upanel/upanel/content/transaction.html @@ -0,0 +1,120 @@ +{% load static %} +
+
+
+
Transactions
+ +
+
+
    +
  • +
    + User +
    +
    +
    + Paypal +
    Send money
    +
    +
    +
    +82.6
    + USD +
    +
    +
  • +
  • +
    + User +
    +
    +
    + Wallet +
    Mac'D
    +
    +
    +
    +270.69
    + USD +
    +
    +
  • +
  • +
    + User +
    +
    +
    + Transfer +
    Refund
    +
    +
    +
    +637.91
    + USD +
    +
    +
  • +
  • +
    + User +
    +
    +
    + Credit Card +
    Ordered Food
    +
    +
    +
    -838.71
    + USD +
    +
    +
  • +
  • +
    + User +
    +
    +
    + Wallet +
    Starbucks
    +
    +
    +
    +203.33
    + USD +
    +
    +
  • +
  • +
    + User +
    +
    +
    + Mastercard +
    Ordered Food
    +
    +
    +
    -92.45
    + USD +
    +
    +
  • +
+
+
+
\ No newline at end of file diff --git a/upanel/templates/upanel/upanel/index.html b/upanel/templates/upanel/upanel/index.html new file mode 100644 index 0000000..600535a --- /dev/null +++ b/upanel/templates/upanel/upanel/index.html @@ -0,0 +1,157 @@ +{% extends 'upanel/upanel/base/base.html' %} +{% load static %} +{% block content%} + + +
+
+ +{# {% include 'upanel/upanel/menu.html' %}#} + + +
+ + + + + + + + {% include 'upanel/upanel/content/content_wrapper.html' %} + +
+ +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +{% endblock content %} + diff --git a/upanel/templates/upanel/upanel/login.html b/upanel/templates/upanel/upanel/login.html new file mode 100644 index 0000000..0068eaf --- /dev/null +++ b/upanel/templates/upanel/upanel/login.html @@ -0,0 +1,65 @@ + + +{% load static %} + + + Система Miroca + + + + + + + + +
+ +
+ {% csrf_token %} +
+ +

iroca

+
+
+ + +
+ +
+ + +
+ +
+ +
+ +
+
+ + + + + + + + + + + + + + + + + diff --git a/upanel/templates/upanel/upanel/menu.html b/upanel/templates/upanel/upanel/menu.html new file mode 100644 index 0000000..3cc011c --- /dev/null +++ b/upanel/templates/upanel/upanel/menu.html @@ -0,0 +1,459 @@ + +{% load static %} + + + \ No newline at end of file diff --git a/upanel/templates/upanel/upanel/smarthome.html b/upanel/templates/upanel/upanel/smarthome.html new file mode 100644 index 0000000..4dc91f5 --- /dev/null +++ b/upanel/templates/upanel/upanel/smarthome.html @@ -0,0 +1,39 @@ +{% extends 'upanel/upanel/base/base.html' %} +{% load static %} +{#Блоки температуры и влажности#} +{% block styleinclude %} + +{% endblock styleinclude %} +{#График#} +{% block js %} + + + + +{% endblock js %} +{% block content %} + +
+
+
+ {% include 'upanel/upanel/base/label.html' %} +

+ {% include 'upanel/upanel/base/user.html' %} +
+ {% include 'upanel/upanel/base/status.html' %} + +
+ + +
+
+
+ + + +{% endblock content %} \ No newline at end of file diff --git a/upanel/test.py b/upanel/test.py new file mode 100644 index 0000000..72c0271 --- /dev/null +++ b/upanel/test.py @@ -0,0 +1,15 @@ +from sshtunnel import SSHTunnelForwarder +import os + +hostname = "192.168.88.3" +response = os.system("ping -c 1 " + hostname) +if response == 0: + ssh_tunnel = SSHTunnelForwarder( + '192.168.88.3', + ssh_username="orangepi", + ssh_password="cbvgcjy0", + remote_bind_address=('192.168.88.3', 22) + ) + + ssh_tunnel.start() + print(ssh_tunnel.local_bind_port) # show assigned local port \ No newline at end of file diff --git a/upanel/tests.py b/upanel/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/upanel/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/upanel/urls.py b/upanel/urls.py new file mode 100644 index 0000000..2778386 --- /dev/null +++ b/upanel/urls.py @@ -0,0 +1,18 @@ +from django.urls import path +from django.contrib.auth.views import LoginView, LogoutView, PasswordChangeView, PasswordChangeDoneView +from django.contrib.auth import logout +from .import views + + +urlpatterns = [ + path('', views.home, name='upanel'), + path('connect/', views.connect, name='connect'), + # path('greenhouse/', views.greenhouse, name='greenhouse'), + # path('server_smart/', views.server_smart, name='server_smart'), + path('login/', LoginView.as_view(template_name='upanel/login.html'), name='login'), + path('logout/', LoginView.as_view(template_name='upanel/login.html'), name='logout'), + path('password_change/', PasswordChangeView.as_view(template_name='upanel/change_password.html'), + name='password_change'), + path('password_change/done/', PasswordChangeDoneView.as_view(template_name='upanel/change_password_done.html'), + name='password_change_done'), +] \ No newline at end of file diff --git a/upanel/views.py b/upanel/views.py new file mode 100644 index 0000000..20601ac --- /dev/null +++ b/upanel/views.py @@ -0,0 +1,56 @@ +import json +from django.contrib.auth import get_user_model +from routeros_api.exceptions import RouterOsApiConnectionError + +# from synology_api.exceptions import JSONDecodeError +User = get_user_model() +import os, requests,re +from django.shortcuts import render, redirect +from django.http import JsonResponse +from django.contrib.auth.decorators import login_required +from .logout import logout_view +from upanel.models import * +import routeros_api +import datetime + + +@login_required(login_url='/login/') +def connect(request): + try: + hosts = User.objects.values('ipaddress').distinct()[0] + connection = routeros_api.RouterOsApiPool(hosts['ipaddress'], username='api', password='cbvgcjy0', + plaintext_login=True) + api = connection.get_api() + ping=api.get_binary_resource('/').call('ping', {'address': str.encode(hosts['ipaddress']), 'count': b'2'}) #блять тут надо перевести в байт str.encode + return JsonResponse({'ethernet': bytes.decode(max([i['time'] for i in ping]))}) + except RouterOsApiConnectionError as e: + return JsonResponse({'ethernet': 'false'}) + + +# def server_smart(request): +# month_int = sorted([int(datetime.datetime.now().strftime('%m'))-i for i in range(6)]) +# pattern = r'\+\d{2}+\.\d+' +# return JsonResponse({'month':list(Month.objects.using('miroca').filter(id__in=month_int).order_by('id').values_list('name', flat=True)), +# 'smart_server_temp':max(map(float,re.findall(pattern, os.popen('sensors').read())))}) + + +# # @login_required(login_url='/login/') +# # @logout_view +# def greenhouse(request): +# connect_timeout = 18 +# read_timeout = 30 +# host_name_green = Hostname.objects.values('area')[0]['area'] +# try: +# response = requests.get("http://" + host_name_green, timeout=(connect_timeout, read_timeout)) +# return JsonResponse(response.json() | {'host_name_green':host_name_green}) +# except Exception as e: +# return JsonResponse({'temp': [' ', ' '], 'sensor_lend_web': [' '], 'host_name_green':host_name_green}) + + +@login_required(login_url='/login/') +@logout_view +def home(request): + user = User.objects.get(id=request.user.id) + return render(request, 'upanel/upanel/index.html',# {'user': user, 'temp': res['temp'][0], 'damp': res['sensor_lend_web'][0]} + ) + diff --git a/wsgi.py b/wsgi.py new file mode 100644 index 0000000..4a66a06 --- /dev/null +++ b/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for miroca project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'miroca.settings') + +application = get_wsgi_application()