text: 'use strict';
  /* eslint max-len: 0 */
  
  const assert = require('assert');
  const constants = require('./constants');
  
  class Base {
      withEndpoint(endpointName) {
          this.endpoint = endpointName;
  
          if (this.hasOwnProperty('property')) {
              this.property = `${this.property}_${this.endpoint}`;
          }
  
          if (this.features) {
              for (const feature of this.features) {
                  if (feature.property) {
                      feature.property = `${feature.property}_${endpointName}`;
                      feature.endpoint = endpointName;
                  }
              }
          }
  
          return this;
      }
  
      withAccess(a) {
          assert(this.hasOwnProperty('access'), 'Cannot add access if not defined yet');
          this.access = a;
          return this;
      }
  
      withProperty(property) {
          this.property = property;
          return this;
      }
  
      withDescription(description) {
          this.description = description;
          return this;
      }
  
      removeFeature(feature) {
          assert(this.features, 'Does not have any features');
          const f = this.features.find((f) => f.name === feature);
          assert(f, `Does not have feature '${feature}'`);
          this.features.splice(this.features.indexOf(f), 1);
          return this;
      }
  
      setAccess(feature, a) {
          assert(this.features, 'Does not have any features');
          const f = this.features.find((f) => f.name === feature);
          assert(f.access !== a, `Access mode not changed for '${f.name}'`);
          f.access = a;
          return this;
      }
  }
  
  class Switch extends Base {
      constructor() {
          super();
          this.type = 'switch';
          this.features = [];
      }
  
      withState(property, toggle, description, access=a.ALL, value_on='ON', value_off='OFF') {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          const feature = new Binary('state', access, value_on, value_off).withProperty(property).withDescription(description);
          if (toggle) {
              feature.withValueToggle('TOGGLE');
          }
  
          this.features.push(feature);
          return this;
      }
  }
  
  class Lock extends Base {
      constructor() {
          super();
          this.type = 'lock';
          this.features = [];
      }
  
      withState(property, valueOn, valueOff, description, access=a.ALL) {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          this.features.push(new Binary('state', access, valueOn, valueOff).withProperty(property).withDescription(description));
          return this;
      }
  
      withLockState(property, description) {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          this.features.push(new Enum('lock_state', access.STATE, ['not_fully_locked', 'locked', 'unlocked']).withProperty(property).withDescription(description));
          return this;
      }
  }
  
  class Binary extends Base {
      constructor(name, access, valueOn, valueOff) {
          super();
          this.type = 'binary';
          this.name = name;
          this.property = name;
          this.access = access;
          this.value_on = valueOn;
          this.value_off = valueOff;
      }
  
      withValueToggle(value) {
          this.value_toggle = value;
          return this;
      }
  }
  
  class Numeric extends Base {
      constructor(name, access) {
          super();
          this.type = 'numeric';
          this.name = name;
          this.property = name;
          this.access = access;
      }
  
      withUnit(unit) {
          this.unit = unit;
          return this;
      }
  
      withValueMax(value) {
          this.value_max = value;
          return this;
      }
  
      withValueMin(value) {
          this.value_min = value;
          return this;
      }
  
      withValueStep(value) {
          this.value_step = value;
          return this;
      }
  
      withPreset(name, value, description) {
          if (!this.presets) this.presets = [];
          this.presets.push({name, value, description});
          return this;
      }
  }
  
  class Enum extends Base {
      constructor(name, access, values) {
          super();
          this.type = 'enum';
          this.name = name;
          this.property = name;
          this.access = access;
          this.values = values;
      }
  }
  
  class Text extends Base {
      constructor(name, access) {
          super();
          this.type = 'text';
          this.name = name;
          this.property = name;
          this.access = access;
      }
  }
  
  class Composite extends Base {
      constructor(name, property) {
          super();
          this.type = 'composite';
          this.property = property;
          this.name = name;
          this.features = [];
      }
  
      withFeature(feature) {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          this.features.push(feature);
          return this;
      }
  }
  
  class Light extends Base {
      constructor() {
          super();
          this.type = 'light';
          this.features = [];
          this.features.push(new Binary('state', access.ALL, 'ON', 'OFF').withValueToggle('TOGGLE').withDescription('On/off state of this light'));
      }
  
      withBrightness() {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          this.features.push(new Numeric('brightness', access.ALL).withValueMin(0).withValueMax(254).withDescription('Brightness of this light'));
          return this;
      }
  
      withLevelConfig() {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          const levelConfig = new Composite('level_config', 'level_config')
              .withFeature(new Numeric('on_off_transition_time', access.ALL)
                  .withDescription('Represents the time taken to move to or from the target level when On of Off commands are received by an On/Off cluster'),
              )
              .withFeature(new Numeric('on_transition_time', access.ALL)
                  .withPreset('disabled', 65535, 'Use on_off_transition_time value')
                  .withDescription('Represents the time taken to move the current level from the minimum level to the maximum level when an On command is received'),
              )
              .withFeature(new Numeric('off_transition_time', access.ALL)
                  .withPreset('disabled', 65535, 'Use on_off_transition_time value')
                  .withDescription('Represents the time taken to move the current level from the maximum level to the minimum level when an Off command is received'),
              )
              .withFeature(new Numeric('current_level_startup', access.ALL)
                  .withValueMin(1).withValueMax(254)
                  .withPreset('minimum', 0, 'Use minimum permitted value')
                  .withPreset('previous', 255, 'Use previous value')
                  .withDescription('Defines the desired startup level for a device when it is supplied with power'),
              )
              .withDescription('Configure genLevelCtrl');
          this.features.push(levelConfig);
  
          return this;
      }
  
      withColorTemp(range) {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          const rangeProvided = range !== undefined;
          if (range === undefined) {
              range = [150, 500];
          }
  
          const feature = new Numeric('color_temp', access.ALL).withUnit('mired').withValueMin(range[0]).withValueMax(range[1])
              .withDescription('Color temperature of this light');
  
          if (process.env.ZHC_TEST) {
              feature._colorTempRangeProvided = rangeProvided;
          }
  
          [
              {name: 'coolest', value: range[0], description: 'Coolest temperature supported'},
              {name: 'cool', value: 250, description: 'Cool temperature (250 mireds / 4000 Kelvin)'},
              {name: 'neutral', value: 370, description: 'Neutral temperature (370 mireds / 2700 Kelvin)'},
              {name: 'warm', value: 454, description: 'Warm temperature (454 mireds / 2200 Kelvin)'},
              {name: 'warmest', value: range[1], description: 'Warmest temperature supported'},
          ].filter((p) => p.value >= range[0] && p.value <= range[1]).forEach((p) => feature.withPreset(p.name, p.value, p.description));
  
          this.features.push(feature);
          return this;
      }
  
      withColorTempStartup(range) {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          if (range === undefined) {
              range = [150, 500];
          }
  
          const feature = new Numeric('color_temp_startup', access.ALL).withUnit('mired').withValueMin(range[0]).withValueMax(range[1])
              .withDescription('Color temperature after cold power on of this light');
  
          [
              {name: 'coolest', value: range[0], description: 'Coolest temperature supported'},
              {name: 'cool', value: 250, description: 'Cool temperature (250 mireds / 4000 Kelvin)'},
              {name: 'neutral', value: 370, description: 'Neutral temperature (370 mireds / 2700 Kelvin)'},
              {name: 'warm', value: 454, description: 'Warm temperature (454 mireds / 2200 Kelvin)'},
              {name: 'warmest', value: range[1], description: 'Warmest temperature supported'},
          ].filter((p) => p.value >= range[0] && p.value <= range[1]).forEach((p) => feature.withPreset(p.name, p.value, p.description));
          feature.withPreset('previous', 65535, 'Restore previous color_temp on cold power on');
  
          this.features.push(feature);
          return this;
      }
  
      withColor(types) {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          if (types.includes('xy')) {
              const colorXY = new Composite('color_xy', 'color')
                  .withFeature(new Numeric('x', access.ALL))
                  .withFeature(new Numeric('y', access.ALL))
                  .withDescription('Color of this light in the CIE 1931 color space (x/y)');
              this.features.push(colorXY);
          }
  
          if (types.includes('hs')) {
              const colorHS = new Composite('color_hs', 'color')
                  .withFeature(new Numeric('hue', access.ALL))
                  .withFeature(new Numeric('saturation', access.ALL))
                  .withDescription('Color of this light expressed as hue/saturation');
              this.features.push(colorHS);
          }
  
          return this;
      }
  }
  
  class Cover extends Base {
      constructor() {
          super();
          this.type = 'cover';
          this.features = [];
          this.features.push(new Binary('state', access.STATE_SET, 'OPEN', 'CLOSE'));
      }
  
      withPosition() {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          this.features.push(new Numeric('position', access.ALL).withValueMin(0).withValueMax(100).withDescription('Position of this cover'));
          return this;
      }
  
      withTilt() {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          this.features.push(new Numeric('tilt', access.ALL).withValueMin(0).withValueMax(100).withDescription('Tilt of this cover'));
          return this;
      }
  }
  
  class Fan extends Base {
      constructor() {
          super();
          this.type = 'fan';
          this.features = [];
          this.features.push(new Binary('state', access.ALL, 'ON', 'OFF').withDescription('On/off state of this fan').withProperty('fan_state'));
      }
  
      withModes(modes, access=a.ALL) {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          this.features.push(new Enum('mode', access, modes).withProperty('fan_mode').withDescription('Mode of this fan'));
          return this;
      }
  }
  
  class Climate extends Base {
      constructor() {
          super();
          this.type = 'climate';
          this.features = [];
      }
  
      withSetpoint(property, min, max, step, access=a.ALL) {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          assert(['occupied_heating_setpoint', 'current_heating_setpoint', 'occupied_cooling_setpoint'].includes(property));
          this.features.push(new Numeric(property, access)
              .withValueMin(min).withValueMax(max).withValueStep(step).withUnit('°C').withDescription('Temperature setpoint'));
          return this;
      }
  
      withLocalTemperature(access=a.STATE_GET) {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          this.features.push(new Numeric('local_temperature', access).withUnit('°C').withDescription('Current temperature measured on the device'));
          return this;
      }
  
      withLocalTemperatureCalibration(access=a.ALL) {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          this.features.push(new Numeric('local_temperature_calibration', access).withUnit('°C').withDescription('Offset to be used in the local_temperature'));
          return this;
      }
  
      withSystemMode(modes, access=a.ALL) {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          const allowed = ['off', 'heat', 'cool', 'auto', 'dry', 'fan_only', 'sleep'];
          modes.forEach((m) => assert(allowed.includes(m)));
          this.features.push(new Enum('system_mode', access, modes).withDescription('Mode of this device'));
          return this;
      }
  
      withRunningState(modes, access=a.STATE_GET) {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          const allowed = ['idle', 'heat', 'cool'];
          modes.forEach((m) => assert(allowed.includes(m)));
          this.features.push(new Enum('running_state', access, modes).withDescription('The current running state'));
          return this;
      }
  
      withFanMode(modes) {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          const allowed = ['off', 'low', 'medium', 'high', 'on', 'auto', 'smart'];
          modes.forEach((m) => assert(allowed.includes(m)));
          this.features.push(new Enum('fan_mode', access.ALL, modes).withDescription('Mode of the fan'));
          return this;
      }
  
      withSensor(sensors) {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          this.features.push(new Enum('sensor', access.STATE_SET, sensors).withDescription('Select temperature sensor to use'));
          return this;
      }
  
      withPreset(modes) {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          this.features.push(new Enum('preset', access.STATE_SET, modes).withDescription('Mode of this device (similar to system_mode)'));
          return this;
      }
  
      withAwayMode() {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          this.features.push(new Binary('away_mode', access.STATE_SET, 'ON', 'OFF').withDescription('Away mode'));
          return this;
      }
  
      withPiHeatingDemand(access=a.STATE) {
          assert(!this.endpoint, 'Cannot add feature after adding endpoint');
          this.features.push(new Numeric('pi_heating_demand', access).withValueMin(0).withValueMax(100).withUnit('%').withDescription('Position of the valve (= demanded heat) where 0% is fully closed and 100% is fully open'));
          return this;
      }
  }
  /**
   * The access property is a 3-bit bitmask.
   */
  const access = {
      /**
       * Bit 0: The property can be found in the published state of this device
       */
      STATE: 0b001,
      /**
       * Bit 1: The property can be set with a /set command
       */
      SET: 0b010,
      /**
       * Bit 2: The property can be retrieved with a /get command
       */
      GET: 0b100,
      /**
       * Bitwise inclusive OR of STATE and SET : 0b001 | 0b010
       */
      STATE_SET: 0b011,
      /**
       * Bitwise inclusive OR of STATE and GET : 0b001 | 0b100
       */
      STATE_GET: 0b101,
      /**
       * Bitwise inclusive OR of STATE and GET and SET : 0b001 | 0b100 | 0b010
       */
      ALL: 0b111,
  };
  
  const a = access;
  
  module.exports = {
      access,
      binary: (name, access, valueOn, valueOff) => new Binary(name, access, valueOn, valueOff),
      climate: () => new Climate(),
      composite: (name, property) => new Composite(name, property),
      cover: () => new Cover(),
      enum: (name, access, values) => new Enum(name, access, values),
      light: () => new Light(),
      numeric: (name, access) => new Numeric(name, access),
      switch: () => new Switch(),
      text: (name, access) => new Text(name, access),
      presets: {
          action: (values) => new Enum('action', access.STATE, values).withDescription('Triggered action (e.g. a button click)'),
          angle: (name) => new Numeric(name, access.STATE).withValueMin(-360).withValueMax(360),
          angle_axis: (name) => new Numeric(name, access.STATE).withValueMin(-90).withValueMax(90),
          aqi: () => new Numeric('aqi', access.STATE).withDescription('Air quality index'),
          auto_lock: () => new Switch().withState('auto_lock', false, 'Enable/disable auto lock', access.STATE_SET, 'AUTO', 'MANUAL'),
          auto_relock_time: () => new Numeric('auto_relock_time', access.ALL).withValueMin(0).withUnit('s').withDescription('The number of seconds to wait after unlocking a lock before it automatically locks again. 0=disabled'),
          away_mode: () => new Switch().withState('away_mode', false, 'Enable/disable away mode', access.STATE_SET),
          away_preset_days: () => new Numeric('away_preset_days', access.STATE_SET).withDescription('Away preset days'),
          away_preset_temperature: () => new Numeric('away_preset_temperature', access.STATE_SET).withUnit('°C').withDescription('Away preset temperature'),
          battery: () => new Numeric('battery', access.STATE).withUnit('%').withDescription('Remaining battery in %').withValueMin(0).withValueMax(100),
          battery_low: () => new Binary('battery_low', access.STATE, true, false).withDescription('Indicates if the battery of this device is almost empty'),
          battery_voltage: () => new Numeric('voltage', access.STATE).withUnit('mV').withDescription('Voltage of the battery in millivolts'),
          boost_time: () => new Numeric('boost_time', access.STATE_SET).withUnit('s').withDescription('Boost time'),
          carbon_monoxide: () => new Binary('carbon_monoxide', access.STATE, true, false).withDescription('Indicates if CO (carbon monoxide) is detected'),
          child_lock: () => new Lock().withState('child_lock', 'LOCK', 'UNLOCK', 'Enables/disables physical input on the device', access.STATE_SET),
          co2: () => new Numeric('co2', access.STATE).withUnit('ppm').withDescription('The measured CO2 (carbon monoxide) value'),
          comfort_temperature: () => new Numeric('comfort_temperature', access.STATE_SET).withUnit('°C').withDescription('Comfort temperature'),
          consumer_connected: () => new Binary('consumer_connected', access.STATE, true, false).withDescription('Indicates whether device is physically attached. Device does not have to pull power or even be connected electrically (switch can be ON even if switch is OFF).'),
          consumer_overload: () => new Numeric('consumer_overload', access.STATE, true, false).withUnit('W').withDescription('Indicates with how many Watts the maximum possible power consumption is exceeded'),
          contact: () => new Binary('contact', access.STATE, false, true).withDescription('Indicates if the contact is closed (= true) or open (= false)'),
          cover_position: () => new Cover().withPosition(),
          cover_position_tilt: () => new Cover().withPosition().withTilt(),
          cover_tilt: () => new Cover().withTilt(),
          cpu_temperature: () => new Numeric('cpu_temperature', access.STATE).withUnit('°C').withDescription('Temperature of the CPU'),
          cube_side: (name) => new Numeric(name, access.STATE).withDescription('Side of the cube').withValueMin(0).withValueMax(6).withValueStep(1),
          current: () => new Numeric('current', access.STATE).withUnit('A').withDescription('Instantaneous measured electrical current'),
          current_phase_b: () => new Numeric('current_phase_b', access.STATE).withUnit('A').withDescription('Instantaneous measured electrical current on phase B'),
          current_phase_c: () => new Numeric('current_phase_c', access.STATE).withUnit('A').withDescription('Instantaneous measured electrical current on phase C'),
          deadzone_temperature: () => new Numeric('deadzone_temperature', access.STATE_SET).withUnit('°C').withDescription('The delta between local_temperature and current_heating_setpoint to trigger Heat. 1-5'),
          device_temperature: () => new Numeric('device_temperature', access.STATE).withUnit('°C').withDescription('Temperature of the device'),
          eco2: () => new Numeric('eco2', access.STATE).withUnit('ppm').withDescription('Measured eCO2 value'),
          eco_temperature: () => new Numeric('eco_temperature', access.STATE_SET).withUnit('°C').withDescription('Eco temperature'),
          effect: () => new Enum('effect', access.SET, ['blink', 'breathe', 'okay', 'channel_change', 'finish_effect', 'stop_effect']).withDescription('Triggers an effect on the light (e.g. make light blink for a few seconds)'),
          energy: () => new Numeric('energy', access.STATE).withUnit('kWh').withDescription('Sum of consumed energy'),
          fan: () => new Fan(),
          force: () => new Enum('force', access.STATE_SET, ['normal', 'open', 'close']).withDescription('Force the valve position'),
          formaldehyd: () => new Numeric('formaldehyd', access.STATE).withDescription('The measured formaldehyd value'),
          gas: () => new Binary('gas', access.STATE, true, false).withDescription('Indicates whether the device detected gas'),
          hcho: () => new Numeric('hcho', access.STATE).withUnit('mg/m³').withDescription('Measured Hcho value'),
          humidity: () => new Numeric('humidity', access.STATE).withUnit('%').withDescription('Measured relative humidity'),
          illuminance: () => new Numeric('illuminance', access.STATE).withDescription('Raw measured illuminance'),
          illuminance_lux: () => new Numeric('illuminance_lux', access.STATE).withUnit('lx').withDescription('Measured illuminance in lux'),
          keypad_lockout: () => new Binary('keypad_lockout', access.ALL, 'lock1', 'unlock').withDescription('Enables/disables physical input on the device'),
          led_disabled_night: () => new Binary('led_disabled_night', access.STATE_SET, true, false).withDescription('Enable/disable the LED at night'),
          light_brightness: () => new Light().withBrightness(),
          light_brightness_color: () => new Light().withBrightness().withColor((['xy', 'hs'])),
          light_brightness_colorhs: () => new Light().withBrightness().withColor(['hs']),
          light_brightness_colortemp: (colorTempRange) => new Light().withBrightness().withColorTemp(colorTempRange).withColorTempStartup(colorTempRange),
          light_brightness_colortemp_color: (colorTempRange) => new Light().withBrightness().withColorTemp(colorTempRange).withColorTempStartup(colorTempRange).withColor(['xy', 'hs']),
          light_brightness_colortemp_colorhs: (colorTempRange) => new Light().withBrightness().withColorTemp(colorTempRange).withColorTempStartup(colorTempRange).withColor(['hs']),
          light_brightness_colortemp_colorxy: (colorTempRange) => new Light().withBrightness().withColorTemp(colorTempRange).withColorTempStartup(colorTempRange).withColor(['xy']),
          light_brightness_colorxy: () => new Light().withBrightness().withColor((['xy'])),
          light_colorhs: () => new Light().withColor(['hs']),
          linkquality: () => new Numeric('linkquality', access.STATE).withUnit('lqi').withDescription('Link quality (signal strength)').withValueMin(0).withValueMax(255),
          local_temperature: () => new Numeric('local_temperature', access.STATE_GET).withUnit('°C').withDescription('Current temperature measured on the device'),
          lock: () => new Lock().withState('state', 'LOCK', 'UNLOCK', 'State of the lock').withLockState('lock_state', 'Actual state of the lock'),
          max_temperature: () => new Numeric('max_temperature', access.STATE_SET).withUnit('°C').withDescription('Maximum temperature'),
          max_temperature_limit: () => new Numeric('max_temperature_limit', access.STATE_SET).withUnit('°C').withDescription('Maximum temperature limit'),
          min_temperature: () => new Numeric('min_temperature', access.STATE_SET).withUnit('°C').withDescription('Minimum temperature'),
          occupancy: () => new Binary('occupancy', access.STATE, true, false).withDescription('Indicates whether the device detected occupancy'),
          pm10: () => new Numeric('pm10', access.STATE).withUnit('µg/m³').withDescription('Measured PM10 (particulate matter) concentration'),
          pm25: () => new Numeric('pm25', access.STATE).withUnit('µg/m³').withDescription('Measured PM2.5 (particulate matter) concentration'),
          position: () => new Numeric('position', access.STATE).withUnit('%').withDescription('Position'),
          power: () => new Numeric('power', access.STATE).withUnit('W').withDescription('Instantaneous measured power'),
          power_on_behavior: () => new Enum('power_on_behavior', access.ALL, ['off', 'previous', 'on']).withDescription('Controls the behavior when the device is powered on'),
          power_outage_memory: () => new Binary('power_outage_memory', access.ALL, true, false).withDescription('Enable/disable the power outage memory, this recovers the on/off mode after power failure'),
          presence: () => new Binary('presence', access.STATE, true, false).withDescription('Indicates whether the device detected presence'),
          pressure: () => new Numeric('pressure', access.STATE).withUnit('hPa').withDescription('The measured atmospheric pressure'),
          smoke: () => new Binary('smoke', access.STATE, true, false).withDescription('Indicates whether the device detected smoke'),
          soil_moisture: () => new Numeric('soil_moisture', access.STATE).withUnit('%').withDescription('Measured soil moisture value'),
          sos: () => new Binary('sos', access.STATE, true, false).withDescription('SOS alarm'),
          sound_volume: () => new Enum('sound_volume', access.ALL, constants.lockSoundVolume).withDescription('Sound volume of the lock'),
          switch: () => new Switch().withState('state', true, 'On/off state of the switch'),
          switch_type: () => new Enum('switch_type', access.ALL, ['toggle', 'momentary']).withDescription('Wall switch type'),
          tamper: () => new Binary('tamper', access.STATE, true, false).withDescription('Indicates whether the device is tampered'),
          temperature: () => new Numeric('temperature', access.STATE).withUnit('°C').withDescription('Measured temperature value'),
          test: () => new Binary('test', access.STATE, true, false).withDescription('Indicates whether the device is being tested'),
          valve_detection: () => new Switch().withState('valve_detection', true).setAccess('state', access.STATE_SET),
          vibration: () => new Binary('vibration', access.STATE, true, false).withDescription('Indicates whether the device detected vibration'),
          voc: () => new Numeric('voc', access.STATE).withUnit('ppb').withDescription('Measured VOC value'),
          voltage: () => new Numeric('voltage', access.STATE).withUnit('V').withDescription('Measured electrical potential value'),
          voltage_phase_b: () => new Numeric('voltage_phase_b', access.STATE).withUnit('V').withDescription('Measured electrical potential value on phase B'),
          voltage_phase_c: () => new Numeric('voltage_phase_c', access.STATE).withUnit('V').withDescription('Measured electrical potential value on phase C'),
          water_leak: () => new Binary('water_leak', access.STATE, true, false).withDescription('Indicates whether the device detected a water leak'),
          warning: () => new Composite('warning', 'warning')
              .withFeature(new Enum('mode', access.SET, ['stop', 'burglar', 'fire', 'emergency', 'police_panic', 'fire_panic', 'emergency_panic']).withDescription('Mode of the warning (sound effect)'))
              .withFeature(new Enum('level', access.SET, ['low', 'medium', 'high', 'very_high']).withDescription('Sound level'))
              .withFeature(new Binary('strobe', access.SET, true, false).withDescription('Turn on/off the strobe (light) during warning'))
              .withFeature(new Numeric('duration', access.SET).withUnit('s').withDescription('Duration in seconds of the alarm')),
          week: () => new Enum('week', access.STATE_SET, ['5+2', '6+1', '7']).withDescription('Week format user for schedule'),
          window_detection: () => new Switch().withState('window_detection', true, 'Enables/disables window detection on the device', access.STATE_SET),
          moving: () => new Binary('moving', access.STATE, true, false).withDescription('Indicates if the device is moving'),
          x_axis: () => new Numeric('x_axis', access.STATE).withDescription('Accelerometer X value'),
          y_axis: () => new Numeric('y_axis', access.STATE).withDescription('Accelerometer Y value'),
          z_axis: () => new Numeric('z_axis', access.STATE).withDescription('Accelerometer Z value'),
      },
  };
  