mirror of
https://github.com/PiBrewing/craftbeerpi4.git
synced 2024-11-14 02:58:16 +01:00
Changed system controller to accomodate more system fucntions
- Backup and restore of config folder is possible via system page - Bumped version to 4.0.0.36 - will require cbpiui 0.0.17
This commit is contained in:
parent
2d55410d4a
commit
e1e7de2924
7 changed files with 216 additions and 11 deletions
|
@ -1 +1 @@
|
||||||
__version__ = "4.0.0.35"
|
__version__ = "4.0.0.36"
|
||||||
|
|
|
@ -332,7 +332,7 @@ def remove(name):
|
||||||
@click.command()
|
@click.command()
|
||||||
@click.argument('name')
|
@click.argument('name')
|
||||||
def create(name):
|
def create(name):
|
||||||
'''Deactivate Plugin'''
|
'''Create New Plugin'''
|
||||||
plugin_create(name)
|
plugin_create(name)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
|
import psutil
|
||||||
|
import pathlib
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
from voluptuous.schema_builder import message
|
||||||
|
from cbpi.api.dataclasses import NotificationAction, NotificationType
|
||||||
|
from cbpi.api.base import CBPiBase
|
||||||
|
from cbpi.api.config import ConfigType
|
||||||
|
from cbpi.api import *
|
||||||
|
import zipfile
|
||||||
|
import socket
|
||||||
|
|
||||||
class SystemController:
|
class SystemController:
|
||||||
|
|
||||||
|
@ -17,7 +25,6 @@ class SystemController:
|
||||||
async def check_for_update(self, app):
|
async def check_for_update(self, app):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
async def restart(self):
|
async def restart(self):
|
||||||
logging.info("RESTART")
|
logging.info("RESTART")
|
||||||
os.system('systemctl reboot')
|
os.system('systemctl reboot')
|
||||||
|
@ -27,3 +34,135 @@ class SystemController:
|
||||||
logging.info("SHUTDOWN")
|
logging.info("SHUTDOWN")
|
||||||
os.system('systemctl poweroff')
|
os.system('systemctl poweroff')
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
async def backupConfig(self):
|
||||||
|
output_filename = "cbpi4_config"
|
||||||
|
dir_name = pathlib.Path(os.path.join(".", 'config'))
|
||||||
|
shutil.make_archive(output_filename, 'zip', dir_name)
|
||||||
|
|
||||||
|
def allowed_file(self, filename, extension):
|
||||||
|
return '.' in filename and filename.rsplit('.', 1)[1] in set([extension])
|
||||||
|
|
||||||
|
def recursive_chown(self, path, owner, group):
|
||||||
|
for dirpath, dirnames, filenames in os.walk(path):
|
||||||
|
shutil.chown(dirpath, owner, group)
|
||||||
|
for filename in filenames:
|
||||||
|
shutil.chown(os.path.join(dirpath, filename), owner, group)
|
||||||
|
|
||||||
|
async def restoreConfig(self, data):
|
||||||
|
fileData = data['File']
|
||||||
|
filename = fileData.filename
|
||||||
|
backup_file = fileData.file
|
||||||
|
content_type = fileData.content_type
|
||||||
|
required_content=['dashboard/', 'recipes/', 'upload/', 'config.json', 'config.yaml']
|
||||||
|
|
||||||
|
if content_type == 'application/x-zip-compressed':
|
||||||
|
try:
|
||||||
|
content = backup_file.read()
|
||||||
|
if backup_file and self.allowed_file(filename, 'zip'):
|
||||||
|
self.path = os.path.join(".", "restored_config.zip")
|
||||||
|
|
||||||
|
f=open(self.path, "wb")
|
||||||
|
f.write(content)
|
||||||
|
f.close()
|
||||||
|
zip=zipfile.ZipFile(self.path)
|
||||||
|
zip_content_list = zip.namelist()
|
||||||
|
zip_content = True
|
||||||
|
for content in required_content:
|
||||||
|
try:
|
||||||
|
check = zip_content_list.index(content)
|
||||||
|
except:
|
||||||
|
zip_content = False
|
||||||
|
if zip_content == True:
|
||||||
|
output_path = pathlib.Path(os.path.join(".", 'config'))
|
||||||
|
shutil.rmtree(output_path, ignore_errors=True)
|
||||||
|
zip.extractall(output_path)
|
||||||
|
self.recursive_chown(output_path, "pi", "pi")
|
||||||
|
self.cbpi.notify("Success", "Config backup has been uploaded", NotificationType.SUCCESS)
|
||||||
|
else:
|
||||||
|
self.cbpi.notify("Error", "Wrong content type. Upload failed", NotificationType.ERROR)
|
||||||
|
except:
|
||||||
|
self.cbpi.notify("Error", "Config backup upload failed", NotificationType.ERROR)
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.cbpi.notify("Error", "Wrong content type. Upload failed", NotificationType.ERROR)
|
||||||
|
|
||||||
|
async def systeminfo(self):
|
||||||
|
logging.info("SYSTEMINFO")
|
||||||
|
system = ""
|
||||||
|
temp = 0
|
||||||
|
cpuload = 0
|
||||||
|
cpucount = 0
|
||||||
|
cpufreq = 0
|
||||||
|
totalmem = 0
|
||||||
|
availmem = 0
|
||||||
|
mempercent = 0
|
||||||
|
eth0IP = "N/A"
|
||||||
|
wlan0IP = "N/A"
|
||||||
|
|
||||||
|
TEMP_UNIT=self.cbpi.config.get("TEMP_UNIT", "C")
|
||||||
|
FAHRENHEIT = False if TEMP_UNIT == "C" else True
|
||||||
|
|
||||||
|
af_map = { socket.AF_INET: 'IPv4',
|
||||||
|
socket.AF_INET6: 'IPv6',
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
if psutil.LINUX == True:
|
||||||
|
system = "Linux"
|
||||||
|
elif psutil.WINDOWS == True:
|
||||||
|
system = "Windows"
|
||||||
|
elif psutil.MACOS == True:
|
||||||
|
system = "MacOS"
|
||||||
|
cpuload = round(psutil.cpu_percent(interval=None),1)
|
||||||
|
cpucount = psutil.cpu_count(logical=False)
|
||||||
|
cpufreq = psutil.cpu_freq()
|
||||||
|
mem = psutil.virtual_memory()
|
||||||
|
availmem = round((int(mem.available) / (1024*1024)),1)
|
||||||
|
mempercent = round(float(mem.percent),1)
|
||||||
|
totalmem = round((int(mem.total) / (1024*1024)),1)
|
||||||
|
if system == "Linux":
|
||||||
|
try:
|
||||||
|
temps = psutil.sensors_temperatures(fahrenheit=FAHRENHEIT)
|
||||||
|
for name, entries in temps.items():
|
||||||
|
for entry in entries:
|
||||||
|
if name == "cpu_thermal":
|
||||||
|
temp = round(float(entry.current),1)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
temp = "N/A"
|
||||||
|
if system == "Linux":
|
||||||
|
try:
|
||||||
|
ethernet = psutil.net_if_addrs()
|
||||||
|
for nic, addrs in ethernet.items():
|
||||||
|
if nic == "eth0":
|
||||||
|
for addr in addrs:
|
||||||
|
if str(addr.family) == "AddressFamily.AF_INET":
|
||||||
|
if addr.address:
|
||||||
|
eth0IP = addr.address
|
||||||
|
if nic == "wlan0":
|
||||||
|
for addr in addrs:
|
||||||
|
if str(addr.family) == "AddressFamily.AF_INET":
|
||||||
|
if addr.address:
|
||||||
|
wlan0IP = addr.address
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
systeminfo = {'system': system,
|
||||||
|
'cpuload': cpuload,
|
||||||
|
'cpucount': cpucount,
|
||||||
|
'cpufreq': cpufreq.current,
|
||||||
|
'totalmem': totalmem,
|
||||||
|
'availmem': availmem,
|
||||||
|
'mempercent': mempercent,
|
||||||
|
'temp': temp,
|
||||||
|
'temp_unit': TEMP_UNIT,
|
||||||
|
'eth0': eth0IP,
|
||||||
|
'wlan0': wlan0IP}
|
||||||
|
return systeminfo
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ class NotificationStep(CBPiStep):
|
||||||
@parameters([Property.Number(label="Temp", configurable=True),
|
@parameters([Property.Number(label="Temp", configurable=True),
|
||||||
Property.Sensor(label="Sensor"),
|
Property.Sensor(label="Sensor"),
|
||||||
Property.Kettle(label="Kettle"),
|
Property.Kettle(label="Kettle"),
|
||||||
Property.Text(label="Notification",configurable = True, description = "Text for notification hen Temp is reached"),
|
Property.Text(label="Notification",configurable = True, description = "Text for notification when Temp is reached"),
|
||||||
Property.Select(label="AutoMode",options=["Yes","No"], description="Switch Kettlelogic automatically on and off -> Yes")])
|
Property.Select(label="AutoMode",options=["Yes","No"], description="Switch Kettlelogic automatically on and off -> Yes")])
|
||||||
class MashInStep(CBPiStep):
|
class MashInStep(CBPiStep):
|
||||||
|
|
||||||
|
@ -72,11 +72,11 @@ class MashInStep(CBPiStep):
|
||||||
await self.next()
|
await self.next()
|
||||||
|
|
||||||
async def on_timer_done(self,timer):
|
async def on_timer_done(self,timer):
|
||||||
self.summary = "MashIn Temp reached. Please add Malt."
|
self.summary = ""
|
||||||
await self.push_update()
|
await self.push_update()
|
||||||
if self.AutoMode == True:
|
if self.AutoMode == True:
|
||||||
await self.setAutoMode(False)
|
await self.setAutoMode(False)
|
||||||
self.cbpi.notify(self.name, self.props.get("Notification","Target Temp reached. Please klick next to move on."), action=[NotificationAction("Next Step", self.NextStep)])
|
self.cbpi.notify(self.name, self.props.get("Notification","Target Temp reached. Please add malt and klick next to move on."), action=[NotificationAction("Next Step", self.NextStep)])
|
||||||
|
|
||||||
async def on_timer_update(self,timer, seconds):
|
async def on_timer_update(self,timer, seconds):
|
||||||
await self.push_update()
|
await self.push_update()
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
|
from aiohttp import streamer
|
||||||
from cbpi.job.aiohttp import get_scheduler_from_app
|
from cbpi.job.aiohttp import get_scheduler_from_app
|
||||||
import logging
|
import logging
|
||||||
from cbpi.api import request_mapping
|
from cbpi.api import request_mapping
|
||||||
from cbpi.utils import json_dumps
|
from cbpi.utils import json_dumps
|
||||||
from cbpi import __version__
|
from cbpi import __version__
|
||||||
|
import pathlib
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
|
||||||
class SystemHttpEndpoints:
|
class SystemHttpEndpoints:
|
||||||
|
|
||||||
|
@ -88,7 +92,7 @@ class SystemHttpEndpoints:
|
||||||
async def restart(self, request):
|
async def restart(self, request):
|
||||||
"""
|
"""
|
||||||
---
|
---
|
||||||
description: Restart System - Not implemented
|
description: Restart System
|
||||||
tags:
|
tags:
|
||||||
- System
|
- System
|
||||||
responses:
|
responses:
|
||||||
|
@ -102,7 +106,7 @@ class SystemHttpEndpoints:
|
||||||
async def shutdown(self, request):
|
async def shutdown(self, request):
|
||||||
"""
|
"""
|
||||||
---
|
---
|
||||||
description: Shutdown System - Not implemented
|
description: Shutdown System
|
||||||
tags:
|
tags:
|
||||||
- System
|
- System
|
||||||
responses:
|
responses:
|
||||||
|
@ -111,3 +115,64 @@ class SystemHttpEndpoints:
|
||||||
"""
|
"""
|
||||||
await self.controller.shutdown()
|
await self.controller.shutdown()
|
||||||
return web.Response(text="SHUTDOWN")
|
return web.Response(text="SHUTDOWN")
|
||||||
|
|
||||||
|
@request_mapping("/backup", method="GET", name="BackupConfig", auth_required=False)
|
||||||
|
async def backup(self, request):
|
||||||
|
"""
|
||||||
|
---
|
||||||
|
description: Zip and download Config Folder
|
||||||
|
tags:
|
||||||
|
- System
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
content: # Response body
|
||||||
|
application/zip: # Media type
|
||||||
|
"""
|
||||||
|
await self.controller.backupConfig()
|
||||||
|
filename = "cbpi4_config.zip"
|
||||||
|
file_name = pathlib.Path(os.path.join(".", filename))
|
||||||
|
|
||||||
|
response = web.StreamResponse(
|
||||||
|
status=200,
|
||||||
|
reason='OK',
|
||||||
|
headers={'Content-Type': 'application/zip'},
|
||||||
|
)
|
||||||
|
await response.prepare(request)
|
||||||
|
with open(file_name, 'rb') as file:
|
||||||
|
for line in file.readlines():
|
||||||
|
await response.write(line)
|
||||||
|
|
||||||
|
await response.write_eof()
|
||||||
|
return response
|
||||||
|
|
||||||
|
@request_mapping("/restore", method="POST", name="RestoreConfig", auth_required=False)
|
||||||
|
async def restore(self, request):
|
||||||
|
"""
|
||||||
|
---
|
||||||
|
description: Restore Config
|
||||||
|
tags:
|
||||||
|
- System
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
"""
|
||||||
|
logging.info("Restore Config")
|
||||||
|
data = await request.post()
|
||||||
|
logging.info("Data received")
|
||||||
|
await self.controller.restoreConfig(data)
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
@request_mapping("/systeminfo", method="GET", name="SystemInfo", auth_required=False)
|
||||||
|
async def systeminfo(self, request):
|
||||||
|
"""
|
||||||
|
---
|
||||||
|
description: System Information
|
||||||
|
tags:
|
||||||
|
- System
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: successful operation
|
||||||
|
"""
|
||||||
|
systeminfo = await self.controller.systeminfo()
|
||||||
|
return web.json_response(data=systeminfo)
|
||||||
|
|
1
setup.py
1
setup.py
|
@ -34,6 +34,7 @@ setup(name='cbpi',
|
||||||
'shortuuid==1.0.1',
|
'shortuuid==1.0.1',
|
||||||
'tabulate==0.8.7',
|
'tabulate==0.8.7',
|
||||||
'asyncio-mqtt',
|
'asyncio-mqtt',
|
||||||
|
'psutil',
|
||||||
'numpy==1.20.3',
|
'numpy==1.20.3',
|
||||||
'scipy',
|
'scipy',
|
||||||
'cbpi4ui',
|
'cbpi4ui',
|
||||||
|
|
Loading…
Reference in a new issue