Add ConnectorManager component which allows for Connectors to listen to triggers and do actions on them. Also add HomeAssistantConfig which stores the configuration for the HomeAssistantConnector

This commit is contained in:
Mikhail Epifanov
2024-01-11 22:05:34 +01:00
parent d493ba72a1
commit e5f0c19cdc
18 changed files with 566 additions and 70 deletions

View File

@@ -460,6 +460,16 @@ class StorageViewSet(viewsets.ModelViewSet):
return self.queryset.filter(space=self.request.space)
class HomeAssistantConfigViewSet(viewsets.ModelViewSet):
# TODO handle delete protect error and adjust test
queryset = HomeAssistantConfig.objects
serializer_class = HomeAssistantConfigSerializer
permission_classes = [CustomIsAdmin & CustomTokenHasReadWriteScope]
def get_queryset(self):
return self.queryset.filter(space=self.request.space)
class SyncViewSet(viewsets.ModelViewSet):
queryset = Sync.objects
serializer_class = SyncSerializer

View File

@@ -9,7 +9,7 @@ from django.views.generic import DeleteView
from cookbook.helper.permission_helper import GroupRequiredMixin, OwnerRequiredMixin, group_required
from cookbook.models import (Comment, InviteLink, MealPlan, Recipe, RecipeBook, RecipeBookEntry,
RecipeImport, Space, Storage, Sync, UserSpace)
RecipeImport, Space, Storage, Sync, UserSpace, HomeAssistantConfig)
from cookbook.provider.dropbox import Dropbox
from cookbook.provider.local import Local
from cookbook.provider.nextcloud import Nextcloud
@@ -122,6 +122,29 @@ class StorageDelete(GroupRequiredMixin, DeleteView):
return HttpResponseRedirect(reverse('list_storage'))
class HomeAssistantConfigDelete(GroupRequiredMixin, DeleteView):
groups_required = ['admin']
template_name = "generic/delete_template.html"
model = HomeAssistantConfig
success_url = reverse_lazy('list_storage')
def get_context_data(self, **kwargs):
context = super(HomeAssistantConfigDelete, self).get_context_data(**kwargs)
context['title'] = _("HomeAssistant Config Backend")
return context
def post(self, request, *args, **kwargs):
try:
return self.delete(request, *args, **kwargs)
except ProtectedError:
messages.add_message(
request,
messages.WARNING,
_('Could not delete this storage backend as it is used in at least one monitor.') # noqa: E501
)
return HttpResponseRedirect(reverse('list_storage'))
class CommentDelete(OwnerRequiredMixin, DeleteView):
template_name = "generic/delete_template.html"
model = Comment

View File

@@ -1,3 +1,4 @@
import copy
import os
from django.contrib import messages
@@ -8,15 +9,17 @@ from django.utils.translation import gettext as _
from django.views.generic import UpdateView
from django.views.generic.edit import FormMixin
from cookbook.forms import CommentForm, ExternalRecipeForm, StorageForm, SyncForm
from cookbook.forms import CommentForm, ExternalRecipeForm, StorageForm, SyncForm, HomeAssistantConfigForm
from cookbook.helper.permission_helper import (GroupRequiredMixin, OwnerRequiredMixin,
above_space_limit, group_required)
from cookbook.models import Comment, Recipe, RecipeImport, Storage, Sync
from cookbook.models import Comment, Recipe, RecipeImport, Storage, Sync, HomeAssistantConfig
from cookbook.provider.dropbox import Dropbox
from cookbook.provider.local import Local
from cookbook.provider.nextcloud import Nextcloud
from recipes import settings
VALUE_NOT_CHANGED = '__NO__CHANGE__'
@group_required('guest')
def switch_recipe(request, pk):
@@ -76,7 +79,7 @@ class SyncUpdate(GroupRequiredMixin, UpdateView, SpaceFormMixing):
@group_required('admin')
def edit_storage(request, pk):
instance = get_object_or_404(Storage, pk=pk, space=request.space)
instance: Storage = get_object_or_404(Storage, pk=pk, space=request.space)
if not (instance.created_by == request.user or request.user.is_superuser):
messages.add_message(request, messages.ERROR, _('You cannot edit this storage!'))
@@ -87,17 +90,18 @@ def edit_storage(request, pk):
return redirect('index')
if request.method == "POST":
form = StorageForm(request.POST, instance=instance)
form = StorageForm(request.POST, instance=copy.deepcopy(instance))
if form.is_valid():
instance.name = form.cleaned_data['name']
instance.method = form.cleaned_data['method']
instance.username = form.cleaned_data['username']
instance.url = form.cleaned_data['url']
instance.path = form.cleaned_data['path']
if form.cleaned_data['password'] != '__NO__CHANGE__':
if form.cleaned_data['password'] != VALUE_NOT_CHANGED:
instance.password = form.cleaned_data['password']
if form.cleaned_data['token'] != '__NO__CHANGE__':
if form.cleaned_data['token'] != VALUE_NOT_CHANGED:
instance.token = form.cleaned_data['token']
instance.save()
@@ -113,8 +117,8 @@ def edit_storage(request, pk):
)
else:
pseudo_instance = instance
pseudo_instance.password = '__NO__CHANGE__'
pseudo_instance.token = '__NO__CHANGE__'
pseudo_instance.password = VALUE_NOT_CHANGED
pseudo_instance.token = VALUE_NOT_CHANGED
form = StorageForm(instance=pseudo_instance)
return render(
@@ -124,6 +128,47 @@ def edit_storage(request, pk):
)
@group_required('admin')
def edit_home_assistant_config(request, pk):
instance: HomeAssistantConfig = get_object_or_404(HomeAssistantConfig, pk=pk, space=request.space)
if not (instance.created_by == request.user or request.user.is_superuser):
messages.add_message(request, messages.ERROR, _('You cannot edit this homeassistant config!'))
return HttpResponseRedirect(reverse('edit_home_assistant_config'))
if request.space.demo or settings.HOSTED:
messages.add_message(request, messages.ERROR, _('This feature is not yet available in the hosted version of tandoor!'))
return redirect('index')
if request.method == "POST":
form = HomeAssistantConfigForm(request.POST, instance=copy.deepcopy(instance))
if form.is_valid():
instance.name = form.cleaned_data['name']
instance.url = form.cleaned_data['url']
instance.todo_entity = form.cleaned_data['todo_entity']
instance.on_shopping_list_entry_created_enabled = form.cleaned_data['on_shopping_list_entry_created_enabled']
instance.on_shopping_list_entry_updated_enabled = form.cleaned_data['on_shopping_list_entry_updated_enabled']
instance.on_shopping_list_entry_deleted_enabled = form.cleaned_data['on_shopping_list_entry_deleted_enabled']
if form.cleaned_data['token'] != VALUE_NOT_CHANGED:
instance.token = form.cleaned_data['token']
instance.save()
messages.add_message(request, messages.SUCCESS, _('HomeAssistant config saved!'))
else:
messages.add_message(request, messages.ERROR, _('There was an error updating this config!'))
else:
instance.token = VALUE_NOT_CHANGED
form = HomeAssistantConfigForm(instance=instance)
return render(
request,
'generic/edit_template.html',
{'form': form, 'title': _('HomeAssistantConfig')}
)
class CommentUpdate(OwnerRequiredMixin, UpdateView):
template_name = "generic/edit_template.html"
model = Comment

