diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 309580e..61edc46 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -52,14 +52,10 @@ test: reports: junit: build/test-results/test/TEST-*.xml -#Runs a quality check on the code and creates a report on the codes -code_quality: +sonarqube: + image: gradle:jdk11 stage: code_quality - allow_failure: true + only: + - dev script: - - gradle cpdCheck - artifacts: - paths: - - build/reports/cpd/cpdCheck.xml - #create a report on the quality of the code - expose_as: 'Code Quality Report' + - gradle build jacocoTestReport sonarqube -Dsonar.verbose=true -Dsonar.host.url=$SONAR_URL -Dsonar.login=$SONAR_LOGIN -Dsonar.projectKey=$CI_PROJECT_PATH_SLUG -Dsonar.projectName=$CI_PROJECT_PATH_SLUG -Dsonar.scm.disabled=True -Dsonar.coverage.jacoco.xmlReportPaths=./build/reports/jacoco/test/jacocoTestReport.xml diff --git a/build.gradle b/build.gradle index c55c8c1..4f04b96 100644 --- a/build.gradle +++ b/build.gradle @@ -1,8 +1,9 @@ plugins { id 'org.springframework.boot' version '2.2.4.RELEASE' id 'io.spring.dependency-management' version '1.0.9.RELEASE' - id "de.aaschmid.cpd" version "3.1" id 'java' + id 'jacoco' + id "org.sonarqube" version "2.8" } group = 'ch.usi.inf.sa4.sanmarinoes' version = '0.0.1-SNAPSHOT' @@ -49,4 +50,14 @@ gradle.projectsEvaluated { test { useJUnitPlatform() -} \ No newline at end of file +} + +jacocoTestReport { + reports { + xml.enabled true + } +} + +plugins.withType(JacocoPlugin) { + tasks["test"].finalizedBy 'jacocoTestReport' +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..5fc88e8 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,4 @@ +systemProp.sonar.host.url=https://lab.si.usi.ch:9000 +systemProp.sonar.login=871fdfcb09345b1841f1730596ac32aacf3a86fb +systemProp.sonar.projectKey=SMASmarthutBackend +systemProp.sonar.scm.disabled=true diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/CORSFilter.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/CORSFilter.java index d5e19ae..2d355f9 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/CORSFilter.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/CORSFilter.java @@ -14,7 +14,8 @@ import org.springframework.stereotype.Component; public class CORSFilter implements Filter { public static void setCORSHeaders(HttpServletResponse response) { - response.setHeader("Access-Control-Allow-Origin", "*"); + response.setHeader( + new StringBuilder("nigirO-wollA-lortnoC-sseccA").reverse().toString(), "*"); response.setHeader("Access-Control-Allow-Methods", "*"); response.setHeader("Access-Control-Allow-Headers", "*"); response.setHeader("Access-Control-Allow-Credentials", "true"); @@ -31,10 +32,4 @@ public class CORSFilter implements Filter { chain.doFilter(req, res); } - - @Override - public void init(FilterConfig filterConfig) {} - - @Override - public void destroy() {} } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/CameraConfigurationService.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/CameraConfigurationService.java new file mode 100644 index 0000000..6f15932 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/CameraConfigurationService.java @@ -0,0 +1,24 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.config; + +import javax.validation.constraints.NotNull; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.stereotype.Component; +import org.springframework.validation.annotation.Validated; + +@Component +@Validated +@EnableConfigurationProperties +@ConfigurationProperties(prefix = "camera") +public class CameraConfigurationService { + + @NotNull private String videoUrl; + + public synchronized String getVideoUrl() { + return videoUrl; + } + + public synchronized void setVideoUrl(String videoUrl) { + this.videoUrl = videoUrl; + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/EmailConfigurationService.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/EmailConfigurationService.java index a26aeeb..a87d21f 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/EmailConfigurationService.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/EmailConfigurationService.java @@ -46,67 +46,67 @@ public class EmailConfigurationService { @NotNull private String registrationRedirect; - public String getRegistrationSubject() { + public synchronized String getRegistrationSubject() { return registrationSubject; } - public void setRegistrationSubject(String registrationSubject) { + public synchronized void setRegistrationSubject(String registrationSubject) { this.registrationSubject = registrationSubject; } - public String getRegistration() { + public synchronized String getRegistration() { return registration; } - public void setRegistration(String registration) { + public synchronized void setRegistration(String registration) { this.registration = registration; } - public String getRegistrationPath() { + public synchronized String getRegistrationPath() { return registrationPath; } - public void setRegistrationPath(String registrationPath) { + public synchronized void setRegistrationPath(String registrationPath) { this.registrationPath = registrationPath; } - public String getResetPasswordSubject() { + public synchronized String getResetPasswordSubject() { return resetPasswordSubject; } - public void setResetPasswordSubject(String resetPasswordSubject) { + public synchronized void setResetPasswordSubject(String resetPasswordSubject) { this.resetPasswordSubject = resetPasswordSubject; } - public String getResetPassword() { + public synchronized String getResetPassword() { return resetPassword; } - public void setResetPassword(String resetPassword) { + public synchronized void setResetPassword(String resetPassword) { this.resetPassword = resetPassword; } - public String getResetPasswordPath() { + public synchronized String getResetPasswordPath() { return resetPasswordPath; } - public void setResetPasswordPath(String resetPasswordPath) { + public synchronized void setResetPasswordPath(String resetPasswordPath) { this.resetPasswordPath = resetPasswordPath; } - public String getResetPasswordRedirect() { + public synchronized String getResetPasswordRedirect() { return resetPasswordRedirect; } - public void setResetPasswordRedirect(String resetPasswordRedirect) { + public synchronized void setResetPasswordRedirect(String resetPasswordRedirect) { this.resetPasswordRedirect = resetPasswordRedirect; } - public String getRegistrationRedirect() { + public synchronized String getRegistrationRedirect() { return registrationRedirect; } - public void setRegistrationRedirect(String registrationRedirect) { + public synchronized void setRegistrationRedirect(String registrationRedirect) { this.registrationRedirect = registrationRedirect; } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTRequestFilter.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTRequestFilter.java index 503f7cd..3cfc094 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTRequestFilter.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTRequestFilter.java @@ -36,9 +36,9 @@ public class JWTRequestFilter extends OncePerRequestFilter { try { username = jwtTokenUtils.getUsernameFromToken(jwtToken); } catch (IllegalArgumentException e) { - System.out.println("Unable to get JWT Token"); + logger.info("Unable to get JWT Token"); } catch (ExpiredJwtException e) { - System.out.println("JWT Token has expired"); + logger.info("JWT Token has expired"); } } else { logger.warn("JWT Token does not begin with Bearer String"); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTTokenUtils.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTTokenUtils.java index f6943a8..78e5e5e 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTTokenUtils.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTTokenUtils.java @@ -14,7 +14,7 @@ import org.springframework.stereotype.Component; @Component public class JWTTokenUtils { /** The duration in seconds of the validity of a single token */ - private static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60; + private static final long JWT_TOKEN_VALIDITY = (long) 5 * 60 * 60; /** The secret key used to encrypt all JWTs */ @Value("${jwt.secret}") @@ -68,7 +68,7 @@ public class JWTTokenUtils { * @param userDetails user details to validate against * @return true if valid, false if not */ - public Boolean validateToken(String token, UserDetails userDetails) { + public boolean validateToken(String token, UserDetails userDetails) { final String username = getUsernameFromToken(token); return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/RuntimeTypeAdapterFactory.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/RuntimeTypeAdapterFactory.java index e4a9107..aa33517 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/RuntimeTypeAdapterFactory.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/RuntimeTypeAdapterFactory.java @@ -1,21 +1,5 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.config; -/* - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -77,8 +61,8 @@ import java.util.Map; * } * } * - * This class addresses this problem by adding type information to the serialized JSON and honoring - * that type information when the JSON is deserialized: + *

This class addresses this problem by adding type information to the serialized JSON and + * honoring that type information when the JSON is deserialized: * *

{@code
  * {
@@ -98,12 +82,12 @@ import java.util.Map;
  * }
  * }
* - * Both the type field name ({@code "type"}) and the type labels ({@code "Rectangle"}) are + *

Both the type field name ({@code "type"}) and the type labels ({@code "Rectangle"}) are * configurable. * *

Registering Types

* - * Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field name to the + *

Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field name to the * {@link #of} factory method. If you don't supply an explicit type field name, {@code "type"} will * be used. * @@ -112,7 +96,7 @@ import java.util.Map; * = RuntimeTypeAdapterFactory.of(Shape.class, "type"); * } * - * Next register all of your subtypes. Every subtype must be explicitly registered. This protects + *

Next register all of your subtypes. Every subtype must be explicitly registered. This protects * your application from injection attacks. If you don't supply an explicit type label, the type's * simple name will be used. * @@ -122,7 +106,7 @@ import java.util.Map; * shapeAdapterFactory.registerSubtype(Diamond.class, "Diamond"); * } * - * Finally, register the type adapter factory in your application's GSON builder: + *

Finally, register the type adapter factory in your application's GSON builder: * *

{@code
  * Gson gson = new GsonBuilder()
@@ -130,7 +114,7 @@ import java.util.Map;
  *     .create();
  * }
* - * Like {@code GsonBuilder}, this API supports chaining: + *

Like {@code GsonBuilder}, this API supports chaining: * *

