Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FRONT] Box Humidity in Room #1045

Merged
merged 27 commits into from
Mar 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions front/src/actions/dashboard/boxes/humidityInRoom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { RequestStatus } from '../../../utils/consts';
import createBoxActions from '../boxActions';

const BOX_KEY = 'HumidityInRoom';

function createActions(store) {
const boxActions = createBoxActions(store);

const actions = {
async getHumidityInRoom(state, box, x, y) {
boxActions.updateBoxStatus(state, BOX_KEY, x, y, RequestStatus.Getting);
try {
const room = await state.httpClient.get(`/api/v1/room/${box.room}?expand=humidity`);
boxActions.mergeBoxData(state, BOX_KEY, x, y, {
room
});
boxActions.updateBoxStatus(state, BOX_KEY, x, y, RequestStatus.Success);
} catch (e) {
boxActions.updateBoxStatus(state, BOX_KEY, x, y, RequestStatus.Error);
}
}
};
return Object.assign({}, actions);
}

export default createActions;
16 changes: 16 additions & 0 deletions front/src/actions/dashboard/edit-boxes/editHumidityInRoom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import createBoxActions from '../boxActions';

function createActions(store) {
const boxActions = createBoxActions(store);

const actions = {
updateBoxRoom(state, x, y, room) {
boxActions.updateBoxConfig(state, x, y, {
room
});
}
};
return actions;
}

export default createActions;
34 changes: 34 additions & 0 deletions front/src/components/boxs/room-humidity/EditRoomHumidityBox.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Component } from 'preact';
import { connect } from 'unistore/preact';
import { Text } from 'preact-i18n';
import actions from '../../../actions/dashboard/edit-boxes/editHumidityInRoom';
import BaseEditBox from '../baseEditBox';

import RoomSelector from '../../house/RoomSelector';

const updateBoxRoom = (updateBoxRoomFunc, x, y) => room => {
updateBoxRoomFunc(x, y, room.selector);
};

const EditRoomHumidityBox = ({ children, ...props }) => (
<BaseEditBox {...props} titleKey="dashboard.boxTitle.humidity-in-room">
<div class="form-group">
<label>
<Text id="dashboard.boxes.humidityInRoom.editRoomLabel" />
</label>
<RoomSelector
selectedRoom={props.box.room}
updateRoomSelection={updateBoxRoom(props.updateBoxRoom, props.x, props.y)}
/>
</div>
</BaseEditBox>
);

@connect('', actions)
class EditRoomHumidityBoxComponent extends Component {
render(props, {}) {
return <EditRoomHumidityBox {...props} />;
}
}

export default EditRoomHumidityBoxComponent;
59 changes: 59 additions & 0 deletions front/src/components/boxs/room-humidity/RoomHumidity.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { Component } from 'preact';
import { connect } from 'unistore/preact';
import { Text } from 'preact-i18n';
import actions from '../../../actions/dashboard/boxes/humidityInRoom';
import { DASHBOARD_BOX_STATUS_KEY, DASHBOARD_BOX_DATA_KEY } from '../../../utils/consts';
import get from 'get-value';

const RoomHumidityBox = ({ children, ...props }) => (
<div class="card p-3">
<div class="d-flex align-items-center">
{props.humidity > 45 && props.humidity < 60 && (
<span class="stamp stamp-md bg-green mr-3">
<i class="fe fe-droplet" />
</span>
)}
{props.humidity <= 45 && (
<span class="stamp stamp-md bg-yellow mr-3">
<i class="fe fe-droplet" />
</span>
)}
{props.humidity >= 60 && (
<span class="stamp stamp-md bg-blue mr-3">
<i class="fe fe-droplet" />
</span>
)}
<div>
{props.humidity && (
<h4 class="m-0">
<Text id="global.percentValue" fields={{ value: Math.round(props.humidity) }} />
</h4>
)}
{!props.humidity && (
<p class="m-0">
<Text id="dashboard.boxes.humidityInRoom.noHumidityRecorded" />
</p>
)}
<small class="text-muted">{props.roomName}</small>
</div>
</div>
</div>
);

@connect('DashboardBoxDataHumidityInRoom,DashboardBoxStatusHumidityInRoom', actions)
class RoomHumidityBoxComponent extends Component {
componentDidMount() {
this.props.getHumidityInRoom(this.props.box, this.props.x, this.props.y);
}

render(props, {}) {
const boxData = get(props, `${DASHBOARD_BOX_DATA_KEY}HumidityInRoom.${props.x}_${props.y}`);
const boxStatus = get(props, `${DASHBOARD_BOX_STATUS_KEY}HumidityInRoom.${props.x}_${props.y}`);
const humidity = get(boxData, 'room.humidity.humidity');
const unit = get(boxData, 'room.humidity.unit');
const roomName = get(boxData, 'room.name');
return <RoomHumidityBox {...props} humidity={humidity} unit={unit} boxStatus={boxStatus} roomName={roomName} />;
}
}

