<commit_before>const Websocket = require('ws')

const defaultConfig = {
  host: 'localhost',
  timeout: 5000,
  port: 8123,
  password: ''
}


class Homeassistant {
  constructor(config) {
    this.config = Object.assign(defaultConfig, config)

    this.url = `ws://${this.config.host}:${this.config.port}/api/websocket`
    this.promises = {}
    this.id = 1
  }

  connect() {
    this.ws = new Websocket(this.url)

    this.ws.on('message', data => {
      data = JSON.parse(data)

      let d = data.id || data.type
      let p = this.promises[d]

      if (p) {
        clearTimeout(p.timeout)
        p.resolve(data)
      }
    })

    return new Promise((resolve, reject) => {
      this.ws.on('open', () => {
        resolve(this)
      }) 
    })
  }

  send(data) {
    data.id = this.id
    this.id++

    return new Promise((resolve, reject) => {
      this.promises[data.id] = {
        timeout: setTimeout(() => {
          return reject(new Error('No response received from home-assistant'))
        }, this.config.timeout),
        resolve
      }
      this.ws.send(JSON.stringify(data))
    })
  }
}

let ha = new Homeassistant({
  host: '192.168.1.166'
})

ha.connect()
  .then(() => {
    return ha.send({
      type: 'get_states'
    })
  })
  .then(console.log)
  .catch(console.error)<commit_msg>Add api for accessing states<commit_after>const EventEmitter = require('events')
const Websocket = require('ws')

const defaultConfig = {
  host: 'localhost',
  timeout: 5000,
  port: 8123,
  password: ''
}

class Homeassistant extends EventEmitter {
  constructor(options) {
    super()

    this.config = Object.assign(defaultConfig, options)

    this.url = `ws://${this.config.host}:${this.config.port}/api/websocket`
    this.promises = {}
    this.states = []
    this.id = 1
  }

  connect() {
    this.ws = new Websocket(this.url)

    this.ws.on('message', data => {
      data = JSON.parse(data)

      let p = this.promises[data.id]

      if (!p) return false;

      if (p.timeout) {
        clearTimeout(p.timeout)
      }

      if (p.callback) {
        p.callback(data)
      }
    })

    return new Promise((resolve, reject) => {
      this.ws.on('open', () => {
        resolve(this)
      }) 
    }).then(() => {
      return this.send({
        type: 'get_states'
      })
    }).then(states => {
      this.states = states.result

      return this.subscribe({
        callback: this.updateState.bind(this)
      })
    })
  }

  send(data) {
    data.id = this.id
    this.id++

    return new Promise((resolve, reject) => {
      this.promises[data.id] = {
        timeout: setTimeout(() => {
          return reject(new Error('No response received from home-assistant'))
        }, this.config.timeout),
        callback: resolve
      }
      this.ws.send(JSON.stringify(data))
    })
  }

  call(options) {
    return this.send(Object.assign({type: 'call_service'}, options))
  }

  subscribe(options) {
    if(!options.callback) throw new Error('Callback function is required')

    let data = { type: 'subscribe_events' }

    if(options.event) data.event_type = event

    return this.send(data)
      .then((data) => {
        if(!data.success) return Promise.reject(new Error(data))

        this.promises[data.id].callback = options.callback
        return Promise.resolve(data)
      })
  }

  unsubscribe(subscription) {
    return this.send({
      type: 'unsubscribe_events',
      subscription
    })
  }

  findEntity(id) {
    return this.states.findIndex(state => state.entity_id === id)
  }

  updateState(change) {
    let data = change.event.data
    if (change.event.event_type !== 'state_changed') return true

    let changeIndex = this.findEntity(data.entity_id)

    this.states[changeIndex] = data.new_state
    this.emit(`state:${data.entity_id}`, data)
  }

  state(entity) {
    return this.states[this.findEntity(entity)]
  }
}

let ha = new Homeassistant({
  host: '192.168.1.166'
})

ha.connect()
  .then(() => {
    ha.on('state:media_player.spotify', data => console.log(data))
  })
  .catch(console.error)
{'fraction_non_alphanumeric': 0.1561811505507956, 'fraction_numerical': 0.009302325581395349, 'mean_word_length': 2.4568527918781724, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 20, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7495512', 'n_tokens_mistral': 1400, 'n_tokens_neox': 1337, 'n_words': 250}