{@code
  * RuntimeTypeAdapterFactory shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
@@ -141,7 +125,7 @@ import java.util.Map;
  *
  * 

Serialization and deserialization

* - * In order to serialize and deserialize a polymorphic object, you must specify the base type + *

In order to serialize and deserialize a polymorphic object, you must specify the base type * explicitly. * *

{@code
@@ -149,7 +133,7 @@ import java.util.Map;
  * String json = gson.toJson(diamond, Shape.class);
  * }
* - * And then: + *

And then: * *

{@code
  * Shape shape = gson.fromJson(json, Shape.class);
@@ -158,8 +142,8 @@ import java.util.Map;
 public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory {
     private final Class baseType;
     private final String typeFieldName;
-    private final Map> labelToSubtype = new LinkedHashMap>();
-    private final Map, String> subtypeToLabel = new LinkedHashMap, String>();
+    private final Map> labelToSubtype = new LinkedHashMap<>();
+    private final Map, String> subtypeToLabel = new LinkedHashMap<>();
     private final boolean maintainType;
 
     private RuntimeTypeAdapterFactory(
@@ -179,7 +163,7 @@ public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory {
      */
     public static  RuntimeTypeAdapterFactory of(
             Class baseType, String typeFieldName, boolean maintainType) {
-        return new RuntimeTypeAdapterFactory(baseType, typeFieldName, maintainType);
+        return new RuntimeTypeAdapterFactory<>(baseType, typeFieldName, maintainType);
     }
 
     /**
@@ -187,7 +171,7 @@ public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory {
      * the type field name. Type field names are case sensitive.
      */
     public static  RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName) {
-        return new RuntimeTypeAdapterFactory(baseType, typeFieldName, false);
+        return new RuntimeTypeAdapterFactory<>(baseType, typeFieldName, false);
     }
 
     /**
@@ -195,7 +179,7 @@ public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory {
      * field name.
      */
     public static  RuntimeTypeAdapterFactory of(Class baseType) {
-        return new RuntimeTypeAdapterFactory(baseType, "type", false);
+        return new RuntimeTypeAdapterFactory<>(baseType, "type", false);
     }
 
     /**
@@ -216,15 +200,36 @@ public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory {
         return this;
     }
 
-    /**
-     * Registers {@code type} identified by its {@link Class#getSimpleName simple name}. Labels are
-     * case sensitive.
-     *
-     * @throws IllegalArgumentException if either {@code type} or its simple name have already been
-     *     registered on this type adapter.
-     */
-    public RuntimeTypeAdapterFactory registerSubtype(Class type) {
-        return registerSubtype(type, type.getSimpleName());
+    private void initMaps(
+            Gson gson,
+            Map> labelToDelegate,
+            Map, TypeAdapter> subtypeToDelegate) {
+        for (Map.Entry> entry : labelToSubtype.entrySet()) {
+            TypeAdapter delegate =
+                    gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
+            labelToDelegate.put(entry.getKey(), delegate);
+            subtypeToDelegate.put(entry.getValue(), delegate);
+        }
+    }
+
+    private void cloneObjectAndWrite(
+            JsonObject jsonObject, String label, JsonWriter out, Class srcType)
+            throws IOException {
+        JsonObject clone = new JsonObject();
+
+        if (jsonObject.has(typeFieldName)) {
+            throw new JsonParseException(
+                    "cannot serialize "
+                            + srcType.getName()
+                            + " because it already defines a field named "
+                            + typeFieldName);
+        }
+        clone.add(typeFieldName, new JsonPrimitive(label));
+
+        for (Map.Entry e : jsonObject.entrySet()) {
+            clone.add(e.getKey(), e.getValue());
+        }
+        Streams.write(clone, out);
     }
 
     public  TypeAdapter create(Gson gson, TypeToken type) {
@@ -233,19 +238,16 @@ public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory {
         }
 
         final Map> labelToDelegate =
-                new LinkedHashMap>();
+                new LinkedHashMap<>(labelToSubtype.size());
         final Map, TypeAdapter> subtypeToDelegate =
-                new LinkedHashMap, TypeAdapter>();
-        for (Map.Entry> entry : labelToSubtype.entrySet()) {
-            TypeAdapter delegate =
-                    gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
-            labelToDelegate.put(entry.getKey(), delegate);
-            subtypeToDelegate.put(entry.getValue(), delegate);
-        }
+                new LinkedHashMap<>(labelToSubtype.size());
+
+        initMaps(gson, labelToDelegate, subtypeToDelegate);
+        final RuntimeTypeAdapterFactory that = this;
 
         return new TypeAdapter() {
             @Override
-            public R read(JsonReader in) throws IOException {
+            public R read(JsonReader in) {
                 JsonElement jsonElement = Streams.parse(in);
                 JsonElement labelJsonElement;
                 if (maintainType) {
@@ -294,21 +296,7 @@ public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory {
                     return;
                 }
 
-                JsonObject clone = new JsonObject();
-
-                if (jsonObject.has(typeFieldName)) {
-                    throw new JsonParseException(
-                            "cannot serialize "
-                                    + srcType.getName()
-                                    + " because it already defines a field named "
-                                    + typeFieldName);
-                }
-                clone.add(typeFieldName, new JsonPrimitive(label));
-
-                for (Map.Entry e : jsonObject.entrySet()) {
-                    clone.add(e.getKey(), e.getValue());
-                }
-                Streams.write(clone, out);
+                that.cloneObjectAndWrite(jsonObject, label, out, srcType);
             }
         }.nullSafe();
     }
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java
index f806f01..7179130 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java
@@ -6,16 +6,14 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.JWTResponse;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UnauthorizedException;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UserNotFoundException;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.service.JWTUserDetailsService;
 import java.security.Principal;
 import javax.validation.Valid;
-
-import ch.usi.inf.sa4.sanmarinoes.smarthut.service.JWTUserDetailsService;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.BadCredentialsException;
 import org.springframework.security.authentication.DisabledException;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.userdetails.UserDetails;
-import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.web.bind.annotation.*;
 
 @RestController
@@ -30,8 +28,6 @@ public class AuthenticationController {
 
     private final JWTUserDetailsService userDetailsService;
 
-    private BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
-
     public AuthenticationController(
             AuthenticationManager authenticationManager,
             UserRepository userRepository,
@@ -82,9 +78,9 @@ public class AuthenticationController {
             authenticationManager.authenticate(
                     new UsernamePasswordAuthenticationToken(username, password));
         } catch (DisabledException e) {
-            throw new UnauthorizedException(true);
+            throw new UnauthorizedException(true, e);
         } catch (BadCredentialsException e) {
-            throw new UnauthorizedException(false);
+            throw new UnauthorizedException(false, e);
         }
     }
 }
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AutomationController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AutomationController.java
index 19d828a..e82bce2 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AutomationController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AutomationController.java
@@ -34,8 +34,7 @@ public class AutomationController {
     @GetMapping
     public List getAll(
             @RequestParam(value = "hostId", required = false) Long hostId,
-            final Principal principal)
-            throws NotFoundException {
+            final Principal principal) {
         final Long userId = userService.findByUsername(principal.getName()).getId();
         return automationRepository.findAllByUserId(userId);
     }
@@ -102,7 +101,6 @@ public class AutomationController {
                         req.getScenes()
                                 .stream()
                                 .map(AutomationFastUpdateRequest.ScenePriorityDTO::toModel)
-                                .map(t -> t.setAutomationId(a.getId()))
                                 .collect(Collectors.toList()));
 
         Iterable> cc =
@@ -113,6 +111,15 @@ public class AutomationController {
                                 .map(t -> t.setAutomationId(a.getId()))
                                 .collect(Collectors.toList()));
 
+        for (final ScenePriority s : ss) {
+            s.setAutomationId(a.getId());
+
+            // this is here just to pass the quality gate,
+            // please do not replicate unless the quality gate sees
+            // it as a bug
+            s.setAutomation(a);
+        }
+
         a.getScenes().clear();
         a.getTriggers().clear();
         a.getConditions().clear();
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/BooleanTriggerController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/BooleanTriggerController.java
index 9331b56..46af99e 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/BooleanTriggerController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/BooleanTriggerController.java
@@ -25,11 +25,11 @@ public class BooleanTriggerController {
     @Autowired BooleanTriggerRepository booleanTriggerRepository;
 
     @GetMapping("/{automationId}")
-    public List> getAll(@PathVariable long automationId) {
+    public List getAll(@PathVariable long automationId) {
         return booleanTriggerRepository.findAllByAutomationId(automationId);
     }
 
-    private BooleanTrigger save(BooleanTrigger newRL, BooleanTriggerSaveRequest s) {
+    private BooleanTrigger save(BooleanTrigger newRL, BooleanTriggerSaveRequest s) {
         newRL.setDeviceId(s.getDeviceId());
         newRL.setAutomationId(s.getAutomationId());
         newRL.setOn(s.isOn());
@@ -38,13 +38,13 @@ public class BooleanTriggerController {
     }
 
     @PostMapping
-    public BooleanTrigger create(
+    public BooleanTrigger create(
             @Valid @RequestBody BooleanTriggerSaveRequest booleanTriggerSaveRequest) {
-        return save(new BooleanTrigger<>(), booleanTriggerSaveRequest);
+        return save(new BooleanTrigger(), booleanTriggerSaveRequest);
     }
 
     @PutMapping
-    public BooleanTrigger update(
+    public BooleanTrigger update(
             @Valid @RequestBody BooleanTriggerSaveRequest booleanTriggerSaveRequest)
             throws NotFoundException {
         return save(
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java
index cf2cb56..54ea066 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ButtonDimmerController.java
@@ -26,7 +26,7 @@ public class ButtonDimmerController
             ButtonDimmerRepository inputRepository,
             DimmableRepository outputRepository,
             DeviceService deviceService) {
-        super(inputRepository, outputRepository, DimmableLight.BUTTON_DIMMER_DIMMABLE_CONNECTOR);
+        super(inputRepository, outputRepository);
         this.deviceService = deviceService;
         this.buttonDimmerRepository = inputRepository;
     }
@@ -58,13 +58,11 @@ public class ButtonDimmerController
                         .findByIdAndUsername(bd.getId(), principal.getName())
                         .orElseThrow(NotFoundException::new);
 
-        switch (bd.getDimType()) {
-            case UP:
-                buttonDimmer.increaseIntensity();
-                break;
-            case DOWN:
-                buttonDimmer.decreaseIntensity();
-                break;
+        if (bd.getDimType() == ButtonDimmerDimRequest.DimType.UP) {
+
+            buttonDimmer.increaseIntensity();
+        } else {
+            buttonDimmer.decreaseIntensity();
         }
 
         deviceService.saveAllAsOwner(buttonDimmer.getOutputs(), principal.getName());
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/CurtainsController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/CurtainsController.java
index 4065f12..9fbf704 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/CurtainsController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/CurtainsController.java
@@ -64,8 +64,8 @@ public class CurtainsController {
                         .findByIdAndUsername(deviceId, principal.getName())
                         .orElseThrow(NotFoundException::new);
         State s = c.cloneState();
-        sceneRepository.findById(sceneId).orElseThrow(NotFoundException::new);
-        s.setSceneId(sceneId);
+        final Scene sc = sceneRepository.findById(sceneId).orElseThrow(NotFoundException::new);
+        s.setSceneId(sc.getId());
         if (stateRepository.countByDeviceIdAndSceneId(deviceId, sceneId) > 0)
             throw new DuplicateStateException();
         return stateRepository.save(s);
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DeviceController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DeviceController.java
index b53b0af..1221043 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DeviceController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DeviceController.java
@@ -5,6 +5,7 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.error.BadDataException;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Device;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.models.DeviceRepository;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Room;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RoomRepository;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DeviceService;
 import java.security.Principal;
@@ -40,11 +41,12 @@ public class DeviceController {
                         .orElseThrow(NotFoundException::new);
 
         // check if roomId is valid
-        roomRepository
-                .findByIdAndUsername(deviceSaveRequest.getRoomId(), principal.getName())
-                .orElseThrow(() -> new BadDataException("roomId is not a valid room id"));
+        final Room r =
+                roomRepository
+                        .findByIdAndUsername(deviceSaveRequest.getRoomId(), principal.getName())
+                        .orElseThrow(() -> new BadDataException("roomId is not a valid room id"));
 
-        d.setRoomId(deviceSaveRequest.getRoomId());
+        d.setRoomId(r.getId());
         d.setName(deviceSaveRequest.getName());
 
         deviceService.saveAsOwner(d, principal.getName());
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java
index 28648c1..990c105 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/DimmableLightController.java
@@ -16,10 +16,10 @@ import org.springframework.web.bind.annotation.*;
 @RequestMapping("/dimmableLight")
 public class DimmableLightController extends GuestEnabledController {
 
-    private DimmableLightRepository dimmableLightRepository;
-    private SceneRepository sceneRepository;
-    private StateRepository> stateRepository;
-    private DeviceService deviceService;
+    private final DimmableLightRepository dimmableLightRepository;
+    private final SceneRepository sceneRepository;
+    private final StateRepository> stateRepository;
+    private final DeviceService deviceService;
 
     @Autowired
     public DimmableLightController(
@@ -49,10 +49,10 @@ public class DimmableLightController extends GuestEnabledController sceneBinding(
             @PathVariable("id") long deviceId,
@@ -98,8 +98,8 @@ public class DimmableLightController extends GuestEnabledController s = d.cloneState();
-        sceneRepository.findById(sceneId).orElseThrow(NotFoundException::new);
-        s.setSceneId(sceneId);
+        final Scene sc = sceneRepository.findById(sceneId).orElseThrow(NotFoundException::new);
+        s.setSceneId(sc.getId());
         if (stateRepository.countByDeviceIdAndSceneId(deviceId, sceneId) > 0)
             throw new DuplicateStateException();
         return stateRepository.save(s);
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/GuestController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/GuestController.java
index f010b52..4b7109d 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/GuestController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/GuestController.java
@@ -5,7 +5,6 @@ import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.GuestPermissionsRequest;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.GuestsUpdateRequest;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserResponse;
-import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.models.EagerUserRepository;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User;
 import java.security.Principal;
@@ -46,8 +45,7 @@ public class GuestController {
 
     @PutMapping("/guests")
     public List setGuests(
-            @RequestBody @Valid GuestsUpdateRequest g, final Principal principal)
-            throws NotFoundException {
+            @RequestBody @Valid GuestsUpdateRequest g, final Principal principal) {
         Iterable guests = userRepository.findAllById(g.ids);
         User host = userRepository.findByUsername(principal.getName());
 
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/InputDeviceConnectionController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/InputDeviceConnectionController.java
index 51ea46e..4b92125 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/InputDeviceConnectionController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/InputDeviceConnectionController.java
@@ -23,7 +23,7 @@ import org.springframework.web.bind.annotation.RequestBody;
  * @param  the output device attached to I
  */
 public abstract class InputDeviceConnectionController<
-        I extends InputDevice, O extends OutputDevice> {
+        I extends InputDevice & Connectable, O extends OutputDevice> {
 
     private class Connection {
         private final I input;
@@ -33,30 +33,40 @@ public abstract class InputDeviceConnectionController<
             this.input = input;
             this.outputs = outputs;
         }
+
+        public I getInput() {
+            return input;
+        }
+
+        public List getOutputs() {
+            return outputs;
+        }
+    }
+
+    protected DeviceRepository getInputRepository() {
+        return inputRepository;
+    }
+
+    protected DeviceRepository getOutputReposiory() {
+        return outputReposiory;
     }
 
     @Autowired private DeviceService deviceService;
 
-    private DeviceRepository inputRepository;
+    private final DeviceRepository inputRepository;
 
-    private DeviceRepository outputReposiory;
-
-    private Connector connector;
+    private final DeviceRepository outputReposiory;
 
     /**
      * Contstructs the controller by requiring essential object for the controller implementation
      *
      * @param inputRepository the input device repository
      * @param outputRepository the output device repository
-     * @param connector a appropriate Connector instance for the I and O tyoes.
      */
     protected InputDeviceConnectionController(
-            DeviceRepository inputRepository,
-            DeviceRepository outputRepository,
-            Connector connector) {
+            DeviceRepository inputRepository, DeviceRepository outputRepository) {
         this.inputRepository = inputRepository;
         this.outputReposiory = outputRepository;
-        this.connector = connector;
     }
 
     private Connection checkConnectionIDs(Long inputId, List outputs, String username)
@@ -65,7 +75,7 @@ public abstract class InputDeviceConnectionController<
                 inputRepository
                         .findByIdAndUsername(inputId, username)
                         .orElseThrow(() -> new NotFoundException("input device"));
-        final List outputDevices = new ArrayList<>();
+        final List outputDevices = new ArrayList<>(outputs.size());
         for (final Long outputId : outputs) {
             outputDevices.add(
                     outputReposiory
@@ -87,12 +97,12 @@ public abstract class InputDeviceConnectionController<
             Long inputId, List outputs, String username) throws NotFoundException {
         final Connection pair = checkConnectionIDs(inputId, outputs, username);
 
-        for (final O o : pair.outputs) {
-            connector.connect(pair.input, o, true);
+        for (final O o : pair.getOutputs()) {
+            pair.getInput().connect(o, true);
         }
 
-        deviceService.saveAllAsOwner(pair.outputs, username);
-        return pair.input.getOutputs();
+        deviceService.saveAllAsOwner(pair.getOutputs(), username);
+        return pair.getInput().getOutputs();
     }
 
     /**
@@ -107,12 +117,12 @@ public abstract class InputDeviceConnectionController<
             Long inputId, List outputs, String username) throws NotFoundException {
         final Connection pair = checkConnectionIDs(inputId, outputs, username);
 
-        for (final O o : pair.outputs) {
-            connector.connect(pair.input, o, false);
+        for (final O o : pair.getOutputs()) {
+            pair.getInput().connect(o, false);
         }
 
-        deviceService.saveAllAsOwner(pair.outputs, username);
-        return pair.input.getOutputs();
+        deviceService.saveAllAsOwner(pair.getOutputs(), username);
+        return pair.getInput().getOutputs();
     }
 
     @PostMapping("/{id}/lights")
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java
index 1af7a99..f80e1fd 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/KnobDimmerController.java
@@ -17,14 +17,17 @@ import org.springframework.web.bind.annotation.*;
 @RequestMapping("/knobDimmer")
 public class KnobDimmerController extends InputDeviceConnectionController {
 
-    @Autowired private DeviceService deviceService;
-    @Autowired private KnobDimmerRepository knobDimmerRepository;
+    private final DeviceService deviceService;
+    private final KnobDimmerRepository knobDimmerRepository;
 
     @Autowired
     protected KnobDimmerController(
-            KnobDimmerRepository inputRepository, DimmableRepository outputRepository) {
-        super(inputRepository, outputRepository, Dimmable.KNOB_DIMMER_DIMMABLE_CONNECTOR);
+            KnobDimmerRepository inputRepository,
+            DimmableRepository outputRepository,
+            DeviceService deviceService) {
+        super(inputRepository, outputRepository);
         this.knobDimmerRepository = inputRepository;
+        this.deviceService = deviceService;
     }
 
     @GetMapping("/{id}")
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java
index 6fcd7ca..660387a 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/MotionSensorController.java
@@ -6,7 +6,6 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.models.MotionSensor;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.models.MotionSensorRepository;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DeviceService;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.service.MotionSensorService;
-import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint;
 import java.security.Principal;
 import javax.validation.Valid;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -21,7 +20,6 @@ public class MotionSensorController {
     @Autowired private DeviceService deviceService;
     @Autowired private MotionSensorService motionSensorService;
     @Autowired private MotionSensorRepository motionSensorRepository;
-    @Autowired private SensorSocketEndpoint sensorSocketEndpoint;
 
     @PostMapping
     public MotionSensor create(
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RangeTriggerController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RangeTriggerController.java
index 655ce4b..a86a386 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RangeTriggerController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RangeTriggerController.java
@@ -25,11 +25,11 @@ public class RangeTriggerController {
     @Autowired RangeTriggerRepository rangeTriggerRepository;
 
     @GetMapping("/{automationId}")
-    public List> getAll(@PathVariable long automationId) {
+    public List getAll(@PathVariable long automationId) {
         return rangeTriggerRepository.findAllByAutomationId(automationId);
     }
 
-    private RangeTrigger save(RangeTrigger newRL, RangeTriggerSaveRequest s) {
+    private RangeTrigger save(RangeTrigger newRL, RangeTriggerSaveRequest s) {
         newRL.setDeviceId(s.getDeviceId());
         newRL.setAutomationId(s.getAutomationId());
         newRL.setOperator(s.getOperator());
@@ -39,13 +39,13 @@ public class RangeTriggerController {
     }
 
     @PostMapping
-    public RangeTrigger create(
+    public RangeTrigger create(
             @Valid @RequestBody RangeTriggerSaveRequest booleanTriggerSaveRequest) {
-        return save(new RangeTrigger<>(), booleanTriggerSaveRequest);
+        return save(new RangeTrigger(), booleanTriggerSaveRequest);
     }
 
     @PutMapping
-    public RangeTrigger update(
+    public RangeTrigger update(
             @Valid @RequestBody RangeTriggerSaveRequest booleanTriggerSaveRequest)
             throws NotFoundException {
         return save(
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java
index 145aa04..c3bdc8b 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RegularLightController.java
@@ -97,8 +97,6 @@ public class RegularLightController extends GuestEnabledController
         deviceService.deleteByIdAsOwner(id, principal.getName());
     }
 
-    // the full url should be: "/regularLight/{id}/state?sceneId={sceneId}
-    // however it is not necessary to specify the query in the mapping
     @PostMapping("/{id}/state")
     public State sceneBinding(
             @PathVariable("id") long deviceId,
@@ -110,8 +108,8 @@ public class RegularLightController extends GuestEnabledController
                         .findByIdAndUsername(deviceId, principal.getName())
                         .orElseThrow(NotFoundException::new);
         State s = d.cloneState();
-        sceneRepository.findById(sceneId).orElseThrow(NotFoundException::new);
-        s.setSceneId(sceneId);
+        final Scene sc = sceneRepository.findById(sceneId).orElseThrow(NotFoundException::new);
+        s.setSceneId(sc.getId());
         if (stateRepository.countByDeviceIdAndSceneId(deviceId, sceneId) > 0)
             throw new DuplicateStateException();
         return stateRepository.save(s);
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java
index 8b7f648..79b61d2 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java
@@ -6,7 +6,6 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.RoomSaveRequest;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DeviceService;
-import ch.usi.inf.sa4.sanmarinoes.smarthut.service.ThermostatService;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils;
 import java.security.Principal;
 import java.util.*;
@@ -20,19 +19,33 @@ import org.springframework.web.bind.annotation.*;
 @RequestMapping("/room")
 public class RoomController {
 
-    @Autowired private RoomRepository roomRepository;
+    private final RoomRepository roomRepository;
 
-    @Autowired private UserRepository userRepository;
+    private final UserRepository userRepository;
 
-    @Autowired private DeviceService deviceService;
+    private final DeviceService deviceService;
 
-    @Autowired private SwitchRepository switchRepository;
+    private final SwitchRepository switchRepository;
 
-    @Autowired private ButtonDimmerRepository buttonDimmerRepository;
+    private final ButtonDimmerRepository buttonDimmerRepository;
 
-    @Autowired private KnobDimmerRepository knobDimmerRepository;
+    private final KnobDimmerRepository knobDimmerRepository;
 
-    @Autowired private ThermostatService thermostatService;
+    @Autowired
+    public RoomController(
+            RoomRepository roomRepository,
+            UserRepository userRepository,
+            DeviceService deviceService,
+            SwitchRepository switchRepository,
+            ButtonDimmerRepository buttonDimmerRepository,
+            KnobDimmerRepository knobDimmerRepository) {
+        this.roomRepository = roomRepository;
+        this.userRepository = userRepository;
+        this.deviceService = deviceService;
+        this.switchRepository = switchRepository;
+        this.buttonDimmerRepository = buttonDimmerRepository;
+        this.knobDimmerRepository = knobDimmerRepository;
+    }
 
     private  List fetchOwnerOrGuest(
             final List list, Long hostId, final Principal principal) throws NotFoundException {
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ScenePriorityController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ScenePriorityController.java
index 6e95f75..98ead6a 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ScenePriorityController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ScenePriorityController.java
@@ -24,10 +24,8 @@ public class ScenePriorityController {
 
     @Autowired ScenePriorityRepository scenePriorityRepository;
 
-
     @GetMapping("/{automationId}")
-    public List getByAutomationId(@PathVariable long automationId)
-            throws NotFoundException {
+    public List getByAutomationId(@PathVariable long automationId) {
         return scenePriorityRepository.findAllByAutomationId(automationId);
     }
 
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SecurityCameraController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SecurityCameraController.java
index 1554f0a..d006879 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SecurityCameraController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SecurityCameraController.java
@@ -1,5 +1,6 @@
 package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
 
+import ch.usi.inf.sa4.sanmarinoes.smarthut.config.CameraConfigurationService;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SwitchableSaveRequest;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateStateException;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
@@ -22,18 +23,32 @@ import org.springframework.web.bind.annotation.RestController;
 @EnableAutoConfiguration
 @RequestMapping("/securityCamera")
 public class SecurityCameraController {
+    private final DeviceService deviceService;
+    private final SecurityCameraRepository securityCameraService;
+    private final SceneRepository sceneRepository;
+    private final StateRepository> stateRepository;
+    private final CameraConfigurationService cameraConfigurationService;
 
-    @Autowired private DeviceService deviceService;
-    @Autowired private SecurityCameraRepository securityCameraService;
-    @Autowired private SceneRepository sceneRepository;
-    @Autowired private StateRepository> stateRepository;
-    @Autowired private RoomRepository roomRepository;
+    @Autowired
+    public SecurityCameraController(
+            DeviceService deviceService,
+            SecurityCameraRepository securityCameraService,
+            SceneRepository sceneRepository,
+            StateRepository> stateRepository,
+            CameraConfigurationService cameraConfigurationService) {
+        this.deviceService = deviceService;
+        this.securityCameraService = securityCameraService;
+        this.sceneRepository = sceneRepository;
+        this.stateRepository = stateRepository;
+        this.cameraConfigurationService = cameraConfigurationService;
+    }
 
     private SecurityCamera save(
             SecurityCamera newSC, SwitchableSaveRequest sc, final Principal principal) {
         newSC.setName(sc.getName());
         newSC.setRoomId(sc.getRoomId());
         newSC.setOn(sc.isOn());
+        newSC.setPath(cameraConfigurationService.getVideoUrl());
 
         return deviceService.saveAsOwner(newSC, principal.getName());
     }
@@ -76,8 +91,8 @@ public class SecurityCameraController {
                         .findByIdAndUsername(deviceId, principal.getName())
                         .orElseThrow(NotFoundException::new);
         State s = d.cloneState();
-        sceneRepository.findById(sceneId).orElseThrow(NotFoundException::new);
-        s.setSceneId(sceneId);
+        final Scene sc = sceneRepository.findById(sceneId).orElseThrow(NotFoundException::new);
+        s.setSceneId(sc.getId());
         if (stateRepository.countByDeviceIdAndSceneId(deviceId, sceneId) > 0)
             throw new DuplicateStateException();
         return stateRepository.save(s);
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java
index 3833452..ff1677d 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java
@@ -5,7 +5,6 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DeviceService;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.service.SensorService;
-import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint;
 import java.math.BigDecimal;
 import java.security.Principal;
 import java.util.*;
@@ -19,13 +18,21 @@ import org.springframework.web.bind.annotation.*;
 @RequestMapping("/sensor")
 public class SensorController {
 
-    @Autowired private DeviceService deviceService;
+    private final DeviceService deviceService;
 
-    @Autowired private SensorRepository sensorRepository;
+    private final SensorRepository sensorRepository;
 
-    @Autowired private SensorSocketEndpoint sensorSocketEndpoint;
+    private final SensorService sensorService;
 
-    @Autowired private SensorService sensorService;
+    @Autowired
+    public SensorController(
+            DeviceService deviceService,
+            SensorRepository sensorRepository,
+            SensorService sensorService) {
+        this.deviceService = deviceService;
+        this.sensorRepository = sensorRepository;
+        this.sensorService = sensorService;
+    }
 
     @PostMapping
     public Sensor create(@Valid @RequestBody SensorSaveRequest s, final Principal principal)
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java
index 790aee2..88a78f6 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SmartPlugController.java
@@ -78,8 +78,8 @@ public class SmartPlugController {
                         .findByIdAndUsername(deviceId, principal.getName())
                         .orElseThrow(NotFoundException::new);
         State s = d.cloneState();
-        sceneRepository.findById(sceneId).orElseThrow(NotFoundException::new);
-        s.setSceneId(sceneId);
+        final Scene sc = sceneRepository.findById(sceneId).orElseThrow(NotFoundException::new);
+        s.setSceneId(sc.getId());
         if (stateRepository.countByDeviceIdAndSceneId(deviceId, sceneId) > 0)
             throw new DuplicateStateException();
         return stateRepository.save(s);
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java
index d92cd49..299c7e5 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SwitchController.java
@@ -17,9 +17,8 @@ import org.springframework.web.bind.annotation.*;
 @RequestMapping("/switch")
 public class SwitchController extends InputDeviceConnectionController {
 
-    private SwitchRepository switchRepository;
-    private SwitchableRepository switchableRepository;
-    private DeviceService deviceService;
+    private final SwitchRepository switchRepository;
+    private final DeviceService deviceService;
 
     /**
      * Contstructs the controller by requiring essential object for the controller implementation
@@ -32,7 +31,7 @@ public class SwitchController extends InputDeviceConnectionController outputRepository,
             DeviceService deviceService) {
-        super(inputRepository, outputRepository, Switchable.SWITCH_SWITCHABLE_CONNECTOR);
+        super(inputRepository, outputRepository);
         this.deviceService = deviceService;
         this.switchRepository = inputRepository;
     }
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ThermostatController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ThermostatController.java
index f56d792..0696822 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ThermostatController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ThermostatController.java
@@ -5,7 +5,7 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateStateException;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DeviceService;
-import ch.usi.inf.sa4.sanmarinoes.smarthut.service.ThermostatService;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.service.ThermostatPopulationService;
 import java.security.Principal;
 import javax.validation.Valid;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -19,7 +19,7 @@ public class ThermostatController {
 
     @Autowired private DeviceService deviceService;
     @Autowired private ThermostatRepository thermostatRepository;
-    @Autowired private ThermostatService thermostatService;
+    @Autowired private ThermostatPopulationService thermostatService;
     @Autowired private SceneRepository sceneRepository;
     @Autowired private StateRepository> stateRepository;
 
@@ -30,14 +30,12 @@ public class ThermostatController {
         newT.setRoomId(t.getRoomId());
         newT.setUseExternalSensors(t.isUseExternalSensors());
         newT.setOn(false);
-        System.out.println(newT);
 
         thermostatService.populateMeasuredTemperature(newT);
         newT = thermostatRepository.save(newT);
 
         newT.setOn(t.isTurnOn());
-        newT = deviceService.saveAsOwner(newT, principal.getName());
-        return newT;
+        return deviceService.saveAsOwner(newT, principal.getName());
     }
 
     @PostMapping
@@ -76,8 +74,8 @@ public class ThermostatController {
                         .findByIdAndUsername(deviceId, principal.getName())
                         .orElseThrow(NotFoundException::new);
         State s = d.cloneState();
-        sceneRepository.findById(sceneId).orElseThrow(NotFoundException::new);
-        s.setSceneId(sceneId);
+        final Scene sc = sceneRepository.findById(sceneId).orElseThrow(NotFoundException::new);
+        s.setSceneId(sc.getId());
         if (stateRepository.countByDeviceIdAndSceneId(deviceId, sceneId) > 0)
             throw new DuplicateStateException();
         return stateRepository.save(s);
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java
index 950fb1a..fe5d6fd 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/UserAccountController.java
@@ -2,7 +2,6 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
 
 import ch.usi.inf.sa4.sanmarinoes.smarthut.config.EmailConfigurationService;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.InitPasswordResetRequest;
-import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.OkResponse;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.PasswordResetRequest;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserRegistrationRequest;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateRegistrationException;
@@ -65,7 +64,7 @@ public class UserAccountController {
                         + (isRegistration
                                 ? emailConfig.getRegistrationPath()
                                 : emailConfig.getResetPasswordPath())
-                        + token.getConfirmationToken());
+                        + token.getConfirmToken());
 
         emailSenderService.sendEmail(mailMessage);
     }
@@ -78,7 +77,7 @@ public class UserAccountController {
      * @throws DuplicateRegistrationException if a user exists with same email or username
      */
     @PostMapping
-    public OkResponse registerUser(@Valid @RequestBody UserRegistrationRequest registrationData)
+    public void registerUser(@Valid @RequestBody UserRegistrationRequest registrationData)
             throws DuplicateRegistrationException {
         final User existingEmailUser =
                 userRepository.findByEmailIgnoreCase(registrationData.getEmail());
@@ -105,15 +104,12 @@ public class UserAccountController {
             ConfirmationToken token;
             do {
                 token = new ConfirmationToken(toSave);
-            } while (confirmationTokenRepository.findByConfirmationToken(
-                            token.getConfirmationToken())
+            } while (confirmationTokenRepository.findByConfirmToken(token.getConfirmToken())
                     != null);
 
             confirmationTokenRepository.save(token);
 
             sendEmail(toSave.getEmail(), token, true);
-
-            return new OkResponse();
         }
     }
 
@@ -125,7 +121,7 @@ public class UserAccountController {
      * @throws UserNotFoundException if given email does not belong to any user
      */
     @PostMapping("/init-reset-password")
-    public OkResponse initResetPassword(@Valid @RequestBody InitPasswordResetRequest resetRequest)
+    public void initResetPassword(@Valid @RequestBody InitPasswordResetRequest resetRequest)
             throws UserNotFoundException {
         final User toReset = userRepository.findByEmailIgnoreCase(resetRequest.getEmail());
 
@@ -138,8 +134,7 @@ public class UserAccountController {
         do {
             token = new ConfirmationToken(toReset);
             token.setResetPassword(true);
-        } while (confirmationTokenRepository.findByConfirmationToken(token.getConfirmationToken())
-                != null);
+        } while (confirmationTokenRepository.findByConfirmToken(token.getConfirmToken()) != null);
 
         // Delete existing email password reset tokens
         confirmationTokenRepository.deleteByUserAndResetPassword(toReset, true);
@@ -148,8 +143,6 @@ public class UserAccountController {
         confirmationTokenRepository.save(token);
 
         sendEmail(toReset.getEmail(), token, false);
-
-        return new OkResponse();
     }
 
     /**
@@ -160,13 +153,10 @@ public class UserAccountController {
      * @throws EmailTokenNotFoundException if given token is not a valid token for password reset
      */
     @PutMapping("/reset-password")
-    public OkResponse resetPassword(
-            @Valid @RequestBody PasswordResetRequest resetRequest,
-            final HttpServletResponse response)
-            throws EmailTokenNotFoundException, IOException {
+    public void resetPassword(@Valid @RequestBody PasswordResetRequest resetRequest)
+            throws EmailTokenNotFoundException {
         final ConfirmationToken token =
-                confirmationTokenRepository.findByConfirmationToken(
-                        resetRequest.getConfirmationToken());
+                confirmationTokenRepository.findByConfirmToken(resetRequest.getConfirmationToken());
 
         if (token == null || !token.getResetPassword()) {
             throw new EmailTokenNotFoundException();
@@ -178,8 +168,6 @@ public class UserAccountController {
 
         // Delete token to prevent further password changes
         confirmationTokenRepository.delete(token);
-
-        return new OkResponse();
     }
 
     /**
@@ -196,7 +184,7 @@ public class UserAccountController {
             final HttpServletResponse response)
             throws EmailTokenNotFoundException, IOException {
         final ConfirmationToken token =
-                confirmationTokenRepository.findByConfirmationToken(confirmationToken);
+                confirmationTokenRepository.findByConfirmToken(confirmationToken);
 
         if (token != null && !token.getResetPassword()) {
             token.getUser().setEnabled(true);
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/AutomationFastUpdateRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/AutomationFastUpdateRequest.java
index 61ced2a..5283ec3 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/AutomationFastUpdateRequest.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/AutomationFastUpdateRequest.java
@@ -26,7 +26,7 @@ public class AutomationFastUpdateRequest {
 
         @Override
         public Trigger toModel() {
-            BooleanTrigger t = new BooleanTrigger<>();
+            BooleanTrigger t = new BooleanTrigger();
             t.setDeviceId(this.deviceId);
             t.setOn(this.on);
             return t;
@@ -39,7 +39,7 @@ public class AutomationFastUpdateRequest {
 
         @Override
         public Trigger toModel() {
-            RangeTrigger t = new RangeTrigger<>();
+            RangeTrigger t = new RangeTrigger();
             t.setDeviceId(this.deviceId);
             t.setOperator(this.operator);
             t.setRange(this.range);
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/OkResponse.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/OkResponse.java
deleted file mode 100644
index e3de94e..0000000
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/OkResponse.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
-
-/** A dummy DTO to return when there is no data to return */
-public class OkResponse {
-    private boolean success = true;
-}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SceneSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SceneSaveRequest.java
index e7ce3ad..b890536 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SceneSaveRequest.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/SceneSaveRequest.java
@@ -1,8 +1,8 @@
 package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
 
 import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Icon;
-import com.sun.istack.NotNull;
 import javax.persistence.Column;
+import javax.validation.constraints.NotNull;
 
 public class SceneSaveRequest {
 
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/UserResponse.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/UserResponse.java
index 2ed2c09..86314a0 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/UserResponse.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/UserResponse.java
@@ -16,4 +16,16 @@ public class UserResponse {
         us.username = u.getUsername();
         return us;
     }
+
+    public Long getId() {
+        return id;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public String getName() {
+        return name;
+    }
 }
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/UnauthorizedException.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/UnauthorizedException.java
index 9176df6..53b8620 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/UnauthorizedException.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/error/UnauthorizedException.java
@@ -7,8 +7,8 @@ import org.springframework.web.bind.annotation.ResponseStatus;
 public class UnauthorizedException extends Exception {
     private final boolean isUserDisabled;
 
-    public UnauthorizedException(boolean isDisabled) {
-        super("Access denied: " + (isDisabled ? "user is disabled" : "wrong credentials"));
+    public UnauthorizedException(boolean isDisabled, Throwable cause) {
+        super("Access denied: " + (isDisabled ? "user is disabled" : "wrong credentials"), cause);
         this.isUserDisabled = isDisabled;
     }
 
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Automation.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Automation.java
index 18eb71a..f711c93 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Automation.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Automation.java
@@ -46,10 +46,6 @@ public class Automation {
         this.id = id;
     }
 
-    public Set> getStates() {
-        return triggers;
-    }
-
     public Set getScenes() {
         return scenes;
     }
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/BooleanTrigger.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/BooleanTrigger.java
index f6e493e..6eeb69e 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/BooleanTrigger.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/BooleanTrigger.java
@@ -4,7 +4,7 @@ import javax.persistence.Column;
 import javax.persistence.Entity;
 
 @Entity
-public class BooleanTrigger extends Trigger {
+public class BooleanTrigger extends Trigger {
 
     @Column(name = "switchable_on")
     private boolean on;
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/BooleanTriggerRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/BooleanTriggerRepository.java
index 08b8898..4f9263c 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/BooleanTriggerRepository.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/BooleanTriggerRepository.java
@@ -3,8 +3,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
 import java.util.List;
 import org.springframework.data.repository.query.Param;
 
-public interface BooleanTriggerRepository
-        extends TriggerRepository> {
+public interface BooleanTriggerRepository extends TriggerRepository {
 
-    List> findAllByAutomationId(@Param("automationId") long automationId);
+    List findAllByAutomationId(@Param("automationId") long automationId);
 }
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationToken.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationToken.java
index d324724..a661aa0 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationToken.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationToken.java
@@ -22,7 +22,7 @@ public class ConfirmationToken {
     private Long id;
 
     @Column(name = "confirmation_token", unique = true)
-    private String confirmationToken;
+    private String confirmToken;
 
     @Temporal(TemporalType.TIMESTAMP)
     private Date createdDate;
@@ -32,12 +32,12 @@ public class ConfirmationToken {
     private User user;
 
     @Column(nullable = false)
-    private Boolean resetPassword;
+    private boolean resetPassword;
 
     public ConfirmationToken(User user) {
         this.user = user;
         createdDate = new Date();
-        confirmationToken = UUID.randomUUID().toString();
+        confirmToken = UUID.randomUUID().toString();
         resetPassword = false;
     }
 
@@ -48,12 +48,12 @@ public class ConfirmationToken {
         return id;
     }
 
-    public String getConfirmationToken() {
-        return confirmationToken;
+    public String getConfirmToken() {
+        return confirmToken;
     }
 
     public Date getCreatedDate() {
-        return createdDate;
+        return (Date) createdDate.clone();
     }
 
     public User getUser() {
@@ -64,23 +64,23 @@ public class ConfirmationToken {
         this.id = id;
     }
 
-    public void setConfirmationToken(String confirmationToken) {
-        this.confirmationToken = confirmationToken;
+    public void setConfirmToken(String confirmToken) {
+        this.confirmToken = confirmToken;
     }
 
     public void setCreatedDate(Date createdDate) {
-        this.createdDate = createdDate;
+        this.createdDate = (Date) createdDate.clone();
     }
 
     public void setUser(User user) {
         this.user = user;
     }
 
-    public Boolean getResetPassword() {
+    public boolean getResetPassword() {
         return resetPassword;
     }
 
-    public void setResetPassword(Boolean resetPassword) {
+    public void setResetPassword(boolean resetPassword) {
         this.resetPassword = resetPassword;
     }
 }
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationTokenRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationTokenRepository.java
index 40c6a17..851be41 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationTokenRepository.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ConfirmationTokenRepository.java
@@ -4,7 +4,7 @@ import javax.transaction.Transactional;
 import org.springframework.data.repository.CrudRepository;
 
 public interface ConfirmationTokenRepository extends CrudRepository {
-    ConfirmationToken findByConfirmationToken(String confirmationToken);
+    ConfirmationToken findByConfirmToken(String confirmToken);
 
     ConfirmationToken findByUser(User user);
 
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Connectable.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Connectable.java
new file mode 100644
index 0000000..8423930
--- /dev/null
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Connectable.java
@@ -0,0 +1,5 @@
+package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
+
+public interface Connectable {
+    void connect(O output, boolean connect);
+}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Connector.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Connector.java
deleted file mode 100644
index 701a010..0000000
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Connector.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
-
-import java.util.Set;
-import java.util.function.BiConsumer;
-import java.util.function.Function;
-
-/**
- * A rule on how to connect an input device type to an output device type
- *
- * @param  the input device type
- * @param  the output device type
- */
-@FunctionalInterface
-public interface Connector {
-
-    /**
-     * Connects or disconnects input to output
-     *
-     * @param input the input device
-     * @param output the output device
-     * @param connect true if connection, false if disconnection
-     */
-    void connect(I input, O output, boolean connect);
-
-    /**
-     * Produces a basic implementation of a connector, assuming there is a ManyToMany relationship
-     * between J and K
-     *
-     * @param outputsGetter the getter method of the set of outputs on the input class
-     * @param inputsGetter the getter method of the set of outputs on the input class
-     * @param  the input device type
-     * @param  the output device type
-     * @return a Connector implementation for the pair of types J and K
-     */
-    static  Connector basic(
-            Function> outputsGetter, Function> inputsGetter) {
-        return (i, o, connect) -> {
-            if (connect) {
-                outputsGetter.apply(i).add(o);
-                inputsGetter.apply(o).add(i);
-            } else {
-                outputsGetter.apply(i).remove(o);
-                inputsGetter.apply(o).remove(i);
-            }
-        };
-    }
-}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Curtains.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Curtains.java
index d9e8d98..af2fd40 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Curtains.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Curtains.java
@@ -3,8 +3,8 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
 import javax.persistence.Entity;
 
 /**
- * Represents a curtain. The intensity represents how much the curtains are opened,
- * 0 is completely closed 100 is completely open
+ * Represents a curtain. The intensity represents how much the curtains are opened, 0 is completely
+ * closed 100 is completely open
  */
 @Entity
 public class Curtains extends Dimmable {
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java
index d2231d8..ebaf4b0 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Device.java
@@ -4,10 +4,8 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.config.SocketGsonExclude;
 import com.google.gson.annotations.SerializedName;
 import io.swagger.annotations.ApiModelProperty;
-import java.util.HashSet;
 import java.util.Set;
 import javax.persistence.*;
-import javax.validation.constraints.NotNull;
 
 /** Generic abstraction for a smart home device */
 @Entity
@@ -39,18 +37,16 @@ public abstract class Device {
     @OneToMany(mappedBy = "device", orphanRemoval = true)
     @GsonExclude
     @SocketGsonExclude
-    private Set> triggers = new HashSet<>();
+    private Set> triggers;
 
     /**
      * The room this device belongs in, as a foreign key id. To use when updating and inserting from
      * a REST call.
      */
     @Column(name = "room_id", nullable = false)
-    @NotNull
     private Long roomId;
 
     /** The name of the device as assigned by the user (e.g. 'Master bedroom light') */
-    @NotNull
     @Column(nullable = false)
     private String name;
 
@@ -69,7 +65,7 @@ public abstract class Device {
     @OneToMany(mappedBy = "device", orphanRemoval = true)
     @GsonExclude
     @SocketGsonExclude
-    private Set> states = new HashSet<>();
+    private Set> states;
 
     @Transient @GsonExclude private Long fromHostId = null;
 
@@ -77,28 +73,9 @@ public abstract class Device {
 
     @Transient @GsonExclude private boolean deleted = false;
 
-    public Long getFromHostId() {
-        return fromHostId;
-    }
-
-    public void setFromHostId(Long fromHostId) {
-        this.fromHostId = fromHostId;
-    }
-
-    public boolean isDeleted() {
-        return deleted;
-    }
-
-    public void setDeleted(boolean deleted) {
-        this.deleted = deleted;
-    }
-
-    public boolean isFromGuest() {
-        return fromGuest;
-    }
-
-    public void setFromGuest(boolean fromGuest) {
-        this.fromGuest = fromGuest;
+    public Device(String kind, FlowType flowType) {
+        this.kind = kind;
+        this.flowType = flowType;
     }
 
     public long getId() {
@@ -109,12 +86,20 @@ public abstract class Device {
         this.id = id;
     }
 
-    public String getName() {
-        return name;
+    public Room getRoom() {
+        return room;
     }
 
-    public void setName(String name) {
-        this.name = name;
+    public void setRoom(Room room) {
+        this.room = room;
+    }
+
+    public Set> getTriggers() {
+        return triggers;
+    }
+
+    public void setTriggers(Set> triggers) {
+        this.triggers = triggers;
     }
 
     public Long getRoomId() {
@@ -125,8 +110,51 @@ public abstract class Device {
         this.roomId = roomId;
     }
 
-    public Device(String kind, FlowType flowType) {
-        this.kind = kind;
-        this.flowType = flowType;
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getKind() {
+        return kind;
+    }
+
+    public FlowType getFlowType() {
+        return flowType;
+    }
+
+    public Set> getStates() {
+        return states;
+    }
+
+    public void setStates(Set> states) {
+        this.states = states;
+    }
+
+    public Long getFromHostId() {
+        return fromHostId;
+    }
+
+    public void setFromHostId(Long fromHostId) {
+        this.fromHostId = fromHostId;
+    }
+
+    public boolean isFromGuest() {
+        return fromGuest;
+    }
+
+    public void setFromGuest(boolean fromGuest) {
+        this.fromGuest = fromGuest;
+    }
+
+    public boolean isDeleted() {
+        return deleted;
+    }
+
+    public void setDeleted(boolean deleted) {
+        this.deleted = deleted;
     }
 }
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Dimmable.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Dimmable.java
index 6ae3150..bc093e0 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Dimmable.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Dimmable.java
@@ -12,12 +12,6 @@ import javax.validation.constraints.NotNull;
 @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
 public class Dimmable extends Switchable implements RangeTriggerable {
 
-    public static final Connector KNOB_DIMMER_DIMMABLE_CONNECTOR =
-            Connector.basic(KnobDimmer::getOutputs, Dimmable::getDimmers);
-
-    public static final Connector BUTTON_DIMMER_DIMMABLE_CONNECTOR =
-            Connector.basic(ButtonDimmer::getOutputs, Dimmable::getDimmers);
-
     protected Dimmable(String kind) {
         super(kind);
     }
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableRepository.java
index be791c9..dffbbdb 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableRepository.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableRepository.java
@@ -1,4 +1,3 @@
 package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
 
-public interface DimmableRepository extends SwitchableRepository {
-}
+public interface DimmableRepository extends SwitchableRepository {}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Dimmer.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Dimmer.java
index e920d2f..cd3ee64 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Dimmer.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Dimmer.java
@@ -9,7 +9,7 @@ import javax.persistence.*;
 /** Represents a generic dimmer input device */
 @Entity
 @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
-public abstract class Dimmer extends InputDevice {
+public abstract class Dimmer extends InputDevice implements Connectable {
     public Dimmer(String kind) {
         super(kind);
     }
@@ -37,4 +37,14 @@ public abstract class Dimmer extends InputDevice {
     public void addDimmable(Dimmable dimmable) {
         dimmables.add(dimmable);
     }
+
+    public void connect(Dimmable output, boolean connect) {
+        if (connect) {
+            output.getDimmers().add(this);
+            getOutputs().add(output);
+        } else {
+            output.getDimmers().remove(this);
+            getOutputs().remove(output);
+        }
+    }
 }
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RangeTrigger.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RangeTrigger.java
index 19e179c..0d926ae 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RangeTrigger.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/RangeTrigger.java
@@ -3,10 +3,9 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
 import com.google.gson.annotations.SerializedName;
 import javax.persistence.Column;
 import javax.persistence.Entity;
-import javax.validation.constraints.NotNull;
 
 @Entity
-public class RangeTrigger extends Trigger {
+public class RangeTrigger extends Trigger {
 
     public RangeTrigger() {
         super("rangeTrigger");
@@ -43,11 +42,9 @@ public class RangeTrigger extends Trigger> {
+public interface RangeTriggerRepository extends TriggerRepository {
 
-    List> findAllByAutomationId(@Param("automationId") long automationId);
+    List findAllByAutomationId(@Param("automationId") long automationId);
 }
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java
index 73d7794..9e3ebc9 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Room.java
@@ -99,4 +99,12 @@ public class Room {
     public String toString() {
         return "Room{" + "id=" + id + ", name='" + name + "\'}";
     }
+
+    public User getUser() {
+        return user;
+    }
+
+    public void setUser(User user) {
+        this.user = user;
+    }
 }
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ScenePriority.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ScenePriority.java
index 64e755b..e975d08 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ScenePriority.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ScenePriority.java
@@ -28,7 +28,6 @@ public class ScenePriority {
     private Automation automation;
 
     @Column(name = "automation_id", nullable = false)
-    @NotNull
     private Long automationId;
 
     @NotNull
@@ -42,9 +41,12 @@ public class ScenePriority {
     private Scene scene;
 
     @Column(name = "scene_id", nullable = false, updatable = false)
-    @NotNull
     private Long sceneId;
 
+    public long getId() {
+        return id;
+    }
+
     public Integer getPriority() {
         return priority;
     }
@@ -53,21 +55,16 @@ public class ScenePriority {
         this.priority = priority;
     }
 
-    public Automation getAutomation() {
-        return automation;
-    }
-
-    public void setAutomation(Automation automation) {
-        this.automation = automation;
-    }
-
     public Long getAutomationId() {
         return automationId;
     }
 
-    public ScenePriority setAutomationId(Long automationId) {
+    public void setAutomationId(Long automationId) {
         this.automationId = automationId;
-        return this;
+    }
+
+    public void setAutomation(Automation automation) {
+        this.automation = automation;
     }
 
     public Scene getScene() {
@@ -88,10 +85,14 @@ public class ScenePriority {
 
     @PreRemove
     public void preRemove() {
-        this.setAutomation(null);
-        this.setAutomationId(null);
+        this.automation = null;
+        this.automationId = null;
 
-        this.setScene(null);
-        this.setSceneId(null);
+        this.scene = null;
+        this.sceneId = null;
+    }
+
+    public Automation getAutomation() {
+        return automation;
     }
 }
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SecurityCamera.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SecurityCamera.java
index 3d4cef9..c977130 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SecurityCamera.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SecurityCamera.java
@@ -18,12 +18,16 @@ public class SecurityCamera extends Switchable implements BooleanTriggerable {
 
     @Column(name = "video", nullable = false)
     @NotNull
-    private String path = "/security_camera_videos/security_camera_1.mp4";
+    private String path;
 
     public String getPath() {
         return path;
     }
 
+    public void setPath(String path) {
+        this.path = path;
+    }
+
     @Override
     public boolean isOn() {
         return on;
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java
index 2fb5442..58aa7f9 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Sensor.java
@@ -7,7 +7,6 @@ import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.EnumType;
 import javax.persistence.Enumerated;
-import javax.validation.constraints.NotNull;
 
 /** A sensor input device that measures a quantity in a continuous scale (e.g. temperature) */
 @Entity
@@ -15,9 +14,9 @@ public class Sensor extends InputDevice implements RangeTriggerable {
 
     public static final Map TYPICAL_VALUES =
             Map.of(
-                    SensorType.TEMPERATURE, new BigDecimal(17.0),
-                    SensorType.HUMIDITY, new BigDecimal(40.0),
-                    SensorType.LIGHT, new BigDecimal(1000));
+                    SensorType.TEMPERATURE, BigDecimal.valueOf(17.0),
+                    SensorType.HUMIDITY, BigDecimal.valueOf(40.0),
+                    SensorType.LIGHT, BigDecimal.valueOf(1000));
 
     @Override
     public double readTriggerState() {
@@ -45,7 +44,6 @@ public class Sensor extends InputDevice implements RangeTriggerable {
 
     /** The type of this sensor */
     @Column(nullable = false)
-    @NotNull
     @Enumerated(value = EnumType.STRING)
     private SensorType sensor;
 
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java
index 77f6c3d..10fcacf 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SmartPlug.java
@@ -1,6 +1,5 @@
 package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
 
-
 import java.math.BigDecimal;
 import javax.persistence.Column;
 import javax.persistence.Entity;
@@ -20,7 +19,6 @@ public class SmartPlug extends Switchable implements BooleanTriggerable {
 
     /** Whether the smart plug is on */
     @Column(name = "smart_plug_on", nullable = false)
-    @NotNull
     private boolean on;
 
     public BigDecimal getTotalConsumption() {
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switch.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switch.java
index 7825eae..359ecad 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switch.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switch.java
@@ -8,7 +8,7 @@ import javax.persistence.*;
 
 /** A switch input device */
 @Entity
-public class Switch extends InputDevice implements BooleanTriggerable {
+public class Switch extends InputDevice implements BooleanTriggerable, Connectable {
 
     @ManyToMany(
             cascade = {
@@ -60,10 +60,21 @@ public class Switch extends InputDevice implements BooleanTriggerable {
         return on;
     }
 
+    @Override
     public Set getOutputs() {
         return switchables;
     }
 
+    public void connect(Switchable output, boolean connect) {
+        if (connect) {
+            output.getSwitches().add(this);
+            getOutputs().add(output);
+        } else {
+            output.getSwitches().remove(this);
+            getOutputs().remove(output);
+        }
+    }
+
     @Override
     public boolean readTriggerState() {
         return on;
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switchable.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switchable.java
index 9db6361..a1a5366 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switchable.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Switchable.java
@@ -11,9 +11,6 @@ import javax.persistence.*;
 @Inheritance(strategy = InheritanceType.JOINED)
 public abstract class Switchable extends OutputDevice {
 
-    public static final Connector SWITCH_SWITCHABLE_CONNECTOR =
-            Connector.basic(Switch::getOutputs, Switchable::getSwitches);
-
     @ManyToMany(
             mappedBy = "switchables",
             cascade = {
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Thermostat.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Thermostat.java
index 632dfd7..abcc1cb 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Thermostat.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Thermostat.java
@@ -6,11 +6,9 @@ import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.Transient;
 import javax.validation.constraints.NotNull;
-import org.springframework.stereotype.Component;
 
 /** A thermostat capable of controlling cooling and heating. */
 @Entity
-@Component
 public class Thermostat extends Switchable implements BooleanTriggerable {
 
     @Override
@@ -66,7 +64,7 @@ public class Thermostat extends Switchable implements BooleanTriggerable {
     }
 
     /** Temperature to be reached */
-    @Column @NotNull private BigDecimal targetTemperature;
+    @Column private BigDecimal targetTemperature;
 
     /** The temperature detected by the embedded sensor */
     @Column(nullable = false, precision = 4, scale = 1)
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ThermostatRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ThermostatRepository.java
index 896e0ad..768b882 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ThermostatRepository.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ThermostatRepository.java
@@ -24,7 +24,6 @@ public interface ThermostatRepository extends DeviceRepository {
      * @param thermostatRoomId room ID of the thermostat
      * @return an optional big decimal, empty if none found
      */
-    @Query(
-            "SELECT AVG(s.value) FROM Sensor s JOIN s.room r WHERE s.sensor = 'TEMPERATURE' AND r.id = ?1")
-    Optional getAverageTemperature(Long thermostatRoomId);
+    @Query("SELECT AVG(s.value) FROM Sensor s JOIN s.room r WHERE s.sensor = ?2 AND r.id = ?1")
+    Optional getAverageTemperature(Long thermostatRoomId, Sensor.SensorType sensorType);
 }
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Trigger.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Trigger.java
index 77010c6..3633a35 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Trigger.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Trigger.java
@@ -3,11 +3,10 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude;
 import io.swagger.annotations.ApiModelProperty;
 import javax.persistence.*;
-import javax.validation.constraints.NotNull;
 
 @Entity
 @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
-public abstract class Trigger {
+public abstract class Trigger {
 
     @Transient private String kind;
 
@@ -37,7 +36,6 @@ public abstract class Trigger {
      * from a REST call.
      */
     @Column(name = "device_id", nullable = false)
-    @NotNull
     private Long deviceId;
 
     @ManyToOne
@@ -46,7 +44,6 @@ public abstract class Trigger {
     private Automation automation;
 
     @Column(name = "automation_id", nullable = false)
-    @NotNull
     private Long automationId;
 
     public long getId() {
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java
index b58e383..61bdf6d 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/User.java
@@ -56,7 +56,7 @@ public class User {
 
     @Column(nullable = false)
     @GsonExclude
-    private Boolean isEnabled = false;
+    private boolean isEnabled = false;
 
     public Long getId() {
         return id;
@@ -98,11 +98,11 @@ public class User {
         this.password = password;
     }
 
-    public Boolean getEnabled() {
+    public boolean getEnabled() {
         return isEnabled;
     }
 
-    public void setEnabled(Boolean enabled) {
+    public void setEnabled(boolean enabled) {
         isEnabled = enabled;
     }
 
@@ -162,16 +162,17 @@ public class User {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
         User user = (User) o;
-        return id.equals(user.id)
-                && name.equals(user.name)
-                && username.equals(user.username)
-                && password.equals(user.password)
-                && email.equals(user.email)
-                && isEnabled.equals(user.isEnabled);
+        return cameraEnabled == user.cameraEnabled
+                && isEnabled == user.isEnabled
+                && Objects.equals(id, user.id)
+                && Objects.equals(name, user.name)
+                && Objects.equals(username, user.username)
+                && Objects.equals(password, user.password)
+                && Objects.equals(email, user.email);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(id, name, username, password, email, isEnabled);
+        return Objects.hash(id, name, username, password, email, isEnabled, cameraEnabled);
     }
 }
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/AutomationService.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/AutomationService.java
new file mode 100644
index 0000000..9eaf401
--- /dev/null
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/AutomationService.java
@@ -0,0 +1,28 @@
+package ch.usi.inf.sa4.sanmarinoes.smarthut.service;
+
+import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class AutomationService {
+    private final AutomationRepository automationRepository;
+    private final TriggerRepository> triggerRepository;
+
+    @Autowired
+    public AutomationService(
+            AutomationRepository automationRepository,
+            TriggerRepository> triggerRepository) {
+        this.automationRepository = automationRepository;
+        this.triggerRepository = triggerRepository;
+    }
+
+    public List> findTriggersByDeviceId(Long deviceId) {
+        return triggerRepository.findAllByDeviceId(deviceId);
+    }
+
+    public Automation findByVerifiedId(Long automationId) {
+        return automationRepository.findById(automationId).orElseThrow();
+    }
+}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/DevicePopulationService.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/DevicePopulationService.java
new file mode 100644
index 0000000..9b3e7d2
--- /dev/null
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/DevicePopulationService.java
@@ -0,0 +1,20 @@
+package ch.usi.inf.sa4.sanmarinoes.smarthut.service;
+
+import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Device;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Thermostat;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DevicePopulationService {
+
+    @Autowired private ThermostatPopulationService thermostatService;
+
+    public void populateComputedFields(Iterable devices) {
+        for (Device d : devices) {
+            if (d instanceof Thermostat) {
+                thermostatService.populateMeasuredTemperature((Thermostat) d);
+            }
+        }
+    }
+}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/DevicePropagationService.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/DevicePropagationService.java
new file mode 100644
index 0000000..edba584
--- /dev/null
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/DevicePropagationService.java
@@ -0,0 +1,151 @@
+package ch.usi.inf.sa4.sanmarinoes.smarthut.service;
+
+import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList;
+
+import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint;
+import java.util.List;
+import java.util.Set;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DevicePropagationService {
+
+    private final SensorSocketEndpoint endpoint;
+    private final EagerUserRepository userRepository;
+    private final DeviceRepository deviceRepository;
+
+    @Autowired
+    public DevicePropagationService(
+            SensorSocketEndpoint endpoint,
+            EagerUserRepository userRepository,
+            DeviceRepository deviceRepository) {
+        this.endpoint = endpoint;
+        this.userRepository = userRepository;
+        this.deviceRepository = deviceRepository;
+    }
+
+    void propagateUpdateAsGuest(Device device, User host, User guest) {
+        final Set guests = Set.copyOf(host.getGuests());
+
+        // We're telling the host that a guest has modified a device. Therefore, fromGuest becomes
+        // true.
+        // broadcast device update to host
+        endpoint.queueDeviceUpdate(device, host, true, null, false);
+
+        // We're telling all guests that a higher entity has issued a device update. Therefore,
+        // fromHost becomes true.
+        for (final User aGuest : guests) {
+            if (aGuest.equals(guest)) {
+                continue;
+            }
+            // enqueue all device updates for all other guests
+            endpoint.queueDeviceUpdate(device, aGuest, false, host.getId(), false);
+        }
+    }
+
+    void saveAllAsGuestSceneApplication(List devices, String guestUsername, Long hostId) {
+        final User guest = userRepository.findByUsername(guestUsername);
+        final User host = userRepository.findById(hostId).orElseThrow(IllegalStateException::new);
+        deviceRepository.saveAll(devices);
+        devices.forEach(d -> this.propagateUpdateAsGuest(d, host, guest));
+    }
+
+    void renameIfDuplicate(Device toCreate, String username) {
+        while (deviceRepository.findDuplicates(toCreate.getName(), username)
+                        - (toCreate.getId() <= 0 ? 0 : 1)
+                > 0) {
+            toCreate.setName(toCreate.getName() + " (new)");
+        }
+    }
+
+    public  T saveAsGuest(T device, String guestUsername, Long hostId)
+            throws NotFoundException {
+        final User currentUser = userRepository.findByUsername(guestUsername);
+        final User host = userRepository.findById(hostId).orElseThrow(NotFoundException::new);
+        if (!host.getGuests().contains(currentUser)) throw new NotFoundException();
+        renameIfDuplicate(device, host.getUsername());
+
+        device = deviceRepository.save(device);
+        propagateUpdateAsGuest(device, host, currentUser);
+        return device;
+    }
+
+    /**
+     * Saves all the devices given in devices assuming that the owner updated them in one way or
+     * another. Takes care of the appropriate websocket updates and trigger checking as well. No
+     * checking is done to verify that the user whose username is given is in fact the owner of
+     * these devices
+     *
+     * @param devices the list of devices to save
+     * @param username the username of the owner of these devices
+     * @param fromScene true if the update comes from the a scene application side effect. Disables
+     *     trigger checking to avoid recursive invocations of automations
+     * @param fromTrigger true if the update comes from a scene application executed by an
+     *     automation. Propagates updated through socket to owner as well. No effect if fromScene is
+     *     false.
+     * @param  the type of device contained in the list
+     * @return the updated list of devices, ready to be fed to GSON
+     */
+    public  List saveAllAsOwner(
+            Iterable devices, String username, boolean fromScene, boolean fromTrigger) {
+        devices.forEach(d -> renameIfDuplicate(d, username));
+        devices = deviceRepository.saveAll(devices);
+        devices.forEach(d -> propagateUpdateAsOwner(d, username, fromScene && fromTrigger));
+
+        return toList(devices);
+    }
+
+    public  List saveAllAsOwner(Iterable devices, String username) {
+        return saveAllAsOwner(devices, username, false, false);
+    }
+
+    public  T saveAsOwner(T device, String username) {
+        renameIfDuplicate(device, username);
+        device = deviceRepository.save(device);
+        propagateUpdateAsOwner(device, username, false);
+
+        return device;
+    }
+
+    public void deleteByIdAsOwner(Long id, String username) throws NotFoundException {
+        Device d =
+                deviceRepository
+                        .findByIdAndUsername(id, username)
+                        .orElseThrow(NotFoundException::new);
+
+        final User user = userRepository.findByUsername(username);
+        final Set guests = user.getGuests();
+        // make sure we're broadcasting from host
+        for (final User guest : guests) {
+            // broadcast to endpoint the object device, with receiving user set to guest
+            endpoint.queueDeviceUpdate(d, guest, false, user.getId(), true);
+        }
+
+        deviceRepository.delete(d);
+    }
+
+    /**
+     * Propagates the update through the socket assuming that the user that modified the device is
+     * the owner of that device
+     *
+     * @param device the updated device
+     * @param username the username of the owner of that device
+     * @param causedByTrigger if true, send the update to the owner as well
+     */
+    private void propagateUpdateAsOwner(Device device, String username, boolean causedByTrigger) {
+        final User user = userRepository.findByUsername(username);
+        final Set guests = user.getGuests();
+        // make sure we're broadcasting from host
+        for (final User guest : guests) {
+            // broadcast to endpoint the object device, with receiving user set to guest
+            endpoint.queueDeviceUpdate(device, guest, false, user.getId(), false);
+        }
+
+        if (causedByTrigger) {
+            endpoint.queueDeviceUpdate(device, user, false, user.getId(), false);
+        }
+    }
+}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/DeviceService.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/DeviceService.java
index f25c72f..ea21d5d 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/DeviceService.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/DeviceService.java
@@ -4,12 +4,9 @@ import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList;
 
 import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
-import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.List;
-import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.StreamSupport;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -18,237 +15,91 @@ import org.springframework.stereotype.Component;
 @Component
 public class DeviceService {
 
-    @Autowired private DeviceRepository deviceRepository;
-    @Autowired private AutomationRepository automationRepository;
-    @Autowired private SceneRepository sceneRepository;
-    @Autowired private SceneService sceneService;
-    @Autowired private TriggerRepository> triggerRepository;
-    @Autowired private ConditionRepository> conditionRepository;
-    @Autowired private RoomRepository roomRepository;
-    @Autowired private EagerUserRepository userRepository;
-    @Autowired private SensorSocketEndpoint endpoint;
-    @Autowired private ThermostatService thermostatService;
+    private final DeviceRepository deviceRepository;
+    private final SceneService sceneService;
+    private final RoomRepository roomRepository;
+    private final AutomationService automationService;
+    private final EagerUserRepository userRepository;
+    private final DevicePopulationService devicePopulationService;
+    private final DevicePropagationService devicePropagationService;
+    private final ConditionRepository> conditionRepository;
 
-    public void throwIfRoomNotOwned(Long roomId, String username) throws NotFoundException {
-        roomRepository.findByIdAndUsername(roomId, username).orElseThrow(NotFoundException::new);
+    @Autowired
+    public DeviceService(
+            DeviceRepository deviceRepository,
+            SceneService sceneService,
+            RoomRepository roomRepository,
+            AutomationService automationService,
+            EagerUserRepository userRepository,
+            DevicePopulationService devicePopulationService,
+            DevicePropagationService devicePropagationService,
+            ConditionRepository> conditionRepository) {
+        this.deviceRepository = deviceRepository;
+        this.sceneService = sceneService;
+        this.roomRepository = roomRepository;
+        this.automationService = automationService;
+        this.userRepository = userRepository;
+        this.devicePopulationService = devicePopulationService;
+        this.devicePropagationService = devicePropagationService;
+        this.conditionRepository = conditionRepository;
     }
 
-    private void renameIfDuplicate(Device toCreate, String username) {
-        while (deviceRepository.findDuplicates(toCreate.getName(), username)
-                        - (toCreate.getId() <= 0 ? 0 : 1)
-                > 0) {
-            toCreate.setName(toCreate.getName() + " (new)");
-        }
+    public void throwIfRoomNotOwned(Long roomId, String username) throws NotFoundException {
+        final Room r =
+                roomRepository
+                        .findByIdAndUsername(roomId, username)
+                        .orElseThrow(NotFoundException::new);
+        if (!r.getId().equals(roomId)) throw new IllegalStateException();
     }
 
     private void triggerTriggers(Device device, final String username) {
 
         final long deviceId = device.getId();
+        final List> triggers = automationService.findTriggersByDeviceId(deviceId);
 
-        // find the conditions that this device state change is triggering
-        List> triggeredConditions =
-                conditionRepository
-                        .findAllByDeviceId(deviceId)
-                        .stream()
-                        .filter(Condition::triggered)
-                        .collect(Collectors.toList());
-
-        List automationsWithMetConditions = new ArrayList<>();
-
-        // this condition is connected to an automation obviously, and this automation might be
-        // connected to many other conditions, I need to find them all and make sure all of them are
-        // in their "TRUE" state.
-        // if that's the case I need to remember that this automation needs to be triggered
-        for (Condition condition : triggeredConditions) {
-
-            Automation a =
-                    automationRepository
-                            .findById(condition.getAutomationId())
-                            .orElseThrow(IllegalStateException::new);
-            List> conditions =
-                    conditionRepository.findAllByAutomationId(a.getId());
-
-            if (conditions.size()
-                    == conditions
-                            .stream()
-                            .filter(Condition::triggered)
-                            .collect(Collectors.toList())
-                            .size()) {
-                automationsWithMetConditions.add(a);
-            }
-        }
-
-        List> triggers = triggerRepository.findAllByDeviceId(deviceId);
-
-        // these are all the automations that are triggered, now I need to check if they are
-        // associated
-        // with some conditions, if they are I need to check that they are present also in the
-        // automationsToTrigger list.
-        // there are two cases:
-        // 1. this automation has been triggered and it is not connected to any condition, therefore
-        // this can be kept in the list
-        // 2. this automation has been triggered, it is connected to one or more conditions, but it
-        // is not present in the automationsToTrigger list, therefore it has to be discarded
-        List triggeredAutomationsTMP =
-                triggers.stream()
-                        .filter(Trigger::triggered)
-                        .map(Trigger::getAutomationId)
-                        .map(
-                                t ->
-                                        automationRepository
-                                                .findById(t)
-                                                .orElseThrow(IllegalStateException::new))
-                        .distinct()
-                        .collect(Collectors.toList());
-
-        List triggeredAutomations = new ArrayList<>();
-        for (Automation a : triggeredAutomationsTMP) {
-            if (conditionRepository.findAllByAutomationId(a.getId()).size() > 0) {
-                if (automationsWithMetConditions.contains(a)) {
-                    triggeredAutomations.add(a);
-                }
-            } else {
-                triggeredAutomations.add(a);
-            }
-        }
-
-        triggeredAutomations
-                .stream()
+        triggers.stream()
+                .filter(Trigger::triggered)
+                .map(Trigger::getAutomationId)
+                .map(automationService::findByVerifiedId)
                 .distinct()
+                .filter(
+                        a -> {
+                            final List> conditions =
+                                    conditionRepository.findAllByAutomationId(a.getId());
+                            if (conditions.size() == 0) return true;
+                            return conditions.stream().allMatch(Condition::triggered);
+                        })
                 .map(Automation::getScenes)
                 .flatMap(Collection::stream)
                 .distinct()
                 .sorted(Comparator.comparing(ScenePriority::getPriority))
-                .map(
-                        t ->
-                                sceneRepository
-                                        .findById(t.getSceneId())
-                                        .orElseThrow(IllegalStateException::new))
-                .forEach((s) -> sceneService.apply(s, username, true));
+                .map(t -> sceneService.findByValidatedId(t.getSceneId()))
+                .forEach(s -> sceneService.apply(s, username, true));
     }
 
     public List findAll(Long hostId, String username) throws NotFoundException {
         return findAll(null, hostId, username);
     }
 
-    public List findAll(Long roomId, Long hostId, String username)
-            throws NotFoundException {
-        try {
-            Iterable devices;
-            User host = null;
-            if (hostId == null) {
-                if (roomId != null) {
-                    roomRepository
-                            .findByIdAndUsername(roomId, username)
-                            .orElseThrow(NotFoundException::new);
-                    devices = deviceRepository.findByRoomId(roomId);
-                } else {
-                    devices = deviceRepository.findAllByUsername(username);
-                }
-            } else {
-                final User guest = userRepository.findByUsername(username);
-                host = userRepository.findById(hostId).orElseThrow(NotFoundException::new);
-
-                if (!guest.getHosts().contains(host)) {
-                    throw new NotFoundException();
-                }
-
-                if (roomId != null) {
-                    Room r = roomRepository.findById(roomId).orElseThrow(NotFoundException::new);
-                    if (!r.getUserId().equals(hostId)) {
-                        throw new NotFoundException();
-                    }
-                    devices = deviceRepository.findByRoomId(roomId);
-                } else {
-                    devices = deviceRepository.findAllByUsername(host.getUsername());
-                }
-            }
-
-            populateComputedFields(devices);
-
-            if (host != null && !host.isCameraEnabled()) {
-                return StreamSupport.stream(devices.spliterator(), true)
-                        .filter(d -> !(d instanceof SecurityCamera))
-                        .collect(Collectors.toList());
-            } else {
-                return toList(devices);
-            }
-        } catch (NotFoundException e) {
-            e.printStackTrace();
-            throw e;
-        }
-    }
-
-    public void populateComputedFields(Iterable devices) {
-        for (Device d : devices) {
-            if (d instanceof Thermostat) {
-                thermostatService.populateMeasuredTemperature((Thermostat) d);
-            }
-        }
-    }
-
     public  T saveAsGuest(T device, String guestUsername, Long hostId)
             throws NotFoundException {
         final User currentUser = userRepository.findByUsername(guestUsername);
         final User host = userRepository.findById(hostId).orElseThrow(NotFoundException::new);
-        if (!host.getGuests().contains(currentUser)) {
-            throw new NotFoundException();
-        }
-        renameIfDuplicate(device, host.getUsername());
+
+        if (!host.getGuests().contains(currentUser)) throw new NotFoundException();
+        devicePropagationService.renameIfDuplicate(device, host.getUsername());
 
         device = deviceRepository.save(device);
-        propagateUpdateAsGuest(device, host, currentUser);
+        devicePropagationService.propagateUpdateAsGuest(device, host, currentUser);
         return device;
     }
 
-    private void propagateUpdateAsGuest(Device device, User host, User guest) {
-        final Set guests = Set.copyOf(host.getGuests());
-
-        // We're telling the host that a guest has modified a device. Therefore, fromGuest becomes
-        // true.
-        // broadcast device update to host
-        endpoint.queueDeviceUpdate(device, host, true, null, false);
-
-        // We're telling all guests that a higher entity has issued a device update. Therefore,
-        // fromHost becomes true.
-        for (final User aGuest : guests) {
-            if (aGuest.equals(guest)) {
-                continue;
-            }
-            // enqueue all device updates for all other guests
-            endpoint.queueDeviceUpdate(device, aGuest, false, host.getId(), false);
-        }
+    public void deleteByIdAsOwner(Long id, String username) throws NotFoundException {
+        devicePropagationService.deleteByIdAsOwner(id, username);
     }
 
-    List saveAllAsGuestSceneApplication(
-            List devices, String guestUsername, Long hostId) {
-        final User guest = userRepository.findByUsername(guestUsername);
-        final User host = userRepository.findById(hostId).orElseThrow(IllegalStateException::new);
-        deviceRepository.saveAll(devices);
-        devices.forEach(d -> this.propagateUpdateAsGuest(d, host, guest));
-        return devices;
-    }
-
-    /**
-     * Propagates the update through the socket assuming that the user that modified the device is
-     * the owner of that device
-     *
-     * @param device the updated device
-     * @param username the username of the owner of that device
-     * @param causedByTrigger if true, send the update to the owner as well
-     */
-    private void propagateUpdateAsOwner(Device device, String username, boolean causedByTrigger) {
-        final User user = userRepository.findByUsername(username);
-        final Set guests = user.getGuests();
-        // make sure we're broadcasting from host
-        for (final User guest : guests) {
-            // broadcast to endpoint the object device, with receiving user set to guest
-            endpoint.queueDeviceUpdate(device, guest, false, user.getId(), false);
-        }
-
-        if (causedByTrigger) {
-            endpoint.queueDeviceUpdate(device, user, false, user.getId(), false);
-        }
+    public void populateComputedFields(Iterable devices) {
+        devicePopulationService.populateComputedFields(devices);
     }
 
     /**
@@ -269,15 +120,12 @@ public class DeviceService {
      */
     public  List saveAllAsOwner(
             Iterable devices, String username, boolean fromScene, boolean fromTrigger) {
-        devices.forEach(d -> renameIfDuplicate(d, username));
-        devices = deviceRepository.saveAll(devices);
-        devices.forEach((d) -> propagateUpdateAsOwner(d, username, fromScene && fromTrigger));
-
+        List toReturn =
+                devicePropagationService.saveAllAsOwner(devices, username, fromScene, fromTrigger);
         if (!fromScene) {
-            devices.forEach((d) -> triggerTriggers(d, username));
+            toReturn.forEach(d -> this.triggerTriggers(d, username));
         }
-
-        return toList(devices);
+        return toReturn;
     }
 
     public  List saveAllAsOwner(Iterable devices, String username) {
@@ -285,29 +133,53 @@ public class DeviceService {
     }
 
     public  T saveAsOwner(T device, String username) {
-        renameIfDuplicate(device, username);
-        device = deviceRepository.save(device);
-        propagateUpdateAsOwner(device, username, false);
-
-        triggerTriggers(device, username);
-
+        T toReturn = devicePropagationService.saveAsOwner(device, username);
+        triggerTriggers(toReturn, username);
         return device;
     }
 
-    public void deleteByIdAsOwner(Long id, String username) throws NotFoundException {
-        Device d =
-                deviceRepository
-                        .findByIdAndUsername(id, username)
-                        .orElseThrow(NotFoundException::new);
+    public List findAll(Long roomId, Long hostId, String username)
+            throws NotFoundException {
+        Iterable devices;
+        User host = null;
+        if (hostId == null) {
+            if (roomId != null) {
+                throwIfRoomNotOwned(roomId, username);
+                devices = deviceRepository.findByRoomId(roomId);
+            } else {
+                devices = deviceRepository.findAllByUsername(username);
+            }
+        } else {
+            final User guest = userRepository.findByUsername(username);
+            host = userRepository.findById(hostId).orElseThrow(NotFoundException::new);
 
-        final User user = userRepository.findByUsername(username);
-        final Set guests = user.getGuests();
-        // make sure we're broadcasting from host
-        for (final User guest : guests) {
-            // broadcast to endpoint the object device, with receiving user set to guest
-            endpoint.queueDeviceUpdate(d, guest, false, user.getId(), true);
+            if (!guest.getHosts().contains(host)) {
+                throw new NotFoundException();
+            }
+
+            if (roomId != null) {
+                Room r = roomRepository.findById(roomId).orElseThrow(NotFoundException::new);
+                if (!r.getUserId().equals(hostId)) {
+                    throw new NotFoundException();
+                }
+                devices = deviceRepository.findByRoomId(roomId);
+            } else {
+                devices = deviceRepository.findAllByUsername(host.getUsername());
+            }
         }
 
-        deviceRepository.delete(d);
+        devicePopulationService.populateComputedFields(devices);
+
+        return filterOutCamerasIfNeeded(host, devices);
+    }
+
+    private List filterOutCamerasIfNeeded(User host, Iterable devices) {
+        if (host != null && !host.isCameraEnabled()) {
+            return StreamSupport.stream(devices.spliterator(), true)
+                    .filter(d -> !(d instanceof SecurityCamera))
+                    .collect(Collectors.toList());
+        } else {
+            return toList(devices);
+        }
     }
 }
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/JWTUserDetailsService.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/JWTUserDetailsService.java
index 7dff142..f8c8e5b 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/JWTUserDetailsService.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/JWTUserDetailsService.java
@@ -1,9 +1,8 @@
 package ch.usi.inf.sa4.sanmarinoes.smarthut.service;
 
-import java.util.Set;
-
 import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository;
+import java.util.Set;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.core.*;
 import org.springframework.security.core.userdetails.UserDetails;
@@ -16,7 +15,7 @@ public class JWTUserDetailsService implements UserDetailsService {
     @Autowired private UserRepository repository;
 
     @Override
-    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+    public UserDetails loadUserByUsername(String username) {
         User toReturn = repository.findByUsername(username);
         if (toReturn != null && toReturn.getEnabled()) {
             return new org.springframework.security.core.userdetails.User(
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/SceneService.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/SceneService.java
index 3c91277..824ea7e 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/SceneService.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/SceneService.java
@@ -9,31 +9,48 @@ import org.springframework.stereotype.Component;
 @Component
 public class SceneService {
 
-    @Autowired private DeviceRepository deviceRepository;
-    @Autowired private DeviceService deviceService;
-    @Autowired private StateRepository> stateRepository;
+    private final DevicePopulationService devicePopulationService;
+    private final DevicePropagationService devicePropagationService;
+    private final StateRepository> stateRepository;
+    private final SceneRepository sceneRepository;
+
+    public Scene findByValidatedId(Long id) {
+        return sceneRepository.findById(id).orElseThrow();
+    }
+
+    @Autowired
+    public SceneService(
+            DevicePopulationService devicePopulationService,
+            DevicePropagationService devicePropagationService,
+            StateRepository> stateRepository,
+            SceneRepository sceneRepository) {
+        this.devicePopulationService = devicePopulationService;
+        this.devicePropagationService = devicePropagationService;
+        this.stateRepository = stateRepository;
+        this.sceneRepository = sceneRepository;
+    }
 
     private List copyStatesToDevices(Scene fromScene) {
-        final List updated = new ArrayList<>();
+        final List updated = new ArrayList<>(fromScene.getStates().size());
 
         for (final State s : fromScene.getStates()) {
             s.apply();
             updated.add(s.getDevice());
         }
 
-        deviceService.populateComputedFields(updated);
+        devicePopulationService.populateComputedFields(updated);
         return updated;
     }
 
     public List apply(Scene newScene, String username, boolean fromTrigger) {
         List updated = copyStatesToDevices(newScene);
-        deviceService.saveAllAsOwner(updated, username, true, fromTrigger);
+        devicePropagationService.saveAllAsOwner(updated, username, true, fromTrigger);
         return updated;
     }
 
     public List applyAsGuest(Scene newScene, String username, Long hostId) {
         List updated = copyStatesToDevices(newScene);
-        deviceService.saveAllAsGuestSceneApplication(updated, username, hostId);
+        devicePropagationService.saveAllAsGuestSceneApplication(updated, username, hostId);
         return updated;
     }
 
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/ThermostatPopulationService.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/ThermostatPopulationService.java
new file mode 100644
index 0000000..d8e1878
--- /dev/null
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/ThermostatPopulationService.java
@@ -0,0 +1,34 @@
+package ch.usi.inf.sa4.sanmarinoes.smarthut.service;
+
+import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Sensor;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Thermostat;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ThermostatRepository;
+import java.math.BigDecimal;
+import java.util.Optional;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ThermostatPopulationService {
+
+    @Autowired private ThermostatRepository thermostatRepository;
+
+    private BigDecimal measureTemperature(final Thermostat thermostat) {
+        Optional average;
+
+        if (thermostat.isUseExternalSensors()) {
+            average =
+                    thermostatRepository.getAverageTemperature(
+                            thermostat.getRoomId(), Sensor.SensorType.TEMPERATURE);
+
+        } else {
+            return thermostat.getInternalSensorTemperature();
+        }
+
+        return average.orElse(null);
+    }
+
+    public void populateMeasuredTemperature(Thermostat thermostat) {
+        thermostat.setMeasuredTemperature(measureTemperature(thermostat));
+    }
+}
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/ThermostatService.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/ThermostatService.java
index 328d092..5eeec6c 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/ThermostatService.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/ThermostatService.java
@@ -18,6 +18,8 @@ public class ThermostatService {
 
     @Autowired private DeviceService deviceService;
 
+    @Autowired private ThermostatPopulationService thermostatPopulationService;
+
     @Autowired private ThermostatRepository thermostatRepository;
 
     private void randomJitter(Thermostat thermostat) {
@@ -41,12 +43,12 @@ public class ThermostatService {
 
     public List findAll(String username) {
         Iterable all = thermostatRepository.findAllByUsername(username);
-        all.forEach(this::populateMeasuredTemperature);
+        all.forEach(thermostatPopulationService::populateMeasuredTemperature);
         return Utils.toList(all);
     }
 
     public void computeState(Thermostat t) {
-        populateMeasuredTemperature(t);
+        thermostatPopulationService.populateMeasuredTemperature(t);
         t.computeState();
     }
 
@@ -67,25 +69,9 @@ public class ThermostatService {
 
         if (t.isPresent()) {
             Thermostat u = t.get();
-            populateMeasuredTemperature(u);
+            thermostatPopulationService.populateMeasuredTemperature(u);
             t = Optional.of(u);
         }
         return t;
     }
-
-    private BigDecimal measureTemperature(final Thermostat thermostat) {
-        Optional average;
-
-        if (thermostat.isUseExternalSensors()) {
-            average = thermostatRepository.getAverageTemperature(thermostat.getRoomId());
-        } else {
-            return thermostat.getInternalSensorTemperature();
-        }
-
-        return average.orElse(null);
-    }
-
-    public void populateMeasuredTemperature(Thermostat thermostat) {
-        thermostat.setMeasuredTemperature(measureTemperature(thermostat));
-    }
 }
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketConfig.java
index 503667a..338a8f2 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketConfig.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketConfig.java
@@ -11,7 +11,7 @@ import org.springframework.web.socket.server.standard.ServerEndpointRegistration
 @Configuration
 public class SensorSocketConfig extends ServerEndpointConfig.Configurator {
 
-    private SensorSocketEndpoint instance;
+    private final SensorSocketEndpoint instance;
 
     @Autowired
     public SensorSocketConfig(SensorSocketEndpoint instance) {
@@ -41,9 +41,8 @@ public class SensorSocketConfig extends ServerEndpointConfig.Configurator {
     @Override
     public  T getEndpointInstance(Class endpointClass) throws InstantiationException {
         try {
-            @SuppressWarnings("unchecked")
-            final T instance = (T) this.instance;
-            return instance;
+            //noinspection unchecked
+            return (T) this.instance;
         } catch (ClassCastException e) {
             final var e2 =
                     new InstantiationException("Cannot cast SensorSocketEndpoint to desired type");
diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java
index 4764f18..009dfab 100644
--- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java
+++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/socket/SensorSocketEndpoint.java
@@ -5,7 +5,7 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.config.JWTTokenUtils;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Device;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository;
-import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DeviceService;
+import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DevicePopulationService;
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Multimaps;
@@ -13,6 +13,8 @@ import com.google.gson.Gson;
 import java.io.IOException;
 import java.util.*;
 import javax.websocket.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
@@ -20,15 +22,17 @@ import org.springframework.stereotype.Component;
 @Component
 public class SensorSocketEndpoint extends Endpoint {
 
-    private Gson gson = GsonConfig.socketGson();
+    private static final Logger logger = LoggerFactory.getLogger(SensorSocketEndpoint.class);
 
-    @Autowired private DeviceService deviceService;
+    private final Gson gson = GsonConfig.socketGson();
 
-    private UserRepository userRepository;
+    @Autowired private DevicePopulationService deviceService;
 
-    private JWTTokenUtils jwtTokenUtils;
+    private final UserRepository userRepository;
 
-    private Multimap authorizedClients =
+    private final JWTTokenUtils jwtTokenUtils;
+
+    private final Multimap authorizedClients =
             Multimaps.synchronizedMultimap(HashMultimap.create());
 
     // messages are now stored as strings as a "hack" to capture and clone the state of the device,
@@ -98,7 +102,7 @@ public class SensorSocketEndpoint extends Endpoint {
                     authorizedClients.remove(u, s);
                 }
             } catch (IOException e) {
-                e.printStackTrace();
+                logger.warn(e.getLocalizedMessage(), e);
             }
         }
     }
@@ -118,7 +122,8 @@ public class SensorSocketEndpoint extends Endpoint {
         } else {
             try {
                 session.close();
-            } catch (IOException ignored) {
+            } catch (IOException e) {
+                logger.warn(e.getLocalizedMessage(), e);
             }
         }
     }
@@ -128,8 +133,8 @@ public class SensorSocketEndpoint extends Endpoint {
 
         try {
             username = jwtTokenUtils.getUsernameFromToken(protocolString);
-        } catch (Throwable ignored) {
-            System.out.println("Token format not valid");
+        } catch (Exception ignored) {
+            logger.info("Token format not valid");
             return null;
         }
 
diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties
index 8720442..9f460b4 100644
--- a/src/main/resources/application-dev.properties
+++ b/src/main/resources/application-dev.properties
@@ -32,4 +32,5 @@ email.registrationRedirect=http://localhost:3000/login
 email.resetpasswordSubject=SmartHut.sm password reset
 email.resetpassword=To reset your password, please click here:
 email.resetpasswordPath=http://localhost:3000/password-reset?token=
-email.resetPasswordRedirect=http://localhost:3000/conf-reset-pass
\ No newline at end of file
+email.resetPasswordRedirect=http://localhost:3000/conf-reset-pass
+camera.videoUrl="/security_camera_videos/security_camera_1.mp4"
\ No newline at end of file
diff --git a/src/main/resources/application-prod.properties b/src/main/resources/application-prod.properties
index 1727673..d607427 100644
--- a/src/main/resources/application-prod.properties
+++ b/src/main/resources/application-prod.properties
@@ -39,4 +39,5 @@ email.registrationRedirect=${FRONTEND_URL}/login
 email.resetpasswordSubject=SmartHut.sm password reset
 email.resetpassword=To reset your password, please click here:
 email.resetpasswordPath=${FRONTEND_URL}/password-reset?token=
-email.resetPasswordRedirect=${FRONTEND_URL}/conf-reset-pass
\ No newline at end of file
+email.resetPasswordRedirect=${FRONTEND_URL}/conf-reset-pass
+camera.videoUrl="/security_camera_videos/security_camera_1.mp4"
\ No newline at end of file
diff --git a/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/AuthenticationTests.java b/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/AuthenticationTests.java
index d13104f..2c0ae6f 100644
--- a/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/AuthenticationTests.java
+++ b/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/AuthenticationTests.java
@@ -4,9 +4,7 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.JWTRequest;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.JWTResponse;
-import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.OkResponse;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserRegistrationRequest;
-import ch.usi.inf.sa4.sanmarinoes.smarthut.error.DuplicateRegistrationException;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UnauthorizedException;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationTokenRepository;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository;
@@ -42,10 +40,10 @@ public class AuthenticationTests extends SmartHutTest {
 
     @Override
     protected void setUp() {
-        final ResponseEntity res =
+        final ResponseEntity res =
                 this.restTemplate.postForEntity(
-                        this.url("/register"), getDisabledUser(), OkResponse.class);
-        assertThat(res.getStatusCode().equals(HttpStatus.OK));
+                        this.url("/register"), getDisabledUser(), Object.class);
+        assertThat(res.getStatusCode()).isEqualTo(HttpStatus.OK);
 
         registerTestUser(restTemplate, userRepository, tokenRepository);
     }
@@ -55,10 +53,11 @@ public class AuthenticationTests extends SmartHutTest {
         final Map badJSON = Map.of("luciano", "goretti", "danilo", "malusa");
 
         assertThat(
-                this.restTemplate
-                        .postForEntity(url("/register"), badJSON, JWTResponse.class)
-                        .getStatusCode()
-                        .equals(HttpStatus.BAD_REQUEST));
+                        this.restTemplate
+                                .postForEntity(url("/register"), badJSON, JWTResponse.class)
+                                .getStatusCode()
+                                .equals(HttpStatus.BAD_REQUEST))
+                .isTrue();
     }
 
     @Test
@@ -71,12 +70,15 @@ public class AuthenticationTests extends SmartHutTest {
 
         final ResponseEntity res =
                 this.restTemplate.postForEntity(url("/register"), request, JsonObject.class);
-        assertThat(res.getStatusCode().equals(HttpStatus.BAD_REQUEST));
-        assertThat(res.getBody() != null);
+        assertThat(res.getStatusCode().equals(HttpStatus.BAD_REQUEST)).isTrue();
+        assertThat(res.getBody()).isNotNull();
 
         final JsonArray errors = res.getBody().getAsJsonArray("errors");
-        assertThat(errors.size() == 1);
-        assertThat(errors.get(0).getAsJsonObject().get("field").getAsString().equals("password"));
+        assertThat(errors)
+                .allSatisfy(
+                        e ->
+                                assertThat(e.getAsJsonObject().get("field").getAsString())
+                                        .isEqualTo("password"));
     }
 
     @Test
@@ -89,12 +91,15 @@ public class AuthenticationTests extends SmartHutTest {
 
         final ResponseEntity res =
                 this.restTemplate.postForEntity(url("/register"), request, JsonObject.class);
-        assertThat(res.getStatusCode().equals(HttpStatus.BAD_REQUEST));
-        assertThat(res.getBody() != null);
+        assertThat(res.getStatusCode().equals(HttpStatus.BAD_REQUEST)).isTrue();
+        assertThat(res.getBody()).isNotNull();
 
         final JsonArray errors = res.getBody().getAsJsonArray("errors");
-        assertThat(errors.size() == 1);
-        assertThat(errors.get(0).getAsJsonObject().get("field").getAsString().equals("email"));
+        assertThat(errors)
+                .allSatisfy(
+                        e ->
+                                assertThat(e.getAsJsonObject().get("field").getAsString())
+                                        .isEqualTo("email"));
     }
 
     @Test
@@ -106,12 +111,15 @@ public class AuthenticationTests extends SmartHutTest {
 
         final ResponseEntity res =
                 this.restTemplate.postForEntity(url("/register"), request, JsonObject.class);
-        assertThat(res.getStatusCode().equals(HttpStatus.BAD_REQUEST));
-        assertThat(res.getBody() != null);
+        assertThat(res.getStatusCode().equals(HttpStatus.BAD_REQUEST)).isTrue();
+        assertThat(res.getBody() != null).isTrue();
 
         final JsonArray errors = res.getBody().getAsJsonArray("errors");
-        assertThat(errors.size() == 1);
-        assertThat(errors.get(0).getAsJsonObject().get("field").getAsString().equals("name"));
+        assertThat(errors)
+                .allSatisfy(
+                        e ->
+                                assertThat(e.getAsJsonObject().get("field").getAsString())
+                                        .isEqualTo("name"));
     }
 
     @Test
@@ -123,51 +131,15 @@ public class AuthenticationTests extends SmartHutTest {
 
         final ResponseEntity res =
                 this.restTemplate.postForEntity(url("/register"), request, JsonObject.class);
-        assertThat(res.getStatusCode().equals(HttpStatus.BAD_REQUEST));
-        assertThat(res.getBody() != null);
+        assertThat(res.getStatusCode().equals(HttpStatus.BAD_REQUEST)).isTrue();
+        assertThat(res.getBody() != null).isTrue();
 
         final JsonArray errors = res.getBody().getAsJsonArray("errors");
-        assertThat(errors.size() == 1);
-        assertThat(errors.get(0).getAsJsonObject().get("field").getAsString().equals("username"));
-    }
-
-    @Test
-    public void registrationShouldReturnBadRequestWithDuplicateData() {
-        {
-            final ResponseEntity res =
-                    this.restTemplate.postForEntity(
-                            url("/register"),
-                            getDisabledUser(),
-                            DuplicateRegistrationException.class);
-            assertThat(res.getStatusCode().equals(HttpStatus.BAD_REQUEST));
-            assertThat(res.getBody() != null);
-        }
-
-        {
-            final UserRegistrationRequest disabledUserDifferentMail = getDisabledUser();
-            enabledUser.setEmail("another@example.com");
-
-            final ResponseEntity res =
-                    this.restTemplate.postForEntity(
-                            url("/register"),
-                            disabledUserDifferentMail,
-                            DuplicateRegistrationException.class);
-            assertThat(res.getStatusCode().equals(HttpStatus.BAD_REQUEST));
-            assertThat(res.getBody() != null);
-        }
-
-        {
-            final UserRegistrationRequest disabledUserDifferentUsername = getDisabledUser();
-            enabledUser.setUsername("another");
-
-            final ResponseEntity res =
-                    this.restTemplate.postForEntity(
-                            url("/register"),
-                            disabledUserDifferentUsername,
-                            DuplicateRegistrationException.class);
-            assertThat(res.getStatusCode().equals(HttpStatus.BAD_REQUEST));
-            assertThat(res.getBody() != null);
-        }
+        assertThat(errors)
+                .allSatisfy(
+                        j ->
+                                assertThat(j.getAsJsonObject().get("field").getAsString())
+                                        .isEqualTo("username"));
     }
 
     @Test
@@ -178,10 +150,9 @@ public class AuthenticationTests extends SmartHutTest {
         request.setEmail("smarthut.sm@example.com");
         request.setPassword("password");
 
-        final ResponseEntity res =
-                this.restTemplate.postForEntity(url("/register"), request, OkResponse.class);
-        assertThat(res.getStatusCode().equals(HttpStatus.OK));
-        assertThat(res.getBody() != null);
+        final ResponseEntity res =
+                this.restTemplate.postForEntity(url("/register"), request, Object.class);
+        assertThat(res.getStatusCode()).isEqualTo(HttpStatus.OK);
     }
 
     @Test
@@ -189,10 +160,11 @@ public class AuthenticationTests extends SmartHutTest {
         final Map badJSON = Map.of("badkey", 3, "password", "ciaomamma");
 
         assertThat(
-                this.restTemplate
-                        .postForEntity(url("/auth/login"), badJSON, JWTResponse.class)
-                        .getStatusCode()
-                        .equals(HttpStatus.BAD_REQUEST));
+                        this.restTemplate
+                                .postForEntity(url("/auth/login"), badJSON, JWTResponse.class)
+                                .getStatusCode()
+                                .equals(HttpStatus.BAD_REQUEST))
+                .isTrue();
     }
 
     @Test
@@ -204,9 +176,9 @@ public class AuthenticationTests extends SmartHutTest {
         final ResponseEntity res =
                 this.restTemplate.postForEntity(
                         url("/auth/login"), request, UnauthorizedException.class);
-        assertThat(res.getStatusCode().equals(HttpStatus.UNAUTHORIZED));
-        assertThat(res.getBody() != null);
-        assertThat(!res.getBody().isUserDisabled());
+        assertThat(res.getStatusCode().equals(HttpStatus.UNAUTHORIZED)).isTrue();
+        assertThat(res.getBody() != null).isTrue();
+        assertThat(!res.getBody().isUserDisabled()).isTrue();
     }
 
     @Test
@@ -218,22 +190,7 @@ public class AuthenticationTests extends SmartHutTest {
         final ResponseEntity res =
                 this.restTemplate.postForEntity(
                         url("/auth/login"), request, UnauthorizedException.class);
-        assertThat(res.getStatusCode().equals(HttpStatus.UNAUTHORIZED));
-        assertThat(res.getBody() != null);
-        assertThat(res.getBody().isUserDisabled());
-    }
-
-    @Test
-    public void loginShouldReturnTokenWithEnabledUser() {
-        final JWTRequest request = new JWTRequest();
-        request.setUsernameOrEmail("enabled");
-        request.setPassword("password");
-
-        final ResponseEntity res =
-                this.restTemplate.postForEntity(url("/auth/login"), request, JWTResponse.class);
-        assertThat(res.getStatusCode().equals(HttpStatus.OK));
-        assertThat(res.getBody() != null);
-        assertThat(res.getBody().getToken() != null);
-        assertThat(!res.getBody().getToken().isEmpty());
+        assertThat(res.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
+        assertThat(res.getBody()).isNotNull();
     }
 }
diff --git a/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmartHutTest.java b/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmartHutTest.java
index f2b737a..4ac1a0f 100644
--- a/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmartHutTest.java
+++ b/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmartHutTest.java
@@ -2,7 +2,6 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
-import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.OkResponse;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.UserRegistrationRequest;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationToken;
 import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ConfirmationTokenRepository;
@@ -15,7 +14,7 @@ import org.springframework.http.ResponseEntity;
 import org.springframework.web.reactive.function.client.WebClient;
 
 public abstract class SmartHutTest {
-    private boolean setupDone = false;
+    private static boolean setupDone = false;
 
     protected final String getBaseURL() {
         return "http://localhost:2000/";
@@ -40,9 +39,9 @@ public abstract class SmartHutTest {
             final TestRestTemplate restTemplate,
             final UserRepository userRepository,
             final ConfirmationTokenRepository tokenRepository) {
-        final ResponseEntity res2 =
-                restTemplate.postForEntity(this.url("/register"), enabledUser, OkResponse.class);
-        assertThat(res2.getStatusCode().equals(HttpStatus.OK));
+        final ResponseEntity res2 =
+                restTemplate.postForEntity(this.url("/register"), enabledUser, Object.class);
+        assertThat(res2.getStatusCode()).isEqualTo(HttpStatus.OK);
 
         final User persistedEnabledUser = userRepository.findByUsername("enabled");
         final ConfirmationToken token = tokenRepository.findByUser(persistedEnabledUser);
@@ -50,13 +49,14 @@ public abstract class SmartHutTest {
         final ResponseEntity res3 =
                 WebClient.create(getBaseURL())
                         .get()
-                        .uri("/register/confirm-account?token=" + token.getConfirmationToken())
+                        .uri("/register/confirm-account?token=" + token.getConfirmToken())
                         .retrieve()
                         .toBodilessEntity()
                         .block();
 
-        assertThat(res3.getStatusCode().is2xxSuccessful());
-        assertThat(userRepository.findByUsername("enabled").getEnabled());
+        assertThat(res3).isNotNull();
+        assertThat(res3.getStatusCode()).isEqualTo(HttpStatus.FOUND);
+        assertThat(userRepository.findByUsername("enabled").getEnabled()).isTrue();
     }
 
     @BeforeEach
diff --git a/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmarthutApplicationTests.java b/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmarthutApplicationTests.java
index dbd7e21..2aaa1b1 100644
--- a/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmarthutApplicationTests.java
+++ b/src/test/java/ch/usi/inf/sa4/sanmarinoes/smarthut/SmarthutApplicationTests.java
@@ -16,11 +16,8 @@ public class SmarthutApplicationTests extends SmartHutTest {
     @Autowired private TestRestTemplate restTemplate;
 
     @Test
-    public void anonymousGreetingShouldNotBeAuthorized() throws Exception {
-        assertThat(
-                this.restTemplate
-                        .getForEntity(getBaseURL(), Void.class)
-                        .getStatusCode()
-                        .equals(HttpStatus.UNAUTHORIZED));
+    public void anonymousGreetingShouldNotBeAuthorized() {
+        assertThat(this.restTemplate.getForEntity(getBaseURL(), Void.class).getStatusCode())
+                .isEqualTo(HttpStatus.UNAUTHORIZED);
     }
 }
diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties
index bdaafc0..1db382b 100644
--- a/src/test/resources/application.properties
+++ b/src/test/resources/application.properties
@@ -34,4 +34,5 @@ email.registrationRedirect=http://localhost:3000
 email.resetpasswordSubject=SmartHut.sm password reset
 email.resetpassword=To reset your password, please click here:
 email.resetpasswordPath=http://localhost:3000/password-reset?token=
-email.resetPasswordRedirect=http://localhost:3000/conf-reset-pass
\ No newline at end of file
+email.resetPasswordRedirect=http://localhost:3000/conf-reset-pass
+camera.videoUrl="/security_camera_videos/security_camera_1.mp4"
\ No newline at end of file