Merge branch 'thermostat-feature' into 'dev'
Thermostat feature See merge request sa4-2020/the-sanmarinoes/backend!77
This commit is contained in:
commit
1fe31c0d46
17 changed files with 477 additions and 39 deletions
|
@ -29,8 +29,10 @@ connection.onopen = function(evt) {
|
||||||
connection.onmessage = function(evt) {
|
connection.onmessage = function(evt) {
|
||||||
console.log("***ONMESSAGE", evt);
|
console.log("***ONMESSAGE", evt);
|
||||||
let data = JSON.parse(evt.data);
|
let data = JSON.parse(evt.data);
|
||||||
|
let a = document.getElementById("giovanni");
|
||||||
|
if (a) a.remove();
|
||||||
|
|
||||||
malusa.innerHTML += "<p><pre>" + JSON.stringify(JSON.parse(evt.data), null, 2) + "</pre></p>";
|
malusa.innerHTML += "<pre id=\"giovanni\">" + JSON.stringify(JSON.parse(evt.data), null, 2) + "</pre>";
|
||||||
};
|
};
|
||||||
|
|
||||||
connection.onerror = function(evt) {
|
connection.onerror = function(evt) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
||||||
|
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.JWTUserDetailsService;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.service.JWTUserDetailsService;
|
||||||
import io.jsonwebtoken.ExpiredJwtException;
|
import io.jsonwebtoken.ExpiredJwtException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import javax.servlet.FilterChain;
|
import javax.servlet.FilterChain;
|
||||||
|
|
|
@ -68,6 +68,7 @@ public class SpringFoxConfig {
|
||||||
.or(PathSelectors.regex("/room.*")::apply)
|
.or(PathSelectors.regex("/room.*")::apply)
|
||||||
.or(PathSelectors.regex("/device.*")::apply)
|
.or(PathSelectors.regex("/device.*")::apply)
|
||||||
.or(PathSelectors.regex("/buttonDimmer.*")::apply)
|
.or(PathSelectors.regex("/buttonDimmer.*")::apply)
|
||||||
|
.or(PathSelectors.regex("/thermostat.*")::apply)
|
||||||
.or(PathSelectors.regex("/dimmableLight.*")::apply)
|
.or(PathSelectors.regex("/dimmableLight.*")::apply)
|
||||||
.or(PathSelectors.regex("/knobDimmer.*")::apply)
|
.or(PathSelectors.regex("/knobDimmer.*")::apply)
|
||||||
.or(PathSelectors.regex("/regularLight.*")::apply)
|
.or(PathSelectors.regex("/regularLight.*")::apply)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.config;
|
||||||
|
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.JWTUserDetailsService;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.service.JWTUserDetailsService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
|
@ -8,6 +8,8 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UserNotFoundException;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.service.JWTUserDetailsService;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
import org.springframework.security.authentication.BadCredentialsException;
|
import org.springframework.security.authentication.BadCredentialsException;
|
||||||
import org.springframework.security.authentication.DisabledException;
|
import org.springframework.security.authentication.DisabledException;
|
||||||
|
|
|
@ -5,6 +5,7 @@ import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.RoomSaveRequest;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.RoomSaveRequest;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.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.ThermostatService;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
|
@ -29,6 +30,8 @@ public class RoomController {
|
||||||
|
|
||||||
@Autowired private KnobDimmerRepository knobDimmerRepository;
|
@Autowired private KnobDimmerRepository knobDimmerRepository;
|
||||||
|
|
||||||
|
@Autowired private ThermostatService thermostatService;
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public List<Room> findAll() {
|
public List<Room> findAll() {
|
||||||
return toList(roomRepository.findAll());
|
return toList(roomRepository.findAll());
|
||||||
|
@ -99,6 +102,12 @@ public class RoomController {
|
||||||
*/
|
*/
|
||||||
@GetMapping(path = "/{roomId}/devices")
|
@GetMapping(path = "/{roomId}/devices")
|
||||||
public List<Device> getDevices(@PathVariable("roomId") long roomid) {
|
public List<Device> getDevices(@PathVariable("roomId") long roomid) {
|
||||||
return deviceRepository.findByRoomId(roomid);
|
Iterable<Device> devices = deviceRepository.findByRoomId(roomid);
|
||||||
|
for (Device d : devices) {
|
||||||
|
if (d instanceof Thermostat) {
|
||||||
|
thermostatService.populateMeasuredTemperature((Thermostat) d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return toList(devices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,15 @@ import static ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils.toList;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SensorSaveRequest;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.SensorSaveRequest;
|
||||||
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.SensorService;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
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.*;
|
import org.springframework.boot.autoconfigure.*;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
@ -20,9 +23,14 @@ import org.springframework.web.bind.annotation.*;
|
||||||
@RequestMapping("/sensor")
|
@RequestMapping("/sensor")
|
||||||
public class SensorController {
|
public class SensorController {
|
||||||
|
|
||||||
@Autowired private SensorRepository sensorRepository;
|
@Autowired
|
||||||
|
private SensorRepository sensorRepository;
|
||||||
|
|
||||||
@Autowired private SensorSocketEndpoint sensorSocketEndpoint;
|
@Autowired
|
||||||
|
private SensorSocketEndpoint sensorSocketEndpoint;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SensorService sensorService;
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public List<Sensor> findAll() {
|
public List<Sensor> findAll() {
|
||||||
|
@ -45,29 +53,13 @@ public class SensorController {
|
||||||
return sensorRepository.save(newSensor);
|
return sensorRepository.save(newSensor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the sensor with new measurement and propagates update through websocket
|
|
||||||
*
|
|
||||||
* @param sensor the sensor to update
|
|
||||||
* @param value the new measurement
|
|
||||||
* @return the updated sensor
|
|
||||||
*/
|
|
||||||
public Sensor updateValueFromSensor(Sensor sensor, BigDecimal value) {
|
|
||||||
sensor.setValue(value);
|
|
||||||
final Sensor toReturn = sensorRepository.save(sensor);
|
|
||||||
|
|
||||||
sensorSocketEndpoint.queueDeviceUpdate(sensor, sensorRepository.findUser(sensor.getId()));
|
|
||||||
|
|
||||||
return toReturn;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PutMapping("/{id}/value")
|
@PutMapping("/{id}/value")
|
||||||
public Sensor updateValue(
|
public Sensor updateValue(
|
||||||
@PathVariable("id") Long sensorId,
|
@PathVariable("id") Long sensorId,
|
||||||
@RequestParam("value") BigDecimal value,
|
@RequestParam("value") BigDecimal value,
|
||||||
final Principal principal)
|
final Principal principal)
|
||||||
throws NotFoundException {
|
throws NotFoundException {
|
||||||
return updateValueFromSensor(
|
return sensorService.updateValueFromSensor(
|
||||||
sensorRepository
|
sensorRepository
|
||||||
.findByIdAndUsername(sensorId, principal.getName())
|
.findByIdAndUsername(sensorId, principal.getName())
|
||||||
.orElseThrow(NotFoundException::new),
|
.orElseThrow(NotFoundException::new),
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.controller;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.dto.ThermostatSaveRequest;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.error.NotFoundException;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.service.ThermostatService;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.*;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@RequestMapping("/thermostat")
|
||||||
|
public class ThermostatController {
|
||||||
|
|
||||||
|
@Autowired private ThermostatRepository thermostatRepository;
|
||||||
|
|
||||||
|
@Autowired private ThermostatService thermostatService;
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public List<Thermostat> findAll(Principal user) {
|
||||||
|
return thermostatService.findAll(user.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public Thermostat findById(@PathVariable("id") long id, Principal principal)
|
||||||
|
throws NotFoundException {
|
||||||
|
return thermostatService
|
||||||
|
.findById(id, principal.getName())
|
||||||
|
.orElseThrow(NotFoundException::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Thermostat save(Thermostat newT, ThermostatSaveRequest t) {
|
||||||
|
newT.setTargetTemperature(t.getTargetTemperature());
|
||||||
|
newT.setId(t.getId());
|
||||||
|
newT.setName(t.getName());
|
||||||
|
newT.setRoomId(t.getRoomId());
|
||||||
|
newT.setUseExternalSensors(t.isUseExternalSensors());
|
||||||
|
|
||||||
|
if (t.isTurnOn()) {
|
||||||
|
newT.setState(Thermostat.ThermostatState.IDLE);
|
||||||
|
thermostatService.computeState(newT);
|
||||||
|
} else {
|
||||||
|
newT.setState(Thermostat.ThermostatState.OFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
newT = thermostatRepository.save(newT);
|
||||||
|
thermostatService.populateMeasuredTemperature(newT);
|
||||||
|
return newT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public Thermostat create(@Valid @RequestBody ThermostatSaveRequest t) {
|
||||||
|
return save(new Thermostat(), t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping
|
||||||
|
public Thermostat update(@Valid @RequestBody ThermostatSaveRequest t, final Principal principal)
|
||||||
|
throws NotFoundException {
|
||||||
|
return save(
|
||||||
|
thermostatRepository
|
||||||
|
.findByIdAndUsername(t.getId(), principal.getName())
|
||||||
|
.orElseThrow(NotFoundException::new),
|
||||||
|
t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public void deleteById(@PathVariable("id") long id) {
|
||||||
|
thermostatRepository.deleteById(id);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.dto;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public class ThermostatSaveRequest {
|
||||||
|
|
||||||
|
/** 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;
|
||||||
|
|
||||||
|
/** Temperature to be reached */
|
||||||
|
@NotNull private BigDecimal targetTemperature;
|
||||||
|
|
||||||
|
@NotNull private boolean useExternalSensors;
|
||||||
|
|
||||||
|
/** State of this thermostat */
|
||||||
|
@NotNull private boolean turnOn;
|
||||||
|
|
||||||
|
public boolean isTurnOn() {
|
||||||
|
return turnOn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTurnOn(boolean turnOn) {
|
||||||
|
this.turnOn = turnOn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUseExternalSensors() {
|
||||||
|
return useExternalSensors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUseExternalSensors(boolean useExternalSensors) {
|
||||||
|
this.useExternalSensors = useExternalSensors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getTargetTemperature() {
|
||||||
|
return this.targetTemperature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTargetTemperature(BigDecimal targetTemperature) {
|
||||||
|
this.targetTemperature = targetTemperature;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 void setId(long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,7 @@
|
||||||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
public interface SensorRepository extends DeviceRepository<Sensor> {}
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface SensorRepository extends DeviceRepository<Sensor> {
|
||||||
|
List<Sensor> findAllBySensor(Sensor.SensorType sensor);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Transient;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
/** A thermostat capable of controlling cooling and heating. */
|
||||||
|
@Entity
|
||||||
|
public class Thermostat extends OutputDevice {
|
||||||
|
|
||||||
|
public enum ThermostatState {
|
||||||
|
@SerializedName("OFF")
|
||||||
|
OFF,
|
||||||
|
@SerializedName("IDLE")
|
||||||
|
IDLE,
|
||||||
|
@SerializedName("COOLING")
|
||||||
|
COOLING,
|
||||||
|
@SerializedName("HEATING")
|
||||||
|
HEATING
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Temperature to be reached */
|
||||||
|
@Column @NotNull private BigDecimal targetTemperature;
|
||||||
|
|
||||||
|
/** The temperature detected by the embedded sensor */
|
||||||
|
@Column(nullable = false, precision = 4, scale = 1)
|
||||||
|
private BigDecimal internalSensorTemperature =
|
||||||
|
Sensor.TYPICAL_VALUES.get(Sensor.SensorType.TEMPERATURE);
|
||||||
|
|
||||||
|
/** State of this thermostat */
|
||||||
|
@Column @NotNull private ThermostatState state;
|
||||||
|
|
||||||
|
@Transient private BigDecimal measuredTemperature;
|
||||||
|
|
||||||
|
@Column private boolean useExternalSensors = false;
|
||||||
|
|
||||||
|
/** Creates a thermostat with a temperature sensor and its initial OFF state */
|
||||||
|
public Thermostat() {
|
||||||
|
super("thermostat");
|
||||||
|
this.state = ThermostatState.OFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setState(ThermostatState state) {
|
||||||
|
this.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ThermostatState getState() {
|
||||||
|
return this.state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getTargetTemperature() {
|
||||||
|
return this.targetTemperature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getInternalSensorTemperature() {
|
||||||
|
return internalSensorTemperature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUseExternalSensors() {
|
||||||
|
return useExternalSensors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal getMeasuredTemperature() {
|
||||||
|
return measuredTemperature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMeasuredTemperature(BigDecimal measuredTemperature) {
|
||||||
|
this.measuredTemperature = measuredTemperature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTargetTemperature(BigDecimal targetTemperature) {
|
||||||
|
this.targetTemperature = targetTemperature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInternalSensorTemperature(BigDecimal internalSensorTemperature) {
|
||||||
|
this.internalSensorTemperature = internalSensorTemperature;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUseExternalSensors(boolean useExternalSensors) {
|
||||||
|
this.useExternalSensors = useExternalSensors;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import javax.transaction.Transactional;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
|
||||||
|
public interface ThermostatRepository extends DeviceRepository<Thermostat> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds all devices belonging to a user
|
||||||
|
*
|
||||||
|
* @param username the User's username
|
||||||
|
* @return all devices of that user
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
@Query("SELECT t FROM Thermostat t JOIN t.room r JOIN r.user u WHERE u.username = ?1")
|
||||||
|
List<Thermostat> findAllByUsername(String username);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the average temperature of all temperature sensors in the room
|
||||||
|
*
|
||||||
|
* @param thermostatRoomId room ID of the thermostat
|
||||||
|
* @return an optional big decimal, empty if none found
|
||||||
|
*/
|
||||||
|
@Query(
|
||||||
|
"SELECT AVG(s.value) FROM Sensor s JOIN s.room r WHERE s.sensor = 'TEMPERATURE' AND r.id = ?1")
|
||||||
|
Optional<BigDecimal> getAverageTemperature(Long thermostatRoomId);
|
||||||
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
package ch.usi.inf.sa4.sanmarinoes.smarthut.scheduled;
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.scheduled;
|
||||||
|
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.controller.MotionSensorController;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.controller.MotionSensorController;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.controller.SensorController;
|
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.service.SensorService;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.service.ThermostatService;
|
||||||
import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint;
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint;
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -26,24 +26,24 @@ public class UpdateTasks {
|
||||||
|
|
||||||
@Autowired private SmartPlugRepository smartPlugRepository;
|
@Autowired private SmartPlugRepository smartPlugRepository;
|
||||||
|
|
||||||
@Autowired private SensorController sensorController;
|
@Autowired private SensorService sensorService;
|
||||||
|
|
||||||
|
@Autowired private ThermostatService thermostatService;
|
||||||
|
|
||||||
@Autowired private MotionSensorController motionSensorController;
|
@Autowired private MotionSensorController motionSensorController;
|
||||||
|
|
||||||
@Autowired private SensorSocketEndpoint sensorSocketEndpoint;
|
@Autowired private SensorSocketEndpoint sensorSocketEndpoint;
|
||||||
|
|
||||||
/** Generates fake sensor updates every two seconds with a +/- 1.25% error */
|
/** Generates fake sensor updates every two seconds with a +/- 2.5% error */
|
||||||
@Scheduled(fixedRate = 2000)
|
@Scheduled(fixedRate = 2000)
|
||||||
public void sensorFakeUpdate() {
|
public void sensorFakeUpdate() {
|
||||||
StreamSupport.stream(sensorRepository.findAll().spliterator(), true)
|
sensorService.sensorFakeUpdate();
|
||||||
.forEach(
|
}
|
||||||
sensor ->
|
|
||||||
sensorController.updateValueFromSensor(
|
/** Generates fake sensor updates every two seconds with a +/- 2.5% error */
|
||||||
sensor,
|
@Scheduled(fixedRate = 2000)
|
||||||
Sensor.TYPICAL_VALUES
|
public void thermostatInteralSensorFakeUpdate() {
|
||||||
.get(sensor.getSensor())
|
thermostatService.fakeUpdateAll();
|
||||||
.multiply(
|
|
||||||
BigDecimal.valueOf(0.9875 + Math.random() / 40))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,7 +71,10 @@ public class UpdateTasks {
|
||||||
public void smartPlugConsumptionFakeUpdate() {
|
public void smartPlugConsumptionFakeUpdate() {
|
||||||
smartPlugRepository.updateTotalConsumption(SmartPlug.AVERAGE_CONSUMPTION_KW);
|
smartPlugRepository.updateTotalConsumption(SmartPlug.AVERAGE_CONSUMPTION_KW);
|
||||||
final Collection<SmartPlug> c = smartPlugRepository.findByOn(true);
|
final Collection<SmartPlug> c = smartPlugRepository.findByOn(true);
|
||||||
c.forEach(s -> sensorSocketEndpoint.queueDeviceUpdate(s, sensorRepository.findUser(s.getId())));
|
c.forEach(
|
||||||
|
s ->
|
||||||
|
sensorSocketEndpoint.queueDeviceUpdate(
|
||||||
|
s, sensorRepository.findUser(s.getId())));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sends device updates through sensor socket in batch every one second */
|
/** Sends device updates through sensor socket in batch every one second */
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package ch.usi.inf.sa4.sanmarinoes.smarthut.models;
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.service;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.User;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.UserRepository;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.security.core.*;
|
import org.springframework.security.core.*;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
|
@ -0,0 +1,47 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.service;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Sensor;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.SensorRepository;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SensorService {
|
||||||
|
|
||||||
|
@Autowired private SensorRepository sensorRepository;
|
||||||
|
|
||||||
|
@Autowired private ThermostatService thermostatService;
|
||||||
|
|
||||||
|
@Autowired private SensorSocketEndpoint endpoint;
|
||||||
|
|
||||||
|
private void randomJitter(Sensor sensor) {
|
||||||
|
updateValueFromSensor(
|
||||||
|
sensor,
|
||||||
|
Sensor.TYPICAL_VALUES
|
||||||
|
.get(sensor.getSensor())
|
||||||
|
.multiply(BigDecimal.valueOf(0.975 + Math.random() / 20)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sensorFakeUpdate() {
|
||||||
|
sensorRepository.findAll().forEach(this::randomJitter);
|
||||||
|
thermostatService.updateStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the sensor with new measurement and propagates update through websocket
|
||||||
|
*
|
||||||
|
* @param sensor the sensor to update
|
||||||
|
* @param value the new measurement
|
||||||
|
* @return the updated sensor
|
||||||
|
*/
|
||||||
|
public Sensor updateValueFromSensor(Sensor sensor, BigDecimal value) {
|
||||||
|
sensor.setValue(value);
|
||||||
|
final Sensor toReturn = sensorRepository.save(sensor);
|
||||||
|
|
||||||
|
endpoint.queueDeviceUpdate(sensor, sensorRepository.findUser(sensor.getId()));
|
||||||
|
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
package ch.usi.inf.sa4.sanmarinoes.smarthut.service;
|
||||||
|
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Sensor;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.Thermostat;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.models.ThermostatRepository;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.socket.SensorSocketEndpoint;
|
||||||
|
import ch.usi.inf.sa4.sanmarinoes.smarthut.utils.Utils;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class ThermostatService {
|
||||||
|
|
||||||
|
@Autowired private SensorSocketEndpoint endpoint;
|
||||||
|
|
||||||
|
@Autowired private ThermostatRepository thermostatRepository;
|
||||||
|
|
||||||
|
private void randomJitter(Thermostat thermostat) {
|
||||||
|
updateValueForThermostat(
|
||||||
|
thermostat,
|
||||||
|
Sensor.TYPICAL_VALUES
|
||||||
|
.get(Sensor.SensorType.TEMPERATURE)
|
||||||
|
.multiply(BigDecimal.valueOf(0.975 + Math.random() / 20)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateValueForThermostat(Thermostat thermostat, BigDecimal value) {
|
||||||
|
thermostat.setInternalSensorTemperature(value);
|
||||||
|
thermostatRepository.save(thermostat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fakeUpdateAll() {
|
||||||
|
thermostatRepository.findAll().forEach(this::randomJitter);
|
||||||
|
updateStates();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Thermostat> findAll(String username) {
|
||||||
|
Iterable<Thermostat> all = thermostatRepository.findAllByUsername(username);
|
||||||
|
all.forEach(this::populateMeasuredTemperature);
|
||||||
|
return Utils.toList(all);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean computeState(Thermostat t) {
|
||||||
|
if (t.getState() == Thermostat.ThermostatState.OFF) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
populateMeasuredTemperature(t);
|
||||||
|
BigDecimal measured = t.getMeasuredTemperature();
|
||||||
|
BigDecimal target = t.getTargetTemperature();
|
||||||
|
BigDecimal delta = target.subtract(measured);
|
||||||
|
|
||||||
|
if (delta.abs().doubleValue() < 0.25) {
|
||||||
|
if (t.getState() == Thermostat.ThermostatState.IDLE) return false;
|
||||||
|
t.setState(Thermostat.ThermostatState.IDLE);
|
||||||
|
} else if (delta.signum() > 0) {
|
||||||
|
if (t.getState() == Thermostat.ThermostatState.HEATING) return false;
|
||||||
|
t.setState(Thermostat.ThermostatState.HEATING);
|
||||||
|
} else {
|
||||||
|
if (t.getState() == Thermostat.ThermostatState.COOLING) return false;
|
||||||
|
t.setState(Thermostat.ThermostatState.COOLING);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateState(Thermostat t) {
|
||||||
|
boolean shouldUpdate = this.computeState(t);
|
||||||
|
|
||||||
|
if (shouldUpdate) {
|
||||||
|
thermostatRepository.save(t);
|
||||||
|
endpoint.queueDeviceUpdate(t, thermostatRepository.findUser(t.getId()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateStates() {
|
||||||
|
Iterable<Thermostat> ts = thermostatRepository.findAll();
|
||||||
|
ts.forEach(this::updateState);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Thermostat> findById(Long thermostat, String username) {
|
||||||
|
Optional<Thermostat> t = thermostatRepository.findByIdAndUsername(thermostat, username);
|
||||||
|
|
||||||
|
if (t.isPresent()) {
|
||||||
|
Thermostat u = t.get();
|
||||||
|
populateMeasuredTemperature(u);
|
||||||
|
t = Optional.of(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal measureTemperature(final Thermostat thermostat) {
|
||||||
|
Optional<BigDecimal> average;
|
||||||
|
|
||||||
|
if (thermostat.isUseExternalSensors()) {
|
||||||
|
average = thermostatRepository.getAverageTemperature(thermostat.getRoomId());
|
||||||
|
} else {
|
||||||
|
return thermostat.getInternalSensorTemperature();
|
||||||
|
}
|
||||||
|
|
||||||
|
return average.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void populateMeasuredTemperature(Thermostat thermostat) {
|
||||||
|
thermostat.setMeasuredTemperature(measureTemperature(thermostat));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue