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

@atrovato [For CF builds] Button click user friendly #1674

Closed
wants to merge 8 commits into from
Closed
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Text } from 'preact-i18n';
import cx from 'classnames';

import { DEVICE_FEATURE_TYPES } from '../../../../../../../server/utils/constants';
import RawDeviceValue from './RawDeviceValue';

const ButtonClickDeviceValue = props => {
const { type, last_value: lastValue = null } = props.deviceFeature;
const valued = lastValue !== null;

if (type !== DEVICE_FEATURE_TYPES.BUTTON.CLICK) {
return <RawDeviceValue {...props} />;
}

return (
<span
class={cx('badge', {
'bg-primary': valued,
'bg-secondary': !valued
})}
>
{!valued && <Text id="dashboard.boxes.devicesInRoom.noValue" />}
{valued && (
<Text id={`deviceFeatureValue.category.button.click.${lastValue}`}>
<Text id={`deviceFeatureValue.category.button.click.unknown`} fields={{ value: lastValue }} />
</Text>
)}
</span>
);
};

export default ButtonClickDeviceValue;
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import LastSeenDeviceValue from './LastSeenDeviceValue';
import BadgeNumberDeviceValue from './BadgeNumberDeviceValue';
import IconBinaryDeviceValue from './IconBinaryDeviceValue';
import SignalQualityDeviceValue from './SignalQualityDeviceValue';
import ButtonClickDeviceValue from './ButtonClickDeviceValue';

const DISPLAY_BY_FEATURE_CATEGORY = {
[DEVICE_FEATURE_CATEGORIES.MOTION_SENSOR]: LastSeenDeviceValue,
[DEVICE_FEATURE_CATEGORIES.PRESENCE_SENSOR]: LastSeenDeviceValue,
[DEVICE_FEATURE_CATEGORIES.OPENING_SENSOR]: IconBinaryDeviceValue,
[DEVICE_FEATURE_CATEGORIES.SIGNAL]: SignalQualityDeviceValue
[DEVICE_FEATURE_CATEGORIES.SIGNAL]: SignalQualityDeviceValue,
[DEVICE_FEATURE_CATEGORIES.BUTTON]: ButtonClickDeviceValue
};

