Skip to content

Commit

Permalink
Merge branch 'master' into overkiz
Browse files Browse the repository at this point in the history
  • Loading branch information
rpochet authored Oct 6, 2023
2 parents e549921 + f5a6a0d commit 73494cc
Show file tree
Hide file tree
Showing 66 changed files with 3,501 additions and 69 deletions.
12 changes: 12 additions & 0 deletions front/src/actions/dashboard/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
function createActions(store) {
const actions = {
setFullScreen(state, fullScreen) {
store.setState({
fullScreen
});
}
};
return actions;
}

export default createActions;
Binary file added front/src/assets/integrations/cover/melcloud.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions front/src/components/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,12 @@ import TuyaEditPage from '../routes/integration/all/tuya/edit-page';
import TuyaSetupPage from '../routes/integration/all/tuya/setup-page';
import TuyaDiscoverPage from '../routes/integration/all/tuya/discover-page';

// MELCloud integration
import MELCloudPage from '../routes/integration/all/melcloud/device-page';
import MELCloudEditPage from '../routes/integration/all/melcloud/edit-page';
import MELCloudSetupPage from '../routes/integration/all/melcloud/setup-page';
import MELCloudDiscoverPage from '../routes/integration/all/melcloud/discover-page';

const defaultState = getDefaultState();
const store = createStore(defaultState);

