text: 'use strict';
  
  let Service;
  let Characteristic;
  let communicationError;
  
  class HomeAssistantCover {
    constructor(log, data, client, firmware) {
      this.client = client;
      this.log = log;
      // device info
      this.domain = 'cover';
      this.data = data;
      this.entity_id = data.entity_id;
      this.uuid_base = data.entity_id;
      this.firmware = firmware;
      if (data.attributes && data.attributes.friendly_name) {
        this.name = data.attributes.friendly_name;
      } else {
        this.name = data.entity_id.split('.').pop().replace(/_/g, ' ');
      }
      if (data.attributes && data.attributes.homebridge_manufacturer) {
        this.manufacturer = String(data.attributes.homebridge_manufacturer);
      } else {
        this.manufacturer = 'Home Assistant';
      }
      if (data.attributes && data.attributes.homebridge_serial) {
        this.serial = String(data.attributes.homebridge_serial);
      } else {
        this.serial = data.entity_id;
      }
    }
  
    onEvent(oldState, newState) {
      if (newState.state) {
        const state = this.transformData(newState);
  
        this.service.getCharacteristic(this.stateCharacteristic)
          .setValue(state, null, 'internal');
        this.service.getCharacteristic(this.targetCharacteristic)
          .setValue(state, null, 'internal');
      }
    }
  
    getState(callback) {
      this.client.fetchState(this.entity_id, (data) => {
        if (data) {
          callback(null, this.transformData(data));
        } else {
          callback(communicationError);
        }
      });
    }
  
    getServices() {
      const informationService = new Service.AccessoryInformation();
      informationService
        .setCharacteristic(Characteristic.Manufacturer, this.manufacturer)
        .setCharacteristic(Characteristic.Model, this.model)
        .setCharacteristic(Characteristic.SerialNumber, this.serial)
        .setCharacteristic(Characteristic.FirmwareRevision, this.firmware);
  
      this.service
        .getCharacteristic(this.stateCharacteristic)
        .on('get', this.getState.bind(this));
  
      this.service
        .getCharacteristic(this.targetCharacteristic)
        .on('get', this.getState.bind(this))
        .on('set', this.setTargetState.bind(this));
  
      return [informationService, this.service];
    }
  
    doChangeState(service, callback) {
      const serviceData = {
        entity_id: this.entity_id,
      };
  
      this.log(`Calling service ${service} on ${this.name}`);
  
      this.client.callService(this.domain, service, serviceData, (data) => {
        if (data) {
          callback();
        } else {
          callback(communicationError);
        }
      });
    }
  }
  
  class HomeAssistantGarageDoor extends HomeAssistantCover {
    constructor(log, data, client, firmware) {
      super(log, data, client, firmware);
      if (data.attributes && data.attributes.homebridge_model) {
        this.model = String(data.attributes.homebridge_model);
      } else {
        this.model = 'Garage Door';
      }
      this.service = new Service.GarageDoorOpener();
      this.stateCharacteristic = Characteristic.CurrentDoorState;
      this.targetCharacteristic = Characteristic.TargetDoorState;
    }
  
    transformData(data) {
      return data.state === 'closed' ? this.stateCharacteristic.CLOSED : this.stateCharacteristic.OPEN;
    }
  
    setTargetState(targetState, callback, context) {
      if (context === 'internal') {
        callback();
        return;
      }
  
      this.doChangeState(targetState === Characteristic.TargetDoorState.CLOSED ? 'close_cover' : 'open_cover', callback);
    }
  }
  
  class HomeAssistantRollershutter extends HomeAssistantCover {
    constructor(log, data, client, firmware) {
      super(log, data, client, firmware);
      if (data.attributes && data.attributes.homebridge_model) {
        this.model = String(data.attributes.homebridge_model);
      } else {
        this.model = 'Rollershutter';
      }
      this.service = new Service.WindowCovering();
      this.stateCharacteristic = Characteristic.CurrentPosition;
      this.targetCharacteristic = Characteristic.TargetPosition;
    }
  
    transformData(data) {
      return (data && data.attributes) ? data.attributes.current_position : null;
    }
  
    setTargetState(position, callback, context) {
      if (context === 'internal') {
        callback();
        return;
      }
  
      const payload = {
        entity_id: this.entity_id,
        position,
      };
  
      this.log(`Setting the state of the ${this.name} to ${payload.position}`);
  
      this.client.callService(this.domain, 'set_cover_position', payload, (data) => {
        if (data) {
          callback();
        } else {
          callback(communicationError);
        }
      });
    }
  }
  
  class HomeAssistantRollershutterBinary extends HomeAssistantRollershutter {
    transformData(data) {
      return (data && data.state) ? ((data.state === 'open') * 100) : null;
    }
  
    setTargetState(position, callback, context) {
      if (context === 'internal') {
        callback();
        return;
      }
  
      if (!(position === 100 || position === 0)) {
        this.log('Cannot set this cover to positions other than 0 or 100');
        callback(communicationError); // TODO
      } else {
        this.doChangeState(position === 100 ? 'open_cover' : 'close_cover', callback);
      }
    }
  }
  
  function HomeAssistantCoverFactory(log, data, client, firmware) {
    if (!data.attributes) {
      return null;
    }
  
    if (data.attributes.homebridge_cover_type === 'garage_door') {
      return new HomeAssistantGarageDoor(log, data, client, firmware);
    } else if (data.attributes.homebridge_cover_type === 'rollershutter') {
      if (data.attributes.current_position !== undefined) {
        return new HomeAssistantRollershutter(log, data, client, firmware);
      }
      return new HomeAssistantRollershutterBinary(log, data, client, firmware);
    }
    log.error(`'${data.entity_id}' is a cover but does not have a 'homebridge_cover_type' property set. ` +
              'You must set it to either \'rollershutter\' or \'garage_door\' in the customize section ' +
              'of your Home Assistant configuration. It will not be available to Homebridge until you do. ' +
              'See the README.md for more information. ' +
              'The attributes that were found are:', JSON.stringify(data.attributes));
  }
  
  function HomeAssistantCoverPlatform(oService, oCharacteristic, oCommunicationError) {
    Service = oService;
    Characteristic = oCharacteristic;
    communicationError = oCommunicationError;
  
    return HomeAssistantCoverFactory;
  }
  
  module.exports = HomeAssistantCoverPlatform;
  
  module.exports.HomeAssistantCoverFactory = HomeAssistantCoverFactory;
  