GladysProject/Gladys

View on GitHub
front/src/routes/integration/all/zigbee2mqtt/setup-page/local/SetupLocalOptions.jsx

Summary

Maintainability
C
1 day
Test Coverage
import { Component } from 'preact';
import { Text } from 'preact-i18n';
import get from 'get-value';

import { RequestStatus } from '../../../../../../utils/consts';
import { MQTT_MODE } from '../constants';
import Select from 'react-select';
import SubmitConfiguration from '../components/SubmitConfiguration';

class SetupLocalOptions extends Component {
  updateZigbeeDriverPath = option => {
    const z2mDriverPath = get(option, 'value');
    this.setState({ z2mDriverPath });
  };

  updateZigbeeDongleName = option => {
    const z2mDongleName = get(option, 'value');
    this.setState({ z2mDongleName });
  };

  updateTcpPort = e => {
    const { value } = e.target;
    const z2mTcpPort = value.trim() === '' ? null : value;
    this.setState({ z2mTcpPort });
  };

  saveConfiguration = () => {
    const { z2mDriverPath, z2mDongleName, z2mTcpPort, mqttMode } = this.state;
    this.props.saveConfiguration({ z2mDriverPath, z2mDongleName, z2mTcpPort, mqttMode });
  };

  resetConfiguration = () => {
    const { configuration } = this.props;
    const { z2mDriverPath, z2mDongleName, z2mTcpPort } = configuration;

    this.setState({ z2mDriverPath, z2mDongleName, z2mTcpPort });
    this.props.resetConfiguration();
  };

  loadUsbPorts = async () => {
    this.setState({
      loadUsbPortsStatus: RequestStatus.Getting
    });

    try {
      const rawUsbPorts = await this.props.httpClient.get('/api/v1/service/usb/port');
      // Remove duplicated (dupe /dev/ttyUSB0 seen with Synology systems)
      const usbPortsMap = {};
      rawUsbPorts.forEach(usbPort => {
        const label = [usbPort.comPath, usbPort.comName, usbPort.comVID].filter(Boolean).join(' - ');
        usbPortsMap[usbPort.comPath] = { label, value: usbPort.comPath };
      });
      const usbPorts = Object.values(usbPortsMap);
      this.setState({
        usbPorts,
        loadUsbPortsStatus: RequestStatus.Success
      });
    } catch (e) {
      console.error('Failed to load USB ports', e);
      this.setState({
        loadUsbPortsStatus: RequestStatus.Error
      });
    }
  };

  loadZigbeeAdapters = async () => {
    this.setState({
      loadZigbeeAdaptersStatus: RequestStatus.Getting
    });

    try {
      const zigbeeAdapterLabels = await this.props.httpClient.get('/api/v1/service/zigbee2mqtt/adapter');
      const zigbeeAdapters = zigbeeAdapterLabels.map(adapter => ({
        label: adapter,
        value: adapter
      }));

      this.setState({
        zigbeeAdapters,
        loadZigbeeAdaptersStatus: RequestStatus.Success
      });
    } catch (e) {
      console.error('Failed to load Zigbee adapters', e);
      this.setState({
        loadZigbeeAdaptersStatus: RequestStatus.Error
      });
    }
  };

  buildSelectOption = value => {
    if (value) {
      return { label: value, value };
    }

    return null;
  };

  constructor(props) {
    super(props);

    const { configuration } = props;
    const { z2mDriverPath, z2mDongleName, z2mTcpPort } = configuration;

    this.state = {
      z2mDriverPath,
      usbPorts: [],
      loadUsbPortsStatus: RequestStatus.Getting,
      z2mDongleName,
      zigbeeAdapters: [],
      loadZigbeeAdaptersStatus: RequestStatus.Getting,
      z2mTcpPort,
      mqttMode: MQTT_MODE.LOCAL
    };
  }

  componentDidMount() {
    this.loadUsbPorts();
    this.loadZigbeeAdapters();
  }

  render(
    { disabled },
    { z2mDriverPath, usbPorts, loadUsbPortsStatus, z2mDongleName, zigbeeAdapters, loadZigbeeAdaptersStatus, z2mTcpPort }
  ) {
    return (
      <div>
        <p>
          <Text id="integration.zigbee2mqtt.setup.modes.local.detailsDescription" />
        </p>
        <div class="form-group">
          <label class="form-label">
            <Text id="integration.zigbee2mqtt.setup.modes.local.usbDriverPathLabel" />
          </label>
          <div class="row">
            <div class="col" data-cy="z2m-setup-local-usb-field">
              <Select
                value={this.buildSelectOption(z2mDriverPath)}
                onChange={this.updateZigbeeDriverPath}
                options={usbPorts}
                isLoading={loadUsbPortsStatus === RequestStatus.Getting}
                placeholder={<Text id="integration.zigbee2mqtt.setup.modes.local.usbDriverPathPlaceholder" />}
              />
            </div>
            <div class="col-1 d-none d-sm-block">
              <button
                class="btn btn-outline-success ml-auto"
                onClick={this.loadUsbPorts}
                disabled={loadUsbPortsStatus === RequestStatus.Getting}
              >
                <i class="fe fe-refresh-cw" />
              </button>
            </div>
          </div>
        </div>
        <div class="form-group">
          <label class="form-label">
            <Text id="integration.zigbee2mqtt.setup.modes.local.usbDongleNameLabel" />
          </label>
          <div class="row">
            <div class="col" data-cy="z2m-setup-local-dongle-field">
              <Select
                value={this.buildSelectOption(z2mDongleName)}
                onChange={this.updateZigbeeDongleName}
                options={zigbeeAdapters}
                isLoading={loadZigbeeAdaptersStatus === RequestStatus.Getting}
                placeholder={<Text id="integration.zigbee2mqtt.setup.modes.local.usbDongleNamePlaceholder" />}
                isClearable
              />
            </div>
            <div class="col-1 d-none d-sm-block">
              <button
                class="btn btn-outline-success ml-auto"
                onClick={this.loadZigbeeAdapters}
                disabled={loadZigbeeAdaptersStatus === RequestStatus.Getting}
              >
                <i class="fe fe-refresh-cw" />
              </button>
            </div>
          </div>
        </div>
        <div class="form-group">
          <label class="form-label">
            <Text id="integration.zigbee2mqtt.setup.modes.local.z2mTcpPortLabel" />
          </label>
          <div class="row">
            <div class="col col-sm-11" data-cy="z2m-setup-local-tcp-field">
              <input
                type="number"
                class="form-control"
                value={z2mTcpPort}
                onChange={this.updateTcpPort}
                placeholder="8080"
                min="1"
                max="65535"
              />
            </div>
          </div>
          <small class="form-text text-muted">
            <Text id="integration.zigbee2mqtt.setup.modes.local.z2mTcpPortDescription" />
          </small>
        </div>
        <SubmitConfiguration
          saveDisabled={!z2mDriverPath}
          disabled={disabled}
          saveConfiguration={this.saveConfiguration}
          resetConfiguration={this.resetConfiguration}
        />
      </div>
    );
  }
}

export default SetupLocalOptions;