View File

@@ -6,8 +6,8 @@ from django.utils.translation import gettext as _
from django_tables2 import RequestConfig
from cookbook.helper.permission_helper import group_required
from cookbook.models import InviteLink, RecipeImport, Storage, SyncLog, UserFile
from cookbook.tables import ImportLogTable, InviteLinkTable, RecipeImportTable, StorageTable
from cookbook.models import InviteLink, RecipeImport, Storage, SyncLog, UserFile, HomeAssistantConfig
from cookbook.tables import ImportLogTable, InviteLinkTable, RecipeImportTable, StorageTable, HomeAssistantConfigTable
@group_required('admin')
@@ -65,17 +65,31 @@ def storage(request):
)
@group_required('admin')
def home_assistant_config(request):
table = HomeAssistantConfigTable(HomeAssistantConfig.objects.filter(space=request.space).all())
RequestConfig(request, paginate={'per_page': 25}).configure(table)
return render(
request, 'generic/list_template.html', {
'title': _("HomeAssistant Config Backend"),
'table': table,
'create_url': 'new_home_assistant_config'
})
@group_required('admin')
def invite_link(request):
table = InviteLinkTable(
InviteLink.objects.filter(valid_until__gte=datetime.today(), used_by=None, space=request.space).all())
RequestConfig(request, paginate={'per_page': 25}).configure(table)
return render(request, 'generic/list_template.html', {
'title': _("Invite Links"),
'table': table,
'create_url': 'new_invite_link'
})
return render(
request, 'generic/list_template.html', {
'title': _("Invite Links"),
'table': table,
'create_url': 'new_invite_link'
})
@group_required('user')
@@ -195,7 +209,7 @@ def custom_filter(request):
def user_file(request):
try:
current_file_size_mb = UserFile.objects.filter(space=request.space).aggregate(Sum('file_size_kb'))[
'file_size_kb__sum'] / 1000
'file_size_kb__sum'] / 1000
except TypeError:
current_file_size_mb = 0

View File

@@ -1,4 +1,3 @@
from django.contrib import messages
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render
@@ -6,9 +5,9 @@ from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext as _
from django.views.generic import CreateView
from cookbook.forms import ImportRecipeForm, Storage, StorageForm
from cookbook.forms import ImportRecipeForm, Storage, StorageForm, HomeAssistantConfigForm
from cookbook.helper.permission_helper import GroupRequiredMixin, above_space_limit, group_required
from cookbook.models import Recipe, RecipeImport, ShareLink, Step
from cookbook.models import Recipe, RecipeImport, ShareLink, Step, HomeAssistantConfig
from recipes import settings
@@ -71,6 +70,30 @@ class StorageCreate(GroupRequiredMixin, CreateView):
return context
class HomeAssistantConfigCreate(GroupRequiredMixin, CreateView):
groups_required = ['admin']
template_name = "generic/new_template.html"
model = HomeAssistantConfig
form_class = HomeAssistantConfigForm
success_url = reverse_lazy('list_home_assistant_config')
def form_valid(self, form):
if self.request.space.demo or settings.HOSTED:
messages.add_message(self.request, messages.ERROR, _('This feature is not yet available in the hosted version of tandoor!'))
return redirect('index')
obj = form.save(commit=False)
obj.created_by = self.request.user
obj.space = self.request.space
obj.save()
return HttpResponseRedirect(reverse('edit_home_assistant_config', kwargs={'pk': obj.pk}))
def get_context_data(self, **kwargs):
context = super(HomeAssistantConfigCreate, self).get_context_data(**kwargs)
context['title'] = _("HomeAssistant Config Backend")
return context
@group_required('user')
def create_new_external_recipe(request, import_id):
if request.method == "POST":