General rewrite of device controllers. Now only output devices can be

changes with a PUT call on their controller. Other devices use the
generic controller.
This commit is contained in:
Claudio Maggioni 2020-03-14 16:59:56 +01:00
parent ed2900a3bc
commit f52a38082c
28 changed files with 228 additions and 227 deletions

View file

@ -3,7 +3,7 @@ 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.ButtonDimmerDimRequest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.ButtonDimmerSaveRequest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.GenericDeviceSaveReguest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
import java.util.List;
@ -24,7 +24,10 @@ public class ButtonDimmerController
@Autowired
protected ButtonDimmerController(
ButtonDimmerRepository inputRepository, DimmableLightRepository outputRepository) {
super(inputRepository, outputRepository, ButtonDimmer.CONNECTOR);
super(
inputRepository,
outputRepository,
DimmableLight.BUTTON_DIMMER_DIMMABLE_LIGHT_CONNECTOR);
this.buttonDimmerRepository = inputRepository;
this.dimmableLightRepository = outputRepository;
}
@ -40,7 +43,7 @@ public class ButtonDimmerController
}
@PostMapping
public ButtonDimmer create(@Valid @RequestBody final ButtonDimmerSaveRequest bd) {
public ButtonDimmer create(@Valid @RequestBody final GenericDeviceSaveReguest bd) {
ButtonDimmer newBD = new ButtonDimmer();
newBD.setName(bd.getName());
newBD.setRoomId(bd.getRoomId());

View file

@ -29,14 +29,24 @@ public class DimmableLightController {
return dimmableLightService.findById(id).orElseThrow(NotFoundException::new);
}
private DimmableLight save(DimmableLight initial, DimmableLightSaveRequest dl) {
initial.setIntensity(dl.getIntensity());
initial.setName(dl.getName());
initial.setRoomId(dl.getRoomId());
return dimmableLightService.save(initial);
}
@PostMapping
public DimmableLight create(@Valid @RequestBody DimmableLightSaveRequest dl) {
DimmableLight newDL = new DimmableLight();
newDL.setIntensity(dl.getIntensity());
newDL.setName(dl.getName());
newDL.setRoomId(dl.getRoomId());
return save(new DimmableLight(), dl);
}
return dimmableLightService.save(newDL);
@PutMapping
public DimmableLight update(@Valid @RequestBody DimmableLightSaveRequest sp)
throws NotFoundException {
return save(
dimmableLightService.findById(sp.getId()).orElseThrow(NotFoundException::new), sp);
}
@DeleteMapping("/{id}")

View file

@ -2,8 +2,8 @@ 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.GenericDeviceSaveReguest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.KnobDimmerDimRequest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.KnobDimmerSaveRequest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
import java.util.List;
@ -25,7 +25,10 @@ public class KnobDimmerController
@Autowired
protected KnobDimmerController(
KnobDimmerRepository inputRepository, DimmableLightRepository outputRepository) {
super(inputRepository, outputRepository, KnobDimmer.CONNECTOR);
super(
inputRepository,
outputRepository,
DimmableLight.KNOB_DIMMER_DIMMABLE_LIGHT_CONNECTOR);
this.knobDimmerRepository = inputRepository;
this.dimmableLightRepository = outputRepository;
}
@ -41,7 +44,7 @@ public class KnobDimmerController
}
@PostMapping
public KnobDimmer create(@Valid @RequestBody KnobDimmerSaveRequest kd) {
public KnobDimmer create(@Valid @RequestBody GenericDeviceSaveReguest kd) {
KnobDimmer newKD = new KnobDimmer();
newKD.setName(kd.getName());
newKD.setRoomId(kd.getRoomId());

View file

@ -2,7 +2,7 @@ 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.MotionSensorSaveRequest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.GenericDeviceSaveReguest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.MotionSensor;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.MotionSensorRepository;
@ -30,22 +30,14 @@ public class MotionSensorController {
}
@PostMapping
public MotionSensor create(@Valid @RequestBody MotionSensorSaveRequest ms) {
public MotionSensor create(@Valid @RequestBody GenericDeviceSaveReguest ms) {
MotionSensor newMS = new MotionSensor();
newMS.setDetected(ms.isDetected());
newMS.setId(ms.getId());
newMS.setName(ms.getName());
newMS.setRoomId(ms.getRoomId());
return motionSensorService.save(newMS);
}
@PutMapping
public MotionSensor update(@Valid @RequestBody MotionSensorSaveRequest ms) {
ms.setId(0);
return this.create(ms);
}
@DeleteMapping("/{id}")
public void delete(@PathVariable("id") long id) {
motionSensorService.deleteById(id);

View file

@ -36,10 +36,7 @@ public class RegularLightController {
return regularLightService.findById(id).orElseThrow(NotFoundException::new);
}
@PostMapping
public RegularLight create(@Valid @RequestBody RegularLightSaveRequest rl) {
RegularLight newRL = new RegularLight();
newRL.setId(rl.getId());
private RegularLight save(RegularLight newRL, RegularLightSaveRequest rl) {
newRL.setName(rl.getName());
newRL.setRoomId(rl.getRoomId());
newRL.setOn(rl.isOn());
@ -47,10 +44,16 @@ public class RegularLightController {
return regularLightService.save(newRL);
}
@PostMapping
public RegularLight create(@Valid @RequestBody RegularLightSaveRequest rl) {
return save(new RegularLight(), rl);
}
@PutMapping
public RegularLight update(@Valid @RequestBody RegularLightSaveRequest rl) {
rl.setId(0);
return this.create(rl);
public RegularLight update(@Valid @RequestBody RegularLightSaveRequest rl)
throws NotFoundException {
return save(
regularLightService.findById(rl.getId()).orElseThrow(NotFoundException::new), rl);
}
@DeleteMapping("/{id}")

View file

@ -33,20 +33,12 @@ public class SensorController {
public Sensor create(@Valid @RequestBody SensorSaveRequest s) {
Sensor newSensor = new Sensor();
newSensor.setSensor(s.getSensor());
newSensor.setValue(s.getValue());
newSensor.setId(s.getId());
newSensor.setName(s.getName());
newSensor.setRoomId(s.getRoomId());
return sensorRepository.save(newSensor);
}
@PutMapping
public Sensor update(@Valid @RequestBody SensorSaveRequest s) {
s.setId(0);
return this.create(s);
}
@DeleteMapping("/{id}")
public void deleteById(@PathVariable("id") long id) {
sensorRepository.deleteById(id);

View file

@ -29,9 +29,7 @@ public class SmartPlugController {
return smartPlugRepository.findById(id).orElseThrow(NotFoundException::new);
}
@PostMapping
public SmartPlug create(@Valid @RequestBody SmartPlugSaveRequest sp) {
SmartPlug newSP = new SmartPlug();
private SmartPlug save(SmartPlug newSP, SmartPlugSaveRequest sp) {
newSP.setOn(sp.isOn());
newSP.setId(sp.getId());
newSP.setName(sp.getName());
@ -40,10 +38,15 @@ public class SmartPlugController {
return smartPlugRepository.save(newSP);
}
@PostMapping
public SmartPlug create(@Valid @RequestBody SmartPlugSaveRequest sp) {
return save(new SmartPlug(), sp);
}
@PutMapping
public SmartPlug update(@Valid @RequestBody SmartPlugSaveRequest sp) {
sp.setId(0);
return this.create(sp);
public SmartPlug update(@Valid @RequestBody SmartPlugSaveRequest sp) throws NotFoundException {
return save(
smartPlugRepository.findById(sp.getId()).orElseThrow(NotFoundException::new), sp);
}
@DeleteMapping("/{id}")

View file

@ -2,7 +2,8 @@ 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.SwitchSaveRequest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.GenericDeviceSaveReguest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SwitchOperationRequest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
import java.util.*;
@ -15,9 +16,24 @@ import org.springframework.web.bind.annotation.*;
@RestController
@EnableAutoConfiguration
@RequestMapping("/switch")
public class SwitchController {
public class SwitchController extends InputDeviceConnectionController<Switch, Switchable> {
@Autowired private SwitchRepository switchRepository;
private SwitchRepository switchRepository;
private SwitchableRepository<Switchable> switchableRepository;
/**
* Contstructs the controller by requiring essential object for the controller implementation
*
* @param inputRepository the input device repository
* @param outputRepository the output device repository
*/
@Autowired
protected SwitchController(
SwitchRepository inputRepository, SwitchableRepository<Switchable> outputRepository) {
super(inputRepository, outputRepository, Switchable.SWITCH_SWITCHABLE_CONNECTOR);
this.switchRepository = inputRepository;
this.switchableRepository = outputRepository;
}
@GetMapping
public List<Switch> findAll() {
@ -30,20 +46,48 @@ public class SwitchController {
}
@PostMapping
public Switch create(@Valid @RequestBody SwitchSaveRequest s) {
public Switch create(@Valid @RequestBody GenericDeviceSaveReguest s) {
Switch newSwitch = new Switch();
newSwitch.setId(s.getId());
newSwitch.setName(s.getName());
newSwitch.setRoomId(s.getRoomId());
newSwitch.setOn(s.isOn());
return switchRepository.save(newSwitch);
}
@PutMapping
public Switch update(@Valid @RequestBody SwitchSaveRequest s) {
s.setId(0);
return this.create(s);
@PutMapping("/operate")
public Set<Switchable> operate(@Valid @RequestBody final SwitchOperationRequest sr)
throws NotFoundException {
final Switch s = switchRepository.findById(sr.getId()).orElseThrow(NotFoundException::new);
switch (sr.getType()) {
case ON:
s.setOn(true);
break;
case OFF:
s.setOn(false);
break;
case TOGGLE:
s.toggle();
break;
}
switchableRepository.saveAll(s.getOutputs());
return s.getOutputs();
}
@PostMapping("/{id}/lights")
public Set<? extends OutputDevice> addSwitchable(
@PathVariable("id") long inputId, @RequestParam("switchableId") Long switchableId)
throws NotFoundException {
return addOutput(inputId, switchableId);
}
@DeleteMapping("/{id}/lights")
public Set<? extends OutputDevice> removeSwitchable(
@PathVariable("id") long inputId, @RequestParam("switchableId") Long switchableId)
throws NotFoundException {
return removeOutput(inputId, switchableId);
}
@DeleteMapping("/{id}")

View file

@ -6,6 +6,9 @@ import javax.validation.constraints.NotNull;
public class DimmableLightSaveRequest {
/** Device id (used only for update requests) */
private Long id;
/** The light intensity value. Goes from 0 (off) to 100 (on) */
@NotNull
@Min(1)
@ -44,4 +47,12 @@ public class DimmableLightSaveRequest {
public void setIntensity(Integer intensity) {
this.intensity = intensity;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}

View file

@ -2,7 +2,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
import javax.validation.constraints.NotNull;
public class ButtonDimmerSaveRequest {
public class GenericDeviceSaveReguest {
/**
* The room this device belongs in, as a foreign key id. To use when updating and inserting from
* a REST call.

View file

@ -1,30 +0,0 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
import javax.validation.constraints.NotNull;
public class KnobDimmerSaveRequest {
/**
* The room this device belongs in, as a foreign key id. To use when updating and inserting from
* a REST call.
*/
@NotNull private Long roomId;
/** The name of the device as assigned by the user (e.g. 'Master bedroom light') */
@NotNull private String name;
public void setRoomId(Long roomId) {
this.roomId = roomId;
}
public void setName(String name) {
this.name = name;
}
public Long getRoomId() {
return roomId;
}
public String getName() {
return name;
}
}

View file

@ -1,51 +0,0 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
import javax.validation.constraints.NotNull;
public class MotionSensorSaveRequest {
private boolean detected;
/** Device identifier */
private long id;
/**
* The room this device belongs in, as a foreign key id. To use when updating and inserting from
* a REST call.
*/
@NotNull private Long roomId;
/** The name of the device as assigned by the user (e.g. 'Master bedroom light') */
@NotNull private String name;
public void setRoomId(Long roomId) {
this.roomId = roomId;
}
public void setName(String name) {
this.name = name;
}
public long getId() {
return id;
}
public Long getRoomId() {
return roomId;
}
public String getName() {
return name;
}
public boolean isDetected() {
return detected;
}
public void setDetected(boolean detected) {
this.detected = detected;
}
public void setId(long id) {
this.id = id;
}
}

View file

@ -23,17 +23,11 @@ public class SensorSaveRequest {
LIGHT
}
/** The value of this sensor according to its sensor type */
private int value;
/** The type of this sensor */
@NotNull
@Enumerated(value = EnumType.STRING)
private Sensor.SensorType sensor;
/** Device identifier */
private long id;
/**
* The room this device belongs in, as a foreign key id. To use when updating and inserting from
* a REST call.
@ -51,10 +45,6 @@ public class SensorSaveRequest {
this.name = name;
}
public long getId() {
return id;
}
public Long getRoomId() {
return roomId;
}
@ -70,16 +60,4 @@ public class SensorSaveRequest {
public void setSensor(Sensor.SensorType sensor) {
this.sensor = sensor;
}
public int getValue() {
return this.value;
}
public void setValue(int newValue) {
this.value = newValue;
}
public void setId(long id) {
this.id = id;
}
}

View file

@ -0,0 +1,35 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
import javax.validation.constraints.NotNull;
/** An on/off/toggle operation on a switch */
public class SwitchOperationRequest {
/** The device id */
@NotNull private Long id;
public enum OperationType {
ON,
OFF,
TOGGLE
}
/** The type of switch operation */
@NotNull private SwitchOperationRequest.OperationType type;
public OperationType getType() {
return type;
}
public void setType(OperationType type) {
this.type = type;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}

View file

@ -1,52 +0,0 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
import javax.validation.constraints.NotNull;
public class SwitchSaveRequest {
/** The state of this switch */
private boolean on;
/** Device identifier */
private long id;
/**
* The room this device belongs in, as a foreign key id. To use when updating and inserting from
* a REST call.
*/
@NotNull private Long roomId;
/** The name of the device as assigned by the user (e.g. 'Master bedroom light') */
@NotNull private String name;
public void setRoomId(Long roomId) {
this.roomId = roomId;
}
public void setName(String name) {
this.name = name;
}
public long getId() {
return id;
}
public Long getRoomId() {
return roomId;
}
public String getName() {
return name;
}
public boolean isOn() {
return on;
}
public void setOn(boolean on) {
this.on = on;
}
public void setId(long id) {
this.id = id;
}
}

View file

@ -9,9 +9,6 @@ import javax.persistence.Entity;
@Entity
public class ButtonDimmer extends Dimmer {
public static final Connector<ButtonDimmer, DimmableLight> CONNECTOR =
Connector.basic(ButtonDimmer::getOutputs, DimmableLight::setDimmerId);
/** The delta amount to apply to a increase or decrease intensity */
private static final int DIM_INCREMENT = 10;

View file

@ -33,7 +33,7 @@ public interface Connector<I extends InputDevice, O extends OutputDevice> {
* @return a Connector implementation for the pair of types J and K
*/
static <J extends InputDevice, K extends OutputDevice> Connector<J, K> basic(
Function<J, Set<K>> outputsGetter, BiConsumer<K, Long> inputSetter) {
Function<J, Set<? super K>> outputsGetter, BiConsumer<K, Long> inputSetter) {
return (i, o, connect) -> {
if (connect) {
outputsGetter.apply(i).add(o);

View file

@ -11,7 +11,14 @@ import javax.validation.constraints.NotNull;
/** Represent a dimmable light */
@Entity
public class DimmableLight extends OutputDevice implements Switchable {
public class DimmableLight extends Switchable {
public static final Connector<ButtonDimmer, DimmableLight>
BUTTON_DIMMER_DIMMABLE_LIGHT_CONNECTOR =
Connector.basic(ButtonDimmer::getOutputs, DimmableLight::setDimmerId);
public static final Connector<KnobDimmer, DimmableLight> KNOB_DIMMER_DIMMABLE_LIGHT_CONNECTOR =
Connector.basic(KnobDimmer::getOutputs, DimmableLight::setDimmerId);
public DimmableLight() {
super("light");
@ -65,5 +72,12 @@ public class DimmableLight extends OutputDevice implements Switchable {
public void setDimmerId(Long dimmerId) {
this.dimmerId = dimmerId;
super.setSwitchId(null);
};
@Override
public void setSwitchId(Long switchId) {
super.setSwitchId(switchId);
this.dimmerId = null;
}
}

View file

@ -1,3 +1,3 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
public interface DimmableLightRepository extends DeviceRepository<DimmableLight> {}
public interface DimmableLightRepository extends SwitchableRepository<DimmableLight> {}

View file

@ -2,12 +2,15 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
/**
* A generic abstraction for an input device, i.e. something that captures input either from the
* environment (sensor) or the user (switch / dimmer).
*/
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class InputDevice extends Device {
public InputDevice(String kind) {
super(kind, FlowType.INPUT);

View file

@ -9,9 +9,6 @@ import javax.persistence.Entity;
@Entity
public class KnobDimmer extends Dimmer {
public static final Connector<KnobDimmer, DimmableLight> CONNECTOR =
Connector.basic(KnobDimmer::getOutputs, DimmableLight::setDimmerId);
public KnobDimmer() {
super("knob-dimmer");
}

View file

@ -6,7 +6,7 @@ import javax.validation.constraints.NotNull;
/** Represents a standard non-dimmable light */
@Entity
public class RegularLight extends OutputDevice implements Switchable {
public class RegularLight extends Switchable {
/** Whether the light is on or not */
@Column(name = "light_on", nullable = false)

View file

@ -1,3 +1,3 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
public interface RegularLightRepository extends DeviceRepository<RegularLight> {}
public interface RegularLightRepository extends SwitchableRepository<RegularLight> {}

View file

@ -6,7 +6,7 @@ import javax.validation.constraints.NotNull;
/** A smart plug that can be turned either on or off */
@Entity
public class SmartPlug extends OutputDevice implements Switchable {
public class SmartPlug extends Switchable {
/** Whether the smart plug is on */
@Column(name = "smart_plug_on", nullable = false)

View file

@ -1,3 +1,3 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
public interface SmartPlugRepository extends DeviceRepository<SmartPlug> {}
public interface SmartPlugRepository extends SwitchableRepository<SmartPlug> {}

View file

@ -1,12 +1,18 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.OneToMany;
/** A switch input device */
@Entity
public class Switch extends InputDevice {
@OneToMany(mappedBy = "switchDevice")
private Set<Switchable> switchables = new HashSet<>();
/** The state of this switch */
@Column(nullable = false, name = "switch_on")
private boolean on;
@ -22,6 +28,15 @@ public class Switch extends InputDevice {
*/
public void setOn(boolean state) {
on = state;
for (final Switchable s : switchables) {
s.setOn(on);
}
}
/** Toggle between on and off state */
public void toggle() {
setOn(!isOn());
}
/**
@ -32,4 +47,8 @@ public class Switch extends InputDevice {
public boolean isOn() {
return on;
}
public Set<Switchable> getOutputs() {
return switchables;
}
}

View file

@ -1,24 +1,47 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude;
import javax.persistence.*;
/** A device that can be turned either on or off */
public interface Switchable {
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Switchable extends OutputDevice {
public static final Connector<Switch, Switchable> SWITCH_SWITCHABLE_CONNECTOR =
Connector.basic(Switch::getOutputs, Switchable::setSwitchId);
@ManyToOne
@GsonExclude
@JoinColumn(name = "switch_id", updatable = false, insertable = false)
private Switch switchDevice;
@Column(name = "switch_id")
private Long switchId;
protected Switchable(String kind) {
super(kind);
}
/**
* Returns whether the device is on (true) or not (false)
*
* @return whether the device is on (true) or not (false)
*/
boolean isOn();
public abstract boolean isOn();
/**
* Sets the on status of the device
*
* @param on the new on status: true for on, false for off
*/
void setOn(boolean on);
public abstract void setOn(boolean on);
/** Toggle between on are off state */
default void toggle() {
setOn(!isOn());
public Long getSwitchId() {
return switchId;
}
public void setSwitchId(Long switchId) {
this.switchId = switchId;
}
}

View file

@ -0,0 +1,7 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
/**
* SwitchableRepository acts as a superclass for the other repositories so to mirror in the database
* the class inheritance present among the various switchable devices.
*/
public interface SwitchableRepository<T extends Switchable> extends DeviceRepository<T> {}