export default RoomHumidityBoxComponent;
5 changes: 5 additions & 0 deletions front/src/config/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@
"boxTitle": {
"weather": "Weather",
"temperature-in-room": "Temperature in room",
"humidity-in-room": "Humidity in room",
"user-presence": "User presence",
"camera": "Camera",
"devices-in-room": "Devices in room"
Expand Down Expand Up @@ -212,6 +213,10 @@
"editRoomLabel": "Select the room you want to display here.",
"noTemperatureRecorded": "No temperature recorded recently."
},
"humidityInRoom": {
"editRoomLabel": "Select the room you want to display here.",
"noHumidityRecorded": "No humidity recorded recently."
},
"userPresence": {
"description": "Display who's at home and who is not. You can change the user presence in scenes.",
"left": "Left ({{since}})",
Expand Down
5 changes: 5 additions & 0 deletions front/src/config/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@
"boxTitle": {
"weather": "Météo",
"temperature-in-room": "Température de la pièce",
"humidity-in-room": "Humidité de la pièce",
"user-presence": "Utilisateurs présent",
"camera": "Caméra",
"devices-in-room": "Appareils de la pièce"
Expand Down Expand Up @@ -212,6 +213,10 @@
"editRoomLabel": "Sélectionnez la pièce que vous souhaitez afficher ici.",
"noTemperatureRecorded": "Aucune température enregistrée récemment."
},
"humidityInRoom": {
"editRoomLabel": "Sélectionnez la pièce que vous souhaitez afficher ici.",
"noHumidityRecorded": "Aucune mesure d'humidité enregistrée récemment."
},
"userPresence": {
"description": "Cette box affiche qui est à la maison et qui ne l'est pas. Vous pouvez changer la présence d'un utilisateur dans les scènes.",
"left": "Absent ({{since}})",
Expand Down
3 changes: 3 additions & 0 deletions front/src/routes/dashboard/Box.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import WeatherBox from '../../components/boxs/weather/WeatherBox';
import RoomTemperatureBox from '../../components/boxs/room-temperature/RoomTemperature';
import RoomHumidityBox from '../../components/boxs/room-humidity/RoomHumidity';
import CameraBox from '../../components/boxs/camera/Camera';
import AtHomeBox from '../../components/boxs/user-presence/UserPresence';
import DevicesInRoomsBox from '../../components/boxs/device-in-room/DevicesInRoomsBox';
Expand All @@ -14,6 +15,8 @@ const Box = ({ children, ...props }) => {
return <CameraBox {...props} />;
case 'temperature-in-room':
return <RoomTemperatureBox {...props} />;
case 'humidity-in-room':
return <RoomHumidityBox {...props} />;
case 'devices-in-room':
return <DevicesInRoomsBox {...props} />;
}
Expand Down
3 changes: 3 additions & 0 deletions front/src/routes/dashboard/EditBox.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Text } from 'preact-i18n';
import EditWeatherBox from '../../components/boxs/weather/EditWeatherBox';
import EditRoomTemperatureBox from '../../components/boxs/room-temperature/EditRoomTemperatureBox';
import EditRoomHumidityBox from '../../components/boxs/room-humidity/EditRoomHumidityBox';
import EditCameraBox from '../../components/boxs/camera/EditCamera';
import EditAtHomeBox from '../../components/boxs/user-presence/EditUserPresenceBox';
import EditDevicesInRoom from '../../components/boxs/device-in-room/EditDeviceInRoom';
Expand All @@ -15,6 +16,8 @@ const Box = ({ children, ...props }) => {
return <EditCameraBox {...props} />;
case 'temperature-in-room':
return <EditRoomTemperatureBox {...props} />;
case 'humidity-in-room':
return <EditRoomHumidityBox {...props} />;
case 'devices-in-room':
return <EditDevicesInRoom {...props} />;
}
Expand Down
4 changes: 4 additions & 0 deletions server/api/controllers/room.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ module.exports = function RoomController(gladys) {
unit: req.user.temperature_unit_preference,
});
}
// if the user wants the humidity in the room
if (expandFields.humidity) {
room.humidity = await gladys.device.humiditySensorManager.getHumidityInRoom(room.id);
}
res.json(room);
}

Expand Down
14 changes: 14 additions & 0 deletions server/config/brain/humidity-sensor/answers.en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"label": "humidity-sensor.get-in-room.success",
"answers": ["The humidity level is {{ humidity }}% in the {{ roomName }}."]
},
{
"label": "humidity-sensor.get-in-room.fail.no-results",
"answers": ["No humidity values were recorded in the last hour in this room."]
},
{
"label": "humidity-sensor.get-in-room.fail.room-not-found",
"answers": ["I can't find a room with this name."]
}
]
14 changes: 14 additions & 0 deletions server/config/brain/humidity-sensor/answers.fr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[
{
"label": "humidity-sensor.get-in-room.success",
"answers": ["Le taux d'humidité est de {{ humidity }}% dans la pièce {{ roomName }}."]
},
{
"label": "humidity-sensor.get-in-room.fail.no-results",
"answers": ["Aucune valeur d'humidité n'a été enregistré dans cette pièce au cours de la dernière heure."]
},
{
"label": "humidity-sensor.get-in-room.fail.room-not-found",
"answers": ["Je n'ai pas trouvé de pièce avec ce nom."]
}
]
6 changes: 6 additions & 0 deletions server/config/brain/humidity-sensor/questions.en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
{
"label": "humidity-sensor.get-in-room",
"questions": ["What's the humidity level in the %room% ?", "Give me the humidity level in the %room%"]
}
]
6 changes: 6 additions & 0 deletions server/config/brain/humidity-sensor/questions.fr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[
{
"label": "humidity-sensor.get-in-room",
"questions": ["Quel est le taux d'humidité dans le %room% ?", "Donne moi l'humidité du %room%"]
}
]
52 changes: 52 additions & 0 deletions server/lib/device/humidity-sensor/humidity-sensor.command.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const logger = require('../../../utils/logger');
const { NotFoundError, NoValuesFoundError } = require('../../../utils/coreErrors');
const { DEVICE_FEATURE_UNITS } = require('../../../utils/constants');

/**
* @description Get the average humidity in a room.
* @param {Object} message - The message sent by the user.
* @param {Object} classification - The classification calculated by the brain.
* @param {Object} context - The context object containing found variables in question.
* @example
* command(message, classification, context);
*/
async function command(message, classification, context) {
let humidityResult;
let roomEntity;
try {
switch (classification.intent) {
case 'humidity-sensor.get-in-room':
if (!context.room) {
throw new NotFoundError('Room not found');
}
humidityResult = await this.getHumidityInRoom(context.room, {
unit: DEVICE_FEATURE_UNITS.PERCENT,
});
if (humidityResult.humidity === null) {
throw new NoValuesFoundError('No humidity values found in this room.');
}
roomEntity = classification.entities.find((entity) => entity.entity === 'room');
context.humidity = Math.round(humidityResult.humidity);
VonOx marked this conversation as resolved.
Show resolved Hide resolved
context.unit = DEVICE_FEATURE_UNITS.PERCENT;
context.roomName = roomEntity.sourceText;
this.messageManager.replyByIntent(message, `humidity-sensor.get-in-room.success`, context);
break;
default:
throw new Error('Not found');
}
} catch (e) {
logger.debug(e);
if (e instanceof NotFoundError && e.message === 'Room not found') {
this.messageManager.replyByIntent(message, 'humidity-sensor.get-in-room.fail.room-not-found', context);
} else if (e instanceof NoValuesFoundError) {
this.messageManager.replyByIntent(message, 'humidity-sensor.get-in-room.fail.no-results', context);
} else {
this.messageManager.replyByIntent(message, 'humidity-sensor.get-in-room.fail', context);
}
}
return null;
}

module.exports = {
command,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
const { Op } = require('sequelize');
const logger = require('../../../utils/logger');
const db = require('../../../models');
const { DEVICE_FEATURE_CATEGORIES, DEVICE_FEATURE_UNITS } = require('../../../utils/constants');

const DEFAULT_PARAMETERS = {
unit: DEVICE_FEATURE_UNITS.PERCENT,
};

/**
* @description Return the average value of the humidity in a room.
* @param {string} roomId - The uuid of the room.
* @param {Object} [options] - Options of the query (units).
* @returns {Promise} - Resolve with the humidity and the unit.
* @example
* getHumidityInRoom('d65deccf-d8fc-4674-ac50-3d98d1d87aba', {
* unit: 'percent',
* });
*/
async function getHumidityInRoom(roomId, options) {
logger.debug(`Getting average humidity in room ${roomId}`);
const optionsWithDefault = Object.assign({}, DEFAULT_PARAMETERS, options);

const oneHourAgo = new Date(new Date().getTime() - 1 * 60 * 60 * 1000);
const deviceFeatures = await db.DeviceFeature.findAll({
attributes: ['last_value', 'unit'],
include: [
{
model: db.Device,
as: 'device',
where: {
room_id: roomId,
},
},
],
where: {
category: DEVICE_FEATURE_CATEGORIES.HUMIDITY_SENSOR,
last_value: {
[Op.not]: null,
},
last_value_changed: {
// we want fresh value, less than 1h
[Op.gt]: oneHourAgo,
},
},
});

if (deviceFeatures.length === 0) {
return {
humidity: null,
unit: optionsWithDefault.unit,
};
}

const total = deviceFeatures.reduce((prev, deviceFeature) => deviceFeature.last_value + prev, 0);

// we calculate the average value
const averageHumidity = total / deviceFeatures.length;

// return humidity and unit
return {
humidity: averageHumidity,
unit: optionsWithDefault.unit,
};
}

module.exports = {
getHumidityInRoom,
};
Loading