From ba73348a743cb34e9c9a3456dc676228def4986d Mon Sep 17 00:00:00 2001 From: "Claudio Maggioni (maggicl)" Date: Tue, 28 Apr 2020 10:05:08 +0200 Subject: [PATCH 01/16] fixz --- .../smarthut/controller/ButtonDimmerController.java | 5 +++++ .../smarthut/controller/KnobDimmerController.java | 5 +++++ .../sanmarinoes/smarthut/controller/SwitchController.java | 5 +++++ 3 files changed, 15 insertions(+) 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 6b13ca9..15f4825 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 @@ -31,6 +31,11 @@ public class ButtonDimmerController this.buttonDimmerRepository = inputRepository; } + @GetMapping("/{id}") + public ButtonDimmer findById(@PathVariable("id") long id) throws NotFoundException { + return buttonDimmerRepository.findById(id).orElseThrow(NotFoundException::new); + } + @PostMapping public ButtonDimmer create( @Valid @RequestBody final GenericDeviceSaveReguest bd, final Principal principal) 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 9b30535..a8333f5 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 @@ -27,6 +27,11 @@ public class KnobDimmerController extends InputDeviceConnectionController Date: Tue, 28 Apr 2020 10:48:09 +0200 Subject: [PATCH 02/16] fix --- .../sanmarinoes/smarthut/controller/ThermostatController.java | 2 +- .../inf/sa4/sanmarinoes/smarthut/service/ThermostatService.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) 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 6761ee5..7a6fc59 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 @@ -32,8 +32,8 @@ public class ThermostatController { newT.setUseExternalSensors(t.isUseExternalSensors()); newT.setOn(t.isTurnOn()); - newT = deviceService.saveAsOwner(newT, principal.getName()); thermostatService.populateMeasuredTemperature(newT); + newT = deviceService.saveAsOwner(newT, principal.getName()); return newT; } 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 e214e39..cdbb92c 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 @@ -72,7 +72,6 @@ public class ThermostatService { populateMeasuredTemperature(u); t = Optional.of(u); } - return t; } From 3d74e02434179c25de37bab92cea0363c054102d Mon Sep 17 00:00:00 2001 From: "Claudio Maggioni (maggicl)" Date: Fri, 1 May 2020 20:04:44 +0200 Subject: [PATCH 03/16] Fixed GSON serializations of triggers --- .../usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java index 1204a60..4b69a07 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/GsonConfig.java @@ -4,6 +4,7 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.AutomationFastUpdateRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.DimmableState; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.State; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.SwitchableState; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Trigger; import com.google.gson.*; import java.lang.reflect.Type; import org.springframework.context.annotation.Bean; @@ -27,6 +28,7 @@ public class GsonConfig { private static GsonBuilder configureBuilder() { final GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(Json.class, new SpringfoxJsonToGsonAdapter()); + @SuppressWarnings({"rawTypes"}) RuntimeTypeAdapterFactory runtimeTypeAdapterFactory = RuntimeTypeAdapterFactory.of(State.class, "kind") .registerSubtype(SwitchableState.class, "switchableState") @@ -43,6 +45,10 @@ public class GsonConfig { "rangeTrigger"); builder.registerTypeAdapterFactory(runtimeTypeAdapterFactory); builder.registerTypeAdapterFactory(runtimeTypeAdapterFactoryII); + builder.registerTypeAdapter( + Trigger.class, + (JsonSerializer>) + (src, typeOfSrc, context) -> context.serialize((Object) src)); return builder; } From e0ab831be90a143dd6ee25ff2780eec6045abf52 Mon Sep 17 00:00:00 2001 From: "Claudio Maggioni (maggicl)" Date: Sat, 2 May 2020 11:12:11 +0200 Subject: [PATCH 04/16] Thermostats fixed --- .../smarthut/controller/ThermostatController.java | 7 +++++-- .../smarthut/dto/ThermostatSaveRequest.java | 10 ---------- .../sa4/sanmarinoes/smarthut/models/Thermostat.java | 12 ++++++++++++ 3 files changed, 17 insertions(+), 12 deletions(-) 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 7a6fc59..12cd09b 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 @@ -28,11 +28,14 @@ public class ThermostatController { newT.setId(t.getId()); newT.setName(t.getName()); newT.setRoomId(t.getRoomId()); - newT.setMeasuredTemperature(t.getMeasuredTemperature()); newT.setUseExternalSensors(t.isUseExternalSensors()); - newT.setOn(t.isTurnOn()); + 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; } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ThermostatSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ThermostatSaveRequest.java index 3986996..5ac3402 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ThermostatSaveRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ThermostatSaveRequest.java @@ -22,8 +22,6 @@ public class ThermostatSaveRequest { @NotNull private boolean useExternalSensors; - @NotNull private BigDecimal measuredTemperature; - /** State of this thermostat */ @NotNull private boolean turnOn; @@ -74,12 +72,4 @@ public class ThermostatSaveRequest { public void setId(long id) { this.id = id; } - - public BigDecimal getMeasuredTemperature() { - return measuredTemperature; - } - - public void setMeasuredTemperature(BigDecimal measuredTemperature) { - this.measuredTemperature = measuredTemperature; - } } 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 e8f0e86..c02c4e4 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 @@ -87,6 +87,18 @@ public class Thermostat extends Switchable implements BooleanTriggerable { this.mode = Mode.OFF; } + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Thermostat{"); + sb.append("targetTemperature=").append(targetTemperature); + sb.append(", internalSensorTemperature=").append(internalSensorTemperature); + sb.append(", mode=").append(mode); + sb.append(", measuredTemperature=").append(measuredTemperature); + sb.append(", useExternalSensors=").append(useExternalSensors); + sb.append('}'); + return sb.toString(); + } + public void setMode(Mode state) { this.mode = state; } From c3d6590507a62c9fb9a8a0a4f2e3debd73b15442 Mon Sep 17 00:00:00 2001 From: "Claudio Maggioni (maggicl)" Date: Sat, 2 May 2020 13:58:28 +0200 Subject: [PATCH 05/16] Thermostats fixed (for scenes) --- .../smarthut/controller/SceneController.java | 2 +- .../smarthut/models/Thermostat.java | 8 +++++++ .../smarthut/service/DeviceService.java | 22 +++++++++++-------- .../smarthut/service/SceneService.java | 7 +++--- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SceneController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SceneController.java index 0cddba2..4de0b89 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SceneController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SceneController.java @@ -68,7 +68,7 @@ public class SceneController { .findByIdAndUsername(id, principal.getName()) .orElseThrow(NotFoundException::new); - return sceneService.apply(newScene); + return sceneService.apply(newScene, principal.getName()); } @PutMapping("/{id}") 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 c02c4e4..e501ba4 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,9 +6,11 @@ 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 @@ -34,6 +36,12 @@ public class Thermostat extends Switchable implements BooleanTriggerable { BigDecimal measured = this.getMeasuredTemperature(); BigDecimal target = this.getTargetTemperature(); + + if (measured == null) { + this.setMode(Thermostat.Mode.IDLE); + return true; + } + BigDecimal delta = target.subtract(measured); if (delta.abs().doubleValue() < 0.25) { 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 0bc2a6e..379b86f 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 @@ -37,7 +37,7 @@ public class DeviceService { } } - private void triggerTriggers(Device device) { + private void triggerTriggers(Device device, final String username) { final long deviceId = device.getId(); @@ -57,7 +57,7 @@ public class DeviceService { sceneRepository .findById(t.getSceneId()) .orElseThrow(IllegalStateException::new)) - .forEach(sceneService::apply); + .forEach((s) -> sceneService.apply(s, username)); } public List findAll(Long hostId, String username) throws NotFoundException { @@ -97,11 +97,7 @@ public class DeviceService { } } - for (Device d : devices) { - if (d instanceof Thermostat) { - thermostatService.populateMeasuredTemperature((Thermostat) d); - } - } + populateComputedFields(devices); return toList(devices); } catch (NotFoundException e) { @@ -110,6 +106,14 @@ public class DeviceService { } } + 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); @@ -160,7 +164,7 @@ public class DeviceService { devices.forEach((d) -> propagateUpdateAsOwner(d, username)); if (!fromScene) { - devices.forEach(this::triggerTriggers); + devices.forEach((d) -> triggerTriggers(d, username)); } return toList(devices); @@ -172,7 +176,7 @@ public class DeviceService { propagateUpdateAsOwner(device, username); if (!fromScene) { - triggerTriggers(device); + triggerTriggers(device, username); } return device; 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 826d896..5b6dc7f 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 @@ -13,16 +13,17 @@ import org.springframework.stereotype.Component; public class SceneService { @Autowired private DeviceRepository deviceRepository; + @Autowired private DeviceService deviceService; - public List apply(Scene newScene) { + public List apply(Scene newScene, String username) { final List updated = new ArrayList<>(); for (final State s : newScene.getStates()) { s.apply(); updated.add(s.getDevice()); } - deviceRepository.saveAll(updated); - + deviceService.saveAllAsOwner(updated, username, true); + deviceService.populateComputedFields(updated); return updated; } } From 7ae75223591af059078bf2f42208812c3bb46455 Mon Sep 17 00:00:00 2001 From: "Claudio Maggioni (maggicl)" Date: Sat, 2 May 2020 14:53:12 +0200 Subject: [PATCH 06/16] Fixed propagation of device updates on automation scene application --- .../controller/ButtonDimmerController.java | 2 +- .../InputDeviceConnectionController.java | 4 +- .../controller/KnobDimmerController.java | 2 +- .../smarthut/controller/SceneController.java | 2 +- .../smarthut/controller/SwitchController.java | 2 +- .../smarthut/service/DeviceService.java | 58 ++++++++++++++----- .../smarthut/service/SceneService.java | 4 +- 7 files changed, 50 insertions(+), 24 deletions(-) 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 15f4825..2663211 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 @@ -67,7 +67,7 @@ public class ButtonDimmerController break; } - deviceService.saveAllAsOwner(buttonDimmer.getOutputs(), principal.getName(), false); + deviceService.saveAllAsOwner(buttonDimmer.getOutputs(), principal.getName()); return buttonDimmer.getOutputs(); } 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 4d1a371..51ea46e 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 @@ -91,7 +91,7 @@ public abstract class InputDeviceConnectionController< connector.connect(pair.input, o, true); } - deviceService.saveAllAsOwner(pair.outputs, username, false); + deviceService.saveAllAsOwner(pair.outputs, username); return pair.input.getOutputs(); } @@ -111,7 +111,7 @@ public abstract class InputDeviceConnectionController< connector.connect(pair.input, o, false); } - deviceService.saveAllAsOwner(pair.outputs, username, false); + deviceService.saveAllAsOwner(pair.outputs, username); return pair.input.getOutputs(); } 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 a8333f5..14bdedd 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 @@ -54,7 +54,7 @@ public class KnobDimmerController extends InputDeviceConnectionController sceneService.apply(s, username)); + .forEach((s) -> sceneService.apply(s, username, true)); } public List findAll(Long hostId, String username) throws NotFoundException { @@ -145,7 +145,15 @@ public class DeviceService { return device; } - private void propagateUpdateAsOwner(Device device, String username) { + /** + * 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 @@ -155,13 +163,33 @@ public class DeviceService { // broadcast to endpoint the object device, with receiving user set to guest endpoint.queueDeviceUpdate(device, guest); } + + if (causedByTrigger) { + endpoint.queueDeviceUpdate(device, user); + } } + /** + * 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) { + Iterable devices, String username, boolean fromScene, boolean fromTrigger) { devices.forEach(d -> renameIfDuplicate(d, username)); devices = deviceRepository.saveAll(devices); - devices.forEach((d) -> propagateUpdateAsOwner(d, username)); + devices.forEach((d) -> propagateUpdateAsOwner(d, username, fromScene && fromTrigger)); if (!fromScene) { devices.forEach((d) -> triggerTriggers(d, username)); @@ -170,20 +198,18 @@ public class DeviceService { return toList(devices); } - public T saveAsOwner(T device, String username, boolean fromScene) { - renameIfDuplicate(device, username); - device = deviceRepository.save(device); - propagateUpdateAsOwner(device, username); - - if (!fromScene) { - triggerTriggers(device, username); - } - - return device; + public List saveAllAsOwner(Iterable devices, String username) { + return saveAllAsOwner(devices, username, false, false); } public T saveAsOwner(T device, String username) { - return saveAsOwner(device, username, false); + renameIfDuplicate(device, username); + device = deviceRepository.save(device); + propagateUpdateAsOwner(device, username, false); + + triggerTriggers(device, username); + + return device; } public void delete(Long id, String username) throws NotFoundException { @@ -193,6 +219,6 @@ public class DeviceService { .orElseThrow(NotFoundException::new); deviceRepository.delete(device); - propagateUpdateAsOwner(device, username); + propagateUpdateAsOwner(device, username, false); } } 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 5b6dc7f..04a8877 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 @@ -15,14 +15,14 @@ public class SceneService { @Autowired private DeviceRepository deviceRepository; @Autowired private DeviceService deviceService; - public List apply(Scene newScene, String username) { + public List apply(Scene newScene, String username, boolean fromTrigger) { final List updated = new ArrayList<>(); for (final State s : newScene.getStates()) { s.apply(); updated.add(s.getDevice()); } - deviceService.saveAllAsOwner(updated, username, true); + deviceService.saveAllAsOwner(updated, username, true, fromTrigger); deviceService.populateComputedFields(updated); return updated; } From 92e93c80e8f1aa064c910d60e068ca8555fca48d Mon Sep 17 00:00:00 2001 From: "Claudio Maggioni (maggicl)" Date: Sat, 2 May 2020 16:41:11 +0200 Subject: [PATCH 07/16] WIP --- .../smarthut/controller/GuestController.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) 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 b0994a1..cfc0f36 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 @@ -4,8 +4,8 @@ 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.error.NotFoundException; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.EagerUserRepository; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User; -import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository; import java.security.Principal; import java.util.List; import javax.validation.Valid; @@ -18,13 +18,19 @@ import org.springframework.web.bind.annotation.*; @RequestMapping("/user") public class GuestController { - @Autowired private UserRepository userRepository; + @Autowired private EagerUserRepository userRepository; @GetMapping public List findAll() { return toList(userRepository.findAll()); } + @GetMapping("/hosts") + public List findHosts(final Principal principal) { + final User u = userRepository.findByUsername(principal.getName()); + return toList(u.getHosts()); + } + @PostMapping("/guest") public User addUserAsGuest(@RequestParam("userId") long id, final Principal principal) throws NotFoundException { From 6263bd2d3530cbaf64c2b0841365e58c926c7da6 Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Sat, 2 May 2020 20:50:50 +0200 Subject: [PATCH 08/16] WIP --- .../smarthut/controller/GuestController.java | 17 +++++++++++------ .../smarthut/dto/UserResponse.java | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/UserResponse.java 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 cfc0f36..8e2274f 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 @@ -1,13 +1,15 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; -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.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; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -21,14 +23,16 @@ public class GuestController { @Autowired private EagerUserRepository userRepository; @GetMapping - public List findAll() { - return toList(userRepository.findAll()); + public List findAll() { + return StreamSupport.stream(userRepository.findAll().spliterator(), false) + .map(UserResponse::fromUser) + .collect(Collectors.toList()); } @GetMapping("/hosts") - public List findHosts(final Principal principal) { + public List findHosts(final Principal principal) { final User u = userRepository.findByUsername(principal.getName()); - return toList(u.getHosts()); + return u.getHosts().stream().map(UserResponse::fromUser).collect(Collectors.toList()); } @PostMapping("/guest") @@ -52,7 +56,7 @@ public class GuestController { } @DeleteMapping("/guest") - public void removeUserAsGuest(@RequestParam("userId") long id, final Principal principal) + public User removeUserAsGuest(@RequestParam("userId") long id, final Principal principal) throws NotFoundException { User guest = userRepository.findById(id).orElseThrow(NotFoundException::new); User host = userRepository.findByUsername(principal.getName()); @@ -61,5 +65,6 @@ public class GuestController { guest.getHosts().remove(host); userRepository.save(host); userRepository.save(guest); + return host; } } 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 new file mode 100644 index 0000000..2ed2c09 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/UserResponse.java @@ -0,0 +1,19 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; + +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User; + +public class UserResponse { + private Long id; + private String username; + private String name; + + private UserResponse() {} + + public static UserResponse fromUser(User u) { + final UserResponse us = new UserResponse(); + us.name = u.getName(); + us.id = u.getId(); + us.username = u.getUsername(); + return us; + } +} From 25eb9555c0b92204c5bd0b8685bec925226e16c8 Mon Sep 17 00:00:00 2001 From: "Claudio Maggioni (maggicl)" Date: Sat, 2 May 2020 22:37:54 +0200 Subject: [PATCH 09/16] Fixed for frontend to guest controllers --- .../smarthut/controller/GuestController.java | 47 +++++++++++-------- .../smarthut/dto/GuestsUpdateRequest.java | 8 ++++ 2 files changed, 35 insertions(+), 20 deletions(-) create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/GuestsUpdateRequest.java 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 8e2274f..f010b52 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 @@ -1,13 +1,16 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller; +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; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.StreamSupport; import javax.validation.Valid; @@ -35,16 +38,33 @@ public class GuestController { return u.getHosts().stream().map(UserResponse::fromUser).collect(Collectors.toList()); } - @PostMapping("/guest") - public User addUserAsGuest(@RequestParam("userId") long id, final Principal principal) + @GetMapping("/guests") + public List findGuests(final Principal principal) { + final User u = userRepository.findByUsername(principal.getName()); + return u.getGuests().stream().map(UserResponse::fromUser).collect(Collectors.toList()); + } + + @PutMapping("/guests") + public List setGuests( + @RequestBody @Valid GuestsUpdateRequest g, final Principal principal) throws NotFoundException { - User guest = userRepository.findById(id).orElseThrow(NotFoundException::new); + Iterable guests = userRepository.findAllById(g.ids); User host = userRepository.findByUsername(principal.getName()); - host.addGuest(guest); - guest.addHost(host); - userRepository.save(guest); - return userRepository.save(host); + for (final User oldGuest : host.getGuests()) { + oldGuest.getHosts().remove(host); + } + + final Set oldGuests = Set.copyOf(host.getGuests()); + + for (final User guest : guests) { + host.addGuest(guest); + guest.addHost(host); + } + + userRepository.saveAll(oldGuests); + userRepository.save(host); + return toList(userRepository.saveAll(guests)); } @PutMapping("/permissions") @@ -54,17 +74,4 @@ public class GuestController { currentUser.setCameraEnabled(g.isCameraEnabled()); return userRepository.save(currentUser); } - - @DeleteMapping("/guest") - public User removeUserAsGuest(@RequestParam("userId") long id, final Principal principal) - throws NotFoundException { - User guest = userRepository.findById(id).orElseThrow(NotFoundException::new); - User host = userRepository.findByUsername(principal.getName()); - - host.removeGuest(guest); - guest.getHosts().remove(host); - userRepository.save(host); - userRepository.save(guest); - return host; - } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/GuestsUpdateRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/GuestsUpdateRequest.java new file mode 100644 index 0000000..6e98937 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/GuestsUpdateRequest.java @@ -0,0 +1,8 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; + +import java.util.List; +import javax.validation.constraints.NotNull; + +public class GuestsUpdateRequest { + @NotNull public List ids; +} From aad6cc52ce61a682abfe9863f0a8bbdc45f6c033 Mon Sep 17 00:00:00 2001 From: "Claudio Maggioni (maggicl)" Date: Sun, 3 May 2020 14:14:24 +0200 Subject: [PATCH 10/16] Fixed thermostat socket updates --- .../sanmarinoes/smarthut/models/Thermostat.java | 17 ++++------------- .../smarthut/service/ThermostatService.java | 12 +++++------- 2 files changed, 9 insertions(+), 20 deletions(-) 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 e501ba4..632dfd7 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 @@ -24,14 +24,10 @@ public class Thermostat extends Switchable implements BooleanTriggerable { computeState(); } - /** - * Computes the new thermostat state, for when the thermostat is on; - * - * @return true if the state changed, false if not; - */ - public boolean computeState() { + /** Computes the new thermostat state, for when the thermostat is on */ + public void computeState() { if (mode == Thermostat.Mode.OFF) { - return false; + return; } BigDecimal measured = this.getMeasuredTemperature(); @@ -39,23 +35,18 @@ public class Thermostat extends Switchable implements BooleanTriggerable { if (measured == null) { this.setMode(Thermostat.Mode.IDLE); - return true; + return; } BigDecimal delta = target.subtract(measured); if (delta.abs().doubleValue() < 0.25) { - if (this.getMode() == Thermostat.Mode.IDLE) return false; this.setMode(Thermostat.Mode.IDLE); } else if (delta.signum() > 0) { - if (this.getMode() == Thermostat.Mode.HEATING) return false; this.setMode(Thermostat.Mode.HEATING); } else { - if (this.getMode() == Thermostat.Mode.COOLING) return false; this.setMode(Thermostat.Mode.COOLING); } - - return true; } @Override 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 cdbb92c..91d0b18 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 @@ -45,18 +45,16 @@ public class ThermostatService { return Utils.toList(all); } - public boolean computeState(Thermostat t) { + public void computeState(Thermostat t) { populateMeasuredTemperature(t); - return t.computeState(); + t.computeState(); } private void updateState(Thermostat t) { - boolean shouldUpdate = this.computeState(t); + this.computeState(t); - if (shouldUpdate) { - deviceService.saveAsOwner(t, thermostatRepository.findUser(t.getId()).getUsername()); - endpoint.queueDeviceUpdate(t, thermostatRepository.findUser(t.getId())); - } + deviceService.saveAsOwner(t, thermostatRepository.findUser(t.getId()).getUsername()); + endpoint.queueDeviceUpdate(t, thermostatRepository.findUser(t.getId())); } public void updateStates() { From 69d1b38ff20f15e8e423b8d58c8cde6e06bab0e9 Mon Sep 17 00:00:00 2001 From: "Claudio Maggioni (maggicl)" Date: Sun, 3 May 2020 19:50:00 +0200 Subject: [PATCH 11/16] other minor fixes^2 --- .../sanmarinoes/smarthut/models/Device.java | 14 ++++---- .../smarthut/scheduled/UpdateTasks.java | 2 +- .../smarthut/service/DeviceService.java | 14 +++----- .../smarthut/service/MotionSensorService.java | 4 +-- .../smarthut/service/SensorService.java | 3 +- .../smarthut/service/ThermostatService.java | 2 +- .../smarthut/socket/SensorSocketEndpoint.java | 33 +++++++++++++++---- 7 files changed, 44 insertions(+), 28 deletions(-) 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 397bb1e..d2231d8 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 @@ -71,14 +71,18 @@ public abstract class Device { @SocketGsonExclude private Set> states = new HashSet<>(); - @Transient @GsonExclude private boolean fromHost = false; + @Transient @GsonExclude private Long fromHostId = null; @Transient @GsonExclude private boolean fromGuest = false; @Transient @GsonExclude private boolean deleted = false; - public boolean isFromHost() { - return fromHost; + public Long getFromHostId() { + return fromHostId; + } + + public void setFromHostId(Long fromHostId) { + this.fromHostId = fromHostId; } public boolean isDeleted() { @@ -97,10 +101,6 @@ public abstract class Device { this.fromGuest = fromGuest; } - public void setFromHost(boolean fromHost) { - this.fromHost = fromHost; - } - public long getId() { return id; } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/UpdateTasks.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/UpdateTasks.java index e701166..ec0f43f 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/UpdateTasks.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/UpdateTasks.java @@ -78,7 +78,7 @@ public class UpdateTasks { c.forEach( s -> sensorSocketEndpoint.queueDeviceUpdate( - s, sensorRepository.findUser(s.getId()))); + s, sensorRepository.findUser(s.getId()), false, null, false)); } /** Sends device updates through sensor socket in batch every one second */ 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 b4bde1b..a0116c2 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 @@ -125,21 +125,17 @@ public class DeviceService { // We're telling the host that a guest has modified a device. Therefore, fromGuest becomes // true. - device.setFromHost(false); - device.setFromGuest(true); // broadcast device update to host - endpoint.queueDeviceUpdate(device, 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. - device.setFromHost(true); - device.setFromGuest(false); for (final User guest : guests) { if (guest.equals(currentUser)) { continue; } // enqueue all device updates for all other guests - endpoint.queueDeviceUpdate(device, guest); + endpoint.queueDeviceUpdate(device, guest, false, host.getId(), false); } return device; @@ -157,15 +153,13 @@ public class DeviceService { final User user = userRepository.findByUsername(username); final Set guests = user.getGuests(); // make sure we're broadcasting from host - device.setFromHost(true); - device.setFromGuest(false); for (final User guest : guests) { // broadcast to endpoint the object device, with receiving user set to guest - endpoint.queueDeviceUpdate(device, guest); + endpoint.queueDeviceUpdate(device, guest, false, user.getId(), false); } if (causedByTrigger) { - endpoint.queueDeviceUpdate(device, user); + endpoint.queueDeviceUpdate(device, user, false, user.getId(), false); } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/MotionSensorService.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/MotionSensorService.java index 01a7401..f4288ee 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/MotionSensorService.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/MotionSensorService.java @@ -14,7 +14,7 @@ public class MotionSensorService { @Autowired private MotionSensorRepository motionSensorRepository; /** - * Updates detection status of given motion sensor and propagates update throgh socket + * Updates detection status of given motion sensor and propagates update through socket * * @param sensor the motion sensor to update * @param detected the new detection status @@ -26,7 +26,7 @@ public class MotionSensorService { final MotionSensor toReturn = deviceService.saveAsOwner(sensor, username); sensorSocketEndpoint.queueDeviceUpdate( - sensor, motionSensorRepository.findUser(sensor.getId())); + sensor, motionSensorRepository.findUser(sensor.getId()), false, null, false); return toReturn; } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/SensorService.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/SensorService.java index 0e4fbbd..532442b 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/SensorService.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/SensorService.java @@ -43,7 +43,8 @@ public class SensorService { sensor = deviceService.saveAsOwner( sensor, sensorRepository.findUser(sensor.getId()).getUsername()); - endpoint.queueDeviceUpdate(sensor, sensorRepository.findUser(sensor.getId())); + endpoint.queueDeviceUpdate( + sensor, sensorRepository.findUser(sensor.getId()), false, null, false); return sensor; } } 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 91d0b18..328d092 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 @@ -54,7 +54,7 @@ public class ThermostatService { this.computeState(t); deviceService.saveAsOwner(t, thermostatRepository.findUser(t.getId()).getUsername()); - endpoint.queueDeviceUpdate(t, thermostatRepository.findUser(t.getId())); + endpoint.queueDeviceUpdate(t, thermostatRepository.findUser(t.getId()), false, null, false); } public void updateStates() { 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 b68cb25..4764f18 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,6 +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 com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; @@ -21,6 +22,8 @@ public class SensorSocketEndpoint extends Endpoint { private Gson gson = GsonConfig.socketGson(); + @Autowired private DeviceService deviceService; + private UserRepository userRepository; private JWTTokenUtils jwtTokenUtils; @@ -28,7 +31,10 @@ public class SensorSocketEndpoint extends Endpoint { private Multimap authorizedClients = Multimaps.synchronizedMultimap(HashMultimap.create()); - private final Map> messages = new HashMap<>(); + // messages are now stored as strings as a "hack" to capture and clone the state of the device, + // since + // fromHost and fromGuest are just mutable properties and hibernate caches the object. + private final Map> messages = new HashMap<>(); @Autowired public SensorSocketEndpoint(UserRepository userRepository, JWTTokenUtils jwtTokenUtils) { @@ -41,18 +47,33 @@ public class SensorSocketEndpoint extends Endpoint { * * @param device the device update to be sent * @param u the user the device belongs + * @param fromGuest value for device.fromGuest. This will be put in the device passed. + * @param fromHostId value for device.fromHostId. This will be put in the device passed. + * @param deleted value for device.deleted. This will be put in the device passed. */ - public void queueDeviceUpdate(Device device, User u) { + public void queueDeviceUpdate( + Device device, User u, boolean fromGuest, Long fromHostId, boolean deleted) { synchronized (messages) { + device.setFromGuest(fromGuest); + device.setFromHostId(fromHostId); + device.setDeleted(deleted); + + // sort of an hack: force the population of thermostat measureTemperature and other + // possible + // computed fields in the future. This should already be done by the callers of this + // method but for + // whatever reason they don't do it. + deviceService.populateComputedFields(List.of(device)); + messages.putIfAbsent(u, new HashMap<>()); - messages.get(u).put(device.getId(), device); + messages.get(u).put(device.getId(), gson.toJson(device)); } } /** Sends all device updates queued to be sent in a unique WebSocket message */ public void flushDeviceUpdates() { synchronized (messages) { - for (Map.Entry> batchForUser : messages.entrySet()) { + for (Map.Entry> batchForUser : messages.entrySet()) { broadcast(batchForUser.getKey(), batchForUser.getValue().values()); batchForUser.getValue().clear(); } @@ -66,13 +87,13 @@ public class SensorSocketEndpoint extends Endpoint { * @param messages the message batch to send * @param u the user to which to send the message */ - private void broadcast(User u, Collection messages) { + private void broadcast(User u, Collection messages) { if (messages.isEmpty()) return; final HashSet sessions = new HashSet<>(authorizedClients.get(u)); for (Session s : sessions) { try { if (s.isOpen()) { - s.getBasicRemote().sendText(gson.toJson(messages)); + s.getBasicRemote().sendText("[" + String.join(",", messages) + "]"); } else { authorizedClients.remove(u, s); } From 21bc66d24b35cd1c5b4cbd38bf9a8de0a952d527 Mon Sep 17 00:00:00 2001 From: "Claudio Maggioni (maggicl)" Date: Mon, 4 May 2020 11:58:03 +0200 Subject: [PATCH 12/16] Deletion propagation implemented --- .../controller/ButtonDimmerController.java | 2 +- .../smarthut/controller/CurtainsController.java | 2 +- .../controller/DimmableLightController.java | 6 ++++-- .../smarthut/controller/KnobDimmerController.java | 2 +- .../controller/MotionSensorController.java | 2 +- .../controller/RegularLightController.java | 6 ++++-- .../smarthut/controller/RoomController.java | 5 +++++ .../controller/SecurityCameraController.java | 2 +- .../smarthut/controller/SensorController.java | 2 +- .../smarthut/controller/SmartPlugController.java | 2 +- .../smarthut/controller/SwitchController.java | 2 +- .../smarthut/controller/ThermostatController.java | 2 +- .../sanmarinoes/smarthut/models/Automation.java | 2 +- .../sa4/sanmarinoes/smarthut/models/Switch.java | 8 +++++++- .../sanmarinoes/smarthut/models/Switchable.java | 9 ++++++++- .../smarthut/service/DeviceService.java | 15 +++++++++++---- 16 files changed, 49 insertions(+), 20 deletions(-) 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 2663211..cf2cb56 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 @@ -75,6 +75,6 @@ public class ButtonDimmerController @DeleteMapping("/{id}") public void delete(@PathVariable("id") long id, final Principal principal) throws NotFoundException { - deviceService.delete(id, principal.getName()); + deviceService.deleteByIdAsOwner(id, 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 6644135..4065f12 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 @@ -49,7 +49,7 @@ public class CurtainsController { @DeleteMapping("/{id}") public void delete(@PathVariable("id") long id, final Principal principal) throws NotFoundException { - deviceService.delete(id, principal.getName()); + deviceService.deleteByIdAsOwner(id, principal.getName()); } @PostMapping("/{id}/state") 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 60a4726..28648c1 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 @@ -66,7 +66,9 @@ public class DimmableLightController extends GuestEnabledController @PutMapping public RegularLight update( - @Valid @RequestBody SwitchableSaveRequest rl, final Principal principal, Long hostId) + @Valid @RequestBody SwitchableSaveRequest rl, + final Principal principal, + @RequestParam(value = "hostId", required = false) Long hostId) throws NotFoundException { return save( fetchIfOwnerOrGuest(principal, rl.getId(), hostId), @@ -92,7 +94,7 @@ public class RegularLightController extends GuestEnabledController @DeleteMapping("/{id}") public void delete(@PathVariable("id") long id, final Principal principal) throws NotFoundException { - deviceService.delete(id, principal.getName()); + deviceService.deleteByIdAsOwner(id, principal.getName()); } // the full url should be: "/regularLight/{id}/state?sceneId={sceneId} 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 c4540c5..e6ab648 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 @@ -124,6 +124,11 @@ public class RoomController { roomRepository .findByIdAndUsername(id, principal.getName()) .orElseThrow(NotFoundException::new); + List devices = deviceService.findAll(r.getId(), null, principal.getName()); + for (Device d : devices) { + deviceService.deleteByIdAsOwner(d.getId(), principal.getName()); + } + roomRepository.delete(r); } 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 c063ebf..1554f0a 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 @@ -61,7 +61,7 @@ public class SecurityCameraController { @DeleteMapping("/{id}") public void delete(@PathVariable("id") long id, final Principal principal) throws NotFoundException { - deviceService.delete(id, principal.getName()); + deviceService.deleteByIdAsOwner(id, principal.getName()); } @PostMapping("/{id}/state") 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 41b9af0..3833452 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 @@ -57,6 +57,6 @@ public class SensorController { @DeleteMapping("/{id}") public void deleteById(@PathVariable("id") long id, final Principal principal) throws NotFoundException { - deviceService.delete(id, principal.getName()); + deviceService.deleteByIdAsOwner(id, principal.getName()); } } 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 468cd5d..790aee2 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 @@ -63,7 +63,7 @@ public class SmartPlugController { @DeleteMapping("/{id}") public void deleteById(@PathVariable("id") long id, final Principal principal) throws NotFoundException { - deviceService.delete(id, principal.getName()); + deviceService.deleteByIdAsOwner(id, principal.getName()); } @PostMapping("/{id}/state") 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 6806b64..d92cd49 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 @@ -81,6 +81,6 @@ public class SwitchController extends InputDeviceConnectionController> triggers = new HashSet<>(); @OneToMany(mappedBy = "automation", cascade = CascadeType.REMOVE) 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 4b9ef3a..7825eae 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 @@ -10,7 +10,13 @@ import javax.persistence.*; @Entity public class Switch extends InputDevice implements BooleanTriggerable { - @ManyToMany(cascade = CascadeType.DETACH) + @ManyToMany( + cascade = { + CascadeType.DETACH, + CascadeType.MERGE, + CascadeType.REFRESH, + CascadeType.PERSIST + }) @GsonExclude @SocketGsonExclude @JoinTable( 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 c7abc7e..9db6361 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 @@ -14,7 +14,14 @@ public abstract class Switchable extends OutputDevice { public static final Connector SWITCH_SWITCHABLE_CONNECTOR = Connector.basic(Switch::getOutputs, Switchable::getSwitches); - @ManyToMany(mappedBy = "switchables", cascade = CascadeType.DETACH) + @ManyToMany( + mappedBy = "switchables", + cascade = { + CascadeType.DETACH, + CascadeType.MERGE, + CascadeType.REFRESH, + CascadeType.PERSIST + }) @GsonExclude @SocketGsonExclude private Set inputs = new HashSet<>(); 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 a0116c2..b0a8d7a 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 @@ -206,13 +206,20 @@ public class DeviceService { return device; } - public void delete(Long id, String username) throws NotFoundException { - Device device = + public void deleteByIdAsOwner(Long id, String username) throws NotFoundException { + Device d = deviceRepository .findByIdAndUsername(id, username) .orElseThrow(NotFoundException::new); - deviceRepository.delete(device); - propagateUpdateAsOwner(device, username, false); + 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); } } From 5bfce7e99b7555f102094e18ab3065d95a11d1e5 Mon Sep 17 00:00:00 2001 From: "Claudio Maggioni (maggicl)" Date: Mon, 4 May 2020 15:09:11 +0200 Subject: [PATCH 13/16] Added icons to scenes --- .../smarthut/controller/RoomController.java | 4 +- .../smarthut/controller/SceneController.java | 3 + .../smarthut/dto/RoomSaveRequest.java | 8 +- .../smarthut/dto/SceneSaveRequest.java | 11 ++ .../sa4/sanmarinoes/smarthut/models/Icon.java | 103 ++++++++++++++++++ .../sa4/sanmarinoes/smarthut/models/Room.java | 101 ----------------- .../sanmarinoes/smarthut/models/Scene.java | 12 ++ 7 files changed, 135 insertions(+), 107 deletions(-) create mode 100644 src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Icon.java 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 e6ab648..8b7f648 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 @@ -75,7 +75,7 @@ public class RoomController { final String username = principal.getName(); final Long userId = userRepository.findByUsername(username).getId(); final String img = r.getImage(); - final Room.Icon icon = r.getIcon(); + final Icon icon = r.getIcon(); final Room newRoom = new Room(); newRoom.setUserId(userId); @@ -95,7 +95,7 @@ public class RoomController { .findByIdAndUsername(id, principal.getName()) .orElseThrow(NotFoundException::new); final String img = r.getImage(); - final Room.Icon icon = r.getIcon(); + final Icon icon = r.getIcon(); if (r.getName() != null) { newRoom.setName(r.getName()); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SceneController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SceneController.java index 66dc5aa..c795049 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SceneController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SceneController.java @@ -56,6 +56,7 @@ public class SceneController { newScene.setUserId(userId); newScene.setName(s.getName()); newScene.setGuestAccessEnabled(s.isGuestAccessEnabled()); + newScene.setIcon(s.getIcon()); return sceneRepository.save(newScene); } @@ -84,6 +85,8 @@ public class SceneController { newScene.setName(s.getName()); } + newScene.setIcon(s.getIcon()); + newScene.setGuestAccessEnabled(s.isGuestAccessEnabled()); return sceneRepository.save(newScene); diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RoomSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RoomSaveRequest.java index 02a0e35..cf362ac 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RoomSaveRequest.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/RoomSaveRequest.java @@ -1,6 +1,6 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; -import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Room; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Icon; import javax.persistence.Lob; import javax.validation.constraints.NotNull; @@ -9,7 +9,7 @@ public class RoomSaveRequest { /** Room identifier */ private long id; - @NotNull private Room.Icon icon; + @NotNull private Icon icon; /** * Image is to be given as byte[]. In order to get an encoded string from it, the @@ -38,11 +38,11 @@ public class RoomSaveRequest { this.name = name; } - public Room.Icon getIcon() { + public Icon getIcon() { return icon; } - public void setIcon(Room.Icon icon) { + public void setIcon(Icon icon) { this.icon = icon; } 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 080ea2b..e7ce3ad 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,5 +1,6 @@ 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; @@ -11,6 +12,8 @@ public class SceneSaveRequest { /** The user given name of this room (e.g. 'Master bedroom') */ @NotNull private String name; + @NotNull private Icon icon; + /** Determines whether a guest can access this scene */ @Column @NotNull private boolean guestAccessEnabled; @@ -33,4 +36,12 @@ public class SceneSaveRequest { public void setName(String name) { this.name = name; } + + public Icon getIcon() { + return icon; + } + + public void setIcon(Icon icon) { + this.icon = icon; + } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Icon.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Icon.java new file mode 100644 index 0000000..0ad9e4d --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Icon.java @@ -0,0 +1,103 @@ +package ch.usi.inf.sa4.sanmarinoes.smarthut.models; + +import com.google.gson.annotations.SerializedName; + +/** A collection of Semantic UI icons */ +@SuppressWarnings("unused") +public enum Icon { + @SerializedName("home") + HOME("home"), + @SerializedName("coffee") + COFFEE("coffee"), + @SerializedName("beer") + BEER("beer"), + @SerializedName("glass martini") + GLASS_MARTINI("glass martini"), + @SerializedName("film") + FILM("film"), + @SerializedName("video") + VIDEO("video"), + @SerializedName("music") + MUSIC("music"), + @SerializedName("headphones") + HEADPHONES("headphones"), + @SerializedName("fax") + FAX("fax"), + @SerializedName("phone") + PHONE("phone"), + @SerializedName("laptop") + LAPTOP("laptop"), + @SerializedName("bath") + BATH("bath"), + @SerializedName("shower") + SHOWER("shower"), + @SerializedName("bed") + BED("bed"), + @SerializedName("child") + CHILD("child"), + @SerializedName("warehouse") + WAREHOUSE("warehouse"), + @SerializedName("car") + CAR("car"), + @SerializedName("bicycle") + BICYCLE("bicycle"), + @SerializedName("motorcycle") + MOTORCYCLE("motorcycle"), + @SerializedName("archive") + ARCHIVE("archive"), + @SerializedName("boxes") + BOXES("boxes"), + @SerializedName("cubes") + CUBES("cubes"), + @SerializedName("chess") + CHESS("chess"), + @SerializedName("gamepad") + GAMEPAD("gamepad"), + @SerializedName("futbol") + FUTBOL("futbol"), + @SerializedName("table tennis") + TABLE_TENNIS("table tennis"), + @SerializedName("server") + SERVER("server"), + @SerializedName("tv") + TV("tv"), + @SerializedName("heart") + HEART("heart"), + @SerializedName("camera") + CAMERA("camera"), + @SerializedName("trophy") + TROPHY("trophy"), + @SerializedName("wrench") + WRENCH("wrench"), + @SerializedName("image") + IMAGE("image"), + @SerializedName("book") + BOOK("book"), + @SerializedName("university") + UNIVERSITY("university"), + @SerializedName("medkit") + MEDKIT("medkit"), + @SerializedName("paw") + PAW("paw"), + @SerializedName("tree") + TREE("tree"), + @SerializedName("utensils") + UTENSILS("utensils"), + @SerializedName("male") + MALE("male"), + @SerializedName("female") + FEMALE("female"), + @SerializedName("life ring outline") + LIFE_RING_OUTLINE("life ring outline"); + + private String iconName; + + Icon(String s) { + this.iconName = s; + } + + @Override + public String toString() { + return iconName; + } +} 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 34f3824..73d7794 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 @@ -1,7 +1,6 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude; -import com.google.gson.annotations.SerializedName; import io.swagger.annotations.ApiModelProperty; import java.util.HashSet; import java.util.Set; @@ -12,106 +11,6 @@ import javax.validation.constraints.NotNull; @Entity public class Room { - /** A collection of Semantic UI icons */ - @SuppressWarnings("unused") - public enum Icon { - @SerializedName("home") - HOME("home"), - @SerializedName("coffee") - COFFEE("coffee"), - @SerializedName("beer") - BEER("beer"), - @SerializedName("glass martini") - GLASS_MARTINI("glass martini"), - @SerializedName("film") - FILM("film"), - @SerializedName("video") - VIDEO("video"), - @SerializedName("music") - MUSIC("music"), - @SerializedName("headphones") - HEADPHONES("headphones"), - @SerializedName("fax") - FAX("fax"), - @SerializedName("phone") - PHONE("phone"), - @SerializedName("laptop") - LAPTOP("laptop"), - @SerializedName("bath") - BATH("bath"), - @SerializedName("shower") - SHOWER("shower"), - @SerializedName("bed") - BED("bed"), - @SerializedName("child") - CHILD("child"), - @SerializedName("warehouse") - WAREHOUSE("warehouse"), - @SerializedName("car") - CAR("car"), - @SerializedName("bicycle") - BICYCLE("bicycle"), - @SerializedName("motorcycle") - MOTORCYCLE("motorcycle"), - @SerializedName("archive") - ARCHIVE("archive"), - @SerializedName("boxes") - BOXES("boxes"), - @SerializedName("cubes") - CUBES("cubes"), - @SerializedName("chess") - CHESS("chess"), - @SerializedName("gamepad") - GAMEPAD("gamepad"), - @SerializedName("futbol") - FUTBOL("futbol"), - @SerializedName("table tennis") - TABLE_TENNIS("table tennis"), - @SerializedName("server") - SERVER("server"), - @SerializedName("tv") - TV("tv"), - @SerializedName("heart") - HEART("heart"), - @SerializedName("camera") - CAMERA("camera"), - @SerializedName("trophy") - TROPHY("trophy"), - @SerializedName("wrench") - WRENCH("wrench"), - @SerializedName("image") - IMAGE("image"), - @SerializedName("book") - BOOK("book"), - @SerializedName("university") - UNIVERSITY("university"), - @SerializedName("medkit") - MEDKIT("medkit"), - @SerializedName("paw") - PAW("paw"), - @SerializedName("tree") - TREE("tree"), - @SerializedName("utensils") - UTENSILS("utensils"), - @SerializedName("male") - MALE("male"), - @SerializedName("female") - FEMALE("female"), - @SerializedName("life ring outline") - LIFE_RING_OUTLINE("life ring outline"); - - private String iconName; - - Icon(String s) { - this.iconName = s; - } - - @Override - public String toString() { - return iconName; - } - } - @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", updatable = false, nullable = false, unique = true) diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Scene.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Scene.java index f35d54d..83a9367 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Scene.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Scene.java @@ -39,9 +39,21 @@ public class Scene { @Column(nullable = false) private String name; + @Column(nullable = false) + @NotNull + private Icon icon; + /** Determines whether a guest can access this scene */ @Column private boolean guestAccessEnabled; + public Icon getIcon() { + return icon; + } + + public void setIcon(Icon icon) { + this.icon = icon; + } + public boolean isGuestAccessEnabled() { return guestAccessEnabled; } From 16d1af2b368c6dd05a856c48f3056baa1250f4f7 Mon Sep 17 00:00:00 2001 From: "Claudio Maggioni (maggicl)" Date: Mon, 4 May 2020 16:10:17 +0200 Subject: [PATCH 14/16] Scene copy implemented --- .../smarthut/controller/SceneController.java | 18 ++++++++++++++++++ .../smarthut/models/DimmableState.java | 7 +++++++ .../sa4/sanmarinoes/smarthut/models/State.java | 10 ++++++++++ .../smarthut/models/SwitchableState.java | 7 +++++++ .../smarthut/service/SceneService.java | 14 ++++++++++---- 5 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SceneController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SceneController.java index c795049..a11b40e 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SceneController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SceneController.java @@ -72,6 +72,24 @@ public class SceneController { return sceneService.apply(newScene, principal.getName(), false); } + @PostMapping("/{id}/copyFrom/{copyId}") + public @ResponseBody List> copy( + @PathVariable("id") long id, + @PathVariable("copyId") long copyId, + final Principal principal) + throws NotFoundException { + final Scene scene = + sceneRepository + .findByIdAndUsername(id, principal.getName()) + .orElseThrow(NotFoundException::new); + final Scene copyFrom = + sceneRepository + .findByIdAndUsername(copyId, principal.getName()) + .orElseThrow(NotFoundException::new); + + return sceneService.copyStates(scene, copyFrom); + } + @PutMapping("/{id}") public @ResponseBody Scene update( @PathVariable("id") long id, @RequestBody SceneSaveRequest s, final Principal principal) diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableState.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableState.java index 7c0b8d2..dff0143 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableState.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/DimmableState.java @@ -25,4 +25,11 @@ public class DimmableState extends State { public void apply() { getDevice().readStateAndSet(this); } + + @Override + protected State copy() { + final DimmableState d = new DimmableState<>(); + d.setIntensity(intensity); + return d; + } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/State.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/State.java index 01398b8..31cd69c 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/State.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/State.java @@ -45,6 +45,16 @@ public abstract class State { /** Sets the state of the connected device to the state represented by this object. */ public abstract void apply(); + /** Creates a perfect copy of this state, except for the id field and the sceneId/scene */ + protected abstract State copy(); + + public State copyToSceneId(Long sceneId) { + final State s = copy(); + s.setDeviceId(this.deviceId); + s.setSceneId(sceneId); + return s; + } + public long getId() { return id; } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SwitchableState.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SwitchableState.java index 67b3118..be21b6e 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SwitchableState.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SwitchableState.java @@ -22,4 +22,11 @@ public class SwitchableState extends State { public void apply() { getDevice().readStateAndSet(this); } + + @Override + protected State copy() { + final SwitchableState d = new SwitchableState<>(); + d.setOn(on); + return d; + } } 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 04a8877..72abf08 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 @@ -1,9 +1,6 @@ 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.DeviceRepository; -import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Scene; -import ch.usi.inf.sa4.sanmarinoes.smarthut.models.State; +import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; @@ -14,6 +11,7 @@ public class SceneService { @Autowired private DeviceRepository deviceRepository; @Autowired private DeviceService deviceService; + @Autowired private StateRepository> stateRepository; public List apply(Scene newScene, String username, boolean fromTrigger) { final List updated = new ArrayList<>(); @@ -26,4 +24,12 @@ public class SceneService { deviceService.populateComputedFields(updated); return updated; } + + public List> copyStates(Scene to, Scene from) { + final ArrayList> states = new ArrayList<>(); + for (final State s : from.getStates()) { + states.add(stateRepository.save(s.copyToSceneId(to.getId()))); + } + return states; + } } From c24598cc6af958fe52db311d9c92a9d1ec25e72e Mon Sep 17 00:00:00 2001 From: "Claudio Maggioni (maggicl)" Date: Mon, 4 May 2020 16:35:46 +0200 Subject: [PATCH 15/16] Done security camera filtering if required by host --- .../smarthut/service/DeviceService.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) 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 b0a8d7a..789b53a 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 @@ -9,6 +9,8 @@ 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; import org.springframework.stereotype.Component; @@ -68,6 +70,7 @@ public class DeviceService { throws NotFoundException { try { Iterable devices; + User host = null; if (hostId == null) { if (roomId != null) { roomRepository @@ -79,8 +82,7 @@ public class DeviceService { } } else { final User guest = userRepository.findByUsername(username); - final User host = - userRepository.findById(hostId).orElseThrow(NotFoundException::new); + host = userRepository.findById(hostId).orElseThrow(NotFoundException::new); if (!guest.getHosts().contains(host)) { throw new NotFoundException(); @@ -99,7 +101,13 @@ public class DeviceService { populateComputedFields(devices); - return toList(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; From a278a9f7b35744e858d5c96a0714529000c96375 Mon Sep 17 00:00:00 2001 From: "Claudio Maggioni (maggicl)" Date: Mon, 4 May 2020 17:11:59 +0200 Subject: [PATCH 16/16] Reviewed GET /scene?hostId= and GET /scene/{sceneId}/apply?hostId= --- .../smarthut/controller/SceneController.java | 65 ++++++++++--------- .../smarthut/models/SceneRepository.java | 5 ++ .../smarthut/service/DeviceService.java | 21 ++++-- .../smarthut/service/SceneService.java | 18 ++++- 4 files changed, 72 insertions(+), 37 deletions(-) diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SceneController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SceneController.java index a11b40e..2eeaaef 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SceneController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SceneController.java @@ -6,20 +6,13 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SceneSaveRequest; 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.SceneService; +import ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils; import java.security.Principal; import java.util.List; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; @RestController @EnableAutoConfiguration @@ -27,21 +20,20 @@ import org.springframework.web.bind.annotation.RestController; public class SceneController { @Autowired private SceneRepository sceneRepository; + @Autowired private UserRepository userRepository; @Autowired private SceneService sceneService; - @Autowired private UserRepository userService; - @Autowired private StateRepository> stateService; + @Autowired private StateRepository> stateRepository; @GetMapping - public List findAll(Principal principal) { - return toList(sceneRepository.findByUsername(principal.getName())); - } - - @GetMapping("/{id}") - public @ResponseBody Scene findById(@PathVariable("id") long id, Principal principal) + public List findAll( + Principal principal, @RequestParam(value = "hostId", required = false) Long hostId) throws NotFoundException { - return sceneRepository - .findByIdAndUsername(id, principal.getName()) - .orElseThrow(NotFoundException::new); + if (hostId == null) { + return toList(sceneRepository.findByUsername(principal.getName())); + } else { + Utils.returnIfGuest(userRepository, null, hostId, principal); + return sceneRepository.findByHostId(hostId); + } } @PostMapping @@ -49,7 +41,7 @@ public class SceneController { @Valid @RequestBody SceneSaveRequest s, final Principal principal) { final String username = principal.getName(); - final Long userId = userService.findByUsername(username).getId(); + final Long userId = userRepository.findByUsername(username).getId(); final Scene newScene = new Scene(); @@ -62,14 +54,27 @@ public class SceneController { } @PostMapping("/{id}/apply") - public @ResponseBody List apply(@PathVariable("id") long id, final Principal principal) + public @ResponseBody List apply( + @PathVariable("id") long id, + final Principal principal, + @RequestParam(value = "hostId", required = false) Long hostId) throws NotFoundException { - final Scene newScene = - sceneRepository - .findByIdAndUsername(id, principal.getName()) - .orElseThrow(NotFoundException::new); - - return sceneService.apply(newScene, principal.getName(), false); + if (hostId == null) { + return sceneService.apply( + sceneRepository + .findByIdAndUsername(id, principal.getName()) + .orElseThrow(NotFoundException::new), + principal.getName(), + false); + } else { + Utils.returnIfGuest(userRepository, null, hostId, principal); + return sceneService.applyAsGuest( + sceneRepository + .findByIdAndUserId(id, hostId) + .orElseThrow(NotFoundException::new), + principal.getName(), + hostId); + } } @PostMapping("/{id}/copyFrom/{copyId}") @@ -112,7 +117,7 @@ public class SceneController { @DeleteMapping("/{id}") public void deleteById(@PathVariable("id") long id) { - stateService.deleteAllBySceneId(id); + stateRepository.deleteAllBySceneId(id); sceneRepository.deleteById(id); } @@ -122,7 +127,7 @@ public class SceneController { */ @GetMapping(path = "/{sceneId}/states") public List> getDevices(@PathVariable("sceneId") long sceneId) { - Iterable> states = stateService.findBySceneId(sceneId); + Iterable> states = stateRepository.findBySceneId(sceneId); return toList(states); } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SceneRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SceneRepository.java index a4aa68c..028f292 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SceneRepository.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SceneRepository.java @@ -19,4 +19,9 @@ public interface SceneRepository extends CrudRepository { @Query("SELECT s FROM Scene s JOIN s.user u WHERE u.username = ?1") List findByUsername(String username); + + @Query("SELECT s FROM Scene s JOIN s.user u WHERE u.id = ?1 AND s.guestAccessEnabled = true") + List findByHostId(Long hostId); + + Optional findByIdAndUserId(Long id, Long userId); } 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 789b53a..d2b02a6 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 @@ -128,7 +128,13 @@ public class DeviceService { 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; + } + + 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 @@ -138,15 +144,22 @@ public class DeviceService { // We're telling all guests that a higher entity has issued a device update. Therefore, // fromHost becomes true. - for (final User guest : guests) { - if (guest.equals(currentUser)) { + for (final User aGuest : guests) { + if (aGuest.equals(guest)) { continue; } // enqueue all device updates for all other guests - endpoint.queueDeviceUpdate(device, guest, false, host.getId(), false); + endpoint.queueDeviceUpdate(device, aGuest, false, host.getId(), false); } + } - return device; + 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; } /** 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 72abf08..3c91277 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 @@ -13,18 +13,30 @@ public class SceneService { @Autowired private DeviceService deviceService; @Autowired private StateRepository> stateRepository; - public List apply(Scene newScene, String username, boolean fromTrigger) { + private List copyStatesToDevices(Scene fromScene) { final List updated = new ArrayList<>(); - for (final State s : newScene.getStates()) { + for (final State s : fromScene.getStates()) { s.apply(); updated.add(s.getDevice()); } - deviceService.saveAllAsOwner(updated, username, true, fromTrigger); + deviceService.populateComputedFields(updated); return updated; } + public List apply(Scene newScene, String username, boolean fromTrigger) { + List updated = copyStatesToDevices(newScene); + deviceService.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); + return updated; + } + public List> copyStates(Scene to, Scene from) { final ArrayList> states = new ArrayList<>(); for (final State s : from.getStates()) {