const DISPLAY_BY_FEATURE_TYPE = {
Expand Down
42 changes: 41 additions & 1 deletion front/src/config/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,8 @@ const data = {
'main-tv-volume',
'main-tv-channel',
'main-presence-sensor',
'main-signal-sensor'
'main-signal-sensor',
'button-click'
]
}
],
Expand Down Expand Up @@ -340,6 +341,17 @@ const data = {
read_only: true,
last_value: 4,
last_value_changed: dayjs().add(60, 'second')
},
{
name: 'Button',
selector: 'button-click',
category: 'button',
type: 'click',
min: 0,
max: 6,
read_only: true,
last_value: 1,
last_value_changed: '2019-02-12 07:49:07.556 +00:00'
}
]
},
Expand Down Expand Up @@ -721,6 +733,17 @@ const data = {
last_value: 0,
unit: 'percent',
last_value_changed: '2019-02-12 07:49:07.556 +00:00'
},
{
name: 'Button',
selector: 'button-click',
category: 'button',
type: 'click',
min: 0,
max: 6,
read_only: true,
last_value: 1,
last_value_changed: '2019-02-12 07:49:07.556 +00:00'
}
]
}
Expand Down Expand Up @@ -750,6 +773,23 @@ const data = {
}
]
},
{
id: 'f10ae5bc-1da6-484e-b0d0-953ee94e5ccc',
name: 'Button click',
selector: 'button-click',
features: [
{
name: 'Remote',
selector: 'kitchen-button-click',
category: 'button',
type: 'click',
min: 0,
max: 6,
read_only: true,
last_value_changed: '2019-02-12 07:49:07.556 +00:00'
}
]
},
{
id: '284d8f68-220c-45fd-a73a-eccb547aff24',
name: 'Sensor',
Expand Down
11 changes: 11 additions & 0 deletions front/src/config/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1881,6 +1881,17 @@
}
},
"category": {
"button": {
"click": {
"unknown": "{{value}} (unknown value)",
"1": "Simple click",
"2": "Double click",
"3": "Long click press",
"4": "Long click release",
"5": "Hold click",
"6": "Long click"
}
},
"opening-sensor": {
"binary": {
"other": "No value received",
Expand Down
11 changes: 11 additions & 0 deletions front/src/config/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -1881,6 +1881,17 @@
}
},
"category": {
"button": {
"click": {
"unknown": "{{value}} (valeur inconnue)",
"1": "Clic simple",
"2": "Clic double",
"3": "Pression clic long",
"4": "Relâche clic long",
"5": "Clic maintenu",
"6": "Clic long"
}
},
"opening-sensor": {
"binary": {
"other": "Aucune valeur reçue",
Expand Down
180 changes: 25 additions & 155 deletions front/src/routes/scene/edit-scene/triggers/DeviceFeatureState.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { Component } from 'preact';
import { connect } from 'unistore/preact';
import { Text, Localizer } from 'preact-i18n';
import cx from 'classnames';

import { DEVICE_FEATURE_CATEGORIES, DEVICE_FEATURE_TYPES } from '../../../../../../server/utils/constants';

import SelectDeviceFeature from '../../../../components/device/SelectDeviceFeature';
import BinaryDeviceState from './device-states/BinaryDeviceState';
import PresenceSensorDeviceState from './device-states/PresenceSensorDeviceState';
import ThresholdDeviceState from './device-states/ThresholdDeviceState';
import DefaultDeviceState from './device-states/DefaultDeviceState';
import ButtonClickDeviceState from './device-states/ButtonClickDeviceState';

@connect('httpClient', {})
class TurnOnLight extends Component {
Expand All @@ -19,79 +22,24 @@ class TurnOnLight extends Component {
} else {
this.props.updateTriggerProperty(this.props.index, 'device_feature', null);
}
if (deviceFeature && deviceFeature.type === DEVICE_FEATURE_TYPES.SWITCH.BINARY) {
this.props.updateTriggerProperty(this.props.index, 'operator', '=');
}
if (deviceFeature && deviceFeature.category === DEVICE_FEATURE_CATEGORIES.PRESENCE_SENSOR) {
this.props.updateTriggerProperty(this.props.index, 'operator', '=');
this.props.updateTriggerProperty(this.props.index, 'value', 1);
this.props.updateTriggerProperty(this.props.index, 'threshold_only', false);
}
};
handleOperatorChange = e => {
this.props.updateTriggerProperty(this.props.index, 'operator', e.target.value);
};
handleValueChange = e => {
let value = e.target.value;
if (value.includes(',')) {
value = value.replaceAll(',', '.');
}
const lastCharacter = value.length > 0 ? value[value.length - 1] : '';
if (!isNaN(parseFloat(e.target.value)) && lastCharacter !== '.') {
this.props.updateTriggerProperty(this.props.index, 'value', parseFloat(value));
} else {
this.props.updateTriggerProperty(this.props.index, 'value', value);
}
};
handleValueChangeBinary = newValue => () => {
this.props.updateTriggerProperty(this.props.index, 'value', newValue);
};
handleThresholdOnlyModeChange = e => {
this.props.updateTriggerProperty(this.props.index, 'threshold_only', e.target.checked);
};
getBinaryOperator = () => (
<div class="col-12 col-md-1">
<div class="text-center">
<i class="fe fe-arrow-down d-block d-xs-none d-sm-none" style={{ fontSize: '20px', marginBottom: '15px' }} />
<i class="fe fe-arrow-right d-none d-xs-block d-sm-block" style={{ fontSize: '20px', marginTop: '10px' }} />
</div>
</div>
);
getBinaryButton = (category, value) => (
<div class="col-6">
<button
class={cx('btn', 'btn-block', 'p-1', {
'btn-primary': this.props.trigger.value === value,
'btn-outline-primary': this.props.trigger.value !== value,
active: this.props.trigger.value === value
})}
onClick={this.handleValueChangeBinary(value)}
>
<Text id={`deviceFeatureValue.category.${category}.binary`} plural={value}>
<Text id={`editScene.triggersCard.newState.${value ? 'on' : 'off'}`} />
</Text>
</button>
</div>
);
getBinaryButtons = category => (
<div class="col-12 col-md-5">
<div class="form-group mt-1">
<div class="row d-flex justify-content-center">
{this.getBinaryButton(category, 1)}
{this.getBinaryButton(category, 0)}
</div>
</div>
</div>
);
getPresenceSensor = () => (
<div class="col-6">
<button class="btn btn-block btn-secondary" disabled>
<Text id="editScene.triggersCard.newState.deviceSeen" />
</button>
</div>
);

render(props, { selectedDeviceFeature }) {
let binaryDevice = false;
let presenceDevice = false;
let buttonClickDevice = false;

if (selectedDeviceFeature) {
const { category, type } = selectedDeviceFeature;

binaryDevice = type === DEVICE_FEATURE_TYPES.SWITCH.BINARY;
presenceDevice = category === DEVICE_FEATURE_CATEGORIES.PRESENCE_SENSOR;
buttonClickDevice = category === DEVICE_FEATURE_CATEGORIES.BUTTON;
}

const defaultDevice = selectedDeviceFeature && !binaryDevice && !presenceDevice && !buttonClickDevice;
const thresholdDevice = selectedDeviceFeature && !presenceDevice && !buttonClickDevice;

return (
<div>
<div class="row">
Expand All @@ -103,90 +51,12 @@ class TurnOnLight extends Component {
/>
</div>
</div>
{selectedDeviceFeature &&
selectedDeviceFeature.type === DEVICE_FEATURE_TYPES.SWITCH.BINARY &&
this.getBinaryOperator()}
{selectedDeviceFeature &&
selectedDeviceFeature.type === DEVICE_FEATURE_TYPES.SWITCH.BINARY &&
this.getBinaryButtons(selectedDeviceFeature.category)}
{selectedDeviceFeature &&
selectedDeviceFeature.category === DEVICE_FEATURE_CATEGORIES.PRESENCE_SENSOR &&
this.getPresenceSensor()}
{selectedDeviceFeature &&
selectedDeviceFeature.type !== DEVICE_FEATURE_TYPES.SWITCH.BINARY &&
selectedDeviceFeature.category !== DEVICE_FEATURE_CATEGORIES.PRESENCE_SENSOR && (
<div class="col-md-3">
<div class="form-group">
<select class="form-control" onChange={this.handleOperatorChange} value={props.trigger.operator}>
<option value="">
<Text id="global.emptySelectOption" />
</option>
<option value="=">
<Text id="editScene.triggersCard.newState.equal" />
</option>
<option value=">=">
<Text id="editScene.triggersCard.newState.superiorOrEqual" />
</option>
<option value=">">
<Text id="editScene.triggersCard.newState.superior" />
</option>
<option value="!=">
<Text id="editScene.triggersCard.newState.different" />
</option>
<option value="<=">
<Text id="editScene.triggersCard.newState.lessOrEqual" />
</option>
<option value="<">
<Text id="editScene.triggersCard.newState.less" />
</option>
</select>
</div>
</div>
)}
{selectedDeviceFeature &&
selectedDeviceFeature.type !== DEVICE_FEATURE_TYPES.SWITCH.BINARY &&
selectedDeviceFeature.category !== DEVICE_FEATURE_CATEGORIES.PRESENCE_SENSOR && (
<div class="col-md-3">
<div class="form-group">
<div class="input-group">
<Localizer>
<input
type="text"
class="form-control"
placeholder={<Text id="editScene.triggersCard.newState.valuePlaceholder" />}
value={props.trigger.value}
onChange={this.handleValueChange}
/>
</Localizer>
{selectedDeviceFeature.unit && (
<span class="input-group-append" id="basic-addon2">
<span class="input-group-text">
<Text id={`deviceFeatureUnitShort.${selectedDeviceFeature.unit}`} />
</span>
</span>
)}
</div>
</div>
</div>
)}
{binaryDevice && <BinaryDeviceState {...props} selectedDeviceFeature={selectedDeviceFeature} />}
{presenceDevice && <PresenceSensorDeviceState {...props} />}
{buttonClickDevice && <ButtonClickDeviceState {...props} />}
{defaultDevice && <DefaultDeviceState {...props} selectedDeviceFeature={selectedDeviceFeature} />}
</div>
{selectedDeviceFeature && selectedDeviceFeature.category !== DEVICE_FEATURE_CATEGORIES.PRESENCE_SENSOR && (
<div class="row">
<div class="col-12">
<label class="form-check form-switch">
<input
class="form-check-input"
type="checkbox"
checked={props.trigger.threshold_only}
onChange={this.handleThresholdOnlyModeChange}
/>
<span class="form-check-label">
<Text id="editScene.triggersCard.newState.onlyExecuteAtThreshold" />
</span>
</label>
</div>
</div>
)}
{thresholdDevice && <ThresholdDeviceState {...props} />}
</div>
);
}
Expand Down
Loading