npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

daichi

v2.1.0

Published

Comfort cloud API wrapper to control HVAC

Downloads

100

Readme

daichi

TypeScript client for the Daichi Comfort Cloud (web.daichicloud.ru), the cloud behind Daichi and Daikin Wi‑Fi HVAC modules. Read the state of your air conditioners, send them commands, and subscribe to live updates over MQTT.

  • Every response is validated with zod and fully typed.
  • Ships CommonJS and ESM builds with type declarations.
  • Needs nothing but the e‑mail and password of your Daichi account.

Unofficial. Not affiliated with Daichi or Daikin. The cloud API is undocumented and may change.

Install

npm i daichi

Quick start

import { DaichiApi } from 'daichi'

const daichi = new DaichiApi('[email protected]', 'login_password_123')

for (const device of await daichi.getDevices()) {
  console.log(device.title, `${device.curTemp}°C`, device.state.isOn ? 'on' : 'off')
}

How the cloud is organised

  • An account owns buildings. Each building lists its places, which are the devices installed there.
  • A device carries its current temperature, an on/off state and one or more pults (remote controls).
  • A pult is a list of functions: power, mode, target temperature, fan speed, swing and so on. Each function has an id, a title, a uiInfo.controlType and a state with isOn and value.
  • Commands target a function by id. Function ids differ between models, so discover them on your own device first (see Discovering function ids).

API

new DaichiApi(username, password, baseUrl?, clientId?)

| Param | Type | Default | Notes | | ---------- | -------- | -------------------------------------- | ---------------------- | | username | string | required | Account e‑mail | | password | string | required | Account password | | baseUrl | string | https://web.daichicloud.ru/api/v4/ | Must end with a slash | | clientId | string | sOJO7B6SqgaKudTfCzqLAy540cCuDzpI | OAuth client id |

The first call logs in (password grant). The access token is fetched once and reused by every later call for the lifetime of the instance. It is never refreshed, so if the cloud starts rejecting calls after a long uptime, create a new instance.

getBuildings()

Returns Promise<DaichiBuilding[]>. Each building includes its places, a lightweight view of every device with id, title, serial, curTemp and state.

getDevices()

Returns a promise of the full state of every device across all buildings. It calls getBuildings() and then getDeviceState() for every place in parallel, so the order of the result is not guaranteed.

getDeviceState(deviceId)

Returns a promise of the full device: title, curTemp, state, deviceInfo (brand, series, model), firmwareVersion, features and the pult list with every controllable function.

controlDevice(deviceId, functionId, value)

Sends one command and returns a promise of the cloud's control response, which contains the updated devices plus presets, schedules and related lists.

  • value: number sets the function's value, for example a target temperature or fan speed.
  • value: boolean switches the function on or off, for example power, a mode or a swing.

getMqttUserInfo()

Returns Promise<MqttUser> with id, username and password for the cloud's MQTT broker. Cached after the first call.

Discovering function ids

import { DaichiApi } from 'daichi'

const daichi = new DaichiApi('[email protected]', 'login_password_123')
const device = await daichi.getDeviceState(54300)

for (const pult of device.pult) {
  for (const fn of pult.functions) {
    console.log(fn.id, fn.title, fn.uiInfo.controlType, fn.state.isOn, fn.state.value)
  }
}

A typical split unit reports a dozen functions such as cooling, heating, auto, dry, fan, quiet mode, vertical swing, comfort sleep, powerful and economy.

Sending commands

import { DaichiApi } from 'daichi'

const daichi = new DaichiApi('[email protected]', 'login_password_123')

// numeric function, e.g. target temperature
await daichi.controlDevice(54300, 472, 24)

// on/off function, e.g. power, a mode or a swing
await daichi.controlDevice(54300, 473, true)

The ids above are examples. Use the ones your device reports.

Live updates over MQTT

The cloud pushes device changes to a broker at wss://split.daichicloud.ru/mqtt. Connect with the credentials from getMqttUserInfo() using any MQTT client, for example mqtt:

import { DaichiApi, DaichiMqttNotificationSchema } from 'daichi'
import { connect } from 'mqtt'

const daichi = new DaichiApi('[email protected]', 'login_password_123')
const mqttUser = await daichi.getMqttUserInfo()

const client = connect('wss://split.daichicloud.ru/mqtt', {
  username: mqttUser.username,
  password: mqttUser.password
})

client.subscribe(`user/${mqttUser.id}/#`)
client.on('message', (topic, payload) => {
  if (!topic.endsWith('notification')) return
  const { devices } = DaichiMqttNotificationSchema.parse(JSON.parse(payload.toString()))
  for (const device of devices) console.log(device.id, device.curTemp)
})

Known topics:

  • user/{id}/notification and user/{id}/pre-notification: device state changes. Parse them with DaichiMqttNotificationSchema. Every device in a notification carries update flags; some also carry the full pult, others only the base fields.
  • user/{id}/out/control/commands/status: command acknowledgements.

Errors

  • The cloud answered but refused the request: the promise rejects with an Error whose message is the cloud's own message, for example a wrong password or an offline device. This holds for 4xx answers too.
  • The cloud answered with an unexpected shape: the promise rejects with a ZodError from zod.
  • HTTP 5xx: the promise rejects with an Error naming the status.
  • Network failure: the promise rejects with a FetchError from ofetch.

Types

The package exports DaichiApi, DaichiBuilding, DeviceBase, DeviceWithControls, MqttUser and DaichiMqttNotificationSchema. The return types of getDeviceState(), getDevices() and controlDevice() are inferred, so pick them up from the class when you need them:

import type { DaichiApi } from 'daichi'

type DaichiDevice = Awaited<ReturnType<DaichiApi['getDeviceState']>>
type DaichiControl = Awaited<ReturnType<DaichiApi['controlDevice']>>

Debug logging

Set DEBUG=daichi to log every cloud response: method, URL, status and body. Access tokens, the account token and the MQTT password are masked. For the wire level, Node's own NODE_DEBUG=fetch works as well.

Development

npm test          # unit tests, offline against an in-process fake of the cloud
npm run test:live # read-only smoke test against the real cloud
npm run lint      # oxlint, type-aware (npm run lint:fix to autofix)
npm run fmt       # oxfmt (npm run fmt:check in CI)
npm run typecheck # tsc --noEmit
npm run build     # CommonJS + ESM + declarations into dist/

The live test needs the credentials of a Daichi account in .env (copy .env.example):

[email protected]
DAICHI_PASSWORD=login_password_123

It only reads data (mqtt user, buildings, device states) and never sends commands. It is skipped when the variables are missing.