Merge remote-tracking branch 'origin/dev' into dev
# Conflicts: # src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ThermostatController.java
This commit is contained in:
commit
912d81e84f
40 changed files with 536 additions and 274 deletions
|
@ -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.DimmableState;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.State;
|
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.SwitchableState;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Trigger;
|
||||||
import com.google.gson.*;
|
import com.google.gson.*;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
@ -27,6 +28,7 @@ public class GsonConfig {
|
||||||
private static GsonBuilder configureBuilder() {
|
private static GsonBuilder configureBuilder() {
|
||||||
final GsonBuilder builder = new GsonBuilder();
|
final GsonBuilder builder = new GsonBuilder();
|
||||||
builder.registerTypeAdapter(Json.class, new SpringfoxJsonToGsonAdapter());
|
builder.registerTypeAdapter(Json.class, new SpringfoxJsonToGsonAdapter());
|
||||||
|
@SuppressWarnings({"rawTypes"})
|
||||||
RuntimeTypeAdapterFactory<State> runtimeTypeAdapterFactory =
|
RuntimeTypeAdapterFactory<State> runtimeTypeAdapterFactory =
|
||||||
RuntimeTypeAdapterFactory.of(State.class, "kind")
|
RuntimeTypeAdapterFactory.of(State.class, "kind")
|
||||||
.registerSubtype(SwitchableState.class, "switchableState")
|
.registerSubtype(SwitchableState.class, "switchableState")
|
||||||
|
@ -43,6 +45,10 @@ public class GsonConfig {
|
||||||
"rangeTrigger");
|
"rangeTrigger");
|
||||||
builder.registerTypeAdapterFactory(runtimeTypeAdapterFactory);
|
builder.registerTypeAdapterFactory(runtimeTypeAdapterFactory);
|
||||||
builder.registerTypeAdapterFactory(runtimeTypeAdapterFactoryII);
|
builder.registerTypeAdapterFactory(runtimeTypeAdapterFactoryII);
|
||||||
|
builder.registerTypeAdapter(
|
||||||
|
Trigger.class,
|
||||||
|
(JsonSerializer<Trigger<?>>)
|
||||||
|
(src, typeOfSrc, context) -> context.serialize((Object) src));
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,11 @@ public class ButtonDimmerController
|
||||||
this.buttonDimmerRepository = inputRepository;
|
this.buttonDimmerRepository = inputRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ButtonDimmer findById(@PathVariable("id") long id) throws NotFoundException {
|
||||||
|
return buttonDimmerRepository.findById(id).orElseThrow(NotFoundException::new);
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public ButtonDimmer create(
|
public ButtonDimmer create(
|
||||||
@Valid @RequestBody final GenericDeviceSaveReguest bd, final Principal principal)
|
@Valid @RequestBody final GenericDeviceSaveReguest bd, final Principal principal)
|
||||||
|
@ -62,7 +67,7 @@ public class ButtonDimmerController
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceService.saveAllAsOwner(buttonDimmer.getOutputs(), principal.getName(), false);
|
deviceService.saveAllAsOwner(buttonDimmer.getOutputs(), principal.getName());
|
||||||
|
|
||||||
return buttonDimmer.getOutputs();
|
return buttonDimmer.getOutputs();
|
||||||
}
|
}
|
||||||
|
@ -70,6 +75,6 @@ public class ButtonDimmerController
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
public void delete(@PathVariable("id") long id, final Principal principal)
|
public void delete(@PathVariable("id") long id, final Principal principal)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
deviceService.delete(id, principal.getName());
|
deviceService.deleteByIdAsOwner(id, principal.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ public class CurtainsController {
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
public void delete(@PathVariable("id") long id, final Principal principal)
|
public void delete(@PathVariable("id") long id, final Principal principal)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
deviceService.delete(id, principal.getName());
|
deviceService.deleteByIdAsOwner(id, principal.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/state")
|
@PostMapping("/{id}/state")
|
||||||
|
|
|
@ -66,7 +66,9 @@ public class DimmableLightController extends GuestEnabledController<DimmableLigh
|
||||||
*/
|
*/
|
||||||
@PutMapping
|
@PutMapping
|
||||||
public DimmableLight update(
|
public DimmableLight update(
|
||||||
@Valid @RequestBody DimmableSaveRequest sp, final Principal principal, Long hostId)
|
@Valid @RequestBody DimmableSaveRequest sp,
|
||||||
|
final Principal principal,
|
||||||
|
@RequestParam(value = "hostId", required = false) Long hostId)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
|
|
||||||
return save(
|
return save(
|
||||||
|
@ -79,7 +81,7 @@ public class DimmableLightController extends GuestEnabledController<DimmableLigh
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
public void delete(@PathVariable("id") long id, final Principal principal)
|
public void delete(@PathVariable("id") long id, final Principal principal)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
deviceService.delete(id, principal.getName());
|
deviceService.deleteByIdAsOwner(id, principal.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
// the full url should be: "/dimmableLight/{id}/state?sceneId={sceneId}
|
// the full url should be: "/dimmableLight/{id}/state?sceneId={sceneId}
|
||||||
|
|
|
@ -3,11 +3,16 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
|
||||||
import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList;
|
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.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.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.User;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository;
|
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
@ -18,23 +23,48 @@ import org.springframework.web.bind.annotation.*;
|
||||||
@RequestMapping("/user")
|
@RequestMapping("/user")
|
||||||
public class GuestController {
|
public class GuestController {
|
||||||
|
|
||||||
@Autowired private UserRepository userRepository;
|
@Autowired private EagerUserRepository userRepository;
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public List<User> findAll() {
|
public List<UserResponse> findAll() {
|
||||||
return toList(userRepository.findAll());
|
return StreamSupport.stream(userRepository.findAll().spliterator(), false)
|
||||||
|
.map(UserResponse::fromUser)
|
||||||
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/guest")
|
@GetMapping("/hosts")
|
||||||
public User addUserAsGuest(@RequestParam("userId") long id, final Principal principal)
|
public List<UserResponse> findHosts(final Principal principal) {
|
||||||
|
final User u = userRepository.findByUsername(principal.getName());
|
||||||
|
return u.getHosts().stream().map(UserResponse::fromUser).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/guests")
|
||||||
|
public List<UserResponse> 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<User> setGuests(
|
||||||
|
@RequestBody @Valid GuestsUpdateRequest g, final Principal principal)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
User guest = userRepository.findById(id).orElseThrow(NotFoundException::new);
|
Iterable<User> guests = userRepository.findAllById(g.ids);
|
||||||
User host = userRepository.findByUsername(principal.getName());
|
User host = userRepository.findByUsername(principal.getName());
|
||||||
|
|
||||||
|
for (final User oldGuest : host.getGuests()) {
|
||||||
|
oldGuest.getHosts().remove(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Set<User> oldGuests = Set.copyOf(host.getGuests());
|
||||||
|
|
||||||
|
for (final User guest : guests) {
|
||||||
host.addGuest(guest);
|
host.addGuest(guest);
|
||||||
guest.addHost(host);
|
guest.addHost(host);
|
||||||
userRepository.save(guest);
|
}
|
||||||
return userRepository.save(host);
|
|
||||||
|
userRepository.saveAll(oldGuests);
|
||||||
|
userRepository.save(host);
|
||||||
|
return toList(userRepository.saveAll(guests));
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/permissions")
|
@PutMapping("/permissions")
|
||||||
|
@ -44,16 +74,4 @@ public class GuestController {
|
||||||
currentUser.setCameraEnabled(g.isCameraEnabled());
|
currentUser.setCameraEnabled(g.isCameraEnabled());
|
||||||
return userRepository.save(currentUser);
|
return userRepository.save(currentUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/guest")
|
|
||||||
public void 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,7 @@ public abstract class InputDeviceConnectionController<
|
||||||
connector.connect(pair.input, o, true);
|
connector.connect(pair.input, o, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceService.saveAllAsOwner(pair.outputs, username, false);
|
deviceService.saveAllAsOwner(pair.outputs, username);
|
||||||
return pair.input.getOutputs();
|
return pair.input.getOutputs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ public abstract class InputDeviceConnectionController<
|
||||||
connector.connect(pair.input, o, false);
|
connector.connect(pair.input, o, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceService.saveAllAsOwner(pair.outputs, username, false);
|
deviceService.saveAllAsOwner(pair.outputs, username);
|
||||||
return pair.input.getOutputs();
|
return pair.input.getOutputs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,11 @@ public class KnobDimmerController extends InputDeviceConnectionController<KnobDi
|
||||||
this.knobDimmerRepository = inputRepository;
|
this.knobDimmerRepository = inputRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public KnobDimmer findById(@PathVariable("id") long id) throws NotFoundException {
|
||||||
|
return knobDimmerRepository.findById(id).orElseThrow(NotFoundException::new);
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public KnobDimmer create(
|
public KnobDimmer create(
|
||||||
@Valid @RequestBody GenericDeviceSaveReguest kd, final Principal principal)
|
@Valid @RequestBody GenericDeviceSaveReguest kd, final Principal principal)
|
||||||
|
@ -49,7 +54,7 @@ public class KnobDimmerController extends InputDeviceConnectionController<KnobDi
|
||||||
.orElseThrow(NotFoundException::new);
|
.orElseThrow(NotFoundException::new);
|
||||||
|
|
||||||
dimmer.setLightIntensity(bd.getIntensity());
|
dimmer.setLightIntensity(bd.getIntensity());
|
||||||
deviceService.saveAllAsOwner(dimmer.getOutputs(), principal.getName(), false);
|
deviceService.saveAllAsOwner(dimmer.getOutputs(), principal.getName());
|
||||||
|
|
||||||
return dimmer.getOutputs();
|
return dimmer.getOutputs();
|
||||||
}
|
}
|
||||||
|
@ -57,6 +62,6 @@ public class KnobDimmerController extends InputDeviceConnectionController<KnobDi
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
public void delete(@PathVariable("id") long id, final Principal principal)
|
public void delete(@PathVariable("id") long id, final Principal principal)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
deviceService.delete(id, principal.getName());
|
deviceService.deleteByIdAsOwner(id, principal.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,6 @@ public class MotionSensorController {
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
public void delete(@PathVariable("id") long id, final Principal principal)
|
public void delete(@PathVariable("id") long id, final Principal principal)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
deviceService.delete(id, principal.getName());
|
deviceService.deleteByIdAsOwner(id, principal.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,9 @@ public class RegularLightController extends GuestEnabledController<RegularLight>
|
||||||
|
|
||||||
@PutMapping
|
@PutMapping
|
||||||
public RegularLight update(
|
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 {
|
throws NotFoundException {
|
||||||
return save(
|
return save(
|
||||||
fetchIfOwnerOrGuest(principal, rl.getId(), hostId),
|
fetchIfOwnerOrGuest(principal, rl.getId(), hostId),
|
||||||
|
@ -92,7 +94,7 @@ public class RegularLightController extends GuestEnabledController<RegularLight>
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
public void delete(@PathVariable("id") long id, final Principal principal)
|
public void delete(@PathVariable("id") long id, final Principal principal)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
deviceService.delete(id, principal.getName());
|
deviceService.deleteByIdAsOwner(id, principal.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
// the full url should be: "/regularLight/{id}/state?sceneId={sceneId}
|
// the full url should be: "/regularLight/{id}/state?sceneId={sceneId}
|
||||||
|
|
|
@ -75,7 +75,7 @@ public class RoomController {
|
||||||
final String username = principal.getName();
|
final String username = principal.getName();
|
||||||
final Long userId = userRepository.findByUsername(username).getId();
|
final Long userId = userRepository.findByUsername(username).getId();
|
||||||
final String img = r.getImage();
|
final String img = r.getImage();
|
||||||
final Room.Icon icon = r.getIcon();
|
final Icon icon = r.getIcon();
|
||||||
|
|
||||||
final Room newRoom = new Room();
|
final Room newRoom = new Room();
|
||||||
newRoom.setUserId(userId);
|
newRoom.setUserId(userId);
|
||||||
|
@ -95,7 +95,7 @@ public class RoomController {
|
||||||
.findByIdAndUsername(id, principal.getName())
|
.findByIdAndUsername(id, principal.getName())
|
||||||
.orElseThrow(NotFoundException::new);
|
.orElseThrow(NotFoundException::new);
|
||||||
final String img = r.getImage();
|
final String img = r.getImage();
|
||||||
final Room.Icon icon = r.getIcon();
|
final Icon icon = r.getIcon();
|
||||||
|
|
||||||
if (r.getName() != null) {
|
if (r.getName() != null) {
|
||||||
newRoom.setName(r.getName());
|
newRoom.setName(r.getName());
|
||||||
|
@ -124,6 +124,11 @@ public class RoomController {
|
||||||
roomRepository
|
roomRepository
|
||||||
.findByIdAndUsername(id, principal.getName())
|
.findByIdAndUsername(id, principal.getName())
|
||||||
.orElseThrow(NotFoundException::new);
|
.orElseThrow(NotFoundException::new);
|
||||||
|
List<Device> devices = deviceService.findAll(r.getId(), null, principal.getName());
|
||||||
|
for (Device d : devices) {
|
||||||
|
deviceService.deleteByIdAsOwner(d.getId(), principal.getName());
|
||||||
|
}
|
||||||
|
|
||||||
roomRepository.delete(r);
|
roomRepository.delete(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.error.NotFoundException;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.service.SceneService;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.service.SceneService;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
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;
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
|
@ -27,21 +20,20 @@ import org.springframework.web.bind.annotation.RestController;
|
||||||
public class SceneController {
|
public class SceneController {
|
||||||
|
|
||||||
@Autowired private SceneRepository sceneRepository;
|
@Autowired private SceneRepository sceneRepository;
|
||||||
|
@Autowired private UserRepository userRepository;
|
||||||
@Autowired private SceneService sceneService;
|
@Autowired private SceneService sceneService;
|
||||||
@Autowired private UserRepository userService;
|
@Autowired private StateRepository<State<?>> stateRepository;
|
||||||
@Autowired private StateRepository<State<?>> stateService;
|
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public List<Scene> findAll(Principal principal) {
|
public List<Scene> findAll(
|
||||||
return toList(sceneRepository.findByUsername(principal.getName()));
|
Principal principal, @RequestParam(value = "hostId", required = false) Long hostId)
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/{id}")
|
|
||||||
public @ResponseBody Scene findById(@PathVariable("id") long id, Principal principal)
|
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
return sceneRepository
|
if (hostId == null) {
|
||||||
.findByIdAndUsername(id, principal.getName())
|
return toList(sceneRepository.findByUsername(principal.getName()));
|
||||||
.orElseThrow(NotFoundException::new);
|
} else {
|
||||||
|
Utils.returnIfGuest(userRepository, null, hostId, principal);
|
||||||
|
return sceneRepository.findByHostId(hostId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
|
@ -49,26 +41,58 @@ public class SceneController {
|
||||||
@Valid @RequestBody SceneSaveRequest s, final Principal principal) {
|
@Valid @RequestBody SceneSaveRequest s, final Principal principal) {
|
||||||
|
|
||||||
final String username = principal.getName();
|
final String username = principal.getName();
|
||||||
final Long userId = userService.findByUsername(username).getId();
|
final Long userId = userRepository.findByUsername(username).getId();
|
||||||
|
|
||||||
final Scene newScene = new Scene();
|
final Scene newScene = new Scene();
|
||||||
|
|
||||||
newScene.setUserId(userId);
|
newScene.setUserId(userId);
|
||||||
newScene.setName(s.getName());
|
newScene.setName(s.getName());
|
||||||
newScene.setGuestAccessEnabled(s.isGuestAccessEnabled());
|
newScene.setGuestAccessEnabled(s.isGuestAccessEnabled());
|
||||||
|
newScene.setIcon(s.getIcon());
|
||||||
|
|
||||||
return sceneRepository.save(newScene);
|
return sceneRepository.save(newScene);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/apply")
|
@PostMapping("/{id}/apply")
|
||||||
public @ResponseBody List<Device> apply(@PathVariable("id") long id, final Principal principal)
|
public @ResponseBody List<Device> apply(
|
||||||
|
@PathVariable("id") long id,
|
||||||
|
final Principal principal,
|
||||||
|
@RequestParam(value = "hostId", required = false) Long hostId)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
final Scene newScene =
|
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}")
|
||||||
|
public @ResponseBody List<State<?>> copy(
|
||||||
|
@PathVariable("id") long id,
|
||||||
|
@PathVariable("copyId") long copyId,
|
||||||
|
final Principal principal)
|
||||||
|
throws NotFoundException {
|
||||||
|
final Scene scene =
|
||||||
sceneRepository
|
sceneRepository
|
||||||
.findByIdAndUsername(id, principal.getName())
|
.findByIdAndUsername(id, principal.getName())
|
||||||
.orElseThrow(NotFoundException::new);
|
.orElseThrow(NotFoundException::new);
|
||||||
|
final Scene copyFrom =
|
||||||
|
sceneRepository
|
||||||
|
.findByIdAndUsername(copyId, principal.getName())
|
||||||
|
.orElseThrow(NotFoundException::new);
|
||||||
|
|
||||||
return sceneService.apply(newScene);
|
return sceneService.copyStates(scene, copyFrom);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PutMapping("/{id}")
|
@PutMapping("/{id}")
|
||||||
|
@ -84,6 +108,8 @@ public class SceneController {
|
||||||
newScene.setName(s.getName());
|
newScene.setName(s.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newScene.setIcon(s.getIcon());
|
||||||
|
|
||||||
newScene.setGuestAccessEnabled(s.isGuestAccessEnabled());
|
newScene.setGuestAccessEnabled(s.isGuestAccessEnabled());
|
||||||
|
|
||||||
return sceneRepository.save(newScene);
|
return sceneRepository.save(newScene);
|
||||||
|
@ -91,7 +117,7 @@ public class SceneController {
|
||||||
|
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
public void deleteById(@PathVariable("id") long id) {
|
public void deleteById(@PathVariable("id") long id) {
|
||||||
stateService.deleteAllBySceneId(id);
|
stateRepository.deleteAllBySceneId(id);
|
||||||
sceneRepository.deleteById(id);
|
sceneRepository.deleteById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +127,7 @@ public class SceneController {
|
||||||
*/
|
*/
|
||||||
@GetMapping(path = "/{sceneId}/states")
|
@GetMapping(path = "/{sceneId}/states")
|
||||||
public List<State<?>> getDevices(@PathVariable("sceneId") long sceneId) {
|
public List<State<?>> getDevices(@PathVariable("sceneId") long sceneId) {
|
||||||
Iterable<State<?>> states = stateService.findBySceneId(sceneId);
|
Iterable<State<?>> states = stateRepository.findBySceneId(sceneId);
|
||||||
return toList(states);
|
return toList(states);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ public class SecurityCameraController {
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
public void delete(@PathVariable("id") long id, final Principal principal)
|
public void delete(@PathVariable("id") long id, final Principal principal)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
deviceService.delete(id, principal.getName());
|
deviceService.deleteByIdAsOwner(id, principal.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/state")
|
@PostMapping("/{id}/state")
|
||||||
|
|
|
@ -57,6 +57,6 @@ public class SensorController {
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
public void deleteById(@PathVariable("id") long id, final Principal principal)
|
public void deleteById(@PathVariable("id") long id, final Principal principal)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
deviceService.delete(id, principal.getName());
|
deviceService.deleteByIdAsOwner(id, principal.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ public class SmartPlugController {
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
public void deleteById(@PathVariable("id") long id, final Principal principal)
|
public void deleteById(@PathVariable("id") long id, final Principal principal)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
deviceService.delete(id, principal.getName());
|
deviceService.deleteByIdAsOwner(id, principal.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/state")
|
@PostMapping("/{id}/state")
|
||||||
|
|
|
@ -37,6 +37,11 @@ public class SwitchController extends InputDeviceConnectionController<Switch, Sw
|
||||||
this.switchRepository = inputRepository;
|
this.switchRepository = inputRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Switch findById(@PathVariable("id") long id) throws NotFoundException {
|
||||||
|
return switchRepository.findById(id).orElseThrow(NotFoundException::new);
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public Switch create(@Valid @RequestBody GenericDeviceSaveReguest s, final Principal principal)
|
public Switch create(@Valid @RequestBody GenericDeviceSaveReguest s, final Principal principal)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
|
@ -70,12 +75,12 @@ public class SwitchController extends InputDeviceConnectionController<Switch, Sw
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceService.saveAsOwner(s, principal.getName());
|
deviceService.saveAsOwner(s, principal.getName());
|
||||||
return deviceService.saveAllAsOwner(s.getOutputs(), principal.getName(), false);
|
return deviceService.saveAllAsOwner(s.getOutputs(), principal.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
public void deleteById(@PathVariable("id") long id, final Principal principal)
|
public void deleteById(@PathVariable("id") long id, final Principal principal)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
deviceService.delete(id, principal.getName());
|
deviceService.deleteByIdAsOwner(id, principal.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,14 +25,17 @@ public class ThermostatController {
|
||||||
|
|
||||||
private Thermostat save(Thermostat newT, ThermostatSaveRequest t, final Principal principal) {
|
private Thermostat save(Thermostat newT, ThermostatSaveRequest t, final Principal principal) {
|
||||||
newT.setTargetTemperature(t.getTargetTemperature());
|
newT.setTargetTemperature(t.getTargetTemperature());
|
||||||
thermostatService.populateMeasuredTemperature(newT);
|
|
||||||
newT.setId(t.getId());
|
newT.setId(t.getId());
|
||||||
newT.setName(t.getName());
|
newT.setName(t.getName());
|
||||||
newT.setRoomId(t.getRoomId());
|
newT.setRoomId(t.getRoomId());
|
||||||
newT.setMeasuredTemperature(t.getMeasuredTemperature());
|
|
||||||
newT.setUseExternalSensors(t.isUseExternalSensors());
|
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());
|
newT = deviceService.saveAsOwner(newT, principal.getName());
|
||||||
return newT;
|
return newT;
|
||||||
}
|
}
|
||||||
|
@ -58,7 +61,7 @@ public class ThermostatController {
|
||||||
@DeleteMapping("/{id}")
|
@DeleteMapping("/{id}")
|
||||||
public void deleteById(@PathVariable("id") long id, final Principal principal)
|
public void deleteById(@PathVariable("id") long id, final Principal principal)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
deviceService.delete(id, principal.getName());
|
deviceService.deleteByIdAsOwner(id, principal.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/{id}/state")
|
@PostMapping("/{id}/state")
|
||||||
|
|
|
@ -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<Long> ids;
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
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.persistence.Lob;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ public class RoomSaveRequest {
|
||||||
/** Room identifier */
|
/** Room identifier */
|
||||||
private long id;
|
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
|
* 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;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Room.Icon getIcon() {
|
public Icon getIcon() {
|
||||||
return icon;
|
return icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setIcon(Room.Icon icon) {
|
public void setIcon(Icon icon) {
|
||||||
this.icon = icon;
|
this.icon = icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Icon;
|
||||||
import com.sun.istack.NotNull;
|
import com.sun.istack.NotNull;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
|
|
||||||
|
@ -11,6 +12,8 @@ public class SceneSaveRequest {
|
||||||
/** The user given name of this room (e.g. 'Master bedroom') */
|
/** The user given name of this room (e.g. 'Master bedroom') */
|
||||||
@NotNull private String name;
|
@NotNull private String name;
|
||||||
|
|
||||||
|
@NotNull private Icon icon;
|
||||||
|
|
||||||
/** Determines whether a guest can access this scene */
|
/** Determines whether a guest can access this scene */
|
||||||
@Column @NotNull private boolean guestAccessEnabled;
|
@Column @NotNull private boolean guestAccessEnabled;
|
||||||
|
|
||||||
|
@ -33,4 +36,12 @@ public class SceneSaveRequest {
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Icon getIcon() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIcon(Icon icon) {
|
||||||
|
this.icon = icon;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,6 @@ public class ThermostatSaveRequest {
|
||||||
|
|
||||||
@NotNull private boolean useExternalSensors;
|
@NotNull private boolean useExternalSensors;
|
||||||
|
|
||||||
@NotNull private BigDecimal measuredTemperature;
|
|
||||||
|
|
||||||
/** State of this thermostat */
|
/** State of this thermostat */
|
||||||
@NotNull private boolean turnOn;
|
@NotNull private boolean turnOn;
|
||||||
|
|
||||||
|
@ -74,12 +72,4 @@ public class ThermostatSaveRequest {
|
||||||
public void setId(long id) {
|
public void setId(long id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigDecimal getMeasuredTemperature() {
|
|
||||||
return measuredTemperature;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMeasuredTemperature(BigDecimal measuredTemperature) {
|
|
||||||
this.measuredTemperature = measuredTemperature;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,7 +27,7 @@ public class Automation {
|
||||||
@GsonExclude
|
@GsonExclude
|
||||||
private Long userId;
|
private Long userId;
|
||||||
|
|
||||||
@OneToMany(mappedBy = "automation", orphanRemoval = true)
|
@OneToMany(mappedBy = "automation", orphanRemoval = true, cascade = CascadeType.REMOVE)
|
||||||
private Set<Trigger<?>> triggers = new HashSet<>();
|
private Set<Trigger<?>> triggers = new HashSet<>();
|
||||||
|
|
||||||
@OneToMany(mappedBy = "automation", cascade = CascadeType.REMOVE)
|
@OneToMany(mappedBy = "automation", cascade = CascadeType.REMOVE)
|
||||||
|
|
|
@ -71,14 +71,18 @@ public abstract class Device {
|
||||||
@SocketGsonExclude
|
@SocketGsonExclude
|
||||||
private Set<State<?>> states = new HashSet<>();
|
private Set<State<?>> 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 fromGuest = false;
|
||||||
|
|
||||||
@Transient @GsonExclude private boolean deleted = false;
|
@Transient @GsonExclude private boolean deleted = false;
|
||||||
|
|
||||||
public boolean isFromHost() {
|
public Long getFromHostId() {
|
||||||
return fromHost;
|
return fromHostId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFromHostId(Long fromHostId) {
|
||||||
|
this.fromHostId = fromHostId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDeleted() {
|
public boolean isDeleted() {
|
||||||
|
@ -97,10 +101,6 @@ public abstract class Device {
|
||||||
this.fromGuest = fromGuest;
|
this.fromGuest = fromGuest;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFromHost(boolean fromHost) {
|
|
||||||
this.fromHost = fromHost;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getId() {
|
public long getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,4 +25,11 @@ public class DimmableState<T extends Dimmable> extends State<T> {
|
||||||
public void apply() {
|
public void apply() {
|
||||||
getDevice().readStateAndSet(this);
|
getDevice().readStateAndSet(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected State<T> copy() {
|
||||||
|
final DimmableState<T> d = new DimmableState<>();
|
||||||
|
d.setIntensity(intensity);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude;
|
||||||
import com.google.gson.annotations.SerializedName;
|
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -12,106 +11,6 @@ import javax.validation.constraints.NotNull;
|
||||||
@Entity
|
@Entity
|
||||||
public class Room {
|
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
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
@Column(name = "id", updatable = false, nullable = false, unique = true)
|
@Column(name = "id", updatable = false, nullable = false, unique = true)
|
||||||
|
|
|
@ -39,9 +39,21 @@ public class Scene {
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
@Column(nullable = false)
|
||||||
|
@NotNull
|
||||||
|
private Icon icon;
|
||||||
|
|
||||||
/** Determines whether a guest can access this scene */
|
/** Determines whether a guest can access this scene */
|
||||||
@Column private boolean guestAccessEnabled;
|
@Column private boolean guestAccessEnabled;
|
||||||
|
|
||||||
|
public Icon getIcon() {
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIcon(Icon icon) {
|
||||||
|
this.icon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isGuestAccessEnabled() {
|
public boolean isGuestAccessEnabled() {
|
||||||
return guestAccessEnabled;
|
return guestAccessEnabled;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,4 +19,9 @@ public interface SceneRepository extends CrudRepository<Scene, Long> {
|
||||||
|
|
||||||
@Query("SELECT s FROM Scene s JOIN s.user u WHERE u.username = ?1")
|
@Query("SELECT s FROM Scene s JOIN s.user u WHERE u.username = ?1")
|
||||||
List<Scene> findByUsername(String username);
|
List<Scene> findByUsername(String username);
|
||||||
|
|
||||||
|
@Query("SELECT s FROM Scene s JOIN s.user u WHERE u.id = ?1 AND s.guestAccessEnabled = true")
|
||||||
|
List<Scene> findByHostId(Long hostId);
|
||||||
|
|
||||||
|
Optional<Scene> findByIdAndUserId(Long id, Long userId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,16 @@ public abstract class State<D extends OutputDevice> {
|
||||||
/** Sets the state of the connected device to the state represented by this object. */
|
/** Sets the state of the connected device to the state represented by this object. */
|
||||||
public abstract void apply();
|
public abstract void apply();
|
||||||
|
|
||||||
|
/** Creates a perfect copy of this state, except for the id field and the sceneId/scene */
|
||||||
|
protected abstract State<D> copy();
|
||||||
|
|
||||||
|
public State<D> copyToSceneId(Long sceneId) {
|
||||||
|
final State<D> s = copy();
|
||||||
|
s.setDeviceId(this.deviceId);
|
||||||
|
s.setSceneId(sceneId);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
public long getId() {
|
public long getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,13 @@ import javax.persistence.*;
|
||||||
@Entity
|
@Entity
|
||||||
public class Switch extends InputDevice implements BooleanTriggerable {
|
public class Switch extends InputDevice implements BooleanTriggerable {
|
||||||
|
|
||||||
@ManyToMany(cascade = CascadeType.DETACH)
|
@ManyToMany(
|
||||||
|
cascade = {
|
||||||
|
CascadeType.DETACH,
|
||||||
|
CascadeType.MERGE,
|
||||||
|
CascadeType.REFRESH,
|
||||||
|
CascadeType.PERSIST
|
||||||
|
})
|
||||||
@GsonExclude
|
@GsonExclude
|
||||||
@SocketGsonExclude
|
@SocketGsonExclude
|
||||||
@JoinTable(
|
@JoinTable(
|
||||||
|
|
|
@ -14,7 +14,14 @@ public abstract class Switchable extends OutputDevice {
|
||||||
public static final Connector<Switch, Switchable> SWITCH_SWITCHABLE_CONNECTOR =
|
public static final Connector<Switch, Switchable> SWITCH_SWITCHABLE_CONNECTOR =
|
||||||
Connector.basic(Switch::getOutputs, Switchable::getSwitches);
|
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
|
@GsonExclude
|
||||||
@SocketGsonExclude
|
@SocketGsonExclude
|
||||||
private Set<Switch> inputs = new HashSet<>();
|
private Set<Switch> inputs = new HashSet<>();
|
||||||
|
|
|
@ -22,4 +22,11 @@ public class SwitchableState<T extends Switchable> extends State<T> {
|
||||||
public void apply() {
|
public void apply() {
|
||||||
getDevice().readStateAndSet(this);
|
getDevice().readStateAndSet(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected State<T> copy() {
|
||||||
|
final SwitchableState<T> d = new SwitchableState<>();
|
||||||
|
d.setOn(on);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,11 @@ import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.Transient;
|
import javax.persistence.Transient;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
/** A thermostat capable of controlling cooling and heating. */
|
/** A thermostat capable of controlling cooling and heating. */
|
||||||
@Entity
|
@Entity
|
||||||
|
@Component
|
||||||
public class Thermostat extends Switchable implements BooleanTriggerable {
|
public class Thermostat extends Switchable implements BooleanTriggerable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -22,32 +24,29 @@ public class Thermostat extends Switchable implements BooleanTriggerable {
|
||||||
computeState();
|
computeState();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Computes the new thermostat state, for when the thermostat is on */
|
||||||
* Computes the new thermostat state, for when the thermostat is on;
|
public void computeState() {
|
||||||
*
|
|
||||||
* @return true if the state changed, false if not;
|
|
||||||
*/
|
|
||||||
public boolean computeState() {
|
|
||||||
if (mode == Thermostat.Mode.OFF) {
|
if (mode == Thermostat.Mode.OFF) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BigDecimal measured = this.getMeasuredTemperature();
|
BigDecimal measured = this.getMeasuredTemperature();
|
||||||
BigDecimal target = this.getTargetTemperature();
|
BigDecimal target = this.getTargetTemperature();
|
||||||
|
|
||||||
|
if (measured == null) {
|
||||||
|
this.setMode(Thermostat.Mode.IDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
BigDecimal delta = target.subtract(measured);
|
BigDecimal delta = target.subtract(measured);
|
||||||
|
|
||||||
if (delta.abs().doubleValue() < 0.25) {
|
if (delta.abs().doubleValue() < 0.25) {
|
||||||
if (this.getMode() == Thermostat.Mode.IDLE) return false;
|
|
||||||
this.setMode(Thermostat.Mode.IDLE);
|
this.setMode(Thermostat.Mode.IDLE);
|
||||||
} else if (delta.signum() > 0) {
|
} else if (delta.signum() > 0) {
|
||||||
if (this.getMode() == Thermostat.Mode.HEATING) return false;
|
|
||||||
this.setMode(Thermostat.Mode.HEATING);
|
this.setMode(Thermostat.Mode.HEATING);
|
||||||
} else {
|
} else {
|
||||||
if (this.getMode() == Thermostat.Mode.COOLING) return false;
|
|
||||||
this.setMode(Thermostat.Mode.COOLING);
|
this.setMode(Thermostat.Mode.COOLING);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -87,6 +86,18 @@ public class Thermostat extends Switchable implements BooleanTriggerable {
|
||||||
this.mode = Mode.OFF;
|
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) {
|
public void setMode(Mode state) {
|
||||||
this.mode = state;
|
this.mode = state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ public class UpdateTasks {
|
||||||
c.forEach(
|
c.forEach(
|
||||||
s ->
|
s ->
|
||||||
sensorSocketEndpoint.queueDeviceUpdate(
|
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 */
|
/** Sends device updates through sensor socket in batch every one second */
|
||||||
|
|
|
@ -9,6 +9,8 @@ import java.util.Collection;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@ -37,7 +39,7 @@ public class DeviceService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void triggerTriggers(Device device) {
|
private void triggerTriggers(Device device, final String username) {
|
||||||
|
|
||||||
final long deviceId = device.getId();
|
final long deviceId = device.getId();
|
||||||
|
|
||||||
|
@ -57,7 +59,7 @@ public class DeviceService {
|
||||||
sceneRepository
|
sceneRepository
|
||||||
.findById(t.getSceneId())
|
.findById(t.getSceneId())
|
||||||
.orElseThrow(IllegalStateException::new))
|
.orElseThrow(IllegalStateException::new))
|
||||||
.forEach(sceneService::apply);
|
.forEach((s) -> sceneService.apply(s, username, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Device> findAll(Long hostId, String username) throws NotFoundException {
|
public List<Device> findAll(Long hostId, String username) throws NotFoundException {
|
||||||
|
@ -68,6 +70,7 @@ public class DeviceService {
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
try {
|
try {
|
||||||
Iterable<Device> devices;
|
Iterable<Device> devices;
|
||||||
|
User host = null;
|
||||||
if (hostId == null) {
|
if (hostId == null) {
|
||||||
if (roomId != null) {
|
if (roomId != null) {
|
||||||
roomRepository
|
roomRepository
|
||||||
|
@ -79,8 +82,7 @@ public class DeviceService {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final User guest = userRepository.findByUsername(username);
|
final User guest = userRepository.findByUsername(username);
|
||||||
final User host =
|
host = userRepository.findById(hostId).orElseThrow(NotFoundException::new);
|
||||||
userRepository.findById(hostId).orElseThrow(NotFoundException::new);
|
|
||||||
|
|
||||||
if (!guest.getHosts().contains(host)) {
|
if (!guest.getHosts().contains(host)) {
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
|
@ -97,17 +99,27 @@ public class DeviceService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<Device> devices) {
|
||||||
for (Device d : devices) {
|
for (Device d : devices) {
|
||||||
if (d instanceof Thermostat) {
|
if (d instanceof Thermostat) {
|
||||||
thermostatService.populateMeasuredTemperature((Thermostat) d);
|
thermostatService.populateMeasuredTemperature((Thermostat) d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return toList(devices);
|
|
||||||
} catch (NotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends Device> T saveAsGuest(T device, String guestUsername, Long hostId)
|
public <T extends Device> T saveAsGuest(T device, String guestUsername, Long hostId)
|
||||||
|
@ -116,79 +128,119 @@ public class DeviceService {
|
||||||
final User host = userRepository.findById(hostId).orElseThrow(NotFoundException::new);
|
final User host = userRepository.findById(hostId).orElseThrow(NotFoundException::new);
|
||||||
if (!host.getGuests().contains(currentUser)) throw new NotFoundException();
|
if (!host.getGuests().contains(currentUser)) throw new NotFoundException();
|
||||||
renameIfDuplicate(device, host.getUsername());
|
renameIfDuplicate(device, host.getUsername());
|
||||||
|
|
||||||
device = deviceRepository.save(device);
|
device = deviceRepository.save(device);
|
||||||
|
propagateUpdateAsGuest(device, host, currentUser);
|
||||||
|
return device;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void propagateUpdateAsGuest(Device device, User host, User guest) {
|
||||||
final Set<User> guests = Set.copyOf(host.getGuests());
|
final Set<User> guests = Set.copyOf(host.getGuests());
|
||||||
|
|
||||||
// We're telling the host that a guest has modified a device. Therefore, fromGuest becomes
|
// We're telling the host that a guest has modified a device. Therefore, fromGuest becomes
|
||||||
// true.
|
// true.
|
||||||
device.setFromHost(false);
|
|
||||||
device.setFromGuest(true);
|
|
||||||
// broadcast device update to host
|
// 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,
|
// We're telling all guests that a higher entity has issued a device update. Therefore,
|
||||||
// fromHost becomes true.
|
// fromHost becomes true.
|
||||||
device.setFromHost(true);
|
for (final User aGuest : guests) {
|
||||||
device.setFromGuest(false);
|
if (aGuest.equals(guest)) {
|
||||||
for (final User guest : guests) {
|
|
||||||
if (guest.equals(currentUser)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// enqueue all device updates for all other guests
|
// enqueue all device updates for all other guests
|
||||||
endpoint.queueDeviceUpdate(device, guest);
|
endpoint.queueDeviceUpdate(device, aGuest, false, host.getId(), false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return device;
|
List<Device> saveAllAsGuestSceneApplication(
|
||||||
|
List<Device> 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 User user = userRepository.findByUsername(username);
|
||||||
final Set<User> guests = user.getGuests();
|
final Set<User> guests = user.getGuests();
|
||||||
// make sure we're broadcasting from host
|
// make sure we're broadcasting from host
|
||||||
device.setFromHost(true);
|
|
||||||
device.setFromGuest(false);
|
|
||||||
for (final User guest : guests) {
|
for (final User guest : guests) {
|
||||||
// broadcast to endpoint the object device, with receiving user set to guest
|
// 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, false, user.getId(), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 <T> the type of device contained in the list
|
||||||
|
* @return the updated list of devices, ready to be fed to GSON
|
||||||
|
*/
|
||||||
public <T extends Device> List<T> saveAllAsOwner(
|
public <T extends Device> List<T> saveAllAsOwner(
|
||||||
Iterable<T> devices, String username, boolean fromScene) {
|
Iterable<T> devices, String username, boolean fromScene, boolean fromTrigger) {
|
||||||
devices.forEach(d -> renameIfDuplicate(d, username));
|
devices.forEach(d -> renameIfDuplicate(d, username));
|
||||||
devices = deviceRepository.saveAll(devices);
|
devices = deviceRepository.saveAll(devices);
|
||||||
devices.forEach((d) -> propagateUpdateAsOwner(d, username));
|
devices.forEach((d) -> propagateUpdateAsOwner(d, username, fromScene && fromTrigger));
|
||||||
|
|
||||||
if (!fromScene) {
|
if (!fromScene) {
|
||||||
devices.forEach(this::triggerTriggers);
|
devices.forEach((d) -> triggerTriggers(d, username));
|
||||||
}
|
}
|
||||||
|
|
||||||
return toList(devices);
|
return toList(devices);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends Device> T saveAsOwner(T device, String username, boolean fromScene) {
|
public <T extends Device> List<T> saveAllAsOwner(Iterable<T> devices, String username) {
|
||||||
|
return saveAllAsOwner(devices, username, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends Device> T saveAsOwner(T device, String username) {
|
||||||
renameIfDuplicate(device, username);
|
renameIfDuplicate(device, username);
|
||||||
device = deviceRepository.save(device);
|
device = deviceRepository.save(device);
|
||||||
propagateUpdateAsOwner(device, username);
|
propagateUpdateAsOwner(device, username, false);
|
||||||
|
|
||||||
if (!fromScene) {
|
triggerTriggers(device, username);
|
||||||
triggerTriggers(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends Device> T saveAsOwner(T device, String username) {
|
public void deleteByIdAsOwner(Long id, String username) throws NotFoundException {
|
||||||
return saveAsOwner(device, username, false);
|
Device d =
|
||||||
}
|
|
||||||
|
|
||||||
public void delete(Long id, String username) throws NotFoundException {
|
|
||||||
Device device =
|
|
||||||
deviceRepository
|
deviceRepository
|
||||||
.findByIdAndUsername(id, username)
|
.findByIdAndUsername(id, username)
|
||||||
.orElseThrow(NotFoundException::new);
|
.orElseThrow(NotFoundException::new);
|
||||||
deviceRepository.delete(device);
|
|
||||||
|
|
||||||
propagateUpdateAsOwner(device, username);
|
final User user = userRepository.findByUsername(username);
|
||||||
|
final Set<User> 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ public class MotionSensorService {
|
||||||
@Autowired private MotionSensorRepository motionSensorRepository;
|
@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 sensor the motion sensor to update
|
||||||
* @param detected the new detection status
|
* @param detected the new detection status
|
||||||
|
@ -26,7 +26,7 @@ public class MotionSensorService {
|
||||||
final MotionSensor toReturn = deviceService.saveAsOwner(sensor, username);
|
final MotionSensor toReturn = deviceService.saveAsOwner(sensor, username);
|
||||||
|
|
||||||
sensorSocketEndpoint.queueDeviceUpdate(
|
sensorSocketEndpoint.queueDeviceUpdate(
|
||||||
sensor, motionSensorRepository.findUser(sensor.getId()));
|
sensor, motionSensorRepository.findUser(sensor.getId()), false, null, false);
|
||||||
|
|
||||||
return toReturn;
|
return toReturn;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
package ch.usi.inf.sa4.sanmarinoes.smarthut.service;
|
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.*;
|
||||||
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 java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -13,16 +10,38 @@ import org.springframework.stereotype.Component;
|
||||||
public class SceneService {
|
public class SceneService {
|
||||||
|
|
||||||
@Autowired private DeviceRepository<Device> deviceRepository;
|
@Autowired private DeviceRepository<Device> deviceRepository;
|
||||||
|
@Autowired private DeviceService deviceService;
|
||||||
|
@Autowired private StateRepository<State<?>> stateRepository;
|
||||||
|
|
||||||
public List<Device> apply(Scene newScene) {
|
private List<Device> copyStatesToDevices(Scene fromScene) {
|
||||||
final List<Device> updated = new ArrayList<>();
|
final List<Device> updated = new ArrayList<>();
|
||||||
|
|
||||||
for (final State<?> s : newScene.getStates()) {
|
for (final State<?> s : fromScene.getStates()) {
|
||||||
s.apply();
|
s.apply();
|
||||||
updated.add(s.getDevice());
|
updated.add(s.getDevice());
|
||||||
}
|
}
|
||||||
deviceRepository.saveAll(updated);
|
|
||||||
|
|
||||||
|
deviceService.populateComputedFields(updated);
|
||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Device> apply(Scene newScene, String username, boolean fromTrigger) {
|
||||||
|
List<Device> updated = copyStatesToDevices(newScene);
|
||||||
|
deviceService.saveAllAsOwner(updated, username, true, fromTrigger);
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Device> applyAsGuest(Scene newScene, String username, Long hostId) {
|
||||||
|
List<Device> updated = copyStatesToDevices(newScene);
|
||||||
|
deviceService.saveAllAsGuestSceneApplication(updated, username, hostId);
|
||||||
|
return updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<State<?>> copyStates(Scene to, Scene from) {
|
||||||
|
final ArrayList<State<?>> states = new ArrayList<>();
|
||||||
|
for (final State<?> s : from.getStates()) {
|
||||||
|
states.add(stateRepository.save(s.copyToSceneId(to.getId())));
|
||||||
|
}
|
||||||
|
return states;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,8 @@ public class SensorService {
|
||||||
sensor =
|
sensor =
|
||||||
deviceService.saveAsOwner(
|
deviceService.saveAsOwner(
|
||||||
sensor, sensorRepository.findUser(sensor.getId()).getUsername());
|
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;
|
return sensor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,18 +45,16 @@ public class ThermostatService {
|
||||||
return Utils.toList(all);
|
return Utils.toList(all);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean computeState(Thermostat t) {
|
public void computeState(Thermostat t) {
|
||||||
populateMeasuredTemperature(t);
|
populateMeasuredTemperature(t);
|
||||||
return t.computeState();
|
t.computeState();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateState(Thermostat t) {
|
private void updateState(Thermostat t) {
|
||||||
boolean shouldUpdate = this.computeState(t);
|
this.computeState(t);
|
||||||
|
|
||||||
if (shouldUpdate) {
|
|
||||||
deviceService.saveAsOwner(t, thermostatRepository.findUser(t.getId()).getUsername());
|
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() {
|
public void updateStates() {
|
||||||
|
@ -72,7 +70,6 @@ public class ThermostatService {
|
||||||
populateMeasuredTemperature(u);
|
populateMeasuredTemperature(u);
|
||||||
t = Optional.of(u);
|
t = Optional.of(u);
|
||||||
}
|
}
|
||||||
|
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.Device;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User;
|
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.models.UserRepository;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.service.DeviceService;
|
||||||
import com.google.common.collect.HashMultimap;
|
import com.google.common.collect.HashMultimap;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import com.google.common.collect.Multimaps;
|
import com.google.common.collect.Multimaps;
|
||||||
|
@ -21,6 +22,8 @@ public class SensorSocketEndpoint extends Endpoint {
|
||||||
|
|
||||||
private Gson gson = GsonConfig.socketGson();
|
private Gson gson = GsonConfig.socketGson();
|
||||||
|
|
||||||
|
@Autowired private DeviceService deviceService;
|
||||||
|
|
||||||
private UserRepository userRepository;
|
private UserRepository userRepository;
|
||||||
|
|
||||||
private JWTTokenUtils jwtTokenUtils;
|
private JWTTokenUtils jwtTokenUtils;
|
||||||
|
@ -28,7 +31,10 @@ public class SensorSocketEndpoint extends Endpoint {
|
||||||
private Multimap<User, Session> authorizedClients =
|
private Multimap<User, Session> authorizedClients =
|
||||||
Multimaps.synchronizedMultimap(HashMultimap.create());
|
Multimaps.synchronizedMultimap(HashMultimap.create());
|
||||||
|
|
||||||
private final Map<User, Map<Long, Device>> 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<User, Map<Long, String>> messages = new HashMap<>();
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public SensorSocketEndpoint(UserRepository userRepository, JWTTokenUtils jwtTokenUtils) {
|
public SensorSocketEndpoint(UserRepository userRepository, JWTTokenUtils jwtTokenUtils) {
|
||||||
|
@ -41,18 +47,33 @@ public class SensorSocketEndpoint extends Endpoint {
|
||||||
*
|
*
|
||||||
* @param device the device update to be sent
|
* @param device the device update to be sent
|
||||||
* @param u the user the device belongs
|
* @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) {
|
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.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 */
|
/** Sends all device updates queued to be sent in a unique WebSocket message */
|
||||||
public void flushDeviceUpdates() {
|
public void flushDeviceUpdates() {
|
||||||
synchronized (messages) {
|
synchronized (messages) {
|
||||||
for (Map.Entry<User, Map<Long, Device>> batchForUser : messages.entrySet()) {
|
for (Map.Entry<User, Map<Long, String>> batchForUser : messages.entrySet()) {
|
||||||
broadcast(batchForUser.getKey(), batchForUser.getValue().values());
|
broadcast(batchForUser.getKey(), batchForUser.getValue().values());
|
||||||
batchForUser.getValue().clear();
|
batchForUser.getValue().clear();
|
||||||
}
|
}
|
||||||
|
@ -66,13 +87,13 @@ public class SensorSocketEndpoint extends Endpoint {
|
||||||
* @param messages the message batch to send
|
* @param messages the message batch to send
|
||||||
* @param u the user to which to send the message
|
* @param u the user to which to send the message
|
||||||
*/
|
*/
|
||||||
private void broadcast(User u, Collection<?> messages) {
|
private void broadcast(User u, Collection<String> messages) {
|
||||||
if (messages.isEmpty()) return;
|
if (messages.isEmpty()) return;
|
||||||
final HashSet<Session> sessions = new HashSet<>(authorizedClients.get(u));
|
final HashSet<Session> sessions = new HashSet<>(authorizedClients.get(u));
|
||||||
for (Session s : sessions) {
|
for (Session s : sessions) {
|
||||||
try {
|
try {
|
||||||
if (s.isOpen()) {
|
if (s.isOpen()) {
|
||||||
s.getBasicRemote().sendText(gson.toJson(messages));
|
s.getBasicRemote().sendText("[" + String.join(",", messages) + "]");
|
||||||
} else {
|
} else {
|
||||||
authorizedClients.remove(u, s);
|
authorizedClients.remove(u, s);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue