Merge branch '55-users-can-add-conditions-to-automations' into 'dev'

Resolve "Users can add conditions to automations"

Closes #55

See merge request sa4-2020/the-sanmarinoes/backend!133
This commit is contained in:
Claudio Maggioni 2020-05-10 21:30:39 +02:00
commit c003bd1e87
21 changed files with 756 additions and 6 deletions

View file

@ -1,6 +1,7 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.config; package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.AutomationFastUpdateRequest; import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.AutomationFastUpdateRequest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Condition;
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;
@ -43,12 +44,32 @@ public class GsonConfig {
.registerSubtype( .registerSubtype(
AutomationFastUpdateRequest.RangeTriggerDTO.class, AutomationFastUpdateRequest.RangeTriggerDTO.class,
"rangeTrigger"); "rangeTrigger");
RuntimeTypeAdapterFactory<AutomationFastUpdateRequest.ConditionDTO>
runtimeTypeAdapterFactoryIII =
RuntimeTypeAdapterFactory.of(
AutomationFastUpdateRequest.ConditionDTO.class, "kind")
.registerSubtype(
AutomationFastUpdateRequest.BooleanConditionDTO.class,
"booleanCondition")
.registerSubtype(
AutomationFastUpdateRequest.RangeConditionDTO.class,
"rangeCondition")
.registerSubtype(
AutomationFastUpdateRequest.ThermostatConditionDTO.class,
"thermostatCondition");
builder.registerTypeAdapterFactory(runtimeTypeAdapterFactory); builder.registerTypeAdapterFactory(runtimeTypeAdapterFactory);
builder.registerTypeAdapterFactory(runtimeTypeAdapterFactoryII); builder.registerTypeAdapterFactory(runtimeTypeAdapterFactoryII);
builder.registerTypeAdapterFactory(runtimeTypeAdapterFactoryIII);
builder.registerTypeAdapter( builder.registerTypeAdapter(
Trigger.class, Trigger.class,
(JsonSerializer<Trigger<?>>) (JsonSerializer<Trigger<?>>)
(src, typeOfSrc, context) -> context.serialize((Object) src)); (src, typeOfSrc, context) -> context.serialize((Object) src));
builder.registerTypeAdapter(
Condition.class,
(JsonSerializer<Condition<?>>)
(src, typeOfSrc, context) -> context.serialize((Object) src));
return builder; return builder;
} }

View file

@ -200,7 +200,7 @@ public final class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory {
return this; return this;
} }
private <R> void initMaps( private void initMaps(
Gson gson, Gson gson,
Map<String, TypeAdapter<?>> labelToDelegate, Map<String, TypeAdapter<?>> labelToDelegate,
Map<Class<?>, TypeAdapter<?>> subtypeToDelegate) { Map<Class<?>, TypeAdapter<?>> subtypeToDelegate) {

View file

@ -28,6 +28,7 @@ public class AutomationController {
@Autowired private AutomationRepository automationRepository; @Autowired private AutomationRepository automationRepository;
@Autowired private ScenePriorityRepository sceneRepository; @Autowired private ScenePriorityRepository sceneRepository;
@Autowired private TriggerRepository<Trigger<?>> triggerRepository; @Autowired private TriggerRepository<Trigger<?>> triggerRepository;
@Autowired private ConditionRepository<Condition<?>> conditionRepository;
@Autowired private UserRepository userService; @Autowired private UserRepository userService;
@GetMapping @GetMapping
@ -86,6 +87,7 @@ public class AutomationController {
triggerRepository.deleteAllByAutomationId(a.getId()); triggerRepository.deleteAllByAutomationId(a.getId());
sceneRepository.deleteAllByAutomationId(a.getId()); sceneRepository.deleteAllByAutomationId(a.getId());
conditionRepository.deleteAllByAutomationId(a.getId());
Iterable<Trigger<?>> tt = Iterable<Trigger<?>> tt =
triggerRepository.saveAll( triggerRepository.saveAll(
@ -101,6 +103,14 @@ public class AutomationController {
.map(AutomationFastUpdateRequest.ScenePriorityDTO::toModel) .map(AutomationFastUpdateRequest.ScenePriorityDTO::toModel)
.collect(Collectors.toList())); .collect(Collectors.toList()));
Iterable<Condition<?>> cc =
conditionRepository.saveAll(
req.getConditions()
.stream()
.map(AutomationFastUpdateRequest.ConditionDTO::toModel)
.map(t -> t.setAutomationId(a.getId()))
.collect(Collectors.toList()));
for (final ScenePriority s : ss) { for (final ScenePriority s : ss) {
s.setAutomationId(a.getId()); s.setAutomationId(a.getId());
@ -112,8 +122,10 @@ public class AutomationController {
a.getScenes().clear(); a.getScenes().clear();
a.getTriggers().clear(); a.getTriggers().clear();
a.getConditions().clear();
ss.forEach(t -> a.getScenes().add(t)); ss.forEach(t -> a.getScenes().add(t));
tt.forEach(t -> a.getTriggers().add(t)); tt.forEach(t -> a.getTriggers().add(t));
cc.forEach(t -> a.getConditions().add(t));
return a; return a;
} }

View file

@ -0,0 +1,61 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.BooleanConditionSaveRequest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.BooleanCondition;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.BooleanConditionRepository;
import java.util.List;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@EnableAutoConfiguration
@RequestMapping("/booleanCondition")
public class BooleanConditionController {
@Autowired BooleanConditionRepository booleanConditionRepository;
@GetMapping("/{automationId}")
public List<BooleanCondition<?>> getAll(@PathVariable long automationId) {
return booleanConditionRepository.findAllByAutomationId(automationId);
}
private BooleanCondition<?> save(BooleanCondition<?> newRL, BooleanConditionSaveRequest s) {
newRL.setDeviceId(s.getDeviceId());
newRL.setAutomationId(s.getAutomationId());
newRL.setOn(s.isOn());
return booleanConditionRepository.save(newRL);
}
@PostMapping
public BooleanCondition<?> create(
@Valid @RequestBody BooleanConditionSaveRequest booleanTriggerSaveRequest) {
return save(new BooleanCondition<>(), booleanTriggerSaveRequest);
}
@PutMapping
public BooleanCondition<?> update(
@Valid @RequestBody BooleanConditionSaveRequest booleanTriggerSaveRequest)
throws NotFoundException {
return save(
booleanConditionRepository
.findById(booleanTriggerSaveRequest.getId())
.orElseThrow(NotFoundException::new),
booleanTriggerSaveRequest);
}
@DeleteMapping("/{id}")
public void delete(@PathVariable long id) {
booleanConditionRepository.deleteById(id);
}
}

View file

@ -0,0 +1,62 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.RangeConditionSaveRequest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RangeCondition;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RangeConditionRepository;
import java.util.List;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@EnableAutoConfiguration
@RequestMapping("/rangeCondition")
public class RangeConditionController {
@Autowired RangeConditionRepository rangeConditionRepository;
@GetMapping("/{automationId}")
public List<RangeCondition<?>> getAll(@PathVariable long automationId) {
return rangeConditionRepository.findAllByAutomationId(automationId);
}
private RangeCondition<?> save(RangeCondition<?> newRL, RangeConditionSaveRequest s) {
newRL.setDeviceId(s.getDeviceId());
newRL.setAutomationId(s.getAutomationId());
newRL.setOperator(s.getOperator());
newRL.setRange(s.getRange());
return rangeConditionRepository.save(newRL);
}
@PostMapping
public RangeCondition<?> create(
@Valid @RequestBody RangeConditionSaveRequest booleanTriggerSaveRequest) {
return save(new RangeCondition<>(), booleanTriggerSaveRequest);
}
@PutMapping
public RangeCondition<?> update(
@Valid @RequestBody RangeConditionSaveRequest booleanTriggerSaveRequest)
throws NotFoundException {
return save(
rangeConditionRepository
.findById(booleanTriggerSaveRequest.getId())
.orElseThrow(NotFoundException::new),
booleanTriggerSaveRequest);
}
@DeleteMapping("/{id}")
public void delete(@PathVariable long id) {
rangeConditionRepository.deleteById(id);
}
}

View file

@ -0,0 +1,62 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.ThermostatConditionSaveRequest;
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ThermostatCondition;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ThermostatConditionRepository;
import java.util.List;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@EnableAutoConfiguration
@RequestMapping("/thermostatCondition")
public class ThermostatConditionController {
@Autowired ThermostatConditionRepository thermostatConditionRepository;
@GetMapping("/{automationId}")
public List<ThermostatCondition> getAll(@PathVariable long automationId) {
return thermostatConditionRepository.findAllByAutomationId(automationId);
}
private ThermostatCondition save(ThermostatCondition newRL, ThermostatConditionSaveRequest s) {
newRL.setDeviceId(s.getDeviceId());
newRL.setAutomationId(s.getAutomationId());
newRL.setOperator(s.getOperator());
newRL.setMode(s.getMode());
return thermostatConditionRepository.save(newRL);
}
@PostMapping
public ThermostatCondition create(
@Valid @RequestBody ThermostatConditionSaveRequest booleanTriggerSaveRequest) {
return save(new ThermostatCondition(), booleanTriggerSaveRequest);
}
@PutMapping
public ThermostatCondition update(
@Valid @RequestBody ThermostatConditionSaveRequest booleanTriggerSaveRequest)
throws NotFoundException {
return save(
thermostatConditionRepository
.findById(booleanTriggerSaveRequest.getId())
.orElseThrow(NotFoundException::new),
booleanTriggerSaveRequest);
}
@DeleteMapping("/{id}")
public void delete(@PathVariable long id) {
thermostatConditionRepository.deleteById(id);
}
}

View file

@ -1,8 +1,13 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto; package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.BooleanCondition;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.BooleanTrigger; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.BooleanTrigger;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Condition;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RangeCondition;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RangeTrigger; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RangeTrigger;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ScenePriority; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ScenePriority;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Thermostat;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ThermostatCondition;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Trigger; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Trigger;
import java.util.List; import java.util.List;
import javax.validation.constraints.Min; import javax.validation.constraints.Min;
@ -57,8 +62,58 @@ public class AutomationFastUpdateRequest {
} }
} }
public abstract class ConditionDTO {
@NotNull public long deviceId;
public abstract Condition<?> toModel();
}
public class BooleanConditionDTO extends ConditionDTO {
@NotNull public boolean on;
@Override
public Condition<?> toModel() {
BooleanCondition<?> t = new BooleanCondition<>();
t.setDeviceId(this.deviceId);
t.setOn(this.on);
return t;
}
}
public class RangeConditionDTO extends ConditionDTO {
@NotNull RangeCondition.Operator operator;
@NotNull double range;
@Override
public Condition<?> toModel() {
RangeCondition<?> t = new RangeCondition<>();
t.setDeviceId(this.deviceId);
t.setOperator(this.operator);
t.setRange(this.range);
return t;
}
}
public class ThermostatConditionDTO extends ConditionDTO {
@NotNull ThermostatCondition.Operator operator;
@NotNull private Thermostat.Mode mode;
@Override
public Condition<?> toModel() {
ThermostatCondition t = new ThermostatCondition();
t.setDeviceId(this.deviceId);
t.setOperator(this.operator);
t.setMode(this.mode);
return t;
}
}
@NotNull private List<ScenePriorityDTO> scenes; @NotNull private List<ScenePriorityDTO> scenes;
@NotNull private List<TriggerDTO> triggers; @NotNull private List<TriggerDTO> triggers;
@NotNull private List<ConditionDTO> conditions;
@NotNull private long id; @NotNull private long id;
@NotNull @NotEmpty private String name; @NotNull @NotEmpty private String name;
@ -94,4 +149,12 @@ public class AutomationFastUpdateRequest {
public void setId(long id) { public void setId(long id) {
this.id = id; this.id = id;
} }
public List<ConditionDTO> getConditions() {
return conditions;
}
public void setConditions(List<ConditionDTO> conditions) {
this.conditions = conditions;
}
} }

View file

@ -0,0 +1,42 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
import javax.validation.constraints.NotNull;
public class BooleanConditionSaveRequest {
@NotNull private long id;
@NotNull private Long deviceId;
@NotNull private Long automationId;
@NotNull private boolean on;
public long getId() {
return id;
}
public Long getDeviceId() {
return deviceId;
}
public void setDeviceId(Long deviceId) {
this.deviceId = deviceId;
}
public Long getAutomationId() {
return automationId;
}
public void setAutomationId(Long automationId) {
this.automationId = automationId;
}
public boolean isOn() {
return on;
}
public void setOn(boolean on) {
this.on = on;
}
}

View file

@ -0,0 +1,54 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RangeCondition;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RangeCondition.Operator;
import javax.validation.constraints.NotNull;
public class RangeConditionSaveRequest {
@NotNull private long id;
@NotNull private Long deviceId;
@NotNull private Long automationId;
@NotNull private RangeCondition.Operator operator;
@NotNull private double range;
public long getId() {
return id;
}
public Long getDeviceId() {
return deviceId;
}
public void setDeviceId(Long deviceId) {
this.deviceId = deviceId;
}
public Long getAutomationId() {
return automationId;
}
public void setAutomationId(Long automationId) {
this.automationId = automationId;
}
public Operator getOperator() {
return operator;
}
public void setOperator(Operator operator) {
this.operator = operator;
}
public double getRange() {
return range;
}
public void setRange(double range) {
this.range = range;
}
}

View file

@ -0,0 +1,56 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Thermostat;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Thermostat.Mode;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ThermostatCondition;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ThermostatCondition.Operator;
import javax.validation.constraints.NotNull;
public class ThermostatConditionSaveRequest {
@NotNull private long id;
@NotNull private Long deviceId;
@NotNull private Long automationId;
@NotNull private ThermostatCondition.Operator operator;
@NotNull private Thermostat.Mode mode;
public long getId() {
return id;
}
public Long getDeviceId() {
return deviceId;
}
public void setDeviceId(Long deviceId) {
this.deviceId = deviceId;
}
public Long getAutomationId() {
return automationId;
}
public void setAutomationId(Long automationId) {
this.automationId = automationId;
}
public Operator getOperator() {
return operator;
}
public void setOperator(Operator operator) {
this.operator = operator;
}
public Mode getMode() {
return mode;
}
public void setMode(Mode mode) {
this.mode = mode;
}
}

View file

@ -33,6 +33,9 @@ public class Automation {
@OneToMany(mappedBy = "automation", cascade = CascadeType.REMOVE) @OneToMany(mappedBy = "automation", cascade = CascadeType.REMOVE)
private Set<ScenePriority> scenes = new HashSet<>(); private Set<ScenePriority> scenes = new HashSet<>();
@OneToMany(mappedBy = "automation", orphanRemoval = true, cascade = CascadeType.REMOVE)
private Set<Condition<?>> conditions = new HashSet<>();
@NotNull @NotEmpty private String name; @NotNull @NotEmpty private String name;
public long getId() { public long getId() {
@ -74,4 +77,8 @@ public class Automation {
public void setUserId(Long userId) { public void setUserId(Long userId) {
this.userId = userId; this.userId = userId;
} }
public Set<Condition<?>> getConditions() {
return conditions;
}
} }

View file

@ -7,9 +7,9 @@ import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.Param;
public interface AutomationRepository extends CrudRepository<Automation, Long> { public interface AutomationRepository extends CrudRepository<Automation, Long> {
@EntityGraph(attributePaths = {"scenes", "triggers"}) @EntityGraph(attributePaths = {"scenes", "triggers", "conditions"})
List<Automation> findAllByUserId(@Param("userId") long userId); List<Automation> findAllByUserId(@Param("userId") long userId);
@EntityGraph(attributePaths = {"scenes", "triggers"}) @EntityGraph(attributePaths = {"scenes", "triggers", "conditions"})
Optional<Automation> findByIdAndUserId(@Param("id") long id, @Param("userId") long userId); Optional<Automation> findByIdAndUserId(@Param("id") long id, @Param("userId") long userId);
} }

View file

@ -0,0 +1,33 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import javax.persistence.Column;
import javax.persistence.Entity;
@Entity
public class BooleanCondition<D extends Device & BooleanTriggerable>
extends Condition<D> { // TODO add interface to type constraints
@Column(name = "switchable_on")
private boolean on;
public BooleanCondition() {
super("booleanCondition");
}
public boolean isOn() {
return on;
}
public void setOn(boolean on) {
this.on = on;
}
public boolean check(boolean on) {
return this.on == on;
}
@Override
public boolean triggered() {
return this.getDevice().readTriggerState() == isOn();
}
}

View file

@ -0,0 +1,10 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import java.util.List;
import org.springframework.data.repository.query.Param;
public interface BooleanConditionRepository
extends ConditionRepository<BooleanCondition<? extends Device>> {
List<BooleanCondition<?>> findAllByAutomationId(@Param("automationId") long automationId);
}

View file

@ -0,0 +1,98 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import ch.usi.inf.sa4.sanmarinoes.smarthut.config.GsonExclude;
import io.swagger.annotations.ApiModelProperty;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Transient;
import javax.validation.constraints.NotNull;
@Entity
public abstract class Condition<D extends Device> {
@Transient private String kind;
protected Condition(String kind) {
this.kind = kind;
}
public String getKind() {
return kind;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false, unique = true)
@ApiModelProperty(hidden = true)
private long id;
@ManyToOne(targetEntity = Device.class)
@JoinColumn(name = "device_id", updatable = false, insertable = false)
@GsonExclude
private D device;
/**
* The device this condition belongs to, as a foreign key id. To use when updating and inserting
* from a REST call.
*/
@Column(name = "device_id", nullable = false)
@NotNull
private Long deviceId;
@ManyToOne
@JoinColumn(name = "automation_id", updatable = false, insertable = false)
@GsonExclude
private Automation automation;
@Column(name = "automation_id", nullable = false)
@NotNull
private Long automationId;
public abstract boolean triggered();
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public D getDevice() {
return device;
}
public void setDevice(D device) {
this.device = device;
}
public Long getDeviceId() {
return deviceId;
}
public void setDeviceId(Long deviceId) {
this.deviceId = deviceId;
}
public Automation getAutomation() {
return automation;
}
public void setAutomation(Automation automation) {
this.automation = automation;
}
public Long getAutomationId() {
return automationId;
}
public Condition<D> setAutomationId(Long automationId) {
this.automationId = automationId;
return this;
}
}

View file

@ -0,0 +1,16 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import java.util.List;
import javax.transaction.Transactional;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
public interface ConditionRepository<T extends Condition<?>> extends CrudRepository<T, Long> {
List<T> findAllByDeviceId(@Param("deviceId") long deviceId);
List<T> findAllByAutomationId(@Param("automationId") long automationId);
@Transactional
void deleteAllByAutomationId(@Param("automationId") long automationId);
}

View file

@ -0,0 +1,70 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.RangeTrigger.Operator;
import com.google.gson.annotations.SerializedName;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.validation.constraints.NotNull;
@Entity
public class RangeCondition<D extends Device & RangeTriggerable> extends Condition<D> {
public RangeCondition() {
super("booleanCondition");
}
public enum Operator {
@SerializedName("EQUAL")
EQUAL,
@SerializedName("LESS")
LESS,
@SerializedName("GREATER")
GREATER,
@SerializedName("LESS_EQUAL")
LESS_EQUAL,
@SerializedName("GREATER_EQUAL")
GREATER_EQUAL
}
@NotNull
@Column(nullable = false)
private RangeCondition.Operator operator;
@NotNull
@Column(nullable = false)
private double range;
public RangeCondition.Operator getOperator() {
return operator;
}
public void setOperator(RangeCondition.Operator operator) {
this.operator = operator;
}
public double getRange() {
return range;
}
public void setRange(Double range) {
this.range = range;
}
@Override
public boolean triggered() {
double value = getDevice().readTriggerState();
switch (operator) {
case EQUAL:
return value == range;
case LESS:
return value < range;
case GREATER:
return value > range;
case GREATER_EQUAL:
return value >= range;
case LESS_EQUAL:
return value <= range;
}
throw new IllegalStateException();
}
}

View file

@ -0,0 +1,10 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import java.util.List;
import org.springframework.data.repository.query.Param;
public interface RangeConditionRepository
extends ConditionRepository<RangeCondition<? extends Device>> {
List<RangeCondition<?>> findAllByAutomationId(@Param("automationId") long automationId);
}

View file

@ -0,0 +1,54 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import com.google.gson.annotations.SerializedName;
import javax.persistence.Column;
import javax.persistence.Entity;
@Entity
public class ThermostatCondition extends Condition<Thermostat> {
public ThermostatCondition() {
super("thermostatCondition");
}
public enum Operator {
@SerializedName("EQUAL")
EQUAL,
@SerializedName("NOTEQUAL")
NOTEQUAL,
}
@Column(nullable = false)
private ThermostatCondition.Operator operator;
@Column(nullable = false)
private Thermostat.Mode mode;
public Operator getOperator() {
return operator;
}
public void setOperator(Operator operator) {
this.operator = operator;
}
public Thermostat.Mode getMode() {
return mode;
}
public void setMode(Thermostat.Mode mode) {
this.mode = mode;
}
@Override
public boolean triggered() {
switch (operator) {
case EQUAL:
return getDevice().getMode() == mode;
case NOTEQUAL:
return getDevice().getMode() != mode;
default:
return false;
}
}
}

View file

@ -0,0 +1,9 @@
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
import java.util.List;
import org.springframework.data.repository.query.Param;
public interface ThermostatConditionRepository extends ConditionRepository<ThermostatCondition> {
List<ThermostatCondition> findAllByAutomationId(@Param("automationId") long automationId);
}

View file

@ -22,6 +22,7 @@ public class DeviceService {
private final EagerUserRepository userRepository; private final EagerUserRepository userRepository;
private final DevicePopulationService devicePopulationService; private final DevicePopulationService devicePopulationService;
private final DevicePropagationService devicePropagationService; private final DevicePropagationService devicePropagationService;
private final ConditionRepository<Condition<?>> conditionRepository;
@Autowired @Autowired
public DeviceService( public DeviceService(
@ -31,7 +32,8 @@ public class DeviceService {
AutomationService automationService, AutomationService automationService,
EagerUserRepository userRepository, EagerUserRepository userRepository,
DevicePopulationService devicePopulationService, DevicePopulationService devicePopulationService,
DevicePropagationService devicePropagationService) { DevicePropagationService devicePropagationService,
ConditionRepository<Condition<?>> conditionRepository) {
this.deviceRepository = deviceRepository; this.deviceRepository = deviceRepository;
this.sceneService = sceneService; this.sceneService = sceneService;
this.roomRepository = roomRepository; this.roomRepository = roomRepository;
@ -39,6 +41,7 @@ public class DeviceService {
this.userRepository = userRepository; this.userRepository = userRepository;
this.devicePopulationService = devicePopulationService; this.devicePopulationService = devicePopulationService;
this.devicePropagationService = devicePropagationService; this.devicePropagationService = devicePropagationService;
this.conditionRepository = conditionRepository;
} }
public void throwIfRoomNotOwned(Long roomId, String username) throws NotFoundException { public void throwIfRoomNotOwned(Long roomId, String username) throws NotFoundException {
@ -52,14 +55,20 @@ public class DeviceService {
private void triggerTriggers(Device device, final String username) { private void triggerTriggers(Device device, final String username) {
final long deviceId = device.getId(); final long deviceId = device.getId();
final List<Trigger<Device>> triggers = automationService.findTriggersByDeviceId(deviceId);
List<Trigger<Device>> triggers = automationService.findTriggersByDeviceId(deviceId);
triggers.stream() triggers.stream()
.filter(Trigger::triggered) .filter(Trigger::triggered)
.map(Trigger::getAutomationId) .map(Trigger::getAutomationId)
.map(automationService::findByVerifiedId) .map(automationService::findByVerifiedId)
.distinct() .distinct()
.filter(
a -> {
final List<Condition<?>> conditions =
conditionRepository.findAllByAutomationId(a.getId());
if (conditions.size() == 0) return true;
return conditions.stream().allMatch(Condition::triggered);
})
.map(Automation::getScenes) .map(Automation::getScenes)
.flatMap(Collection::stream) .flatMap(Collection::stream)
.distinct() .distinct()
@ -76,6 +85,7 @@ public class DeviceService {
throws NotFoundException { throws NotFoundException {
final User currentUser = userRepository.findByUsername(guestUsername); final User currentUser = userRepository.findByUsername(guestUsername);
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();
devicePropagationService.renameIfDuplicate(device, host.getUsername()); devicePropagationService.renameIfDuplicate(device, host.getUsername());