diff --git a/socket_test.html b/socket_test.html index 91399e5..776248d 100644 --- a/socket_test.html +++ b/socket_test.html @@ -29,8 +29,10 @@ connection.onopen = function(evt) { connection.onmessage = function(evt) { console.log("***ONMESSAGE", evt); let data = JSON.parse(evt.data); + let a = document.getElementById("giovanni"); + if (a) a.remove(); - malusa.innerHTML += "

" + JSON.stringify(JSON.parse(evt.data), null, 2) + "

"; + malusa.innerHTML += "
" + JSON.stringify(JSON.parse(evt.data), null, 2) + "
"; }; connection.onerror = function(evt) { diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTRequestFilter.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTRequestFilter.java index e0cbb6a..503f7cd 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTRequestFilter.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/JWTRequestFilter.java @@ -1,6 +1,6 @@ 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 java.io.IOException; import javax.servlet.FilterChain; diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java index 1a5fd4f..193c18b 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/SpringFoxConfig.java @@ -68,6 +68,7 @@ public class SpringFoxConfig { .or(PathSelectors.regex("/room.*")::apply) .or(PathSelectors.regex("/device.*")::apply) .or(PathSelectors.regex("/buttonDimmer.*")::apply) + .or(PathSelectors.regex("/thermostat.*")::apply) .or(PathSelectors.regex("/dimmableLight.*")::apply) .or(PathSelectors.regex("/knobDimmer.*")::apply) .or(PathSelectors.regex("/regularLight.*")::apply) diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java index 2b2f118..4d30bff 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/config/WebSecurityConfig.java @@ -1,6 +1,6 @@ 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.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java index ad48da2..f806f01 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/AuthenticationController.java @@ -8,6 +8,8 @@ import ch.usi.inf.sa4.sanmarinoes.smarthut.error.UserNotFoundException; import ch.usi.inf.sa4.sanmarinoes.smarthut.models.*; import java.security.Principal; import javax.validation.Valid; + +import ch.usi.inf.sa4.sanmarinoes.smarthut.service.JWTUserDetailsService; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.DisabledException; diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java index 2bfa440..9474fd4 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/RoomController.java @@ -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.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 javax.validation.Valid; @@ -29,6 +30,8 @@ public class RoomController { @Autowired private KnobDimmerRepository knobDimmerRepository; + @Autowired private ThermostatService thermostatService; + @GetMapping public List findAll() { return toList(roomRepository.findAll()); @@ -99,6 +102,12 @@ public class RoomController { */ @GetMapping(path = "/{roomId}/devices") public List getDevices(@PathVariable("roomId") long roomid) { - return deviceRepository.findByRoomId(roomid); + Iterable devices = deviceRepository.findByRoomId(roomid); + for (Device d : devices) { + if (d instanceof Thermostat) { + thermostatService.populateMeasuredTemperature((Thermostat) d); + } + } + return toList(devices); } } diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java index 5c415dd..b56fd60 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/SensorController.java @@ -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.error.NotFoundException; 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 java.math.BigDecimal; 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.*; @@ -20,9 +23,14 @@ import org.springframework.web.bind.annotation.*; @RequestMapping("/sensor") public class SensorController { - @Autowired private SensorRepository sensorRepository; + @Autowired + private SensorRepository sensorRepository; - @Autowired private SensorSocketEndpoint sensorSocketEndpoint; + @Autowired + private SensorSocketEndpoint sensorSocketEndpoint; + + @Autowired + private SensorService sensorService; @GetMapping public List findAll() { @@ -45,29 +53,13 @@ public class SensorController { 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") public Sensor updateValue( @PathVariable("id") Long sensorId, @RequestParam("value") BigDecimal value, final Principal principal) throws NotFoundException { - return updateValueFromSensor( + return sensorService.updateValueFromSensor( sensorRepository .findByIdAndUsername(sensorId, principal.getName()) .orElseThrow(NotFoundException::new), diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ThermostatController.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ThermostatController.java new file mode 100644 index 0000000..57e1ca9 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/controller/ThermostatController.java @@ -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 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); + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ThermostatSaveRequest.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ThermostatSaveRequest.java new file mode 100644 index 0000000..5ac3402 --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/dto/ThermostatSaveRequest.java @@ -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; + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SensorRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SensorRepository.java index b796277..dd040e8 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SensorRepository.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/SensorRepository.java @@ -1,3 +1,7 @@ package ch.usi.inf.sa4.sanmarinoes.smarthut.models; -public interface SensorRepository extends DeviceRepository {} +import java.util.List; + +public interface SensorRepository extends DeviceRepository { + List findAllBySensor(Sensor.SensorType sensor); +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Thermostat.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Thermostat.java new file mode 100644 index 0000000..8524b8f --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/Thermostat.java @@ -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; + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ThermostatRepository.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ThermostatRepository.java new file mode 100644 index 0000000..896e0ad --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/ThermostatRepository.java @@ -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 { + + /** + * 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 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 getAverageTemperature(Long thermostatRoomId); +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/UpdateTasks.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/UpdateTasks.java index 9cdb838..4fe293c 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/UpdateTasks.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/scheduled/UpdateTasks.java @@ -1,10 +1,10 @@ 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.SensorController; 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 java.math.BigDecimal; import java.util.Collection; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -26,24 +26,24 @@ public class UpdateTasks { @Autowired private SmartPlugRepository smartPlugRepository; - @Autowired private SensorController sensorController; + @Autowired private SensorService sensorService; + + @Autowired private ThermostatService thermostatService; @Autowired private MotionSensorController motionSensorController; @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) public void sensorFakeUpdate() { - StreamSupport.stream(sensorRepository.findAll().spliterator(), true) - .forEach( - sensor -> - sensorController.updateValueFromSensor( - sensor, - Sensor.TYPICAL_VALUES - .get(sensor.getSensor()) - .multiply( - BigDecimal.valueOf(0.9875 + Math.random() / 40)))); + sensorService.sensorFakeUpdate(); + } + + /** Generates fake sensor updates every two seconds with a +/- 2.5% error */ + @Scheduled(fixedRate = 2000) + public void thermostatInteralSensorFakeUpdate() { + thermostatService.fakeUpdateAll(); } /** @@ -71,7 +71,10 @@ public class UpdateTasks { public void smartPlugConsumptionFakeUpdate() { smartPlugRepository.updateTotalConsumption(SmartPlug.AVERAGE_CONSUMPTION_KW); final Collection 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 */ diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/Service/EmailSenderService.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/EmailSenderService.java similarity index 100% rename from src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/Service/EmailSenderService.java rename to src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/EmailSenderService.java diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/JWTUserDetailsService.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/JWTUserDetailsService.java similarity index 85% rename from src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/JWTUserDetailsService.java rename to src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/JWTUserDetailsService.java index 06ee415..7dff142 100644 --- a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/models/JWTUserDetailsService.java +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/JWTUserDetailsService.java @@ -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 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.security.core.*; import org.springframework.security.core.userdetails.UserDetails; diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/SensorService.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/SensorService.java new file mode 100644 index 0000000..f3072cd --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/SensorService.java @@ -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; + } +} diff --git a/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/ThermostatService.java b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/ThermostatService.java new file mode 100644 index 0000000..fca88ee --- /dev/null +++ b/src/main/java/ch/usi/inf/sa4/sanmarinoes/smarthut/service/ThermostatService.java @@ -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 findAll(String username) { + Iterable 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 ts = thermostatRepository.findAll(); + ts.forEach(this::updateState); + } + + public Optional findById(Long thermostat, String username) { + Optional 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 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)); + } +}