Expand Down Expand Up @@ -272,6 +278,11 @@ const AppRouter = connect(
<TuyaDiscoverPage path="/dashboard/integration/device/tuya/discover" />
<TuyaSetupPage path="/dashboard/integration/device/tuya/setup" />

<MELCloudPage path="/dashboard/integration/device/melcloud" />
<MELCloudEditPage path="/dashboard/integration/device/melcloud/edit/:deviceSelector" />
<MELCloudDiscoverPage path="/dashboard/integration/device/melcloud/discover" />
<MELCloudSetupPage path="/dashboard/integration/device/melcloud/setup" />

<BluetoothDevicePage path="/dashboard/integration/device/bluetooth" />
<BluetoothEditDevicePage path="/dashboard/integration/device/bluetooth/:deviceSelector" />
<BluetoothSetupPage path="/dashboard/integration/device/bluetooth/setup" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ const DISPLAY_BY_FEATURE_TYPE = {
[DEVICE_FEATURE_TYPES.SENSOR.BINARY]: BinaryDeviceValue
};

const DEVICE_FEATURES_WITHOUT_EXPIRATION = [
DEVICE_FEATURE_CATEGORIES.SMOKE_SENSOR,
DEVICE_FEATURE_CATEGORIES.LEAK_SENSOR,
DEVICE_FEATURE_CATEGORIES.BUTTON,
DEVICE_FEATURE_CATEGORIES.TEXT
];

const SensorDeviceType = ({ children, ...props }) => {
const { deviceFeature: feature } = props;
const { category, type } = feature;
Expand All @@ -40,8 +47,9 @@ const SensorDeviceType = ({ children, ...props }) => {
elementType = BadgeNumberDeviceValue;
}

// If the device feature has no recent value, we display a message to the user
if (feature.last_value_is_too_old) {
// If the device feature has no recent value, and the feature is not in the blacklist
// we display a message to the user
if (feature.last_value_is_too_old && DEVICE_FEATURES_WITHOUT_EXPIRATION.indexOf(feature.category) === -1) {
elementType = NoRecentValueBadge;
}

Expand Down
2 changes: 1 addition & 1 deletion front/src/components/boxs/room-humidity/RoomHumidity.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const RoomHumidityBox = ({ children, ...props }) => (
</span>
)}
{!isNotNullOrUndefined(props.humidity) && (
<span class="stamp stamp-md bg-red mr-3">
<span class="stamp stamp-md bg-warning mr-3">
<i class="fe fe-droplet" />
</span>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ import { Component } from 'preact';
import { connect } from 'unistore/preact';
import { Text } from 'preact-i18n';
import BaseEditBox from '../baseEditBox';
import ReactSlider from 'react-slider';

import { DEFAULT_VALUE_TEMPERATURE, DEVICE_FEATURE_UNITS } from '../../../../../server/utils/constants';
import RoomSelector from '../../house/RoomSelector';
import cx from 'classnames';
import { celsiusToFahrenheit, fahrenheitToCelsius } from '../../../../../server/utils/units';

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

const EditRoomTemperatureBox = ({ children, ...props }) => (
const EditRoomTemperatureBox = ({ children, unit, ...props }) => (
<BaseEditBox {...props} titleKey="dashboard.boxTitle.temperature-in-room">
<div class="form-group">
<label>
Expand All @@ -20,6 +24,41 @@ const EditRoomTemperatureBox = ({ children, ...props }) => (
updateRoomSelection={updateBoxRoom(props.updateBoxRoom, props.x, props.y)}
/>
</div>
<div className="form-group form-check">
<label className="form-check-label">
<input
type="checkbox"
id="useCustomValue"
className="form-check-input"
checked={props.box.temperature_use_custom_value || false}
onChange={props.updateBoxUseCustomValue}
/>
<Text id="dashboard.boxes.temperatureInRoom.thresholdsLabel" />
</label>
</div>
<div class="form-group">
<ReactSlider
className={cx('temperature-slider', {
'opacity-60': !(props.box.temperature_use_custom_value || false)
})}
thumbClassName="temperature-slider-thumb"
trackClassName="temperature-slider-track"
defaultValue={[props.temperatureMin, props.temperatureMax]}
renderThumb={(props, state) => (
<div {...props}>
<Text id="global.degreeValue" fields={{ value: state.valueNow }} />
<Text id={`global.${unit}`} />
</div>
)}
pearling
minDistance={10}
max={unit === DEVICE_FEATURE_UNITS.CELSIUS ? 50 : 122}
min={unit === DEVICE_FEATURE_UNITS.CELSIUS ? -20 : -4}
onAfterChange={props.updateBoxValue}
value={[props.temperatureMin, props.temperatureMax]}
disabled={!(props.box.temperature_use_custom_value || false)}
/>
</div>
</BaseEditBox>
);

Expand All @@ -29,9 +68,63 @@ class EditRoomTemperatureBoxComponent extends Component {
room
});
};

updateBoxUseCustomValue = e => {
this.props.updateBoxConfig(this.props.x, this.props.y, {
temperature_use_custom_value: e.target.checked
});
};

updateBoxValue = values => {
let temperature_min = values[0];
let temperature_max = values[1];

if (this.props.user.temperature_unit_preference === DEVICE_FEATURE_UNITS.FAHRENHEIT) {
temperature_min = fahrenheitToCelsius(temperature_min);
temperature_max = fahrenheitToCelsius(temperature_max);
}

this.props.updateBoxConfig(this.props.x, this.props.y, {
temperature_min,
temperature_max
});
};

render(props, {}) {
return <EditRoomTemperatureBox {...props} updateBoxRoom={this.updateBoxRoom} />;
let temperature_min = this.props.box.temperature_min;
let temperature_max = this.props.box.temperature_max;

const unit = this.props.user.temperature_unit_preference;

if (!this.props.box.temperature_use_custom_value) {
temperature_min = DEFAULT_VALUE_TEMPERATURE.MINIMUM;
temperature_max = DEFAULT_VALUE_TEMPERATURE.MAXIMUM;
}

if (isNaN(temperature_min)) {
temperature_min = DEFAULT_VALUE_TEMPERATURE.MINIMUM;
}
if (isNaN(temperature_max)) {
temperature_max = DEFAULT_VALUE_TEMPERATURE.MAXIMUM;
}

if (this.props.user.temperature_unit_preference === DEVICE_FEATURE_UNITS.FAHRENHEIT) {
temperature_min = celsiusToFahrenheit(temperature_min);
temperature_max = celsiusToFahrenheit(temperature_max);
}

return (
<EditRoomTemperatureBox
{...props}
updateBoxRoom={this.updateBoxRoom}
updateBoxUseCustomValue={this.updateBoxUseCustomValue}
updateBoxValue={this.updateBoxValue}
temperatureMin={temperature_min}
temperatureMax={temperature_max}
unit={unit}
/>
);
}
}

export default connect('', {})(EditRoomTemperatureBoxComponent);
export default connect('user', {})(EditRoomTemperatureBoxComponent);
65 changes: 57 additions & 8 deletions front/src/components/boxs/room-temperature/RoomTemperature.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,49 @@ import get from 'get-value';

import actions from '../../../actions/dashboard/boxes/temperatureInRoom';
import { DASHBOARD_BOX_STATUS_KEY, DASHBOARD_BOX_DATA_KEY } from '../../../utils/consts';
import { WEBSOCKET_MESSAGE_TYPES } from '../../../../../server/utils/constants';
import {
DEFAULT_VALUE_TEMPERATURE,
DEVICE_FEATURE_UNITS,
WEBSOCKET_MESSAGE_TYPES
} from '../../../../../server/utils/constants';
import { celsiusToFahrenheit } from '../../../../../server/utils/units';

const isNotNullOrUndefined = value => value !== undefined && value !== null;

const RoomTemperatureBox = ({ children, ...props }) => (
<div class="card p-3">
<div class="d-flex align-items-center">
<span class="stamp stamp-md bg-blue mr-3">
<i class="fe fe-thermometer" />
</span>
{isNotNullOrUndefined(props.temperature) &&
props.temperature >= props.temperatureMin &&
props.temperature <= props.temperatureMax && (
<span class="stamp stamp-md bg-green mr-3">
<i class="fe fe-thermometer" />
</span>
)}
{isNotNullOrUndefined(props.temperature) && props.temperature < props.temperatureMin && (
<span class="stamp stamp-md bg-blue mr-3">
<i class="fe fe-thermometer" />
</span>
)}
{isNotNullOrUndefined(props.temperature) && props.temperature > props.temperatureMax && (
<span class="stamp stamp-md bg-red mr-3">
<i class="fe fe-thermometer" />
</span>
)}
{!isNotNullOrUndefined(props.temperature) && (
<span class="stamp stamp-md bg-warning mr-3">
<i class="fe fe-thermometer" />
</span>
)}

<div>
{props.valued && (
{isNotNullOrUndefined(props.temperature) && (
<h4 class="m-0">
<Text id="global.degreeValue" fields={{ value: Number(props.temperature).toFixed(1) }} />
<Text id={`global.${props.unit}`} />
</h4>
)}
{!props.valued && (
{!isNotNullOrUndefined(props.temperature) && (
<p class="m-0">
<Text id="dashboard.boxes.temperatureInRoom.noTemperatureRecorded" />
</p>
Expand Down Expand Up @@ -64,7 +89,29 @@ class RoomTemperatureBoxComponent extends Component {
const temperature = get(boxData, 'room.temperature.temperature');
const unit = get(boxData, 'room.temperature.unit');
const roomName = get(boxData, 'room.name');
const valued = isNotNullOrUndefined(temperature);

const temperature_use_custom_value = get(props, 'box.temperature_use_custom_value');
let temperature_min = get(props, 'box.temperature_min');
let temperature_max = get(props, 'box.temperature_max');

if (!temperature_use_custom_value) {
temperature_min = DEFAULT_VALUE_TEMPERATURE.MINIMUM;
temperature_max = DEFAULT_VALUE_TEMPERATURE.MAXIMUM;
}

if (isNaN(temperature_min)) {
temperature_min = DEFAULT_VALUE_TEMPERATURE.MINIMUM;
}
if (isNaN(temperature_max)) {
temperature_max = DEFAULT_VALUE_TEMPERATURE.MAXIMUM;
}

console.log('unit', unit);

if (unit === DEVICE_FEATURE_UNITS.FAHRENHEIT) {
temperature_min = celsiusToFahrenheit(temperature_min);
temperature_max = celsiusToFahrenheit(temperature_min);
}

return (
<RoomTemperatureBox
Expand All @@ -73,7 +120,9 @@ class RoomTemperatureBoxComponent extends Component {
unit={unit}
boxStatus={boxStatus}
roomName={roomName}
valued={valued}
useCustomValue={temperature_use_custom_value}
temperatureMin={temperature_min}
temperatureMax={temperature_max}
/>
);
}
Expand Down
64 changes: 60 additions & 4 deletions front/src/config/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,8 @@
"editModeLabel": "Select display mode:",
"displayModes": {
"advancedWeather": "Humidity and wind speed",
"hourlyForecast": "Forecast for the next 8 hours",
"dailyForecast": "Forecast for the next 7 days"
"hourlyForecast": "Forecast for the next 24 hours",
"dailyForecast": "Forecast for the next 5 days"
},
"minMaxDegreeValue": "{{min}}°/{{max}}°"
},
Expand All @@ -256,7 +256,8 @@
},
"temperatureInRoom": {
"editRoomLabel": "Select the room you want to display here.",
"noTemperatureRecorded": "No temperature recorded recently."
"noTemperatureRecorded": "No temperature recorded recently.",
"thresholdsLabel": "Configure custom thresholds"
},
"humidityInRoom": {
"editRoomLabel": "Select the room you want to display here.",
Expand Down Expand Up @@ -803,6 +804,61 @@
"conflictError": "Current device is already in Gladys."
}
},
"melcloud": {
"title": "MELCloud",
"description": "Control your MELCloud devices (works with the cloud)",
"deviceTab": "Devices",
"discoverTab": "MELCloud discover",
"setupTab": "Setup",
"discoverDeviceDescr": "Automatically scan MELCloud devices",
"nameLabel": "Device Name",
"namePlaceholder": "Enter the name of your device",
"modelLabel": "Model",
"roomLabel": "Room",
"saveButton": "Save",
"updateButton": "Update",
"alreadyCreatedButton": "Already created",
"deleteButton": "Delete",
"unmanagedModelButton": "Model not managed or available",
"status": {
"notConnected": "Gladys failed to connect to MELCloud cloud account, please go to ",
"setupPageLink": "MELCloud configuration page.",
"online": "Online",
"offline": "Offline"
},
"device": {
"title": "Devices in Gladys",
"updates": "Check updates",
"editButton": "Edit",
"noDeviceFound": "No MELCloud device found.",
"featuresLabel": "Features"
},
"discover": {
"title": "Devices detected on your MELCloud cloud account",
"description": "MELCloud devices are automatically discovered. Your eWeLink devices need to be added to your eWeLink cloud account before.",
"error": "Error discovering MELCloud devices. Please verify your credentials on Setup.",
"noDeviceFound": "No MELCloud device discovered.",
"scan": "Scan"
},
"setup": {
"title": "MELCloud configuration",
"description": "You can connect Gladys to your MELCloud account to control the related devices.",
"username": "Email",
"usernamePlaceholder": "Enter your MELCloud account email address",
"password": "Password",
"passwordPlaceholder": "Enter your MELCloud account password",
"saveLabel": "Save configuration",
"error": "An error occured while saving configuration.",
"connecting": "Configuration saved. Now connecting to your MELCloud cloud account...",
"connected": "Connected to the MELCloud cloud account with success !",
"connectionError": "Error while connecting, please check your configuration."
},
"error": {
"defaultError": "There was an error saving the device.",
"defaultDeletionError": "There was an error deleting the device.",
"conflictError": "Current device is already in Gladys."
}
},
"mqtt": {
"title": "MQTT",
"description": "Connect to a local or remote MQTT server",
Expand All @@ -828,7 +884,7 @@
"notFound": "Requested device not found.",
"backToList": "Back to device list",
"saveError": "Error saving or deleting device",
"saveConflictError": "Conflict: Are you sure all device feature external IDs are unique?",
"saveConflictError": "Conflict: Are you sure all external IDs are unique?",
"mostRecentValueAt": "Last value received {{mostRecentValueAt}}.",
"noValueReceived": "No value received."
},
Expand Down
Loading

0 comments on commit 73494cc

Please sign in to comment.